From fe4d1082a6c04b1e7eb2eb320d55fe2f3578af70 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 7 May 2024 20:09:16 +0530 Subject: [PATCH 001/178] Implement scaffolding --- .../api/constants/RuntimeConstants.java | 2 + .../runtime/api/types/BasicTypeBitSet.java | 37 +++ .../api/types/SemType/BasicTypeCode.java | 97 ++++++ .../runtime/api/types/SemType/Builder.java | 85 +++++ .../runtime/api/types/SemType/Context.java | 23 ++ .../runtime/api/types/SemType/Core.java | 288 +++++++++++++++++ .../runtime/api/types/SemType/SemType.java | 32 ++ .../api/types/SemType/SemTypeHelper.java | 26 ++ .../api/types/SemType/SemTypeWrapper.java | 26 ++ .../runtime/api/types/SemType/SubType.java | 47 +++ .../runtime/internal/FallbackTypeChecker.java | 201 ++++++++++++ .../runtime/internal/TypeChecker.java | 214 +++---------- .../runtime/internal/types/BType.java | 15 +- .../internal/types/semtype/AllOrNothing.java | 32 ++ .../types/semtype/BBasicTypeBitSet.java | 129 ++++++++ .../types/semtype/BBooleanSubType.java | 160 ++++++++++ .../types/semtype/BDecimalSubType.java | 176 ++++++++++ .../internal/types/semtype/BFloatSubType.java | 153 +++++++++ .../internal/types/semtype/BIntSubType.java | 302 ++++++++++++++++++ .../internal/types/semtype/BSemType.java | 166 ++++++++++ .../types/semtype/BSemTypeWithIdentity.java | 143 +++++++++ .../types/semtype/BStringSubType.java | 199 ++++++++++++ .../internal/types/semtype/BSubType.java | 76 +++++ .../types/semtype/BSubTypeWrapper.java | 0 .../internal/types/semtype/BTypeAdapter.java | 121 +++++++ .../types/semtype/EnumerableSubtypeData.java | 163 ++++++++++ .../internal/types/semtype/SubTypeData.java | 28 ++ .../internal/types/semtype/SubtypePair.java | 25 ++ .../types/semtype/SubtypePairIterator.java | 69 ++++ .../internal/types/semtype/SubtypePairs.java | 41 +++ .../internal/types/semtype/TypeMetadata.java | 33 ++ .../src/main/java/module-info.java | 5 +- .../types/semtype/BBooleanSubTypeTests.java | 95 ++++++ .../types/semtype/BDecimalSubTypeTest.java | 61 ++++ .../internal/types/semtype/CoreTests.java | 121 +++++++ 35 files changed, 3213 insertions(+), 178 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/BasicTypeBitSet.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/BasicTypeCode.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Builder.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Context.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Core.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubTypeWrapper.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.java create mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java create mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java create mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java 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/types/BasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/BasicTypeBitSet.java new file mode 100644 index 000000000000..8b057e341ddf --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/BasicTypeBitSet.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; + +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; + +public interface BasicTypeBitSet extends SemType { + + @Override + default int some() { + return 0; + } + + @Override + default List subTypeData() { + return List.of(); + } +} 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..c5647c7c48be --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/BasicTypeCode.java @@ -0,0 +1,97 @@ +/* + * 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 java.util.HashMap; +import java.util.Map; + +public final class BasicTypeCode { + + private final static Map cache = new HashMap<>(); + + static final int CODE_NIL = 0x00; + static final int CODE_BOOLEAN = 0x01; + static final int CODE_INT = 0x02; + static final int CODE_FLOAT = 0x03; + static final int CODE_DECIMAL = 0x04; + static final int CODE_STRING = 0x05; + static final int CODE_ERROR = 0x06; + static final int CODE_TYPEDESC = 0x07; + static final int CODE_HANDLE = 0x08; + static final int CODE_FUNCTION = 0x09; + static final int CODE_FUTURE = 0x0A; + static final int CODE_STREAM = 0x0B; + static final int CODE_LIST = 0x0C; + static final int CODE_MAPPING = 0x0D; + static final int CODE_TABLE = 0x0E; + static final int CODE_XML = 0x0F; + static final int CODE_OBJECT = 0x10; + static final int CODE_CELL = 0x11; + static final int CODE_UNDEF = 0x12; + static final int CODE_B_TYPE = 0x13; + + // Inherently immutable + public static final BasicTypeCode BT_NIL = from(CODE_NIL); + public static final BasicTypeCode BT_BOOLEAN = from(CODE_BOOLEAN); + public static final BasicTypeCode BT_INT = from(CODE_INT); + public static final BasicTypeCode BT_FLOAT = from(CODE_FLOAT); + public static final BasicTypeCode BT_DECIMAL = from(CODE_DECIMAL); + public static final BasicTypeCode BT_STRING = from(CODE_STRING); + public static final BasicTypeCode BT_ERROR = from(CODE_ERROR); + public static final BasicTypeCode BT_TYPEDESC = from(CODE_TYPEDESC); + public static final BasicTypeCode BT_HANDLE = from(CODE_HANDLE); + public static final BasicTypeCode BT_FUNCTION = from(CODE_FUNCTION); + + // Inherently mutable + public static final BasicTypeCode BT_FUTURE = from(CODE_FUTURE); + public static final BasicTypeCode BT_STREAM = from(CODE_STREAM); + + // Selectively immutable + public static final BasicTypeCode BT_LIST = from(CODE_LIST); + public static final BasicTypeCode BT_MAPPING = from(CODE_MAPPING); + public static final BasicTypeCode BT_TABLE = from(CODE_TABLE); + public static final BasicTypeCode BT_XML = from(CODE_XML); + public static final BasicTypeCode BT_OBJECT = from(CODE_OBJECT); + + // Non-val + public static final BasicTypeCode BT_CELL = from(CODE_CELL); + public static final BasicTypeCode BT_UNDEF = from(CODE_UNDEF); + public static final BasicTypeCode BT_B_TYPE = from(CODE_B_TYPE); + + // 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 = 0x0A; + public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; + + private int code; + + private BasicTypeCode(int code) { + this.code = code; + } + + public static BasicTypeCode from(int code) { + return cache.computeIfAbsent(code, BasicTypeCode::new); + } + + public int code() { + return code; + } +} 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..9b5b2aed8513 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Builder.java @@ -0,0 +1,85 @@ +/* + * 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.api.types.Type; +import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; +import io.ballerina.runtime.internal.types.semtype.BFloatSubType; +import io.ballerina.runtime.internal.types.semtype.BIntSubType; +import io.ballerina.runtime.internal.types.semtype.BSemType; +import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import io.ballerina.runtime.internal.types.semtype.BSubType; + +import java.util.ArrayList; +import java.util.List; + +public final class Builder { + + private Builder() { + } + + public static SemType from(BasicTypeCode typeCode) { + return BBasicTypeBitSet.from(1 << typeCode.code()); + } + + public static SemType from(Type type) { + if (type instanceof SemType semType) { + return semType; + } else if (type instanceof BType bType) { + return from(bType); + } + throw new IllegalArgumentException("Unsupported type: " + type); + } + + public static SemType from(BType innerType) { + return basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(innerType)); + } + + public static SemType basicTypeUnion(int bitset) { + return BBasicTypeBitSet.from(bitset); + } + + public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { + return BSemType.from(0, 1 << basicTypeCode.code(), List.of(subType)); + } + + public static SemType intConst(long value) { + List values = new ArrayList<>(1); + values.add(value); + return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(values)); + } + + public static SemType floatConst(double value) { + Double[] values = {value}; + return basicSubType(BasicTypeCode.BT_FLOAT, BFloatSubType.createFloatSubType(true, values)); + } + + public static SemType stringConst(String value) { + BStringSubType subType; + String[] values = {value}; + String[] empty = new String[0]; + if (value.codePoints().count() == 1) { + subType = BStringSubType.createStringSubType(true, values, false, empty); + } else { + subType = BStringSubType.createStringSubType(false, empty, true, values); + } + return basicSubType(BasicTypeCode.BT_STRING, subType); + } +} 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..c6bd2227623d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Context.java @@ -0,0 +1,23 @@ +/* + * 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; + +public class Context { + +} 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..26e801a2d5b2 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Core.java @@ -0,0 +1,288 @@ +/* + * 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.api.types.BasicTypeBitSet; +import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.internal.types.semtype.AllOrNothing; +import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; +import io.ballerina.runtime.internal.types.semtype.BSemType; +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.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.BT_B_TYPE; +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_UNDEF; +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.VT_MASK; + +public final class Core { + + private static final SemType SEMTYPE_TOP = BBasicTypeBitSet.from((1 << (CODE_UNDEF + 1)) - 1); + + private Core() { + } + + public static SemType diff(SemType t1, SemType t2) { + if (t1.some() == 0) { + if (t2.some() == 0) { + return Builder.basicTypeUnion(t1.all() & ~t2.all()); + } else { + if (t1.all() == 0) { + return t1; + } + } + } else { + if (t2.some() == 0) { + if (t2.all() == VT_MASK) { + return BBasicTypeBitSet.from(0); + } + } + } + int all1 = t1.all(); + int all2 = t2.all(); + int some1 = t1.some(); + int some2 = t2.some(); + int all = all1 & ~(all2 | some2); + int some = (all1 | some1) & ~all2; + some = some & ~all; + if (some == 0) { + return BBasicTypeBitSet.from(all); + } + List subtypes = new ArrayList<>(); + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + int typeCode = 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 << typeCode; + some &= ~(1 << typeCode); + subtypes.add(null); + } else if (data.isNothing()) { + some &= ~(1 << typeCode); + subtypes.add(null); + } else { + subtypes.add(data); + } + } + return BSemType.from(all, some, subtypes); + } + + public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { + throw new IllegalStateException("Unimplemented"); + } + + public static SemType union(SemType t1, SemType t2) { + if (t1.some() == 0) { + if (t2.some() == 0) { + return BBasicTypeBitSet.from(t1.all() | t2.all()); + } + } + int all1 = t1.all(); + int some1 = t1.some(); + int all2 = t2.all(); + int some2 = t2.some(); + + int all = all1 | all2; + int some = (some1 | some2) & ~all; + if (some == 0) { + return BBasicTypeBitSet.from(all); + } + List subtypes = new ArrayList<>(); + 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()) { + all |= 1 << code; + some &= ~(1 << code); + subtypes.add(null); + } else { + subtypes.add(data); + } + } + if (some == 0) { + return BBasicTypeBitSet.from(all); + } + return BSemType.from(all, some, subtypes); + } + + public static SemType intersect(SemType t1, SemType t2) { + if (t1.some() == 0) { + if (t2.some() == 0) { + return BBasicTypeBitSet.from(t1.all() & t2.all()); + } else { + if (t1.all() == 0) { + return t1; + } + if (t1.all() == VT_MASK) { + return t2; + } + } + } else if (t2.some() == 0) { + if (t2.all() == 0) { + return t2; + } + if (t2.all() == VT_MASK) { + return t1; + } + } + int all1 = t1.all(); + int some1 = t1.some(); + int all2 = t2.all(); + int some2 = t2.some(); + + int all = all1 & all2; + int some = (some1 | all1) & (some2 | all2); + some = some & ~all; + if (some == 0) { + return BBasicTypeBitSet.from(all); + } + + List subtypes = new ArrayList<>(); + 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.add(data); + } else { + some &= ~(1 << code); + subtypes.add(null); + } + } + if (some == 0) { + return BBasicTypeBitSet.from(all); + } + return BSemType.from(all, some, 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()) { + if (subType == null) { + continue; + } + if (!subType.isEmpty()) { + return false; + } + } + return true; + } + + public static SemType complement(SemType t1) { + throw new IllegalStateException("Unimplemented"); + } + + public static boolean isNever(SemType t) { + throw new IllegalStateException("Unimplemented"); + } + + public static boolean isSubType(Context cx, SemType t1, SemType t2) { + // IF t1 and t2 are not pure semtypes calling this is an error + return isEmpty(cx, diff(t1, t2)); + } + + public static boolean isSubType(Context cx, SemType t1, SemType t2, + BiFunction fallback) { + SemType s1 = intersect(t1, SEMTYPE_TOP); + SemType s2 = intersect(t2, SEMTYPE_TOP); + return isEmpty(cx, diff(s1, s2)) && applyFallback(t1, t2, fallback); + } + + private static boolean applyFallback(SemType t1, SemType t2, + BiFunction fallback) { + BType bType1 = (BType) subTypeData(t1, BT_B_TYPE); + BType bType2 = (BType) subTypeData(t2, BT_B_TYPE); + return fallback.apply(bType1, bType2); + } + + private static SubTypeData subTypeData(SemType s, BasicTypeCode code) { + if ((s.all() & (1 << code.code())) != 0) { + return AllOrNothing.ALL; + } + if (s.some() == 0) { + return AllOrNothing.NOTHING; + } + return s.subTypeData().get(code.code()).data(); + } + + public static boolean isSubTypeSimple(SemType t1, BasicTypeBitSet t2) { + throw new IllegalStateException("Unimplemented"); + } + + public static boolean isSameType(Context cx, SemType t1, SemType t2) { + return isSubType(cx, t1, t2) && isSubType(cx, t2, t1); + } + + public static BasicTypeBitSet widenToBasicTypes(SemType t) { + int all = t.all() | t.some(); + if (cardinality(all) > 1) { + throw new IllegalStateException("Cannot widen to basic type for a type with multiple basic types"); + } + return BBasicTypeBitSet.from(all); + } + + private static boolean isSet(int bitset, int index) { + return (bitset & (1 << index)) != 0; + } + + private static int cardinality(int bitset) { + int count = 0; + while (bitset != 0) { + count += bitset & 1; + bitset >>= 1; + } + return count; + } +} 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..83d4d5b1475f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemType.java @@ -0,0 +1,32 @@ +/* + * 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.api.types.Type; + +import java.util.List; + +public interface SemType extends Type { + + int all(); + + int some(); + + List subTypeData(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java new file mode 100644 index 000000000000..04636b56b040 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java @@ -0,0 +1,26 @@ +/* + * 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; + +public final class SemTypeHelper { + + private SemTypeHelper() { + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java new file mode 100644 index 000000000000..dbfbdcf68953 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java @@ -0,0 +1,26 @@ +/* + * 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.api.types.Type; + +public interface SemTypeWrapper extends Type { + + SemType semType(); +} 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..9b538a2a24a9 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SubType.java @@ -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. + */ + +package io.ballerina.runtime.api.types.SemType; + +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +/** + * Describe set of operation supported by each basic Type + * + * @since 2201.10.0 + */ +public interface SubType { + + SubType union(SubType other); + + SubType intersect(SubType other); + + default SubType diff(SubType other) { + return this.intersect(other.complement()); + } + + SubType complement(); + + boolean isEmpty(); + + boolean isAll(); + + boolean isNothing(); + + SubTypeData data(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java new file mode 100644 index 000000000000..05b105657d85 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java @@ -0,0 +1,201 @@ +/* + * 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; + +import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.ParameterizedType; +import io.ballerina.runtime.api.types.ReferenceType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.XmlNodeType; +import io.ballerina.runtime.internal.types.BFiniteType; +import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.BXmlType; +import io.ballerina.runtime.internal.values.XmlValue; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; + +final class FallbackTypeChecker { + + private FallbackTypeChecker() { + } + + static boolean checkIsType(List errors, Object sourceVal, BType sourceType, BType targetType) { + if (TypeChecker.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 TypeChecker.checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), + false, null); + } + } + + if (TypeChecker.isMutable(sourceVal, sourceType)) { + return false; + } + + return TypeChecker.checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, + null); + } + + @Deprecated + static boolean checkIsType(BType sourceType, BType targetType, List unresolvedTypes) { + // First check whether both types are the same. + if (sourceType == targetType || (sourceType.getTag() == targetType.getTag() && sourceType.equals(targetType))) { + return true; + } + + if (TypeChecker.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 TypeChecker.checkIsType(((IntersectionType) sourceType).getEffectiveType(), + targetTypeTag != TypeTags.INTERSECTION_TAG ? targetType : + ((IntersectionType) targetType).getEffectiveType(), unresolvedTypes); + case TypeTags.TYPE_REFERENCED_TYPE_TAG: + return TypeChecker.checkIsType(((ReferenceType) sourceType).getReferredType(), + targetTypeTag != TypeTags.TYPE_REFERENCED_TYPE_TAG ? targetType : + ((ReferenceType) targetType).getReferredType(), unresolvedTypes); + case TypeTags.PARAMETERIZED_TYPE_TAG: + if (targetTypeTag != TypeTags.PARAMETERIZED_TYPE_TAG) { + return TypeChecker.checkIsType(((ParameterizedType) sourceType).getParamValueType(), targetType, + unresolvedTypes); + } + return TypeChecker.checkIsType(((ParameterizedType) sourceType).getParamValueType(), + ((ParameterizedType) targetType).getParamValueType(), unresolvedTypes); + case TypeTags.READONLY_TAG: + return TypeChecker.checkIsType(PredefinedTypes.ANY_AND_READONLY_OR_ERROR_TYPE, + targetType, unresolvedTypes); + case TypeTags.UNION_TAG: + return TypeChecker.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 TypeChecker.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 -> TypeChecker.checkIsAnyType(sourceType); + case TypeTags.ANYDATA_TAG -> sourceType.isAnydata(); + case TypeTags.SERVICE_TAG -> TypeChecker.checkIsServiceType(sourceType, targetType, + unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); + case TypeTags.HANDLE_TAG -> sourceTypeTag == TypeTags.HANDLE_TAG; + case TypeTags.READONLY_TAG -> + TypeChecker.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 -> + TypeChecker.checkIsType(sourceType, ((IntersectionType) targetType).getEffectiveType(), + unresolvedTypes); + case TypeTags.TYPE_REFERENCED_TYPE_TAG -> + TypeChecker.checkIsType(sourceType, ((ReferenceType) targetType).getReferredType(), + unresolvedTypes); + default -> TypeChecker.checkIsRecursiveType(sourceType, targetType, + unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); + }; + } + + static boolean checkIsType(Object sourceVal, BType sourceBType, BType targetBType, + List unresolvedTypes) { + Type sourceType = getImpliedType(sourceBType); + Type targetType = getImpliedType(targetBType); + + 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 TypeChecker.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 -> TypeChecker.checkIsAnyType(sourceType); + case TypeTags.READONLY_TAG -> TypeChecker.isInherentlyImmutableType(sourceType) || sourceType.isReadOnly(); + default -> TypeChecker.checkIsRecursiveTypeOnValue(sourceVal, sourceType, targetType, sourceTypeTag, + targetTypeTag, unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); + }; + } +} 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 ef6ce2b8b65a..da6e59d69e36 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 @@ -18,7 +18,6 @@ package io.ballerina.runtime.internal; import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.ArrayType.ArrayState; @@ -26,6 +25,9 @@ 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.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.Context; +import io.ballerina.runtime.api.types.SemType.Core; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.types.XmlNodeType; @@ -141,6 +143,7 @@ public final class TypeChecker { private static final byte MAX_TYPECAST_ERROR_COUNT = 20; private static final String REG_EXP_TYPENAME = "RegExp"; + private static final Context cx = new Context(); public static Object checkCast(Object sourceVal, Type targetType) { @@ -279,7 +282,8 @@ 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); + return Core.isSubType(cx, Builder.from(getType(sourceVal)), Builder.from(targetType), + (sourceTy, targetTy) -> FallbackTypeChecker.checkIsType(null, sourceVal, sourceTy, targetTy)); } /** @@ -292,22 +296,8 @@ 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); - } - } - - if (isMutable(sourceVal, sourceType)) { - return false; - } - - return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); + return Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + (sourceTy, targetTy) -> FallbackTypeChecker.checkIsType(errors, sourceVal, sourceTy, targetTy)); } /** @@ -330,8 +320,8 @@ 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); + return checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), + allowNumericConversion, null); } /** @@ -560,147 +550,21 @@ 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 Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceBType, targetBType, null)); } @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); - }; + return Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceBType, targetBType, + 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); - }; + static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, List unresolvedTypes) { + return Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceVal, sourceBType, targetBType, + unresolvedTypes)); } // Private methods @@ -715,7 +579,7 @@ private static boolean checkTypeDescType(Type sourceType, BTypedescType targetTy return checkIsType(sourceTypedesc.getConstraint(), targetType.getConstraint(), unresolvedTypes); } - private static boolean checkIsRecursiveType(Type sourceType, Type targetType, List unresolvedTypes) { + 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); @@ -738,9 +602,9 @@ private static boolean checkIsRecursiveType(Type sourceType, Type targetType, Li }; } - private static boolean checkIsRecursiveTypeOnValue(Object sourceVal, Type sourceType, Type targetType, - int sourceTypeTag, int targetTypeTag, - List unresolvedTypes) { + 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) { @@ -768,7 +632,7 @@ private static boolean checkIsRecursiveTypeOnValue(Object sourceVal, Type source }; } - private static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { + static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { for (Object bValue : sourceType.valueSpace) { if (!checkIsType(bValue, targetType)) { return false; @@ -777,7 +641,7 @@ private static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType return true; } - private static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, List unresolvedTypes) { + static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, List unresolvedTypes) { for (Type type : sourceType.getMemberTypes()) { if (!checkIsType(type, targetType, unresolvedTypes)) { return false; @@ -1570,7 +1434,7 @@ private static boolean checkIsTupleType(Type sourceType, BTupleType targetType, return checkIsTupleType((BTupleType) sourceType, targetType, unresolvedTypes); } - private static boolean checkIsAnyType(Type sourceType) { + static boolean checkIsAnyType(Type sourceType) { sourceType = getImpliedType(sourceType); return switch (sourceType.getTag()) { case TypeTags.ERROR_TAG, @@ -1887,7 +1751,7 @@ private static boolean hasIncompatibleTransactionalFlags(FunctionType target, Fu .isFlagOn(target.getFlags(), SymbolFlags.TRANSACTIONAL); } - private static boolean checkIsServiceType(Type sourceType, Type targetType, List unresolvedTypes) { + static boolean checkIsServiceType(Type sourceType, Type targetType, List unresolvedTypes) { sourceType = getImpliedType(sourceType); if (sourceType.getTag() == TypeTags.SERVICE_TAG) { return checkObjectEquivalency(sourceType, (BObjectType) targetType, unresolvedTypes); @@ -2034,7 +1898,7 @@ private static boolean checkConstraints(Type sourceConstraint, Type targetConstr return checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); } - private static boolean isMutable(Object value, Type sourceType) { + static boolean isMutable(Object value, Type sourceType) { // All the value types are immutable sourceType = getImpliedType(sourceType); if (value == null || sourceType.getTag() < TypeTags.NULL_TAG || @@ -2045,7 +1909,7 @@ private static boolean isMutable(Object value, Type sourceType) { return !((BRefValue) value).isFrozen(); } - private static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(Type type) { + static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(Type type) { Set visitedTypeSet = new HashSet<>(); return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(type, visitedTypeSet); } @@ -2113,15 +1977,15 @@ private static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(T * @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<>())) { + List unresolvedValues, boolean allowNumericConversion, + String varName) { + Type sourceType = TypeChecker.getType(sourceValue); + if (TypeChecker.checkIsType(sourceType, targetType, new ArrayList<>())) { return true; } - return checkIsLikeOnValue(errors, sourceValue, sourceType, targetType, unresolvedValues, allowNumericConversion, - varName); + return TypeChecker.checkIsLikeOnValue(errors, sourceValue, sourceType, targetType, unresolvedValues, + allowNumericConversion, varName); } /** @@ -2137,9 +2001,9 @@ private static boolean checkIsLikeType(List errors, Object sourceValue, * @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) { + 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(); @@ -3004,7 +2868,7 @@ static boolean isStructuredType(Type type) { * * @since 0.995.0 */ - private static class TypePair { + static class TypePair { Type sourceType; Type targetType; 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 5d2aba9ddc8f..2412e3eb44a1 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 @@ -21,11 +21,15 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.SubTypeData; import java.util.Objects; +import java.util.function.Supplier; /** * {@code BType} represents a type in Ballerina. @@ -37,13 +41,14 @@ * * @since 0.995.0 */ -public abstract class BType implements Type { +public abstract class BType implements Type, SubTypeData, Supplier { protected String typeName; protected Module pkg; protected Class valueClass; private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; + private SemType cachedSemType = null; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -231,4 +236,12 @@ public void setCachedImpliedType(Type type) { public Type getCachedImpliedType() { return this.cachedImpliedType; } + + @Override + public SemType get() { + if (cachedSemType == null) { + cachedSemType = Builder.from(this); + } + return cachedSemType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java new file mode 100644 index 000000000000..d7e95aa12494 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * 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 2201.10.0 + */ +public enum AllOrNothing implements SubTypeData { + ALL, + NOTHING +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java new file mode 100644 index 000000000000..4949d392c9e1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java @@ -0,0 +1,129 @@ +/* + * 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.Module; +import io.ballerina.runtime.api.types.BasicTypeBitSet; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.Type; + +import java.util.HashMap; +import java.util.Map; + +public final class BBasicTypeBitSet implements BasicTypeBitSet { + + private final int all; + private final BTypeAdapter adapter; + + private final static Map cache = new HashMap<>(); + + private BBasicTypeBitSet(int all) { + this.all = all; + this.adapter = new BTypeAdapter(this); + } + + public static BasicTypeBitSet from(int all) { + return cache.computeIfAbsent(all, BBasicTypeBitSet::new); + } + + @Override + public V getZeroValue() { + return adapter.getZeroValue(); + } + + @Override + public V getEmptyValue() { + return adapter.getEmptyValue(); + } + + @Override + public int getTag() { + return adapter.getTag(); + } + + @Override + public boolean isNilable() { + return adapter.isNilable(); + } + + @Override + public String getName() { + return adapter.getName(); + } + + @Override + public String getQualifiedName() { + return adapter.getQualifiedName(); + } + + @Override + public Module getPackage() { + return adapter.getPackage(); + } + + @Override + public boolean isPublic() { + return adapter.isPublic(); + } + + @Override + public boolean isNative() { + return adapter.isNative(); + } + + @Override + public boolean isAnydata() { + return adapter.isAnydata(); + } + + @Override + public boolean isPureType() { + return adapter.isPureType(); + } + + @Override + public boolean isReadOnly() { + return adapter.isReadOnly(); + } + + @Override + public long getFlags() { + return adapter.getFlags(); + } + + @Override + public Type getImmutableType() { + return adapter.getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + adapter.setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return adapter.getPkg(); + } + + @Override + public int all() { + return all; + } +} 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..725717e1d7dd --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java @@ -0,0 +1,160 @@ +/* + * 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; + +public final class BBooleanSubType implements SubType { + + private final BBooleanSubTypeData data; + private final static BBooleanSubType ALL = new BBooleanSubType(BBooleanSubTypeData.ALL); + private final static BBooleanSubType NOTHING = new BBooleanSubType(BBooleanSubTypeData.NOTHING); + private final static BBooleanSubType TRUE = new BBooleanSubType(BBooleanSubTypeData.TRUE); + private final static BBooleanSubType FALSE = new BBooleanSubType(BBooleanSubTypeData.FALSE); + + private BBooleanSubType(BBooleanSubTypeData data) { + 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()) { + return this; + } + if (other.isAll()) { + return other; + } + 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()) { + if (other.isNothing()) { + return this; + } + 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() { + return data.isNothing(); + } + + @Override + public boolean isAll() { + return data.isAll(); + } + + @Override + public boolean isNothing() { + return data.isNothing(); + } + + @Override + public SubTypeData data() { + return data.toData(); + } + + private record BBooleanSubTypeData(boolean isAll, boolean isNothing, boolean value) { + + static final BBooleanSubTypeData ALL = new BBooleanSubTypeData(true, false, false); + static final BBooleanSubTypeData NOTHING = new BBooleanSubTypeData(false, true, false); + static final BBooleanSubTypeData TRUE = new BBooleanSubTypeData(false, false, true); + static final BBooleanSubTypeData FALSE = new BBooleanSubTypeData(false, false, false); + + static BBooleanSubTypeData from(boolean value) { + return value ? TRUE : 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/BDecimalSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java new file mode 100644 index 000000000000..bcc71a04d80b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java @@ -0,0 +1,176 @@ +/* + * 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.SubType; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of DecimalSubType. + * + * @since 2201.10.0 + */ +public final class BDecimalSubType implements SubType { + + final SubTypeData data; + + private BDecimalSubType(SubTypeData data) { + 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() { + return data == AllOrNothing.NOTHING; + } + + @Override + public boolean isAll() { + return data == AllOrNothing.ALL; + } + + @Override + public boolean isNothing() { + 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 + boolean allowed() { + return allowed; + } + + @Override + BigDecimal[] values() { + return values; + } + } +} 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..d1144438d313 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java @@ -0,0 +1,153 @@ +/* + * 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.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of FloatSubType. + * + * @since 2201.10.0 + */ +public final class BFloatSubType implements SubType { + + final SubTypeData data; + + private BFloatSubType(SubTypeData data) { + 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; + } + } + Arrays.sort(values); + 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() { + return data == AllOrNothing.NOTHING; + } + + @Override + public boolean isAll() { + return data == AllOrNothing.ALL; + } + + @Override + public boolean isNothing() { + 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 = values; + } + + @Override + boolean allowed() { + return allowed; + } + + @Override + Double[] values() { + return values; + } + } +} 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..6fe4d6ac96ba --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java @@ -0,0 +1,302 @@ +/* + * 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.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 IntSubType. + * + * @since 2201.10.0 + */ +public final class BIntSubType implements SubType { + + final SubTypeData data; + + private BIntSubType(SubTypeData data) { + 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(Range[] ranges) { + 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() { + return data == AllOrNothing.NOTHING; + } + + @Override + public boolean isAll() { + return data == AllOrNothing.ALL; + } + + @Override + public boolean isNothing() { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + record Range(long min, long max) { + + } + + static final class IntSubTypeData implements SubTypeData { + + private final Range[] ranges; + + private IntSubTypeData(Range[] ranges) { + this.ranges = ranges; + } + + 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/BSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java new file mode 100644 index 000000000000..1f63945a375d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.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.Module; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.Type; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class BSemType implements SemType { + + private final int all; + private final int some; + // TODO: subTypeData is a sparse list to make iteration simple, when we have an efficient implementation fix this + private final List subTypeData; + private final BTypeAdapter adapter; + + private BSemType(int all, int some, List subTypeData) { + this.all = all; + this.some = some; + this.subTypeData = toSparseList(some, subTypeData); + adapter = new BTypeAdapter(this); + } + + private static List toSparseList(int some, List subTypeData) { + if (some == 0) { + return List.of(); + } + List sparse = new ArrayList<>(subTypeData.size()); + int index = 0; + int code = 0; + while (index < subTypeData.size()) { + if ((some & (1 << code)) != 0) { + sparse.add(subTypeData.get(index)); + index++; + } else { + sparse.add(null); + } + code++; + } + return sparse; + } + + public static SemType from(int all, int some, List subTypeData) { + if (some == 0) { + return BBasicTypeBitSet.from(all); + } + return new BSemType(all, some, subTypeData); + } + + @Override + public V getZeroValue() { + return adapter.getZeroValue(); + } + + @Override + public V getEmptyValue() { + return adapter.getEmptyValue(); + } + + @Override + public int getTag() { + return adapter.getTag(); + } + + @Override + public boolean isNilable() { + return adapter.isNilable(); + } + + @Override + public String getName() { + return adapter.getName(); + } + + @Override + public String getQualifiedName() { + return adapter.getQualifiedName(); + } + + @Override + public Module getPackage() { + return adapter.getPackage(); + } + + @Override + public boolean isPublic() { + return adapter.isPublic(); + } + + @Override + public boolean isNative() { + return adapter.isNative(); + } + + @Override + public boolean isAnydata() { + return adapter.isAnydata(); + } + + @Override + public boolean isPureType() { + return adapter.isPureType(); + } + + @Override + public boolean isReadOnly() { + return adapter.isReadOnly(); + } + + @Override + public long getFlags() { + return adapter.getFlags(); + } + + @Override + public Type getImmutableType() { + return adapter.getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + adapter.setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return adapter.getPkg(); + } + + @Override + public int all() { + return all; + } + + @Override + public int some() { + return some; + } + + @Override + public List subTypeData() { + return Collections.unmodifiableList(subTypeData); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java new file mode 100644 index 000000000000..3f2cf1e04a05 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java @@ -0,0 +1,143 @@ +/* + * 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.Module; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.Type; + +import java.util.List; + +public final class BSemTypeWithIdentity implements SemType { + + private final SemType ty; + final TypeMetadata metadata; + private final BTypeAdapter adapter; + + private BSemTypeWithIdentity(SemType ty, TypeMetadata metadata) { + this.ty = ty; + this.metadata = metadata; + adapter = new BTypeAdapter(this); + } + + public static BSemTypeWithIdentity from(int all, int some, List subTypeData, TypeMetadata metadata) { + return new BSemTypeWithIdentity(BSemType.from(all, some, subTypeData), metadata); + } + + public static BSemTypeWithIdentity from(int all, int some, List subTypeData) { + return new BSemTypeWithIdentity(BSemType.from(all, some, subTypeData), TypeMetadata.empty()); + } + + @Override + public V getZeroValue() { + return adapter.getZeroValue(); + } + + @Override + public V getEmptyValue() { + return adapter.getEmptyValue(); + } + + @Override + public int getTag() { + return adapter.getTag(); + } + + @Override + public boolean isNilable() { + return adapter.isNilable(); + } + + @Override + public String getName() { + return adapter.getName(); + } + + @Override + public String getQualifiedName() { + return adapter.getQualifiedName(); + } + + @Override + public Module getPackage() { + return adapter.getPackage(); + } + + @Override + public boolean isPublic() { + return adapter.isPublic(); + } + + @Override + public boolean isNative() { + return adapter.isNative(); + } + + @Override + public boolean isAnydata() { + return adapter.isAnydata(); + } + + @Override + public boolean isPureType() { + return adapter.isPureType(); + } + + @Override + public boolean isReadOnly() { + return adapter.isReadOnly(); + } + + @Override + public long getFlags() { + return adapter.getFlags(); + } + + @Override + public Type getImmutableType() { + return adapter.getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + adapter.setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return adapter.getPkg(); + } + + @Override + public int all() { + return ty.all(); + } + + @Override + public int some() { + return ty.some(); + } + + @Override + public List subTypeData() { + return ty.subTypeData(); + } +} 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..df8be56cd870 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java @@ -0,0 +1,199 @@ +/* + * 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.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of StringSubType. + * + * @since 2201.10.0 + */ +public final class BStringSubType implements 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) { + 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() { + return data == AllOrNothing.NOTHING; + } + + @Override + public boolean isAll() { + return data == AllOrNothing.ALL; + } + + @Override + public boolean isNothing() { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + private record StringSubTypeData(ValueData chars, ValueData nonChars) implements SubTypeData { + + } + + 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 + boolean allowed() { + return allowed; + } + + @Override + String[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java new file mode 100644 index 000000000000..2a0c06d7629e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java @@ -0,0 +1,76 @@ +/* + * 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; +import io.ballerina.runtime.internal.types.BType; + +public class BSubType implements SubType { + + private final BType data; + + private BSubType(BType innerType) { + data = innerType; + } + + public static BSubType wrap(BType innerType) { + return new BSubType(innerType); + } + + @Override + public SubType union(SubType other) { + throw new IllegalArgumentException("BSubType don't support semType operations"); + } + + @Override + public SubType intersect(SubType other) { + throw new IllegalArgumentException("BSubType don't support semType operations"); + } + + @Override + public SubType diff(SubType other) { + throw new IllegalArgumentException("BSubType don't support semType operations"); + } + + @Override + public SubType complement() { + throw new IllegalArgumentException("BSubType don't support semType operations"); + } + + @Override + public boolean isEmpty() { + throw new IllegalArgumentException("BSubType don't support semType operations"); + } + + // NOTE: we are allowing isAll() and isNothing() so we can get the union of PureSemTypes and PureBTypes + @Override + public boolean isAll() { + return false; + } + + @Override + public boolean isNothing() { + return false; + } + + @Override + public SubTypeData data() { + return data; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubTypeWrapper.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java new file mode 100644 index 000000000000..68697d33b1e2 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java @@ -0,0 +1,121 @@ +/* + * 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.Module; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.Type; + +// All the logic for supporting various Type operations on SemTypes is defined here +final class BTypeAdapter implements Type { + + private final SemType semType; + + BTypeAdapter(SemType semType) { + this.semType = semType; + } + + @Override + public V getZeroValue() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public V getEmptyValue() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public int getTag() { + // TODO: cache this + throw new IllegalStateException("unimplemented"); + } + + @Override + public boolean isNilable() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public String getName() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public String getQualifiedName() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public Module getPackage() { + if (semType instanceof BSemTypeWithIdentity ty) { + return ty.metadata.pkg; + } + throw new IllegalStateException("semtype without identity"); + } + + @Override + public boolean isPublic() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public boolean isNative() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public boolean isAnydata() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public boolean isPureType() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public boolean isReadOnly() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public long getFlags() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public Type getImmutableType() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + throw new IllegalArgumentException("SemTypes are unmodifiable"); + } + + @Override + public Module getPkg() { + if (semType instanceof BSemTypeWithIdentity ty) { + return ty.metadata.pkg; + } + throw new IllegalStateException("semtype without identity"); + } +} 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..d3b6a2b7b42b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java @@ -0,0 +1,163 @@ +/* + * + * 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.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.10.0 + */ +abstract class EnumerableSubtypeData> { + + abstract boolean allowed(); + + abstract E[] values(); + + 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/SubTypeData.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java new file mode 100644 index 000000000000..55bb8d32a94c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * Marker interface for SubTypeData. + * + * @since 2201.10.0 + */ +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..8e788c2299c1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java @@ -0,0 +1,25 @@ +/* + * 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; + +public record SubtypePair(int typeCode, SubType subType1, SubType subType2) { + +} 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..16409b085bf2 --- /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.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.Iterator; + +public final class SubtypePairIterator implements Iterator { + + private int index = 0; + private final int maxIndex; + private final int bits; + private final SemType t1; + private final SemType t2; + + SubtypePairIterator(SemType t1, SemType t2, int bits) { + maxIndex = Integer.max(t1.subTypeData().size(), t2.subTypeData().size()); + this.bits = bits; + this.t1 = t1; + this.t2 = t2; + incrementIndex(); + } + + @Override + public boolean hasNext() { + return index < maxIndex; + } + + private void incrementIndex() { + while (index < maxIndex && (bits & (1 << index)) == 0) { + index++; + } + } + + private SubType subTypeAtIndex(SemType t, int index) { + if ((t.some() & (1 << index)) != 0) { + return t.subTypeData().get(index); + } + return null; + } + + @Override + public SubtypePair next() { + SubType subType1 = subTypeAtIndex(t1, index); + SubType subType2 = subTypeAtIndex(t2, index); + 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..ab2303ddb757 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java @@ -0,0 +1,41 @@ +/* + * 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; + +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/TypeMetadata.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.java new file mode 100644 index 000000000000..b433116404ae --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.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.Module; + +public final class TypeMetadata { + + protected Module pkg = null; + + private TypeMetadata() { + } + + public static TypeMetadata empty() { + return new TypeMetadata(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index de8f45222601..cdf38ae7701f 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -67,10 +67,11 @@ io.ballerina.lang.xml, org.ballerinalang.debugadapter.runtime, io.ballerina.lang.query, io.ballerina.lang.function, io.ballerina.lang.regexp, io.ballerina.lang.value, io.ballerina.lang.internal, io.ballerina.lang.array; exports io.ballerina.runtime.internal.configurable to io.ballerina.lang.internal; - exports io.ballerina.runtime.internal.types to io.ballerina.lang.typedesc, io.ballerina.testerina.runtime, - org.ballerinalang.debugadapter.runtime, io.ballerina.lang.function, io.ballerina.lang.regexp, io.ballerina.testerina.core; exports io.ballerina.runtime.observability.metrics.noop; exports io.ballerina.runtime.observability.tracer.noop; exports io.ballerina.runtime.internal.regexp; exports io.ballerina.runtime.internal.configurable.providers to org.ballerinalang.debugadapter.runtime; + exports io.ballerina.runtime.internal.types; + exports io.ballerina.runtime.internal.types.semtype; + exports io.ballerina.runtime.api.types.SemType; } diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java new file mode 100644 index 000000000000..0da6bbe5fafc --- /dev/null +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java @@ -0,0 +1,95 @@ +/* + * 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.internal.types.semtype; + +import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class BBooleanSubTypeTests { + + private static final BBooleanSubType TRUE = BBooleanSubType.from(true); + private static final BBooleanSubType FALSE = BBooleanSubType.from(false); + + @Test + public static void testSimpleUnion() { + SubType res = TRUE.union(FALSE); + Assert.assertTrue(res.isAll()); + Assert.assertFalse(res.isNothing()); + Assert.assertFalse(res.isNothing()); + + res = FALSE.union(TRUE); + Assert.assertTrue(res.isAll()); + Assert.assertFalse(res.isNothing()); + Assert.assertFalse(res.isNothing()); + } + + @Test + public static void testSimpleIntersection() { + SubType res = TRUE.intersect(FALSE); + Assert.assertFalse(res.isAll()); + Assert.assertTrue(res.isEmpty()); + Assert.assertTrue(res.isNothing()); + + res = FALSE.intersect(TRUE); + Assert.assertFalse(res.isAll()); + Assert.assertTrue(res.isEmpty()); + Assert.assertTrue(res.isNothing()); + + res = TRUE.intersect(TRUE); + Assert.assertFalse(res.isAll()); + Assert.assertFalse(res.isEmpty()); + Assert.assertFalse(res.isNothing()); + } + + @Test + public static void testSimpleDiff() { + SubType res = TRUE.diff(FALSE); + Assert.assertFalse(res.isAll()); + Assert.assertFalse(res.isEmpty()); + Assert.assertFalse(res.isNothing()); + + res = TRUE.diff(TRUE); + Assert.assertFalse(res.isAll()); + Assert.assertTrue(res.isEmpty()); + Assert.assertTrue(res.isNothing()); + + SubType all = TRUE.union(FALSE); + res = all.diff(TRUE); + Assert.assertFalse(res.isAll()); + Assert.assertFalse(res.isEmpty()); + Assert.assertFalse(res.isNothing()); + } + + @Test + public static void testSimpleComplement() { + SubType all = TRUE.union(FALSE); + SubType nothing = all.complement(); + Assert.assertTrue(nothing.isNothing()); + + SubType res = TRUE.complement(); + Assert.assertFalse(res.isAll()); + Assert.assertFalse(res.isEmpty()); + Assert.assertFalse(res.isNothing()); + + SubType otherNothing = TRUE.intersect(FALSE); + Assert.assertTrue(otherNothing == nothing); // Boolean subtype is interned + } +} diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java new file mode 100644 index 000000000000..f14bec456681 --- /dev/null +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java @@ -0,0 +1,61 @@ +/* + * 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.internal.types.semtype; + +import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.math.BigDecimal; + +import static org.testng.Assert.*; + +public class BDecimalSubTypeTest { + + @Test + public void testSimpleUnion() { + BigDecimal[] values = {BigDecimal.valueOf(1), BigDecimal.valueOf(2)}; + BDecimalSubType t1 = BDecimalSubType.createDecimalSubType(true, values); + BDecimalSubType t2 = BDecimalSubType.createDecimalSubType(false, values); + + SubType res = t1.union(t2); + Assert.assertTrue(res.isAll()); + } + + @Test + public void testSimpleIntersect() { + BigDecimal[] values = {BigDecimal.valueOf(1), BigDecimal.valueOf(2)}; + BDecimalSubType t1 = BDecimalSubType.createDecimalSubType(true, values); + BDecimalSubType t2 = BDecimalSubType.createDecimalSubType(false, values); + + SubType res = t1.intersect(t2); + Assert.assertTrue(res.isNothing()); + } + + @Test + public void testComplement() { + BigDecimal[] values = {BigDecimal.valueOf(1), BigDecimal.valueOf(2)}; + BDecimalSubType t1 = BDecimalSubType.createDecimalSubType(true, values); + BDecimalSubType t2 = BDecimalSubType.createDecimalSubType(false, values); + SubType all = t1.union(t2); + SubType res = all.complement(); + Assert.assertTrue(res.isNothing()); + } +} \ No newline at end of file diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java new file mode 100644 index 000000000000..c995e60141f9 --- /dev/null +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java @@ -0,0 +1,121 @@ +/* + * 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.internal.types.semtype; + +import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.types.BasicTypeBitSet; +import io.ballerina.runtime.api.types.SemType.BasicTypeCode; +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.BBasicTypeBitSet; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class CoreTests { + + private final Context cx = new Context(); + + @Test + public static void testSimpleUnion() { + BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1); + BasicTypeBitSet t2 = BBasicTypeBitSet.from(1 << 2); + BasicTypeBitSet result = (BasicTypeBitSet) Core.union(t1, t2); + Assert.assertTrue(isBasicTypeSame(result, BBasicTypeBitSet.from(1 << 1 | 1 << 2))); + } + + @Test + public static void testSimpleUnionWithNever() { + BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1); + BasicTypeBitSet t2 = BBasicTypeBitSet.from(0); + BasicTypeBitSet result = (BasicTypeBitSet) Core.union(t1, t2); + Assert.assertTrue(isBasicTypeSame(result, t1)); + } + + @Test + public static void testSimpleDiff() { + BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1 | 1 << 2); + BasicTypeBitSet t2 = BBasicTypeBitSet.from(1 << 2); + BasicTypeBitSet res = (BasicTypeBitSet) Core.diff(t1, t2); + Assert.assertTrue(isBasicTypeSame(res, BBasicTypeBitSet.from(1 << 1))); + + BasicTypeBitSet res2 = (BasicTypeBitSet) Core.diff(t2, t1); + Assert.assertTrue(isBasicTypeSame(res2, BBasicTypeBitSet.from(0))); + } + + @Test + public static void testSimpleIntersection() { + BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1 | 1 << 2); + BasicTypeBitSet t2 = BBasicTypeBitSet.from(1 << 2); + BasicTypeBitSet res = (BasicTypeBitSet) Core.intersect(t1, t2); + Assert.assertTrue(isBasicTypeSame(res, t2)); + + BasicTypeBitSet t3 = BBasicTypeBitSet.from(0); + BasicTypeBitSet res2 = (BasicTypeBitSet) Core.intersect(t1, t3); + Assert.assertTrue(isBasicTypeSame(res2, t3)); + } + + @Test + public void testSimpleSubType() { + SemType intSingleton1 = Builder.intConst(1); + SemType intTop = Builder.from(BasicTypeCode.BT_INT); + Assert.assertTrue(Core.isSubType(cx, intSingleton1, intTop)); + Assert.assertFalse(Core.isSubType(cx, intTop, intSingleton1)); + + SemType intSingleton2 = Builder.intConst(2); + SemType intUnion = Core.union(intSingleton1, intSingleton2); + Assert.assertTrue(Core.isSubType(cx, intSingleton1, intUnion)); + Assert.assertTrue(Core.isSubType(cx, intSingleton2, intUnion)); + Assert.assertTrue(Core.isSubType(cx, intUnion, intTop)); + } + + @Test + public void testBTypeSubType() { + SemType booleanTy = Builder.from(PredefinedTypes.TYPE_BOOLEAN); + SemType anyTy = Builder.from(PredefinedTypes.TYPE_ANY); + Assert.assertTrue(Core.isSubType(cx, booleanTy, anyTy, (t1, t2) -> TypeChecker.checkIsType(t1, t2))); + } + + @Test + public void testMixSubType() { + SemType intSingleton1 = Builder.intConst(1); + SemType BooleanBType = Builder.from(PredefinedTypes.TYPE_BOOLEAN); + SemType T1 = Core.union(intSingleton1, BooleanBType); // 1(semType) | boolean (BType) + + SemType intType = Builder.from(BasicTypeCode.BT_INT); + SemType T2 = Core.union(intType, BooleanBType); // int(semType) | boolean (BType) + + Assert.assertTrue(Core.isSubType(cx, T1, T2, (t1, t2) -> TypeChecker.checkIsType(t1, t2))); + } + + @Test + public void testSimpleTypeArithmetic() { + SemType int1 = Builder.intConst(1); + SemType int2 = Builder.intConst(2); + SemType int12 = Core.union(int1, int2); + SemType int1New = Core.diff(int12, int2); + Assert.assertTrue(Core.isSameType(cx, int1New, int1)); + } + + private static boolean isBasicTypeSame(BasicTypeBitSet t1, BasicTypeBitSet t2) { + return t1.all() == t2.all(); + } +} From 62515336728c492374ff0c33ab9c603f6bdd1bc8 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 9 May 2024 10:09:12 +0530 Subject: [PATCH 002/178] Port never type --- .../runtime/api/types/SemType/Builder.java | 8 +- .../runtime/api/types/SemType/Core.java | 22 +++- .../internal/types/BTypeConverter.java | 105 ++++++++++++++++++ .../runtime/internal/types/BUnionType.java | 21 ++++ 4 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java 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 index 9b5b2aed8513..516ab681ee39 100644 --- 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 @@ -20,12 +20,12 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.internal.types.BTypeConverter; import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; import io.ballerina.runtime.internal.types.semtype.BFloatSubType; import io.ballerina.runtime.internal.types.semtype.BIntSubType; import io.ballerina.runtime.internal.types.semtype.BSemType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; -import io.ballerina.runtime.internal.types.semtype.BSubType; import java.util.ArrayList; import java.util.List; @@ -49,7 +49,11 @@ public static SemType from(Type type) { } public static SemType from(BType innerType) { - return basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(innerType)); + return BTypeConverter.from(innerType); + } + + public static SemType neverType() { + return basicTypeUnion(0); } public static SemType basicTypeUnion(int bitset) { 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 index 26e801a2d5b2..f3b9b23a0d35 100644 --- 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 @@ -38,6 +38,7 @@ public final class Core { private static final SemType SEMTYPE_TOP = BBasicTypeBitSet.from((1 << (CODE_UNDEF + 1)) - 1); + private static final BasicTypeBitSet B_TYPE_TOP = BBasicTypeBitSet.from(1 << BT_B_TYPE.code()); private Core() { } @@ -225,7 +226,7 @@ public static SemType complement(SemType t1) { } public static boolean isNever(SemType t) { - throw new IllegalStateException("Unimplemented"); + return t.all() == 0 && t.some() == 0; } public static boolean isSubType(Context cx, SemType t1, SemType t2) { @@ -242,9 +243,14 @@ public static boolean isSubType(Context cx, SemType t1, SemType t2, private static boolean applyFallback(SemType t1, SemType t2, BiFunction fallback) { - BType bType1 = (BType) subTypeData(t1, BT_B_TYPE); - BType bType2 = (BType) subTypeData(t2, BT_B_TYPE); - return fallback.apply(bType1, bType2); + boolean t1HasBType = containsBasicType(t1, B_TYPE_TOP); + boolean t2HasBType = containsBasicType(t2, B_TYPE_TOP); + if (t1HasBType && t2HasBType) { + BType bType1 = (BType) subTypeData(t1, BT_B_TYPE); + BType bType2 = (BType) subTypeData(t2, BT_B_TYPE); + return fallback.apply(bType1, bType2); + } + return !t1HasBType; } private static SubTypeData subTypeData(SemType s, BasicTypeCode code) { @@ -257,8 +263,14 @@ private static SubTypeData subTypeData(SemType s, BasicTypeCode code) { return s.subTypeData().get(code.code()).data(); } + private static boolean containsBasicType(SemType t1, BasicTypeBitSet t2) { + int bits = t1.all() | t1.some(); + return (bits & t2.all()) != 0; + } + public static boolean isSubTypeSimple(SemType t1, BasicTypeBitSet t2) { - throw new IllegalStateException("Unimplemented"); + int bits = t1.all() | t1.some(); + return (bits & ~t2.all()) == 0; } public static boolean isSameType(Context cx, SemType t1, SemType t2) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java new file mode 100644 index 000000000000..99815087c5e5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -0,0 +1,105 @@ +/* + * 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.flags.SymbolFlags; +import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.SemType.BasicTypeCode; +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.Type; +import io.ballerina.runtime.internal.types.semtype.BSubType; + +import java.util.ArrayList; +import java.util.List; + +// NOTE: this is so that we don't have to expose any utility constructors as public to builder +public final class BTypeConverter { + + private BTypeConverter() { + } + + private static SemType from(Type type) { + if (type instanceof SemType semType) { + return semType; + } else if (type instanceof BType bType) { + return from(bType); + } + throw new IllegalArgumentException("Unsupported type: " + type); + } + + public static SemType from(BType innerType) { + if (innerType instanceof BNeverType) { + return Builder.neverType(); + } else if (innerType instanceof BUnionType unionType) { + return fromUnionType(unionType); + } else if (innerType instanceof BRecordType recordType) { + return fromRecordType(recordType); + } else if (innerType instanceof BTupleType tupleType) { + return fromTupleType(tupleType); + } + return wrapAsPureBType(innerType); + } + + private static SemType fromTupleType(BTupleType tupleType) { + for (Type type : tupleType.getTupleTypes()) { + if (Core.isNever(from(type))) { + return Builder.neverType(); + } + } + return wrapAsPureBType(tupleType); + } + + private static SemType wrapAsPureBType(BType tupleType) { + return Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(tupleType)); + } + + private static SemType fromRecordType(BRecordType recordType) { + for (Field field : recordType.fields.values()) { + if (!SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL)) { + SemType fieldType = from(field.getFieldType()); + if (Core.isNever(fieldType)) { + return Builder.neverType(); + } + } + } + return wrapAsPureBType(recordType); + } + + private static SemType fromUnionType(BUnionType unionType) { + List members = unionType.getMemberTypes(); + List bTypeMembers = new ArrayList<>(members.size()); + SemType semTypePart = Builder.neverType(); + for (Type member : members) { + if (isSemType(member)) { + semTypePart = Core.union(from(member), semTypePart); + } else { + bTypeMembers.add(member); + } + } + BUnionType newUnionType = unionType.cloneWithMembers(bTypeMembers); + SemType bTypePart = Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(newUnionType)); + return Core.union(semTypePart, bTypePart); + } + + private static boolean isSemType(Type type) { + return type instanceof BNeverType; + } +} 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 3f957eac231a..fecd80e02406 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 @@ -135,6 +135,27 @@ protected BUnionType(String typeName, Module pkg, boolean readonly, Class newMembers) { + BUnionType newUnionType = new BUnionType(memberTypes, this.typeFlags, this.readonly, this.isCyclic); + newUnionType.isCyclic = isCyclic; + newUnionType.memberTypes = newMembers; + newUnionType.originalMemberTypes = newMembers; + newUnionType.nullable = nullable; + newUnionType.flags = flags; + newUnionType.typeFlags = typeFlags; + newUnionType.readonly = readonly; + newUnionType.immutableType = immutableType; + newUnionType.intersectionType = intersectionType; + newUnionType.cachedToString = cachedToString; + newUnionType.resolving = resolving; + newUnionType.resolvingReadonly = resolvingReadonly; + + newUnionType.typeName = typeName; + newUnionType.pkg = pkg; + + return newUnionType; + } + /** * Constructor used when defining union type defs where cyclic reference is possible. * From f78bd540707b19bbb254cd47be25e298c38b3b23 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 10 May 2024 09:48:14 +0530 Subject: [PATCH 003/178] Port nil type Avoid repeated type creation --- .../runtime/api/types/SemType/Builder.java | 7 +- .../api/types/SemType/SemTypeHelper.java | 57 +++++++++ .../runtime/internal/TypeChecker.java | 23 +++- .../runtime/internal/types/BAnyType.java | 6 + .../runtime/internal/types/BFiniteType.java | 18 +++ .../internal/types/BIntersectionType.java | 7 ++ .../runtime/internal/types/BNeverType.java | 7 ++ .../runtime/internal/types/BNullType.java | 7 ++ .../runtime/internal/types/BReadonlyType.java | 6 + .../runtime/internal/types/BRecordType.java | 5 + .../internal/types/BStructureType.java | 1 + .../runtime/internal/types/BTupleType.java | 6 + .../runtime/internal/types/BType.java | 13 +- .../internal/types/BTypeConverter.java | 119 ++++++++++++++---- .../internal/types/BTypeReferenceType.java | 7 ++ .../runtime/internal/types/BUnionType.java | 28 ++--- .../types/semtype/BBasicTypeBitSet.java | 6 + .../internal/types/semtype/BSemType.java | 6 + .../types/semtype/BSemTypeWithIdentity.java | 6 + 19 files changed, 280 insertions(+), 55 deletions(-) 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 index 516ab681ee39..f4dcb8241cec 100644 --- 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 @@ -20,7 +20,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.BType; -import io.ballerina.runtime.internal.types.BTypeConverter; import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; import io.ballerina.runtime.internal.types.semtype.BFloatSubType; import io.ballerina.runtime.internal.types.semtype.BIntSubType; @@ -49,13 +48,17 @@ public static SemType from(Type type) { } public static SemType from(BType innerType) { - return BTypeConverter.from(innerType); + return innerType.get(); } public static SemType neverType() { return basicTypeUnion(0); } + public static SemType nilType() { + return from(BasicTypeCode.BT_NIL); + } + public static SemType basicTypeUnion(int bitset) { return BBasicTypeBitSet.from(bitset); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java index 04636b56b040..3151c35aebd5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java @@ -18,9 +18,66 @@ package io.ballerina.runtime.api.types.SemType; +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_B_TYPE; +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_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; + public final class SemTypeHelper { private SemTypeHelper() { } + public static String stringRepr(SemType ty) { + return "all[" + bitSetRepr(ty.all()) + "] some [" + bitSetRepr(ty.some()) + "]"; + } + + private static String bitSetRepr(int bits) { + StringBuilder sb = new StringBuilder(); + appendBitSetRepr(sb, bits, CODE_NIL, "NIL"); + 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_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"); + appendBitSetRepr(sb, bits, CODE_B_TYPE, "B_TYPE"); + 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/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index da6e59d69e36..3d8b8069d62c 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 @@ -25,6 +25,7 @@ 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.ParameterizedType; import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.Context; import io.ballerina.runtime.api.types.SemType.Core; @@ -95,6 +96,8 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANY; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANYDATA; @@ -282,7 +285,7 @@ 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 Core.isSubType(cx, Builder.from(getType(sourceVal)), Builder.from(targetType), + return isSubType(getType(sourceVal), targetType, (sourceTy, targetTy) -> FallbackTypeChecker.checkIsType(null, sourceVal, sourceTy, targetTy)); } @@ -296,7 +299,7 @@ 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) { - return Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + return isSubType(sourceType, targetType, (sourceTy, targetTy) -> FallbackTypeChecker.checkIsType(errors, sourceVal, sourceTy, targetTy)); } @@ -550,23 +553,33 @@ 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 Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + return isSubType(sourceType, targetType, (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceBType, targetBType, null)); } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - return Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + return isSubType(sourceType, targetType, (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceBType, targetBType, unresolvedTypes)); } static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, List unresolvedTypes) { - return Core.isSubType(cx, Builder.from(sourceType), Builder.from(targetType), + return isSubType(sourceType, targetType, (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceVal, sourceBType, targetBType, unresolvedTypes)); } + private static boolean isSubType(Type t1, Type t2, BiFunction fallback) { + if (t1 instanceof ParameterizedType paramTy1) { + if (t2 instanceof ParameterizedType paramTy2) { + return isSubType(paramTy1.getParamValueType(), paramTy2.getParamValueType(), fallback); + } + return isSubType(paramTy1.getParamValueType(), t2, fallback); + } + return Core.isSubType(cx, Builder.from(t1), Builder.from(t2), fallback); + } + // Private methods private static boolean checkTypeDescType(Type sourceType, BTypedescType targetType, 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 6c9dfbbd046f..06cedc3be032 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 @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.AnyType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.values.RefValue; @@ -100,4 +101,9 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + SemType createSemType() { + return BTypeConverter.fromAnyType(this); + } } 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 c232100029f9..e138271e0520 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,6 +21,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.RefValue; @@ -57,6 +58,18 @@ public BFiniteType(String typeName, String originalName, Set values, int this.originalName = originalName; } + BFiniteType cloneWithValueSpace(Set valueSpace) { + BFiniteType newFiniteType = new BFiniteType(typeName, originalName, valueSpace, typeFlags); + newFiniteType.valueSpace = valueSpace; + newFiniteType.typeFlags = typeFlags; + newFiniteType.originalName = originalName; + + newFiniteType.typeName = typeName; + newFiniteType.pkg = pkg; + + return newFiniteType; + } + @Override public V getZeroValue() { if (valueSpace.stream().anyMatch(val -> val == null || TypeChecker.getType(val).isNilable())) { @@ -189,4 +202,9 @@ public boolean equals(Object o) { } return this.valueSpace.size() == that.valueSpace.size() && this.valueSpace.containsAll(that.valueSpace); } + + @Override + SemType createSemType() { + return BTypeConverter.fromFiniteType(this); + } } 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 c2112f4a4578..f91f1743adb3 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectableReferenceType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import java.util.ArrayList; @@ -215,4 +216,10 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + SemType createSemType() { + BType effectiveType = (BType) getEffectiveType(); + return effectiveType.createSemType(); + } } 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 9f0d4da9901b..288272ad388b 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,6 +21,8 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.NeverType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; /** * {@code BNeverType} represents the type of a {@code Never}. @@ -46,4 +48,9 @@ public boolean isAnydata() { public int getTag() { return TypeTags.NEVER_TAG; } + + @Override + SemType createSemType() { + return Builder.neverType(); + } } 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 702133228119..a1e0dc983b17 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,6 +20,8 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.NullType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; /** * {@code BNullType} represents the type of a {@code NullLiteral}. @@ -62,4 +64,9 @@ public boolean isNilable() { public boolean isReadOnly() { return true; } + + @Override + SemType createSemType() { + return Builder.nilType(); + } } 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 8fb84c638a1e..0d84acaea71a 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,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ReadonlyType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.internal.values.RefValue; /** @@ -57,4 +58,9 @@ public boolean isNilable() { public boolean isReadOnly() { return true; } + + @Override + SemType createSemType() { + return BTypeConverter.fromReadonly(this); + } } 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 27b9286de00e..c5d5bffd1797 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 @@ -27,6 +27,7 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.RecordType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BFunctionPointer; @@ -221,4 +222,8 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul return defaultValues; } + @Override + SemType createSemType() { + return BTypeConverter.fromRecordType(this); + } } 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..3b5575b8a353 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; + resetSemTypeCache(); } @Override 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 21fb42a59f92..cf499122dd45 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -299,4 +300,9 @@ public void setIntersectionType(IntersectionType intersectionType) { public String getAnnotationKey() { return Utils.decodeIdentifier(this.typeName); } + + @Override + SemType createSemType() { + return BTypeConverter.fromTupleType(this); + } } 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 2412e3eb44a1..2860a0596fbb 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 @@ -21,7 +21,6 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; @@ -48,7 +47,7 @@ public abstract class BType implements Type, SubTypeData, Supplier { private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; - private SemType cachedSemType = null; + SemType cachedSemType = null; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -237,10 +236,18 @@ public Type getCachedImpliedType() { return this.cachedImpliedType; } + void resetSemTypeCache() { + cachedSemType = null; + } + + SemType createSemType() { + return BTypeConverter.wrapAsPureBType(this); + } + @Override public SemType get() { if (cachedSemType == null) { - cachedSemType = Builder.from(this); + cachedSemType = createSemType(); } return cachedSemType; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 99815087c5e5..8bda29fdaeed 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -18,8 +18,10 @@ package io.ballerina.runtime.internal.types; +import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.SemType.BasicTypeCode; import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.Core; @@ -28,7 +30,10 @@ import io.ballerina.runtime.internal.types.semtype.BSubType; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; // NOTE: this is so that we don't have to expose any utility constructors as public to builder public final class BTypeConverter { @@ -45,20 +50,23 @@ private static SemType from(Type type) { throw new IllegalArgumentException("Unsupported type: " + type); } + // TODO: ideally this should be only called by BTypes (ie. no need for this to be public) and they should call + // the correct method (ie. no need for this instance of thing) public static SemType from(BType innerType) { - if (innerType instanceof BNeverType) { - return Builder.neverType(); - } else if (innerType instanceof BUnionType unionType) { - return fromUnionType(unionType); - } else if (innerType instanceof BRecordType recordType) { - return fromRecordType(recordType); - } else if (innerType instanceof BTupleType tupleType) { - return fromTupleType(tupleType); - } - return wrapAsPureBType(innerType); + return innerType.get(); + } + + static SemType fromReadonly(BReadonlyType readonlyType) { + SemType semTypePart = Builder.nilType(); + SemType bTypePart = wrapAsPureBType(readonlyType); + return Core.union(semTypePart, bTypePart); } - private static SemType fromTupleType(BTupleType tupleType) { + static SemType fromTypeReference(ReferenceType referenceType) { + return from(referenceType.getReferredType()); + } + + static SemType fromTupleType(BTupleType tupleType) { for (Type type : tupleType.getTupleTypes()) { if (Core.isNever(from(type))) { return Builder.neverType(); @@ -67,11 +75,17 @@ private static SemType fromTupleType(BTupleType tupleType) { return wrapAsPureBType(tupleType); } - private static SemType wrapAsPureBType(BType tupleType) { + static SemType wrapAsPureBType(BType tupleType) { return Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(tupleType)); } - private static SemType fromRecordType(BRecordType recordType) { + static SemType fromAnyType(BAnyType anyType) { + SemType semTypePart = Builder.nilType(); + SemType bTypePart = wrapAsPureBType(anyType); + return Core.union(semTypePart, bTypePart); + } + + static SemType fromRecordType(BRecordType recordType) { for (Field field : recordType.fields.values()) { if (!SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL)) { SemType fieldType = from(field.getFieldType()); @@ -83,23 +97,80 @@ private static SemType fromRecordType(BRecordType recordType) { return wrapAsPureBType(recordType); } - private static SemType fromUnionType(BUnionType unionType) { - List members = unionType.getMemberTypes(); - List bTypeMembers = new ArrayList<>(members.size()); + static SemType fromFiniteType(BFiniteType finiteType) { + BTypeParts parts = splitFiniteType(finiteType); + BType newFiniteType = (BType) parts.bTypeParts().get(0); + SemType bTypePart = wrapAsPureBType(newFiniteType); + return Core.union(parts.semTypePart(), bTypePart); + } + + static SemType fromUnionType(BUnionType unionType) { + BTypeParts parts = splitUnion(unionType); + SemType bTypePart = Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(unionType)); + return Core.union(parts.semTypePart(), bTypePart); + } + + private record BTypeParts(SemType semTypePart, List bTypeParts) { + + } + + private static BTypeParts split(Type type) { + if (isSemType(type)) { + return new BTypeParts(from(type), Collections.emptyList()); + } else if (type instanceof BUnionType unionType) { + return splitUnion(unionType); + } else if (type instanceof BAnyType) { + return new BTypeParts(Builder.nilType(), List.of(type)); + } else if (type instanceof BTypeReferenceType referenceType) { + return split(referenceType.getReferredType()); + } else if (type instanceof BIntersectionType intersectionType) { + return split(intersectionType.getEffectiveType()); + } else if (type instanceof BReadonlyType readonlyType) { + return splitReadonly(readonlyType); + } else if (type instanceof BFiniteType finiteType) { + return splitFiniteType(finiteType); + } else { + return new BTypeParts(Builder.neverType(), List.of(type)); + } + } + + private static BTypeParts splitFiniteType(BFiniteType finiteType) { + Set newValueSpace = new HashSet<>(finiteType.valueSpace.size()); SemType semTypePart = Builder.neverType(); - for (Type member : members) { - if (isSemType(member)) { - semTypePart = Core.union(from(member), semTypePart); + for (var each : finiteType.valueSpace) { + // TODO: lift this to Builder (Object) -> Type + if (each == null) { + semTypePart = Core.union(semTypePart, Builder.nilType()); } else { - bTypeMembers.add(member); + newValueSpace.add(each); } } - BUnionType newUnionType = unionType.cloneWithMembers(bTypeMembers); - SemType bTypePart = Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(newUnionType)); - return Core.union(semTypePart, bTypePart); + BFiniteType newFiniteType = finiteType.cloneWithValueSpace(newValueSpace); + return new BTypeParts(semTypePart, List.of(newFiniteType)); + } + + private static BTypeParts splitReadonly(BReadonlyType readonlyType) { + SemType semTypePart = Builder.nilType(); + // TODO: this is not exactly correct + return new BTypeParts(semTypePart, List.of(readonlyType)); + } + + private static BTypeParts splitUnion(BUnionType unionType) { + List members = Collections.unmodifiableList(unionType.getMemberTypes()); + List bTypeMembers = new ArrayList<>(members.size()); + SemType semTypePart = Builder.neverType(); + for (Type member : members) { + BTypeParts memberParts = split(member); + semTypePart = Core.union(memberParts.semTypePart(), semTypePart); + bTypeMembers.addAll(memberParts.bTypeParts()); + } + return new BTypeParts(semTypePart, Collections.unmodifiableList(bTypeMembers)); } private static boolean isSemType(Type type) { - return type instanceof BNeverType; + return switch (type.getTag()) { + case TypeTags.NEVER_TAG, TypeTags.NULL_TAG -> true; + default -> 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 4d228b2d78f6..75ee0e3b1a8e 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 @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectableReferenceType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import java.util.Objects; @@ -126,4 +127,10 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + SemType createSemType() { + BType referredType = (BType) getReferredType(); + return referredType.createSemType(); + } } 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 fecd80e02406..f6ef5ab9c331 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.TypeUtils; @@ -135,27 +136,6 @@ protected BUnionType(String typeName, Module pkg, boolean readonly, Class newMembers) { - BUnionType newUnionType = new BUnionType(memberTypes, this.typeFlags, this.readonly, this.isCyclic); - newUnionType.isCyclic = isCyclic; - newUnionType.memberTypes = newMembers; - newUnionType.originalMemberTypes = newMembers; - newUnionType.nullable = nullable; - newUnionType.flags = flags; - newUnionType.typeFlags = typeFlags; - newUnionType.readonly = readonly; - newUnionType.immutableType = immutableType; - newUnionType.intersectionType = intersectionType; - newUnionType.cachedToString = cachedToString; - newUnionType.resolving = resolving; - newUnionType.resolvingReadonly = resolvingReadonly; - - newUnionType.typeName = typeName; - newUnionType.pkg = pkg; - - return newUnionType; - } - /** * Constructor used when defining union type defs where cyclic reference is possible. * @@ -188,6 +168,7 @@ public void setMemberTypes(Type[] members) { } this.memberTypes = readonly ? getReadOnlyTypes(members) : Arrays.asList(members); setFlagsBasedOnMembers(); + resetSemTypeCache(); } public void setOriginalMemberTypes(Type[] originalMemberTypes) { @@ -561,4 +542,9 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + SemType createSemType() { + return BTypeConverter.fromUnionType(this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java index 4949d392c9e1..ff2bc03644f4 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.BasicTypeBitSet; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.SemTypeHelper; import io.ballerina.runtime.api.types.Type; import java.util.HashMap; @@ -126,4 +127,9 @@ public Module getPkg() { public int all() { return all; } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java index 1f63945a375d..b5b7e2db6fc0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SemTypeHelper; import io.ballerina.runtime.api.types.SemType.SubType; import io.ballerina.runtime.api.types.Type; @@ -163,4 +164,9 @@ public int some() { public List subTypeData() { return Collections.unmodifiableList(subTypeData); } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java index 3f2cf1e04a05..a0d9ca48277b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SemTypeHelper; import io.ballerina.runtime.api.types.SemType.SubType; import io.ballerina.runtime.api.types.Type; @@ -140,4 +141,9 @@ public int some() { public List subTypeData() { return ty.subTypeData(); } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } } From e0ba173af94bc17e8e327a60cc093721eabb3da3 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 10 May 2024 13:27:50 +0530 Subject: [PATCH 004/178] Port Decimal type --- .../runtime/api/PredefinedTypes.java | 2 +- .../runtime/api/types/SemType/Builder.java | 11 ++++++++ .../runtime/internal/types/BDecimalType.java | 20 +++++++++++++++ .../runtime/internal/types/BFloatType.java | 21 +++++++++++++++- .../internal/types/BTypeConverter.java | 25 ++++++++++++------- .../runtime/internal/values/DecimalValue.java | 7 ++++-- 6 files changed, 73 insertions(+), 13 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java index 3d0c7e49e7d5..21284059c752 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java @@ -89,7 +89,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/SemType/Builder.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Builder.java index f4dcb8241cec..11ff4e1bc176 100644 --- 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 @@ -21,11 +21,13 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; +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.BSemType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -59,6 +61,10 @@ public static SemType nilType() { return from(BasicTypeCode.BT_NIL); } + public static SemType decimalType() { + return from(BasicTypeCode.BT_DECIMAL); + } + public static SemType basicTypeUnion(int bitset) { return BBasicTypeBitSet.from(bitset); } @@ -73,6 +79,11 @@ public static SemType intConst(long value) { return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(values)); } + public static SemType decimalConst(BigDecimal value) { + BigDecimal[] values = {value}; + return basicSubType(BasicTypeCode.BT_DECIMAL, BDecimalSubType.createDecimalSubType(true, values)); + } + public static SemType floatConst(double value) { Double[] values = {value}; return basicSubType(BasicTypeCode.BT_FLOAT, BFloatSubType.createFloatSubType(true, values)); 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 a5af3b4d220a..cb2d046df3b2 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 @@ -20,11 +20,16 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.DecimalType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; +import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; + /** * {@code BDecimalType} represents decimal type in Ballerina. * This is a 128-bit decimal floating-point number according to the standard IEEE 754-2008 specifications. @@ -38,8 +43,18 @@ public class BDecimalType extends BType implements DecimalType { * * @param typeName string name of the type */ + private final SemType semType; public BDecimalType(String typeName, Module pkg) { + this(typeName, pkg, Builder.decimalType()); + } + + public static BDecimalType singletonType(BigDecimal value) { + return new BDecimalType(TypeConstants.DECIMAL_TNAME, EMPTY_MODULE, Builder.decimalConst(value)); + } + + private BDecimalType(String typeName, Module pkg, SemType semType) { super(typeName, pkg, DecimalValue.class); + this.semType = semType; } @Override @@ -63,4 +78,9 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + SemType createSemType() { + return semType; + } } 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 2ef0d084d3e9..b767c93fbc14 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 @@ -20,6 +20,10 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.FloatType; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; /** * {@code BFloatType} represents a integer which is a 32-bit floating-point number according to the @@ -28,7 +32,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BFloatType extends BType implements FloatType { +public class BFloatType extends BType implements FloatType, SemType { /** * Create a {@code BFloatType} which represents the boolean type. @@ -58,4 +62,19 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 8bda29fdaeed..f7e78b2a4aae 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -28,6 +28,7 @@ import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.semtype.BSubType; +import io.ballerina.runtime.internal.values.DecimalValue; import java.util.ArrayList; import java.util.Collections; @@ -41,6 +42,9 @@ public final class BTypeConverter { private BTypeConverter() { } + private static final SemType READONLY_SEMTYPE_PART = Core.union(Builder.nilType(), Builder.decimalType()); + private static final SemType ANY_SEMTYPE_PART = Core.union(Builder.nilType(), Builder.decimalType()); + private static SemType from(Type type) { if (type instanceof SemType semType) { return semType; @@ -57,9 +61,8 @@ public static SemType from(BType innerType) { } static SemType fromReadonly(BReadonlyType readonlyType) { - SemType semTypePart = Builder.nilType(); SemType bTypePart = wrapAsPureBType(readonlyType); - return Core.union(semTypePart, bTypePart); + return Core.union(READONLY_SEMTYPE_PART, bTypePart); } static SemType fromTypeReference(ReferenceType referenceType) { @@ -80,9 +83,8 @@ static SemType wrapAsPureBType(BType tupleType) { } static SemType fromAnyType(BAnyType anyType) { - SemType semTypePart = Builder.nilType(); SemType bTypePart = wrapAsPureBType(anyType); - return Core.union(semTypePart, bTypePart); + return Core.union(ANY_SEMTYPE_PART, bTypePart); } static SemType fromRecordType(BRecordType recordType) { @@ -119,8 +121,8 @@ private static BTypeParts split(Type type) { return new BTypeParts(from(type), Collections.emptyList()); } else if (type instanceof BUnionType unionType) { return splitUnion(unionType); - } else if (type instanceof BAnyType) { - return new BTypeParts(Builder.nilType(), List.of(type)); + } else if (type instanceof BAnyType anyType) { + return splitAnyType(anyType); } else if (type instanceof BTypeReferenceType referenceType) { return split(referenceType.getReferredType()); } else if (type instanceof BIntersectionType intersectionType) { @@ -134,6 +136,10 @@ private static BTypeParts split(Type type) { } } + private static BTypeParts splitAnyType(BAnyType anyType) { + return new BTypeParts(ANY_SEMTYPE_PART, List.of(anyType)); + } + private static BTypeParts splitFiniteType(BFiniteType finiteType) { Set newValueSpace = new HashSet<>(finiteType.valueSpace.size()); SemType semTypePart = Builder.neverType(); @@ -141,6 +147,8 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { // TODO: lift this to Builder (Object) -> Type if (each == null) { semTypePart = Core.union(semTypePart, Builder.nilType()); + } else if (each instanceof DecimalValue decimalValue) { + semTypePart = Core.union(semTypePart, Builder.decimalConst(decimalValue.value())); } else { newValueSpace.add(each); } @@ -150,9 +158,8 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { } private static BTypeParts splitReadonly(BReadonlyType readonlyType) { - SemType semTypePart = Builder.nilType(); // TODO: this is not exactly correct - return new BTypeParts(semTypePart, List.of(readonlyType)); + return new BTypeParts(READONLY_SEMTYPE_PART, List.of(readonlyType)); } private static BTypeParts splitUnion(BUnionType unionType) { @@ -169,7 +176,7 @@ private static BTypeParts splitUnion(BUnionType unionType) { private static boolean isSemType(Type type) { return switch (type.getTag()) { - case TypeTags.NEVER_TAG, TypeTags.NULL_TAG -> true; + case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG -> true; default -> false; }; } 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 2771264baf37..436adf97e5eb 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 @@ -29,6 +29,7 @@ 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 java.math.BigDecimal; import java.math.MathContext; @@ -61,8 +62,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; @@ -84,7 +87,7 @@ public DecimalValue(String value) { throw exception; } this.value = getValidDecimalValue(bd); - + this.singletonType = BDecimalType.singletonType(this.value); if (!this.booleanValue()) { this.valueKind = DecimalValueKind.ZERO; } @@ -230,7 +233,7 @@ public BigDecimal value() { */ @Override public Type getType() { - return PredefinedTypes.TYPE_DECIMAL; + return singletonType; } //========================= Mathematical operations supported =============================== From dee36472ca1e050adca5857ba20844993c07f4e9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 10 May 2024 14:50:44 +0530 Subject: [PATCH 005/178] Port float type --- .../runtime/api/types/SemType/Builder.java | 4 ++++ .../runtime/internal/TypeChecker.java | 5 +++-- .../runtime/internal/types/BFloatType.java | 20 +++++++++++++++++++ .../internal/types/BTypeConverter.java | 16 ++++++++++++--- 4 files changed, 40 insertions(+), 5 deletions(-) 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 index 11ff4e1bc176..9f48798882c6 100644 --- 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 @@ -65,6 +65,10 @@ public static SemType decimalType() { return from(BasicTypeCode.BT_DECIMAL); } + public static SemType floatType() { + return from(BasicTypeCode.BT_FLOAT); + } + public static SemType basicTypeUnion(int bitset) { return BBasicTypeBitSet.from(bitset); } 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 3d8b8069d62c..526f00fb311f 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 @@ -46,6 +46,7 @@ import io.ballerina.runtime.internal.types.BErrorType; import io.ballerina.runtime.internal.types.BField; import io.ballerina.runtime.internal.types.BFiniteType; +import io.ballerina.runtime.internal.types.BFloatType; import io.ballerina.runtime.internal.types.BFunctionType; import io.ballerina.runtime.internal.types.BFutureType; import io.ballerina.runtime.internal.types.BIntersectionType; @@ -344,8 +345,8 @@ public static Type getType(Object value) { } else if (value instanceof Number) { if (value instanceof Long) { return TYPE_INT; - } else if (value instanceof Double) { - return TYPE_FLOAT; + } else if (value instanceof Double doubleValue) { + return BFloatType.singletonType(doubleValue); } else if (value instanceof Integer || value instanceof Byte) { return TYPE_BYTE; } 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 b767c93fbc14..553263420799 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 @@ -19,12 +19,16 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.FloatType; +import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.SemType.SubType; import java.util.List; +import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; + /** * {@code BFloatType} represents a integer which is a 32-bit floating-point number according to the * standard IEEE 754 specifications. @@ -39,8 +43,15 @@ public class BFloatType extends BType implements FloatType, SemType { * * @param typeName string name of the type */ + private final SemType semType; + public BFloatType(String typeName, Module pkg) { + this(typeName, pkg, Builder.floatType()); + } + + private BFloatType(String typeName, Module pkg, SemType semType) { super(typeName, pkg, Double.class); + this.semType = semType; } @Override @@ -58,6 +69,10 @@ public int getTag() { return TypeTags.FLOAT_TAG; } + public static BFloatType singletonType(Double value) { + return new BFloatType(TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.floatConst(value)); + } + @Override public boolean isReadOnly() { return true; @@ -77,4 +92,9 @@ public int some() { public List subTypeData() { return get().subTypeData(); } + + @Override + SemType createSemType() { + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index f7e78b2a4aae..5db15f67fb8b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -42,8 +42,10 @@ public final class BTypeConverter { private BTypeConverter() { } - private static final SemType READONLY_SEMTYPE_PART = Core.union(Builder.nilType(), Builder.decimalType()); - private static final SemType ANY_SEMTYPE_PART = Core.union(Builder.nilType(), Builder.decimalType()); + private static final SemType READONLY_SEMTYPE_PART = + Core.union(Builder.floatType(), Core.union(Builder.nilType(), Builder.decimalType())); + private static final SemType ANY_SEMTYPE_PART = + Core.union(Builder.floatType(), Core.union(Builder.nilType(), Builder.decimalType())); private static SemType from(Type type) { if (type instanceof SemType semType) { @@ -101,6 +103,9 @@ static SemType fromRecordType(BRecordType recordType) { static SemType fromFiniteType(BFiniteType finiteType) { BTypeParts parts = splitFiniteType(finiteType); + if (parts.bTypeParts().isEmpty()) { + return parts.semTypePart(); + } BType newFiniteType = (BType) parts.bTypeParts().get(0); SemType bTypePart = wrapAsPureBType(newFiniteType); return Core.union(parts.semTypePart(), bTypePart); @@ -149,10 +154,15 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { semTypePart = Core.union(semTypePart, Builder.nilType()); } else if (each instanceof DecimalValue decimalValue) { semTypePart = Core.union(semTypePart, Builder.decimalConst(decimalValue.value())); + } else if (each instanceof Double doubleValue) { + semTypePart = Core.union(semTypePart, Builder.floatConst(doubleValue)); } else { newValueSpace.add(each); } } + if (newValueSpace.isEmpty()) { + return new BTypeParts(semTypePart, List.of()); + } BFiniteType newFiniteType = finiteType.cloneWithValueSpace(newValueSpace); return new BTypeParts(semTypePart, List.of(newFiniteType)); } @@ -176,7 +186,7 @@ private static BTypeParts splitUnion(BUnionType unionType) { private static boolean isSemType(Type type) { return switch (type.getTag()) { - case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG -> true; + case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG -> true; default -> false; }; } From 914aa5c8cab657bb824dc699833135b3086af280 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 10 May 2024 16:04:05 +0530 Subject: [PATCH 006/178] Refactor caches to use DATs --- .../api/types/SemType/BasicTypeCode.java | 68 +++++++++++-------- .../types/semtype/BBasicTypeBitSet.java | 38 +++++++++-- 2 files changed, 73 insertions(+), 33 deletions(-) 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 index c5647c7c48be..c8587d54777b 100644 --- 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 @@ -18,33 +18,28 @@ package io.ballerina.runtime.api.types.SemType; -import java.util.HashMap; -import java.util.Map; - public final class BasicTypeCode { - private final static Map cache = new HashMap<>(); - - static final int CODE_NIL = 0x00; - static final int CODE_BOOLEAN = 0x01; - static final int CODE_INT = 0x02; - static final int CODE_FLOAT = 0x03; - static final int CODE_DECIMAL = 0x04; - static final int CODE_STRING = 0x05; - static final int CODE_ERROR = 0x06; - static final int CODE_TYPEDESC = 0x07; - static final int CODE_HANDLE = 0x08; - static final int CODE_FUNCTION = 0x09; - static final int CODE_FUTURE = 0x0A; - static final int CODE_STREAM = 0x0B; - static final int CODE_LIST = 0x0C; - static final int CODE_MAPPING = 0x0D; - static final int CODE_TABLE = 0x0E; - static final int CODE_XML = 0x0F; - static final int CODE_OBJECT = 0x10; - static final int CODE_CELL = 0x11; - static final int CODE_UNDEF = 0x12; - static final int CODE_B_TYPE = 0x13; + 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_FUTURE = 0x0A; + public static final int CODE_STREAM = 0x0B; + public static final int CODE_LIST = 0x0C; + public static final int CODE_MAPPING = 0x0D; + public static final int CODE_TABLE = 0x0E; + public static final int CODE_XML = 0x0F; + public static final int CODE_OBJECT = 0x10; + public static final int CODE_CELL = 0x11; + public static final int CODE_UNDEF = 0x12; + public static final int CODE_B_TYPE = 0x13; // Inherently immutable public static final BasicTypeCode BT_NIL = from(CODE_NIL); @@ -75,7 +70,7 @@ public final class BasicTypeCode { public static final BasicTypeCode BT_B_TYPE = from(CODE_B_TYPE); // Helper bit fields (does not represent basic type tag) - static final int VT_COUNT = BT_OBJECT.code + 1; + static final int VT_COUNT = CODE_OBJECT + 1; static final int VT_MASK = (1 << VT_COUNT) - 1; static final int VT_COUNT_INHERENTLY_IMMUTABLE = 0x0A; @@ -88,10 +83,29 @@ private BasicTypeCode(int code) { } public static BasicTypeCode from(int code) { - return cache.computeIfAbsent(code, BasicTypeCode::new); + if (BasicTypeCodeCache.isCached(code)) { + return BasicTypeCodeCache.cache[code]; + } + return new BasicTypeCode(code); } public int code() { return code; } + + private final static class BasicTypeCodeCache { + + private static final BasicTypeCode[] cache; + static { + cache = new BasicTypeCode[CODE_B_TYPE + 2]; + for (int i = CODE_NIL; i < CODE_B_TYPE + 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/internal/types/semtype/BBasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java index ff2bc03644f4..1960ff69441b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java @@ -21,26 +21,29 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.BasicTypeBitSet; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.SemType.BasicTypeCode; import io.ballerina.runtime.api.types.SemType.SemTypeHelper; import io.ballerina.runtime.api.types.Type; -import java.util.HashMap; -import java.util.Map; +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_B_TYPE; +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_NIL; public final class BBasicTypeBitSet implements BasicTypeBitSet { private final int all; private final BTypeAdapter adapter; - private final static Map cache = new HashMap<>(); - private BBasicTypeBitSet(int all) { this.all = all; this.adapter = new BTypeAdapter(this); } public static BasicTypeBitSet from(int all) { - return cache.computeIfAbsent(all, BBasicTypeBitSet::new); + int index = BBasicTypeBitSetCache.cacheIndex(all); + if (index != BBasicTypeBitSetCache.NotFound) { + return BBasicTypeBitSetCache.cache[index]; + } + return new BBasicTypeBitSet(all); } @Override @@ -132,4 +135,27 @@ public int all() { public String toString() { return SemTypeHelper.stringRepr(this); } -} + + // TODO: see if we can use Application CDS to make this even faster + private final static class BBasicTypeBitSetCache { + + private static final BBasicTypeBitSet[] cache; + private static final int NotFound = -1; + static { + cache = new BBasicTypeBitSet[BasicTypeCode.CODE_B_TYPE + 2]; + for (int i = CODE_NIL; i < CODE_B_TYPE + 1; i++) { + cache[i] = new BBasicTypeBitSet(1 << i); + } + } + + private static int cacheIndex(int all) { + // TODO: check this is getting unrolled, otherwise use a switch + for (int i = CODE_NIL; i < CODE_B_TYPE + 1; i++) { + if (all == (1 << i)) { + return i; + } + } + return NotFound; + } + } +} \ No newline at end of file From a6946ee04097cb778d772f453807bad55c96992e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 12 May 2024 06:54:11 +0530 Subject: [PATCH 007/178] Port int type Cache int singleton creation --- .../runtime/api/types/SemType/Builder.java | 28 ++++++++ .../runtime/internal/TypeChecker.java | 12 ++-- .../runtime/internal/types/BByteType.java | 42 +++++++++++- .../runtime/internal/types/BIntegerType.java | 66 ++++++++++++++++++- .../internal/types/BTypeConverter.java | 25 +++++-- .../internal/types/semtype/BIntSubType.java | 4 +- 6 files changed, 163 insertions(+), 14 deletions(-) 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 index 9f48798882c6..41d7875d4442 100644 --- 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 @@ -61,6 +61,10 @@ public static SemType nilType() { return from(BasicTypeCode.BT_NIL); } + public static SemType intType() { + return from(BasicTypeCode.BT_INT); + } + public static SemType decimalType() { return from(BasicTypeCode.BT_DECIMAL); } @@ -78,11 +82,22 @@ public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) } public static SemType intConst(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 intRange(long min, long max) { + return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(min, max)); + } + public static SemType decimalConst(BigDecimal value) { BigDecimal[] values = {value}; return basicSubType(BasicTypeCode.BT_DECIMAL, BDecimalSubType.createDecimalSubType(true, values)); @@ -104,4 +119,17 @@ public static SemType stringConst(String value) { } return basicSubType(BasicTypeCode.BT_STRING, subType); } + + 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); + } + } + } } 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 526f00fb311f..ffa133119712 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 @@ -43,12 +43,14 @@ 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.BByteType; import io.ballerina.runtime.internal.types.BErrorType; import io.ballerina.runtime.internal.types.BField; import io.ballerina.runtime.internal.types.BFiniteType; import io.ballerina.runtime.internal.types.BFloatType; import io.ballerina.runtime.internal.types.BFunctionType; import io.ballerina.runtime.internal.types.BFutureType; +import io.ballerina.runtime.internal.types.BIntegerType; import io.ballerina.runtime.internal.types.BIntersectionType; import io.ballerina.runtime.internal.types.BJsonType; import io.ballerina.runtime.internal.types.BMapType; @@ -342,13 +344,13 @@ public static boolean isSameType(Type sourceType, Type targetType) { public static Type getType(Object value) { if (value == null) { return TYPE_NULL; - } else if (value instanceof Number) { + } else if (value instanceof Number number) { if (value instanceof Long) { - return TYPE_INT; - } else if (value instanceof Double doubleValue) { - return BFloatType.singletonType(doubleValue); + return BIntegerType.singletonType(number.longValue()); + } else if (value instanceof Double) { + return BFloatType.singletonType(number.doubleValue()); } else if (value instanceof Integer || value instanceof Byte) { - return TYPE_BYTE; + return BByteType.singletonType(number.intValue()); } } else if (value instanceof BString) { return TYPE_STRING; 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 8e5cda22dfbc..8b271ae05f98 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 @@ -20,22 +20,42 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.ByteType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; + +import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; /** * {@code BByteType} represents byte type in Ballerina. * * @since 0.995.0 */ -public class BByteType extends BType implements ByteType { +public class BByteType extends BType implements ByteType, SemType { + private final SemType semType; /** * Create a {@code BByteType} which represents the byte type. * * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { + this(typeName, pkg, Builder.intRange(0, UNSIGNED8_MAX_VALUE)); + } + + private BByteType(String typeName, Module pkg, SemType semType) { super(typeName, pkg, Integer.class); + this.semType = semType; + } + + // Java Byte is signed, so we need use int to avoid overflow + public static BByteType singletonType(Integer value) { + return new BByteType(TypeConstants.BYTE_TNAME, EMPTY_MODULE, Builder.intConst(value)); } @Override @@ -59,4 +79,24 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + SemType createSemType() { + return semType; + } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } 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 d8e290013da6..12338f8e2c50 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 @@ -18,8 +18,25 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntegerType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; + +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; /** * {@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 class BIntegerType extends BType implements IntegerType, SemType { private final int tag; + private final SemType semType; /** * Create a {@code BIntegerType} which represents the boolean type. @@ -37,13 +55,22 @@ 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(typeName, pkg, TypeTags.INT_TAG, pickSemType(TypeTags.INT_TAG)); } public BIntegerType(String typeName, Module pkg, int tag) { + this(typeName, pkg, tag, pickSemType(tag)); + } + + private BIntegerType(String typeName, Module pkg, int tag, SemType semType) { super(typeName, pkg, Long.class); this.tag = tag; + this.semType = semType; + } + + public static BIntegerType singletonType(Long value) { + return new BIntegerType(TypeConstants.INT_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.INT_TAG, + Builder.intConst(value)); } @Override @@ -65,4 +92,37 @@ public int getTag() { public boolean isReadOnly() { return true; } + + private static SemType pickSemType(int tag) { + return switch (tag) { + case TypeTags.INT_TAG -> Builder.intType(); + case TypeTags.SIGNED8_INT_TAG -> Builder.intRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); + case TypeTags.SIGNED16_INT_TAG -> Builder.intRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); + case TypeTags.SIGNED32_INT_TAG -> Builder.intRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); + case TypeTags.UNSIGNED8_INT_TAG, TypeTags.BYTE_TAG -> Builder.intRange(0, UNSIGNED8_MAX_VALUE); + case TypeTags.UNSIGNED16_INT_TAG -> Builder.intRange(0, UNSIGNED16_MAX_VALUE); + case TypeTags.UNSIGNED32_INT_TAG -> Builder.intRange(0, UNSIGNED32_MAX_VALUE); + default -> throw new UnsupportedOperationException("Unexpected int tag"); + }; + } + + @Override + SemType createSemType() { + return semType; + } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 5db15f67fb8b..54df9d7684d2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -37,15 +37,23 @@ import java.util.Set; // NOTE: this is so that we don't have to expose any utility constructors as public to builder -public final class BTypeConverter { +final class BTypeConverter { private BTypeConverter() { } private static final SemType READONLY_SEMTYPE_PART = - Core.union(Builder.floatType(), Core.union(Builder.nilType(), Builder.decimalType())); + unionOf(Builder.intType(), Builder.floatType(), Builder.nilType(), Builder.decimalType()); private static final SemType ANY_SEMTYPE_PART = - Core.union(Builder.floatType(), Core.union(Builder.nilType(), Builder.decimalType())); + unionOf(Builder.intType(), Builder.floatType(), Builder.nilType(), Builder.decimalType()); + + private static SemType unionOf(SemType... semTypes) { + SemType result = Builder.neverType(); + for (SemType semType : semTypes) { + result = Core.union(result, semType); + } + return result; + } private static SemType from(Type type) { if (type instanceof SemType semType) { @@ -113,6 +121,9 @@ static SemType fromFiniteType(BFiniteType finiteType) { static SemType fromUnionType(BUnionType unionType) { BTypeParts parts = splitUnion(unionType); + if (parts.bTypeParts().isEmpty()) { + return parts.semTypePart(); + } SemType bTypePart = Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(unionType)); return Core.union(parts.semTypePart(), bTypePart); } @@ -156,6 +167,8 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { semTypePart = Core.union(semTypePart, Builder.decimalConst(decimalValue.value())); } else if (each instanceof Double doubleValue) { semTypePart = Core.union(semTypePart, Builder.floatConst(doubleValue)); + } else if (each instanceof Number intValue) { + semTypePart = Core.union(semTypePart, Builder.intConst(intValue.longValue())); } else { newValueSpace.add(each); } @@ -185,8 +198,12 @@ private static BTypeParts splitUnion(BUnionType unionType) { } private static boolean isSemType(Type type) { + // FIXME: can't we replace this with instanceof check? return switch (type.getTag()) { - case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG -> true; + case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG, + TypeTags.INT_TAG, TypeTags.BYTE_TAG, + TypeTags.SIGNED8_INT_TAG, TypeTags.SIGNED16_INT_TAG, TypeTags.SIGNED32_INT_TAG, + TypeTags.UNSIGNED8_INT_TAG, TypeTags.UNSIGNED16_INT_TAG, TypeTags.UNSIGNED32_INT_TAG -> true; default -> false; }; } 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 index 6fe4d6ac96ba..0f10c8930b1b 100644 --- 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 @@ -62,7 +62,9 @@ public static BIntSubType createIntSubType(List values) { return new BIntSubType(new IntSubTypeData(ranges.toArray(Range[]::new))); } - public static BIntSubType createIntSubType(Range[] ranges) { + public static BIntSubType createIntSubType(long min, long max) { + Range range = new Range(min, max); + Range[] ranges = {range}; return new BIntSubType(new IntSubTypeData(ranges)); } From 965fc0f9d97f511ae446ad5c7a1fbd78e053d45a Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 12 May 2024 10:43:04 +0530 Subject: [PATCH 008/178] Port Boolean type --- .../runtime/api/types/SemType/Builder.java | 20 +++++++++ .../runtime/api/types/SemType/Core.java | 4 -- .../api/types/SemType/SemTypeHelper.java | 2 + .../runtime/internal/TypeChecker.java | 5 ++- .../runtime/internal/types/BBooleanType.java | 44 ++++++++++++++++++- .../internal/types/BTypeConverter.java | 10 +++-- 6 files changed, 75 insertions(+), 10 deletions(-) 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 index 41d7875d4442..9e8cc504fcbc 100644 --- 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 @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; +import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; import io.ballerina.runtime.internal.types.semtype.BFloatSubType; import io.ballerina.runtime.internal.types.semtype.BIntSubType; @@ -73,6 +74,10 @@ public static SemType floatType() { return from(BasicTypeCode.BT_FLOAT); } + public static SemType booleanType() { + return from(BasicTypeCode.BT_BOOLEAN); + } + public static SemType basicTypeUnion(int bitset) { return BBasicTypeBitSet.from(bitset); } @@ -94,6 +99,10 @@ private static SemType createIntSingletonType(long value) { return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(values)); } + public static SemType booleanConst(boolean value) { + return value ? BooleanTypeCache.TRUE : BooleanTypeCache.FALSE; + } + public static SemType intRange(long min, long max) { return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(min, max)); } @@ -132,4 +141,15 @@ private static final class IntTypeCache { } } } + + 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)); + } + + } } 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 index f3b9b23a0d35..4659ad1c0ddd 100644 --- 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 @@ -85,10 +85,8 @@ public static SemType diff(SemType t1, SemType t2) { if (data.isAll()) { all |= 1 << typeCode; some &= ~(1 << typeCode); - subtypes.add(null); } else if (data.isNothing()) { some &= ~(1 << typeCode); - subtypes.add(null); } else { subtypes.add(data); } @@ -132,7 +130,6 @@ public static SemType union(SemType t1, SemType t2) { if (data.isAll()) { all |= 1 << code; some &= ~(1 << code); - subtypes.add(null); } else { subtypes.add(data); } @@ -194,7 +191,6 @@ public static SemType intersect(SemType t1, SemType t2) { subtypes.add(data); } else { some &= ~(1 << code); - subtypes.add(null); } } if (some == 0) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java index 3151c35aebd5..00f1f33facd6 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.api.types.SemType; +import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_BOOLEAN; import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_B_TYPE; import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_CELL; import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_DECIMAL; @@ -50,6 +51,7 @@ public static String stringRepr(SemType ty) { private 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"); 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 ffa133119712..8db75a75c6e6 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 @@ -43,6 +43,7 @@ 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.BBooleanType; import io.ballerina.runtime.internal.types.BByteType; import io.ballerina.runtime.internal.types.BErrorType; import io.ballerina.runtime.internal.types.BField; @@ -354,8 +355,8 @@ public static Type getType(Object value) { } } else if (value instanceof BString) { return TYPE_STRING; - } else if (value instanceof Boolean) { - return TYPE_BOOLEAN; + } else if (value instanceof Boolean booleanValue) { + return BBooleanType.singletonType(booleanValue); } else if (value instanceof BObject bObject) { return bObject.getOriginalType(); } 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 28031b69e41e..2adb9de874c6 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,28 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.BooleanType; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; /** * {@code BBooleanType} represents boolean type in Ballerina. * * @since 0.995.0 */ -public class BBooleanType extends BType implements BooleanType { +public class BBooleanType extends BType implements BooleanType, SemType { + + private final SemType semType; + private final static BBooleanType TRUE = + new BBooleanType(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(true)); + private final static BBooleanType FALSE = + new BBooleanType(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. @@ -34,7 +47,16 @@ public class BBooleanType extends BType implements BooleanType { * @param typeName string name of the type */ public BBooleanType(String typeName, Module pkg) { + this(typeName, pkg, Builder.booleanType()); + } + + public static BBooleanType singletonType(boolean value) { + return value ? TRUE : FALSE; + } + + private BBooleanType(String typeName, Module pkg, SemType semType) { super(typeName, pkg, Boolean.class); + this.semType = semType; } @Override @@ -58,4 +80,24 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + SemType createSemType() { + return semType; + } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 54df9d7684d2..4126113a0da0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -43,9 +43,11 @@ private BTypeConverter() { } private static final SemType READONLY_SEMTYPE_PART = - unionOf(Builder.intType(), Builder.floatType(), Builder.nilType(), Builder.decimalType()); + unionOf(Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.nilType(), + Builder.decimalType()); private static final SemType ANY_SEMTYPE_PART = - unionOf(Builder.intType(), Builder.floatType(), Builder.nilType(), Builder.decimalType()); + unionOf(Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.nilType(), + Builder.decimalType()); private static SemType unionOf(SemType... semTypes) { SemType result = Builder.neverType(); @@ -169,6 +171,8 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { semTypePart = Core.union(semTypePart, Builder.floatConst(doubleValue)); } else if (each instanceof Number intValue) { semTypePart = Core.union(semTypePart, Builder.intConst(intValue.longValue())); + } else if (each instanceof Boolean booleanValue) { + semTypePart = Core.union(semTypePart, Builder.booleanConst(booleanValue)); } else { newValueSpace.add(each); } @@ -201,7 +205,7 @@ private static boolean isSemType(Type type) { // FIXME: can't we replace this with instanceof check? return switch (type.getTag()) { case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG, - TypeTags.INT_TAG, TypeTags.BYTE_TAG, + TypeTags.BOOLEAN_TAG, TypeTags.INT_TAG, TypeTags.BYTE_TAG, TypeTags.SIGNED8_INT_TAG, TypeTags.SIGNED16_INT_TAG, TypeTags.SIGNED32_INT_TAG, TypeTags.UNSIGNED8_INT_TAG, TypeTags.UNSIGNED16_INT_TAG, TypeTags.UNSIGNED32_INT_TAG -> true; default -> false; From 99a20273372a41c72cf857a34cd1b2313ad46b9f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 12 May 2024 12:32:19 +0530 Subject: [PATCH 009/178] Port String type --- .../runtime/api/types/SemType/Builder.java | 23 ++++++++- .../runtime/internal/TypeChecker.java | 6 +-- .../runtime/internal/TypeConverter.java | 2 +- .../runtime/internal/types/BDecimalType.java | 19 ++++++- .../runtime/internal/types/BNeverType.java | 20 +++++++- .../runtime/internal/types/BNullType.java | 20 +++++++- .../runtime/internal/types/BStringType.java | 51 +++++++++++++++++-- .../internal/types/BTypeConverter.java | 29 ++++++----- 8 files changed, 145 insertions(+), 25 deletions(-) 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 index 9e8cc504fcbc..7e90b3671228 100644 --- 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 @@ -78,6 +78,14 @@ public static SemType booleanType() { return from(BasicTypeCode.BT_BOOLEAN); } + public static SemType stringType() { + return from(BasicTypeCode.BT_STRING); + } + + public static SemType charType() { + return StringTypeCache.charType; + } + public static SemType basicTypeUnion(int bitset) { return BBasicTypeBitSet.from(bitset); } @@ -122,9 +130,9 @@ public static SemType stringConst(String value) { String[] values = {value}; String[] empty = new String[0]; if (value.codePoints().count() == 1) { - subType = BStringSubType.createStringSubType(true, values, false, empty); + subType = BStringSubType.createStringSubType(true, values, true, empty); } else { - subType = BStringSubType.createStringSubType(false, empty, true, values); + subType = BStringSubType.createStringSubType(true, empty, true, values); } return basicSubType(BasicTypeCode.BT_STRING, subType); } @@ -152,4 +160,15 @@ private static SemType createBooleanSingletonType(boolean value) { } } + + private static final class StringTypeCache { + + private static final SemType charType; + private static final String[] EMPTY_STRING_ARR = new String[0]; + static { + BStringSubType subTypeData = BStringSubType.createStringSubType(false, EMPTY_STRING_ARR, true, + EMPTY_STRING_ARR); + charType = basicSubType(BasicTypeCode.BT_STRING, subTypeData); + } + } } 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 8db75a75c6e6..2c8cfbd730de 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 @@ -61,6 +61,7 @@ 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.BStringType; import io.ballerina.runtime.internal.types.BTableType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BType; @@ -119,7 +120,6 @@ import static io.ballerina.runtime.api.PredefinedTypes.TYPE_JSON; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_NULL; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_READONLY_JSON; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_STRING; import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_BUILTIN_PKG_PREFIX; import static io.ballerina.runtime.api.constants.RuntimeConstants.BBYTE_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.BBYTE_MIN_VALUE; @@ -353,8 +353,8 @@ public static Type getType(Object value) { } else if (value instanceof Integer || value instanceof Byte) { return BByteType.singletonType(number.intValue()); } - } else if (value instanceof BString) { - return TYPE_STRING; + } else if (value instanceof BString stringValue) { + return BStringType.singletonType(stringValue.getValue()); } else if (value instanceof Boolean booleanValue) { return BBooleanType.singletonType(booleanValue); } else if (value instanceof BObject bObject) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 82f3ffbdb370..541dc38c82f6 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -498,7 +498,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) { 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 cb2d046df3b2..c31944a6a4e6 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 @@ -24,9 +24,11 @@ import io.ballerina.runtime.api.types.DecimalType; import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; +import java.util.List; import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; @@ -36,7 +38,7 @@ * * @since 0.995.0 */ -public class BDecimalType extends BType implements DecimalType { +public class BDecimalType extends BType implements DecimalType, SemType { /** * Create a {@code BDecimalType} which represents the decimal type. @@ -83,4 +85,19 @@ public boolean isReadOnly() { SemType createSemType() { return semType; } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } 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 288272ad388b..6963f8f431d8 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 @@ -23,13 +23,16 @@ import io.ballerina.runtime.api.types.NeverType; import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; /** * {@code BNeverType} represents the type of a {@code Never}. * * @since 2.0.0-preview1 */ -public class BNeverType extends BNullType implements NeverType { +public class BNeverType extends BNullType implements NeverType, SemType { /** * Create a {@code BNeverType} represents the type of a {@code Never}. * @@ -53,4 +56,19 @@ public int getTag() { SemType createSemType() { return Builder.neverType(); } + + @Override + public int all() { + return 0; + } + + @Override + public int some() { + return 0; + } + + @Override + public List subTypeData() { + return List.of(); + } } 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 a1e0dc983b17..d8d168f3c1a0 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 @@ -22,13 +22,16 @@ import io.ballerina.runtime.api.types.NullType; import io.ballerina.runtime.api.types.SemType.Builder; import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; + +import java.util.List; /** * {@code BNullType} represents the type of a {@code NullLiteral}. * * @since 0.995.0 */ -public class BNullType extends BType implements NullType { +public class BNullType extends BType implements NullType, SemType { /** * Create a {@code BNullType} represents the type of a {@code NullLiteral}. @@ -69,4 +72,19 @@ public boolean isReadOnly() { SemType createSemType() { return Builder.nilType(); } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } 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 34be4d3d188f..627aedfec30e 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 @@ -18,19 +18,27 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.RuntimeConstants; +import io.ballerina.runtime.api.constants.TypeConstants; +import io.ballerina.runtime.api.types.SemType.Builder; +import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.SemType.SubType; import io.ballerina.runtime.api.types.StringType; +import java.util.List; + /** * {@code BStringType} represents a String type in ballerina. * * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BStringType extends BType implements StringType { +public class BStringType extends BType implements StringType, SemType { private final int tag; + private final SemType semType; /** * Create a {@code BStringType} which represents the boolean type. @@ -38,13 +46,30 @@ 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(typeName, pkg, TypeTags.STRING_TAG, Builder.stringType()); } public BStringType(String typeName, Module pkg, int tag) { + this(typeName, pkg, tag, pickSemtype(tag)); + } + + private BStringType(String typeName, Module pkg, int tag, SemType semType) { super(typeName, pkg, String.class); this.tag = tag; + this.semType = semType; + } + + public static BStringType singletonType(String value) { + return new BStringType(TypeConstants.STRING_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.STRING_TAG, + Builder.stringConst(value)); + } + + private static SemType pickSemtype(int tag) { + return switch (tag) { + case TypeTags.STRING_TAG -> Builder.stringType(); + case TypeTags.CHAR_STRING_TAG -> Builder.charType(); + default -> throw new IllegalStateException("Unexpected string type tag: " + tag); + }; } @Override @@ -66,4 +91,24 @@ public int getTag() { public boolean isReadOnly() { return true; } + + @Override + SemType createSemType() { + return semType; + } + + @Override + public int all() { + return get().all(); + } + + @Override + public int some() { + return get().some(); + } + + @Override + public List subTypeData() { + return get().subTypeData(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 4126113a0da0..c028a24ee14e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -18,7 +18,6 @@ package io.ballerina.runtime.internal.types; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.ReferenceType; @@ -27,6 +26,7 @@ import io.ballerina.runtime.api.types.SemType.Core; import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.semtype.BSubType; import io.ballerina.runtime.internal.values.DecimalValue; @@ -43,11 +43,11 @@ private BTypeConverter() { } private static final SemType READONLY_SEMTYPE_PART = - unionOf(Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.nilType(), - Builder.decimalType()); + unionOf(Builder.stringType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), + Builder.nilType(), Builder.decimalType()); private static final SemType ANY_SEMTYPE_PART = - unionOf(Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.nilType(), - Builder.decimalType()); + unionOf(Builder.stringType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), + Builder.nilType(), Builder.decimalType()); private static SemType unionOf(SemType... semTypes) { SemType result = Builder.neverType(); @@ -173,6 +173,8 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { semTypePart = Core.union(semTypePart, Builder.intConst(intValue.longValue())); } else if (each instanceof Boolean booleanValue) { semTypePart = Core.union(semTypePart, Builder.booleanConst(booleanValue)); + } else if (each instanceof BString stringValue) { + semTypePart = Core.union(semTypePart, Builder.stringConst(stringValue.getValue())); } else { newValueSpace.add(each); } @@ -202,13 +204,14 @@ private static BTypeParts splitUnion(BUnionType unionType) { } private static boolean isSemType(Type type) { - // FIXME: can't we replace this with instanceof check? - return switch (type.getTag()) { - case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG, - TypeTags.BOOLEAN_TAG, TypeTags.INT_TAG, TypeTags.BYTE_TAG, - TypeTags.SIGNED8_INT_TAG, TypeTags.SIGNED16_INT_TAG, TypeTags.SIGNED32_INT_TAG, - TypeTags.UNSIGNED8_INT_TAG, TypeTags.UNSIGNED16_INT_TAG, TypeTags.UNSIGNED32_INT_TAG -> true; - default -> false; - }; + return type instanceof SemType; +// // FIXME: can't we replace this with instanceof check? +// return switch (type.getTag()) { +// case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG, +// TypeTags.BOOLEAN_TAG, TypeTags.INT_TAG, TypeTags.BYTE_TAG, +// TypeTags.SIGNED8_INT_TAG, TypeTags.SIGNED16_INT_TAG, TypeTags.SIGNED32_INT_TAG, +// TypeTags.UNSIGNED8_INT_TAG, TypeTags.UNSIGNED16_INT_TAG, TypeTags.UNSIGNED32_INT_TAG -> true; +// default -> false; +// }; } } From c3ca7f45f60feee6b7628c9ad3ba47be8fb78e18 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 14 May 2024 09:30:06 +0530 Subject: [PATCH 010/178] Refactor SemType implementation --- .../BasicTypeBitSet.java} | 18 +- .../{SemType => semtype}/BasicTypeCode.java | 4 +- .../types/{SemType => semtype}/Builder.java | 67 +++++-- .../types/{SemType => semtype}/Context.java | 2 +- .../api/types/{SemType => semtype}/Core.java | 180 ++++++++---------- .../runtime/api/types/semtype/SemType.java | 71 +++++++ .../{SemType => semtype}/SemTypeHelper.java | 42 ++-- .../{SemType => semtype}/SemTypeWrapper.java | 2 +- .../types/{SemType => semtype}/SubType.java | 34 ++-- .../ballerina/runtime/api/values/BString.java | 3 + .../runtime/internal/TypeChecker.java | 136 ++++++++++--- .../runtime/internal/types/BAnyType.java | 2 +- .../runtime/internal/types/BBooleanType.java | 87 ++++----- .../runtime/internal/types/BByteType.java | 105 +++++----- .../runtime/internal/types/BDecimalType.java | 102 +++++----- .../runtime/internal/types/BFiniteType.java | 2 +- .../runtime/internal/types/BFloatType.java | 78 +++----- .../runtime/internal/types/BIntegerType.java | 152 ++++++++------- .../internal/types/BIntersectionType.java | 9 +- .../runtime/internal/types/BNeverType.java | 30 +-- .../runtime/internal/types/BNullType.java | 80 ++++---- .../runtime/internal/types/BReadonlyType.java | 2 +- .../runtime/internal/types/BRecordType.java | 2 +- .../internal/types/BSemTypeWrapper.java | 155 +++++++++++++++ .../runtime/internal/types/BStringType.java | 141 +++++++------- .../runtime/internal/types/BTupleType.java | 2 +- .../runtime/internal/types/BType.java | 2 +- .../internal/types/BTypeConverter.java | 16 +- .../internal/types/BTypeReferenceType.java | 9 +- .../runtime/internal/types/BUnionType.java | 2 +- .../types/semtype/BBasicTypeBitSet.java | 161 ---------------- .../types/semtype/BBooleanSubType.java | 31 ++- .../types/semtype/BDecimalSubType.java | 15 +- .../internal/types/semtype/BFloatSubType.java | 15 +- .../internal/types/semtype/BIntSubType.java | 15 +- .../internal/types/semtype/BSemType.java | 172 ----------------- .../types/semtype/BSemTypeWithIdentity.java | 149 --------------- .../types/semtype/BStringSubType.java | 15 +- .../internal/types/semtype/BSubType.java | 18 +- .../types/semtype/BSubTypeWrapper.java | 0 .../internal/types/semtype/BTypeAdapter.java | 8 +- .../types/semtype/PureSemType.java} | 20 +- .../internal/types/semtype/SubtypePair.java | 7 +- .../types/semtype/SubtypePairIterator.java | 16 +- .../internal/types/semtype/SubtypePairs.java | 2 +- .../internal/values/ArrayValueImpl.java | 30 +-- .../runtime/internal/values/StringValue.java | 6 +- .../src/main/java/module-info.java | 2 +- .../types/semtype/BBooleanSubTypeTests.java | 95 --------- .../types/semtype/BDecimalSubTypeTest.java | 61 ------ .../internal/types/semtype/CoreTests.java | 121 ------------ 51 files changed, 986 insertions(+), 1510 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType/SemType.java => semtype/BasicTypeBitSet.java} (75%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/BasicTypeCode.java (97%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/Builder.java (72%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/Context.java (93%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/Core.java (54%) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/SemTypeHelper.java (67%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/SemTypeWrapper.java (94%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/{SemType => semtype}/SubType.java (53%) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubTypeWrapper.java rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api/types/BasicTypeBitSet.java => internal/types/semtype/PureSemType.java} (64%) delete mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java delete mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java delete mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java 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/BasicTypeBitSet.java similarity index 75% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java index 83d4d5b1475f..44d7355fe2a9 100644 --- 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/BasicTypeBitSet.java @@ -16,17 +16,15 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.api.types.Type; +public interface BasicTypeBitSet { -import java.util.List; + default int some() { + return 0; + } -public interface SemType extends Type { - - int all(); - - int some(); - - List subTypeData(); + default SubType[] subTypeData() { + return new SubType[0]; + } } 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 similarity index 97% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/BasicTypeCode.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java index c8587d54777b..fa1513f5f79d 100644 --- 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 @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; public final class BasicTypeCode { @@ -93,7 +93,7 @@ public int code() { return code; } - private final static class BasicTypeCodeCache { + private static final class BasicTypeCodeCache { private static final BasicTypeCode[] cache; static { 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 similarity index 72% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Builder.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java index 7e90b3671228..fc452eb71f4a 100644 --- 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 @@ -16,29 +16,34 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.BType; -import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; 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.BSemType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; + public final class Builder { + private static final String[] EMPTY_STRING_ARR = new String[0]; + private Builder() { } public static SemType from(BasicTypeCode typeCode) { - return BBasicTypeBitSet.from(1 << typeCode.code()); + if (BasicTypeCache.isCached(typeCode)) { + return BasicTypeCache.cache[typeCode.code()]; + } + return SemType.from(1 << typeCode.code()); } public static SemType from(Type type) { @@ -66,6 +71,10 @@ public static SemType intType() { return from(BasicTypeCode.BT_INT); } + public static SemType bType() { + return from(BasicTypeCode.BT_B_TYPE); + } + public static SemType decimalType() { return from(BasicTypeCode.BT_DECIMAL); } @@ -86,12 +95,25 @@ public static SemType charType() { return StringTypeCache.charType; } + private static final SemType NEVER = SemType.from(0); + public static SemType basicTypeUnion(int bitset) { - return BBasicTypeBitSet.from(bitset); + // TODO: may be cache single type bit sets as well as well + if (bitset == 0) { + return NEVER; + } else if (Integer.bitCount(bitset) == 1) { + int code = Integer.numberOfTrailingZeros(bitset); + if (BasicTypeCache.isCached(code)) { + return BasicTypeCache.cache[code]; + } + } + return SemType.from(bitset); } public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { - return BSemType.from(0, 1 << basicTypeCode.code(), List.of(subType)); + SubType[] subTypes = initializeSubtypeArray(); + subTypes[basicTypeCode.code()] = subType; + return SemType.from(0, 1 << basicTypeCode.code(), subTypes); } public static SemType intConst(long value) { @@ -128,8 +150,8 @@ public static SemType floatConst(double value) { public static SemType stringConst(String value) { BStringSubType subType; String[] values = {value}; - String[] empty = new String[0]; - if (value.codePoints().count() == 1) { + 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); @@ -137,6 +159,10 @@ public static SemType stringConst(String value) { return basicSubType(BasicTypeCode.BT_STRING, subType); } + static SubType[] initializeSubtypeArray() { + return new SubType[CODE_B_TYPE + 2]; + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; @@ -164,11 +190,30 @@ private static SemType createBooleanSingletonType(boolean value) { private static final class StringTypeCache { private static final SemType charType; - private static final String[] EMPTY_STRING_ARR = new String[0]; static { - BStringSubType subTypeData = BStringSubType.createStringSubType(false, EMPTY_STRING_ARR, true, - EMPTY_STRING_ARR); + 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_B_TYPE + 2]; + for (int i = 0; i < CODE_B_TYPE + 1; i++) { + cache[i] = SemType.from(1 << i); + } + } + + private static boolean isCached(BasicTypeCode code) { + int i = code.code(); + return 0 < i && i <= CODE_B_TYPE; + } + + private static boolean isCached(int code) { + return 0 < code && code <= CODE_B_TYPE; + } + } } 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 similarity index 93% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Context.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java index c6bd2227623d..06611bee8c97 100644 --- 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 @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; public class Context { 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 similarity index 54% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/Core.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java index 4659ad1c0ddd..62170b0b4597 100644 --- 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 @@ -16,64 +16,56 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.api.types.BasicTypeBitSet; -import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.semtype.AllOrNothing; -import io.ballerina.runtime.internal.types.semtype.BBasicTypeBitSet; -import io.ballerina.runtime.internal.types.semtype.BSemType; 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.util.ArrayList; -import java.util.List; -import java.util.function.BiFunction; - -import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.BT_B_TYPE; -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.BasicTypeCode.BT_B_TYPE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; public final class Core { - private static final SemType SEMTYPE_TOP = BBasicTypeBitSet.from((1 << (CODE_UNDEF + 1)) - 1); - private static final BasicTypeBitSet B_TYPE_TOP = BBasicTypeBitSet.from(1 << BT_B_TYPE.code()); + public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); + public static final SemType B_TYPE_TOP = SemType.from(1 << BT_B_TYPE.code()); private Core() { } public static SemType diff(SemType t1, SemType t2) { - if (t1.some() == 0) { - if (t2.some() == 0) { - return Builder.basicTypeUnion(t1.all() & ~t2.all()); + int all1 = t1.all; + int all2 = t2.all; + int some1 = t1.some; + int some2 = t2.some; + if (some1 == 0) { + if (some2 == 0) { + return Builder.basicTypeUnion(all1 & ~all2); } else { - if (t1.all() == 0) { + if (all1 == 0) { return t1; } } } else { - if (t2.some() == 0) { - if (t2.all() == VT_MASK) { - return BBasicTypeBitSet.from(0); + if (some2 == 0) { + if (all2 == VT_MASK) { + return Builder.basicTypeUnion(0); } } } - int all1 = t1.all(); - int all2 = t2.all(); - int some1 = t1.some(); - int some2 = t2.some(); int all = all1 & ~(all2 | some2); int some = (all1 | some1) & ~all2; some = some & ~all; if (some == 0) { - return BBasicTypeBitSet.from(all); + return SemType.from(all); } - List subtypes = new ArrayList<>(); + SubType[] subtypes = Builder.initializeSubtypeArray(); for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { SubType data1 = pair.subType1(); SubType data2 = pair.subType2(); - int typeCode = pair.typeCode(); + int code = pair.typeCode(); SubType data; if (data1 == null) { data = data2.complement(); @@ -83,15 +75,15 @@ public static SemType diff(SemType t1, SemType t2) { data = data1.diff(data2); } if (data.isAll()) { - all |= 1 << typeCode; - some &= ~(1 << typeCode); + all |= 1 << code; + some &= ~(1 << code); } else if (data.isNothing()) { - some &= ~(1 << typeCode); + some &= ~(1 << code); } else { - subtypes.add(data); + subtypes[code] = data; } } - return BSemType.from(all, some, subtypes); + return SemType.from(all, some, subtypes); } public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { @@ -99,22 +91,22 @@ public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { } public static SemType union(SemType t1, SemType t2) { - if (t1.some() == 0) { - if (t2.some() == 0) { - return BBasicTypeBitSet.from(t1.all() | t2.all()); - } - } int all1 = t1.all(); int some1 = t1.some(); int all2 = t2.all(); int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return Builder.basicTypeUnion(all1 | all2); + } + } int all = all1 | all2; int some = (some1 | some2) & ~all; if (some == 0) { - return BBasicTypeBitSet.from(all); + return Builder.basicTypeUnion(all); } - List subtypes = new ArrayList<>(); + SubType[] subtypes = Builder.initializeSubtypeArray(); for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { int code = pair.typeCode(); SubType data1 = pair.subType1(); @@ -131,48 +123,48 @@ public static SemType union(SemType t1, SemType t2) { all |= 1 << code; some &= ~(1 << code); } else { - subtypes.add(data); + subtypes[code] = data; } } if (some == 0) { - return BBasicTypeBitSet.from(all); + return SemType.from(all); } - return BSemType.from(all, some, subtypes); + return SemType.from(all, some, subtypes); } public static SemType intersect(SemType t1, SemType t2) { - if (t1.some() == 0) { - if (t2.some() == 0) { - return BBasicTypeBitSet.from(t1.all() & t2.all()); + 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 (t1.all() == 0) { + if (all1 == 0) { return t1; } - if (t1.all() == VT_MASK) { + if (all1 == VT_MASK) { return t2; } } - } else if (t2.some() == 0) { - if (t2.all() == 0) { + } else if (some2 == 0) { + if (all2 == 0) { return t2; } - if (t2.all() == VT_MASK) { + if (all2 == VT_MASK) { return t1; } } - int all1 = t1.all(); - int some1 = t1.some(); - int all2 = t2.all(); - int some2 = t2.some(); int all = all1 & all2; int some = (some1 | all1) & (some2 | all2); some = some & ~all; if (some == 0) { - return BBasicTypeBitSet.from(all); + return SemType.from(all); } - List subtypes = new ArrayList<>(); + SubType[] subtypes = Builder.initializeSubtypeArray(); for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { int code = pair.typeCode(); SubType data1 = pair.subType1(); @@ -188,22 +180,22 @@ public static SemType intersect(SemType t1, SemType t2) { } if (!data.isNothing()) { - subtypes.add(data); + subtypes[code] = data; } else { some &= ~(1 << code); } } if (some == 0) { - return BBasicTypeBitSet.from(all); + return SemType.from(all); } - return BSemType.from(all, some, subtypes); + return SemType.from(all, some, subtypes); } public static boolean isEmpty(Context cx, SemType t) { - if (t.some() == 0) { - return t.all() == 0; + if (t.some == 0) { + return t.all == 0; } - if (t.all() != 0) { + if (t.all != 0) { return false; } for (SubType subType : t.subTypeData()) { @@ -222,51 +214,32 @@ public static SemType complement(SemType t1) { } public static boolean isNever(SemType t) { - return t.all() == 0 && t.some() == 0; + return t.all == 0 && t.some == 0; } public static boolean isSubType(Context cx, SemType t1, SemType t2) { - // IF t1 and t2 are not pure semtypes calling this is an error + // IF t1 and t2 are not pure semtypes calling this is an undefined return isEmpty(cx, diff(t1, t2)); } - public static boolean isSubType(Context cx, SemType t1, SemType t2, - BiFunction fallback) { - SemType s1 = intersect(t1, SEMTYPE_TOP); - SemType s2 = intersect(t2, SEMTYPE_TOP); - return isEmpty(cx, diff(s1, s2)) && applyFallback(t1, t2, fallback); - } - - private static boolean applyFallback(SemType t1, SemType t2, - BiFunction fallback) { - boolean t1HasBType = containsBasicType(t1, B_TYPE_TOP); - boolean t2HasBType = containsBasicType(t2, B_TYPE_TOP); - if (t1HasBType && t2HasBType) { - BType bType1 = (BType) subTypeData(t1, BT_B_TYPE); - BType bType2 = (BType) subTypeData(t2, BT_B_TYPE); - return fallback.apply(bType1, bType2); - } - return !t1HasBType; + public static boolean isSubtypeSimple(SemType t1, SemType t2) { + int bits = t1.all | t1.some; + return (bits & ~t2.all()) == 0; } - private static SubTypeData subTypeData(SemType s, BasicTypeCode code) { - if ((s.all() & (1 << code.code())) != 0) { + public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { + if ((s.all & (1 << code.code())) != 0) { return AllOrNothing.ALL; } - if (s.some() == 0) { + if (s.some == 0) { return AllOrNothing.NOTHING; } - return s.subTypeData().get(code.code()).data(); - } - - private static boolean containsBasicType(SemType t1, BasicTypeBitSet t2) { - int bits = t1.all() | t1.some(); - return (bits & t2.all()) != 0; + return s.subTypeData()[code.code()].data(); } - public static boolean isSubTypeSimple(SemType t1, BasicTypeBitSet t2) { - int bits = t1.all() | t1.some(); - return (bits & ~t2.all()) == 0; + 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) { @@ -274,23 +247,22 @@ public static boolean isSameType(Context cx, SemType t1, SemType t2) { } public static BasicTypeBitSet widenToBasicTypes(SemType t) { - int all = t.all() | t.some(); + int all = t.all | t.some; if (cardinality(all) > 1) { throw new IllegalStateException("Cannot widen to basic type for a type with multiple basic types"); } - return BBasicTypeBitSet.from(all); + return Builder.basicTypeUnion(all); } - private static boolean isSet(int bitset, int index) { - return (bitset & (1 << index)) != 0; + private static int cardinality(int bitset) { + return Integer.bitCount(bitset); } - private static int cardinality(int bitset) { - int count = 0; - while (bitset != 0) { - count += bitset & 1; - bitset >>= 1; + public static SemType widenToBasicTypeUnion(SemType t) { + if (t.some == 0) { + return t; } - return count; + int all = t.all | t.some; + return Builder.basicTypeUnion(all); } } 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..19a45b8cef98 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java @@ -0,0 +1,71 @@ +/* + * 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.BSemTypeWrapper; +import io.ballerina.runtime.internal.types.semtype.PureSemType; + +public abstract sealed class SemType implements BasicTypeBitSet permits BSemTypeWrapper, PureSemType { + + final int all; + final int some; + private final SubType[] subTypeData; + private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + + protected SemType(int all, int some, SubType[] subTypeData) { + this.all = all; + this.some = some; + this.subTypeData = subTypeData; + } + + protected SemType(int all) { + this.all = all; + this.some = 0; + this.subTypeData = EMPTY_SUBTYPE_DATA; + } + + protected SemType(SemType semType) { + this(semType.all(), semType.some(), semType.subTypeData()); + } + + public static SemType from(int all, int some, SubType[] subTypeData) { + return new PureSemType(all, some, subTypeData); + } + + public static SemType from(int all) { + return new PureSemType(all); + } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } + + public final int all() { + return all; + } + + public final int some() { + return some; + } + + public final SubType[] subTypeData() { + return subTypeData; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java similarity index 67% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java index 00f1f33facd6..45114379bcbc 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeHelper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java @@ -16,28 +16,28 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; -import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_BOOLEAN; -import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_B_TYPE; -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_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; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_BOOLEAN; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; +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_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; public final class SemTypeHelper { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java similarity index 94% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java index dbfbdcf68953..1109c42f048a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SemTypeWrapper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java @@ -16,7 +16,7 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.api.types.Type; 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 similarity index 53% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/SemType/SubType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java index 9b538a2a24a9..e8382dedae2b 100644 --- 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 @@ -16,32 +16,44 @@ * under the License. */ -package io.ballerina.runtime.api.types.SemType; +package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.internal.types.semtype.SubTypeData; /** - * Describe set of operation supported by each basic Type + * Describe set of operation supported by each basic Type. * * @since 2201.10.0 */ -public interface SubType { +public abstract class SubType { - SubType union(SubType other); + private final boolean all; + private final boolean nothing; - SubType intersect(SubType other); + protected SubType(boolean all, boolean nothing) { + this.all = all; + this.nothing = nothing; + } + + public abstract SubType union(SubType other); + + public abstract SubType intersect(SubType other); - default SubType diff(SubType other) { + public SubType diff(SubType other) { return this.intersect(other.complement()); } - SubType complement(); + public abstract SubType complement(); - boolean isEmpty(); + public abstract boolean isEmpty(); - boolean isAll(); + public final boolean isAll() { + return all; + } - boolean isNothing(); + public final boolean isNothing() { + return nothing; + } - SubTypeData data(); + public abstract SubTypeData data(); } 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/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 2c8cfbd730de..74271629aea4 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 @@ -26,12 +26,14 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ParameterizedType; -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.Type; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.types.XmlNodeType; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +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.values.BDecimal; import io.ballerina.runtime.api.values.BError; @@ -61,7 +63,6 @@ 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.BStringType; import io.ballerina.runtime.internal.types.BTableType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BType; @@ -101,7 +102,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiFunction; import java.util.stream.Collectors; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANY; @@ -133,6 +133,8 @@ 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.semtype.Core.B_TYPE_TOP; +import static io.ballerina.runtime.api.types.semtype.Core.SEMTYPE_TOP; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; import static io.ballerina.runtime.internal.CloneUtils.getErrorMessage; @@ -289,10 +291,25 @@ 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 isSubType(getType(sourceVal), targetType, - (sourceTy, targetTy) -> FallbackTypeChecker.checkIsType(null, sourceVal, sourceTy, targetTy)); + SemType targetSemType = Builder.from(targetType); + SemType targetBasicTypeUnion = Core.widenToBasicTypeUnion(targetSemType); + SemType valueBasicType = basicType(sourceVal); + if (!Core.isSubtypeSimple(valueBasicType, targetBasicTypeUnion)) { + return false; + } + if (targetBasicTypeUnion == targetSemType) { + return true; + } + SemType sourceSemType = Builder.from(getType(sourceVal)); + return switch (isSubTypeInner(sourceSemType, targetSemType)) { + case TRUE -> true; + case FALSE -> false; + case MAYBE -> FallbackTypeChecker.checkIsType(null, sourceVal, bTypePart(sourceSemType), + bTypePart(targetSemType)); + }; } + /** * Check whether a given value belongs to the given type. * @@ -303,8 +320,12 @@ 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) { - return isSubType(sourceType, targetType, - (sourceTy, targetTy) -> FallbackTypeChecker.checkIsType(errors, sourceVal, sourceTy, targetTy)); + return switch (isSubType(sourceType, targetType)) { + case TRUE -> true; + case FALSE -> false; + case MAYBE -> + FallbackTypeChecker.checkIsType(errors, sourceVal, bTypePart(sourceType), bTypePart(targetType)); + }; } /** @@ -346,15 +367,16 @@ public static Type getType(Object value) { if (value == null) { return TYPE_NULL; } else if (value instanceof Number number) { - if (value instanceof Long) { - return BIntegerType.singletonType(number.longValue()); - } else if (value instanceof Double) { + if (value instanceof Double) { return BFloatType.singletonType(number.doubleValue()); + } + long numberValue = + number instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : number.longValue(); + if (value instanceof Long) { + return BIntegerType.singletonType(numberValue); } else if (value instanceof Integer || value instanceof Byte) { - return BByteType.singletonType(number.intValue()); + return BByteType.singletonType(numberValue); } - } else if (value instanceof BString stringValue) { - return BStringType.singletonType(stringValue.getValue()); } else if (value instanceof Boolean booleanValue) { return BBooleanType.singletonType(booleanValue); } else if (value instanceof BObject bObject) { @@ -557,34 +579,88 @@ 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 isSubType(sourceType, targetType, - (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceBType, targetBType, null)); + return switch (isSubType(sourceType, targetType)) { + case TRUE -> true; + case FALSE -> false; + case MAYBE -> FallbackTypeChecker.checkIsType(bTypePart(sourceType), bTypePart(targetType), null); + }; } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - return isSubType(sourceType, targetType, - (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceBType, targetBType, - unresolvedTypes)); + return switch (isSubType(sourceType, targetType)) { + case TRUE -> true; + case FALSE -> false; + case MAYBE -> + FallbackTypeChecker.checkIsType(bTypePart(sourceType), bTypePart(targetType), unresolvedTypes); + }; } static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, List unresolvedTypes) { - return isSubType(sourceType, targetType, - (sourceBType, targetBType) -> FallbackTypeChecker.checkIsType(sourceVal, sourceBType, targetBType, - unresolvedTypes)); + return switch (isSubType(sourceType, targetType)) { + case TRUE -> true; + case FALSE -> false; + case MAYBE -> FallbackTypeChecker.checkIsType(sourceVal, bTypePart(sourceType), bTypePart(targetType), + unresolvedTypes); + }; } - private static boolean isSubType(Type t1, Type t2, BiFunction fallback) { - if (t1 instanceof ParameterizedType paramTy1) { - if (t2 instanceof ParameterizedType paramTy2) { - return isSubType(paramTy1.getParamValueType(), paramTy2.getParamValueType(), fallback); + // Private methods + + private enum TypeCheckResult { + TRUE, + FALSE, + MAYBE + } + + private static TypeCheckResult isSubType(Type source, Type target) { + if (source instanceof ParameterizedType sourceParamType) { + if (target instanceof ParameterizedType targetParamType) { + return isSubType(sourceParamType.getParamValueType(), targetParamType.getParamValueType()); } - return isSubType(paramTy1.getParamValueType(), t2, fallback); + return isSubType(sourceParamType.getParamValueType(), target); } - return Core.isSubType(cx, Builder.from(t1), Builder.from(t2), fallback); + return isSubTypeInner(Builder.from(source), Builder.from(target)); + } + + private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { + if (!Core.containsBasicType(source, B_TYPE_TOP)) { + return Core.isSubType(cx, source, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; + } + if (!Core.containsBasicType(target, B_TYPE_TOP)) { + return TypeCheckResult.FALSE; + } + SemType sourcePureSemType = Core.intersect(source, SEMTYPE_TOP); + SemType targetPureSemType = Core.intersect(target, SEMTYPE_TOP); + return Core.isSubType(cx, sourcePureSemType, targetPureSemType) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; + } + + private static SemType basicType(Object value) { + if (value == null) { + return Builder.nilType(); + } else if (value instanceof Double) { + return Builder.floatType(); + } else if (value instanceof Number) { + return Builder.intType(); + } else if (value instanceof BString) { + return Builder.stringType(); + } else if (value instanceof Boolean) { + return Builder.booleanType(); + } else if (value instanceof DecimalValue) { + return Builder.decimalType(); + } else { + return Builder.bType(); + } + } + + private static BType bTypePart(Type t) { + return bTypePart(Builder.from(t)); + } + + private static BType bTypePart(SemType t) { + return (BType) Core.subTypeData(t, BasicTypeCode.BT_B_TYPE); } - // Private methods private static boolean checkTypeDescType(Type sourceType, BTypedescType targetType, List unresolvedTypes) { 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 06cedc3be032..fec3cc535296 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 @@ -24,8 +24,8 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.AnyType; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; import java.util.Optional; 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 2adb9de874c6..1c03e4159802 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 @@ -22,24 +22,24 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.BooleanType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; - -import java.util.List; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; /** * {@code BBooleanType} represents boolean type in Ballerina. * * @since 0.995.0 */ -public class BBooleanType extends BType implements BooleanType, SemType { +public final class BBooleanType extends BSemTypeWrapper implements BooleanType { + + protected final String typeName; - private final SemType semType; - private final static BBooleanType TRUE = - new BBooleanType(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(true)); - private final static BBooleanType FALSE = - new BBooleanType(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(false)); + private static final BBooleanType TRUE = + new BBooleanType(new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + Builder.booleanConst(true)); + private static final BBooleanType FALSE = + new BBooleanType(new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + Builder.booleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. @@ -47,57 +47,44 @@ public class BBooleanType extends BType implements BooleanType, SemType { * @param typeName string name of the type */ public BBooleanType(String typeName, Module pkg) { - this(typeName, pkg, Builder.booleanType()); + this(new BBooleanTypeImpl(typeName, pkg), Builder.booleanType()); } public static BBooleanType singletonType(boolean value) { return value ? TRUE : FALSE; } - private BBooleanType(String typeName, Module pkg, SemType semType) { - super(typeName, pkg, Boolean.class); - this.semType = semType; + private BBooleanType(BBooleanTypeImpl bType, SemType semType) { + super(bType, semType); + this.typeName = bType.typeName; } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) Boolean.FALSE; - } + private static final class BBooleanTypeImpl extends BType implements BooleanType { - @SuppressWarnings("unchecked") - @Override - public V getEmptyValue() { - return (V) Boolean.FALSE; - } + private BBooleanTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Boolean.class); + } - @Override - public int getTag() { - return TypeTags.BOOLEAN_TAG; - } + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) Boolean.FALSE; + } - @Override - public boolean isReadOnly() { - return true; - } + @SuppressWarnings("unchecked") + @Override + public V getEmptyValue() { + return (V) Boolean.FALSE; + } - @Override - SemType createSemType() { - return semType; - } - - @Override - public int all() { - return get().all(); - } - - @Override - public int some() { - return get().some(); - } + @Override + public int getTag() { + return TypeTags.BOOLEAN_TAG; + } - @Override - public List subTypeData() { - return get().subTypeData(); + @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 8b271ae05f98..2a798c123b7a 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 @@ -22,11 +22,8 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.ByteType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; - -import java.util.List; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; @@ -36,67 +33,67 @@ * * @since 0.995.0 */ -public class BByteType extends BType implements ByteType, SemType { +public final class BByteType extends BSemTypeWrapper implements ByteType { + + protected final String typeName; + private static final BByteTypeImpl DEFAULT_B_TYPE = new BByteTypeImpl(TypeConstants.BYTE_TNAME, EMPTY_MODULE); - private final SemType semType; /** * Create a {@code BByteType} which represents the byte type. * * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { - this(typeName, pkg, Builder.intRange(0, UNSIGNED8_MAX_VALUE)); - } - - private BByteType(String typeName, Module pkg, SemType semType) { - super(typeName, pkg, Integer.class); - this.semType = semType; - } - - // Java Byte is signed, so we need use int to avoid overflow - public static BByteType singletonType(Integer value) { - return new BByteType(TypeConstants.BYTE_TNAME, EMPTY_MODULE, Builder.intConst(value)); - } - - @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 - SemType createSemType() { - return semType; + this(new BByteTypeImpl(typeName, pkg), Builder.intRange(0, UNSIGNED8_MAX_VALUE)); } - @Override - public int all() { - return get().all(); + private BByteType(BByteTypeImpl bType, SemType semType) { + super(bType, semType); + this.typeName = bType.typeName; } - @Override - public int some() { - return get().some(); + public static BByteType singletonType(long value) { + try { + return new BByteType((BByteTypeImpl) DEFAULT_B_TYPE.clone(), Builder.intConst(value)); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } } - @Override - public List subTypeData() { - return get().subTypeData(); + private 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 + protected Object clone() throws CloneNotSupportedException { + BType bType = (BType) super.clone(); + bType.setCachedImpliedType(null); + bType.setCachedReferredType(null); + return bType; + } } } 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 c31944a6a4e6..fd63979fd02a 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 @@ -22,13 +22,11 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.DecimalType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; -import java.util.List; import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; @@ -38,66 +36,68 @@ * * @since 0.995.0 */ -public class BDecimalType extends BType implements DecimalType, SemType { +public final class BDecimalType extends BSemTypeWrapper implements DecimalType { + + protected final String typeName; + private static final BDecimalTypeImpl DEFAULT_B_TYPE = + new BDecimalTypeImpl(TypeConstants.DECIMAL_TNAME, EMPTY_MODULE); /** * Create a {@code BDecimalType} which represents the decimal type. * * @param typeName string name of the type */ - private final SemType semType; public BDecimalType(String typeName, Module pkg) { - this(typeName, pkg, Builder.decimalType()); + this(new BDecimalTypeImpl(typeName, pkg), Builder.decimalType()); } public static BDecimalType singletonType(BigDecimal value) { - return new BDecimalType(TypeConstants.DECIMAL_TNAME, EMPTY_MODULE, Builder.decimalConst(value)); - } - - private BDecimalType(String typeName, Module pkg, SemType semType) { - super(typeName, pkg, DecimalValue.class); - this.semType = semType; - } - - @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 - SemType createSemType() { - return semType; - } - - @Override - public int all() { - return get().all(); + try { + return new BDecimalType((BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), Builder.decimalConst(value)); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } } - @Override - public int some() { - return get().some(); + private BDecimalType(BDecimalTypeImpl bType, SemType semType) { + super(bType, semType); + this.typeName = bType.typeName; } - @Override - public List subTypeData() { - return get().subTypeData(); + private 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 + protected Object clone() throws CloneNotSupportedException { + BType bType = (BType) super.clone(); + bType.setCachedImpliedType(null); + bType.setCachedReferredType(null); + return bType; + } } } 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 e138271e0520..8d423840a11d 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,7 +21,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; -import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.RefValue; 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 553263420799..7b00fba8ba8e 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 @@ -21,11 +21,8 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.FloatType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; - -import java.util.List; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; @@ -36,65 +33,52 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BFloatType extends BType implements FloatType, SemType { +public class BFloatType extends BSemTypeWrapper implements FloatType { + + protected final String typeName; /** * Create a {@code BFloatType} which represents the boolean type. * * @param typeName string name of the type */ - private final SemType semType; - public BFloatType(String typeName, Module pkg) { - this(typeName, pkg, Builder.floatType()); + this(new BFloatTypeImpl(typeName, pkg), Builder.floatType()); } - private BFloatType(String typeName, Module pkg, SemType semType) { - super(typeName, pkg, Double.class); - this.semType = semType; - } - - @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; + private BFloatType(BFloatTypeImpl bType, SemType semType) { + super(bType, semType); + typeName = bType.typeName; } public static BFloatType singletonType(Double value) { - return new BFloatType(TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.floatConst(value)); + return new BFloatType(new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), Builder.floatConst(value)); } - @Override - public boolean isReadOnly() { - return true; - } + private static final class BFloatTypeImpl extends BType implements FloatType { - @Override - public int all() { - return get().all(); - } + private BFloatTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Double.class); + } - @Override - public int some() { - return get().some(); - } + @Override + public V getZeroValue() { + return (V) new Double(0); + } - @Override - public List subTypeData() { - return get().subTypeData(); - } + @Override + public V getEmptyValue() { + return (V) new Double(0); + } + + @Override + public int getTag() { + return TypeTags.FLOAT_TAG; + } - @Override - SemType createSemType() { - return semType; + @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 12338f8e2c50..cf7deece59fa 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,20 +1,20 @@ /* -* 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; @@ -22,11 +22,8 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntegerType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; - -import java.util.List; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MIN_VALUE; @@ -44,10 +41,11 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BIntegerType extends BType implements IntegerType, SemType { +public final class BIntegerType extends BSemTypeWrapper implements IntegerType { - private final int tag; - private final SemType semType; + protected final String typeName; + private static final BIntegerTypeImpl DEFAULT_B_TYPE = + new BIntegerTypeImpl(TypeConstants.INT_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.INT_TAG); /** * Create a {@code BIntegerType} which represents the boolean type. @@ -55,42 +53,16 @@ public class BIntegerType extends BType implements IntegerType, SemType { * @param typeName string name of the type */ public BIntegerType(String typeName, Module pkg) { - this(typeName, pkg, TypeTags.INT_TAG, pickSemType(TypeTags.INT_TAG)); + this(new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), Builder.intType()); } public BIntegerType(String typeName, Module pkg, int tag) { - this(typeName, pkg, tag, pickSemType(tag)); - } - - private BIntegerType(String typeName, Module pkg, int tag, SemType semType) { - super(typeName, pkg, Long.class); - this.tag = tag; - this.semType = semType; - } - - public static BIntegerType singletonType(Long value) { - return new BIntegerType(TypeConstants.INT_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.INT_TAG, - Builder.intConst(value)); - } - - @Override - public V getZeroValue() { - return (V) new Long(0); - } - - @Override - public V getEmptyValue() { - return (V) new Long(0); - } - - @Override - public int getTag() { - return tag; + this(new BIntegerTypeImpl(typeName, pkg, tag), pickSemType(tag)); } - @Override - public boolean isReadOnly() { - return true; + private BIntegerType(BIntegerTypeImpl bType, SemType semType) { + super(bType, semType); + typeName = bType.typeName; } private static SemType pickSemType(int tag) { @@ -106,23 +78,69 @@ private static SemType pickSemType(int tag) { }; } - @Override - SemType createSemType() { - return semType; + 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 int all() { - return get().all(); + private static BIntegerType createSingletonType(long value) { + try { + return new BIntegerType((BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), Builder.intConst(value)); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } } - @Override - public int some() { - return get().some(); + private 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 + protected Object clone() throws CloneNotSupportedException { + BType bType = (BType) super.clone(); + bType.setCachedImpliedType(null); + bType.setCachedReferredType(null); + return bType; + } } - @Override - public List subTypeData() { - return get().subTypeData(); + 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 f91f1743adb3..d71bf3318fa9 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 @@ -23,8 +23,8 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectableReferenceType; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.ArrayList; import java.util.Arrays; @@ -219,7 +219,10 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override SemType createSemType() { - BType effectiveType = (BType) getEffectiveType(); - return effectiveType.createSemType(); + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof SemType semType) { + return semType; + } + return ((BType) effectiveType).createSemType(); } } 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 6963f8f431d8..2667619f6887 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,25 +21,21 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.NeverType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; - -import java.util.List; +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, SemType { +public 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.neverType()); } @Override @@ -51,24 +47,4 @@ public boolean isAnydata() { public int getTag() { return TypeTags.NEVER_TAG; } - - @Override - SemType createSemType() { - return Builder.neverType(); - } - - @Override - public int all() { - return 0; - } - - @Override - public int some() { - return 0; - } - - @Override - public List subTypeData() { - return List.of(); - } } 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 d8d168f3c1a0..0235a9cdd285 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,18 +20,17 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.NullType; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; - -import java.util.List; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; /** * {@code BNullType} represents the type of a {@code NullLiteral}. * * @since 0.995.0 */ -public class BNullType extends BType implements NullType, SemType { +public class BNullType extends BSemTypeWrapper implements NullType { + + protected final String typeName; /** * Create a {@code BNullType} represents the type of a {@code NullLiteral}. @@ -40,51 +39,52 @@ public class BNullType extends BType implements NullType, SemType { * @param pkg package path */ public BNullType(String typeName, Module pkg) { - super(typeName, pkg, null); + this(new BNullTypeImpl(typeName, pkg), Builder.nilType()); } - @Override - public V getZeroValue() { - return null; + BNullType(String typeName, Module pkg, SemType semType) { + this(new BNullTypeImpl(typeName, pkg), semType); } - @Override - public V getEmptyValue() { - return null; + private BNullType(BNullTypeImpl bNullType, SemType semType) { + super(bNullType, semType); + this.typeName = bNullType.typeName; } - @Override - public int getTag() { - return TypeTags.NULL_TAG; - } + private 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 boolean isReadOnly() { - return true; - } + @Override + public V getZeroValue() { + return null; + } - @Override - SemType createSemType() { - return Builder.nilType(); - } + @Override + public V getEmptyValue() { + return null; + } - @Override - public int all() { - return get().all(); - } + @Override + public int getTag() { + return TypeTags.NULL_TAG; + } - @Override - public int some() { - return get().some(); - } + @Override + public boolean isNilable() { + return true; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public List subTypeData() { - return get().subTypeData(); + @Override + SemType createSemType() { + return Builder.nilType(); + } } } 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 0d84acaea71a..858545bf89c1 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,7 +20,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ReadonlyType; -import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; /** 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 c5d5bffd1797..73f7c2018ed8 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 @@ -27,8 +27,8 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.RecordType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BFunctionPointer; import io.ballerina.runtime.api.values.BMap; 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..9478e9869c91 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java @@ -0,0 +1,155 @@ +/* + * 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.SemType; + +public non-sealed class BSemTypeWrapper extends SemType implements Type { + + private final BType bType; + + BSemTypeWrapper(BType bType, SemType semType) { + super(semType); + this.bType = bType; + } + + public Class getValueClass() { + return bType.getValueClass(); + } + + public V getZeroValue() { + return bType.getZeroValue(); + } + + @Override + public V getEmptyValue() { + return bType.getEmptyValue(); + } + + @Override + public int getTag() { + return bType.getTag(); + } + + @Override + public String toString() { + return bType.toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BSemTypeWrapper other)) { + return false; + } + return bType.equals(other.bType); + } + + @Override + public boolean isNilable() { + return bType.isNilable(); + } + + @Override + public int hashCode() { + return bType.hashCode(); + } + + @Override + public String getName() { + return bType.getName(); + } + + @Override + public String getQualifiedName() { + return bType.getQualifiedName(); + } + + @Override + public Module getPackage() { + return bType.getPackage(); + } + + @Override + public boolean isPublic() { + return bType.isPublic(); + } + + @Override + public boolean isNative() { + return bType.isNative(); + } + + @Override + public boolean isAnydata() { + return bType.isAnydata(); + } + + @Override + public boolean isPureType() { + return bType.isPureType(); + } + + @Override + public boolean isReadOnly() { + return bType.isReadOnly(); + } + + @Override + public Type getImmutableType() { + return bType.getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + bType.setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return bType.getPkg(); + } + + @Override + public long getFlags() { + return bType.getFlags(); + } + + @Override + public void setCachedReferredType(Type type) { + bType.setCachedReferredType(type); + } + + @Override + public Type getCachedReferredType() { + return bType.getCachedReferredType(); + } + + @Override + public void setCachedImpliedType(Type type) { + bType.setCachedImpliedType(type); + } + + @Override + public Type getCachedImpliedType() { + return bType.getCachedImpliedType(); + } +} 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 627aedfec30e..9d90256510c6 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 @@ -1,33 +1,29 @@ /* -* 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.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.RuntimeConstants; import io.ballerina.runtime.api.constants.TypeConstants; -import io.ballerina.runtime.api.types.SemType.Builder; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SubType; import io.ballerina.runtime.api.types.StringType; - -import java.util.List; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; /** * {@code BStringType} represents a String type in ballerina. @@ -35,10 +31,13 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BStringType extends BType implements StringType, SemType { +public final class BStringType extends BSemTypeWrapper implements StringType { - private final int tag; - private final SemType semType; + protected final String typeName; + // We are creating separate empty module instead of reusing PredefinedTypes.EMPTY_MODULE to avoid cyclic + // dependencies. + private static final BStringTypeImpl DEFAULT_B_TYPE = + new BStringTypeImpl(TypeConstants.STRING_TNAME, new Module(null, null, null), TypeTags.STRING_TAG); /** * Create a {@code BStringType} which represents the boolean type. @@ -46,22 +45,24 @@ public class BStringType extends BType implements StringType, SemType { * @param typeName string name of the type */ public BStringType(String typeName, Module pkg) { - this(typeName, pkg, TypeTags.STRING_TAG, Builder.stringType()); + this(new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), Builder.stringType()); } public BStringType(String typeName, Module pkg, int tag) { - this(typeName, pkg, tag, pickSemtype(tag)); + this(new BStringTypeImpl(typeName, pkg, tag), pickSemtype(tag)); } - private BStringType(String typeName, Module pkg, int tag, SemType semType) { - super(typeName, pkg, String.class); - this.tag = tag; - this.semType = semType; + private BStringType(BStringTypeImpl bType, SemType semType) { + super(bType, semType); + this.typeName = bType.typeName; } public static BStringType singletonType(String value) { - return new BStringType(TypeConstants.STRING_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.STRING_TAG, - Builder.stringConst(value)); + try { + return new BStringType((BStringTypeImpl) DEFAULT_B_TYPE.clone(), Builder.stringConst(value)); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } } private static SemType pickSemtype(int tag) { @@ -72,43 +73,41 @@ private static SemType pickSemtype(int 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 - SemType createSemType() { - return semType; - } - - @Override - public int all() { - return get().all(); - } - - @Override - public int some() { - return get().some(); - } - - @Override - public List subTypeData() { - return get().subTypeData(); + private 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 + protected Object clone() throws CloneNotSupportedException { + BType bType = (BType) super.clone(); + bType.setCachedImpliedType(null); + bType.setCachedReferredType(null); + return bType; + } } } 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 cf499122dd45..98d6d43852ee 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 @@ -22,9 +22,9 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TupleValueImpl; 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 2860a0596fbb..d83217ef0ad6 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 @@ -21,8 +21,8 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.SubTypeData; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index c028a24ee14e..1955bd045f8b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -21,11 +21,11 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.ReferenceType; -import io.ballerina.runtime.api.types.SemType.BasicTypeCode; -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.Type; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +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.values.BString; import io.ballerina.runtime.internal.types.semtype.BSubType; import io.ballerina.runtime.internal.values.DecimalValue; @@ -205,13 +205,5 @@ private static BTypeParts splitUnion(BUnionType unionType) { private static boolean isSemType(Type type) { return type instanceof SemType; -// // FIXME: can't we replace this with instanceof check? -// return switch (type.getTag()) { -// case TypeTags.NEVER_TAG, TypeTags.NULL_TAG, TypeTags.DECIMAL_TAG, TypeTags.FLOAT_TAG, -// TypeTags.BOOLEAN_TAG, TypeTags.INT_TAG, TypeTags.BYTE_TAG, -// TypeTags.SIGNED8_INT_TAG, TypeTags.SIGNED16_INT_TAG, TypeTags.SIGNED32_INT_TAG, -// TypeTags.UNSIGNED8_INT_TAG, TypeTags.UNSIGNED16_INT_TAG, TypeTags.UNSIGNED32_INT_TAG -> true; -// default -> 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 75ee0e3b1a8e..15671738e5d9 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 @@ -24,8 +24,8 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectableReferenceType; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Objects; import java.util.Optional; @@ -130,7 +130,10 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override SemType createSemType() { - BType referredType = (BType) getReferredType(); - return referredType.createSemType(); + Type referredType = getReferredType(); + if (referredType instanceof SemType semType) { + return semType; + } + return ((BType) referredType).createSemType(); } } 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 f6ef5ab9c331..dff531ac84a9 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 @@ -23,9 +23,9 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.ReadOnlyUtils; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java deleted file mode 100644 index 1960ff69441b..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBasicTypeBitSet.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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.Module; -import io.ballerina.runtime.api.types.BasicTypeBitSet; -import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.BasicTypeCode; -import io.ballerina.runtime.api.types.SemType.SemTypeHelper; -import io.ballerina.runtime.api.types.Type; - -import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_B_TYPE; -import static io.ballerina.runtime.api.types.SemType.BasicTypeCode.CODE_NIL; - -public final class BBasicTypeBitSet implements BasicTypeBitSet { - - private final int all; - private final BTypeAdapter adapter; - - private BBasicTypeBitSet(int all) { - this.all = all; - this.adapter = new BTypeAdapter(this); - } - - public static BasicTypeBitSet from(int all) { - int index = BBasicTypeBitSetCache.cacheIndex(all); - if (index != BBasicTypeBitSetCache.NotFound) { - return BBasicTypeBitSetCache.cache[index]; - } - return new BBasicTypeBitSet(all); - } - - @Override - public V getZeroValue() { - return adapter.getZeroValue(); - } - - @Override - public V getEmptyValue() { - return adapter.getEmptyValue(); - } - - @Override - public int getTag() { - return adapter.getTag(); - } - - @Override - public boolean isNilable() { - return adapter.isNilable(); - } - - @Override - public String getName() { - return adapter.getName(); - } - - @Override - public String getQualifiedName() { - return adapter.getQualifiedName(); - } - - @Override - public Module getPackage() { - return adapter.getPackage(); - } - - @Override - public boolean isPublic() { - return adapter.isPublic(); - } - - @Override - public boolean isNative() { - return adapter.isNative(); - } - - @Override - public boolean isAnydata() { - return adapter.isAnydata(); - } - - @Override - public boolean isPureType() { - return adapter.isPureType(); - } - - @Override - public boolean isReadOnly() { - return adapter.isReadOnly(); - } - - @Override - public long getFlags() { - return adapter.getFlags(); - } - - @Override - public Type getImmutableType() { - return adapter.getImmutableType(); - } - - @Override - public void setImmutableType(IntersectionType immutableType) { - adapter.setImmutableType(immutableType); - } - - @Override - public Module getPkg() { - return adapter.getPkg(); - } - - @Override - public int all() { - return all; - } - - @Override - public String toString() { - return SemTypeHelper.stringRepr(this); - } - - // TODO: see if we can use Application CDS to make this even faster - private final static class BBasicTypeBitSetCache { - - private static final BBasicTypeBitSet[] cache; - private static final int NotFound = -1; - static { - cache = new BBasicTypeBitSet[BasicTypeCode.CODE_B_TYPE + 2]; - for (int i = CODE_NIL; i < CODE_B_TYPE + 1; i++) { - cache[i] = new BBasicTypeBitSet(1 << i); - } - } - - private static int cacheIndex(int all) { - // TODO: check this is getting unrolled, otherwise use a switch - for (int i = CODE_NIL; i < CODE_B_TYPE + 1; i++) { - if (all == (1 << i)) { - return i; - } - } - return NotFound; - } - } -} \ No newline at end of file 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 index 725717e1d7dd..ad0b9e5d227e 100644 --- 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 @@ -18,17 +18,18 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; -public final class BBooleanSubType implements SubType { +public final class BBooleanSubType extends SubType { private final BBooleanSubTypeData data; - private final static BBooleanSubType ALL = new BBooleanSubType(BBooleanSubTypeData.ALL); - private final static BBooleanSubType NOTHING = new BBooleanSubType(BBooleanSubTypeData.NOTHING); - private final static BBooleanSubType TRUE = new BBooleanSubType(BBooleanSubTypeData.TRUE); - private final static BBooleanSubType FALSE = new BBooleanSubType(BBooleanSubTypeData.FALSE); + 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; } @@ -118,16 +119,6 @@ public boolean isEmpty() { return data.isNothing(); } - @Override - public boolean isAll() { - return data.isAll(); - } - - @Override - public boolean isNothing() { - return data.isNothing(); - } - @Override public SubTypeData data() { return data.toData(); @@ -135,10 +126,10 @@ public SubTypeData data() { private record BBooleanSubTypeData(boolean isAll, boolean isNothing, boolean value) { - static final BBooleanSubTypeData ALL = new BBooleanSubTypeData(true, false, false); - static final BBooleanSubTypeData NOTHING = new BBooleanSubTypeData(false, true, false); - static final BBooleanSubTypeData TRUE = new BBooleanSubTypeData(false, false, true); - static final BBooleanSubTypeData FALSE = new BBooleanSubTypeData(false, false, false); + 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); static BBooleanSubTypeData from(boolean value) { return value ? TRUE : FALSE; 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 index bcc71a04d80b..6bdbd4839039 100644 --- 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 @@ -19,7 +19,7 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; import java.math.BigDecimal; import java.util.ArrayList; @@ -31,11 +31,12 @@ * * @since 2201.10.0 */ -public final class BDecimalSubType implements SubType { +public final class BDecimalSubType extends SubType { final SubTypeData data; private BDecimalSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); this.data = data; } @@ -116,16 +117,6 @@ public boolean isEmpty() { return data == AllOrNothing.NOTHING; } - @Override - public boolean isAll() { - return data == AllOrNothing.ALL; - } - - @Override - public boolean isNothing() { - return data == AllOrNothing.NOTHING; - } - @Override public SubTypeData data() { return data; 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 index d1144438d313..5b186eb4d59f 100644 --- 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 @@ -19,7 +19,7 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; import java.util.ArrayList; import java.util.Arrays; @@ -30,11 +30,12 @@ * * @since 2201.10.0 */ -public final class BFloatSubType implements SubType { +public final class BFloatSubType extends SubType { final SubTypeData data; private BFloatSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); this.data = data; } @@ -115,16 +116,6 @@ public boolean isEmpty() { return data == AllOrNothing.NOTHING; } - @Override - public boolean isAll() { - return data == AllOrNothing.ALL; - } - - @Override - public boolean isNothing() { - return data == AllOrNothing.NOTHING; - } - @Override public SubTypeData data() { return data; 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 index 0f10c8930b1b..429751c6f244 100644 --- 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 @@ -18,7 +18,7 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; import java.util.ArrayList; import java.util.Collections; @@ -32,11 +32,12 @@ * * @since 2201.10.0 */ -public final class BIntSubType implements SubType { +public final class BIntSubType extends SubType { final SubTypeData data; private BIntSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); this.data = data; } @@ -136,16 +137,6 @@ public boolean isEmpty() { return data == AllOrNothing.NOTHING; } - @Override - public boolean isAll() { - return data == AllOrNothing.ALL; - } - - @Override - public boolean isNothing() { - return data == AllOrNothing.NOTHING; - } - @Override public SubTypeData data() { return data; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java deleted file mode 100644 index b5b7e2db6fc0..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemType.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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.Module; -import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SemTypeHelper; -import io.ballerina.runtime.api.types.SemType.SubType; -import io.ballerina.runtime.api.types.Type; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class BSemType implements SemType { - - private final int all; - private final int some; - // TODO: subTypeData is a sparse list to make iteration simple, when we have an efficient implementation fix this - private final List subTypeData; - private final BTypeAdapter adapter; - - private BSemType(int all, int some, List subTypeData) { - this.all = all; - this.some = some; - this.subTypeData = toSparseList(some, subTypeData); - adapter = new BTypeAdapter(this); - } - - private static List toSparseList(int some, List subTypeData) { - if (some == 0) { - return List.of(); - } - List sparse = new ArrayList<>(subTypeData.size()); - int index = 0; - int code = 0; - while (index < subTypeData.size()) { - if ((some & (1 << code)) != 0) { - sparse.add(subTypeData.get(index)); - index++; - } else { - sparse.add(null); - } - code++; - } - return sparse; - } - - public static SemType from(int all, int some, List subTypeData) { - if (some == 0) { - return BBasicTypeBitSet.from(all); - } - return new BSemType(all, some, subTypeData); - } - - @Override - public V getZeroValue() { - return adapter.getZeroValue(); - } - - @Override - public V getEmptyValue() { - return adapter.getEmptyValue(); - } - - @Override - public int getTag() { - return adapter.getTag(); - } - - @Override - public boolean isNilable() { - return adapter.isNilable(); - } - - @Override - public String getName() { - return adapter.getName(); - } - - @Override - public String getQualifiedName() { - return adapter.getQualifiedName(); - } - - @Override - public Module getPackage() { - return adapter.getPackage(); - } - - @Override - public boolean isPublic() { - return adapter.isPublic(); - } - - @Override - public boolean isNative() { - return adapter.isNative(); - } - - @Override - public boolean isAnydata() { - return adapter.isAnydata(); - } - - @Override - public boolean isPureType() { - return adapter.isPureType(); - } - - @Override - public boolean isReadOnly() { - return adapter.isReadOnly(); - } - - @Override - public long getFlags() { - return adapter.getFlags(); - } - - @Override - public Type getImmutableType() { - return adapter.getImmutableType(); - } - - @Override - public void setImmutableType(IntersectionType immutableType) { - adapter.setImmutableType(immutableType); - } - - @Override - public Module getPkg() { - return adapter.getPkg(); - } - - @Override - public int all() { - return all; - } - - @Override - public int some() { - return some; - } - - @Override - public List subTypeData() { - return Collections.unmodifiableList(subTypeData); - } - - @Override - public String toString() { - return SemTypeHelper.stringRepr(this); - } -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java deleted file mode 100644 index a0d9ca48277b..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSemTypeWithIdentity.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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.Module; -import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; -import io.ballerina.runtime.api.types.SemType.SemTypeHelper; -import io.ballerina.runtime.api.types.SemType.SubType; -import io.ballerina.runtime.api.types.Type; - -import java.util.List; - -public final class BSemTypeWithIdentity implements SemType { - - private final SemType ty; - final TypeMetadata metadata; - private final BTypeAdapter adapter; - - private BSemTypeWithIdentity(SemType ty, TypeMetadata metadata) { - this.ty = ty; - this.metadata = metadata; - adapter = new BTypeAdapter(this); - } - - public static BSemTypeWithIdentity from(int all, int some, List subTypeData, TypeMetadata metadata) { - return new BSemTypeWithIdentity(BSemType.from(all, some, subTypeData), metadata); - } - - public static BSemTypeWithIdentity from(int all, int some, List subTypeData) { - return new BSemTypeWithIdentity(BSemType.from(all, some, subTypeData), TypeMetadata.empty()); - } - - @Override - public V getZeroValue() { - return adapter.getZeroValue(); - } - - @Override - public V getEmptyValue() { - return adapter.getEmptyValue(); - } - - @Override - public int getTag() { - return adapter.getTag(); - } - - @Override - public boolean isNilable() { - return adapter.isNilable(); - } - - @Override - public String getName() { - return adapter.getName(); - } - - @Override - public String getQualifiedName() { - return adapter.getQualifiedName(); - } - - @Override - public Module getPackage() { - return adapter.getPackage(); - } - - @Override - public boolean isPublic() { - return adapter.isPublic(); - } - - @Override - public boolean isNative() { - return adapter.isNative(); - } - - @Override - public boolean isAnydata() { - return adapter.isAnydata(); - } - - @Override - public boolean isPureType() { - return adapter.isPureType(); - } - - @Override - public boolean isReadOnly() { - return adapter.isReadOnly(); - } - - @Override - public long getFlags() { - return adapter.getFlags(); - } - - @Override - public Type getImmutableType() { - return adapter.getImmutableType(); - } - - @Override - public void setImmutableType(IntersectionType immutableType) { - adapter.setImmutableType(immutableType); - } - - @Override - public Module getPkg() { - return adapter.getPkg(); - } - - @Override - public int all() { - return ty.all(); - } - - @Override - public int some() { - return ty.some(); - } - - @Override - public List subTypeData() { - return ty.subTypeData(); - } - - @Override - public String toString() { - return SemTypeHelper.stringRepr(this); - } -} 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 index df8be56cd870..b438ad50ba32 100644 --- 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 @@ -18,7 +18,7 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; import java.util.ArrayList; import java.util.Arrays; @@ -29,13 +29,14 @@ * * @since 2201.10.0 */ -public final class BStringSubType implements SubType { +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; } @@ -156,16 +157,6 @@ public boolean isEmpty() { return data == AllOrNothing.NOTHING; } - @Override - public boolean isAll() { - return data == AllOrNothing.ALL; - } - - @Override - public boolean isNothing() { - return data == AllOrNothing.NOTHING; - } - @Override public SubTypeData data() { return data; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java index 2a0c06d7629e..70d2d18de928 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java @@ -18,14 +18,15 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.internal.types.BType; -public class BSubType implements SubType { +public class BSubType extends SubType { private final BType data; private BSubType(BType innerType) { + super(false, false); data = innerType; } @@ -33,6 +34,8 @@ public static BSubType wrap(BType innerType) { return new BSubType(innerType); } + // NOTE: we are allowing isAll() and isNothing() (from the parent) so we can get the union of PureSemTypes and + // PureBTypes. All other operations are unsupported for BSubType @Override public SubType union(SubType other) { throw new IllegalArgumentException("BSubType don't support semType operations"); @@ -58,17 +61,6 @@ public boolean isEmpty() { throw new IllegalArgumentException("BSubType don't support semType operations"); } - // NOTE: we are allowing isAll() and isNothing() so we can get the union of PureSemTypes and PureBTypes - @Override - public boolean isAll() { - return false; - } - - @Override - public boolean isNothing() { - return false; - } - @Override public SubTypeData data() { return data; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubTypeWrapper.java deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java index 68697d33b1e2..e29e6d5bf31b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java @@ -20,8 +20,8 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.SemType.SemType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; // All the logic for supporting various Type operations on SemTypes is defined here final class BTypeAdapter implements Type { @@ -65,9 +65,6 @@ public String getQualifiedName() { @Override public Module getPackage() { - if (semType instanceof BSemTypeWithIdentity ty) { - return ty.metadata.pkg; - } throw new IllegalStateException("semtype without identity"); } @@ -113,9 +110,6 @@ public void setImmutableType(IntersectionType immutableType) { @Override public Module getPkg() { - if (semType instanceof BSemTypeWithIdentity ty) { - return ty.metadata.pkg; - } throw new IllegalStateException("semtype without identity"); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/BasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java similarity index 64% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/BasicTypeBitSet.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index 8b057e341ddf..a679cf686953 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/BasicTypeBitSet.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -16,22 +16,18 @@ * under the License. */ -package io.ballerina.runtime.api.types; +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.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; -import java.util.List; +public final class PureSemType extends SemType { -public interface BasicTypeBitSet extends SemType { - - @Override - default int some() { - return 0; + public PureSemType(int all, int some, SubType[] subTypeData) { + super(all, some, subTypeData); } - @Override - default List subTypeData() { - return List.of(); + public PureSemType(int all) { + super(all); } } 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 index 8e788c2299c1..71ac6c9b31c4 100644 --- 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 @@ -18,8 +18,13 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SubType; +import io.ballerina.runtime.api.types.semtype.SubType; 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 index 16409b085bf2..0a1811155725 100644 --- 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 @@ -18,21 +18,21 @@ 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.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; import java.util.Iterator; public final class SubtypePairIterator implements Iterator { private int index = 0; - private final int maxIndex; + private static final int maxIndex = BasicTypeCode.CODE_B_TYPE + 1; private final int bits; private final SemType t1; private final SemType t2; SubtypePairIterator(SemType t1, SemType t2, int bits) { - maxIndex = Integer.max(t1.subTypeData().size(), t2.subTypeData().size()); this.bits = bits; this.t1 = t1; this.t2 = t2; @@ -45,14 +45,14 @@ public boolean hasNext() { } private void incrementIndex() { - while (index < maxIndex && (bits & (1 << index)) == 0) { - index++; - } + int rest = bits >> index; + int offset = Integer.numberOfTrailingZeros(rest); + index += offset; } private SubType subTypeAtIndex(SemType t, int index) { if ((t.some() & (1 << index)) != 0) { - return t.subTypeData().get(index); + return t.subTypeData()[index]; } return null; } 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 index ab2303ddb757..efa69c79b1fa 100644 --- 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 @@ -18,7 +18,7 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.SemType.SemType; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Iterator; 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 e983854997ef..4544d8a36463 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 @@ -614,15 +614,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 +632,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 +659,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 +689,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 +1257,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); } 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 a96fcacd5a07..160cb9e69072 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,10 +17,10 @@ */ package io.ballerina.runtime.internal.values; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.types.Type; 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; @@ -33,15 +33,17 @@ public abstract class StringValue implements BString, SimpleValue { final String value; final boolean isNonBmp; + private final Type type; protected StringValue(String value, boolean isNonBmp) { this.value = value; this.isNonBmp = isNonBmp; + this.type = BStringType.singletonType(value); } @Override public Type getType() { - return PredefinedTypes.TYPE_STRING; + return type; } @Override diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index cdf38ae7701f..a36819d6400e 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -73,5 +73,5 @@ exports io.ballerina.runtime.internal.configurable.providers to org.ballerinalang.debugadapter.runtime; exports io.ballerina.runtime.internal.types; exports io.ballerina.runtime.internal.types.semtype; - exports io.ballerina.runtime.api.types.SemType; + exports io.ballerina.runtime.api.types.semtype; } diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java deleted file mode 100644 index 0da6bbe5fafc..000000000000 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BBooleanSubTypeTests.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.internal.types.semtype; - -import io.ballerina.runtime.api.types.SemType.SubType; -import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class BBooleanSubTypeTests { - - private static final BBooleanSubType TRUE = BBooleanSubType.from(true); - private static final BBooleanSubType FALSE = BBooleanSubType.from(false); - - @Test - public static void testSimpleUnion() { - SubType res = TRUE.union(FALSE); - Assert.assertTrue(res.isAll()); - Assert.assertFalse(res.isNothing()); - Assert.assertFalse(res.isNothing()); - - res = FALSE.union(TRUE); - Assert.assertTrue(res.isAll()); - Assert.assertFalse(res.isNothing()); - Assert.assertFalse(res.isNothing()); - } - - @Test - public static void testSimpleIntersection() { - SubType res = TRUE.intersect(FALSE); - Assert.assertFalse(res.isAll()); - Assert.assertTrue(res.isEmpty()); - Assert.assertTrue(res.isNothing()); - - res = FALSE.intersect(TRUE); - Assert.assertFalse(res.isAll()); - Assert.assertTrue(res.isEmpty()); - Assert.assertTrue(res.isNothing()); - - res = TRUE.intersect(TRUE); - Assert.assertFalse(res.isAll()); - Assert.assertFalse(res.isEmpty()); - Assert.assertFalse(res.isNothing()); - } - - @Test - public static void testSimpleDiff() { - SubType res = TRUE.diff(FALSE); - Assert.assertFalse(res.isAll()); - Assert.assertFalse(res.isEmpty()); - Assert.assertFalse(res.isNothing()); - - res = TRUE.diff(TRUE); - Assert.assertFalse(res.isAll()); - Assert.assertTrue(res.isEmpty()); - Assert.assertTrue(res.isNothing()); - - SubType all = TRUE.union(FALSE); - res = all.diff(TRUE); - Assert.assertFalse(res.isAll()); - Assert.assertFalse(res.isEmpty()); - Assert.assertFalse(res.isNothing()); - } - - @Test - public static void testSimpleComplement() { - SubType all = TRUE.union(FALSE); - SubType nothing = all.complement(); - Assert.assertTrue(nothing.isNothing()); - - SubType res = TRUE.complement(); - Assert.assertFalse(res.isAll()); - Assert.assertFalse(res.isEmpty()); - Assert.assertFalse(res.isNothing()); - - SubType otherNothing = TRUE.intersect(FALSE); - Assert.assertTrue(otherNothing == nothing); // Boolean subtype is interned - } -} diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java deleted file mode 100644 index f14bec456681..000000000000 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/BDecimalSubTypeTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.internal.types.semtype; - -import io.ballerina.runtime.api.types.SemType.SubType; -import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; -import org.testng.Assert; -import org.testng.annotations.Test; - -import java.math.BigDecimal; - -import static org.testng.Assert.*; - -public class BDecimalSubTypeTest { - - @Test - public void testSimpleUnion() { - BigDecimal[] values = {BigDecimal.valueOf(1), BigDecimal.valueOf(2)}; - BDecimalSubType t1 = BDecimalSubType.createDecimalSubType(true, values); - BDecimalSubType t2 = BDecimalSubType.createDecimalSubType(false, values); - - SubType res = t1.union(t2); - Assert.assertTrue(res.isAll()); - } - - @Test - public void testSimpleIntersect() { - BigDecimal[] values = {BigDecimal.valueOf(1), BigDecimal.valueOf(2)}; - BDecimalSubType t1 = BDecimalSubType.createDecimalSubType(true, values); - BDecimalSubType t2 = BDecimalSubType.createDecimalSubType(false, values); - - SubType res = t1.intersect(t2); - Assert.assertTrue(res.isNothing()); - } - - @Test - public void testComplement() { - BigDecimal[] values = {BigDecimal.valueOf(1), BigDecimal.valueOf(2)}; - BDecimalSubType t1 = BDecimalSubType.createDecimalSubType(true, values); - BDecimalSubType t2 = BDecimalSubType.createDecimalSubType(false, values); - SubType all = t1.union(t2); - SubType res = all.complement(); - Assert.assertTrue(res.isNothing()); - } -} \ No newline at end of file diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java deleted file mode 100644 index c995e60141f9..000000000000 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/internal/types/semtype/CoreTests.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.internal.types.semtype; - -import io.ballerina.runtime.api.PredefinedTypes; -import io.ballerina.runtime.api.types.BasicTypeBitSet; -import io.ballerina.runtime.api.types.SemType.BasicTypeCode; -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.BBasicTypeBitSet; -import org.testng.Assert; -import org.testng.annotations.Test; - -public class CoreTests { - - private final Context cx = new Context(); - - @Test - public static void testSimpleUnion() { - BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1); - BasicTypeBitSet t2 = BBasicTypeBitSet.from(1 << 2); - BasicTypeBitSet result = (BasicTypeBitSet) Core.union(t1, t2); - Assert.assertTrue(isBasicTypeSame(result, BBasicTypeBitSet.from(1 << 1 | 1 << 2))); - } - - @Test - public static void testSimpleUnionWithNever() { - BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1); - BasicTypeBitSet t2 = BBasicTypeBitSet.from(0); - BasicTypeBitSet result = (BasicTypeBitSet) Core.union(t1, t2); - Assert.assertTrue(isBasicTypeSame(result, t1)); - } - - @Test - public static void testSimpleDiff() { - BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1 | 1 << 2); - BasicTypeBitSet t2 = BBasicTypeBitSet.from(1 << 2); - BasicTypeBitSet res = (BasicTypeBitSet) Core.diff(t1, t2); - Assert.assertTrue(isBasicTypeSame(res, BBasicTypeBitSet.from(1 << 1))); - - BasicTypeBitSet res2 = (BasicTypeBitSet) Core.diff(t2, t1); - Assert.assertTrue(isBasicTypeSame(res2, BBasicTypeBitSet.from(0))); - } - - @Test - public static void testSimpleIntersection() { - BasicTypeBitSet t1 = BBasicTypeBitSet.from(1 << 1 | 1 << 2); - BasicTypeBitSet t2 = BBasicTypeBitSet.from(1 << 2); - BasicTypeBitSet res = (BasicTypeBitSet) Core.intersect(t1, t2); - Assert.assertTrue(isBasicTypeSame(res, t2)); - - BasicTypeBitSet t3 = BBasicTypeBitSet.from(0); - BasicTypeBitSet res2 = (BasicTypeBitSet) Core.intersect(t1, t3); - Assert.assertTrue(isBasicTypeSame(res2, t3)); - } - - @Test - public void testSimpleSubType() { - SemType intSingleton1 = Builder.intConst(1); - SemType intTop = Builder.from(BasicTypeCode.BT_INT); - Assert.assertTrue(Core.isSubType(cx, intSingleton1, intTop)); - Assert.assertFalse(Core.isSubType(cx, intTop, intSingleton1)); - - SemType intSingleton2 = Builder.intConst(2); - SemType intUnion = Core.union(intSingleton1, intSingleton2); - Assert.assertTrue(Core.isSubType(cx, intSingleton1, intUnion)); - Assert.assertTrue(Core.isSubType(cx, intSingleton2, intUnion)); - Assert.assertTrue(Core.isSubType(cx, intUnion, intTop)); - } - - @Test - public void testBTypeSubType() { - SemType booleanTy = Builder.from(PredefinedTypes.TYPE_BOOLEAN); - SemType anyTy = Builder.from(PredefinedTypes.TYPE_ANY); - Assert.assertTrue(Core.isSubType(cx, booleanTy, anyTy, (t1, t2) -> TypeChecker.checkIsType(t1, t2))); - } - - @Test - public void testMixSubType() { - SemType intSingleton1 = Builder.intConst(1); - SemType BooleanBType = Builder.from(PredefinedTypes.TYPE_BOOLEAN); - SemType T1 = Core.union(intSingleton1, BooleanBType); // 1(semType) | boolean (BType) - - SemType intType = Builder.from(BasicTypeCode.BT_INT); - SemType T2 = Core.union(intType, BooleanBType); // int(semType) | boolean (BType) - - Assert.assertTrue(Core.isSubType(cx, T1, T2, (t1, t2) -> TypeChecker.checkIsType(t1, t2))); - } - - @Test - public void testSimpleTypeArithmetic() { - SemType int1 = Builder.intConst(1); - SemType int2 = Builder.intConst(2); - SemType int12 = Core.union(int1, int2); - SemType int1New = Core.diff(int12, int2); - Assert.assertTrue(Core.isSameType(cx, int1New, int1)); - } - - private static boolean isBasicTypeSame(BasicTypeBitSet t1, BasicTypeBitSet t2) { - return t1.all() == t2.all(); - } -} From 388d5b48ff20dd5a844d6bee9715de7b321fd6b2 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 27 May 2024 08:53:34 +0530 Subject: [PATCH 011/178] Add doc comments --- .../api/types/semtype/BasicTypeBitSet.java | 9 + .../api/types/semtype/BasicTypeCode.java | 5 + .../runtime/api/types/semtype/Builder.java | 30 +- .../runtime/api/types/semtype/Context.java | 7 +- .../runtime/api/types/semtype/Core.java | 5 + .../runtime/api/types/semtype/SemType.java | 5 + .../api/types/semtype/SemTypeHelper.java | 7 +- .../api/types/semtype/SemTypeWrapper.java | 26 - .../runtime/internal/FallbackTypeChecker.java | 2196 ++++++++++++++- .../runtime/internal/TypeChecker.java | 2381 ++--------------- .../runtime/internal/TypeConverter.java | 6 +- .../runtime/internal/types/BBooleanType.java | 3 - .../runtime/internal/types/BByteType.java | 2 - .../runtime/internal/types/BDecimalType.java | 2 - .../runtime/internal/types/BFloatType.java | 3 - .../runtime/internal/types/BIntegerType.java | 2 - .../runtime/internal/types/BNullType.java | 3 - .../internal/types/BSemTypeWrapper.java | 8 + .../runtime/internal/types/BStringType.java | 2 - .../runtime/internal/types/BType.java | 6 +- .../internal/types/BTypeConverter.java | 42 +- .../types/semtype/BBooleanSubType.java | 5 + .../internal/types/semtype/BSubType.java | 5 + .../internal/types/semtype/BTypeAdapter.java | 115 - .../internal/types/semtype/PureSemType.java | 5 + .../types/semtype/SubtypePairIterator.java | 8 +- .../internal/types/semtype/SubtypePairs.java | 5 + .../internal/types/semtype/TypeMetadata.java | 33 - .../src/main/java/module-info.java | 6 +- 29 files changed, 2459 insertions(+), 2473 deletions(-) delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.java 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 index 44d7355fe2a9..47e72ce4cd06 100644 --- 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 @@ -18,6 +18,15 @@ package io.ballerina.runtime.api.types.semtype; +// SEMTYPE-TODO: revisit this after fully implementing semtypes. Added this to match nBallerina where this is just a +// type alias to int. Maybe not needed here due to the way we have modeled type hierarchy (need to check if doing +// instancof checks on this is faster than checking if some is 0) + +/** + * Represents a union of basic types. + * + * @since 2201.10.0 + */ public interface BasicTypeBitSet { default int some() { 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 index fa1513f5f79d..7402bb44ab15 100644 --- 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 @@ -18,6 +18,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Represent bit field that indicate which basic type a semType belongs to. + * + * @since 2201.10.0 + */ public final class BasicTypeCode { public static final int CODE_NIL = 0x00; 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 index fc452eb71f4a..d417a3825203 100644 --- 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 @@ -19,19 +19,27 @@ 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.internal.types.BType; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; 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.BStringSubType; +import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; +/** + * Utility class for creating semtypes. + * + * @since 2201.10.0 + */ public final class Builder { private static final String[] EMPTY_STRING_ARR = new String[0]; @@ -98,7 +106,7 @@ public static SemType charType() { private static final SemType NEVER = SemType.from(0); public static SemType basicTypeUnion(int bitset) { - // TODO: may be cache single type bit sets as well as well + // TODO: may be cache single type bit sets as well if (bitset == 0) { return NEVER; } else if (Integer.bitCount(bitset) == 1) { @@ -163,6 +171,25 @@ static SubType[] initializeSubtypeArray() { return new SubType[CODE_B_TYPE + 2]; } + public static Optional typeOf(Object object) { + if (object == null) { + return Optional.of(nilType()); + } else if (object instanceof DecimalValue decimalValue) { + return Optional.of(decimalConst(decimalValue.value())); + } else if (object instanceof Double doubleValue) { + return Optional.of(floatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(intConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(booleanConst(booleanValue)); + } else if (object instanceof BString stringValue) { + return Optional.of(stringConst(stringValue.getValue())); + } + return Optional.empty(); + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; @@ -184,7 +211,6 @@ private static final class BooleanTypeCache { private static SemType createBooleanSingletonType(boolean value) { return basicSubType(BasicTypeCode.BT_BOOLEAN, BBooleanSubType.from(value)); } - } private static final class StringTypeCache { 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 index 06611bee8c97..5b6b77441641 100644 --- 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 @@ -18,6 +18,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Context in which semtype was defined in. + * + * @since 2201.10.0 + */ public class Context { - + // SEMTYPE-TODO: Fill this in as needed, currently just a placeholder since basic types don't need it } 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 index 62170b0b4597..76c42278ef3f 100644 --- 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 @@ -27,6 +27,11 @@ import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +/** + * Contain functions defined in `core.bal` file. + * + * @since 2201.10.0 + */ public final class Core { public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); 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 index 19a45b8cef98..cc0f397adb1f 100644 --- 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 @@ -21,6 +21,11 @@ import io.ballerina.runtime.internal.types.BSemTypeWrapper; import io.ballerina.runtime.internal.types.semtype.PureSemType; +/** + * Runtime representation of SemType. + * + * @since 2201.10.0 + */ public abstract sealed class SemType implements BasicTypeBitSet permits BSemTypeWrapper, PureSemType { final int all; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java index 45114379bcbc..2f8376db1a4f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java @@ -39,7 +39,12 @@ import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_XML; -public final class SemTypeHelper { +/** + * Collection of utility function on {@code SemType}. + * + * @since 2201.10.0 + */ +final class SemTypeHelper { private SemTypeHelper() { } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java deleted file mode 100644 index 1109c42f048a..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeWrapper.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.api.types.Type; - -public interface SemTypeWrapper extends Type { - - SemType semType(); -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java index 05b105657d85..98e711207b88 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java @@ -18,26 +18,90 @@ package io.ballerina.runtime.internal; +import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.flags.SymbolFlags; +import io.ballerina.runtime.api.types.ArrayType; +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.ParameterizedType; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.Type; +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.values.BObject; +import io.ballerina.runtime.api.values.BRefValue; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BXml; +import io.ballerina.runtime.internal.commons.TypeValuePair; +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.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.values.ArrayValue; +import io.ballerina.runtime.internal.values.DecimalValue; +import io.ballerina.runtime.internal.values.ErrorValue; +import io.ballerina.runtime.internal.values.MapValue; +import io.ballerina.runtime.internal.values.MapValueImpl; +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.XmlSequence; 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.stream.Collectors; +import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANY; +import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANYDATA; +import static io.ballerina.runtime.api.PredefinedTypes.TYPE_JSON; +import static io.ballerina.runtime.api.PredefinedTypes.TYPE_READONLY_JSON; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; +import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; +import static io.ballerina.runtime.internal.TypeChecker.isEqual; +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; +// Contains all the existing non semtype type check logic, SEMTYPE-TODO: remove this once semtype implementation is +// complete final class FallbackTypeChecker { + static final byte MAX_TYPECAST_ERROR_COUNT = 20; + private FallbackTypeChecker() { } @@ -49,16 +113,16 @@ static boolean checkIsType(List errors, Object sourceVal, BType sourceTy if (getImpliedType(sourceType).getTag() == TypeTags.XML_TAG && !targetType.isReadOnly()) { XmlValue val = (XmlValue) sourceVal; if (val.getNodeType() == XmlNodeType.SEQUENCE) { - return TypeChecker.checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), + return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); } } - if (TypeChecker.isMutable(sourceVal, sourceType)) { + if (isMutable(sourceVal, sourceType)) { return false; } - return TypeChecker.checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, + return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); } @@ -69,7 +133,7 @@ static boolean checkIsType(BType sourceType, BType targetType, List TypeChecker.checkIsAnyType(sourceType); + case TypeTags.ANY_TAG -> checkIsAnyType(sourceType); case TypeTags.ANYDATA_TAG -> sourceType.isAnydata(); - case TypeTags.SERVICE_TAG -> TypeChecker.checkIsServiceType(sourceType, targetType, + case TypeTags.SERVICE_TAG -> checkIsServiceType(sourceType, targetType, unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); case TypeTags.HANDLE_TAG -> sourceTypeTag == TypeTags.HANDLE_TAG; case TypeTags.READONLY_TAG -> @@ -152,7 +216,7 @@ static boolean checkIsType(BType sourceType, BType targetType, List TypeChecker.checkIsType(sourceType, ((ReferenceType) targetType).getReferredType(), unresolvedTypes); - default -> TypeChecker.checkIsRecursiveType(sourceType, targetType, + default -> checkIsRecursiveType(sourceType, targetType, unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); }; } @@ -192,10 +256,2120 @@ static boolean checkIsType(Object sourceVal, BType sourceBType, BType targetBTyp } return switch (targetTypeTag) { - case TypeTags.ANY_TAG -> TypeChecker.checkIsAnyType(sourceType); + case TypeTags.ANY_TAG -> checkIsAnyType(sourceType); case TypeTags.READONLY_TAG -> TypeChecker.isInherentlyImmutableType(sourceType) || sourceType.isReadOnly(); - default -> TypeChecker.checkIsRecursiveTypeOnValue(sourceVal, sourceType, targetType, sourceTypeTag, + default -> checkIsRecursiveTypeOnValue(sourceVal, sourceType, targetType, sourceTypeTag, targetTypeTag, unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); }; } + + /** + * Checks if the given decimal number is a real number. + * + * @param decimalValue The decimal value being checked + * @return True if the decimal value is a real number. + */ + static boolean isDecimalRealNumber(DecimalValue decimalValue) { + return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; + } + + static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { + for (Object bValue : sourceType.valueSpace) { + if (!TypeChecker.checkIsType(bValue, targetType)) { + return false; + } + } + return true; + } + + static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, + List unresolvedTypes) { + for (Type type : sourceType.getMemberTypes()) { + if (!TypeChecker.checkIsType(type, targetType, unresolvedTypes)) { + return false; + } + } + return true; + } + + static boolean hasIncompatibleReadOnlyFlags(Field targetField, Field sourceField) { + return SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.READONLY) && !SymbolFlags + .isFlagOn(sourceField.getFlags(), + SymbolFlags.READONLY); + } + + static boolean checkIsAnyType(Type sourceType) { + sourceType = getImpliedType(sourceType); + switch (sourceType.getTag()) { + case TypeTags.ERROR_TAG: + case TypeTags.READONLY_TAG: + return false; + case TypeTags.UNION_TAG: + case TypeTags.ANYDATA_TAG: + case TypeTags.JSON_TAG: + for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { + if (!checkIsAnyType(memberType)) { + return false; + } + } + return true; + default: + return true; + } + } + + static boolean checkObjectEquivalency(Type sourceType, BObjectType targetType, + List unresolvedTypes) { + return checkObjectEquivalency(null, sourceType, targetType, unresolvedTypes); + } + + 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. + TypeChecker.TypePair pair = new TypeChecker.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) || + !TypeChecker.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 = TypeChecker.getType(fieldValue); + + if (fieldValueType.isReadOnly()) { + if (!TypeChecker.checkIsLikeType(fieldValue, lhsField.getFieldType())) { + return false; + } + continue; + } + + if (!TypeChecker.checkIsType(fieldValueType, lhsField.getFieldType(), unresolvedTypes)) { + return false; + } + } else if (!TypeChecker.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 (rhsFunc == null || + !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) || (matchingFuncIsResource && !lhsFuncIsResource)) { + 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 (!TypeChecker.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 (!TypeChecker.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 TypeChecker.checkIsType(source.getReturnType(), target.getReturnType(), unresolvedTypes); + } + + static boolean hasIncompatibleIsolatedFlags(FunctionType target, FunctionType source) { + return SymbolFlags.isFlagOn(target.getFlags(), SymbolFlags.ISOLATED) && !SymbolFlags + .isFlagOn(source.getFlags(), SymbolFlags.ISOLATED); + } + + 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) { + var flags = ((BObjectType) sourceType).flags; + return (flags & SymbolFlags.SERVICE) == SymbolFlags.SERVICE; + } + + return false; + } + + 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(); + } + + 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() != ArrayType.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. + */ + static boolean checkIsLikeType(List errors, Object sourceValue, Type targetType, + List unresolvedValues, boolean allowNumericConversion, + String varName) { + Type sourceType = TypeChecker.getType(sourceValue); + if (TypeChecker.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. + */ + 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; + } + + switch (targetTypeTag) { + case TypeTags.READONLY_TAG: + return true; + case TypeTags.BYTE_TAG: + if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { + return TypeChecker.isByteLiteral(((Number) sourceValue).longValue()); + } + return allowNumericConversion && TypeConverter.isConvertibleToByte(sourceValue); + case TypeTags.INT_TAG: + return allowNumericConversion && TypeConverter.isConvertibleToInt(sourceValue); + 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: + if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { + return TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); + } + return allowNumericConversion && TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); + case TypeTags.FLOAT_TAG: + case TypeTags.DECIMAL_TAG: + return allowNumericConversion && TypeConverter.isConvertibleToFloatingPointTypes(sourceValue); + case TypeTags.CHAR_STRING_TAG: + return TypeConverter.isConvertibleToChar(sourceValue); + case TypeTags.RECORD_TYPE_TAG: + return checkIsLikeRecordType(sourceValue, (BRecordType) targetType, unresolvedValues, + allowNumericConversion, varName, errors); + case TypeTags.TABLE_TAG: + return checkIsLikeTableType(sourceValue, (BTableType) targetType, unresolvedValues, + allowNumericConversion); + case TypeTags.JSON_TAG: + return checkIsLikeJSONType(sourceValue, sourceType, (BJsonType) targetType, unresolvedValues, + allowNumericConversion); + case TypeTags.MAP_TAG: + return checkIsLikeMapType(sourceValue, (BMapType) targetType, unresolvedValues, allowNumericConversion); + case TypeTags.STREAM_TAG: + return checkIsLikeStreamType(sourceValue, (BStreamType) targetType); + case TypeTags.ARRAY_TAG: + return checkIsLikeArrayType(sourceValue, (BArrayType) targetType, unresolvedValues, + allowNumericConversion); + case TypeTags.TUPLE_TAG: + return checkIsLikeTupleType(sourceValue, (BTupleType) targetType, unresolvedValues, + allowNumericConversion); + case TypeTags.ERROR_TAG: + return checkIsLikeErrorType(sourceValue, (BErrorType) targetType, unresolvedValues, + allowNumericConversion); + case TypeTags.ANYDATA_TAG: + return checkIsLikeAnydataType(sourceValue, sourceType, unresolvedValues, allowNumericConversion); + case TypeTags.FINITE_TYPE_TAG: + return checkFiniteTypeAssignable(sourceValue, sourceType, (BFiniteType) targetType, + unresolvedValues, allowNumericConversion); + case TypeTags.XML_ELEMENT_TAG: + case TypeTags.XML_COMMENT_TAG: + case TypeTags.XML_PI_TAG: + case TypeTags.XML_TEXT_TAG: + if (TypeTags.isXMLTypeTag(sourceTypeTag)) { + return checkIsLikeXmlValueSingleton((XmlValue) sourceValue, targetType); + } + return false; + case TypeTags.XML_TAG: + if (TypeTags.isXMLTypeTag(sourceTypeTag)) { + return checkIsLikeXMLSequenceType((XmlValue) sourceValue, targetType); + } + return false; + case TypeTags.UNION_TAG: + return checkIsLikeUnionType(errors, sourceValue, (BUnionType) targetType, unresolvedValues, + allowNumericConversion, varName); + case TypeTags.INTERSECTION_TAG: + return checkIsLikeOnValue(errors, sourceValue, sourceType, + ((BIntersectionType) targetType).getEffectiveType(), unresolvedValues, allowNumericConversion, + varName); + case TypeTags.TYPE_REFERENCED_TYPE_TAG: + return checkIsLikeOnValue(errors, sourceValue, sourceType, + ((BTypeReferenceType) targetType).getReferredType(), unresolvedValues, allowNumericConversion, + varName); + default: + return 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) { + switch (getImpliedType(type).getTag()) { + case TypeTags.XML_ELEMENT_TAG: + return XmlNodeType.ELEMENT; + case TypeTags.XML_COMMENT_TAG: + return XmlNodeType.COMMENT; + case TypeTags.XML_PI_TAG: + return XmlNodeType.PI; + default: + return 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; + } + + private static boolean checkIsLikeAnydataType(Object sourceValue, Type sourceType, + List unresolvedValues, + boolean allowNumericConversion) { + sourceType = getImpliedType(sourceType); + switch (sourceType.getTag()) { + 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()); + switch (getImpliedType(arrayType.getElementType()).getTag()) { + case TypeTags.INT_TAG: + case TypeTags.FLOAT_TAG: + case TypeTags.DECIMAL_TAG: + case TypeTags.STRING_TAG: + case TypeTags.BOOLEAN_TAG: + case TypeTags.BYTE_TAG: + return true; + default: + return 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; + } + + private static boolean checkIsLikeArrayType(Object sourceValue, BArrayType targetType, + List unresolvedValues, boolean allowNumericConversion) { + if (!(sourceValue instanceof ArrayValue)) { + return false; + } + + ArrayValue source = (ArrayValue) sourceValue; + Type targetTypeElementType = targetType.getElementType(); + if (source.getType().getTag() == TypeTags.ARRAY_TAG) { + Type sourceElementType = ((BArrayType) source.getType()).getElementType(); + if (isValueType(sourceElementType)) { + + if (TypeChecker.checkIsType(sourceElementType, targetTypeElementType, new ArrayList<>())) { + return true; + } + + if (allowNumericConversion && TypeChecker.isNumericType(sourceElementType)) { + if (TypeChecker.isNumericType(targetTypeElementType)) { + return true; + } + + if (targetTypeElementType.getTag() != TypeTags.UNION_TAG) { + return false; + } + + List targetNumericTypes = new ArrayList<>(); + for (Type memType : ((BUnionType) targetTypeElementType).getMemberTypes()) { + if (TypeChecker.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; + } + } + } + + int sourceSize = source.size(); + if ((targetType.getState() != ArrayType.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)) { + return false; + } + + for (Object mapEntry : ((MapValueImpl) sourceValue).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)) { + return false; + } + + BStreamType streamType = (BStreamType) ((StreamValue) sourceValue).getType(); + + return streamType.getConstrainedType() == targetType.getConstrainedType(); + } + + 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 (TypeChecker.checkIsType(elementType, targetType, new ArrayList<>())) { + return true; + } + + 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)) { + 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; + } + unresolvedValues.add(typeValuePair); + return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, + allowNumericConversion); + 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; + } + + private static boolean checkIsLikeRecordType(Object sourceValue, BRecordType targetType, + List unresolvedValues, boolean allowNumericConversion, + String varName, List errors) { + if (!(sourceValue instanceof MapValueImpl)) { + return false; + } + + TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); + if (unresolvedValues.contains(typeValuePair)) { + return true; + } + unresolvedValues.add(typeValuePair); + + Map targetFieldTypes = new HashMap<>(); + Type restFieldType = targetType.restFieldType; + boolean returnVal = true; + + for (Field field : targetType.getFields().values()) { + targetFieldTypes.put(field.getFieldName(), field.getFieldType()); + } + + for (Map.Entry targetTypeEntry : targetFieldTypes.entrySet()) { + String fieldName = targetTypeEntry.getKey().toString(); + String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); + Field targetField = targetType.getFields().get(fieldName); + + if (!(((MapValueImpl) sourceValue).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; + } + } + + for (Object object : ((MapValueImpl) sourceValue).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(); + + 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; + } + + 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); + } + } + + private static boolean checkIsLikeTableType(Object sourceValue, BTableType targetType, + List unresolvedValues, boolean allowNumericConversion) { + if (!(sourceValue instanceof TableValueImpl)) { + return false; + } + TableValueImpl tableValue = (TableValueImpl) sourceValue; + BTableType sourceType = (BTableType) getImpliedType(tableValue.getType()); + if (targetType.getKeyType() != null && sourceType.getFieldNames().length == 0) { + return false; + } + + if (sourceType.getKeyType() != null && + !TypeChecker.checkIsType(tableValue.getKeyType(), targetType.getKeyType())) { + return false; + } + + TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); + if (unresolvedValues.contains(typeValuePair)) { + return true; + } + + Object[] objects = tableValue.values().toArray(); + for (Object object : objects) { + if (!TypeChecker.checkIsLikeType(object, targetType.getConstrainedType(), allowNumericConversion)) { + return false; + } + } + return true; + } + + private static boolean checkFiniteTypeAssignable(Object sourceValue, Type sourceType, BFiniteType targetType, + List unresolvedValues, + boolean allowNumericConversion) { + if (targetType.valueSpace.size() == 1) { + Type valueType = getImpliedType(TypeChecker.getType(targetType.valueSpace.iterator().next())); + if (!isSimpleBasicType(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { + return checkIsLikeOnValue(null, sourceValue, sourceType, valueType, unresolvedValues, + allowNumericConversion, null); + } + } + + for (Object valueSpaceItem : targetType.valueSpace) { + // TODO: 8/13/19 Maryam fix for conversion + if (isFiniteTypeValue(sourceValue, sourceType, valueSpaceItem, allowNumericConversion)) { + return true; + } + } + return false; + } + + protected static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object valueSpaceItem, + boolean allowNumericConversion) { + Type valueSpaceItemType = TypeChecker.getType(valueSpaceItem); + int sourceTypeTag = getImpliedType(sourceType).getTag(); + int valueSpaceItemTypeTag = getImpliedType(valueSpaceItemType).getTag(); + if (valueSpaceItemTypeTag > TypeTags.DECIMAL_TAG) { + return valueSpaceItemTypeTag == sourceTypeTag && + (valueSpaceItem == sourceValue || valueSpaceItem.equals(sourceValue)); + } + + switch (sourceTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + switch (valueSpaceItemTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue(); + case TypeTags.FLOAT_TAG: + return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue() && + allowNumericConversion; + case TypeTags.DECIMAL_TAG: + return ((Number) sourceValue).longValue() == ((DecimalValue) valueSpaceItem).intValue() && + allowNumericConversion; + } + case TypeTags.FLOAT_TAG: + switch (valueSpaceItemTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + return ((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() + && allowNumericConversion; + case TypeTags.FLOAT_TAG: + return (((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() || + (Double.isNaN((Double) sourceValue) && Double.isNaN((Double) valueSpaceItem))); + case TypeTags.DECIMAL_TAG: + return ((Number) sourceValue).doubleValue() == ((DecimalValue) valueSpaceItem).floatValue() + && allowNumericConversion; + } + case TypeTags.DECIMAL_TAG: + switch (valueSpaceItemTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + return TypeChecker.checkDecimalEqual((DecimalValue) sourceValue, + DecimalValue.valueOf(((Number) valueSpaceItem).longValue())) && allowNumericConversion; + case TypeTags.FLOAT_TAG: + return TypeChecker.checkDecimalEqual((DecimalValue) sourceValue, + DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && + allowNumericConversion; + case TypeTags.DECIMAL_TAG: + return TypeChecker.checkDecimalEqual((DecimalValue) sourceValue, (DecimalValue) valueSpaceItem); + } + default: + if (sourceTypeTag != valueSpaceItemTypeTag) { + return false; + } + return valueSpaceItem.equals(sourceValue); + } + } + + private static boolean checkIsLikeErrorType(Object sourceValue, BErrorType targetType, + List unresolvedValues, boolean allowNumericConversion) { + Type sourceTypeReferredType = getImpliedType(TypeChecker.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; + } + + static boolean checkTypeDescType(Type sourceType, BTypedescType targetType, + List unresolvedTypes) { + if (sourceType.getTag() != TypeTags.TYPEDESC_TAG) { + return false; + } + + BTypedescType sourceTypedesc = (BTypedescType) sourceType; + return TypeChecker.checkIsType(sourceTypedesc.getConstraint(), targetType.getConstraint(), unresolvedTypes); + } + + static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { + boolean isLhsXmlSequence = lhsValue.getNodeType() == XmlNodeType.SEQUENCE; + boolean isRhsXmlSequence = rhsValue.getNodeType() == XmlNodeType.SEQUENCE; + + if (isLhsXmlSequence && isRhsXmlSequence) { + return isXMLSequenceRefEqual((XmlSequence) lhsValue, (XmlSequence) rhsValue); + } + if (isLhsXmlSequence && lhsValue.isSingleton()) { + return ((XmlSequence) lhsValue).getChildrenList().get(0) == rhsValue; + } + if (isRhsXmlSequence && rhsValue.isSingleton()) { + return ((XmlSequence) rhsValue).getChildrenList().get(0) == lhsValue; + } + if (lhsValue.getNodeType() != rhsValue.getNodeType()) { + return false; + } + if (lhsValue.getNodeType() == XmlNodeType.TEXT && rhsValue.getNodeType() == XmlNodeType.TEXT) { + return isEqual(lhsValue, rhsValue); + } + return false; + } + + private static boolean isXMLSequenceRefEqual(XmlSequence lhsValue, XmlSequence rhsValue) { + Iterator lhsIter = lhsValue.getChildrenList().iterator(); + Iterator rhsIter = rhsValue.getChildrenList().iterator(); + while (lhsIter.hasNext() && rhsIter.hasNext()) { + BXml l = lhsIter.next(); + BXml r = rhsIter.next(); + if (!(l == r || isXMLValueRefEqual((XmlValue) l, (XmlValue) r))) { + return false; + } + } + // lhs hasNext = false & rhs hasNext = false -> empty sequences, hence ref equal + // lhs hasNext = true & rhs hasNext = true would never reach here + // only one hasNext method returns true means sequences are of different sizes, hence not ref equal + return lhsIter.hasNext() == rhsIter.hasNext(); + } + + static boolean checkIsRecursiveType(Type sourceType, Type targetType, List unresolvedTypes) { + switch (targetType.getTag()) { + case TypeTags.MAP_TAG: + return checkIsMapType(sourceType, (BMapType) targetType, unresolvedTypes); + case TypeTags.STREAM_TAG: + return checkIsStreamType(sourceType, (BStreamType) targetType, unresolvedTypes); + case TypeTags.TABLE_TAG: + return checkIsTableType(sourceType, (BTableType) targetType, unresolvedTypes); + case TypeTags.JSON_TAG: + return checkIsJSONType(sourceType, unresolvedTypes); + case TypeTags.RECORD_TYPE_TAG: + return checkIsRecordType(sourceType, (BRecordType) targetType, unresolvedTypes); + case TypeTags.FUNCTION_POINTER_TAG: + return checkIsFunctionType(sourceType, (BFunctionType) targetType); + case TypeTags.ARRAY_TAG: + return checkIsArrayType(sourceType, (BArrayType) targetType, unresolvedTypes); + case TypeTags.TUPLE_TAG: + return checkIsTupleType(sourceType, (BTupleType) targetType, unresolvedTypes); + case TypeTags.UNION_TAG: + return checkIsUnionType(sourceType, (BUnionType) targetType, unresolvedTypes); + case TypeTags.OBJECT_TYPE_TAG: + return checkObjectEquivalency(sourceType, (BObjectType) targetType, + unresolvedTypes); + case TypeTags.FINITE_TYPE_TAG: + return checkIsFiniteType(sourceType, (BFiniteType) targetType); + case TypeTags.FUTURE_TAG: + return checkIsFutureType(sourceType, (BFutureType) targetType, unresolvedTypes); + case TypeTags.ERROR_TAG: + return checkIsErrorType(sourceType, (BErrorType) targetType, unresolvedTypes); + case TypeTags.TYPEDESC_TAG: + return checkTypeDescType(sourceType, (BTypedescType) targetType, unresolvedTypes); + case TypeTags.XML_TAG: + return checkIsXMLType(sourceType, targetType, unresolvedTypes); + default: + // other non-recursive types shouldn't reach here + return false; + } + } + + static boolean checkIsRecursiveTypeOnValue(Object sourceVal, Type sourceType, Type targetType, + int sourceTypeTag, int targetTypeTag, + List unresolvedTypes) { + switch (targetTypeTag) { + case TypeTags.ANYDATA_TAG: + if (sourceTypeTag == TypeTags.OBJECT_TYPE_TAG) { + return false; + } + return checkRecordBelongsToAnydataType((MapValue) sourceVal, (BRecordType) sourceType, unresolvedTypes); + case TypeTags.MAP_TAG: + return checkIsMapType(sourceVal, sourceType, (BMapType) targetType, unresolvedTypes); + case TypeTags.JSON_TAG: + return checkIsMapType(sourceVal, sourceType, + new BMapType(targetType.isReadOnly() ? TYPE_READONLY_JSON : + TYPE_JSON), unresolvedTypes); + case TypeTags.RECORD_TYPE_TAG: + return checkIsRecordType(sourceVal, sourceType, (BRecordType) targetType, unresolvedTypes); + case TypeTags.UNION_TAG: + for (Type type : ((BUnionType) targetType).getMemberTypes()) { + if (TypeChecker.checkIsType(sourceVal, sourceType, type, unresolvedTypes)) { + return true; + } + } + return false; + case TypeTags.OBJECT_TYPE_TAG: + return checkObjectEquivalency(sourceVal, sourceType, (BObjectType) targetType, + unresolvedTypes); + default: + return false; + } + } + + 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); + TypeChecker.TypePair pair = new TypeChecker.TypePair(sourceType, targetType); + if (unresolvedTypes.contains(pair)) { + return true; + } + unresolvedTypes.add(pair); + + switch (sourceType.getTag()) { + case TypeTags.UNION_TAG: + case TypeTags.JSON_TAG: + case TypeTags.ANYDATA_TAG: + return isUnionTypeMatch((BUnionType) sourceType, targetType, unresolvedTypes); + case TypeTags.FINITE_TYPE_TAG: + return isFiniteTypeMatch((BFiniteType) sourceType, targetType); + default: + for (Type type : targetType.getMemberTypes()) { + if (TypeChecker.checkIsType(sourceType, type, unresolvedTypes)) { + return true; + } + } + return false; + + } + } + + 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; + } + } + + private static boolean checkIsMapType(Object sourceVal, 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: + return checkIsMapType((MapValue) sourceVal, (BRecordType) sourceType, unresolvedTypes, + targetConstrainedType); + default: + return false; + } + } + + 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 (!TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceVal.get(name), targetConstrainedType)) { + return false; + } + } + + if (sourceType.sealed) { + return true; + } + + return TypeChecker.checkIsType(sourceType.restFieldType, targetConstrainedType, unresolvedTypes); + } + + 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 TypeChecker.checkIsType(source.constraint, targetConstraint, unresolvedTypes); + } + if (TypeTags.isXMLTypeTag(sourceTag)) { + return TypeChecker.checkIsType(sourceType, target.constraint, unresolvedTypes); + } + return false; + } + + 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 List getWideTypeComponents(BRecordType recType) { + List types = new ArrayList<>(); + for (Field f : recType.getFields().values()) { + types.add(f.getFieldType()); + } + 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; + } + return checkConstraints(((BStreamType) sourceType).getConstrainedType(), targetType.getConstrainedType(), + unresolvedTypes) + && checkConstraints(((BStreamType) sourceType).getCompletionType(), targetType.getCompletionType(), + unresolvedTypes); + } + + 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; + } + + 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); + } + + return Arrays.equals(srcTableType.getFieldNames(), targetType.getFieldNames()); + } + + 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).collect(Collectors.toList()); + + if (fields.size() != memTypes.size()) { + return null; + } + + if (fields.stream().allMatch( + field -> TypeChecker.isSameType(field.getFieldType(), fields.get(0).getFieldType()))) { + return fields.get(0); + } + return null; + default: + return null; + } + } + + 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. + TypeChecker.TypePair pair = new TypeChecker.TypePair(sourceType, jsonType); + if (unresolvedTypes.contains(pair)) { + 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: + case TypeTags.JSON_TAG: + return true; + case TypeTags.ARRAY_TAG: + // Element type of the array should be 'is type' JSON + return TypeChecker.checkIsType(((BArrayType) sourceType).getElementType(), jsonType, unresolvedTypes); + case TypeTags.FINITE_TYPE_TAG: + return isFiniteTypeMatch((BFiniteType) sourceType, jsonType); + case TypeTags.MAP_TAG: + return TypeChecker.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)) { + return false; + } + } + + if (!recordType.sealed) { + return checkIsJSONType(recordType.restFieldType, unresolvedTypes); + } + 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); + switch (sourceType.getTag()) { + case TypeTags.RECORD_TYPE_TAG: + return checkIsRecordType((BRecordType) sourceType, targetType, unresolvedTypes); + case TypeTags.MAP_TAG: + return checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); + default: + return 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. + TypeChecker.TypePair pair = new TypeChecker.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 && + !TypeChecker.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 && + !TypeChecker.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 (!TypeChecker.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 (!TypeChecker.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. + TypeChecker.TypePair pair = new TypeChecker.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()) { + var flags = field.getFlags(); + if (!SymbolFlags.isFlagOn(flags, SymbolFlags.OPTIONAL)) { + return false; + } + + if (SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY) && !sourceType.isReadOnly()) { + return false; + } + + if (!TypeChecker.checkIsType(constraintType, field.getFieldType(), unresolvedTypes)) { + return false; + } + } + + return TypeChecker.checkIsType(constraintType, targetType.restFieldType, unresolvedTypes); + } + + private static boolean checkRecordBelongsToAnydataType(MapValue sourceVal, BRecordType recordType, + List unresolvedTypes) { + Type targetType = TYPE_ANYDATA; + TypeChecker.TypePair pair = new TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceVal.get(fieldNameBString), targetType)) { + return false; + } + } else { + if (!TypeChecker.checkIsType(field.getFieldType(), targetType, unresolvedTypes)) { + return false; + } + } + } + + if (recordType.sealed) { + return true; + } + + return TypeChecker.checkIsType(recordType.restFieldType, targetType, unresolvedTypes); + } + + private static boolean checkIsRecordType(Object sourceVal, Type sourceType, BRecordType targetType, + List unresolvedTypes) { + sourceType = getImpliedType(sourceType); + switch (sourceType.getTag()) { + case TypeTags.RECORD_TYPE_TAG: + return checkIsRecordType((MapValue) sourceVal, (BRecordType) sourceType, targetType, unresolvedTypes); + case TypeTags.MAP_TAG: + return checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); + default: + return false; + } + } + + private static boolean checkIsRecordType(MapValue sourceRecordValue, BRecordType sourceRecordType, + BRecordType targetType, List unresolvedTypes) { + TypeChecker.TypePair pair = new TypeChecker.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 && + !TypeChecker.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 && + !TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceRecordValue.get(fieldNameBString), targetField.getFieldType())) { + return false; + } + } else { + if (!optionalTargetField && optionalSourceField) { + return false; + } + + if (!TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceRecordValue.get(StringUtils.fromString(fieldName)), + targetType.restFieldType)) { + return false; + } + } else if (!TypeChecker.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 checkIsArrayType(BArrayType sourceType, BArrayType targetType, + List unresolvedTypes) { + switch (sourceType.getState()) { + case OPEN: + if (targetType.getState() != ArrayType.ArrayState.OPEN) { + return false; + } + break; + case CLOSED: + if (targetType.getState() == ArrayType.ArrayState.CLOSED && + sourceType.getSize() != targetType.getSize()) { + return false; + } + break; + default: + break; + } + return TypeChecker.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() == ArrayType.ArrayState.OPEN) { + for (Type sourceElementType : tupleTypes) { + if (!TypeChecker.checkIsType(sourceElementType, targetElementType, unresolvedTypes)) { + return false; + } + } + if (sourceRestType != null) { + return TypeChecker.checkIsType(sourceRestType, targetElementType, unresolvedTypes); + } + return true; + } + if (sourceRestType != null) { + return false; + } + if (tupleTypes.size() != targetType.getSize()) { + return false; + } + for (Type sourceElementType : tupleTypes) { + if (!TypeChecker.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 TypeChecker.checkIsType(sourceElementType, targetRestType, unresolvedTypes); + } + return false; + case CLOSED: + if (sourceType.getSize() < targetTypes.size()) { + return false; + } + if (targetTypes.isEmpty()) { + if (targetRestType != null) { + return TypeChecker.checkIsType(sourceElementType, targetRestType, unresolvedTypes); + } + return sourceType.getSize() == 0; + } + + for (Type targetElementType : targetTypes) { + if (!(TypeChecker.checkIsType(sourceElementType, targetElementType, unresolvedTypes))) { + return false; + } + } + if (sourceType.getSize() == targetTypes.size()) { + return true; + } + if (targetRestType != null) { + return TypeChecker.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 (!TypeChecker.checkIsType(sourceTypes.get(i), targetTypes.get(i), unresolvedTypes)) { + return false; + } + } + if (sourceTypeSize == targetTypeSize) { + if (sourceRestType != null) { + return TypeChecker.checkIsType(sourceRestType, targetRestType, unresolvedTypes); + } + return true; + } + + for (int i = targetTypeSize; i < sourceTypeSize; i++) { + if (!TypeChecker.checkIsType(sourceTypes.get(i), targetRestType, unresolvedTypes)) { + return false; + } + } + if (sourceRestType != null) { + return TypeChecker.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 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 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 (!TypeChecker.checkIsType(targetType.parameters[i].type, source.parameters[i].type, new ArrayList<>())) { + return false; + } + } + + return TypeChecker.checkIsType(source.retType, targetType.retType, new ArrayList<>()); + } + + private static boolean hasIncompatibleTransactionalFlags(FunctionType target, FunctionType source) { + return SymbolFlags.isFlagOn(source.getFlags(), SymbolFlags.TRANSACTIONAL) && !SymbolFlags + .isFlagOn(target.getFlags(), SymbolFlags.TRANSACTIONAL); + } + + private static boolean checkConstraints(Type sourceConstraint, Type targetConstraint, + List unresolvedTypes) { + if (sourceConstraint == null) { + sourceConstraint = TYPE_ANY; + } + + if (targetConstraint == null) { + targetConstraint = TYPE_ANY; + } + + return TypeChecker.checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); + } + + private static boolean checkIsErrorType(Type sourceType, BErrorType targetType, + List unresolvedTypes) { + if (sourceType.getTag() != TypeTags.ERROR_TAG) { + return false; + } + // Handle recursive error types. + TypeChecker.TypePair pair = new TypeChecker.TypePair(sourceType, targetType); + if (unresolvedTypes.contains(pair)) { + return true; + } + unresolvedTypes.add(pair); + BErrorType bErrorType = (BErrorType) sourceType; + + if (!TypeChecker.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); + } } 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 74271629aea4..0496bd3c5bff 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 @@ -23,64 +23,44 @@ 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.MethodType; import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.UnionType; -import io.ballerina.runtime.api.types.XmlNodeType; import io.ballerina.runtime.api.types.semtype.BasicTypeCode; 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.values.BDecimal; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BRefValue; 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.BBooleanType; import io.ballerina.runtime.internal.types.BByteType; -import io.ballerina.runtime.internal.types.BErrorType; -import io.ballerina.runtime.internal.types.BField; import io.ballerina.runtime.internal.types.BFiniteType; import io.ballerina.runtime.internal.types.BFloatType; -import io.ballerina.runtime.internal.types.BFunctionType; -import io.ballerina.runtime.internal.types.BFutureType; import io.ballerina.runtime.internal.types.BIntegerType; 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.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; 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.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; @@ -92,20 +72,11 @@ 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.stream.Collectors; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANY; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANYDATA; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BOOLEAN; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BYTE; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_DECIMAL; @@ -117,9 +88,7 @@ import static io.ballerina.runtime.api.PredefinedTypes.TYPE_INT_UNSIGNED_16; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_INT_UNSIGNED_32; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_INT_UNSIGNED_8; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_JSON; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_NULL; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_READONLY_JSON; import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_BUILTIN_PKG_PREFIX; import static io.ballerina.runtime.api.constants.RuntimeConstants.BBYTE_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.BBYTE_MIN_VALUE; @@ -136,11 +105,7 @@ import static io.ballerina.runtime.api.types.semtype.Core.B_TYPE_TOP; import static io.ballerina.runtime.api.types.semtype.Core.SEMTYPE_TOP; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; -import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; import static io.ballerina.runtime.internal.CloneUtils.getErrorMessage; -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; /** * Responsible for performing runtime type checking. @@ -150,7 +115,6 @@ @SuppressWarnings({"rawtypes"}) public final class TypeChecker { - private static final byte MAX_TYPECAST_ERROR_COUNT = 20; private static final String REG_EXP_TYPENAME = "RegExp"; private static final Context cx = new Context(); @@ -309,7 +273,6 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { }; } - /** * Check whether a given value belongs to the given type. * @@ -348,8 +311,9 @@ 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); + return FallbackTypeChecker.checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), + allowNumericConversion, + null); } /** @@ -397,18 +361,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. * @@ -418,20 +370,10 @@ public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsV */ public static boolean checkDecimalExactEqual(DecimalValue lhsValue, DecimalValue rhsValue) { - return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) + return FallbackTypeChecker.isDecimalRealNumber(lhsValue) && FallbackTypeChecker.isDecimalRealNumber(rhsValue) && lhsValue.decimalValue().equals(rhsValue.decimalValue()); } - /** - * Checks if the given decimal number is a real number. - * - * @param decimalValue The decimal value being checked - * @return True if the decimal value is a real number. - */ - private static boolean isDecimalRealNumber(DecimalValue decimalValue) { - return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; - } - /** * Reference equality check for values. If both the values are simple basic types, returns the same * result as {@link #isEqual(Object, Object, Set)} @@ -479,7 +421,7 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { if (!TypeTags.isXMLTypeTag(rhsType.getTag())) { yield false; } - yield isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); + yield FallbackTypeChecker.isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); } case TypeTags.HANDLE_TAG -> { if (rhsType.getTag() != TypeTags.HANDLE_TAG) { @@ -498,44 +440,6 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { }; } - private static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { - boolean isLhsXmlSequence = lhsValue.getNodeType() == XmlNodeType.SEQUENCE; - boolean isRhsXmlSequence = rhsValue.getNodeType() == XmlNodeType.SEQUENCE; - - if (isLhsXmlSequence && isRhsXmlSequence) { - return isXMLSequenceRefEqual((XmlSequence) lhsValue, (XmlSequence) rhsValue); - } - if (isLhsXmlSequence && lhsValue.isSingleton()) { - return ((XmlSequence) lhsValue).getChildrenList().get(0) == rhsValue; - } - if (isRhsXmlSequence && rhsValue.isSingleton()) { - return ((XmlSequence) rhsValue).getChildrenList().get(0) == lhsValue; - } - if (lhsValue.getNodeType() != rhsValue.getNodeType()) { - return false; - } - if (lhsValue.getNodeType() == XmlNodeType.TEXT && rhsValue.getNodeType() == XmlNodeType.TEXT) { - return isEqual(lhsValue, rhsValue); - } - return false; - } - - private static boolean isXMLSequenceRefEqual(XmlSequence lhsValue, XmlSequence rhsValue) { - Iterator lhsIter = lhsValue.getChildrenList().iterator(); - Iterator rhsIter = rhsValue.getChildrenList().iterator(); - while (lhsIter.hasNext() && rhsIter.hasNext()) { - BXml l = lhsIter.next(); - BXml r = rhsIter.next(); - if (!(l == r || isXMLValueRefEqual((XmlValue) l, (XmlValue) r))) { - return false; - } - } - // lhs hasNext = false & rhs hasNext = false -> empty sequences, hence ref equal - // lhs hasNext = true & rhs hasNext = true would never reach here - // only one hasNext method returns true means sequences are of different sizes, hence not ref equal - return lhsIter.hasNext() == rhsIter.hasNext(); - } - /** * Get the typedesc of a value. * @@ -547,7 +451,7 @@ public static TypedescValue getTypedesc(Object value) { if (type == null) { return null; } - if (isSimpleBasicType(type)) { + if (FallbackTypeChecker.isSimpleBasicType(type)) { return new TypedescValueImpl(new BFiniteType(value.toString(), Set.of(value), 0)); } if (value instanceof BRefValue bRefValue) { @@ -605,6 +509,27 @@ static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, L }; } + /** + * 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 FallbackTypeChecker.isDecimalRealNumber(lhsValue) && FallbackTypeChecker.isDecimalRealNumber(rhsValue) && + lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; + } + + public static boolean isNumericType(Type type) { + type = getImpliedType(type); + return type.getTag() < TypeTags.STRING_TAG || TypeTags.isIntegerTypeTag(type.getTag()); + } + + static boolean isByteLiteral(long longValue) { + return (longValue >= BBYTE_MIN_VALUE && longValue <= BBYTE_MAX_VALUE); + } + // Private methods private enum TypeCheckResult { @@ -661,2174 +586,171 @@ private static BType bTypePart(SemType t) { return (BType) Core.subTypeData(t, BasicTypeCode.BT_B_TYPE); } - - 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); - } - - 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; - }; - } - - 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; - }; - } - - static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { - for (Object bValue : sourceType.valueSpace) { - if (!checkIsType(bValue, targetType)) { - return false; - } + public static boolean isInherentlyImmutableType(Type sourceType) { + sourceType = getImpliedType(sourceType); + if (FallbackTypeChecker.isSimpleBasicType(sourceType)) { + return true; } - return true; - } - static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, List unresolvedTypes) { - for (Type type : sourceType.getMemberTypes()) { - if (!checkIsType(type, targetType, unresolvedTypes)) { + switch (sourceType.getTag()) { + case TypeTags.XML_TEXT_TAG: + case TypeTags.FINITE_TYPE_TAG: // Assuming a finite type will only have members from simple basic types. + case TypeTags.READONLY_TAG: + case TypeTags.NULL_TAG: + case TypeTags.NEVER_TAG: + case TypeTags.ERROR_TAG: + case TypeTags.INVOKABLE_TAG: + case TypeTags.SERVICE_TAG: + case TypeTags.TYPEDESC_TAG: + case TypeTags.FUNCTION_POINTER_TAG: + case TypeTags.HANDLE_TAG: + case TypeTags.REG_EXP_TYPE_TAG: + return true; + case TypeTags.XML_TAG: + return ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; + case TypeTags.TYPE_REFERENCED_TYPE_TAG: + return isInherentlyImmutableType(((BTypeReferenceType) sourceType).getReferredType()); + default: 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)) { + public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { + if (!unresolvedTypes.add(type)) { 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; + 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; } } - yield false; - } - }; - } - 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; - } - } + Type tupRestType = tupleType.getRestType(); + if (tupRestType == null) { + return true; + } - 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; - }; - } + 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; + } + } - 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; + Type recordRestType = recordType.restFieldType; + if (recordRestType == null) { + return true; } - continue; - } - BString name = StringUtils.fromString(field.getFieldName()); + return isInherentlyImmutableType(recordRestType) || + isSelectivelyImmutableType(recordRestType, unresolvedTypes); + case TypeTags.OBJECT_TYPE_TAG: + BObjectType objectType = (BObjectType) type; - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL) && !sourceVal.containsKey(name)) { - continue; - } + if (SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.CLASS) && + !SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.READONLY)) { + return false; + } - if (!checkIsLikeType(sourceVal.get(name), targetConstrainedType)) { + 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; - } - } - - if (sourceType.sealed) { - return true; } - - return checkIsType(sourceType.restFieldType, targetConstrainedType, unresolvedTypes); } - 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); - } - return false; - } + static boolean isSigned32LiteralValue(Long longObject) { - 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; + return (longObject >= SIGNED32_MIN_VALUE && longObject <= SIGNED32_MAX_VALUE); } - private static List getWideTypeComponents(BRecordType recType) { - List types = new ArrayList<>(); - for (Field f : recType.getFields().values()) { - types.add(f.getFieldType()); - } - if (!recType.sealed) { - types.add(recType.restFieldType); - } - return types; - } + static boolean isSigned16LiteralValue(Long longObject) { - private static boolean checkIsStreamType(Type sourceType, BStreamType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.STREAM_TAG) { - return false; - } - return checkConstraints(((BStreamType) sourceType).getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes) - && checkConstraints(((BStreamType) sourceType).getCompletionType(), targetType.getCompletionType(), - unresolvedTypes); + return (longObject.intValue() >= SIGNED16_MIN_VALUE && longObject.intValue() <= SIGNED16_MAX_VALUE); } - 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; - } - - if (targetType.getKeyType() != null) { - if (srcTableType.getKeyType() != null && - (checkConstraints(srcTableType.getKeyType(), targetType.getKeyType(), unresolvedTypes))) { - return true; - } + static boolean isSigned8LiteralValue(Long longObject) { - if (srcTableType.getFieldNames().length == 0) { - return false; - } + return (longObject.intValue() >= SIGNED8_MIN_VALUE && longObject.intValue() <= SIGNED8_MAX_VALUE); + } - List fieldTypes = new ArrayList<>(); - Arrays.stream(srcTableType.getFieldNames()).forEach(field -> fieldTypes - .add(Objects.requireNonNull(getTableConstraintField(srcTableType.getConstrainedType(), field)) - .getFieldType())); + static boolean isUnsigned32LiteralValue(Long longObject) { - if (fieldTypes.size() == 1) { - return checkConstraints(fieldTypes.get(0), targetType.getKeyType(), unresolvedTypes); - } + return (longObject >= 0 && longObject <= UNSIGNED32_MAX_VALUE); + } - BTupleType tupleType = new BTupleType(fieldTypes); - return checkConstraints(tupleType, targetType.getKeyType(), unresolvedTypes); - } + static boolean isUnsigned16LiteralValue(Long longObject) { - return Arrays.equals(srcTableType.getFieldNames(), targetType.getFieldNames()); + return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED16_MAX_VALUE); } - 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; - } + static boolean isUnsigned8LiteralValue(Long longObject) { - if (fields.stream().allMatch(field -> isSameType(field.getFieldType(), fields.get(0).getFieldType()))) { - return fields.get(0); - } - return null; - default: - return null; - } + return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED8_MAX_VALUE); } - 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)) { - 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: - case TypeTags.JSON_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)) { - return false; - } - } - - if (!recordType.sealed) { - return checkIsJSONType(recordType.restFieldType, unresolvedTypes); - } - 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); - } - - 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 (rhsFunc == null || - !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) || (matchingFuncIsResource && !lhsFuncIsResource)) { - 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); - } - - 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); - } - - 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(); - } - - 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 = TypeChecker.getType(sourceValue); - if (TypeChecker.checkIsType(sourceType, targetType, new ArrayList<>())) { - return true; - } - - return TypeChecker.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. - */ - 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(((Number) sourceValue).longValue()); - } - 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()) { - 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; - } - - 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; + 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; } - 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) { - 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; - } - } - } - - 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(); - } - - 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; - } - - 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)) { - 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; - } - unresolvedValues.add(typeValuePair); - return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, - allowNumericConversion); - 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; - } - - private static boolean checkIsLikeRecordType(Object sourceValue, BRecordType targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName, List errors) { - if (!(sourceValue instanceof MapValueImpl sourceMapValue)) { - return false; - } - - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; - } - unresolvedValues.add(typeValuePair); - - Map targetFieldTypes = new HashMap<>(); - Type restFieldType = targetType.restFieldType; - boolean returnVal = true; - - for (Field field : targetType.getFields().values()) { - targetFieldTypes.put(field.getFieldName(), field.getFieldType()); - } - - for (Map.Entry targetTypeEntry : targetFieldTypes.entrySet()) { - String fieldName = targetTypeEntry.getKey().toString(); - String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); - Field targetField = targetType.getFields().get(fieldName); - - 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; - } - } - - 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(); - - 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; - } - - 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); - } - } - - 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; - } - - if (sourceType.getKeyType() != null && !checkIsType(tableValue.getKeyType(), targetType.getKeyType())) { - return false; - } - - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; - } - - Object[] objects = tableValue.values().toArray(); - for (Object object : objects) { - if (!checkIsLikeType(object, targetType.getConstrainedType(), allowNumericConversion)) { - return false; - } - } - return true; - } - - 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); - } - } - - for (Object valueSpaceItem : targetType.valueSpace) { - // TODO: 8/13/19 Maryam fix for conversion - if (isFiniteTypeValue(sourceValue, sourceType, valueSpaceItem, allowNumericConversion)) { - return true; - } - } - return false; - } - - static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object valueSpaceItem, - boolean allowNumericConversion) { - Type valueSpaceItemType = getType(valueSpaceItem); - int sourceTypeTag = getImpliedType(sourceType).getTag(); - int valueSpaceItemTypeTag = getImpliedType(valueSpaceItemType).getTag(); - if (valueSpaceItemTypeTag > TypeTags.DECIMAL_TAG) { - return valueSpaceItemTypeTag == sourceTypeTag && - (valueSpaceItem == sourceValue || valueSpaceItem.equals(sourceValue)); - } - - switch (sourceTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - switch (valueSpaceItemTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue(); - case TypeTags.FLOAT_TAG: - return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue() && - allowNumericConversion; - case TypeTags.DECIMAL_TAG: - return ((Number) sourceValue).longValue() == ((DecimalValue) valueSpaceItem).intValue() && - allowNumericConversion; - } - case TypeTags.FLOAT_TAG: - switch (valueSpaceItemTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - return ((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() - && allowNumericConversion; - case TypeTags.FLOAT_TAG: - return (((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() || - (Double.isNaN((Double) sourceValue) && Double.isNaN((Double) valueSpaceItem))); - case TypeTags.DECIMAL_TAG: - return ((Number) sourceValue).doubleValue() == ((DecimalValue) valueSpaceItem).floatValue() - && allowNumericConversion; - } - case TypeTags.DECIMAL_TAG: - switch (valueSpaceItemTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - return checkDecimalEqual((DecimalValue) sourceValue, - DecimalValue.valueOf(((Number) valueSpaceItem).longValue())) && allowNumericConversion; - case TypeTags.FLOAT_TAG: - return checkDecimalEqual((DecimalValue) sourceValue, - DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && allowNumericConversion; - case TypeTags.DECIMAL_TAG: - return checkDecimalEqual((DecimalValue) sourceValue, (DecimalValue) valueSpaceItem); - } - default: - if (sourceTypeTag != valueSpaceItemTypeTag) { - return false; - } - return valueSpaceItem.equals(sourceValue); - } - } - - 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. * @@ -3243,7 +1165,8 @@ private static BError createTypeCastError(Object value, Type targetType, List Class getValueClass() { 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 9d90256510c6..20a7f8d1edbb 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 @@ -33,7 +33,6 @@ @SuppressWarnings("unchecked") public final class BStringType extends BSemTypeWrapper implements StringType { - protected final String typeName; // We are creating separate empty module instead of reusing PredefinedTypes.EMPTY_MODULE to avoid cyclic // dependencies. private static final BStringTypeImpl DEFAULT_B_TYPE = @@ -54,7 +53,6 @@ public BStringType(String typeName, Module pkg, int tag) { private BStringType(BStringTypeImpl bType, SemType semType) { super(bType, semType); - this.typeName = bType.typeName; } public static BStringType singletonType(String value) { 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 d83217ef0ad6..607be6ddf80f 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 @@ -236,16 +236,18 @@ public Type getCachedImpliedType() { return this.cachedImpliedType; } - void resetSemTypeCache() { + // If any child class allow mutation that will affect the SemType, it must call this method. + final void resetSemTypeCache() { cachedSemType = null; } + // If any child class partially implement SemType it must override this method. SemType createSemType() { return BTypeConverter.wrapAsPureBType(this); } @Override - public SemType get() { + public final SemType get() { if (cachedSemType == null) { cachedSemType = createSemType(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 1955bd045f8b..a48604eeddb7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -20,23 +20,26 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; -import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.BasicTypeCode; 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.values.BString; import io.ballerina.runtime.internal.types.semtype.BSubType; -import io.ballerina.runtime.internal.values.DecimalValue; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; -// NOTE: this is so that we don't have to expose any utility constructors as public to builder +/** + * This is a utility class for {@code Builder} class so that BTypes don't need to expose their internal structure as + * public to create semtypes from them. + * + * @since 2201.10.0 + */ final class BTypeConverter { private BTypeConverter() { @@ -61,14 +64,12 @@ private static SemType from(Type type) { if (type instanceof SemType semType) { return semType; } else if (type instanceof BType bType) { - return from(bType); + return fromBType(bType); } throw new IllegalArgumentException("Unsupported type: " + type); } - // TODO: ideally this should be only called by BTypes (ie. no need for this to be public) and they should call - // the correct method (ie. no need for this instance of thing) - public static SemType from(BType innerType) { + private static SemType fromBType(BType innerType) { return innerType.get(); } @@ -77,10 +78,6 @@ static SemType fromReadonly(BReadonlyType readonlyType) { return Core.union(READONLY_SEMTYPE_PART, bTypePart); } - static SemType fromTypeReference(ReferenceType referenceType) { - return from(referenceType.getReferredType()); - } - static SemType fromTupleType(BTupleType tupleType) { for (Type type : tupleType.getTupleTypes()) { if (Core.isNever(from(type))) { @@ -135,7 +132,7 @@ private record BTypeParts(SemType semTypePart, List bTypeParts) { } private static BTypeParts split(Type type) { - if (isSemType(type)) { + if (type instanceof SemType) { return new BTypeParts(from(type), Collections.emptyList()); } else if (type instanceof BUnionType unionType) { return splitUnion(unionType); @@ -163,18 +160,9 @@ private static BTypeParts splitFiniteType(BFiniteType finiteType) { SemType semTypePart = Builder.neverType(); for (var each : finiteType.valueSpace) { // TODO: lift this to Builder (Object) -> Type - if (each == null) { - semTypePart = Core.union(semTypePart, Builder.nilType()); - } else if (each instanceof DecimalValue decimalValue) { - semTypePart = Core.union(semTypePart, Builder.decimalConst(decimalValue.value())); - } else if (each instanceof Double doubleValue) { - semTypePart = Core.union(semTypePart, Builder.floatConst(doubleValue)); - } else if (each instanceof Number intValue) { - semTypePart = Core.union(semTypePart, Builder.intConst(intValue.longValue())); - } else if (each instanceof Boolean booleanValue) { - semTypePart = Core.union(semTypePart, Builder.booleanConst(booleanValue)); - } else if (each instanceof BString stringValue) { - semTypePart = Core.union(semTypePart, Builder.stringConst(stringValue.getValue())); + Optional semType = Builder.typeOf(each); + if (semType.isPresent()) { + semTypePart = Core.union(semTypePart, semType.get()); } else { newValueSpace.add(each); } @@ -202,8 +190,4 @@ private static BTypeParts splitUnion(BUnionType unionType) { } return new BTypeParts(semTypePart, Collections.unmodifiableList(bTypeMembers)); } - - private static boolean isSemType(Type type) { - return type instanceof SemType; - } } 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 index ad0b9e5d227e..9252938a17e8 100644 --- 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 @@ -20,6 +20,11 @@ import io.ballerina.runtime.api.types.semtype.SubType; +/** + * Runtime representation of BooleanSubType. + * + * @since 2201.10.0 + */ public final class BBooleanSubType extends SubType { private final BBooleanSubTypeData data; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java index 70d2d18de928..119c3b7e2912 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java @@ -21,6 +21,11 @@ import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.internal.types.BType; +/** + * Runtime representation of BType part of a semtype. + * + * @since 2201.10.0 + */ public class BSubType extends SubType { private final BType data; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java deleted file mode 100644 index e29e6d5bf31b..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypeAdapter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.Module; -import io.ballerina.runtime.api.types.IntersectionType; -import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.SemType; - -// All the logic for supporting various Type operations on SemTypes is defined here -final class BTypeAdapter implements Type { - - private final SemType semType; - - BTypeAdapter(SemType semType) { - this.semType = semType; - } - - @Override - public V getZeroValue() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public V getEmptyValue() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public int getTag() { - // TODO: cache this - throw new IllegalStateException("unimplemented"); - } - - @Override - public boolean isNilable() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public String getName() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public String getQualifiedName() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public Module getPackage() { - throw new IllegalStateException("semtype without identity"); - } - - @Override - public boolean isPublic() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public boolean isNative() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public boolean isAnydata() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public boolean isPureType() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public boolean isReadOnly() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public long getFlags() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public Type getImmutableType() { - throw new IllegalStateException("unimplemented"); - } - - @Override - public void setImmutableType(IntersectionType immutableType) { - throw new IllegalArgumentException("SemTypes are unmodifiable"); - } - - @Override - public Module getPkg() { - throw new IllegalStateException("semtype without identity"); - } -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index a679cf686953..ec878f4bb183 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -21,6 +21,11 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; +/** + * Represent types that conform only to {@code SemType} APIs. + * + * @since 2201.10.0 + */ public final class PureSemType extends SemType { public PureSemType(int all, int some, SubType[] subTypeData) { 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 index 0a1811155725..0676803fcaf1 100644 --- 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 @@ -24,8 +24,14 @@ import java.util.Iterator; -public final class SubtypePairIterator implements Iterator { +/** + * Iteration implementation of `SubtypePairIterator`. + * + * @since 2201.10.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_B_TYPE + 1; private final int bits; 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 index efa69c79b1fa..07100a7b5ba6 100644 --- 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 @@ -22,6 +22,11 @@ import java.util.Iterator; +/** + * Implements the iterable for `SubtypePairIteratorImpl`. + * + * @since 2201.10.0 + */ public class SubtypePairs implements Iterable { private final SemType t1; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.java deleted file mode 100644 index b433116404ae..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypeMetadata.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.Module; - -public final class TypeMetadata { - - protected Module pkg = null; - - private TypeMetadata() { - } - - public static TypeMetadata empty() { - return new TypeMetadata(); - } -} diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index a36819d6400e..56c5b94c127d 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; @@ -67,11 +68,10 @@ io.ballerina.lang.xml, org.ballerinalang.debugadapter.runtime, io.ballerina.lang.query, io.ballerina.lang.function, io.ballerina.lang.regexp, io.ballerina.lang.value, io.ballerina.lang.internal, io.ballerina.lang.array; exports io.ballerina.runtime.internal.configurable to io.ballerina.lang.internal; + exports io.ballerina.runtime.internal.types to io.ballerina.lang.typedesc, io.ballerina.testerina.runtime, + org.ballerinalang.debugadapter.runtime, io.ballerina.lang.function, io.ballerina.lang.regexp, io.ballerina.testerina.core; exports io.ballerina.runtime.observability.metrics.noop; exports io.ballerina.runtime.observability.tracer.noop; exports io.ballerina.runtime.internal.regexp; exports io.ballerina.runtime.internal.configurable.providers to org.ballerinalang.debugadapter.runtime; - exports io.ballerina.runtime.internal.types; - exports io.ballerina.runtime.internal.types.semtype; - exports io.ballerina.runtime.api.types.semtype; } From 550fcbce078ce4837b05e04a698fe69013bbfa7d Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 29 May 2024 11:25:32 +0530 Subject: [PATCH 012/178] Fix float equality --- .../internal/types/semtype/BFloatSubType.java | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) 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 index 5b186eb4d59f..cb1d63a2f0a3 100644 --- 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 @@ -50,7 +50,6 @@ public static BFloatSubType createFloatSubType(boolean allowed, Double[] values) return NOTHING; } } - Arrays.sort(values); return new BFloatSubType(new FloatSubTypeData(allowed, values)); } @@ -128,7 +127,43 @@ static final class FloatSubTypeData extends EnumerableSubtypeData implem private FloatSubTypeData(boolean allowed, Double[] values) { this.allowed = allowed; - this.values = values; + 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 From 3e5a47d185c56f21e075fb3e9db57a4a616c0db0 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 28 May 2024 07:22:45 +0530 Subject: [PATCH 013/178] Refactor type tests Use new api in CompilerTypeTestApi Extract out the which semtype implementation used for tests Implement semtype tests --- .../port/test/CompilerSemTypeResolver.java | 790 ++++++++++++++++++ .../port/test/CompilerTypeTestAPI.java | 81 ++ .../port/test/CompilerTypeTestEnv.java | 47 ++ .../port/test/ComplierTypeTestContext.java | 52 ++ .../port/test/RuntimeSemTypeResolver.java | 291 +++++++ .../semtype/port/test/RuntimeTypeTestAPI.java | 80 ++ .../port/test/RuntimeTypeTestContext.java | 50 ++ .../semtype/port/test/RuntimeTypeTestEnv.java | 47 ++ .../test/SemTypeAssertionTransformer.java | 79 +- .../semtype/port/test/SemTypeResolver.java | 747 +---------------- .../semtype/port/test/SemTypeTest.java | 113 ++- .../semtype/port/test/TypeAssertion.java | 49 ++ .../semtype/port/test/TypeTestAPI.java | 39 + .../semtype/port/test/TypeTestContext.java | 28 + .../port/test/TypeTestContextBuilder.java | 24 + .../semtype/port/test/TypeTestEnv.java | 28 + .../resources/test-src/type-rel/float-tv.bal | 8 + 17 files changed, 1726 insertions(+), 827 deletions(-) create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java create mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal 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..2bbb867f4a81 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java @@ -0,0 +1,790 @@ +/* + * 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.expressions.BLangSimpleVarRef; +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. + * + * @since 2201.10.0 + */ +public class CompilerSemTypeResolver implements 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); + } + } + } + + private 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"); + } + } + + 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 static int from(Map mod, BLangNode expr) { + if (expr instanceof BLangLiteral literal) { + return listSize((Number) literal.value); + } else if (expr instanceof BLangSimpleVarRef varRef) { + String varName = varRef.variableName.value; + return from(mod, mod.get(varName)); + } else if (expr instanceof BLangConstant constant) { + return 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(); + } + + 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()); + 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..bca96679f892 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java @@ -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. + */ + +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); + } +} 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..315e1c15e4d6 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java @@ -0,0 +1,291 @@ +/* + * 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.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import org.ballerinalang.model.tree.NodeKind; +import org.ballerinalang.model.types.TypeKind; +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.types.BLangBuiltInRefTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +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.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +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 org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; + +public class RuntimeSemTypeResolver implements SemTypeResolver { + + Map attachedSemType = new HashMap<>(); + Map semTypeMemo = new HashMap<>(); + + // TODO: may be we need an abstract class + @Override + public void defineSemTypes(List moduleDefs, TypeTestContext cx) { + Map modTable = new LinkedHashMap<>(); + // TODO: stream + 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); + } + } + } + + private void resolveTypeDefn(TypeTestContext cx, Map modTable, + BLangTypeDefinition typeDefinition) { + resolveTypeDefnRec(cx, modTable, typeDefinition, 0); + } + + 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, BLangType td) { + if (td == null) { + return null; + } + switch (td.getKind()) { + case VALUE_TYPE: + return resolveTypeDesc(cx, (BLangValueType) td); + case BUILT_IN_REF_TYPE: + return resolveTypeDesc((BLangBuiltInRefTypeNode) td); + case INTERSECTION_TYPE_NODE: + return resolveTypeDesc(cx, (BLangIntersectionTypeNode) td, mod, depth, defn); + case UNION_TYPE_NODE: + return resolveTypeDesc(cx, (BLangUnionTypeNode) td, mod, depth, defn); + case USER_DEFINED_TYPE: + return resolveTypeDesc(cx, (BLangUserDefinedType) td, mod, depth); + case FINITE_TYPE_NODE: + return resolveSingletonType((BLangFiniteTypeNode) td); + default: + throw new UnsupportedOperationException("type not implemented: " + td.getKind()); + } + } + + private SemType resolveSingletonType(BLangFiniteTypeNode td) { + return td.valueSpace.stream().map(each -> (BLangLiteral) each) + .map(literal -> resolveSingletonType(literal.value, literal.getDeterminedType().getKind()).get()) + .reduce(Builder.neverType(), Core::union); + } + + // TODO: common code? + private Optional resolveSingletonType(Object value, TypeKind targetTypeKind) { + return switch (targetTypeKind) { + case NIL -> Optional.of(Builder.nilType()); + case BOOLEAN -> Optional.of(Builder.booleanConst((Boolean) value)); + case INT, BYTE -> { + assert !(value instanceof Byte); + yield Optional.of(Builder.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 Optional.empty(); + } + } + yield Optional.of(Builder.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 -> { + String repr = (String) value; + if (repr.contains("d") || repr.contains("D")) { + repr = repr.substring(0, repr.length() - 1); + } + BigDecimal d = new BigDecimal(repr); + yield Optional.of(Builder.decimalConst(d)); + } + case STRING -> Optional.of(Builder.stringConst((String) value)); + 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.charType(); + } + + BLangNode moduleLevelDef = mod.get(name); + if (moduleLevelDef == null) { + throw new IllegalStateException("unknown type: " + name); + } + + if (moduleLevelDef.getKind() == NodeKind.TYPE_DEFINITION) { + return resolveTypeDefnRec(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + } else if (moduleLevelDef.getKind() == NodeKind.CONSTANT) { + BLangConstant constant = (BLangConstant) moduleLevelDef; + return resolveTypeDefnRec(cx, mod, constant.associatedTypeDefinition, depth); + } else { + throw new UnsupportedOperationException("constants and class defns not implemented"); + } + } + + private SemType resolveIntSubtype(String name) { + // TODO: support MAX_VALUE + return switch (name) { + case "Signed8" -> Builder.intRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); + case "Signed16" -> Builder.intRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); + case "Signed32" -> Builder.intRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); + case "Unsigned8" -> Builder.intRange(0, UNSIGNED8_MAX_VALUE); + case "Unsigned16" -> Builder.intRange(0, UNSIGNED16_MAX_VALUE); + case "Unsigned32" -> Builder.intRange(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.neverType(); + default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { + switch (td.typeKind) { + case NIL: + return Builder.nilType(); + case BOOLEAN: + return Builder.booleanType(); + case BYTE: + return Builder.intRange(0, UNSIGNED8_MAX_VALUE); + case INT: + return Builder.intType(); + case FLOAT: + return Builder.floatType(); + case DECIMAL: + return Builder.decimalType(); + case STRING: + return Builder.stringType(); + default: + throw new IllegalStateException("Unknown type: " + td); + } + } + + private SemType evaluateConst(BLangConstant constant) { + return switch (constant.symbol.value.type.getKind()) { + case INT -> Builder.intConst((long) constant.symbol.value.value); + case BOOLEAN -> Builder.booleanConst((boolean) constant.symbol.value.value); + case STRING -> Builder.stringConst((String) constant.symbol.value.value); + case FLOAT -> Builder.floatConst((double) constant.symbol.value.value); + default -> throw new UnsupportedOperationException("Expression type not implemented for const semtype"); + }; + } + + private void resolveConstant(TypeTestContext cx, Map modTable, BLangConstant constant) { + SemType semtype = evaluateConst(constant); + attachToBType(constant.typeNode, semtype); + cx.getEnv().addTypeDef(constant.name.value, 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..e0c0e715868f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.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.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.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(form(cx), t1, t2); + } + + private static Context form(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) { + throw new IllegalArgumentException("list type not implemented"); + } + + @Override + public boolean isMapType(SemType t) { + throw new IllegalArgumentException("map type not implemented"); + } + + @Override + public SemType intConst(long l) { + return Builder.intConst(l); + } + + @Override + public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType semType, SemType m) { + throw new IllegalArgumentException("mapping member type inner val not implemented"); + } + + @Override + public SemType listProj(TypeTestContext context, SemType t, SemType key) { + throw new IllegalArgumentException("list proj not implemented"); + } + + @Override + public SemType listMemberType(TypeTestContext context, SemType t, SemType key) { + throw new IllegalArgumentException("list member type not implemented"); + } +} 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..dba9c3cc2a4f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java @@ -0,0 +1,50 @@ +/* + * 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.SemType; + +public final class RuntimeTypeTestContext implements TypeTestContext { + + private final TypeTestEnv env; + + private RuntimeTypeTestContext(TypeTestEnv env) { + this.env = env; + } + + public static synchronized RuntimeTypeTestContext from(TypeTestEnv env) { + return new RuntimeTypeTestContext(env); + } + + @Override + public TypeTestEnv getEnv() { + return env; + } + + @Override + public Object getInnerEnv() { + throw new IllegalStateException("not implemented"); + } + + @Override + public Object getInnerContext() { + return new Context(); + } +} 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..ed7605804b82 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java @@ -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. + */ + +package io.ballerina.semtype.port.test; + +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 RuntimeTypeTestEnv() { + + } + + public static synchronized RuntimeTypeTestEnv from() { + return new RuntimeTypeTestEnv(); + } + + @Override + public Map getTypeNameSemTypeMap() { + return typeMap; + } + + @Override + public void addTypeDef(String value, SemType semtype) { + typeMap.put(value, semtype); + } +} 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 index c86e7403fc30..eabe09e1eb87 100644 --- 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 @@ -26,14 +26,8 @@ import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.compiler.syntax.tree.Token; -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.testng.Assert; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -45,32 +39,41 @@ * // @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 class SemTypeAssertionTransformer extends NodeVisitor { +public final class SemTypeAssertionTransformer extends NodeVisitor { + private final String fileName; private final SyntaxTree syntaxTree; - private final Env semtypeEnv; - private final Context context; + private final TypeTestEnv semtypeEnv; + private final TypeTestContext context; private final List list; + private final TypeTestAPI semtypeAPI; - private SemTypeAssertionTransformer(String fileName, SyntaxTree syntaxTree, Env semtypeEnv) { + private SemTypeAssertionTransformer(String fileName, SyntaxTree syntaxTree, TypeTestEnv semtypeEnv, + TypeTestContext context, TypeTestAPI semtypeAPI) { this.fileName = fileName; this.syntaxTree = syntaxTree; this.semtypeEnv = semtypeEnv; - this.context = Context.from(semtypeEnv); + this.context = context; list = new ArrayList<>(); + this.semtypeAPI = semtypeAPI; } - public static List getTypeAssertionsFrom(String fileName, SyntaxTree syntaxTree, Env semtypeEnv) { - final SemTypeAssertionTransformer t = new SemTypeAssertionTransformer(fileName, syntaxTree, semtypeEnv); + 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() { + private List> getTypeAssertions() { syntaxTree.rootNode().accept(this); - List assertions = new ArrayList<>(); + List> assertions = new ArrayList<>(); for (String str : list) { String[] parts = splitAssertion(str); if (parts == null) { @@ -80,7 +83,7 @@ private List getTypeAssertions() { 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)); + assertions.add(new TypeAssertion<>(this.context, this.fileName, lhs, rhs, kind, text)); } return assertions; } @@ -101,27 +104,27 @@ private SemType toSemType(String typeExpr) { String memberAccessExpr = typeExpr.substring(leftBracketPos + 1, rightBracketPos); SemType type = typeNameSemTypeMap.get(typeRef); - if (SemTypes.isSubtypeSimple(type, PredefinedType.LIST)) { + if (semtypeAPI.isListType(type)) { SemType m; try { long l = Long.parseLong(memberAccessExpr); - m = SemTypes.intConst(l); + 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 (SemTypes.isSubtypeSimple(type, PredefinedType.MAPPING)) { + } else if (semtypeAPI.isMapType(type)) { SemType m = typeNameSemTypeMap.get(memberAccessExpr); - return SemTypes.mappingMemberTypeInnerVal(context, type, m); + return semtypeAPI.mappingMemberTypeInnerVal(context, type, m); } throw new IllegalStateException("Unsupported type test: " + typeExpr); } - private SemType listProj(Context context, SemType t, SemType m) { - SemType s1 = SemTypes.listProj(context, t, m); - SemType s2 = SemTypes.listMemberType(context, t, m); - if (!SemTypes.isSubtype(context, s1, s2)) { + 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; @@ -189,32 +192,6 @@ public void visit(ModulePartNode modulePartNode) { modulePartNode.eofToken().accept(this); } - /** - * Subtype test. - * - * @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 - */ - record TypeAssertion(Context context, String fileName, SemType lhs, SemType rhs, RelKind kind, String text) { - - TypeAssertion { - if (kind != null) { - assert lhs != null; - assert rhs != null; - } - } - - @Override - public String toString() { - return Paths.get(fileName).getFileName().toString() + ": " + text; - } - } - /** * Relationship to be asserted. * 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 index b3ef21754524..f25e69a2985b 100644 --- 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 @@ -15,755 +15,14 @@ * 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.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.expressions.BLangSimpleVarRef; -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.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; - -/** - * Resolves sem-types for module definitions. - * - * @since 2201.10.0 - */ -public class SemTypeResolver { - - private final Map attachedDefinitions = new HashMap<>(); - - void defineSemTypes(List moduleDefs, Context 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, 0); - } - } - } - - private void resolveConstant(Context cx, Map modTable, BLangConstant constant) { - SemType semtype = evaluateConst(constant); - addSemTypeBType(constant.getTypeNode(), semtype); - cx.env.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"); - } - } - - private SemType resolveTypeDefn(Context 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.env.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(Context 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(Context 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(cx, innerType); - } - return innerType; - } - - private static SemType getDistinctObjectType(Context cx, SemType innerType) { - return Core.intersect(SemTypes.objectDistinct(cx.env.distinctAtomCountGetAndIncrement()), innerType); - } - - private SemType resolveNonDistinctObject(Context cx, Map mod, BLangTypeDefinition defn, - int depth, BLangObjectTypeNode td) { - if (td.defn != null) { - return td.defn.getSemType(cx.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(cx.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(Context cx, Map mod, BLangTypeDefinition defn, int depth, - BLangFunction functionType) { - Definition attached = attachedDefinitions.get(functionType); - if (attached != null) { - return attached.getSemType(cx.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(cx.env, functionType.flagSet.contains(Flag.ISOLATED), - functionType.flagSet.contains(Flag.TRANSACTIONAL)); - return fd.define(cx.env, paramListDefinition.defineListTypeWrapped(cx.env, params, params.size(), rest, - CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); - } - - @NotNull - private List getParameters(Context 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(Context cx, Map mod, BLangTypeDefinition defn, int depth, - BLangFunctionTypeNode td) { - if (isFunctionTop(td)) { - if (td.flagSet.contains(Flag.ISOLATED) || td.flagSet.contains(Flag.TRANSACTIONAL)) { - FunctionQualifiers qualifiers = FunctionQualifiers.from(cx.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(cx.env, PredefinedType.NEVER, PredefinedType.VAL, qualifiers); - } - return PredefinedType.FUNCTION; - } - if (td.defn != null) { - return td.defn.getSemType(cx.env); - } - 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(cx.env, td.flagSet.contains(Flag.ISOLATED), - td.flagSet.contains(Flag.TRANSACTIONAL)); - return fd.define(cx.env, paramListDefinition.defineListTypeWrapped(cx.env, params, params.size(), rest, - CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); - } - - private SemType resolveReturnType(Context 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(cx.env, - !isDependentlyType ? PredefinedType.BOOLEAN : SemTypes.booleanConst(true), innerType); - } - - private SemType resolveDependentlyTypedReturnType(Context 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(Context cx, Map mod, BLangTypeDefinition defn, int depth, - BLangArrayType td) { - if (td.defn != null) { - return td.defn.getSemType(cx.env); - } - 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 static int from(Map mod, BLangNode expr) { - if (expr instanceof BLangLiteral literal) { - return listSize((Number) literal.value); - } else if (expr instanceof BLangSimpleVarRef varRef) { - String varName = varRef.variableName.value; - return from(mod, mod.get(varName)); - } else if (expr instanceof BLangConstant constant) { - return 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(); - } - - private SemType resolveListInner(Context cx, int size, SemType eType) { - ListDefinition ld = new ListDefinition(); - return resolveListInner(cx, ld, size, eType); - } - - private static SemType resolveListInner(Context cx, ListDefinition ld, int size, SemType eType) { - if (size != -1) { - return ld.defineListTypeWrapped(cx.env, List.of(eType), Math.abs(size), PredefinedType.NEVER, - CellAtomicType.CellMutability.CELL_MUT_LIMITED); - } else { - return ld.defineListTypeWrapped(cx.env, List.of(), 0, eType, - CellAtomicType.CellMutability.CELL_MUT_LIMITED); - } - } - - private SemType resolveTypeDesc(Context cx, Map mod, BLangTypeDefinition defn, int depth, - BLangTupleTypeNode td) { - if (td.defn != null) { - return td.defn.getSemType(cx.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(cx.env, memberSemTypes, memberSemTypes.size(), rest); - } - - private SemType resolveTypeDesc(Context cx, BLangValueType td) { - 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(cx); - case JSON: - return Core.createJson(cx); - default: - throw new IllegalStateException("Unknown type: " + td); - } - } - - private SemType resolveTypeDesc(Context cx, BLangBuiltInRefTypeNode td) { - return switch (td.typeKind) { - case NEVER -> PredefinedType.NEVER; - case XML -> PredefinedType.XML; - case JSON -> Core.createJson(cx); - default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); - }; - } - - private SemType resolveTypeDesc(Context 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, Context cx, Map mod, int depth, - BLangTypeDefinition typeDefinition) { - if (td.defn != null) { - return td.defn.getSemType(cx.env); - } - - MappingDefinition d = new MappingDefinition(); - td.defn = d; - - SemType rest = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, td.constraint); - return d.defineMappingTypeWrapped(cx.env, Collections.emptyList(), rest == null ? PredefinedType.NEVER : rest); - } - - private SemType resolveXmlTypeDesc(BLangConstrainedType td, Context 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, Context cx, Map mod, int depth, - BLangTypeDefinition defn) { - SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); - return SemTypes.futureContaining(cx.env, constraint); - } - - private SemType resolveTypedescTypeDesc(BLangConstrainedType td, Context cx, Map mod, int depth, - BLangTypeDefinition defn) { - SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); - return SemTypes.typedescContaining(cx.env, constraint); - } - - private SemType resolveTypeDesc(Context cx, BLangRecordTypeNode td, Map mod, int depth, - BLangTypeDefinition typeDefinition) { - if (td.defn != null) { - return td.defn.getSemType(cx.env); - } - - 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(cx); - } else { - rest = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, td.restFieldType); - } - - return d.defineMappingTypeWrapped(cx.env, fields, rest == null ? PredefinedType.NEVER : rest); - } - - private SemType resolveTypeDesc(Context 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(Context 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(Context 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(Context cx, SemType innerType) { - if (Core.isSubtypeSimple(innerType, PredefinedType.OBJECT)) { - return getDistinctObjectType(cx, innerType); - } else if (Core.isSubtypeSimple(innerType, PredefinedType.ERROR)) { - return getDistinctErrorType(cx, 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(Context cx, Map mod, BLangTypeDefinition defn, int depth, - BLangTableTypeNode td) { - SemType tableConstraint = resolveTypeDesc(cx, mod, defn, depth, td.constraint); - - if (td.tableKeySpecifier != null) { - List fieldNameIdentifierList = td.tableKeySpecifier.fieldNameIdentifierList; - String[] fieldNames = fieldNameIdentifierList.stream().map(IdentifierNode::getValue).toArray(String[]::new); - return TableSubtype.tableContainingKeySpecifier(cx, tableConstraint, fieldNames); - } - - if (td.tableKeyTypeConstraint != null) { - SemType keyConstraint = resolveTypeDesc(cx, mod, defn, depth, td.tableKeyTypeConstraint.keyType); - return TableSubtype.tableContainingKeyConstraint(cx, tableConstraint, keyConstraint); - } - - return TableSubtype.tableContaining(cx.env, tableConstraint); - } - - private SemType resolveTypeDesc(Context 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)) { - err = getDistinctErrorType(cx, err); - } - return err; - } - - private static SemType getDistinctErrorType(Context cx, SemType err) { - return Core.intersect(SemTypes.errorDistinct(cx.env.distinctAtomCountGetAndIncrement()), err); - } - - private SemType resolveTypeDesc(Context cx, Map mod, BLangTypeDefinition defn, int depth, - BLangStreamType td) { - if (td.constraint == null) { - return PredefinedType.STREAM; - } - - if (td.defn != null) { - return td.defn.getSemType(cx.env); - } - StreamDefinition d = new StreamDefinition(); - td.defn = d; +public interface SemTypeResolver { - 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(cx.env, valueType, completionType); - } + void defineSemTypes(List moduleDefs, TypeTestContext cx); } 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 index 93e9219c176c..c82368de4a9d 100644 --- 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 @@ -41,6 +41,7 @@ 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; @@ -101,37 +102,40 @@ public Object[] typeRelTestFileNameProvider() { listAllBalFiles(dataDir, balFiles); Collections.sort(balFiles); - List tests = new ArrayList<>(); + Collection> tests = new ArrayList<>(); for (File file : balFiles) { - List assertions = getTypeAssertions(file); + 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) { + private static List> getTypeAssertions(File file, + SemTypeResolver typeResolver, + TypeTestContext typeCheckContext, + TypeTestEnv typeTestEnv, + TypeTestAPI api, + BCompileUtil.PackageSyntaxTreePair pair) { String fileName = file.getAbsolutePath(); - BCompileUtil.PackageSyntaxTreePair pair = BCompileUtil.compileSemType(fileName); BLangPackage pkgNode = pair.bLangPackage; List typeAndClassDefs = new ArrayList<>(); typeAndClassDefs.addAll(pkgNode.constants); typeAndClassDefs.addAll(pkgNode.typeDefinitions); - SemTypeResolver typeResolver = new SemTypeResolver(); - Context typeCheckContext = Context.from(pkgNode.semtypeEnv); - List assertions; try { typeResolver.defineSemTypes(typeAndClassDefs, typeCheckContext); - assertions = SemTypeAssertionTransformer.getTypeAssertionsFrom(fileName, pair.syntaxTree, - pkgNode.semtypeEnv); + return SemTypeAssertionTransformer.getTypeAssertionsFrom(fileName, pair.syntaxTree, typeTestEnv, + typeCheckContext, api); } catch (Exception e) { - assertions = new ArrayList<>(List.of(new SemTypeAssertionTransformer.TypeAssertion( + return List.of(new TypeAssertion<>( null, fileName, null, null, null, e.getMessage() - ))); + )); } - return assertions; } public void listAllBalFiles(File file, List balFiles) { @@ -205,50 +209,94 @@ public void funcTest(String fileName) { @Test(expectedExceptions = AssertionError.class) public void shouldFailForIncorrectTestStructure() { File wrongAssertionFile = resolvePath("test-src/type-rel-wrong.bal").toFile(); - List typeAssertions = getTypeAssertions(wrongAssertionFile); + TypeCheckData utils = compilerTypeResolverUtilsFromFile(wrongAssertionFile); + List> typeAssertions = getTypeAssertions(wrongAssertionFile, + utils.resolver(), utils.context(), utils.env(), CompilerTypeTestAPI.getInstance(), utils.pair() + ); testSemTypeAssertions(typeAssertions.get(0)); } + @Test() + public void testRuntimeSemTypes() { + File file = resolvePath("test-src/type-rel/float-tv.bal").toFile(); + var utils = runtimeTypeResolverUtilsFromFile(file); + List> assertions = getTypeAssertions(file, + utils.resolver(), utils.context(), utils.env(), RuntimeTypeTestAPI.getInstance(), utils.pair()); + testAssertion(assertions.get(0), RuntimeTypeTestAPI.getInstance()); + } + + 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(); + 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(); - List typeAssertions = getTypeAssertions(wrongAssertionFile); + 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(SemTypeAssertionTransformer.TypeAssertion typeAssertion) { + 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)); + semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); Assert.assertFalse( - SemTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), - formatFailingAssertionDescription(typeAssertion)); + 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.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); Assert.assertFalse( - SemTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + 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)); + 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(SemTypeAssertionTransformer.TypeAssertion typeAssertion) { + private String formatFailingAssertionDescription(TypeAssertion typeAssertion) { return typeAssertion.text() + "\n in: " + typeAssertion.fileName(); } @@ -274,11 +322,12 @@ private List getSubtypeRels(String sourceFilePath) { typeAndClassDefs.addAll(pkgNode.constants); typeAndClassDefs.addAll(pkgNode.typeDefinitions); - SemTypeResolver typeResolver = new SemTypeResolver(); - Context typeCheckContext = Context.from(pkgNode.semtypeEnv); + 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")) @@ -292,10 +341,10 @@ private List getSubtypeRels(String sourceFilePath) { SemType t1 = typeMap.get(name1); SemType t2 = typeMap.get(name2); - if (SemTypes.isSubtype(typeCheckContext, t1, t2)) { + if (api.isSubtype(typeCheckContext, t1, t2)) { subtypeRelations.add(TypeRel.rel(name1, name2)); } - if (SemTypes.isSubtype(typeCheckContext, t2, t1)) { + if (api.isSubtype(typeCheckContext, t2, t1)) { subtypeRelations.add(TypeRel.rel(name2, name1)); } } 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..8db88e809dba --- /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 Actual semtype definition on which assertion is defined on (runtime or compile time) + * @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..63d4ef2b4bfc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.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.semtype.port.test; + +public interface TypeTestAPI { + + boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2); + + // TODO: may be introduce is mapping and is list + 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/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java new file mode 100644 index 000000000000..772140848a92 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java @@ -0,0 +1,24 @@ +/* + * 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 TypeTestContextBuilder { + + Context createFrom(TypeTestEnv env); +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java new file mode 100644 index 000000000000..727232e5acd8 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.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; + +import java.util.Map; + +public interface TypeTestEnv { + + Map getTypeNameSemTypeMap(); + + void addTypeDef(String value, SemType semtype); +} 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..47d07b7a339d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal @@ -0,0 +1,8 @@ +// @type NegativeZero < Float +// @type Zero < Float +// @type NegativeZero = Zero + +type Zero 0.0; +type NegativeZero -0.0; + +type Float float; From 4a1d4a3f60bff7983bfd2c66487a4d6d4b89fcf3 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 28 May 2024 18:38:30 +0530 Subject: [PATCH 014/178] Add runtime type tests provider --- .../semtype/port/test/SemTypeTest.java | 105 ++++++++++++++++-- .../resources/test-src/type-rel/float-tv.bal | 2 +- .../type-rel/{proj10-t.bal => proj10-tv.bal} | 0 .../type-rel/{proj3-t.bal => proj3-tv.bal} | 0 .../type-rel/{proj4-t.bal => proj4-tv.bal} | 0 .../type-rel/{proj5-t.bal => proj5-tv.bal} | 0 .../type-rel/{proj6-t.bal => proj6-tv.bal} | 0 .../type-rel/{proj7-t.bal => proj7-tv.bal} | 0 .../type-rel/{proj8-t.bal => proj8-tv.bal} | 0 .../type-rel/{proj9-t.bal => proj9-tv.bal} | 0 10 files changed, 96 insertions(+), 11 deletions(-) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj10-t.bal => proj10-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj3-t.bal => proj3-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj4-t.bal => proj4-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj5-t.bal => proj5-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj6-t.bal => proj6-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj7-t.bal => proj7-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj8-t.bal => proj8-tv.bal} (100%) rename tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/{proj9-t.bal => proj9-tv.bal} (100%) 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 index c82368de4a9d..dc9c1d746303 100644 --- 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 @@ -46,7 +46,9 @@ 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; @@ -156,10 +158,10 @@ public void listAllBalFiles(File file, List balFiles) { public final HashSet typeRelDirSkipList() { HashSet hashSet = new HashSet<>(); // intersection with negative (!) atom - hashSet.add("proj7-t.bal"); - hashSet.add("proj8-t.bal"); - hashSet.add("proj9-t.bal"); - hashSet.add("proj10-t.bal"); + 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"); @@ -216,13 +218,96 @@ public void shouldFailForIncorrectTestStructure() { testSemTypeAssertions(typeAssertions.get(0)); } - @Test() - public void testRuntimeSemTypes() { - File file = resolvePath("test-src/type-rel/float-tv.bal").toFile(); + @DataProvider(name = "runtimeFileNameProviderFunc") + public Object[] runtimeFileNameProviderFunc() { + File dataDir = resolvePath("test-src/type-rel").toFile(); + List balFiles = new ArrayList<>(); + listAllBalFiles(dataDir, balFiles); + Collections.sort(balFiles); + Predicate tableFilter = createRuntimeFileNameFilter(Set.of( + "anydata-tv.bal", + "table2-t.bal", + "table3-t.bal", + "table-readonly-t.bal", + "table-t.bal" + )); + Predicate listFilter = createRuntimeFileNameFilter(Set.of( + "bdddiff1-tv.bal", + "fixed-length-array-t.bal", + "fixed-length-array2-t.bal", + "fixed-length-array-large-t.bal", + "fixed-length-array-tuple-t.bal", + "list1-tv.bal", + "proj1-tv.bal", + "proj2-tv.bal", + "proj3-tv.bal", + "proj4-tv.bal", + "proj5-tv.bal", + "proj6-tv.bal", + "proj7-tv.bal", + "proj8-tv.bal", + "proj9-tv.bal", + "proj10-tv.bal", + "test_test.bal", + "tuple1-tv.bal", + "tuple2-tv.bal", + "tuple3-tv.bal", + "tuple4-tv.bal" + )); + Predicate functionFilter = createRuntimeFileNameFilter(Set.of( + "function2-tv.bal", + "function-intersection-tv.bal", + "function-param-tv.bal", + "function-rec-tv.bal", + "function-rest-tv.bal", + "function-tv.bal", + "function-union-tv.bal" + )); + Predicate mappingFilter = createRuntimeFileNameFilter(Set.of( + "mapping1-t.bal", + "mapping2-t.bal", + "mapping3-t.bal", + "mapping-record-tv.bal", + "mapping-t.bal", + "mappingIntersect-tv.bal", + "mutable-record-t.bal", + "optional-field-record1-t.bal", + "optional-field-record2-t.bal", + "optional-field-record3-t.bal", + "record-proj-tv.bal", + "record-t.bal", + "recursive-record-t.bal", + "test_test.bal" + )); + Predicate xmlFilter = createRuntimeFileNameFilter(Set.of( + "xml-complex-ro-tv.bal", + "xml-complex-rw-tv.bal", + "xml-never-tv.bal", + "xml-readonly-tv.bal", + "xml-sequence-tv.bal", + "xml-te.bal" + )); + return balFiles.stream() + .filter(listFilter) + .filter(tableFilter) + .filter(functionFilter) + .filter(mappingFilter) + .filter(xmlFilter) + .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); - List> assertions = getTypeAssertions(file, - utils.resolver(), utils.context(), utils.env(), RuntimeTypeTestAPI.getInstance(), utils.pair()); - testAssertion(assertions.get(0), RuntimeTypeTestAPI.getInstance()); + 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) { 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 index 47d07b7a339d..7f2c95edfb46 100644 --- 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 @@ -1,6 +1,6 @@ // @type NegativeZero < Float // @type Zero < Float -// @type NegativeZero = Zero +// @type NegativeZero <> Zero type Zero 0.0; type NegativeZero -0.0; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal similarity index 100% rename from tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-t.bal rename to tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal From de8b70b049727e3e2d516ad3ef5658a9d927df60 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 29 May 2024 09:03:37 +0530 Subject: [PATCH 015/178] Add more basic type tests --- .../resources/test-src/type-rel/basic-tv.bal | 17 +++++++++++++++++ .../resources/test-src/type-rel/decimal-tv.bal | 13 +++++++++++++ .../resources/test-src/type-rel/float-tv.bal | 14 +++++++++++++- .../resources/test-src/type-rel/string-tv.bal | 16 ++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal 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/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/float-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal index 7f2c95edfb46..ecece93d1dc4 100644 --- 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 @@ -1,8 +1,20 @@ // @type NegativeZero < Float // @type Zero < Float -// @type NegativeZero <> Zero +// @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/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; From b0ea088202f9f93762f960d6b5a503123f4bf110 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 29 May 2024 11:36:45 +0530 Subject: [PATCH 016/178] Fix compile time type bugs --- .../ballerina/types/subtypedata/StringSubtype.java | 12 +++++++----- .../java/io/ballerina/types/typeops/FloatOps.java | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java index dda6ea60d977..edd0c4dad869 100644 --- a/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java @@ -35,6 +35,8 @@ */ public class StringSubtype implements ProperSubtypeData { + private static final EnumerableString[] EMPTY_STRING_ARR = {}; + private static final EnumerableCharString[] EMPTY_CHAR_ARR = {}; CharStringSubtype charData; NonCharStringSubtype nonCharData; @@ -98,12 +100,12 @@ public static Optional stringSubtypeSingleValue(SubtypeData d) { public static SemType stringConst(String value) { CharStringSubtype chara; NonCharStringSubtype nonChar; - if (value.length() == 1) { + if (value.codePointCount(0, value.length()) == 1) { chara = CharStringSubtype.from(true, new EnumerableCharString[]{EnumerableCharString.from(value)}); - nonChar = NonCharStringSubtype.from(true, new EnumerableString[]{}); + nonChar = NonCharStringSubtype.from(true, EMPTY_STRING_ARR); } else { - chara = CharStringSubtype.from(true, new EnumerableCharString[]{}); + 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)); @@ -111,8 +113,8 @@ public static SemType stringConst(String value) { public static SemType stringChar() { StringSubtype st = new StringSubtype( - CharStringSubtype.from(false, new EnumerableCharString[]{}), - NonCharStringSubtype.from(true, new EnumerableString[]{})); + CharStringSubtype.from(false, EMPTY_CHAR_ARR), + NonCharStringSubtype.from(true, EMPTY_STRING_ARR)); return PredefinedType.basicSubtype(BasicTypeCode.BT_STRING, st); } diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java index e674317ef86e..1264bbcf9671 100644 --- a/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java @@ -56,7 +56,7 @@ public SubtypeData diff(SubtypeData t1, SubtypeData t2) { @Override public SubtypeData complement(SubtypeData t) { FloatSubtype s = (FloatSubtype) t; - return FloatSubtype.createFloatSubtype(!s.allowed, (EnumerableFloat[]) s.values); + return FloatSubtype.createFloatSubtype(!s.allowed, s.values); } @Override From 865b63a1d99c2d6156476298cf1421b312b00723 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 3 Jun 2024 09:16:05 +0530 Subject: [PATCH 017/178] Refactor type resolvers Fix doc comments --- .../port/test/CompilerSemTypeResolver.java | 12 +- .../port/test/RuntimeSemTypeResolver.java | 103 +++++------------- .../test/SemTypeAssertionTransformer.java | 1 + .../semtype/port/test/SemTypeResolver.java | 35 +++++- .../semtype/port/test/SemTypeTest.java | 11 +- .../semtype/port/test/TypeAssertion.java | 2 +- .../semtype/port/test/TypeTestAPI.java | 1 - .../port/test/TypeTestContextBuilder.java | 24 ---- 8 files changed, 84 insertions(+), 105 deletions(-) delete mode 100644 tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java 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 index 2bbb867f4a81..0ae3c91e47f3 100644 --- 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 @@ -88,7 +88,7 @@ * * @since 2201.10.0 */ -public class CompilerSemTypeResolver implements SemTypeResolver { +public class CompilerSemTypeResolver extends SemTypeResolver { private final Map attachedDefinitions = new HashMap<>(); @@ -111,7 +111,9 @@ public void defineSemTypes(List moduleDefs, TypeTestContext } } - private void resolveConstant(TypeTestContext cx, Map modTable, BLangConstant constant) { + @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); @@ -132,6 +134,12 @@ private SemType evaluateConst(BLangConstant constant) { } } + @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) { 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 index 315e1c15e4d6..d2cdad6b54c8 100644 --- 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 @@ -36,11 +36,8 @@ import org.wso2.ballerinalang.compiler.tree.types.BLangValueType; import java.math.BigDecimal; -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.Optional; @@ -53,38 +50,23 @@ 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 org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; -public class RuntimeSemTypeResolver implements SemTypeResolver { +class RuntimeSemTypeResolver extends SemTypeResolver { Map attachedSemType = new HashMap<>(); Map semTypeMemo = new HashMap<>(); - // TODO: may be we need an abstract class @Override - public void defineSemTypes(List moduleDefs, TypeTestContext cx) { - Map modTable = new LinkedHashMap<>(); - // TODO: stream - 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); - } - } + public void resolveTypeDefn(TypeTestContext cx, Map modTable, + BLangTypeDefinition typeDefinition) { + resolveTypeDefnRec(cx, modTable, typeDefinition, 0); } - private 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, @@ -115,22 +97,15 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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); + default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); + }; } private SemType resolveSingletonType(BLangFiniteTypeNode td) { @@ -139,7 +114,6 @@ private SemType resolveSingletonType(BLangFiniteTypeNode td) { .reduce(Builder.neverType(), Core::union); } - // TODO: common code? private Optional resolveSingletonType(Object value, TypeKind targetTypeKind) { return switch (targetTypeKind) { case NIL -> Optional.of(Builder.nilType()); @@ -160,22 +134,17 @@ private Optional resolveSingletonType(Object value, TypeKind targetType 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 Optional.empty(); } } yield Optional.of(Builder.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 -> { String repr = (String) value; if (repr.contains("d") || repr.contains("D")) { repr = repr.substring(0, repr.length() - 1); } - BigDecimal d = new BigDecimal(repr); - yield Optional.of(Builder.decimalConst(d)); + yield Optional.of(Builder.decimalConst(new BigDecimal(repr))); } case STRING -> Optional.of(Builder.stringConst((String) value)); default -> Optional.empty(); @@ -249,24 +218,16 @@ private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { } private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { - switch (td.typeKind) { - case NIL: - return Builder.nilType(); - case BOOLEAN: - return Builder.booleanType(); - case BYTE: - return Builder.intRange(0, UNSIGNED8_MAX_VALUE); - case INT: - return Builder.intType(); - case FLOAT: - return Builder.floatType(); - case DECIMAL: - return Builder.decimalType(); - case STRING: - return Builder.stringType(); - default: - throw new IllegalStateException("Unknown type: " + td); - } + return switch (td.typeKind) { + case NIL -> Builder.nilType(); + case BOOLEAN -> Builder.booleanType(); + case BYTE -> Builder.intRange(0, UNSIGNED8_MAX_VALUE); + case INT -> Builder.intType(); + case FLOAT -> Builder.floatType(); + case DECIMAL -> Builder.decimalType(); + case STRING -> Builder.stringType(); + default -> throw new IllegalStateException("Unknown type: " + td); + }; } private SemType evaluateConst(BLangConstant constant) { @@ -279,12 +240,6 @@ private SemType evaluateConst(BLangConstant constant) { }; } - private void resolveConstant(TypeTestContext cx, Map modTable, BLangConstant constant) { - SemType semtype = evaluateConst(constant); - attachToBType(constant.typeNode, semtype); - cx.getEnv().addTypeDef(constant.name.value, 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/SemTypeAssertionTransformer.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java index eabe09e1eb87..b43a4fa84f33 100644 --- 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 @@ -36,6 +36,7 @@ /** * 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 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 index f25e69a2985b..f8695377e5cf 100644 --- 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 @@ -18,11 +18,42 @@ 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 java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; -public interface SemTypeResolver { +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; - void defineSemTypes(List moduleDefs, TypeTestContext cx); +public abstract class SemTypeResolver { + + 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 index dc9c1d746303..795eb6bc70ab 100644 --- 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 @@ -261,7 +261,8 @@ public Object[] runtimeFileNameProviderFunc() { "function-rec-tv.bal", "function-rest-tv.bal", "function-tv.bal", - "function-union-tv.bal" + "function-union-tv.bal", + "func-quals-tv.bal" )); Predicate mappingFilter = createRuntimeFileNameFilter(Set.of( "mapping1-t.bal", @@ -287,12 +288,20 @@ public Object[] runtimeFileNameProviderFunc() { "xml-sequence-tv.bal", "xml-te.bal" )); + Predicate objectFilter = createRuntimeFileNameFilter(Set.of( + "object-binaryops-tv.bal", + "object-qulifiers-tv.bal", + "object-rec-tv.bal", + "object-simple-tv.bal", + "object-distinct-tv.bal" + )); return balFiles.stream() .filter(listFilter) .filter(tableFilter) .filter(functionFilter) .filter(mappingFilter) .filter(xmlFilter) + .filter(objectFilter) .map(File::getAbsolutePath).toArray(); } 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 index 8db88e809dba..e4452711b6c1 100644 --- 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 @@ -23,7 +23,7 @@ /** * Subtype test. * - * @param Actual semtype definition on which assertion is defined on (runtime or compile time) + * @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. 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 index 63d4ef2b4bfc..60dc1190abfb 100644 --- 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 @@ -22,7 +22,6 @@ public interface TypeTestAPI { boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2); - // TODO: may be introduce is mapping and is list boolean isSubtypeSimple(SemType t1, SemType t2); boolean isListType(SemType t); diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java deleted file mode 100644 index 772140848a92..000000000000 --- a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContextBuilder.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 TypeTestContextBuilder { - - Context createFrom(TypeTestEnv env); -} From d197c7fd3c5d67f8743d94d211a1bab3b30168b8 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 28 May 2024 18:38:30 +0530 Subject: [PATCH 018/178] Add runtime type tests provider --- .../src/test/resources/test-src/type-rel/float-tv.bal | 2 ++ 1 file changed, 2 insertions(+) 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 index ecece93d1dc4..e11c0d03170c 100644 --- 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 @@ -3,6 +3,7 @@ // @type NegativeZero = Zero type Zero 0.0; + type NegativeZero -0.0; type Float float; @@ -17,4 +18,5 @@ type D1 0.0d; // @type F2 = Zero // @type F2 = NegativeZero type F1 Zero|NegativeZero; + type F2 F1 & Zero; From 7c20ff138f49002cae431622519efbc82737d750 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 29 May 2024 15:53:25 +0530 Subject: [PATCH 019/178] Make it possible to create cell type --- .../runtime/api/types/semtype/Atom.java | 24 ++++++ .../runtime/api/types/semtype/AtomicType.java | 23 ++++++ .../runtime/api/types/semtype/Bdd.java | 23 ++++++ .../api/types/semtype/BddAllOrNothing.java | 24 ++++++ .../runtime/api/types/semtype/BddNode.java | 82 +++++++++++++++++++ .../runtime/api/types/semtype/Builder.java | 8 ++ .../api/types/semtype/CellAtomicType.java | 28 +++++++ .../runtime/api/types/semtype/Env.java | 55 +++++++++++++ .../runtime/api/types/semtype/TypeAtom.java | 26 ++++++ .../runtime/test/semtype/CoreTests.java | 36 ++++++++ 10 files changed, 329 insertions(+) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java create mode 100644 bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java 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..c70309adae90 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java @@ -0,0 +1,24 @@ +/* + * 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; + +public interface 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..051a3da67614 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java @@ -0,0 +1,23 @@ +/* + * 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; + +public interface AtomicType { + +} 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..75b059df4513 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java @@ -0,0 +1,23 @@ +/* + * 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; + +public interface Bdd { + +} 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..7a93ed73b11c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java @@ -0,0 +1,24 @@ +/* + * 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; + +public enum BddAllOrNothing implements Bdd { + ALL, + NOTHING +} 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..5a2c44981aef --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.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.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +public final class BddNode extends SubType implements Bdd { + + private final Atom atom; + private final Bdd left; + private final Bdd middle; + private final Bdd right; + + public BddNode(Atom atom, Bdd left, Bdd middle, Bdd right) { + super(false, false); + this.atom = atom; + this.left = left; + this.middle = middle; + this.right = right; + } + + static BddNode bddAtom(TypeAtom atom) { + return new BddNode(atom, BddAllOrNothing.ALL, BddAllOrNothing.NOTHING, BddAllOrNothing.NOTHING); + } + + public Atom atom() { + return atom; + } + + public Bdd left() { + return left; + } + + public Bdd middle() { + return middle; + } + + public Bdd right() { + return right; + } + + @Override + public SubType union(SubType other) { + throw new IllegalStateException("Unimplemented"); + } + + @Override + public SubType intersect(SubType other) { + throw new IllegalStateException("Unimplemented"); + } + + @Override + public SubType complement() { + throw new IllegalStateException("Unimplemented"); + } + + @Override + public boolean isEmpty() { + throw new IllegalStateException("Unimplemented"); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("Unimplemented"); + } +} 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 index d417a3825203..7026bd3608d8 100644 --- 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 @@ -190,6 +190,14 @@ public static Optional typeOf(Object object) { return Optional.empty(); } + public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { + // TODO:Cache this for pure basic types + never + CellAtomicType atomicCell = new CellAtomicType(ty, mut); + TypeAtom atom = env.cellAtom(atomicCell); + BddNode bdd = BddNode.bddAtom(atom); + return basicSubType(BasicTypeCode.BT_CELL, bdd); + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java new file mode 100644 index 000000000000..1b0af4e1a593 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.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.runtime.api.types.semtype; + +public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { + + public enum CellMutability { + CELL_MUT_NONE, + CELL_MUT_LIMITED, + CELL_MUT_UNLIMITED + } +} 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..3a73a8cafd4a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java @@ -0,0 +1,55 @@ +/* + * 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 java.util.HashMap; +import java.util.Map; + +public final class Env { + + private final static Env INSTANCE = new Env(); + + private final Map atomTable; + + private Env() { + this.atomTable = new HashMap<>(); + } + + public static Env getInstance() { + return INSTANCE; + } + + public TypeAtom cellAtom(CellAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private TypeAtom typeAtom(AtomicType atomicType) { + // FIXME: use a rw lock? + synchronized (this.atomTable) { + TypeAtom ta = this.atomTable.get(atomicType); + if (ta != null) { + return ta; + } else { + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + this.atomTable.put(result.atomicType(), result); + return result; + } + } + } +} 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..2edc97277938 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java @@ -0,0 +1,26 @@ +/* + * 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; + +public record TypeAtom(int index, AtomicType atomicType) implements Atom { + + public static TypeAtom createTypeAtom(int index, AtomicType atomicType) { + return new TypeAtom(index, atomicType); + } +} 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..dcf2cebb6f44 --- /dev/null +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java @@ -0,0 +1,36 @@ +/* + * 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.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import org.testng.annotations.Test; + +public class CoreTests { + + @Test + public void testCellContaining() { + Env env = Env.getInstance(); + SemType intTy = Builder.intType(); + SemType readonlyInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + SemType mutableInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + } +} \ No newline at end of file From 293ddab389d208ea068fd9c7874bcb1592883b3f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 30 May 2024 06:43:52 +0530 Subject: [PATCH 020/178] Implement bdd operations --- .../runtime/api/types/semtype/Bdd.java | 154 +++++++++++++++++- .../api/types/semtype/BddAllOrNothing.java | 11 +- .../runtime/api/types/semtype/BddNode.java | 28 +--- .../runtime/api/types/semtype/RecAtom.java | 28 ++++ .../runtime/test/semtype/CoreTests.java | 6 + 5 files changed, 196 insertions(+), 31 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java 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 index 75b059df4513..3a830d9196ea 100644 --- 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 @@ -18,6 +18,158 @@ package io.ballerina.runtime.api.types.semtype; -public interface Bdd { +import io.ballerina.runtime.internal.types.semtype.SubTypeData; +public abstract class Bdd extends SubType { + + protected 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 complement() { + return bddComplement(); + } + + private Bdd bddComplement() { + if (this == BddAllOrNothing.ALL) { + return BddAllOrNothing.NOTHING; + } else if (this == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.ALL; + } + // TODO: may be factor this out + 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 BddNode(atom, left, middle, right); + } + + @Override + public boolean isEmpty() { + throw new IllegalStateException("Unimplemented"); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("Unimplemented"); + } } 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 index 7a93ed73b11c..faae6239dd16 100644 --- 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 @@ -18,7 +18,12 @@ package io.ballerina.runtime.api.types.semtype; -public enum BddAllOrNothing implements Bdd { - ALL, - NOTHING +public final class BddAllOrNothing extends Bdd { + + public static BddAllOrNothing ALL = new BddAllOrNothing(true); + public static BddAllOrNothing NOTHING = new BddAllOrNothing(false); + + private BddAllOrNothing(boolean all) { + super(all, !all); + } } 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 index 5a2c44981aef..1ad35467ac27 100644 --- 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 @@ -18,9 +18,7 @@ package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.internal.types.semtype.SubTypeData; - -public final class BddNode extends SubType implements Bdd { +public final class BddNode extends Bdd { private final Atom atom; private final Bdd left; @@ -55,28 +53,4 @@ public Bdd right() { return right; } - @Override - public SubType union(SubType other) { - throw new IllegalStateException("Unimplemented"); - } - - @Override - public SubType intersect(SubType other) { - throw new IllegalStateException("Unimplemented"); - } - - @Override - public SubType complement() { - throw new IllegalStateException("Unimplemented"); - } - - @Override - public boolean isEmpty() { - throw new IllegalStateException("Unimplemented"); - } - - @Override - public SubTypeData data() { - throw new IllegalStateException("Unimplemented"); - } } 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..8a862725157f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java @@ -0,0 +1,28 @@ +/* + * 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; + +public class RecAtom implements Atom { + + @Override + public int index() { + throw new IllegalStateException("unimplemented"); + } +} 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 index dcf2cebb6f44..4ed71a08822d 100644 --- 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 @@ -20,6 +20,8 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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 org.testng.annotations.Test; @@ -29,8 +31,12 @@ public class CoreTests { @Test public void testCellContaining() { Env env = Env.getInstance(); + Context cx = new Context(); SemType intTy = Builder.intType(); SemType readonlyInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + assert Core.isSubType(cx, readonlyInt, readonlyInt); SemType mutableInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + assert Core.isSubType(cx, mutableInt, mutableInt); + assert Core.isSubType(cx, readonlyInt, mutableInt); } } \ No newline at end of file From b3f2d6d51ea39695c98337739aaa28d893d13079 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 30 May 2024 07:12:23 +0530 Subject: [PATCH 021/178] Introduce cell delegate --- .../runtime/api/types/semtype/BddNode.java | 2 +- .../runtime/api/types/semtype/Builder.java | 3 +- .../internal/types/semtype/BCellSubType.java | 79 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java 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 index 1ad35467ac27..98ad572d5790 100644 --- 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 @@ -18,7 +18,7 @@ package io.ballerina.runtime.api.types.semtype; -public final class BddNode extends Bdd { +public class BddNode extends Bdd { private final Atom atom; private final Bdd left; 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 index 7026bd3608d8..b3bfafcd75c3 100644 --- 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BType; 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; @@ -195,7 +196,7 @@ public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMut CellAtomicType atomicCell = new CellAtomicType(ty, mut); TypeAtom atom = env.cellAtom(atomicCell); BddNode bdd = BddNode.bddAtom(atom); - return basicSubType(BasicTypeCode.BT_CELL, bdd); + return basicSubType(BasicTypeCode.BT_CELL, BCellSubType.createDelegate(bdd)); } private static final class IntTypeCache { 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..937f15b47611 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java @@ -0,0 +1,79 @@ +/* + * 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.SubType; + +// TODO: would making this a child class of say BddNode be faster than making this a delegate +// -- Problem with this is modeling type operations (union, intersect, complement) since parent must return a Cell +// as well +public class BCellSubType extends SubType { + + private final Bdd inner; + + private BCellSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BCellSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BCellSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + // FIXME: if all or nothing do the same thing as cellSubtypeDataEnsureProper + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BCellSubType bCell) { + return new BCellSubType(bCell.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BCellSubType otherCell)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherCell.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BCellSubType otherCell)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherCell.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } +} From 6994da4b30a9771b59ae711c982a264d296aa5b4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 30 May 2024 15:30:48 +0530 Subject: [PATCH 022/178] Implement cell subtype --- .../runtime/api/types/semtype/Bdd.java | 31 ++++- .../runtime/api/types/semtype/BddNode.java | 2 +- .../api/types/semtype/BddPredicate.java | 26 +++++ .../runtime/api/types/semtype/Builder.java | 27 +++-- .../api/types/semtype/Conjunction.java | 27 +++++ .../runtime/api/types/semtype/Core.java | 2 +- .../runtime/api/types/semtype/SubType.java | 2 +- .../types/semtype/BBooleanSubType.java | 3 +- .../internal/types/semtype/BCellSubType.java | 108 +++++++++++++++++- .../types/semtype/BDecimalSubType.java | 3 +- .../internal/types/semtype/BFloatSubType.java | 3 +- .../internal/types/semtype/BIntSubType.java | 3 +- .../types/semtype/BStringSubType.java | 3 +- .../internal/types/semtype/BSubType.java | 3 +- .../runtime/test/semtype/CoreTests.java | 3 +- 15 files changed, 223 insertions(+), 23 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java 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 index 3a830d9196ea..3873e695ac1d 100644 --- 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 @@ -20,7 +20,9 @@ import io.ballerina.runtime.internal.types.semtype.SubTypeData; -public abstract class Bdd extends SubType { +import static io.ballerina.runtime.api.types.semtype.Conjunction.and; + +public abstract sealed class Bdd extends SubType permits BddAllOrNothing, BddNode { protected Bdd(boolean all, boolean nothing) { super(all, nothing); @@ -164,7 +166,7 @@ private Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { throw new IllegalStateException("Unimplemented"); } @@ -172,4 +174,29 @@ public boolean isEmpty() { public SubTypeData data() { throw new IllegalStateException("Unimplemented"); } + + public 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); + } + + + + + + + + + + + + + + + } 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 index 98ad572d5790..1ad35467ac27 100644 --- 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 @@ -18,7 +18,7 @@ package io.ballerina.runtime.api.types.semtype; -public class BddNode extends Bdd { +public final class BddNode extends Bdd { private final Atom atom; private final Bdd left; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java new file mode 100644 index 000000000000..76a3a57ce1d3 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java @@ -0,0 +1,26 @@ +/* + * 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; + +@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 index b3bfafcd75c3..61d7ecc3d9cb 100644 --- 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 @@ -35,6 +35,7 @@ import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; /** * Utility class for creating semtypes. @@ -105,18 +106,22 @@ public static SemType charType() { } private static final SemType NEVER = SemType.from(0); + private static final SemType VAL = SemType.from(VT_MASK); public static SemType basicTypeUnion(int bitset) { - // TODO: may be cache single type bit sets as well - if (bitset == 0) { - return NEVER; - } else if (Integer.bitCount(bitset) == 1) { - int code = Integer.numberOfTrailingZeros(bitset); - if (BasicTypeCache.isCached(code)) { - return BasicTypeCache.cache[code]; + return switch (bitset) { + case 0 -> NEVER; + case VT_MASK -> VAL; + default -> { + if (Integer.bitCount(bitset) == 1) { + int code = Integer.numberOfTrailingZeros(bitset); + if (BasicTypeCache.isCached(code)) { + yield BasicTypeCache.cache[code]; + } + } + yield SemType.from(bitset); } - } - return SemType.from(bitset); + }; } public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { @@ -199,6 +204,10 @@ public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMut return basicSubType(BasicTypeCode.BT_CELL, BCellSubType.createDelegate(bdd)); } + public static SemType val() { + return basicTypeUnion(VT_MASK); + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java new file mode 100644 index 000000000000..719c8838b4b7 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java @@ -0,0 +1,27 @@ +/* + * 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; + +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/Core.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java index 76c42278ef3f..40f02d9850a8 100644 --- 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 @@ -207,7 +207,7 @@ public static boolean isEmpty(Context cx, SemType t) { if (subType == null) { continue; } - if (!subType.isEmpty()) { + if (!subType.isEmpty(cx)) { return false; } } 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 index e8382dedae2b..a70667c974e4 100644 --- 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 @@ -45,7 +45,7 @@ public SubType diff(SubType other) { public abstract SubType complement(); - public abstract boolean isEmpty(); + public abstract boolean isEmpty(Context cx); public final boolean isAll() { return all; 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 index 9252938a17e8..a6f67f8aa800 100644 --- 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 @@ -18,6 +18,7 @@ package io.ballerina.runtime.internal.types.semtype; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SubType; /** @@ -120,7 +121,7 @@ public SubType complement() { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { return data.isNothing(); } 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 index 937f15b47611..db9e35967110 100644 --- 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 @@ -19,8 +19,18 @@ 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.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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 io.ballerina.runtime.api.types.semtype.TypeAtom; + +import java.util.function.Predicate; // TODO: would making this a child class of say BddNode be faster than making this a delegate // -- Problem with this is modeling type operations (union, intersect, complement) since parent must return a Cell @@ -46,6 +56,10 @@ public static BCellSubType createDelegate(SubType inner) { throw new IllegalArgumentException("Unexpected inner type"); } + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + @Override public SubType union(SubType other) { if (!(other instanceof BCellSubType otherCell)) { @@ -68,12 +82,102 @@ public SubType complement() { } @Override - public boolean isEmpty() { - throw new IllegalStateException("unimplemented"); + public boolean isEmpty(Context cx) { + return Bdd.bddEvery(cx, inner, null, null, BCellSubType::cellFormulaIsEmpty); } @Override public SubTypeData data() { throw new IllegalStateException("unimplemented"); } + + private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + CellAtomicType combined; + if (posList == null) { + combined = new CellAtomicType(Builder.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 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, Builder.val(), cellAtomType(neg.atom()).ty())) { + return false; + } + neg = neg.next(); + } + SemType negListUnionResult = filteredCellListUnion(negList, + conjunction -> 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 = 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.neverType(); + Conjunction neg = negList; + while (neg != null) { + if (predicate.test(neg)) { + negUnion = Core.union(negUnion, cellAtomType(neg.atom()).ty()); + } + neg = neg.next(); + } + return negUnion; + } + + private static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { + SemType ty = Core.intersect(c1.ty(), c2.ty()); + CellAtomicType.CellMutability mut = min(c1.mut(), c2.mut()); + return new CellAtomicType(ty, mut); + } + + private static CellAtomicType.CellMutability min(CellAtomicType.CellMutability m1, + CellAtomicType.CellMutability m2) { + return m1.compareTo(m2) <= 0 ? m1 : m2; + } } 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 index 6bdbd4839039..d534ff842e42 100644 --- 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 @@ -19,6 +19,7 @@ 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; @@ -113,7 +114,7 @@ public SubType complement() { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { return data == AllOrNothing.NOTHING; } 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 index cb1d63a2f0a3..b2a786ddf450 100644 --- 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 @@ -19,6 +19,7 @@ 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; @@ -111,7 +112,7 @@ public SubType complement() { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { return data == AllOrNothing.NOTHING; } 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 index 429751c6f244..0c03f521b3eb 100644 --- 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 @@ -18,6 +18,7 @@ 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; @@ -133,7 +134,7 @@ public SubType complement() { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { return data == AllOrNothing.NOTHING; } 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 index b438ad50ba32..a562c82ccd5e 100644 --- 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 @@ -18,6 +18,7 @@ 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; @@ -153,7 +154,7 @@ public SubType complement() { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { return data == AllOrNothing.NOTHING; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java index 119c3b7e2912..f67f47b8a2f0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.internal.types.semtype; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.internal.types.BType; @@ -62,7 +63,7 @@ public SubType complement() { } @Override - public boolean isEmpty() { + public boolean isEmpty(Context cx) { throw new IllegalArgumentException("BSubType don't support semType operations"); } 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 index 4ed71a08822d..8ab9e53db96a 100644 --- 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 @@ -29,7 +29,7 @@ public class CoreTests { @Test - public void testCellContaining() { + public void testCellTypes() { Env env = Env.getInstance(); Context cx = new Context(); SemType intTy = Builder.intType(); @@ -38,5 +38,6 @@ public void testCellContaining() { SemType mutableInt = Builder.cellContaining(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); } } \ No newline at end of file From 1950eb5347ca9cc879fa94be4ef9b7770008d02b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 3 Jun 2024 11:31:36 +0530 Subject: [PATCH 023/178] Cache cell type creation --- .../ballerina/runtime/api/types/semtype/Bdd.java | 7 ++++--- .../runtime/api/types/semtype/Builder.java | 10 +++++++++- .../ballerina/runtime/api/types/semtype/Env.java | 15 +++++++++++++++ .../ballerina/runtime/test/semtype/CoreTests.java | 12 +++++++++++- 4 files changed, 39 insertions(+), 5 deletions(-) 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 index 3873e695ac1d..73ad432fb04c 100644 --- 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 @@ -125,7 +125,6 @@ private Bdd bddComplement() { } else if (this == BddAllOrNothing.NOTHING) { return BddAllOrNothing.ALL; } - // TODO: may be factor this out Bdd nothing = BddAllOrNothing.NOTHING; BddNode b = (BddNode) this; if (b.right() == nothing) { @@ -167,12 +166,14 @@ private Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { @Override public boolean isEmpty(Context cx) { - throw new IllegalStateException("Unimplemented"); + // Basic types that uses Bdd as a delegate should implement isEmpty instead. + throw new IllegalStateException("Bdd don't support isEmpty"); } @Override public SubTypeData data() { - throw new IllegalStateException("Unimplemented"); + // 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, Conjunction pos, Conjunction neg, BddPredicate predicate) { 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 index 61d7ecc3d9cb..57cba9d6d135 100644 --- 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 @@ -197,7 +197,15 @@ public static Optional typeOf(Object object) { } public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { - // TODO:Cache this for pure basic types + never + Optional cachedSemType = env.getCachedCellType(ty, mut); + return cachedSemType.orElseGet(() -> { + SemType semType = createCellSemType(env, ty, mut); + env.cacheCellType(ty, mut, semType); + return semType; + }); + } + + private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { CellAtomicType atomicCell = new CellAtomicType(ty, mut); TypeAtom atom = env.cellAtom(atomicCell); BddNode bdd = BddNode.bddAtom(atom); 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 index 3a73a8cafd4a..19a3539724d3 100644 --- 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 @@ -20,12 +20,15 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; public final class Env { private final static Env INSTANCE = new Env(); private final Map atomTable; + private final Map cellTypeCache = new ConcurrentHashMap<>(); private Env() { this.atomTable = new HashMap<>(); @@ -52,4 +55,16 @@ private TypeAtom typeAtom(AtomicType atomicType) { } } } + + Optional getCachedCellType(SemType ty, CellAtomicType.CellMutability mut) { + return Optional.ofNullable(this.cellTypeCache.get(new CellSemTypeCacheKey(ty, mut))); + } + + void cacheCellType(SemType ty, CellAtomicType.CellMutability mut, SemType semType) { + this.cellTypeCache.put(new CellSemTypeCacheKey(ty, mut), semType); + } + + private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { + + } } 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 index 8ab9e53db96a..6c9e9765ed08 100644 --- 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 @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import org.testng.annotations.Test; +// These are temporary sanity checks until we have actual types using cell types are implemented public class CoreTests { @Test @@ -40,4 +41,13 @@ public void testCellTypes() { assert Core.isSubType(cx, readonlyInt, mutableInt); assert !Core.isSubType(cx, mutableInt, readonlyInt); } -} \ No newline at end of file + + @Test + public void testCellTypeCaching() { + Env env = Env.getInstance(); + SemType intTy = Builder.intType(); + SemType readonlyInt1 = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + SemType readonlyInt2 = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + assert readonlyInt1 == readonlyInt2; + } +} From 99cc815123ff45e3bdc29acb4126c5071f19b970 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 3 Jun 2024 11:50:39 +0530 Subject: [PATCH 024/178] Use read write locks for atom table --- .../runtime/api/types/semtype/Env.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) 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 index 19a3539724d3..076b65bd09f2 100644 --- 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 @@ -22,12 +22,15 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; public final class Env { private final static Env INSTANCE = new Env(); private final Map atomTable; + private final ReadWriteLock atomTableLock = new ReentrantReadWriteLock(); private final Map cellTypeCache = new ConcurrentHashMap<>(); private Env() { @@ -43,8 +46,19 @@ public TypeAtom cellAtom(CellAtomicType atomicType) { } private TypeAtom typeAtom(AtomicType atomicType) { - // FIXME: use a rw lock? - synchronized (this.atomTable) { + this.atomTableLock.readLock().lock(); + try { + TypeAtom ta = this.atomTable.get(atomicType); + if (ta != null) { + return ta; + } + } finally { + this.atomTableLock.readLock().unlock(); + } + + this.atomTableLock.writeLock().lock(); + try { + // we are double-checking since there may be 2 trying to add at the same time TypeAtom ta = this.atomTable.get(atomicType); if (ta != null) { return ta; @@ -53,6 +67,8 @@ private TypeAtom typeAtom(AtomicType atomicType) { this.atomTable.put(result.atomicType(), result); return result; } + } finally { + this.atomTableLock.writeLock().unlock(); } } From f79bf1cea22734e86aa277a1825af95940028d14 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 3 Jun 2024 13:44:20 +0530 Subject: [PATCH 025/178] Add JDoc comments to new classes --- .../runtime/api/types/semtype/Atom.java | 5 +++++ .../runtime/api/types/semtype/AtomicType.java | 5 +++++ .../runtime/api/types/semtype/Bdd.java | 8 +++++++- .../api/types/semtype/BddAllOrNothing.java | 9 +++++++-- .../runtime/api/types/semtype/BddNode.java | 7 ++++++- .../api/types/semtype/BddPredicate.java | 5 +++++ .../api/types/semtype/CellAtomicType.java | 18 ++++++++++++++++++ .../runtime/api/types/semtype/Conjunction.java | 7 +++++++ .../runtime/api/types/semtype/Env.java | 8 +++++++- .../runtime/api/types/semtype/RecAtom.java | 5 +++++ .../internal/types/semtype/BCellSubType.java | 11 +++++++---- 11 files changed, 79 insertions(+), 9 deletions(-) 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 index c70309adae90..6d993b5f5bc8 100644 --- 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 @@ -18,6 +18,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Represent the BDD atom. + * + * @since 2201.10.0 + */ public interface 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 index 051a3da67614..ed193ee129c8 100644 --- 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 @@ -18,6 +18,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Marker type representing AtomicType. + * + * @since 2201.10.0 + */ public interface AtomicType { } 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 index 73ad432fb04c..b4be86eae4b5 100644 --- 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 @@ -22,9 +22,15 @@ 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.10.0 + */ public abstract sealed class Bdd extends SubType permits BddAllOrNothing, BddNode { - protected Bdd(boolean all, boolean nothing) { + Bdd(boolean all, boolean nothing) { super(all, nothing); } 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 index faae6239dd16..737e644e6deb 100644 --- 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 @@ -18,10 +18,15 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Represent the leaf node of a Bdd. + * + * @since 2201.10.0 + */ public final class BddAllOrNothing extends Bdd { - public static BddAllOrNothing ALL = new BddAllOrNothing(true); - public static BddAllOrNothing NOTHING = new BddAllOrNothing(false); + public static final BddAllOrNothing ALL = new BddAllOrNothing(true); + public static final BddAllOrNothing NOTHING = new BddAllOrNothing(false); private BddAllOrNothing(boolean all) { super(all, !all); 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 index 1ad35467ac27..ba71ee47ece6 100644 --- 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 @@ -18,6 +18,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + * + * @since 2201.10.0 + */ public final class BddNode extends Bdd { private final Atom atom; @@ -25,7 +30,7 @@ public final class BddNode extends Bdd { private final Bdd middle; private final Bdd right; - public BddNode(Atom atom, Bdd left, Bdd middle, Bdd right) { + BddNode(Atom atom, Bdd left, Bdd middle, Bdd right) { super(false, false); this.atom = atom; this.left = left; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java index 76a3a57ce1d3..ec2cbea729f0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java @@ -19,6 +19,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Represents a predicate that can be applied to a BDD conjunction. + * + * @since 2201.10.0 + */ @FunctionalInterface public interface BddPredicate { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 1b0af4e1a593..8d9d4cd3e201 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -18,8 +18,26 @@ package io.ballerina.runtime.api.types.semtype; +import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; + +/** + * 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 { + private static final AtomicType CELL_ATOMIC_VAL = new CellAtomicType( + Builder.val(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + public static final TypeAtom ATOM_CELL_VAL = createTypeAtom(0, CELL_ATOMIC_VAL); + + public static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( + Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + public enum CellMutability { CELL_MUT_NONE, CELL_MUT_LIMITED, diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java index 719c8838b4b7..34fbf71ded29 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java @@ -19,6 +19,13 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Represents the Conjunction in the BDD. + * + * @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.10.0 + */ public record Conjunction(Atom atom, Conjunction next) { public static Conjunction and(Atom atom, Conjunction next) { 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 index 076b65bd09f2..2ce185895caa 100644 --- 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 @@ -25,9 +25,15 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +/** + * Represent the environment in which {@code SemTypes} are defined in. Type checking types defined in different + * environments with each other in undefined. + * + * @since 2201.10.0 + */ public final class Env { - private final static Env INSTANCE = new Env(); + private static final Env INSTANCE = new Env(); private final Map atomTable; private final ReadWriteLock atomTableLock = new ReentrantReadWriteLock(); 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 index 8a862725157f..87e006adc788 100644 --- 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 @@ -19,6 +19,11 @@ package io.ballerina.runtime.api.types.semtype; +/** + * Represent a recursive type atom. + * + * @since 2201.10.0 + */ public class RecAtom implements Atom { @Override 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 index db9e35967110..7caaebf533b2 100644 --- 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 @@ -35,6 +35,12 @@ // TODO: would making this a child class of say BddNode be faster than making this a delegate // -- Problem with this is modeling type operations (union, intersect, complement) since parent must return a Cell // as well + +/** + * Runtime representation of CellSubType. + * + * @since 2201.10.0 + */ public class BCellSubType extends SubType { private final Bdd inner; @@ -47,16 +53,13 @@ private BCellSubType(Bdd inner) { public static BCellSubType createDelegate(SubType inner) { if (inner instanceof Bdd bdd) { return new BCellSubType(bdd); - } else if (inner.isAll() || inner.isNothing()) { - // FIXME: if all or nothing do the same thing as cellSubtypeDataEnsureProper - throw new IllegalStateException("unimplemented"); } else if (inner instanceof BCellSubType bCell) { return new BCellSubType(bCell.inner); } throw new IllegalArgumentException("Unexpected inner type"); } - public static CellAtomicType cellAtomType(Atom atom) { + private static CellAtomicType cellAtomType(Atom atom) { return (CellAtomicType) ((TypeAtom) atom).atomicType(); } From 1ca5853984fbf512dfcb90a0bad349fe3ed8d90a Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 4 Jun 2024 11:32:48 +0530 Subject: [PATCH 026/178] Add minimum implementation to create list types --- .../api/types/semtype/BddAllOrNothing.java | 6 + .../types/semtype/BddIsEmptyPredicate.java | 25 ++ .../runtime/api/types/semtype/BddMemo.java | 38 ++ .../runtime/api/types/semtype/BddNode.java | 25 +- .../runtime/api/types/semtype/Builder.java | 31 +- .../api/types/semtype/CellAtomicType.java | 13 + .../runtime/api/types/semtype/Context.java | 83 +++- .../runtime/api/types/semtype/Core.java | 79 +++- .../runtime/api/types/semtype/Env.java | 33 ++ .../api/types/semtype/ListAtomicType.java | 28 ++ .../runtime/api/types/semtype/Pair.java | 26 ++ .../runtime/api/types/semtype/RecAtom.java | 17 +- .../runtime/internal/TypeChecker.java | 3 +- .../internal/types/semtype/BCellSubType.java | 27 +- .../internal/types/semtype/BListSubType.java | 373 ++++++++++++++++++ .../types/semtype/BddBasedSubType.java | 0 .../types/semtype/DelegatedSubType.java | 0 .../types/semtype/FixedLengthArray.java | 48 +++ .../types/semtype/ListDefinition.java | 83 ++++ .../runtime/test/semtype/CoreTests.java | 24 +- .../io/ballerina/types/typeops/ListOps.java | 2 +- 21 files changed, 938 insertions(+), 26 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddBasedSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java 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 index 737e644e6deb..8ca94be11ec2 100644 --- 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 @@ -31,4 +31,10 @@ public final class BddAllOrNothing extends Bdd { private BddAllOrNothing(boolean all) { super(all, !all); } + + @Override + public int hashCode() { + return 0xa11084 + (this == ALL ? 1 : 0); + } + } 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..4b1f7bb6afa9 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java @@ -0,0 +1,25 @@ +/* + * 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; + +@FunctionalInterface +public interface BddIsEmptyPredicate { + + boolean apply(Context cx, Bdd bdd); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java new file mode 100644 index 000000000000..52f6a250f93a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.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.runtime.api.types.semtype; + +// TODO: consider moving this to inner as well +public final class BddMemo { + + public Status isEmpty; + + public BddMemo() { + this.isEmpty = Status.NULL; + } + + public enum Status { + TRUE, + FALSE, + LOOP, + CYCLIC, + PROVISIONAL, + NULL + } +} 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 index ba71ee47ece6..814f8448a717 100644 --- 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 @@ -18,6 +18,8 @@ package io.ballerina.runtime.api.types.semtype; +import java.util.Objects; + /** * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. * @@ -38,7 +40,7 @@ public final class BddNode extends Bdd { this.right = right; } - static BddNode bddAtom(TypeAtom atom) { + public static BddNode bddAtom(Atom atom) { return new BddNode(atom, BddAllOrNothing.ALL, BddAllOrNothing.NOTHING, BddAllOrNothing.NOTHING); } @@ -58,4 +60,25 @@ public Bdd right() { return 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() { + return Objects.hash(atom, left, middle, right); + } + + boolean isSimple() { + return left.equals(BddAllOrNothing.ALL) && middle.equals(BddAllOrNothing.NOTHING) && + right.equals(BddAllOrNothing.NOTHING); + } } 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 index 57cba9d6d135..208d38a42a98 100644 --- 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 @@ -34,8 +34,11 @@ import java.util.List; import java.util.Optional; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; 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.TypeAtom.createTypeAtom; /** * Utility class for creating semtypes. @@ -44,7 +47,12 @@ */ public final class Builder { + private static final SemType NEVER = SemType.from(0); + private static final SemType VAL = SemType.from(VT_MASK); private static final String[] EMPTY_STRING_ARR = new String[0]; + static final SemType CELL_SEMTYPE_INNER = basicSubType(BT_CELL, + BCellSubType.createDelegate(bddAtom(createTypeAtom(2, CellAtomicType.CELL_ATOMIC_INNER)))); + private static final SemType INNER = basicTypeUnion(val().all() | undef().all); private Builder() { } @@ -77,6 +85,22 @@ public static SemType nilType() { return from(BasicTypeCode.BT_NIL); } + public static SemType undef() { + return from(BasicTypeCode.BT_UNDEF); + } + + public static SemType cell() { + return from(BT_CELL); + } + + protected static SemType cellSemTypeInner() { + return CELL_SEMTYPE_INNER; + } + + public static SemType inner() { + return INNER; + } + public static SemType intType() { return from(BasicTypeCode.BT_INT); } @@ -105,9 +129,6 @@ public static SemType charType() { return StringTypeCache.charType; } - private static final SemType NEVER = SemType.from(0); - private static final SemType VAL = SemType.from(VT_MASK); - public static SemType basicTypeUnion(int bitset) { return switch (bitset) { case 0 -> NEVER; @@ -208,8 +229,8 @@ public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMut private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { CellAtomicType atomicCell = new CellAtomicType(ty, mut); TypeAtom atom = env.cellAtom(atomicCell); - BddNode bdd = BddNode.bddAtom(atom); - return basicSubType(BasicTypeCode.BT_CELL, BCellSubType.createDelegate(bdd)); + BddNode bdd = bddAtom(atom); + return basicSubType(BT_CELL, BCellSubType.createDelegate(bdd)); } public static SemType val() { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 8d9d4cd3e201..2fc47aaba99d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -37,6 +37,19 @@ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicTy public static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED ); + public static final CellAtomicType CELL_ATOMIC_INNER = new CellAtomicType( + Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + + public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { + SemType ty = Core.intersect(c1.ty(), c2.ty()); + CellMutability mut = min(c1.mut(), c2.mut()); + return new CellAtomicType(ty, mut); + } + + private static CellMutability min(CellMutability m1, + CellMutability m2) { + return m1.compareTo(m2) <= 0 ? m1 : m2; + } public enum CellMutability { CELL_MUT_NONE, 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 index 5b6b77441641..5cc85ed9107d 100644 --- 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 @@ -18,11 +18,92 @@ package io.ballerina.runtime.api.types.semtype; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + /** * Context in which semtype was defined in. * * @since 2201.10.0 */ -public class Context { +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 HashMap<>(); // SEMTYPE-TODO: Fill this in as needed, currently just a placeholder since basic types don't need it + + private Context(Env env) { + this.env = env; + } + + public static Context from(Env env) { + return new Context(env); + } + + public boolean memoSubtypeIsEmpty(Map memoTable, BddIsEmptyPredicate isEmptyPredicate, Bdd bdd) { + BddMemo mm = memoTable.get(bdd); + BddMemo m; + if (mm != null) { + switch (mm.isEmpty) { + 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 mm.isEmpty == BddMemo.Status.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.Status.LOOP; + return true; + default: + throw new AssertionError("Unexpected memo status: " + mm.isEmpty); + } + } else { + m = new BddMemo(); + memoTable.put(bdd, m); + } + 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 (!isEmpty || initStackDepth == 0) { + 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) { + memoStack.get(i).isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.NULL; + } + } + if (memoStack.size() > initStackDepth) { + memoStack.subList(initStackDepth, 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.Status.CYCLIC; + } else { + m.isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.FALSE; + } + } + return isEmpty; + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } } 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 index 40f02d9850a8..3783e64de114 100644 --- 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 @@ -19,13 +19,23 @@ package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.internal.types.semtype.AllOrNothing; +import io.ballerina.runtime.internal.types.semtype.BCellSubType; 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.util.Optional; + import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_B_TYPE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; 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.Builder.cellContaining; +import static io.ballerina.runtime.api.types.semtype.Builder.undef; +import static io.ballerina.runtime.api.types.semtype.Builder.val; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.intersectCellAtomicType; +import static io.ballerina.runtime.internal.types.semtype.BCellSubType.cellAtomType; /** * Contain functions defined in `core.bal` file. @@ -37,6 +47,11 @@ public final class Core { public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); public static final SemType B_TYPE_TOP = SemType.from(1 << BT_B_TYPE.code()); + // TODO: move to builder + private static final CellAtomicType CELL_ATOMIC_VAL = new CellAtomicType( + val(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + private Core() { } @@ -92,7 +107,19 @@ public static SemType diff(SemType t1, SemType t2) { } public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { - throw new IllegalStateException("Unimplemented"); +// int c = code.code(); +// c = 1 << c; +// if ((t.all & c) != 0) { +// return AllOrNothingSubtype.createAll(); +// } +// if ((t.some & c) == 0) { +// return AllOrNothingSubtype.createNothing(); +// } + SubType subType = t.subTypeData()[code.code()]; + if (subType instanceof BCellSubType cellSubType) { + return cellSubType.inner; + } + return subType; } public static SemType union(SemType t1, SemType t2) { @@ -270,4 +297,54 @@ public static SemType widenToBasicTypeUnion(SemType t) { int all = t.all | t.some; return Builder.basicTypeUnion(all); } + + public static SemType cellContainingInnerVal(Env env, SemType t) { + CellAtomicType cat = + cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); + return cellContaining(env, diff(cat.ty(), undef()), cat.mut()); + } + + public static SemType intersectMemberSemTypes(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 cellContaining(env, atomicType.ty(), undef().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + } + + private static Optional cellAtomicType(SemType t) { + SemType cell = Builder.cell(); + if (t.some == 0) { + return cell.equals(t) ? Optional.of(CELL_ATOMIC_VAL) : Optional.empty(); + } else { + if (!isSubtypeSimple(t, cell)) { + return Optional.empty(); + } + return bddCellAtomicType((Bdd) getComplexSubtypeData(t, BT_CELL), CELL_ATOMIC_VAL); + } + } + + 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), undef()); + } + + private static SemType cellInner(SemType t) { + CellAtomicType cat = + cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); + return cat.ty(); + } + } 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 index 2ce185895caa..a1d31eff0e24 100644 --- 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 @@ -18,7 +18,9 @@ package io.ballerina.runtime.api.types.semtype; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @@ -37,10 +39,15 @@ public final class Env { private final Map atomTable; private final ReadWriteLock atomTableLock = new ReentrantReadWriteLock(); + + private final List recListAtoms; + private final Map cellTypeCache = new ConcurrentHashMap<>(); private Env() { this.atomTable = new HashMap<>(); + this.recListAtoms = new ArrayList<>(); + // FIXME: add LIST_ATOMIC_RO } public static Env getInstance() { @@ -86,6 +93,32 @@ void cacheCellType(SemType ty, CellAtomicType.CellMutability mut, SemType semTyp this.cellTypeCache.put(new CellSemTypeCacheKey(ty, mut), semType); } + public RecAtom recListAtom() { + // TODO: do we have seperate read and write operations, if so use rw lock + synchronized (this.recListAtoms) { + int result = this.recListAtoms.size(); + // represents adding () in nballerina + this.recListAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public void setRecListAtomType(RecAtom rec, ListAtomicType atomicType) { + synchronized (this.recListAtoms) { + this.recListAtoms.set(rec.index(), atomicType); + } + } + + public Atom listAtom(ListAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public ListAtomicType getRecListAtomType(RecAtom ra) { + synchronized (this.recListAtoms) { + return this.recListAtoms.get(ra.index); + } + } + private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java new file mode 100644 index 000000000000..31f0996c1128 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.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.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; + +// TODO: move this to internal along with cell atomic type +public record ListAtomicType(FixedLengthArray members, SemType rest) implements AtomicType { + + public static final ListAtomicType LIST_ATOMIC_INNER = new ListAtomicType( + FixedLengthArray.empty(), Builder.CELL_SEMTYPE_INNER); +} 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..a139a2a70469 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java @@ -0,0 +1,26 @@ +/* + * 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; + +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/RecAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java index 87e006adc788..3fd21312cb25 100644 --- 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 @@ -26,8 +26,23 @@ */ public class RecAtom implements Atom { + public final int index; + private static final int BDD_REC_ATOM_READONLY = 0; + public 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); + } + @Override public int index() { - throw new IllegalStateException("unimplemented"); + return index; } } 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 0496bd3c5bff..492854f20916 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 @@ -30,6 +30,7 @@ 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.values.BDecimal; import io.ballerina.runtime.api.values.BError; @@ -116,7 +117,7 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; - private static final Context cx = new Context(); + private static final Context cx = Context.from(Env.getInstance()); public static Object checkCast(Object sourceVal, Type targetType) { 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 index 7caaebf533b2..efe549ce2449 100644 --- 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 @@ -41,9 +41,9 @@ * * @since 2201.10.0 */ -public class BCellSubType extends SubType { +public final class BCellSubType extends SubType { - private final Bdd inner; + public final Bdd inner; private BCellSubType(Bdd inner) { super(inner.isAll(), inner.isNothing()); @@ -59,7 +59,7 @@ public static BCellSubType createDelegate(SubType inner) { throw new IllegalArgumentException("Unexpected inner type"); } - private static CellAtomicType cellAtomType(Atom atom) { + public static CellAtomicType cellAtomType(Atom atom) { return (CellAtomicType) ((TypeAtom) atom).atomicType(); } @@ -89,6 +89,15 @@ public boolean isEmpty(Context cx) { return Bdd.bddEvery(cx, inner, null, null, BCellSubType::cellFormulaIsEmpty); } + @Override + public SubType diff(SubType other) { + if (!(other instanceof BCellSubType otherCell)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + + return createDelegate(inner.diff(otherCell.inner)); + } + @Override public SubTypeData data() { throw new IllegalStateException("unimplemented"); @@ -102,7 +111,7 @@ private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conju combined = cellAtomType(posList.atom()); Conjunction p = posList.next(); while (p != null) { - combined = intersectCellAtomicType(combined, cellAtomType(p.atom())); + combined = CellAtomicType.intersectCellAtomicType(combined, cellAtomType(p.atom())); p = p.next(); } } @@ -173,14 +182,4 @@ private static SemType filteredCellListUnion(Conjunction negList, Predicate bddEvery(context, bdd, null, null, BListSubType::listFormulaIsEmpty), inner); + } + + private static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + SemType 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) { + members = fixedArrayShallowCopy(members); + } + 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 (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 + 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; + } + } + + private 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 = cellContainingInnerVal(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. + private 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); + } + + private static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { + for (var t : array.initial()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + return false; + } + + private 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] = + intersectMemberSemTypes(env, listMemberAt(members1, rest1, i), listMemberAt(members2, rest2, i)); + } + return Pair.from(new FixedLengthArray(initial, + Integer.max(members1.fixedLength(), members2.fixedLength())), + intersectMemberSemTypes(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]; + } + + private static FixedLengthArray fixedArrayShallowCopy(FixedLengthArray array) { + return new FixedLengthArray(array.initial().clone(), array.fixedLength()); + } + + private static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, SemType rest, int index) { + return cellInnerVal(listMemberAt(fixedArray, rest, index)); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddBasedSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddBasedSubType.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java new file mode 100644 index 000000000000..e69de29bb2d1 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..e1c4bfab5877 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java @@ -0,0 +1,48 @@ +/* + * 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; + +public record FixedLengthArray(SemType[] initial, int 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; + } + SemType[] buffer = new SemType[i + 2]; + System.arraycopy(initial, 0, buffer, 0, i + 1); + return new FixedLengthArray(buffer, fixedLength); + } + + public static FixedLengthArray empty() { + return EMPTY; + } +} 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..def520407308 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java @@ -0,0 +1,83 @@ +/* + * 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.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.ListAtomicType; +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.cellContaining; +import static io.ballerina.runtime.api.types.semtype.Builder.undef; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +public class ListDefinition { + + private RecAtom rec = null; + private SemType semType = null; + + 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] = cellContaining(env, initial[i], mut); + } + SemType restCell = cellContaining(env, union(rest, undef()), isNever(rest) ? CELL_MUT_NONE : mut); + return define(env, initialCells, fixedLength, restCell); + } + + 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/test/java/io/ballerina/runtime/test/semtype/CoreTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java index 6c9e9765ed08..07d6d88114b1 100644 --- 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 @@ -24,6 +24,7 @@ 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.ListDefinition; import org.testng.annotations.Test; // These are temporary sanity checks until we have actual types using cell types are implemented @@ -32,7 +33,7 @@ public class CoreTests { @Test public void testCellTypes() { Env env = Env.getInstance(); - Context cx = new Context(); + Context cx = Context.from(env); SemType intTy = Builder.intType(); SemType readonlyInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); assert Core.isSubType(cx, readonlyInt, readonlyInt); @@ -50,4 +51,25 @@ public void testCellTypeCaching() { SemType readonlyInt2 = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); assert readonlyInt1 == readonlyInt2; } + + @Test + public void testSimpleList() { + Env env = Env.getInstance(); + SemType intTy = Builder.intType(); + // 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.neverType(), + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + + Context cx = Context.from(env); + assert Core.isSubType(cx, intListTy1, intListTy); + } } diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java index a0e6d1fde971..406bd48e91e3 100644 --- a/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java @@ -197,7 +197,7 @@ static List listSamples(Context cx, FixedLengthArray members, SemType r } for (int i = 0; i < nNeg; i++) { // Be careful to avoid integer overflow. - if (lastBoundary > Long.MAX_VALUE - i) { + if (lastBoundary > Integer.MAX_VALUE - i) { break; } indices.add(lastBoundary + i); From e38b5db11ae0ab8b9edf26d08e4442d547d6b2db Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 4 Jun 2024 18:41:25 +0530 Subject: [PATCH 027/178] Implemented list type projection --- .../runtime/api/types/semtype/Builder.java | 45 +++- .../api/types/semtype/CellAtomicType.java | 2 +- .../runtime/api/types/semtype/Core.java | 40 +++- .../runtime/api/types/semtype/Env.java | 4 +- .../api/types/semtype/ListAtomicType.java | 8 +- .../runtime/api/types/semtype/ListProj.java | 205 ++++++++++++++++++ .../internal/types/semtype/BCellSubType.java | 4 +- .../internal/types/semtype/BIntSubType.java | 33 ++- .../internal/types/semtype/BListSubType.java | 75 ++++++- .../internal/types/semtype/Definition.java | 27 +++ .../types/semtype/FixedLengthArray.java | 17 +- .../types/semtype/ListDefinition.java | 3 +- .../internal/types/semtype/SubTypeData.java | 1 + 13 files changed, 437 insertions(+), 27 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java 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 index 208d38a42a98..6aaaf5ef8fa1 100644 --- 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 @@ -26,6 +26,7 @@ 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.BStringSubType; import io.ballerina.runtime.internal.values.DecimalValue; @@ -35,9 +36,13 @@ import java.util.Optional; 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.CODE_B_TYPE; +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.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.api.types.semtype.Core.union; import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; /** @@ -47,12 +52,28 @@ */ public final class Builder { + private static final String[] EMPTY_STRING_ARR = new String[0]; private static final SemType NEVER = SemType.from(0); private static final SemType VAL = SemType.from(VT_MASK); - private static final String[] EMPTY_STRING_ARR = new String[0]; + private static final SemType UNDEF = from(BasicTypeCode.BT_UNDEF); + private static final SemType INNER = basicTypeUnion(valType().all() | undef().all); + static final SemType CELL_SEMTYPE_INNER = basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(createTypeAtom(2, CellAtomicType.CELL_ATOMIC_INNER)))); - private static final SemType INNER = basicTypeUnion(val().all() | undef().all); + + public static final int BDD_REC_ATOM_READONLY = 0; + // represents both readonly & map and readonly & readonly[] + private static final BddNode BDD_SUBTYPE_RO = bddAtom(RecAtom.createRecAtom(BDD_REC_ATOM_READONLY)); + + public static final SemType VAL_READONLY = Core.union(SemType.from(VT_INHERENTLY_IMMUTABLE), + basicSubType(BT_LIST, BListSubType.createDelegate(BDD_SUBTYPE_RO))); + private static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); + private static final CellAtomicType CELL_ATOMIC_INNER_RO = new CellAtomicType( + INNER_READONLY, CELL_MUT_NONE); + private static final TypeAtom ATOM_CELL_INNER_RO = createTypeAtom(7, CELL_ATOMIC_INNER_RO); + static final SemType CELL_SEMTYPE_INNER_RO = basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(ATOM_CELL_INNER_RO))); + private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); private Builder() { } @@ -86,7 +107,7 @@ public static SemType nilType() { } public static SemType undef() { - return from(BasicTypeCode.BT_UNDEF); + return UNDEF; } public static SemType cell() { @@ -129,6 +150,10 @@ public static SemType charType() { return StringTypeCache.charType; } + public static SemType listType() { + return from(BT_LIST); + } + public static SemType basicTypeUnion(int bitset) { return switch (bitset) { case 0 -> NEVER; @@ -217,6 +242,14 @@ public static Optional typeOf(Object object) { return Optional.empty(); } + public static SemType roCellContaining(Env env, SemType ty) { + return cellContaining(env, ty, CELL_MUT_NONE); + } + + public static SemType cellContaining(Env env, SemType ty) { + return cellContaining(env, ty, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { Optional cachedSemType = env.getCachedCellType(ty, mut); return cachedSemType.orElseGet(() -> { @@ -233,10 +266,14 @@ private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.Cel return basicSubType(BT_CELL, BCellSubType.createDelegate(bdd)); } - public static SemType val() { + public static SemType valType() { return basicTypeUnion(VT_MASK); } + public static SemType anyType() { + return ANY; + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 2fc47aaba99d..2174ef7accc7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -30,7 +30,7 @@ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { private static final AtomicType CELL_ATOMIC_VAL = new CellAtomicType( - Builder.val(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED ); public static final TypeAtom ATOM_CELL_VAL = createTypeAtom(0, CELL_ATOMIC_VAL); 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 index 3783e64de114..2ad8b5da8713 100644 --- 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 @@ -20,6 +20,7 @@ import io.ballerina.runtime.internal.types.semtype.AllOrNothing; import io.ballerina.runtime.internal.types.semtype.BCellSubType; +import io.ballerina.runtime.internal.types.semtype.BListSubType; import io.ballerina.runtime.internal.types.semtype.SubTypeData; import io.ballerina.runtime.internal.types.semtype.SubtypePair; import io.ballerina.runtime.internal.types.semtype.SubtypePairs; @@ -28,14 +29,18 @@ import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_B_TYPE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +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.CODE_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; +import static io.ballerina.runtime.api.types.semtype.Builder.listType; import static io.ballerina.runtime.api.types.semtype.Builder.undef; -import static io.ballerina.runtime.api.types.semtype.Builder.val; +import static io.ballerina.runtime.api.types.semtype.Builder.valType; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.intersectCellAtomicType; import static io.ballerina.runtime.internal.types.semtype.BCellSubType.cellAtomType; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; /** * Contain functions defined in `core.bal` file. @@ -49,7 +54,7 @@ public final class Core { // TODO: move to builder private static final CellAtomicType CELL_ATOMIC_VAL = new CellAtomicType( - val(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED ); private Core() { @@ -116,12 +121,32 @@ public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { // return AllOrNothingSubtype.createNothing(); // } SubType subType = t.subTypeData()[code.code()]; + // FIXME: introduce an interface for this if (subType instanceof BCellSubType cellSubType) { return cellSubType.inner; + } else if (subType instanceof BListSubType listSubType) { + return listSubType.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 & listType().all) != 0 ? Builder.valType() : Builder.neverType(); + } else { + SubTypeData keyData = intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.neverType(); + } + return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData(t, BT_LIST), keyData, Builder.valType()); + } + } + public static SemType union(SemType t1, SemType t2) { int all1 = t1.all(); int some1 = t1.some(); @@ -259,6 +284,15 @@ public static boolean isSubtypeSimple(SemType t1, SemType t2) { return (bits & ~t2.all()) == 0; } + public static boolean isNothingSubtype(SubTypeData data) { + return data == AllOrNothing.NOTHING; + } + + // 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 subTypeData(SemType s, BasicTypeCode code) { if ((s.all & (1 << code.code())) != 0) { return AllOrNothing.ALL; @@ -341,7 +375,7 @@ public static SemType cellInnerVal(SemType t) { return diff(cellInner(t), undef()); } - private static SemType cellInner(SemType t) { + public static SemType cellInner(SemType t) { CellAtomicType cat = cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); return cat.ty(); 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 index a1d31eff0e24..31a7a647b9f4 100644 --- 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 @@ -27,6 +27,8 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import static io.ballerina.runtime.api.types.semtype.ListAtomicType.LIST_ATOMIC_RO; + /** * Represent the environment in which {@code SemTypes} are defined in. Type checking types defined in different * environments with each other in undefined. @@ -47,7 +49,7 @@ public final class Env { private Env() { this.atomTable = new HashMap<>(); this.recListAtoms = new ArrayList<>(); - // FIXME: add LIST_ATOMIC_RO + recListAtoms.add(LIST_ATOMIC_RO); } public static Env getInstance() { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java index 31f0996c1128..57fd654c0a87 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java @@ -20,9 +20,15 @@ import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER; +import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER_RO; + // TODO: move this to internal along with cell atomic type public record ListAtomicType(FixedLengthArray members, SemType rest) implements AtomicType { public static final ListAtomicType LIST_ATOMIC_INNER = new ListAtomicType( - FixedLengthArray.empty(), Builder.CELL_SEMTYPE_INNER); + FixedLengthArray.empty(), CELL_SEMTYPE_INNER); + + public static final ListAtomicType LIST_ATOMIC_RO = new ListAtomicType( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java new file mode 100644 index 000000000000..a594934bd37d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java @@ -0,0 +1,205 @@ +/* + * 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.BIntSubType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +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.cellContaining; +import static io.ballerina.runtime.api.types.semtype.Builder.roCellContaining; +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.fixedArrayShallowCopy; +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; + +public final class ListProj { + + private ListProj() { + } + + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + if (t.some == 0) { + return t == Builder.listType() ? Builder.valType() : Builder.neverType(); + } else { + SubTypeData keyData = Core.intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.neverType(); + } + 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.neverType(); + } 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 = cellContaining(cx.env, union(Builder.valType(), Builder.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); + Pair + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return Builder.neverType(); + } + members = intersected.first(); + rest = intersected.second(); + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return Builder.neverType(); + } + // Ensure that we can use isNever on rest in listInhabited + if (!isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { + rest = roCellContaining(cx.env, Builder.neverType()); + } + } + 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.neverType(); + 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] = 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; + } + + private static Pair listProjSamples(Integer[] indices, SubTypeData k) { + List> v = new ArrayList<>(); + for (int i : indices) { + v.add(Pair.from(i, intSubtypeContains(k, i))); + } + // FIXME: refactor this so we don't have to expose the internal details of BIntSubType + // Maybe we can return optional partitions + 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(p -> p.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/BCellSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java index efe549ce2449..58820350a891 100644 --- 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 @@ -106,7 +106,7 @@ public SubTypeData data() { private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { CellAtomicType combined; if (posList == null) { - combined = new CellAtomicType(Builder.val(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + combined = new CellAtomicType(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); } else { combined = cellAtomType(posList.atom()); Conjunction p = posList.next(); @@ -134,7 +134,7 @@ private static boolean cellMutUnlimitedInhabited(Context cx, SemType pos, Conjun Conjunction neg = negList; while (neg != null) { if (cellAtomType(neg.atom()).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && - Core.isSameType(cx, Builder.val(), cellAtomType(neg.atom()).ty())) { + Core.isSameType(cx, Builder.valType(), cellAtomType(neg.atom()).ty())) { return false; } neg = neg.next(); 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 index 0c03f521b3eb..c5e4ab02da39 100644 --- 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 @@ -143,13 +143,40 @@ public SubTypeData data() { return data; } - record Range(long min, long max) { + public record Range(long min, long max) { } - static final class IntSubTypeData implements SubTypeData { + public static long intSubtypeMax(IntSubTypeData subtype) { + return subtype.ranges[subtype.ranges.length - 1].max; + } + + static boolean intSubtypeOverlapRange(IntSubTypeData subtype, Range range) { + IntSubTypeData subTypeData = subtype.intersect(new IntSubTypeData(range)); + return subTypeData.ranges.length != 0; + } - private final Range[] ranges; + public static boolean intSubtypeContains(SubTypeData d, long n) { + if (d instanceof AllOrNothing allOrNothingSubtype) { + return d == AllOrNothing.ALL; + } + IntSubTypeData v = (IntSubTypeData) d; + // FIXME: move the rest to int subtype data + for (Range r : v.ranges) { + if (r.min <= n && n <= r.max) { + return true; + } + } + return false; + } + + public static final class IntSubTypeData implements SubTypeData { + + public final Range[] ranges; + + private IntSubTypeData(Range range) { + this.ranges = new Range[]{range}; + } private IntSubTypeData(Range[] ranges) { this.ranges = ranges; 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 index e009a3d93fe2..529f61417e67 100644 --- 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 @@ -20,6 +20,9 @@ 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; @@ -36,14 +39,18 @@ import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; import static io.ballerina.runtime.api.types.semtype.Core.cellContainingInnerVal; +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.intersectMemberSemTypes; import static io.ballerina.runtime.api.types.semtype.ListAtomicType.LIST_ATOMIC_INNER; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeMax; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeOverlapRange; // TODO: this has lot of common code with cell (and future mapping), consider refact public class BListSubType extends SubType { - private final Bdd inner; + public final Bdd inner; private BListSubType(Bdd inner) { super(inner.isAll(), inner.isNothing()); @@ -214,8 +221,8 @@ private static boolean listInhabited(Context cx, Integer[] indices, SemType[] me } } - private static Pair listSampleTypes(Context cx, FixedLengthArray members, - SemType rest, Integer[] indices) { + 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++) { @@ -243,7 +250,7 @@ private static Pair listSampleTypes(Context cx, FixedLengthA // 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. - private static Integer[] listSamples(Context cx, FixedLengthArray members, SemType rest, Conjunction neg) { + 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()); @@ -305,7 +312,7 @@ private static Integer[] listSamples(Context cx, FixedLengthArray members, SemTy return indices.toArray(arr); } - private static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { + public static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { for (var t : array.initial()) { if (Core.isEmpty(cx, t)) { return true; @@ -314,8 +321,8 @@ private static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { return false; } - private static Pair listIntersectWith(Env env, FixedLengthArray members1, SemType rest1, - FixedLengthArray members2, SemType rest2) { + public static Pair listIntersectWith(Env env, FixedLengthArray members1, SemType rest1, + FixedLengthArray members2, SemType rest2) { if (listLengthsDisjoint(members1, rest1, members2, rest2)) { return null; @@ -357,14 +364,64 @@ private static SemType fixedArrayGet(FixedLengthArray members, int index) { return members.initial()[i]; } - private static FixedLengthArray fixedArrayShallowCopy(FixedLengthArray array) { + // FIXME: move this to FixedLengthArray + public static FixedLengthArray fixedArrayShallowCopy(FixedLengthArray array) { return new FixedLengthArray(array.initial().clone(), array.fixedLength()); } - private static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, SemType rest, int index) { + 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.neverType(); + } 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.undef()); + } + + 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.neverType(); + 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 (intSubtypeOverlapRange(intSubtype, new BIntSubType.Range(initLen, fixedLen - 1))) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, fixedLen - 1))); + } + } + if (fixedLen == 0 || intSubtypeMax(intSubtype) > 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() { throw new IllegalStateException("unimplemented"); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java new file mode 100644 index 000000000000..7ae93ad1e01b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java @@ -0,0 +1,27 @@ +/* + * 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.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +public interface Definition { + + SemType getSemType(Env env); +} 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 index e1c4bfab5877..ff789825b517 100644 --- 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 @@ -22,6 +22,14 @@ public record FixedLengthArray(SemType[] initial, int fixedLength) { + public FixedLengthArray { + for (SemType semType : initial) { + if (semType == null) { + throw new IllegalArgumentException("initial members can't be null"); + } + } + } + private static final FixedLengthArray EMPTY = new FixedLengthArray(new SemType[0], 0); static FixedLengthArray normalized(SemType[] initial, int fixedLength) { @@ -37,8 +45,13 @@ static FixedLengthArray normalized(SemType[] initial, int fixedLength) { } i -= 1; } - SemType[] buffer = new SemType[i + 2]; - System.arraycopy(initial, 0, buffer, 0, 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); } 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 index def520407308..c420dff26dfc 100644 --- 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 @@ -35,11 +35,12 @@ import static io.ballerina.runtime.api.types.semtype.Core.isNever; import static io.ballerina.runtime.api.types.semtype.Core.union; -public class ListDefinition { +public class ListDefinition implements Definition { private RecAtom rec = null; private SemType semType = null; + @Override public SemType getSemType(Env env) { SemType s = this.semType; if (s == null) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java index 55bb8d32a94c..d55a20832818 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java @@ -23,6 +23,7 @@ * * @since 2201.10.0 */ +// TODO: move this to api public interface SubTypeData { } From 19c430f1c492e8521687fd7e78cac1724ee343e1 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 5 Jun 2024 09:50:07 +0530 Subject: [PATCH 028/178] Implement list semtype --- .../runtime/api/types/semtype/Bdd.java | 38 +++++++++++++ .../runtime/api/types/semtype/Builder.java | 25 +++++++++ .../runtime/internal/TypeChecker.java | 35 ++++++++++-- .../runtime/internal/types/BArrayType.java | 47 ++++++++++++++++ .../internal/types/BIntersectionType.java | 3 +- .../runtime/internal/types/BTupleType.java | 53 ++++++++++++++++++- .../runtime/internal/types/BType.java | 7 +++ .../internal/types/BTypeConverter.java | 45 +++++++++------- .../internal/types/BTypeReferenceType.java | 3 +- .../port/test/RuntimeSemTypeResolver.java | 2 + 10 files changed, 234 insertions(+), 24 deletions(-) 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 index b4be86eae4b5..e9620e257d50 100644 --- 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 @@ -120,6 +120,44 @@ private Bdd bddIntersect(Bdd other) { } } + @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(); 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 index 6aaaf5ef8fa1..124cc1fe81bc 100644 --- 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 @@ -19,6 +19,7 @@ package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; @@ -28,6 +29,7 @@ import io.ballerina.runtime.internal.types.semtype.BIntSubType; import io.ballerina.runtime.internal.types.semtype.BListSubType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; @@ -75,6 +77,8 @@ public final class Builder { BT_CELL, BCellSubType.createDelegate(bddAtom(ATOM_CELL_INNER_RO))); private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); + private static final Env env = Env.getInstance(); + private Builder() { } @@ -154,6 +158,10 @@ public static SemType listType() { return from(BT_LIST); } + public static SemType readonlyType() { + return VAL_READONLY; + } + public static SemType basicTypeUnion(int bitset) { return switch (bitset) { case 0 -> NEVER; @@ -238,10 +246,27 @@ public static Optional typeOf(Object object) { return Optional.of(booleanConst(booleanValue)); } else if (object instanceof BString stringValue) { return Optional.of(stringConst(stringValue.getValue())); + } else if (object instanceof BArray arrayValue) { + return typeOfArray(arrayValue); } return Optional.empty(); } + private static Optional typeOfArray(BArray arrayValue) { + int size = arrayValue.size(); + SemType[] memberTypes = new SemType[size]; + for (int i = 0; i < size; i++) { + Optional memberType = typeOf(arrayValue.get(i)); + if (memberType.isEmpty()) { + return Optional.empty(); + } + memberTypes[i] = memberType.get(); + } + ListDefinition ld = new ListDefinition(); + return Optional.of( + ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); + } + public static SemType roCellContaining(Env env, SemType ty) { return cellContaining(env, ty, CELL_MUT_NONE); } 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 492854f20916..53f78372975f 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 @@ -76,6 +76,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BOOLEAN; @@ -266,7 +267,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { return true; } SemType sourceSemType = Builder.from(getType(sourceVal)); - return switch (isSubTypeInner(sourceSemType, targetSemType)) { + return switch (isSubTypeInner(sourceVal, sourceSemType, targetSemType)) { case TRUE -> true; case FALSE -> false; case MAYBE -> FallbackTypeChecker.checkIsType(null, sourceVal, bTypePart(sourceSemType), @@ -284,7 +285,7 @@ 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) { - return switch (isSubType(sourceType, targetType)) { + return switch (isSubType(sourceVal, sourceType, targetType)) { case TRUE -> true; case FALSE -> false; case MAYBE -> @@ -502,7 +503,7 @@ public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - return switch (isSubType(sourceType, targetType)) { + return switch (isSubType(sourceVal, sourceType, targetType)) { case TRUE -> true; case FALSE -> false; case MAYBE -> FallbackTypeChecker.checkIsType(sourceVal, bTypePart(sourceType), bTypePart(targetType), @@ -539,6 +540,14 @@ private enum TypeCheckResult { MAYBE } + private static TypeCheckResult isSubType(Object sourceValue, Type source, Type target) { + TypeCheckResult result = isSubType(source, target); + if (result != TypeCheckResult.FALSE || !source.isReadOnly()) { + return result; + } + return isSubTypeImmutableValue(sourceValue, Builder.from(target)); + } + private static TypeCheckResult isSubType(Type source, Type target) { if (source instanceof ParameterizedType sourceParamType) { if (target instanceof ParameterizedType targetParamType) { @@ -549,6 +558,24 @@ private static TypeCheckResult isSubType(Type source, Type target) { return isSubTypeInner(Builder.from(source), Builder.from(target)); } + private static TypeCheckResult isSubTypeInner(Object sourceValue, SemType source, SemType target) { + TypeCheckResult result = isSubTypeInner(source, target); + if (result != TypeCheckResult.FALSE || + !Core.isSubType(cx, Core.intersect(source, SEMTYPE_TOP), Builder.readonlyType())) { + return result; + } + return isSubTypeImmutableValue(sourceValue, target); + } + + private static TypeCheckResult isSubTypeImmutableValue(Object sourceValue, SemType target) { + Optional sourceSingletonType = Builder.typeOf(sourceValue); + if (sourceSingletonType.isEmpty()) { + return Core.containsBasicType(target, B_TYPE_TOP) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; + } + SemType singletonType = sourceSingletonType.get(); + return isSubTypeInner(singletonType, target); + } + private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { if (!Core.containsBasicType(source, B_TYPE_TOP)) { return Core.isSubType(cx, source, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; @@ -574,6 +601,8 @@ private static SemType basicType(Object value) { return Builder.booleanType(); } else if (value instanceof DecimalValue) { return Builder.decimalType(); + } else if (value instanceof ArrayValue) { + return Builder.listType(); } else { return Builder.bType(); } 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 d73fb58f5a82..b99ff54c8dc8 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,7 +22,13 @@ import io.ballerina.runtime.api.types.ArrayType; 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.CellAtomicType; +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.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.ArrayValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -41,6 +47,8 @@ */ @SuppressWarnings("unchecked") public class BArrayType extends BType implements ArrayType { + + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private Type elementType; private int dimensions = 1; private int size = -1; @@ -51,6 +59,8 @@ public class BArrayType extends BType implements ArrayType { private IntersectionType immutableType; private IntersectionType intersectionType = null; private int typeFlags; + private ListDefinition defn; + private final Env env = Env.getInstance(); public BArrayType(Type elementType) { this(elementType, false); } @@ -87,6 +97,8 @@ public BArrayType(int typeFlags, int size, boolean readonly, boolean hasFillerVa public void setElementType(Type elementType, int dimensions, boolean elementRO) { this.elementType = readonly && !elementRO ? ReadOnlyUtils.getReadOnlyType(elementType) : elementType; this.dimensions = dimensions; + defn = null; + resetSemTypeCache(); } private void setFlagsBasedOnElementType() { @@ -204,4 +216,39 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + SemType createSemType() { + if (defn != null) { + return defn.getSemType(env); + } + defn = new ListDefinition(); + SemType elementType = Builder.from(getElementType()); +// if (Core.isSubtypeSimple(elementType, Core.B_TYPE_TOP)) { +// SemType semTypePart = defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, Builder.neverType(), +// CellAtomicType.CellMutability.CELL_MUT_NONE); +// SemType bTypePart = BTypeConverter.wrapAsPureBType(this); +// return Core.union(semTypePart, bTypePart); +// } + SemType pureBTypePart = Core.intersect(elementType, Core.B_TYPE_TOP); + if (!Core.isNever(pureBTypePart)) { + SemType pureSemTypePart = Core.intersect(elementType, Core.SEMTYPE_TOP); + SemType semTypePart = getSemTypePart(pureSemTypePart); + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + return Core.union(semTypePart, bTypePart); + } + + return getSemTypePart(elementType); + } + + private SemType getSemTypePart(SemType elementType) { + CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + if (size == -1) { + return defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, elementType, mut); + } else { + SemType[] initial = {elementType}; + return defn.defineListTypeWrapped(env, initial, size, Builder.neverType(), mut); + } + } } 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 d71bf3318fa9..6d8d1add718a 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,6 +24,7 @@ import io.ballerina.runtime.api.types.IntersectableReferenceType; 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.SemType; import java.util.ArrayList; @@ -223,6 +224,6 @@ SemType createSemType() { if (effectiveType instanceof SemType semType) { return semType; } - return ((BType) effectiveType).createSemType(); + return Builder.from(effectiveType); } } 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 98d6d43852ee..5c5258de7c4d 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,7 +24,12 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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.ListDefinition; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TupleValueImpl; @@ -41,6 +46,7 @@ */ public class BTupleType extends BAnnotatableType implements TupleType { + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private List tupleTypes; private Type restType; private int typeFlags; @@ -51,6 +57,8 @@ public class BTupleType extends BAnnotatableType implements TupleType { private boolean resolving; private boolean resolvingReadonly; private String cachedToString; + private ListDefinition defn; + private final Env env = Env.getInstance(); /** * Create a {@code BTupleType} which represents the tuple type. @@ -168,6 +176,8 @@ public void setMemberTypes(List members, Type restType) { this.restType = restType; } checkAllMembers(); + defn = null; + resetSemTypeCache(); } @Override @@ -303,6 +313,47 @@ public String getAnnotationKey() { @Override SemType createSemType() { - return BTypeConverter.fromTupleType(this); + if (defn != null) { + return defn.getSemType(env); + } + defn = new ListDefinition(); + SemType[] memberTypes = new SemType[tupleTypes.size()]; + boolean hasBTypePart = false; + for (int i = 0; i < tupleTypes.size(); i++) { + SemType memberType = Builder.from(tupleTypes.get(i)); + if (Core.isNever(memberType)) { + // TODO: This is not correct and blow up if this is recursive. But current jBal type implementation + // treats these as never while nBal don't. Revisit this once all types are done + return Builder.neverType(); +// } else if (Core.isSubtypeSimple(memberType, Core.B_TYPE_TOP)) { +// SemType semTypePart = defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, Builder.neverType(), +// CellAtomicType.CellMutability.CELL_MUT_NONE); +// SemType bTypePart = BTypeConverter.wrapAsPureBType(this); +// return Core.union(semTypePart, bTypePart); + } else if (!Core.isNever(Core.intersect(memberType, Core.B_TYPE_TOP))) { + hasBTypePart = true; + memberType = Core.intersect(memberType, Core.SEMTYPE_TOP); + } + memberTypes[i] = memberType; + } + CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType rest = restType != null ? Builder.from(restType) : Builder.neverType(); +// if (Core.isSubtypeSimple(rest, Core.B_TYPE_TOP)) { +// SemType semTypePart = +// defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, Builder.neverType(), mut); +// SemType bTypePart = BTypeConverter.wrapAsPureBType(this); +// return Core.union(semTypePart, bTypePart); +// } + if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { + hasBTypePart = true; + rest = Core.intersect(rest, Core.SEMTYPE_TOP); + } + if (hasBTypePart) { + SemType semTypePart = defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + return Core.union(semTypePart, bTypePart); + } + return defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } } 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 607be6ddf80f..58190f9cdf0b 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,6 +22,8 @@ import io.ballerina.runtime.api.creators.ErrorCreator; 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.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; @@ -41,6 +43,8 @@ * @since 0.995.0 */ public abstract class BType implements Type, SubTypeData, Supplier { + + private static final SemType READONLY_WITH_B_TYPE = Core.union(Builder.readonlyType(), Core.B_TYPE_TOP); protected String typeName; protected Module pkg; protected Class valueClass; @@ -250,6 +254,9 @@ SemType createSemType() { public final SemType get() { if (cachedSemType == null) { cachedSemType = createSemType(); + if (isReadOnly()) { + cachedSemType = Core.intersect(cachedSemType, READONLY_WITH_B_TYPE); + } } return cachedSemType; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index a48604eeddb7..924d205e73b5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; /** * This is a utility class for {@code Builder} class so that BTypes don't need to expose their internal structure as @@ -45,12 +46,12 @@ final class BTypeConverter { private BTypeConverter() { } - private static final SemType READONLY_SEMTYPE_PART = - unionOf(Builder.stringType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), - Builder.nilType(), Builder.decimalType()); - private static final SemType ANY_SEMTYPE_PART = - unionOf(Builder.stringType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), - Builder.nilType(), Builder.decimalType()); + // FIXME: + private static final SemType implementedTypes = + unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), + Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType()); + private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); + private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); private static SemType unionOf(SemType... semTypes) { SemType result = Builder.neverType(); @@ -78,17 +79,8 @@ static SemType fromReadonly(BReadonlyType readonlyType) { return Core.union(READONLY_SEMTYPE_PART, bTypePart); } - static SemType fromTupleType(BTupleType tupleType) { - for (Type type : tupleType.getTupleTypes()) { - if (Core.isNever(from(type))) { - return Builder.neverType(); - } - } - return wrapAsPureBType(tupleType); - } - - static SemType wrapAsPureBType(BType tupleType) { - return Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(tupleType)); + static SemType wrapAsPureBType(BType bType) { + return Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(bType)); } static SemType fromAnyType(BAnyType anyType) { @@ -146,13 +138,30 @@ private static BTypeParts split(Type type) { return splitReadonly(readonlyType); } else if (type instanceof BFiniteType finiteType) { return splitFiniteType(finiteType); + } else if (type instanceof BArrayType || type instanceof BTupleType) { + return splitSemTypeSupplier((Supplier) type); } else { return new BTypeParts(Builder.neverType(), List.of(type)); } } + private static BTypeParts splitSemTypeSupplier(Supplier supplier) { + SemType semtype = supplier.get(); + SemType bBTypePart = Core.intersect(semtype, Core.B_TYPE_TOP); + if (Core.isNever(bBTypePart)) { + return new BTypeParts(semtype, Collections.emptyList()); + } + SemType pureSemTypePart = Core.intersect(semtype, Core.SEMTYPE_TOP); + BType bType = (BType) Core.subTypeData(semtype, BasicTypeCode.BT_B_TYPE); + return new BTypeParts(pureSemTypePart, List.of(bType)); + } + private static BTypeParts splitAnyType(BAnyType anyType) { - return new BTypeParts(ANY_SEMTYPE_PART, List.of(anyType)); + SemType semTypePart = ANY_SEMTYPE_PART; + if (anyType.isReadOnly()) { + semTypePart = Core.intersect(semTypePart, Builder.readonlyType()); + } + return new BTypeParts(semTypePart, List.of(anyType)); } private static BTypeParts splitFiniteType(BFiniteType finiteType) { 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 15671738e5d9..cdee20c28d2f 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,6 +25,7 @@ import io.ballerina.runtime.api.types.IntersectableReferenceType; 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.SemType; import java.util.Objects; @@ -134,6 +135,6 @@ SemType createSemType() { if (referredType instanceof SemType semType) { return semType; } - return ((BType) referredType).createSemType(); + return Builder.from(referredType); } } 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 index d2cdad6b54c8..34f0b5704616 100644 --- 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 @@ -226,6 +226,8 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) case FLOAT -> Builder.floatType(); case DECIMAL -> Builder.decimalType(); case STRING -> Builder.stringType(); + case READONLY -> Builder.readonlyType(); + case ANY -> Builder.anyType(); default -> throw new IllegalStateException("Unknown type: " + td); }; } From 5991bb95803c3a22a0b7cbe124aae525ce1aa3c7 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 6 Jun 2024 15:42:07 +0530 Subject: [PATCH 029/178] Refactor list projection --- .../runtime/api/types/semtype/Core.java | 19 +- .../runtime/api/types/semtype/ListProj.java | 182 +-------------- .../runtime/api/types/semtype/SemType.java | 4 +- .../internal/types/BTypeConverter.java | 1 - .../internal/types/semtype/BCellSubType.java | 6 +- .../internal/types/semtype/BIntSubType.java | 42 ++-- .../internal/types/semtype/BListProj.java | 216 ++++++++++++++++++ .../internal/types/semtype/BListSubType.java | 19 +- .../types/semtype/BddBasedSubType.java | 0 .../types/semtype/DelegatedSubType.java | 26 +++ .../types/semtype/FixedLengthArray.java | 4 + 11 files changed, 294 insertions(+), 225 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddBasedSubType.java 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 index 2ad8b5da8713..821af7bddbe2 100644 --- 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 @@ -19,8 +19,7 @@ package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.internal.types.semtype.AllOrNothing; -import io.ballerina.runtime.internal.types.semtype.BCellSubType; -import io.ballerina.runtime.internal.types.semtype.BListSubType; +import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; import io.ballerina.runtime.internal.types.semtype.SubTypeData; import io.ballerina.runtime.internal.types.semtype.SubtypePair; import io.ballerina.runtime.internal.types.semtype.SubtypePairs; @@ -112,20 +111,10 @@ public static SemType diff(SemType t1, SemType t2) { } public static SubType getComplexSubtypeData(SemType 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(); -// } + assert (t.some() & (1 << code.code())) != 0; SubType subType = t.subTypeData()[code.code()]; - // FIXME: introduce an interface for this - if (subType instanceof BCellSubType cellSubType) { - return cellSubType.inner; - } else if (subType instanceof BListSubType listSubType) { - return listSubType.inner; + if (subType instanceof DelegatedSubType wrapper) { + return wrapper.inner(); } return subType; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java index a594934bd37d..13746dfd901e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java @@ -18,188 +18,20 @@ package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.internal.types.semtype.BIntSubType; -import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; -import io.ballerina.runtime.internal.types.semtype.SubTypeData; - -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.cellContaining; -import static io.ballerina.runtime.api.types.semtype.Builder.roCellContaining; -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.fixedArrayShallowCopy; -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; +import io.ballerina.runtime.internal.types.semtype.BListProj; +/** + * Wrapper utility class for list type projection. + * + * @since 2201.10.0 + */ public final class ListProj { private ListProj() { } public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { - if (t.some == 0) { - return t == Builder.listType() ? Builder.valType() : Builder.neverType(); - } else { - SubTypeData keyData = Core.intSubtype(k); - if (isNothingSubtype(keyData)) { - return Builder.neverType(); - } - 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.neverType(); - } 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)))); - } + return BListProj.listProjInnerVal(cx, t, k); } - private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjunction pos, Conjunction neg) { - FixedLengthArray members; - SemType rest; - if (pos == null) { - members = FixedLengthArray.empty(); - rest = cellContaining(cx.env, union(Builder.valType(), Builder.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); - Pair - intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); - if (intersected == null) { - return Builder.neverType(); - } - members = intersected.first(); - rest = intersected.second(); - } - } - if (fixedArrayAnyEmpty(cx, members)) { - return Builder.neverType(); - } - // Ensure that we can use isNever on rest in listInhabited - if (!isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { - rest = roCellContaining(cx.env, Builder.neverType()); - } - } - 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.neverType(); - 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] = 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; - } - - private static Pair listProjSamples(Integer[] indices, SubTypeData k) { - List> v = new ArrayList<>(); - for (int i : indices) { - v.add(Pair.from(i, intSubtypeContains(k, i))); - } - // FIXME: refactor this so we don't have to expose the internal details of BIntSubType - // Maybe we can return optional partitions - 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(p -> p.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/api/types/semtype/SemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java index cc0f397adb1f..1462ade8ba35 100644 --- 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 @@ -28,8 +28,8 @@ */ public abstract sealed class SemType implements BasicTypeBitSet permits BSemTypeWrapper, PureSemType { - final int all; - final int some; + public final int all; + public final int some; private final SubType[] subTypeData; private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 924d205e73b5..46fcd22f3622 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -46,7 +46,6 @@ final class BTypeConverter { private BTypeConverter() { } - // FIXME: private static final SemType implementedTypes = unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType()); 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 index 58820350a891..cb0b28c112a3 100644 --- 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 @@ -41,7 +41,7 @@ * * @since 2201.10.0 */ -public final class BCellSubType extends SubType { +public final class BCellSubType extends SubType implements DelegatedSubType { public final Bdd inner; @@ -182,4 +182,8 @@ private static SemType filteredCellListUnion(Conjunction negList, Predicate result = new ArrayList<>(); int i1 = 0; 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..961cfab65649 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java @@ -0,0 +1,216 @@ +/* + * 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.ListAtomicType; +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.cellContaining; +import static io.ballerina.runtime.api.types.semtype.Builder.roCellContaining; +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.10.0 + */ +public final class BListProj { + + private BListProj() { + } + + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + if (t.some == 0) { + return t == Builder.listType() ? Builder.valType() : Builder.neverType(); + } else { + SubTypeData keyData = Core.intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.neverType(); + } + 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.neverType(); + } 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 = cellContaining(cx.env, union(Builder.valType(), Builder.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 = 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.neverType(); + } + members = intersected.first(); + rest = intersected.second(); + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return Builder.neverType(); + } + // Ensure that we can use isNever on rest in listInhabited + if (!isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { + rest = roCellContaining(cx.env, Builder.neverType()); + } + } + 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.neverType(); + 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] = 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; + } + + 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 index 529f61417e67..449b0eb2c249 100644 --- 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 @@ -44,11 +44,9 @@ import static io.ballerina.runtime.api.types.semtype.Core.intersectMemberSemTypes; import static io.ballerina.runtime.api.types.semtype.ListAtomicType.LIST_ATOMIC_INNER; import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; -import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeMax; -import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeOverlapRange; // TODO: this has lot of common code with cell (and future mapping), consider refact -public class BListSubType extends SubType { +public class BListSubType extends SubType implements DelegatedSubType { public final Bdd inner; @@ -118,7 +116,7 @@ private static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjuncti Conjunction p = pos.next(); // the neg case is in case we grow the array in listInhabited if (p != null || neg != null) { - members = fixedArrayShallowCopy(members); + members = members.shallowCopy(); } while (true) { if (p == null) { @@ -364,11 +362,6 @@ private static SemType fixedArrayGet(FixedLengthArray members, int index) { return members.initial()[i]; } - // FIXME: move this to FixedLengthArray - public static FixedLengthArray fixedArrayShallowCopy(FixedLengthArray array) { - return new FixedLengthArray(array.initial().clone(), array.fixedLength()); - } - public static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, SemType rest, int index) { return cellInnerVal(listMemberAt(fixedArray, rest, index)); } @@ -404,11 +397,11 @@ static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, SemType m = Core.union(m, cellInner(fixedArrayGet(fixedArray, i))); } } - if (intSubtypeOverlapRange(intSubtype, new BIntSubType.Range(initLen, fixedLen - 1))) { + if (intSubtype.isRangeOverlap(new BIntSubType.Range(initLen, fixedLen - 1))) { m = Core.union(m, cellInner(fixedArrayGet(fixedArray, fixedLen - 1))); } } - if (fixedLen == 0 || intSubtypeMax(intSubtype) > fixedLen - 1) { + if (fixedLen == 0 || intSubtype.max() > fixedLen - 1) { m = Core.union(m, cellInner(rest)); } return m; @@ -427,4 +420,8 @@ public SubTypeData data() { throw new IllegalStateException("unimplemented"); } + @Override + public Bdd inner() { + return inner; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddBasedSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddBasedSubType.java deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java index e69de29bb2d1..3a145f5f44c0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java @@ -0,0 +1,26 @@ +/* + * 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.Bdd; + +public interface DelegatedSubType { + + Bdd inner(); +} 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 index ff789825b517..4056f636fe03 100644 --- 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 @@ -58,4 +58,8 @@ static FixedLengthArray normalized(SemType[] initial, int fixedLength) { public static FixedLengthArray empty() { return EMPTY; } + + public FixedLengthArray shallowCopy() { + return new FixedLengthArray(initial.clone(), fixedLength); + } } From 8f7d28efc29d834957fb70d69ecb19ac3a61bfe7 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 4 Jun 2024 13:59:02 +0530 Subject: [PATCH 030/178] Ennable list type tests --- .../port/test/CompilerSemTypeResolver.java | 20 ------ .../port/test/CompilerTypeTestEnv.java | 5 ++ .../port/test/RuntimeSemTypeResolver.java | 67 +++++++++++++++++++ .../semtype/port/test/RuntimeTypeTestAPI.java | 11 +-- .../port/test/RuntimeTypeTestContext.java | 7 +- .../semtype/port/test/RuntimeTypeTestEnv.java | 15 +++-- .../semtype/port/test/SemTypeResolver.java | 21 ++++++ .../semtype/port/test/SemTypeTest.java | 33 ++------- .../semtype/port/test/TypeTestEnv.java | 2 + 9 files changed, 124 insertions(+), 57 deletions(-) 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 index 0ae3c91e47f3..2929936eb974 100644 --- 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 @@ -52,7 +52,6 @@ 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.expressions.BLangSimpleVarRef; import org.wso2.ballerinalang.compiler.tree.types.BLangArrayType; import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; @@ -411,25 +410,6 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangNode expr) { - if (expr instanceof BLangLiteral literal) { - return listSize((Number) literal.value); - } else if (expr instanceof BLangSimpleVarRef varRef) { - String varName = varRef.variableName.value; - return from(mod, mod.get(varName)); - } else if (expr instanceof BLangConstant constant) { - return 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(); - } - private SemType resolveListInner(TypeTestContext cx, int size, SemType eType) { ListDefinition ld = new ListDefinition(); return resolveListInner(cx, ld, size, eType); 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 index bca96679f892..116d114445be 100644 --- 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 @@ -44,4 +44,9 @@ public Map getTypeNameSemTypeMap() { 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/RuntimeSemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java index 34f0b5704616..dc9d2a5abe6c 100644 --- 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 @@ -20,16 +20,21 @@ import io.ballerina.runtime.api.types.semtype.Builder; 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.Definition; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.TypeKind; 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.types.BLangArrayType; import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +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; @@ -50,11 +55,14 @@ 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.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; 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, @@ -104,10 +112,69 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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); default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); }; } + 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.neverType(); + 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.neverType(), 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()) 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 index e0c0e715868f..794ec0c342ee 100644 --- 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 @@ -21,6 +21,7 @@ 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.SemType; public class RuntimeTypeTestAPI implements TypeTestAPI { @@ -36,10 +37,10 @@ public static RuntimeTypeTestAPI getInstance() { @Override public boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2) { - return Core.isSubType(form(cx), t1, t2); + return Core.isSubType(from(cx), t1, t2); } - private static Context form(TypeTestContext cx) { + private static Context from(TypeTestContext cx) { return (Context) cx.getInnerContext(); } @@ -50,7 +51,7 @@ public boolean isSubtypeSimple(SemType t1, SemType t2) { @Override public boolean isListType(SemType t) { - throw new IllegalArgumentException("list type not implemented"); + return Core.isSubtypeSimple(t, Builder.listType()); } @Override @@ -70,11 +71,11 @@ public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemTy @Override public SemType listProj(TypeTestContext context, SemType t, SemType key) { - throw new IllegalArgumentException("list proj not implemented"); + return ListProj.listProjInnerVal(from(context), t, key); } @Override public SemType listMemberType(TypeTestContext context, SemType t, SemType key) { - throw new IllegalArgumentException("list member type not implemented"); + 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 index dba9c3cc2a4f..add06c8db8c5 100644 --- 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 @@ -19,14 +19,17 @@ 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) { @@ -40,11 +43,11 @@ public TypeTestEnv getEnv() { @Override public Object getInnerEnv() { - throw new IllegalStateException("not implemented"); + return env.getInnerEnv(); } @Override public Object getInnerContext() { - return new Context(); + 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 index ed7605804b82..2592d6ba5565 100644 --- 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 @@ -18,6 +18,7 @@ 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; @@ -26,13 +27,14 @@ class RuntimeTypeTestEnv implements TypeTestEnv { private final Map typeMap = new HashMap<>(); + private final Env env; - private RuntimeTypeTestEnv() { - + private RuntimeTypeTestEnv(Env env) { + this.env = env; } - public static synchronized RuntimeTypeTestEnv from() { - return new RuntimeTypeTestEnv(); + public static synchronized RuntimeTypeTestEnv from(Env env) { + return new RuntimeTypeTestEnv(env); } @Override @@ -44,4 +46,9 @@ public Map getTypeNameSemTypeMap() { 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/SemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java index f8695377e5cf..0d9054bd3235 100644 --- 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 @@ -22,6 +22,8 @@ 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; @@ -32,6 +34,25 @@ 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) { 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 index 795eb6bc70ab..8163874528da 100644 --- 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 @@ -17,6 +17,7 @@ */ 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.ValueComparisonUtils; import io.ballerina.tools.diagnostics.Diagnostic; @@ -231,29 +232,6 @@ public Object[] runtimeFileNameProviderFunc() { "table-readonly-t.bal", "table-t.bal" )); - Predicate listFilter = createRuntimeFileNameFilter(Set.of( - "bdddiff1-tv.bal", - "fixed-length-array-t.bal", - "fixed-length-array2-t.bal", - "fixed-length-array-large-t.bal", - "fixed-length-array-tuple-t.bal", - "list1-tv.bal", - "proj1-tv.bal", - "proj2-tv.bal", - "proj3-tv.bal", - "proj4-tv.bal", - "proj5-tv.bal", - "proj6-tv.bal", - "proj7-tv.bal", - "proj8-tv.bal", - "proj9-tv.bal", - "proj10-tv.bal", - "test_test.bal", - "tuple1-tv.bal", - "tuple2-tv.bal", - "tuple3-tv.bal", - "tuple4-tv.bal" - )); Predicate functionFilter = createRuntimeFileNameFilter(Set.of( "function2-tv.bal", "function-intersection-tv.bal", @@ -278,7 +256,11 @@ public Object[] runtimeFileNameProviderFunc() { "record-proj-tv.bal", "record-t.bal", "recursive-record-t.bal", - "test_test.bal" + "test_test.bal", + "proj1-tv.bal", + "proj3-tv.bal", + "tuple1-tv.bal", + "tuple3-tv.bal" )); Predicate xmlFilter = createRuntimeFileNameFilter(Set.of( "xml-complex-ro-tv.bal", @@ -296,7 +278,6 @@ public Object[] runtimeFileNameProviderFunc() { "object-distinct-tv.bal" )); return balFiles.stream() - .filter(listFilter) .filter(tableFilter) .filter(functionFilter) .filter(mappingFilter) @@ -333,7 +314,7 @@ private static TypeCheckData run File file) { String fileName = file.getAbsolutePath(); BCompileUtil.PackageSyntaxTreePair pair = BCompileUtil.compileSemType(fileName); - TypeTestEnv env = RuntimeTypeTestEnv.from(); + TypeTestEnv env = RuntimeTypeTestEnv.from(Env.getInstance()); TypeTestContext context = RuntimeTypeTestContext.from(env); SemTypeResolver resolver = new RuntimeSemTypeResolver(); return new TypeCheckData<>(pair, context, env, resolver); diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java index 727232e5acd8..3a1afd44326f 100644 --- a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java @@ -25,4 +25,6 @@ public interface TypeTestEnv { Map getTypeNameSemTypeMap(); void addTypeDef(String value, SemType semtype); + + Object getInnerEnv(); } From be45d007202781e7dcb78243b1eddf754b46e472 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 7 Jun 2024 09:15:14 +0530 Subject: [PATCH 031/178] Make context thread safe --- .../runtime/api/types/semtype/Context.java | 4 +-- .../runtime/api/types/semtype/Env.java | 26 ++++++++++++------- .../runtime/internal/TypeChecker.java | 21 +++++++++++++-- 3 files changed, 38 insertions(+), 13 deletions(-) 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 index 5cc85ed9107d..4c8366065390 100644 --- 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 @@ -25,7 +25,8 @@ import java.util.Objects; /** - * Context in which semtype was defined in. + * Context in which type checking operations are performed. Note context is not thread safe, requiring external + * synchronization if shared between threads. Multiple contexts may share same environment without issue. * * @since 2201.10.0 */ @@ -35,7 +36,6 @@ public final class Context { private final List memoStack = new ArrayList<>(); public final Env env; public final Map listMemo = new HashMap<>(); - // SEMTYPE-TODO: Fill this in as needed, currently just a placeholder since basic types don't need it private Context(Env env) { this.env = env; 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 index 31a7a647b9f4..d3c7d828b551 100644 --- 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 @@ -31,17 +31,19 @@ /** * Represent the environment in which {@code SemTypes} are defined in. Type checking types defined in different - * environments with each other in undefined. - * + * environments with each other in undefined. This is safe to be shared between multiple threads. * @since 2201.10.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 to have multiple instances of Env. private static final Env INSTANCE = new Env(); private final Map atomTable; private final ReadWriteLock atomTableLock = new ReentrantReadWriteLock(); + private final ReadWriteLock recListLock = new ReentrantReadWriteLock(); private final List recListAtoms; private final Map cellTypeCache = new ConcurrentHashMap<>(); @@ -61,17 +63,17 @@ public TypeAtom cellAtom(CellAtomicType atomicType) { } private TypeAtom typeAtom(AtomicType atomicType) { - this.atomTableLock.readLock().lock(); + atomTableLock.readLock().lock(); try { TypeAtom ta = this.atomTable.get(atomicType); if (ta != null) { return ta; } } finally { - this.atomTableLock.readLock().unlock(); + atomTableLock.readLock().unlock(); } - this.atomTableLock.writeLock().lock(); + atomTableLock.writeLock().lock(); try { // we are double-checking since there may be 2 trying to add at the same time TypeAtom ta = this.atomTable.get(atomicType); @@ -83,7 +85,7 @@ private TypeAtom typeAtom(AtomicType atomicType) { return result; } } finally { - this.atomTableLock.writeLock().unlock(); + atomTableLock.writeLock().unlock(); } } @@ -97,11 +99,14 @@ void cacheCellType(SemType ty, CellAtomicType.CellMutability mut, SemType semTyp public RecAtom recListAtom() { // TODO: do we have seperate read and write operations, if so use rw lock - synchronized (this.recListAtoms) { + 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(); } } @@ -116,8 +121,11 @@ public Atom listAtom(ListAtomicType atomicType) { } public ListAtomicType getRecListAtomType(RecAtom ra) { - synchronized (this.recListAtoms) { - return this.recListAtoms.get(ra.index); + recListLock.readLock().lock(); + try { + return this.recListAtoms.get(ra.index()); + } finally { + recListLock.readLock().unlock(); } } 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 53f78372975f..36c98b6c3e25 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 @@ -76,8 +76,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BOOLEAN; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BYTE; @@ -118,7 +120,7 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; - private static final Context cx = Context.from(Env.getInstance()); + private static final Map contexts = new ConcurrentHashMap<>(10); public static Object checkCast(Object sourceVal, Type targetType) { @@ -147,6 +149,20 @@ public static Object checkCast(Object sourceVal, Type targetType) { throw createTypeCastError(sourceVal, targetType, errors); } + private static Context context() { + // We are pinning each context to thread. This depends on the assumption physical thread is not going to + // get switched while type checking + Thread currentThread = Thread.currentThread(); + long threadID = currentThread.getId(); + Context cx = contexts.get(threadID); + if (cx != null) { + return cx; + } + cx = Context.from(Env.getInstance()); + contexts.put(threadID, cx); + return cx; + } + public static long anyToInt(Object sourceVal) { return TypeConverter.anyToIntCast(sourceVal, () -> ErrorUtils.createTypeCastError(sourceVal, TYPE_INT)); @@ -561,7 +577,7 @@ private static TypeCheckResult isSubType(Type source, Type target) { private static TypeCheckResult isSubTypeInner(Object sourceValue, SemType source, SemType target) { TypeCheckResult result = isSubTypeInner(source, target); if (result != TypeCheckResult.FALSE || - !Core.isSubType(cx, Core.intersect(source, SEMTYPE_TOP), Builder.readonlyType())) { + !Core.isSubType(context(), Core.intersect(source, SEMTYPE_TOP), Builder.readonlyType())) { return result; } return isSubTypeImmutableValue(sourceValue, target); @@ -577,6 +593,7 @@ private static TypeCheckResult isSubTypeImmutableValue(Object sourceValue, SemTy } private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { + Context cx = context(); if (!Core.containsBasicType(source, B_TYPE_TOP)) { return Core.isSubType(cx, source, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; } From 829c5898e035547798eca664af053371bb3ad46b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 7 Jun 2024 16:56:28 +0530 Subject: [PATCH 032/178] Optimize TypeChecker:basicType Avoid doing a `instanceOf` on a abstract type (which trigger a full type check) and instead use a virtual function that create the basic type which should be cheaper --- .../main/java/io/ballerina/runtime/api/values/BValue.java | 6 ++++++ .../java/io/ballerina/runtime/internal/TypeChecker.java | 4 +--- .../io/ballerina/runtime/internal/values/ArrayValue.java | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) 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..77ec7a150d16 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,6 +18,8 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Map; @@ -58,4 +60,8 @@ default String informalStringValue(BLink parent) { String expressionStringValue(BLink parent); Type getType(); + + default SemType basicType() { + return Builder.bType(); + } } 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 36c98b6c3e25..4a68abd19758 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 @@ -618,10 +618,8 @@ private static SemType basicType(Object value) { return Builder.booleanType(); } else if (value instanceof DecimalValue) { return Builder.decimalType(); - } else if (value instanceof ArrayValue) { - return Builder.listType(); } else { - return Builder.bType(); + return ((BValue) value).basicType(); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java index af4febe73476..437ebd6bd002 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.internal.values; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BArray; /** @@ -39,4 +41,9 @@ public interface ArrayValue extends RefValue, BArray, CollectionValue { @Override void setLength(long length); + + @Override + default SemType basicType() { + return Builder.listType(); + } } From 146891b2312796771d607f4f5e0f4535486655af Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 7 Jun 2024 16:56:54 +0530 Subject: [PATCH 033/178] Port list subtype improvements --- .../internal/types/semtype/BListSubType.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 index 449b0eb2c249..0647b7c19536 100644 --- 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 @@ -168,6 +168,20 @@ private static boolean listInhabited(Context cx, Integer[] indices, SemType[] me 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()); } @@ -176,6 +190,7 @@ private static boolean listInhabited(Context cx, Integer[] indices, SemType[] me 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; From 2a0db6d996abef28a5df0b082b63a58e9d1bf8a3 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 9 Jun 2024 06:08:11 +0530 Subject: [PATCH 034/178] Fix hash and equals for semtypes Memoization depends on hash and equals. Otherwise we create duplicate memos for equal types --- .../api/types/semtype/BasicTypeCode.java | 16 +++++++++ .../runtime/api/types/semtype/Bdd.java | 15 --------- .../api/types/semtype/BddAllOrNothing.java | 4 +++ .../runtime/api/types/semtype/BddMemo.java | 18 ++++++++++ .../runtime/api/types/semtype/BddNode.java | 14 ++++++++ .../runtime/api/types/semtype/Core.java | 2 +- .../runtime/api/types/semtype/RecAtom.java | 16 +++++++++ .../runtime/api/types/semtype/SemType.java | 33 +++++++++++++++++++ .../runtime/api/types/semtype/SubType.java | 20 +++++++++++ .../runtime/internal/TypeChecker.java | 6 ++-- .../types/semtype/BBooleanSubType.java | 19 +++++++++++ .../internal/types/semtype/BCellSubType.java | 17 ++++++++++ .../internal/types/semtype/BListSubType.java | 17 ++++++++++ .../types/semtype/EnumerableSubtypeData.java | 17 ++++++++++ .../types/semtype/FixedLengthArray.java | 20 +++++++++++ 15 files changed, 215 insertions(+), 19 deletions(-) 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 index 7402bb44ab15..606e089b205e 100644 --- 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 @@ -98,6 +98,22 @@ 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; 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 index e9620e257d50..43073e7f1e10 100644 --- 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 @@ -229,19 +229,4 @@ public static boolean bddEvery(Context cx, Bdd b, Conjunction pos, Conjunction n && bddEvery(cx, bn.middle(), pos, neg, predicate) && bddEvery(cx, bn.right(), pos, and(bn.atom(), neg), predicate); } - - - - - - - - - - - - - - - } 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 index 8ca94be11ec2..5a7762763e46 100644 --- 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 @@ -37,4 +37,8 @@ public int hashCode() { return 0xa11084 + (this == ALL ? 1 : 0); } + @Override + public boolean equals(Object o) { + return this == o; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java index 52f6a250f93a..30beff46fdb2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java @@ -18,6 +18,8 @@ package io.ballerina.runtime.api.types.semtype; +import java.util.Objects; + // TODO: consider moving this to inner as well public final class BddMemo { @@ -35,4 +37,20 @@ public enum Status { PROVISIONAL, 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); + } } 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 index 814f8448a717..acb47be97b01 100644 --- 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 @@ -31,6 +31,7 @@ public final class BddNode extends Bdd { private final Bdd left; private final Bdd middle; private final Bdd right; + private volatile Integer hashCode = null; BddNode(Atom atom, Bdd left, Bdd middle, Bdd right) { super(false, false); @@ -74,6 +75,19 @@ public boolean equals(Object obj) { @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(atom, left, middle, right); } 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 index 821af7bddbe2..c4fecd2179b1 100644 --- 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 @@ -301,7 +301,7 @@ public static boolean isSameType(Context cx, SemType t1, SemType t2) { return isSubType(cx, t1, t2) && isSubType(cx, t2, t1); } - public static BasicTypeBitSet widenToBasicTypes(SemType t) { + public static SemType widenToBasicTypes(SemType t) { int all = t.all | t.some; if (cardinality(all) > 1) { throw new IllegalStateException("Cannot widen to basic type for a type with multiple basic types"); 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 index 3fd21312cb25..a15f7caccd62 100644 --- 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 @@ -45,4 +45,20 @@ public static RecAtom createRecAtom(int index) { 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 index 1462ade8ba35..9291f2381a81 100644 --- 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 @@ -21,6 +21,9 @@ import io.ballerina.runtime.internal.types.BSemTypeWrapper; import io.ballerina.runtime.internal.types.semtype.PureSemType; +import java.util.Arrays; +import java.util.Objects; + /** * Runtime representation of SemType. * @@ -32,6 +35,7 @@ public abstract sealed class SemType implements BasicTypeBitSet permits BSemType public final int some; private final SubType[] subTypeData; private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + private Integer hashCode; protected SemType(int all, int some, SubType[] subTypeData) { this.all = all; @@ -73,4 +77,33 @@ public final int some() { public final SubType[] subTypeData() { return subTypeData; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof SemType semType)) { + return false; + } + return all == semType.all && some == semType.some && Objects.deepEquals(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)); + } } 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 index a70667c974e4..2850e95e5898 100644 --- 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 @@ -20,6 +20,8 @@ import io.ballerina.runtime.internal.types.semtype.SubTypeData; +import java.util.Objects; + /** * Describe set of operation supported by each basic Type. * @@ -56,4 +58,22 @@ public final boolean isNothing() { } 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/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 4a68abd19758..916a89b89c4b 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 @@ -73,13 +73,13 @@ import io.ballerina.runtime.internal.values.XmlValue; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BOOLEAN; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BYTE; @@ -120,7 +120,7 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; - private static final Map contexts = new ConcurrentHashMap<>(10); + private static final Map contexts = new HashMap<>(10); public static Object checkCast(Object sourceVal, Type targetType) { @@ -151,7 +151,7 @@ public static Object checkCast(Object sourceVal, Type targetType) { private static Context context() { // We are pinning each context to thread. This depends on the assumption physical thread is not going to - // get switched while type checking + // get switched while type checking. Also for the same reason we don't need to synchronize this method. Thread currentThread = Thread.currentThread(); long threadID = currentThread.getId(); Context cx = contexts.get(threadID); 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 index a6f67f8aa800..39619b0ee099 100644 --- 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 @@ -130,6 +130,25 @@ 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); 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 index cb0b28c112a3..951a0d97cb3b 100644 --- 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 @@ -30,6 +30,7 @@ import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.api.types.semtype.TypeAtom; +import java.util.Objects; import java.util.function.Predicate; // TODO: would making this a child class of say BddNode be faster than making this a delegate @@ -186,4 +187,20 @@ private static SemType filteredCellListUnion(Conjunction negList, Predicate> { 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(); 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 index 4056f636fe03..0ce808f7d823 100644 --- 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 @@ -20,6 +20,9 @@ import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.Arrays; +import java.util.Objects; + public record FixedLengthArray(SemType[] initial, int fixedLength) { public FixedLengthArray { @@ -60,6 +63,23 @@ public static FixedLengthArray 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() { + return Objects.hash(Arrays.hashCode(initial), fixedLength); + } } From c257b76e3b8471a62f6cc6ed868d768cda8df375 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 10 Jun 2024 08:43:55 +0530 Subject: [PATCH 035/178] Cache type check results --- .../api/types/semtype/BasicTypeCode.java | 1 + .../runtime/api/types/semtype/Core.java | 8 +++- .../runtime/api/types/semtype/SemType.java | 42 +++++++++++++++++-- .../runtime/api/types/semtype/TypeAtom.java | 4 ++ .../internal/types/semtype/BListSubType.java | 2 +- .../types/semtype/FixedLengthArray.java | 35 +++++++++++++++- 6 files changed, 85 insertions(+), 7 deletions(-) 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 index 606e089b205e..5a19f210ff8a 100644 --- 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 @@ -76,6 +76,7 @@ public final class BasicTypeCode { // Helper bit fields (does not represent basic type tag) static final int VT_COUNT = CODE_OBJECT + 1; + static final int BASIC_TYPE_MASK = (1 << (CODE_STRING + 1)) - 1; static final int VT_MASK = (1 << VT_COUNT) - 1; static final int VT_COUNT_INHERENTLY_IMMUTABLE = 0x0A; 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 index c4fecd2179b1..77dd904aa3c6 100644 --- 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 @@ -265,7 +265,13 @@ public static boolean isNever(SemType t) { public static boolean isSubType(Context cx, SemType t1, SemType t2) { // IF t1 and t2 are not pure semtypes calling this is an undefined - return isEmpty(cx, diff(t1, t2)); + Optional cached = t1.cachedSubTypeRelation(t2); + if (cached.isPresent()) { + return cached.get(); + } + boolean result = isEmpty(cx, diff(t1, t2)); + t1.cacheSubTypeRelation(t2, result); + return result; } public static boolean isSubtypeSimple(SemType t1, SemType t2) { 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 index 9291f2381a81..59ba134d7158 100644 --- 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 @@ -22,7 +22,13 @@ import io.ballerina.runtime.internal.types.semtype.PureSemType; import java.util.Arrays; +import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; /** * Runtime representation of SemType. @@ -36,17 +42,28 @@ public abstract sealed class SemType implements BasicTypeBitSet permits BSemType private final SubType[] subTypeData; private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; private Integer hashCode; + private static volatile AtomicInteger nextId = new AtomicInteger(0); + private final Integer typeID = nextId.getAndIncrement(); + private final Map cachedSubTypeRelations; + private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); + private static final int MAX_CACHE_LIMIT = 1000; + private final boolean useCache; protected SemType(int all, int some, SubType[] subTypeData) { this.all = all; this.some = some; this.subTypeData = subTypeData; + if ((some & CACHEABLE_TYPE_MASK) != 0) { + useCache = true; + this.cachedSubTypeRelations = new ConcurrentHashMap<>(); + } else { + useCache = false; + this.cachedSubTypeRelations = null; + } } protected SemType(int all) { - this.all = all; - this.some = 0; - this.subTypeData = EMPTY_SUBTYPE_DATA; + this(all, 0, EMPTY_SUBTYPE_DATA); } protected SemType(SemType semType) { @@ -106,4 +123,23 @@ public int hashCode() { private int computeHashCode() { return Objects.hash(all, some, Arrays.hashCode(subTypeData)); } + + Optional cachedSubTypeRelation(SemType other) { + if (!useCache) { + return Optional.empty(); + } + if (other.typeID.equals(this.typeID)) { + return Optional.of(true); + } + return Optional.ofNullable(cachedSubTypeRelations.get(other.typeID)); + } + + void cacheSubTypeRelation(SemType other, boolean result) { + if (useCache) { + if (cachedSubTypeRelations.size() > MAX_CACHE_LIMIT) { + cachedSubTypeRelations.clear(); + } + cachedSubTypeRelations.put(other.typeID, result); + } + } } 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 index 2edc97277938..cbc915ccf3f0 100644 --- 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 @@ -20,6 +20,10 @@ 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/internal/types/semtype/BListSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java index fc108041a7cb..79168201e109 100644 --- 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 @@ -347,7 +347,7 @@ public static Pair listIntersectWith(Env env, FixedLe initial[i] = intersectMemberSemTypes(env, listMemberAt(members1, rest1, i), listMemberAt(members2, rest2, i)); } - return Pair.from(new FixedLengthArray(initial, + return Pair.from(FixedLengthArray.from(initial, Integer.max(members1.fixedLength(), members2.fixedLength())), intersectMemberSemTypes(env, rest1, rest2)); } 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 index 0ce808f7d823..9d08326ffa82 100644 --- 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 @@ -23,14 +23,24 @@ import java.util.Arrays; import java.util.Objects; -public record FixedLengthArray(SemType[] initial, int fixedLength) { +public final class FixedLengthArray { - public FixedLengthArray { + 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; + } + + static FixedLengthArray from(SemType[] initial, int fixedLength) { + return new FixedLengthArray(initial, fixedLength); } private static final FixedLengthArray EMPTY = new FixedLengthArray(new SemType[0], 0); @@ -80,6 +90,27 @@ public boolean equals(Object o) { @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; + } } From 11eca87175c3e3dfb91f568c64d55689ed6feeda Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 10 Jun 2024 10:45:53 +0530 Subject: [PATCH 036/178] Reduce the cost of Objects.hash --- .../io/ballerina/runtime/api/types/semtype/BddNode.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 index acb47be97b01..6f4770a9ae3b 100644 --- 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 @@ -18,8 +18,6 @@ package io.ballerina.runtime.api.types.semtype; -import java.util.Objects; - /** * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. * @@ -88,7 +86,11 @@ public int hashCode() { } private int computeHashCode() { - return Objects.hash(atom, left, middle, right); + int result = atom.hashCode(); + result = 31 * result + left.hashCode(); + result = 31 * result + middle.hashCode(); + result = 31 * result + right.hashCode(); + return result; } boolean isSimple() { From 5370798877ff5a2ce02f48983e34bcdd381b214e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 10 Jun 2024 11:07:27 +0530 Subject: [PATCH 037/178] Reduce type check cache overhead --- .../runtime/api/types/semtype/Core.java | 6 +- .../runtime/api/types/semtype/SemType.java | 57 ++++++++++++++----- 2 files changed, 45 insertions(+), 18 deletions(-) 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 index 77dd904aa3c6..95c20acfe296 100644 --- 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 @@ -265,9 +265,9 @@ public static boolean isNever(SemType t) { public static boolean isSubType(Context cx, SemType t1, SemType t2) { // IF t1 and t2 are not pure semtypes calling this is an undefined - Optional cached = t1.cachedSubTypeRelation(t2); - if (cached.isPresent()) { - return cached.get(); + SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); + if (cached != SemType.CachedResult.NOT_FOUND) { + return cached == SemType.CachedResult.TRUE; } boolean result = isEmpty(cx, diff(t1, t2)); t1.cacheSubTypeRelation(t2, result); 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 index 59ba134d7158..66802bf2be7e 100644 --- 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 @@ -22,10 +22,9 @@ import io.ballerina.runtime.internal.types.semtype.PureSemType; import java.util.Arrays; +import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; @@ -42,11 +41,10 @@ public abstract sealed class SemType implements BasicTypeBitSet permits BSemType private final SubType[] subTypeData; private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; private Integer hashCode; - private static volatile AtomicInteger nextId = new AtomicInteger(0); + private static volatile AtomicInteger nextId = new AtomicInteger(1); private final Integer typeID = nextId.getAndIncrement(); - private final Map cachedSubTypeRelations; private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); - private static final int MAX_CACHE_LIMIT = 1000; + private final TypeCheckResultCache resultCache; private final boolean useCache; protected SemType(int all, int some, SubType[] subTypeData) { @@ -55,10 +53,10 @@ protected SemType(int all, int some, SubType[] subTypeData) { this.subTypeData = subTypeData; if ((some & CACHEABLE_TYPE_MASK) != 0) { useCache = true; - this.cachedSubTypeRelations = new ConcurrentHashMap<>(); + this.resultCache = new TypeCheckResultCache(); } else { useCache = false; - this.cachedSubTypeRelations = null; + this.resultCache = null; } } @@ -124,22 +122,51 @@ private int computeHashCode() { return Objects.hash(all, some, Arrays.hashCode(subTypeData)); } - Optional cachedSubTypeRelation(SemType other) { + enum CachedResult { + TRUE, + FALSE, + NOT_FOUND + } + + CachedResult cachedSubTypeRelation(SemType other) { if (!useCache) { - return Optional.empty(); + return CachedResult.NOT_FOUND; } - if (other.typeID.equals(this.typeID)) { - return Optional.of(true); + int tid = other.typeID; + if (tid == typeID) { + return CachedResult.TRUE; } - return Optional.ofNullable(cachedSubTypeRelations.get(other.typeID)); + return resultCache.getCachedResult(tid); } void cacheSubTypeRelation(SemType other, boolean result) { if (useCache) { - if (cachedSubTypeRelations.size() > MAX_CACHE_LIMIT) { - cachedSubTypeRelations.clear(); + resultCache.cacheResult(other.typeID, result); + + CachedResult cachedResult = cachedSubTypeRelation(other); + if (cachedResult != CachedResult.NOT_FOUND && + cachedResult != (result ? CachedResult.TRUE : CachedResult.FALSE)) { + throw new IllegalStateException("Inconsistent cache state"); + } + } + } + + private static final class TypeCheckResultCache { + + private static final int CACHE_LIMIT = 100; + // See if we can use an identity hashmap on semtypes instead of tid + private Map cache = new HashMap<>(); + + private void cacheResult(int tid, boolean result) { + cache.put((long) tid, result); + } + + private CachedResult getCachedResult(int tid) { + Boolean cachedData = cache.get((long) tid); + if (cachedData == null) { + return CachedResult.NOT_FOUND; } - cachedSubTypeRelations.put(other.typeID, result); + return cachedData ? CachedResult.TRUE : CachedResult.FALSE; } } } From 9803e97354a2f697caf52a7807b363ca16f8cc9c Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 11 Jun 2024 10:09:07 +0530 Subject: [PATCH 038/178] Implement MappingDefinition --- .../runtime/api/types/semtype/Builder.java | 50 ++++++-- .../api/types/semtype/CellAtomicType.java | 13 --- .../runtime/api/types/semtype/Core.java | 13 +-- .../runtime/api/types/semtype/Env.java | 45 ++++++- .../api/types/semtype/ListAtomicType.java | 8 -- .../api/types/semtype/MappingAtomicType.java | 29 +++++ .../internal/types/semtype/BListSubType.java | 2 +- .../internal/types/semtype/Definition.java | 1 + .../types/semtype/MappingDefinition.java | 110 ++++++++++++++++++ .../port/test/RuntimeSemTypeResolver.java | 28 +++++ .../test-src/type-rel/mapping-basic-tv.bal | 3 + 11 files changed, 263 insertions(+), 39 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal 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 index 124cc1fe81bc..936dce886bac 100644 --- 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 @@ -29,6 +29,7 @@ import io.ballerina.runtime.internal.types.semtype.BIntSubType; import io.ballerina.runtime.internal.types.semtype.BListSubType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.values.DecimalValue; @@ -39,6 +40,7 @@ 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.CODE_B_TYPE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; @@ -54,27 +56,57 @@ */ public final class Builder { + static final CellAtomicType CELL_ATOMIC_VAL = new CellAtomicType( + valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + public static final SemType MAPPING = from(BT_MAPPING); + static final TypeAtom ATOM_CELL_VAL = createTypeAtom(0, CELL_ATOMIC_VAL); + static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( + neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); private static final String[] EMPTY_STRING_ARR = new String[0]; private static final SemType NEVER = SemType.from(0); private static final SemType VAL = SemType.from(VT_MASK); private static final SemType UNDEF = from(BasicTypeCode.BT_UNDEF); - private static final SemType INNER = basicTypeUnion(valType().all() | undef().all); - - static final SemType CELL_SEMTYPE_INNER = basicSubType(BT_CELL, - BCellSubType.createDelegate(bddAtom(createTypeAtom(2, CellAtomicType.CELL_ATOMIC_INNER)))); + private static final SemType INNER = basicTypeUnion(VAL.all | UNDEF.all); public static final int BDD_REC_ATOM_READONLY = 0; // represents both readonly & map and readonly & readonly[] private static final BddNode BDD_SUBTYPE_RO = bddAtom(RecAtom.createRecAtom(BDD_REC_ATOM_READONLY)); + // FIXME: create delegate? + public static final SemType MAPPING_RO = basicSubType(BT_MAPPING, BDD_SUBTYPE_RO); + public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING_RO = + new CellAtomicType(union(MAPPING_RO, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + static final CellAtomicType CELL_ATOMIC_INNER = new CellAtomicType( + inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + static final SemType CELL_SEMTYPE_INNER = basicSubType(BT_CELL, + BCellSubType.createDelegate(bddAtom(createTypeAtom(2, CELL_ATOMIC_INNER)))); + public static final ListAtomicType LIST_ATOMIC_INNER = new ListAtomicType( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER); public static final SemType VAL_READONLY = Core.union(SemType.from(VT_INHERENTLY_IMMUTABLE), basicSubType(BT_LIST, BListSubType.createDelegate(BDD_SUBTYPE_RO))); - private static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); - private static final CellAtomicType CELL_ATOMIC_INNER_RO = new CellAtomicType( - INNER_READONLY, CELL_MUT_NONE); + public static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); + public static final CellAtomicType CELL_ATOMIC_INNER_RO + = new CellAtomicType(INNER_READONLY, CELL_MUT_NONE); + public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING + = new CellAtomicType(union(MAPPING, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + static final SemType CELL_SEMTYPE_INNER_MAPPING = basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(createTypeAtom(3, CELL_ATOMIC_INNER_MAPPING)))); + public static final ListAtomicType LIST_ATOMIC_MAPPING = + new ListAtomicType(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING); + + static final SemType CELL_SEMTYPE_INNER_MAPPING_RO = basicSubType( + BT_CELL, + BCellSubType.createDelegate(bddAtom(createTypeAtom(5, CELL_ATOMIC_INNER_MAPPING_RO)))); + public static final ListAtomicType LIST_ATOMIC_MAPPING_RO = + new ListAtomicType(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING_RO); + private static final TypeAtom ATOM_CELL_INNER_RO = createTypeAtom(7, CELL_ATOMIC_INNER_RO); static final SemType CELL_SEMTYPE_INNER_RO = basicSubType( BT_CELL, BCellSubType.createDelegate(bddAtom(ATOM_CELL_INNER_RO))); + public static final ListAtomicType LIST_ATOMIC_RO = new ListAtomicType( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); private static final Env env = Env.getInstance(); @@ -299,6 +331,10 @@ public static SemType anyType() { return ANY; } + public static SemType mappingType() { + return MAPPING; + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 2174ef7accc7..3762d9154187 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -18,8 +18,6 @@ package io.ballerina.runtime.api.types.semtype; -import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; - /** * CellAtomicType node. * @@ -29,17 +27,6 @@ */ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { - private static final AtomicType CELL_ATOMIC_VAL = new CellAtomicType( - Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED - ); - public static final TypeAtom ATOM_CELL_VAL = createTypeAtom(0, CELL_ATOMIC_VAL); - - public static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( - Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED - ); - public static final CellAtomicType CELL_ATOMIC_INNER = new CellAtomicType( - Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { SemType ty = Core.intersect(c1.ty(), c2.ty()); CellMutability mut = min(c1.mut(), c2.mut()); 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 index 95c20acfe296..6a231ab26af1 100644 --- 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 @@ -35,7 +35,6 @@ import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.listType; import static io.ballerina.runtime.api.types.semtype.Builder.undef; -import static io.ballerina.runtime.api.types.semtype.Builder.valType; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.intersectCellAtomicType; import static io.ballerina.runtime.internal.types.semtype.BCellSubType.cellAtomType; @@ -51,11 +50,6 @@ public final class Core { public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); public static final SemType B_TYPE_TOP = SemType.from(1 << BT_B_TYPE.code()); - // TODO: move to builder - private static final CellAtomicType CELL_ATOMIC_VAL = new CellAtomicType( - valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED - ); - private Core() { } @@ -137,6 +131,7 @@ public static SemType listMemberTypeInnerVal(Context cx, SemType t, SemType k) { } 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(); @@ -179,6 +174,7 @@ public static SemType union(SemType t1, SemType t2) { } 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; @@ -275,6 +271,7 @@ public static boolean isSubType(Context cx, SemType t1, SemType 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; } @@ -346,12 +343,12 @@ public static SemType intersectMemberSemTypes(Env env, SemType t1, SemType t2) { private static Optional cellAtomicType(SemType t) { SemType cell = Builder.cell(); if (t.some == 0) { - return cell.equals(t) ? Optional.of(CELL_ATOMIC_VAL) : Optional.empty(); + return cell.equals(t) ? Optional.of(Builder.CELL_ATOMIC_VAL) : Optional.empty(); } else { if (!isSubtypeSimple(t, cell)) { return Optional.empty(); } - return bddCellAtomicType((Bdd) getComplexSubtypeData(t, BT_CELL), CELL_ATOMIC_VAL); + return bddCellAtomicType((Bdd) getComplexSubtypeData(t, BT_CELL), Builder.CELL_ATOMIC_VAL); } } 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 index d3c7d828b551..7044b25e8bcb 100644 --- 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 @@ -27,7 +27,8 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static io.ballerina.runtime.api.types.semtype.ListAtomicType.LIST_ATOMIC_RO; +import static io.ballerina.runtime.api.types.semtype.Builder.LIST_ATOMIC_RO; +import static io.ballerina.runtime.api.types.semtype.MappingAtomicType.MAPPING_ATOMIC_RO; /** * Represent the environment in which {@code SemTypes} are defined in. Type checking types defined in different @@ -46,12 +47,28 @@ public final class Env { private final ReadWriteLock recListLock = new ReentrantReadWriteLock(); private final List recListAtoms; + private final ReadWriteLock recMapLock = new ReentrantReadWriteLock(); + private final List recMappingAtoms; + private final Map cellTypeCache = new ConcurrentHashMap<>(); private Env() { this.atomTable = new HashMap<>(); this.recListAtoms = new ArrayList<>(); recListAtoms.add(LIST_ATOMIC_RO); + this.recMappingAtoms = new ArrayList<>(); + recMappingAtoms.add(MAPPING_ATOMIC_RO); + + this.cellAtom(Builder.CELL_ATOMIC_VAL); + this.cellAtom(Builder.CELL_ATOMIC_NEVER); + + this.cellAtom(Builder.CELL_ATOMIC_INNER); + this.cellAtom(Builder.CELL_ATOMIC_INNER_MAPPING); + this.listAtom(Builder.LIST_ATOMIC_MAPPING); + this.cellAtom(Builder.CELL_ATOMIC_INNER_MAPPING_RO); + this.listAtom(Builder.LIST_ATOMIC_MAPPING_RO); + + this.cellAtom(Builder.CELL_ATOMIC_INNER_RO); } public static Env getInstance() { @@ -98,7 +115,6 @@ void cacheCellType(SemType ty, CellAtomicType.CellMutability mut, SemType semTyp } public RecAtom recListAtom() { - // TODO: do we have seperate read and write operations, if so use rw lock recListLock.writeLock().lock(); try { int result = this.recListAtoms.size(); @@ -129,6 +145,31 @@ public ListAtomicType getRecListAtomType(RecAtom ra) { } } + 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.writeLock().lock(); + try { + this.recMappingAtoms.set(rec.index(), atomicType); + } finally { + recMapLock.writeLock().unlock(); + } + } + + public TypeAtom mappingAtom(MappingAtomicType atomicType) { + return this.typeAtom(atomicType); + } + private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java index 57fd654c0a87..5210389264ed 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java @@ -20,15 +20,7 @@ import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; -import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER; -import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER_RO; - // TODO: move this to internal along with cell atomic type public record ListAtomicType(FixedLengthArray members, SemType rest) implements AtomicType { - public static final ListAtomicType LIST_ATOMIC_INNER = new ListAtomicType( - FixedLengthArray.empty(), CELL_SEMTYPE_INNER); - - public static final ListAtomicType LIST_ATOMIC_RO = new ListAtomicType( - FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java new file mode 100644 index 000000000000..2c1b27343941 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java @@ -0,0 +1,29 @@ +/* + * 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; + +import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER_RO; + +public record MappingAtomicType(String[] names, SemType[] types, SemType rest) implements AtomicType { + + public static final MappingAtomicType MAPPING_ATOMIC_RO = new MappingAtomicType( + new String[]{}, new SemType[]{}, CELL_SEMTYPE_INNER_RO + ); +} 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 index 79168201e109..85ad02238399 100644 --- 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 @@ -43,7 +43,7 @@ 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.intersectMemberSemTypes; -import static io.ballerina.runtime.api.types.semtype.ListAtomicType.LIST_ATOMIC_INNER; +import static io.ballerina.runtime.api.types.semtype.Builder.LIST_ATOMIC_INNER; import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; // TODO: this has lot of common code with cell (and future mapping), consider refact diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java index 7ae93ad1e01b..0e144cc1fab9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; +// NOTE: definitions are not thread safe public interface Definition { SemType getSemType(Env env); 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..a4440c9b018c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.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.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.MappingAtomicType; +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.cellContaining; +import static io.ballerina.runtime.api.types.semtype.Builder.undef; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +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; + } + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = bddAtom(atom); + // FIXME: create delegate + this.semType = basicSubType(BasicTypeCode.BT_MAPPING, bdd); + return this.semType; + } + + public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, CellAtomicType.CellMutability mut) { + BCellField[] cellFields = new BCellField[fields.length]; + for (Field field : fields) { + SemType type = field.ty; + SemType cellType = cellContaining(env, field.optional ? union(type, undef()) : type, + field.readonly ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); + cellFields[0] = new BCellField(field.name, cellType); + } + SemType restCell = cellContaining(env, union(rest, undef()), + isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); + return define(env, cellFields, restCell); + } + + private 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) { + + } +} 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 index dc9d2a5abe6c..cb6f439d8f00 100644 --- 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 @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.semtype.Definition; import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.tree.BLangNode; @@ -32,6 +33,7 @@ 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.BLangFiniteTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; @@ -114,10 +116,36 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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); default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); }; } + 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); + default -> throw new UnsupportedOperationException( + "Constrained type not implemented: " + refTypeNode.typeKind); + }; + } + + 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(); 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 From b76a6b8cc0f774790e42f50b3fcff2cc982b326d Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 11 Jun 2024 10:52:05 +0530 Subject: [PATCH 039/178] Implement mapping subtype empty --- .../runtime/api/types/semtype/Builder.java | 8 +- .../runtime/api/types/semtype/Context.java | 9 + .../runtime/api/types/semtype/Env.java | 9 + .../internal/types/semtype/BListSubType.java | 2 +- .../types/semtype/BMappingSubType.java | 404 ++++++++++++++++++ .../types/semtype/MappingDefinition.java | 2 +- 6 files changed, 430 insertions(+), 4 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java 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 index 936dce886bac..3dd8b956f018 100644 --- 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 @@ -28,6 +28,7 @@ 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.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; @@ -73,8 +74,7 @@ public final class Builder { public static final int BDD_REC_ATOM_READONLY = 0; // represents both readonly & map and readonly & readonly[] private static final BddNode BDD_SUBTYPE_RO = bddAtom(RecAtom.createRecAtom(BDD_REC_ATOM_READONLY)); - // FIXME: create delegate? - public static final SemType MAPPING_RO = basicSubType(BT_MAPPING, BDD_SUBTYPE_RO); + public static final SemType MAPPING_RO = basicSubType(BT_MAPPING, BMappingSubType.createDelegate(BDD_SUBTYPE_RO)); public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING_RO = new CellAtomicType(union(MAPPING_RO, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); @@ -108,6 +108,9 @@ public final class Builder { public static final ListAtomicType LIST_ATOMIC_RO = new ListAtomicType( FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); + public static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; + public static final MappingAtomicType MAPPING_ATOMIC_INNER = new MappingAtomicType( + EMPTY_STRING_ARR, EMPTY_TYPES_ARR, CELL_SEMTYPE_INNER); private static final Env env = Env.getInstance(); @@ -211,6 +214,7 @@ public static SemType basicTypeUnion(int bitset) { } public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { + assert !(subType instanceof Bdd) : "BDD should always be wrapped with a delegate"; SubType[] subTypes = initializeSubtypeArray(); subTypes[basicTypeCode.code()] = subType; return SemType.from(0, 1 << basicTypeCode.code(), subTypes); 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 index 4c8366065390..ff8b414008f9 100644 --- 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 @@ -36,6 +36,7 @@ public final class Context { private final List memoStack = new ArrayList<>(); public final Env env; public final Map listMemo = new HashMap<>(); + public final Map mappingMemo = new HashMap<>(); private Context(Env env) { this.env = env; @@ -106,4 +107,12 @@ public ListAtomicType listAtomType(Atom atom) { 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(); + } + } } 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 index 7044b25e8bcb..fc681a2f9bc2 100644 --- 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 @@ -170,6 +170,15 @@ 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(); + } + } + private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { } 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 index 85ad02238399..fad698f6db94 100644 --- 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 @@ -46,7 +46,7 @@ import static io.ballerina.runtime.api.types.semtype.Builder.LIST_ATOMIC_INNER; import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; -// TODO: this has lot of common code with cell (and future mapping), consider refact +// TODO: this has lot of common code with cell (and future mapping), consider refactoring (problem is createDelegate) public class BListSubType extends SubType implements DelegatedSubType { public final Bdd inner; 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..ee5403f56e48 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java @@ -0,0 +1,404 @@ +/* + * 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.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.MappingAtomicType; +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.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; +import static io.ballerina.runtime.api.types.semtype.Builder.MAPPING_ATOMIC_INNER; +import static io.ballerina.runtime.api.types.semtype.Core.cellInner; +import static io.ballerina.runtime.api.types.semtype.Core.intersectMemberSemTypes; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; + +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"); + } + + // FIXME: not sure this is needed (java string comparision should support unicode) + 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; + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BMappingSubType otherList)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherList.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BMappingSubType 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.mappingMemo, + (context, bdd) -> bddEvery(context, bdd, null, null, BMappingSubType::mappingFormulaIsEmpty), inner); + } + + private 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 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(); + 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 || 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); + } + + // FIXME: make this an instance method in mappingAtomicType + private static MappingAtomicType intersectMapping(Env env, MappingAtomicType m1, MappingAtomicType m2) { + int expectedSize = Integer.min(m1.types().length, m2.types().length); + List names = new ArrayList<>(expectedSize); + List types = new ArrayList<>(expectedSize); + FieldPairs pairing = new FieldPairs(m1, m2); + for (FieldPair fieldPair : pairing) { + names.add(fieldPair.name()); + SemType t = intersectMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + if (isNever(cellInner(fieldPair.type1()))) { + return null; + } + types.add(t); + } + SemType rest = intersectMemberSemTypes(env, m1.rest(), m2.rest()); + return new MappingAtomicType(names.toArray(String[]::new), types.toArray(SemType[]::new), rest); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + @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); + } + + private static class FieldPairs implements Iterable { + + MappingAtomicType m1; + MappingAtomicType m2; + public MappingPairIterator itr; + + public FieldPairs(MappingAtomicType m1, MappingAtomicType m2) { + this.m1 = m1; + this.m2 = m2; + } + + @Override + public Iterator iterator() { + itr = new MappingPairIterator(m1, m2); + return itr; + } + } + + private record FieldPair(String name, SemType type1, SemType type2, Integer index1, Integer index2) { + + public static FieldPair create(String name, SemType type1, SemType type2, Integer index1, + Integer index2) { + return new FieldPair(name, type1, type2, index1, index2); + } + } + + // TODO: refact + private static 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 = 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 (codePointCompare(name1, name2)) { + p = FieldPair.create(name1, curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else if (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 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/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 index a4440c9b018c..9ab5218d9339 100644 --- 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 @@ -58,7 +58,7 @@ public SemType getSemType(Env env) { private SemType createSemType(Env env, Atom atom) { BddNode bdd = bddAtom(atom); // FIXME: create delegate - this.semType = basicSubType(BasicTypeCode.BT_MAPPING, bdd); + this.semType = basicSubType(BasicTypeCode.BT_MAPPING, BMappingSubType.createDelegate(bdd)); return this.semType; } From d5f541ef2589bb7d4925e872e78a3f4892f08fc4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 11 Jun 2024 11:20:24 +0530 Subject: [PATCH 040/178] Enable semtype tests expect projections for mappings --- .../runtime/api/types/semtype/Builder.java | 37 +++++++++++++++++ .../runtime/api/types/semtype/Context.java | 1 + .../types/semtype/MappingDefinition.java | 6 ++- .../port/test/RuntimeSemTypeResolver.java | 41 +++++++++++++++++++ .../semtype/port/test/SemTypeTest.java | 11 +---- 5 files changed, 84 insertions(+), 12 deletions(-) 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 index 3dd8b956f018..e48968cf297b 100644 --- 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 @@ -32,6 +32,7 @@ import io.ballerina.runtime.internal.types.semtype.BStringSubType; import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; @@ -46,6 +47,7 @@ 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.CellAtomicType.CellMutability.CELL_MUT_LIMITED; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; import static io.ballerina.runtime.api.types.semtype.Core.union; import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; @@ -112,6 +114,13 @@ public final class Builder { public static final MappingAtomicType MAPPING_ATOMIC_INNER = new MappingAtomicType( EMPTY_STRING_ARR, EMPTY_TYPES_ARR, CELL_SEMTYPE_INNER); + public static final SemType 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())); private static final Env env = Env.getInstance(); private Builder() { @@ -339,6 +348,34 @@ public static SemType mappingType() { return MAPPING; } + public static SemType anyDataType(Context context) { + SemType memo = context.anydataMemo; + if (memo != null) { + return memo; + } + Env env = context.env; + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + // TODO: add table, xml + SemType accum = unionOf(SIMPLE_OR_STRING, listDef.getSemType(env), mapDef.getSemType(env)); + listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); + mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); + context.anydataMemo = accum; + return accum; + } + + private static SemType unionOf(SemType type1, SemType type2) { + return union(type1, type2); + } + + 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 final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; 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 index ff8b414008f9..0486c8d97932 100644 --- 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 @@ -38,6 +38,7 @@ public final class Context { public final Map listMemo = new HashMap<>(); public final Map mappingMemo = new HashMap<>(); + SemType anydataMemo; private Context(Env env) { this.env = env; } 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 index 9ab5218d9339..b2ff01fe88c3 100644 --- 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 @@ -63,12 +63,14 @@ private SemType createSemType(Env env, Atom atom) { } public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, CellAtomicType.CellMutability mut) { + assert rest != null; BCellField[] cellFields = new BCellField[fields.length]; - for (Field field : fields) { + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; SemType type = field.ty; SemType cellType = cellContaining(env, field.optional ? union(type, undef()) : type, field.readonly ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); - cellFields[0] = new BCellField(field.name, cellType); + cellFields[i] = new BCellField(field.name, cellType); } SemType restCell = cellContaining(env, union(rest, undef()), isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); 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 index cb6f439d8f00..3d5874997867 100644 --- 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 @@ -19,15 +19,18 @@ 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.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.semtype.Definition; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; @@ -36,6 +39,7 @@ import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangType; import org.wso2.ballerinalang.compiler.tree.types.BLangUnionTypeNode; @@ -117,10 +121,46 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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); default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); }; } + 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.anyDataType((Context) cx.getInnerContext()); + } else { + rest = resolveTypeDesc(cx, mod, defn, depth + 1, td.getRestFieldType()); + } + if (rest == null) { + rest = Builder.neverType(); + } + 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(); @@ -141,6 +181,7 @@ private SemType resolveMapTypeDesc(TypeTestContext cx, Map mappingFilter = createRuntimeFileNameFilter(Set.of( - "mapping1-t.bal", - "mapping2-t.bal", - "mapping3-t.bal", - "mapping-record-tv.bal", - "mapping-t.bal", - "mappingIntersect-tv.bal", - "mutable-record-t.bal", "optional-field-record1-t.bal", "optional-field-record2-t.bal", "optional-field-record3-t.bal", @@ -258,9 +251,7 @@ public Object[] runtimeFileNameProviderFunc() { "recursive-record-t.bal", "test_test.bal", "proj1-tv.bal", - "proj3-tv.bal", - "tuple1-tv.bal", - "tuple3-tv.bal" + "proj3-tv.bal" )); Predicate xmlFilter = createRuntimeFileNameFilter(Set.of( "xml-complex-ro-tv.bal", From 2b083d8f6fb8154acb2fc494cb2ae85a44336e28 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 11 Jun 2024 15:39:40 +0530 Subject: [PATCH 041/178] Fix illegal modification of mapping atomic type --- .../runtime/internal/types/semtype/BMappingSubType.java | 2 +- .../runtime/internal/types/semtype/MappingDefinition.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) 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 index ee5403f56e48..467b3aae1704 100644 --- 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 @@ -167,7 +167,7 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju // the posType came from the rest type mt = insertField(pos, fieldPair.name(), d); } else { - SemType[] posTypes = pos.types(); + SemType[] posTypes = pos.types().clone(); posTypes[fieldPair.index1()] = d; mt = new MappingAtomicType(pos.names(), posTypes, pos.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 index b2ff01fe88c3..2a83d7376e33 100644 --- 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 @@ -57,7 +57,6 @@ public SemType getSemType(Env env) { private SemType createSemType(Env env, Atom atom) { BddNode bdd = bddAtom(atom); - // FIXME: create delegate this.semType = basicSubType(BasicTypeCode.BT_MAPPING, BMappingSubType.createDelegate(bdd)); return this.semType; } From f44704ff6feea77c5df36f0681b03ddce9c136b8 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 11 Jun 2024 15:40:01 +0530 Subject: [PATCH 042/178] Enable more tests --- .../io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java | 1 + .../test/java/io/ballerina/semtype/port/test/SemTypeTest.java | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) 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 index 3d5874997867..a050f904a5a6 100644 --- 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 @@ -364,6 +364,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) case STRING -> Builder.stringType(); case READONLY -> Builder.readonlyType(); case ANY -> Builder.anyType(); + case ANYDATA -> Builder.anyDataType((Context) cx.getInnerContext()); default -> throw new IllegalStateException("Unknown type: " + td); }; } 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 index 19bb129167a0..b71a57e20973 100644 --- 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 @@ -243,9 +243,6 @@ public Object[] runtimeFileNameProviderFunc() { "func-quals-tv.bal" )); Predicate mappingFilter = createRuntimeFileNameFilter(Set.of( - "optional-field-record1-t.bal", - "optional-field-record2-t.bal", - "optional-field-record3-t.bal", "record-proj-tv.bal", "record-t.bal", "recursive-record-t.bal", From 1ea5a0d5c85c0b904a81457e948f6dad9d415fb9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 11 Jun 2024 16:15:28 +0530 Subject: [PATCH 043/178] Implement mapping type projection WIP: Make Mapping BTypes use semtypes --- .../runtime/api/types/semtype/Builder.java | 7 +- .../runtime/api/types/semtype/Core.java | 6 + .../api/types/semtype/MappingProj.java | 33 +++++ .../runtime/internal/types/BArrayType.java | 6 - .../runtime/internal/types/BMapType.java | 31 +++++ .../runtime/internal/types/BRecordType.java | 43 ++++++- .../internal/types/BStructureType.java | 1 + .../internal/types/BTypeConverter.java | 19 +-- .../internal/types/semtype/BMappingProj.java | 120 ++++++++++++++++++ .../types/semtype/BMappingSubType.java | 32 +---- .../types/semtype/BStringSubType.java | 78 +++++++++++- .../internal/types/semtype/Common.java | 50 ++++++++ .../internal/types/semtype/PureSemType.java | 5 + .../runtime/internal/values/MapValue.java | 6 + .../semtype/port/test/RuntimeTypeTestAPI.java | 7 +- .../semtype/port/test/SemTypeTest.java | 9 -- 16 files changed, 389 insertions(+), 64 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java 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 index e48968cf297b..c003701545ed 100644 --- 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 @@ -86,8 +86,10 @@ public final class Builder { BCellSubType.createDelegate(bddAtom(createTypeAtom(2, CELL_ATOMIC_INNER)))); public static final ListAtomicType LIST_ATOMIC_INNER = new ListAtomicType( FixedLengthArray.empty(), CELL_SEMTYPE_INNER); - public static final SemType VAL_READONLY = Core.union(SemType.from(VT_INHERENTLY_IMMUTABLE), - basicSubType(BT_LIST, BListSubType.createDelegate(BDD_SUBTYPE_RO))); + private static final SemType VAL_READONLY = unionOf(SemType.from(VT_INHERENTLY_IMMUTABLE), + basicSubType(BT_LIST, BListSubType.createDelegate(BDD_SUBTYPE_RO)), + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(BDD_SUBTYPE_RO)) + ); public static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); public static final CellAtomicType CELL_ATOMIC_INNER_RO = new CellAtomicType(INNER_READONLY, CELL_MUT_NONE); @@ -277,6 +279,7 @@ static SubType[] initializeSubtypeArray() { } public static Optional typeOf(Object object) { + // FIXME: handle mapping values here if (object == null) { return Optional.of(nilType()); } else if (object instanceof DecimalValue decimalValue) { 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 index 6a231ab26af1..39db9ab3ef28 100644 --- 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 @@ -30,6 +30,7 @@ import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; 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_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; @@ -285,6 +286,11 @@ public static SubTypeData intSubtype(SemType t) { return subTypeData(t, BT_INT); } + // 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); + } + public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { if ((s.all & (1 << code.code())) != 0) { return AllOrNothing.ALL; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java new file mode 100644 index 000000000000..7ed7e47bed96 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -0,0 +1,33 @@ +/* + * 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; + +import io.ballerina.runtime.internal.types.semtype.BMappingProj; + +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/internal/types/BArrayType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java index b99ff54c8dc8..9d718cb84ba9 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 @@ -224,12 +224,6 @@ SemType createSemType() { } defn = new ListDefinition(); SemType elementType = Builder.from(getElementType()); -// if (Core.isSubtypeSimple(elementType, Core.B_TYPE_TOP)) { -// SemType semTypePart = defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, Builder.neverType(), -// CellAtomicType.CellMutability.CELL_MUT_NONE); -// SemType bTypePart = BTypeConverter.wrapAsPureBType(this); -// return Core.union(semTypePart, bTypePart); -// } SemType pureBTypePart = Core.intersect(elementType, Core.B_TYPE_TOP); if (!Core.isNever(pureBTypePart)) { SemType pureSemTypePart = Core.intersect(elementType, Core.SEMTYPE_TOP); 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 c69aaa56c144..075ee33cfb80 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,7 +24,13 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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.values.BString; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -43,10 +49,13 @@ @SuppressWarnings("unchecked") public class BMapType extends BType implements MapType { + 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 MappingDefinition defn; + private final Env env = Env.getInstance(); public BMapType(Type constraint) { this(constraint, false); @@ -169,4 +178,26 @@ public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + @Override + SemType createSemType() { + if (defn != null) { + return defn.getSemType(env); + } + defn = new MappingDefinition(); + SemType restType = Builder.from(getConstrainedType()); + SemType pureBTypePart = Core.intersect(restType, Core.B_TYPE_TOP); + if (!Core.isNever(pureBTypePart)) { + SemType pureSemTypePart = Core.intersect(restType, Core.SEMTYPE_TOP); + SemType semTypePart = getSemTypePart(pureSemTypePart); + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + return Core.union(semTypePart, bTypePart); + } + return getSemTypePart(restType); + } + + private SemType getSemTypePart(SemType restType) { + CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + return defn.defineMappingTypeWrapped(env, EMPTY_FIELD_ARR, restType, mut); + } } 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 73f7c2018ed8..2c7f52052dd3 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 @@ -28,6 +28,10 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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.utils.StringUtils; import io.ballerina.runtime.api.values.BFunctionPointer; @@ -35,13 +39,16 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.ValueUtils; import io.ballerina.runtime.internal.scheduling.Scheduler; +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.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -58,6 +65,8 @@ public class BRecordType extends BStructureType implements RecordType { private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private MappingDefinition defn; + private final Env env = Env.getInstance(); private final Map> defaultValues = new LinkedHashMap<>(); @@ -224,6 +233,38 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul @Override SemType createSemType() { - return BTypeConverter.fromRecordType(this); + if (defn != null) { + return defn.getSemType(env); + } + defn = new MappingDefinition(); + Field[] fields = getFields().values().toArray(Field[]::new); + MappingDefinition.Field[] mappingFields = new MappingDefinition.Field[fields.length]; + boolean hasBTypePart = false; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + SemType fieldType = Builder.from(field.getFieldType()); + if (Core.isNever(fieldType)) { + return Builder.neverType(); + } else if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { + hasBTypePart = true; + fieldType = Core.intersect(fieldType, Core.SEMTYPE_TOP); + } + mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY), isOptional); + } + CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType rest = restFieldType != null ? Builder.from(restFieldType) : Builder.neverType(); + if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { + hasBTypePart = true; + rest = Core.intersect(rest, Core.SEMTYPE_TOP); + } + if (hasBTypePart) { + SemType semTypePart = defn.defineMappingTypeWrapped(env, mappingFields, rest, mut); + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + return Core.union(semTypePart, bTypePart); + } + return defn.defineMappingTypeWrapped(env, mappingFields, rest, mut); } } 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 3b5575b8a353..87d6ba82d1c6 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 @@ -20,6 +20,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.StructureType; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.HashMap; import java.util.Map; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 46fcd22f3622..4078c1526ff5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -48,7 +48,8 @@ private BTypeConverter() { private static final SemType implementedTypes = unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), - Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType()); + Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType(), + Builder.mappingType()); private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); @@ -87,18 +88,6 @@ static SemType fromAnyType(BAnyType anyType) { return Core.union(ANY_SEMTYPE_PART, bTypePart); } - static SemType fromRecordType(BRecordType recordType) { - for (Field field : recordType.fields.values()) { - if (!SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL)) { - SemType fieldType = from(field.getFieldType()); - if (Core.isNever(fieldType)) { - return Builder.neverType(); - } - } - } - return wrapAsPureBType(recordType); - } - static SemType fromFiniteType(BFiniteType finiteType) { BTypeParts parts = splitFiniteType(finiteType); if (parts.bTypeParts().isEmpty()) { @@ -137,7 +126,9 @@ private static BTypeParts split(Type type) { return splitReadonly(readonlyType); } else if (type instanceof BFiniteType finiteType) { return splitFiniteType(finiteType); - } else if (type instanceof BArrayType || type instanceof BTupleType) { + // FIXME: introduce a marker type for these + } else if (type instanceof BArrayType || type instanceof BTupleType || type instanceof BRecordType || + type instanceof BMapType) { return splitSemTypeSupplier((Supplier) type); } else { return new BTypeParts(Builder.neverType(), List.of(type)); 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..4e884eeb8a1a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java @@ -0,0 +1,120 @@ +/* + * 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.MappingAtomicType; +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; +import static io.ballerina.runtime.internal.types.semtype.BStringSubType.stringSubtypeListCoverage; + +public final class BMappingProj { + + private BMappingProj() { + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), Builder.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. + static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return (t.all() & Builder.mappingType().all()) != 0 ? Builder.valType() : Builder.undef(); + } else { + SubTypeData keyData = stringSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.undef(); + } + return bddMappingMemberTypeInner(cx, (Bdd) getComplexSubtypeData(t, BT_MAPPING), keyData, + Builder.inner()); + } + } + + static SemType bddMappingMemberTypeInner(Context cx, Bdd b, SubTypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : Builder.neverType(); + } 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.undef() : 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 = + stringSubtypeListCoverage((BStringSubType.StringSubTypeData) key, + 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 index 467b3aae1704..88b333a11220 100644 --- 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 @@ -62,30 +62,6 @@ public static BMappingSubType createDelegate(SubType inner) { throw new IllegalArgumentException("Unexpected inner type"); } - // FIXME: not sure this is needed (java string comparision should support unicode) - 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; - } - @Override public Bdd inner() { return inner; @@ -155,10 +131,10 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju } 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()); } + FieldPairs pairing = new FieldPairs(pos, neg); for (FieldPair fieldPair : pairing) { SemType d = Core.diff(fieldPair.type1(), fieldPair.type2()); if (!Core.isEmpty(cx, d)) { @@ -187,7 +163,7 @@ private static MappingAtomicType insertField(MappingAtomicType m, String name, S SemType[] types = shallowCopySemTypes(orgTypes, orgTypes.length + 1); int i = orgNames.length; while (true) { - if (i == 0 || codePointCompare(names[i - 1], name)) { + if (i == 0 || Common.codePointCompare(names[i - 1], name)) { names[i] = name; types[i] = t; break; @@ -360,10 +336,10 @@ private FieldPair internalNext() { } else { String name1 = curName1(); String name2 = curName2(); - if (codePointCompare(name1, name2)) { + if (Common.codePointCompare(name1, name2)) { p = FieldPair.create(name1, curType1(), this.rest2, this.i1, null); this.i1 += 1; - } else if (codePointCompare(name2, name1)) { + } else if (Common.codePointCompare(name2, name1)) { p = FieldPair.create(name2, this.rest1, curType2(), null, this.i2); this.i2 += 1; } else { 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 index a562c82ccd5e..ef8aab8dfdb6 100644 --- 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 @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * Runtime representation of StringSubType. @@ -163,7 +164,82 @@ public SubTypeData data() { return data; } - private record StringSubTypeData(ValueData chars, ValueData nonChars) implements SubTypeData { + // FIXME: make this an instance method + // Returns a description of the relationship between a StringSubtype and a list of strings + // `values` must be ordered. + static StringSubtypeListCoverage stringSubtypeListCoverage(StringSubTypeData stringData, String[] values) { + List indices = new ArrayList<>(); + ValueData ch = stringData.chars(); + ValueData nonChar = stringData.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); + } + + 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 { + + } + + record StringSubtypeListCoverage(boolean isSubType, int[] indices) { } 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..7917a60eb69b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java @@ -0,0 +1,50 @@ +/* + * 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; + +public final class Common { + + private Common() { + } + + // FIXME: not sure this is needed (java string comparision should support unicode) + 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/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index ec878f4bb183..7fe00d5fd286 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -28,11 +28,16 @@ */ public final class PureSemType extends SemType { + private final int index; + private static int nextIndex; + public PureSemType(int all, int some, SubType[] subTypeData) { super(all, some, subTypeData); + index = nextIndex++; } public PureSemType(int all) { super(all); + index = nextIndex++; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java index 262173eedb23..0c8bb4ae2273 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.internal.values; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BMap; /** @@ -34,4 +36,8 @@ */ public interface MapValue extends RefValue, CollectionValue, BMap { + @Override + default SemType basicType() { + return Builder.mappingType(); + } } 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 index 794ec0c342ee..cc11cc8e0161 100644 --- 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 @@ -22,6 +22,7 @@ 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 { @@ -56,7 +57,7 @@ public boolean isListType(SemType t) { @Override public boolean isMapType(SemType t) { - throw new IllegalArgumentException("map type not implemented"); + return Core.isSubtypeSimple(t, Builder.mappingType()); } @Override @@ -65,8 +66,8 @@ public SemType intConst(long l) { } @Override - public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType semType, SemType m) { - throw new IllegalArgumentException("mapping member type inner val not implemented"); + public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType t, SemType key) { + return MappingProj.mappingMemberTypeInnerVal(from(context), t, key); } @Override 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 index b71a57e20973..fded9d1e4887 100644 --- 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 @@ -242,14 +242,6 @@ public Object[] runtimeFileNameProviderFunc() { "function-union-tv.bal", "func-quals-tv.bal" )); - Predicate mappingFilter = createRuntimeFileNameFilter(Set.of( - "record-proj-tv.bal", - "record-t.bal", - "recursive-record-t.bal", - "test_test.bal", - "proj1-tv.bal", - "proj3-tv.bal" - )); Predicate xmlFilter = createRuntimeFileNameFilter(Set.of( "xml-complex-ro-tv.bal", "xml-complex-rw-tv.bal", @@ -268,7 +260,6 @@ public Object[] runtimeFileNameProviderFunc() { return balFiles.stream() .filter(tableFilter) .filter(functionFilter) - .filter(mappingFilter) .filter(xmlFilter) .filter(objectFilter) .map(File::getAbsolutePath).toArray(); From e7531be41425eba04d4eec5010f075353d6ad8cf Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 12 Jun 2024 14:26:05 +0530 Subject: [PATCH 044/178] Implement singleton types for BMaps --- .../runtime/api/types/semtype/Builder.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) 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 index c003701545ed..a65f4c196e71 100644 --- 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 @@ -20,6 +20,7 @@ import io.ballerina.runtime.api.types.Type; 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.BType; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; @@ -38,7 +39,9 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; @@ -279,7 +282,6 @@ static SubType[] initializeSubtypeArray() { } public static Optional typeOf(Object object) { - // FIXME: handle mapping values here if (object == null) { return Optional.of(nilType()); } else if (object instanceof DecimalValue decimalValue) { @@ -296,10 +298,30 @@ public static Optional typeOf(Object object) { return Optional.of(stringConst(stringValue.getValue())); } else if (object instanceof BArray arrayValue) { return typeOfArray(arrayValue); + } else if (object instanceof BMap mapValue) { + return typeOfMap(mapValue); } return Optional.empty(); } + private static Optional typeOfMap(BMap mapValue) { + int nFields = mapValue.size(); + MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; + Map.Entry[] entries = (Map.Entry[]) mapValue.entrySet().toArray(new Map.Entry[0]); + for (int i = 0; i < nFields; i++) { + String key = entries[i].getKey().toString(); + Object value = entries[i].getValue(); + Optional valueType = typeOf(value); + if (valueType.isEmpty()) { + return Optional.empty(); + } + fields[i] = new MappingDefinition.Field(key, valueType.get(), true, false); + } + // TODO: cache this in the map value + MappingDefinition md = new MappingDefinition(); + return Optional.of(md.defineMappingTypeWrapped(env, fields, neverType(), CELL_MUT_NONE)); + } + private static Optional typeOfArray(BArray arrayValue) { int size = arrayValue.size(); SemType[] memberTypes = new SemType[size]; @@ -311,6 +333,7 @@ private static Optional typeOfArray(BArray arrayValue) { memberTypes[i] = memberType.get(); } ListDefinition ld = new ListDefinition(); + // TODO: cache this in the array value return Optional.of( ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); } From 9f93c7ce900a8411bccd10dcb9304a194c95c6a7 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 13 Jun 2024 05:07:44 +0530 Subject: [PATCH 045/178] Fix cyclic recursive type problem --- .../runtime/api/types/semtype/Builder.java | 11 +++-- .../runtime/api/types/semtype/Context.java | 31 ++++++++++++ .../runtime/internal/TypeChecker.java | 48 +++++++++++-------- .../runtime/internal/types/BAnyType.java | 3 +- .../runtime/internal/types/BArrayType.java | 14 ++++-- .../runtime/internal/types/BFiniteType.java | 3 +- .../internal/types/BIntersectionType.java | 5 +- .../runtime/internal/types/BMapType.java | 14 ++++-- .../runtime/internal/types/BNullType.java | 3 +- .../runtime/internal/types/BReadonlyType.java | 3 +- .../runtime/internal/types/BRecordType.java | 16 +++++-- .../internal/types/BSemTypeSupplier.java | 29 +++++++++++ .../runtime/internal/types/BTupleType.java | 16 +++++-- .../runtime/internal/types/BType.java | 12 +++-- .../internal/types/BTypeConverter.java | 44 +++++++++-------- .../internal/types/BTypeReferenceType.java | 5 +- .../runtime/internal/types/BUnionType.java | 5 +- .../types/PartialSemTypeSupplier.java | 24 ++++++++++ 18 files changed, 211 insertions(+), 75 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java 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 index a65f4c196e71..0dea62f4ff42 100644 --- 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 @@ -138,17 +138,20 @@ public static SemType from(BasicTypeCode typeCode) { return SemType.from(1 << typeCode.code()); } - public static SemType from(Type type) { + public static SemType from(Context cx, Type type) { if (type instanceof SemType semType) { return semType; } else if (type instanceof BType bType) { - return from(bType); + return fromBType(cx, bType); } throw new IllegalArgumentException("Unsupported type: " + type); } - public static SemType from(BType innerType) { - return innerType.get(); + private static SemType fromBType(Context cx, BType innerType) { + int staringSize = cx.addProvisionalType(innerType); + SemType result = innerType.get(cx); + cx.emptyProvisionalTypes(staringSize); + return result; } public static SemType neverType() { 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 index 0486c8d97932..a6e7ed070f44 100644 --- 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 @@ -18,6 +18,8 @@ package io.ballerina.runtime.api.types.semtype; +import io.ballerina.runtime.internal.types.BType; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,6 +40,9 @@ public final class Context { public final Map listMemo = new HashMap<>(); public final Map mappingMemo = new HashMap<>(); + private final List provisionalTypes = new ArrayList<>(); + private boolean resetProvisionalTypes = false; + SemType anydataMemo; private Context(Env env) { this.env = env; @@ -116,4 +121,30 @@ public MappingAtomicType mappingAtomType(Atom atom) { return (MappingAtomicType) ((TypeAtom) atom).atomicType(); } } + + public int addProvisionalType(BType type) { + int currentSize = provisionalTypes.size(); + provisionalTypes.add(type); + return currentSize; + } + + public void markProvisionTypeReset() { + resetProvisionalTypes = true; + } + + public void emptyProvisionalTypes(int startingSize) { + if (startingSize != 0) { + return; + } + // FIXME: reset all (if we have cycles we will reset the top one as well) + if (resetProvisionalTypes) { + for (int i = 1; i < provisionalTypes.size(); i++) { + BType type = provisionalTypes.get(i); + // TODO: we should be able to be more selective about resetting the cache + type.resetSemTypeCache(); + } + } + provisionalTypes.clear(); + resetProvisionalTypes = false; + } } 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 916a89b89c4b..06c5c484895d 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 @@ -273,7 +273,8 @@ 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) { - SemType targetSemType = Builder.from(targetType); + Context cx = context(); + SemType targetSemType = Builder.from(cx, targetType); SemType targetBasicTypeUnion = Core.widenToBasicTypeUnion(targetSemType); SemType valueBasicType = basicType(sourceVal); if (!Core.isSubtypeSimple(valueBasicType, targetBasicTypeUnion)) { @@ -282,7 +283,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { if (targetBasicTypeUnion == targetSemType) { return true; } - SemType sourceSemType = Builder.from(getType(sourceVal)); + SemType sourceSemType = Builder.from(cx, getType(sourceVal)); return switch (isSubTypeInner(sourceVal, sourceSemType, targetSemType)) { case TRUE -> true; case FALSE -> false; @@ -301,11 +302,12 @@ 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) { - return switch (isSubType(sourceVal, sourceType, targetType)) { + Context cx = context(); + return switch (isSubType(cx, sourceVal, sourceType, targetType)) { case TRUE -> true; case FALSE -> false; - case MAYBE -> - FallbackTypeChecker.checkIsType(errors, sourceVal, bTypePart(sourceType), bTypePart(targetType)); + case MAYBE -> FallbackTypeChecker.checkIsType(errors, sourceVal, bTypePart(cx, sourceType), + bTypePart(cx, targetType)); }; } @@ -501,28 +503,32 @@ 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 switch (isSubType(sourceType, targetType)) { + Context cx = context(); + return switch (isSubType(cx, sourceType, targetType)) { case TRUE -> true; case FALSE -> false; - case MAYBE -> FallbackTypeChecker.checkIsType(bTypePart(sourceType), bTypePart(targetType), null); + case MAYBE -> FallbackTypeChecker.checkIsType(bTypePart(cx, sourceType), bTypePart(cx, targetType), null); }; } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - return switch (isSubType(sourceType, targetType)) { + Context cx = context(); + return switch (isSubType(cx, sourceType, targetType)) { case TRUE -> true; case FALSE -> false; - case MAYBE -> - FallbackTypeChecker.checkIsType(bTypePart(sourceType), bTypePart(targetType), unresolvedTypes); + case MAYBE -> FallbackTypeChecker.checkIsType(bTypePart(cx, sourceType), bTypePart(cx, targetType), + unresolvedTypes); }; } static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, List unresolvedTypes) { - return switch (isSubType(sourceVal, sourceType, targetType)) { + Context cx = context(); + return switch (isSubType(cx, sourceVal, sourceType, targetType)) { case TRUE -> true; case FALSE -> false; - case MAYBE -> FallbackTypeChecker.checkIsType(sourceVal, bTypePart(sourceType), bTypePart(targetType), + case MAYBE -> + FallbackTypeChecker.checkIsType(sourceVal, bTypePart(cx, sourceType), bTypePart(cx, targetType), unresolvedTypes); }; } @@ -556,22 +562,22 @@ private enum TypeCheckResult { MAYBE } - private static TypeCheckResult isSubType(Object sourceValue, Type source, Type target) { - TypeCheckResult result = isSubType(source, target); + private static TypeCheckResult isSubType(Context cx, Object sourceValue, Type source, Type target) { + TypeCheckResult result = isSubType(cx, source, target); if (result != TypeCheckResult.FALSE || !source.isReadOnly()) { return result; } - return isSubTypeImmutableValue(sourceValue, Builder.from(target)); + return isSubTypeImmutableValue(sourceValue, Builder.from(cx, target)); } - private static TypeCheckResult isSubType(Type source, Type target) { + private static TypeCheckResult isSubType(Context cx, Type source, Type target) { if (source instanceof ParameterizedType sourceParamType) { if (target instanceof ParameterizedType targetParamType) { - return isSubType(sourceParamType.getParamValueType(), targetParamType.getParamValueType()); + return isSubType(cx, sourceParamType.getParamValueType(), targetParamType.getParamValueType()); } - return isSubType(sourceParamType.getParamValueType(), target); + return isSubType(cx, sourceParamType.getParamValueType(), target); } - return isSubTypeInner(Builder.from(source), Builder.from(target)); + return isSubTypeInner(Builder.from(cx, source), Builder.from(cx, target)); } private static TypeCheckResult isSubTypeInner(Object sourceValue, SemType source, SemType target) { @@ -623,8 +629,8 @@ private static SemType basicType(Object value) { } } - private static BType bTypePart(Type t) { - return bTypePart(Builder.from(t)); + private static BType bTypePart(Context cx, Type t) { + return bTypePart(Builder.from(cx, t)); } private static BType bTypePart(SemType t) { 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 fec3cc535296..7a6328955cf2 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,7 @@ import io.ballerina.runtime.api.types.AnyType; import io.ballerina.runtime.api.types.IntersectionType; 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.internal.values.RefValue; @@ -103,7 +104,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { return BTypeConverter.fromAnyType(this); } } 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 9d718cb84ba9..8407be0ef993 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 @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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; @@ -46,7 +47,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BArrayType extends BType implements ArrayType { +public class BArrayType extends BType implements ArrayType, PartialSemTypeSupplier { private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private Type elementType; @@ -218,14 +219,15 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } defn = new ListDefinition(); - SemType elementType = Builder.from(getElementType()); + SemType elementType = Builder.from(cx, getElementType()); SemType pureBTypePart = Core.intersect(elementType, Core.B_TYPE_TOP); if (!Core.isNever(pureBTypePart)) { + cx.markProvisionTypeReset(); SemType pureSemTypePart = Core.intersect(elementType, Core.SEMTYPE_TOP); SemType semTypePart = getSemTypePart(pureSemTypePart); SemType bTypePart = BTypeConverter.wrapAsPureBType(this); @@ -245,4 +247,10 @@ private SemType getSemTypePart(SemType elementType) { return defn.defineListTypeWrapped(env, initial, size, Builder.neverType(), mut); } } + + @Override + public void resetSemTypeCache() { + super.resetSemTypeCache(); + defn = null; + } } 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 8d423840a11d..c672f1582d4c 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,6 +21,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; +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.values.RefValue; @@ -204,7 +205,7 @@ public boolean equals(Object o) { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { return BTypeConverter.fromFiniteType(this); } } 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 6d8d1add718a..d3e7592e96c4 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 @@ -25,6 +25,7 @@ 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.SemType; import java.util.ArrayList; @@ -219,11 +220,11 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { Type effectiveType = getEffectiveType(); if (effectiveType instanceof SemType semType) { return semType; } - return Builder.from(effectiveType); + return Builder.from(cx, effectiveType); } } 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 075ee33cfb80..c1ab00131c3a 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 @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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; @@ -47,7 +48,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BMapType extends BType implements MapType { +public class BMapType extends BType implements MapType, PartialSemTypeSupplier { public static final MappingDefinition.Field[] EMPTY_FIELD_ARR = new MappingDefinition.Field[0]; private final Type constraint; @@ -179,14 +180,15 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } defn = new MappingDefinition(); - SemType restType = Builder.from(getConstrainedType()); + SemType restType = Builder.from(cx, getConstrainedType()); SemType pureBTypePart = Core.intersect(restType, Core.B_TYPE_TOP); if (!Core.isNever(pureBTypePart)) { + cx.markProvisionTypeReset(); SemType pureSemTypePart = Core.intersect(restType, Core.SEMTYPE_TOP); SemType semTypePart = getSemTypePart(pureSemTypePart); SemType bTypePart = BTypeConverter.wrapAsPureBType(this); @@ -195,6 +197,12 @@ SemType createSemType() { return getSemTypePart(restType); } + @Override + public void resetSemTypeCache() { + super.resetSemTypeCache(); + defn = null; + } + private SemType getSemTypePart(SemType restType) { CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; 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 056b1519ca10..65e702da9b4b 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 @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.NullType; import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; /** @@ -80,7 +81,7 @@ public boolean isReadOnly() { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { return Builder.nilType(); } } 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 858545bf89c1..7280c10fcf7b 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,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ReadonlyType; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; @@ -60,7 +61,7 @@ public boolean isReadOnly() { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { return BTypeConverter.fromReadonly(this); } } 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 2c7f52052dd3..d63de83d6368 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 @@ -30,6 +30,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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; @@ -57,7 +58,7 @@ * * @since 0.995.0 */ -public class BRecordType extends BStructureType implements RecordType { +public class BRecordType extends BStructureType implements RecordType, PartialSemTypeSupplier { private final String internalName; public boolean sealed; public Type restFieldType; @@ -232,7 +233,7 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul } @Override - SemType createSemType() { + SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } @@ -243,7 +244,7 @@ SemType createSemType() { for (int i = 0; i < fields.length; i++) { Field field = fields[i]; boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); - SemType fieldType = Builder.from(field.getFieldType()); + SemType fieldType = Builder.from(cx, field.getFieldType()); if (Core.isNever(fieldType)) { return Builder.neverType(); } else if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { @@ -255,16 +256,23 @@ SemType createSemType() { } CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restFieldType != null ? Builder.from(restFieldType) : Builder.neverType(); + SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : Builder.neverType(); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypePart = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); } if (hasBTypePart) { + cx.markProvisionTypeReset(); SemType semTypePart = defn.defineMappingTypeWrapped(env, mappingFields, rest, mut); SemType bTypePart = BTypeConverter.wrapAsPureBType(this); return Core.union(semTypePart, bTypePart); } return defn.defineMappingTypeWrapped(env, mappingFields, rest, mut); } + + @Override + public void resetSemTypeCache() { + super.resetSemTypeCache(); + defn = null; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java new file mode 100644 index 000000000000..a6695b51c75f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java @@ -0,0 +1,29 @@ +/* + * 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; + +@FunctionalInterface +public interface BSemTypeSupplier { + + SemType get(Context cx); +} 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 5c5258de7c4d..bfe8f0112afa 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 @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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; @@ -44,7 +45,7 @@ * * @since 0.995.0 */ -public class BTupleType extends BAnnotatableType implements TupleType { +public class BTupleType extends BAnnotatableType implements TupleType, PartialSemTypeSupplier { private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private List tupleTypes; @@ -312,7 +313,7 @@ public String getAnnotationKey() { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } @@ -320,7 +321,7 @@ SemType createSemType() { SemType[] memberTypes = new SemType[tupleTypes.size()]; boolean hasBTypePart = false; for (int i = 0; i < tupleTypes.size(); i++) { - SemType memberType = Builder.from(tupleTypes.get(i)); + SemType memberType = Builder.from(cx, tupleTypes.get(i)); if (Core.isNever(memberType)) { // TODO: This is not correct and blow up if this is recursive. But current jBal type implementation // treats these as never while nBal don't. Revisit this once all types are done @@ -338,7 +339,7 @@ SemType createSemType() { } CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restType != null ? Builder.from(restType) : Builder.neverType(); + SemType rest = restType != null ? Builder.from(cx, restType) : Builder.neverType(); // if (Core.isSubtypeSimple(rest, Core.B_TYPE_TOP)) { // SemType semTypePart = // defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, Builder.neverType(), mut); @@ -350,10 +351,17 @@ SemType createSemType() { rest = Core.intersect(rest, Core.SEMTYPE_TOP); } if (hasBTypePart) { + cx.markProvisionTypeReset(); SemType semTypePart = defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); SemType bTypePart = BTypeConverter.wrapAsPureBType(this); return Core.union(semTypePart, bTypePart); } return defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } + + @Override + public void resetSemTypeCache() { + super.resetSemTypeCache(); + defn = null; + } } 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 58190f9cdf0b..fb126ca971aa 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 @@ -23,6 +23,7 @@ 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.api.utils.StringUtils; @@ -42,7 +43,7 @@ * * @since 0.995.0 */ -public abstract class BType implements Type, SubTypeData, Supplier { +public abstract class BType implements Type, SubTypeData, BSemTypeSupplier { private static final SemType READONLY_WITH_B_TYPE = Core.union(Builder.readonlyType(), Core.B_TYPE_TOP); protected String typeName; @@ -241,19 +242,20 @@ public Type getCachedImpliedType() { } // If any child class allow mutation that will affect the SemType, it must call this method. - final void resetSemTypeCache() { + // TODO: update this comment to mention what context does + public void resetSemTypeCache() { cachedSemType = null; } // If any child class partially implement SemType it must override this method. - SemType createSemType() { + SemType createSemType(Context cx) { return BTypeConverter.wrapAsPureBType(this); } @Override - public final SemType get() { + public final SemType get(Context cx) { if (cachedSemType == null) { - cachedSemType = createSemType(); + cachedSemType = createSemType(cx); if (isReadOnly()) { cachedSemType = Core.intersect(cachedSemType, READONLY_WITH_B_TYPE); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 4078c1526ff5..04457a80480a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -18,11 +18,10 @@ package io.ballerina.runtime.internal.types; -import io.ballerina.runtime.api.flags.SymbolFlags; -import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.BasicTypeCode; 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.BSubType; @@ -33,7 +32,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; /** * This is a utility class for {@code Builder} class so that BTypes don't need to expose their internal structure as @@ -61,17 +59,20 @@ private static SemType unionOf(SemType... semTypes) { return result; } - private static SemType from(Type type) { + private static SemType from(Context cx, Type type) { if (type instanceof SemType semType) { return semType; } else if (type instanceof BType bType) { - return fromBType(bType); + return fromBType(cx, bType); } throw new IllegalArgumentException("Unsupported type: " + type); } - private static SemType fromBType(BType innerType) { - return innerType.get(); + private static SemType fromBType(Context cx, BType innerType) { + int staringSize = cx.addProvisionalType(innerType); + SemType res = innerType.get(cx); + cx.emptyProvisionalTypes(staringSize); + return res; } static SemType fromReadonly(BReadonlyType readonlyType) { @@ -98,8 +99,8 @@ static SemType fromFiniteType(BFiniteType finiteType) { return Core.union(parts.semTypePart(), bTypePart); } - static SemType fromUnionType(BUnionType unionType) { - BTypeParts parts = splitUnion(unionType); + static SemType fromUnionType(Context cx, BUnionType unionType) { + BTypeParts parts = splitUnion(cx, unionType); if (parts.bTypeParts().isEmpty()) { return parts.semTypePart(); } @@ -111,32 +112,33 @@ private record BTypeParts(SemType semTypePart, List bTypeParts) { } - private static BTypeParts split(Type type) { + private static BTypeParts split(Context cx, Type type) { if (type instanceof SemType) { - return new BTypeParts(from(type), Collections.emptyList()); + return new BTypeParts(from(cx, type), Collections.emptyList()); } else if (type instanceof BUnionType unionType) { - return splitUnion(unionType); + return splitUnion(cx, unionType); } else if (type instanceof BAnyType anyType) { return splitAnyType(anyType); } else if (type instanceof BTypeReferenceType referenceType) { - return split(referenceType.getReferredType()); + return split(cx, referenceType.getReferredType()); } else if (type instanceof BIntersectionType intersectionType) { - return split(intersectionType.getEffectiveType()); + return split(cx, intersectionType.getEffectiveType()); } else if (type instanceof BReadonlyType readonlyType) { return splitReadonly(readonlyType); } else if (type instanceof BFiniteType finiteType) { return splitFiniteType(finiteType); // FIXME: introduce a marker type for these - } else if (type instanceof BArrayType || type instanceof BTupleType || type instanceof BRecordType || - type instanceof BMapType) { - return splitSemTypeSupplier((Supplier) type); + } else if (type instanceof PartialSemTypeSupplier supplier) { + return splitSemTypeSupplier(cx, supplier); } else { return new BTypeParts(Builder.neverType(), List.of(type)); } } - private static BTypeParts splitSemTypeSupplier(Supplier supplier) { - SemType semtype = supplier.get(); + private static BTypeParts splitSemTypeSupplier(Context cx, PartialSemTypeSupplier supplier) { + int startingIndex = cx.addProvisionalType((BType) supplier); + SemType semtype = supplier.get(cx); + cx.emptyProvisionalTypes(startingIndex); SemType bBTypePart = Core.intersect(semtype, Core.B_TYPE_TOP); if (Core.isNever(bBTypePart)) { return new BTypeParts(semtype, Collections.emptyList()); @@ -178,12 +180,12 @@ private static BTypeParts splitReadonly(BReadonlyType readonlyType) { return new BTypeParts(READONLY_SEMTYPE_PART, List.of(readonlyType)); } - private static BTypeParts splitUnion(BUnionType unionType) { + private static BTypeParts splitUnion(Context cx, BUnionType unionType) { List members = Collections.unmodifiableList(unionType.getMemberTypes()); List bTypeMembers = new ArrayList<>(members.size()); SemType semTypePart = Builder.neverType(); for (Type member : members) { - BTypeParts memberParts = split(member); + BTypeParts memberParts = split(cx, member); semTypePart = Core.union(memberParts.semTypePart(), semTypePart); bTypeMembers.addAll(memberParts.bTypeParts()); } 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 cdee20c28d2f..2e655c6077b7 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 @@ -26,6 +26,7 @@ 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.SemType; import java.util.Objects; @@ -130,11 +131,11 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType() { + SemType createSemType(Context cx) { Type referredType = getReferredType(); if (referredType instanceof SemType semType) { return semType; } - return Builder.from(referredType); + return Builder.from(cx, referredType); } } 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 dff531ac84a9..72568dc7641e 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 @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.TypeChecker; @@ -544,7 +545,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType() { - return BTypeConverter.fromUnionType(this); + SemType createSemType(Context cx) { + return BTypeConverter.fromUnionType(cx, this); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java new file mode 100644 index 000000000000..d517776cbcfd --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java @@ -0,0 +1,24 @@ +/* + * 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; + +public interface PartialSemTypeSupplier extends BSemTypeSupplier { + +} From d17e9f1cf0be2f4c0cbe0ca8eea0aced41af8ff1 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 13 Jun 2024 06:00:51 +0530 Subject: [PATCH 046/178] Fix never type not being set correctly for records Fix initialization bug WIP: add hack to make never? on the positive side work --- .../ballerina/runtime/api/types/semtype/Builder.java | 10 +++++----- .../runtime/api/types/semtype/CellAtomicType.java | 4 ++++ .../io/ballerina/runtime/api/types/semtype/Env.java | 5 +++-- .../ballerina/runtime/internal/types/BRecordType.java | 2 +- .../internal/types/semtype/BMappingSubType.java | 5 +++++ 5 files changed, 18 insertions(+), 8 deletions(-) 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 index 0dea62f4ff42..fcded554ea17 100644 --- 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 @@ -62,6 +62,11 @@ */ public final class Builder { + private static final String[] EMPTY_STRING_ARR = new String[0]; + private static final SemType NEVER = SemType.from(0); + private static final SemType VAL = SemType.from(VT_MASK); + private static final SemType UNDEF = from(BasicTypeCode.BT_UNDEF); + private static final SemType INNER = basicTypeUnion(VAL.all | UNDEF.all); static final CellAtomicType CELL_ATOMIC_VAL = new CellAtomicType( valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED ); @@ -70,11 +75,6 @@ public final class Builder { static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED ); - private static final String[] EMPTY_STRING_ARR = new String[0]; - private static final SemType NEVER = SemType.from(0); - private static final SemType VAL = SemType.from(VT_MASK); - private static final SemType UNDEF = from(BasicTypeCode.BT_UNDEF); - private static final SemType INNER = basicTypeUnion(VAL.all | UNDEF.all); public static final int BDD_REC_ATOM_READONLY = 0; // represents both readonly & map and readonly & readonly[] diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 3762d9154187..ad5e969e8620 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -27,6 +27,10 @@ */ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { + public CellAtomicType { + assert ty != null; + } + public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { SemType ty = Core.intersect(c1.ty(), c2.ty()); CellMutability mut = min(c1.mut(), c2.mut()); 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 index fc681a2f9bc2..7210d873e2ba 100644 --- 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 @@ -63,11 +63,12 @@ private Env() { this.cellAtom(Builder.CELL_ATOMIC_NEVER); this.cellAtom(Builder.CELL_ATOMIC_INNER); + this.cellAtom(Builder.CELL_ATOMIC_INNER_MAPPING); this.listAtom(Builder.LIST_ATOMIC_MAPPING); + this.cellAtom(Builder.CELL_ATOMIC_INNER_MAPPING_RO); this.listAtom(Builder.LIST_ATOMIC_MAPPING_RO); - this.cellAtom(Builder.CELL_ATOMIC_INNER_RO); } @@ -97,7 +98,7 @@ private TypeAtom typeAtom(AtomicType atomicType) { if (ta != null) { return ta; } else { - TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size() + 1, atomicType); this.atomTable.put(result.atomicType(), result); return result; } 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 d63de83d6368..397c5b23230b 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 @@ -245,7 +245,7 @@ SemType createSemType(Context cx) { Field field = fields[i]; boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); SemType fieldType = Builder.from(cx, field.getFieldType()); - if (Core.isNever(fieldType)) { + if (!isOptional && Core.isNever(fieldType)) { return Builder.neverType(); } else if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { hasBTypePart = true; 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 index 88b333a11220..d86c9f5c36ba 100644 --- 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 @@ -20,6 +20,7 @@ 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; @@ -143,6 +144,10 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju // the posType came from the rest type mt = insertField(pos, fieldPair.name(), d); } else { + // FIXME: + if (Core.isSubType(cx, fieldPair.type1(), Builder.cellContaining(cx.env, Builder.undef()))) { + continue; + } SemType[] posTypes = pos.types().clone(); posTypes[fieldPair.index1()] = d; mt = new MappingAtomicType(pos.names(), posTypes, pos.rest()); From ed549ef80ef3bb9d5bc14feb308134644b3441cd Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 07:06:55 +0530 Subject: [PATCH 047/178] Make type creation synchronized for definition based types --- .../runtime/internal/types/BArrayType.java | 2 +- .../runtime/internal/types/BMapType.java | 2 +- .../runtime/internal/types/BRecordType.java | 2 +- .../runtime/internal/types/BTupleType.java | 15 +-------------- 4 files changed, 4 insertions(+), 17 deletions(-) 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 8407be0ef993..5bb22a33c8fa 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 @@ -219,7 +219,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType(Context cx) { + synchronized SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } 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 c1ab00131c3a..08d50e725d35 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 @@ -180,7 +180,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType(Context cx) { + synchronized SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } 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 397c5b23230b..b9c7cad7a373 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 @@ -233,7 +233,7 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul } @Override - SemType createSemType(Context cx) { + synchronized SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } 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 bfe8f0112afa..5676e3457adf 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 @@ -313,7 +313,7 @@ public String getAnnotationKey() { } @Override - SemType createSemType(Context cx) { + synchronized SemType createSemType(Context cx) { if (defn != null) { return defn.getSemType(env); } @@ -323,14 +323,7 @@ SemType createSemType(Context cx) { for (int i = 0; i < tupleTypes.size(); i++) { SemType memberType = Builder.from(cx, tupleTypes.get(i)); if (Core.isNever(memberType)) { - // TODO: This is not correct and blow up if this is recursive. But current jBal type implementation - // treats these as never while nBal don't. Revisit this once all types are done return Builder.neverType(); -// } else if (Core.isSubtypeSimple(memberType, Core.B_TYPE_TOP)) { -// SemType semTypePart = defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, Builder.neverType(), -// CellAtomicType.CellMutability.CELL_MUT_NONE); -// SemType bTypePart = BTypeConverter.wrapAsPureBType(this); -// return Core.union(semTypePart, bTypePart); } else if (!Core.isNever(Core.intersect(memberType, Core.B_TYPE_TOP))) { hasBTypePart = true; memberType = Core.intersect(memberType, Core.SEMTYPE_TOP); @@ -340,12 +333,6 @@ SemType createSemType(Context cx) { CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; SemType rest = restType != null ? Builder.from(cx, restType) : Builder.neverType(); -// if (Core.isSubtypeSimple(rest, Core.B_TYPE_TOP)) { -// SemType semTypePart = -// defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, Builder.neverType(), mut); -// SemType bTypePart = BTypeConverter.wrapAsPureBType(this); -// return Core.union(semTypePart, bTypePart); -// } if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypePart = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); From 28ef5b7a7f4f95ac449b3b9b56853fd29a2ff821 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 10:13:03 +0530 Subject: [PATCH 048/178] Extend mapping type projection to support getting cell type of field --- .../api/types/semtype/MappingProj.java | 5 ++ .../internal/types/semtype/BMappingProj.java | 75 +++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java index 7ed7e47bed96..4ee596fa52e1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -21,6 +21,8 @@ import io.ballerina.runtime.internal.types.semtype.BMappingProj; +import static io.ballerina.runtime.api.types.semtype.Core.diff; + public final class MappingProj { private MappingProj() { @@ -30,4 +32,7 @@ public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k return BMappingProj.mappingMemberTypeInnerVal(cx, t, k); } + public static SemType mappingMemberType(Context cx, SemType t, SemType k) { + return BMappingProj.mappingMemberType(cx, t, k); + } } 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 index 4e884eeb8a1a..24ca59ebf065 100644 --- 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 @@ -29,6 +29,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; @@ -43,6 +44,80 @@ public final class BMappingProj { private BMappingProj() { } + // TODO: refactor things to avoid duplication (Bench method ref vs duplication) + public static SemType mappingMemberType(Context cx, SemType t, SemType k) { + // FIXME: cell containting undef + SemType result = diff(mappingMemberTypeAux(cx, t, k), Builder.undef()); + assert Core.isSubType(cx, result, Builder.cell()) : "map field type should be wrapped in a cell"; + return result; + } + + // Same as mappingMemberTypeInner except don't remove the cell + private static SemType mappingMemberTypeAux(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + SemType res = (t.all() & Builder.mappingType().all()) != 0 ? (Builder.valType()) : Builder.undef(); + return Builder.cellContaining(cx.env, res); + } else { + SubTypeData keyData = stringSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.cellContaining(cx.env, Builder.undef()); + } + return bddMappingMemberType(cx, (Bdd) getComplexSubtypeData(t, BT_MAPPING), keyData, + Builder.cell()); + } + } + + static SemType bddMappingMemberType(Context cx, Bdd b, SubTypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : Builder.neverType(); + } else { + BddNode bdd = (BddNode) b; + return Core.union( + bddMappingMemberType(cx, bdd.left(), key, + Core.intersect(mappingAtomicMemberType(cx.mappingAtomType(bdd.atom()), key), + accum)), + Core.union(bddMappingMemberType(cx, bdd.middle(), key, accum), + bddMappingMemberType(cx, bdd.right(), key, accum))); + } + } + + static SemType mappingAtomicMemberType(MappingAtomicType atomic, SubTypeData key) { + SemType memberType = null; + for (SemType ty : mappingAtomicApplicableMemberTypes(atomic, key)) { + if (memberType == null) { + memberType = ty; + } else { + memberType = Core.union(memberType, ty); + } + } + // FIXME: wrap in cell + return memberType == null ? Builder.undef() : memberType; + } + + static List mappingAtomicApplicableMemberTypes(MappingAtomicType atomic, SubTypeData key) { + // FIXME: + List types = new ArrayList<>(atomic.types().length); + Collections.addAll(types, atomic.types()); + + List memberTypes = new ArrayList<>(); + SemType rest = atomic.rest(); + if (isAllSubtype(key)) { + memberTypes.addAll(types); + memberTypes.add(rest); + } else { + BStringSubType.StringSubtypeListCoverage coverage = + stringSubtypeListCoverage((BStringSubType.StringSubTypeData) key, + atomic.names()); + for (int index : coverage.indices()) { + memberTypes.add(types.get(index)); + } + if (!coverage.isSubType()) { + memberTypes.add(rest); + } + } + return memberTypes; + } + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { return diff(mappingMemberTypeInner(cx, t, k), Builder.undef()); } From 0b11b0f2853bc05b255e94ac9955defe21ba8d89 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 11:01:35 +0530 Subject: [PATCH 049/178] Refactor type checker with "widenedType" Widened type for basic types is the basic type where as for other types it is contextually expected type (not the actual shape of the value). This is to avoid the overhead of having to create singleton types for each basic type value --- .../java/io/ballerina/runtime/api/values/BValue.java | 5 +++-- .../io/ballerina/runtime/internal/TypeChecker.java | 6 +++--- .../runtime/internal/values/AbstractArrayValue.java | 10 ++++++++++ .../ballerina/runtime/internal/values/ArrayValue.java | 6 +----- .../runtime/internal/values/ArrayValueImpl.java | 4 ++++ .../io/ballerina/runtime/internal/values/MapValue.java | 7 +++++-- 6 files changed, 26 insertions(+), 12 deletions(-) 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 77ec7a150d16..accc36c2bdfb 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 @@ -19,6 +19,7 @@ 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 java.util.Map; @@ -61,7 +62,7 @@ default String informalStringValue(BLink parent) { Type getType(); - default SemType basicType() { - return Builder.bType(); + default SemType widenedType(Context cx) { + return Builder.from(cx, getType()); } } 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 06c5c484895d..d972ba5636f4 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 @@ -276,7 +276,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { Context cx = context(); SemType targetSemType = Builder.from(cx, targetType); SemType targetBasicTypeUnion = Core.widenToBasicTypeUnion(targetSemType); - SemType valueBasicType = basicType(sourceVal); + SemType valueBasicType = widenedType(cx, sourceVal); if (!Core.isSubtypeSimple(valueBasicType, targetBasicTypeUnion)) { return false; } @@ -611,7 +611,7 @@ private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { return Core.isSubType(cx, sourcePureSemType, targetPureSemType) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; } - private static SemType basicType(Object value) { + private static SemType widenedType(Context cx, Object value) { if (value == null) { return Builder.nilType(); } else if (value instanceof Double) { @@ -625,7 +625,7 @@ private static SemType basicType(Object value) { } else if (value instanceof DecimalValue) { return Builder.decimalType(); } else { - return ((BValue) value).basicType(); + return ((BValue) value).widenedType(cx); } } 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 ab80640c69d0..e2b4998919bb 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,6 +20,10 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; 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.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.IteratorUtils; @@ -274,6 +278,12 @@ protected void prepareForAddForcefully(int intIndex, int currentArraySize) { resetSize(intIndex); } + @Override + public SemType widenedType(Context cx) { + SemType semType = Builder.from(cx, getType()); + return Core.intersect(semType, Builder.listType()); + } + /** * {@code {@link ArrayIterator}} provides iterator implementation for Ballerina array values. * diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java index 437ebd6bd002..492c8aa60d87 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.internal.values; 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.BArray; @@ -41,9 +42,4 @@ public interface ArrayValue extends RefValue, BArray, CollectionValue { @Override void setLength(long length); - - @Override - default SemType basicType() { - return Builder.listType(); - } } 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 4544d8a36463..a35e41c5a2d4 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,10 @@ import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.ArrayType.ArrayState; 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.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java index 0c8bb4ae2273..77752b5f1887 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java @@ -18,6 +18,8 @@ package io.ballerina.runtime.internal.values; 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.BMap; @@ -37,7 +39,8 @@ public interface MapValue extends RefValue, CollectionValue, BMap { @Override - default SemType basicType() { - return Builder.mappingType(); + default SemType widenedType(Context cx) { + SemType semType = Builder.from(cx, getType()); + return Core.intersect(semType, Builder.mappingType()); } } From 1def5bdf9de61964859c38fd8587e6d0c5ef9614 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 11:25:06 +0530 Subject: [PATCH 050/178] Refactor shape calculation --- .../runtime/api/types/semtype/Builder.java | 15 +++++----- .../runtime/internal/TypeChecker.java | 12 ++++---- .../runtime/internal/types/BFiniteType.java | 2 +- .../runtime/internal/types/BMapType.java | 8 +++++- .../internal/types/BTypeConverter.java | 10 +++---- .../runtime/internal/types/TypeWithShape.java | 28 +++++++++++++++++++ 6 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java 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 index fcded554ea17..d3ad01eb8430 100644 --- 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 @@ -41,7 +41,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; @@ -284,7 +283,7 @@ static SubType[] initializeSubtypeArray() { return new SubType[CODE_B_TYPE + 2]; } - public static Optional typeOf(Object object) { + public static Optional shapeOf(Context cx, Object object) { if (object == null) { return Optional.of(nilType()); } else if (object instanceof DecimalValue decimalValue) { @@ -300,21 +299,21 @@ public static Optional typeOf(Object object) { } else if (object instanceof BString stringValue) { return Optional.of(stringConst(stringValue.getValue())); } else if (object instanceof BArray arrayValue) { - return typeOfArray(arrayValue); + return typeOfArray(cx, arrayValue); } else if (object instanceof BMap mapValue) { - return typeOfMap(mapValue); + return typeOfMap(cx, mapValue); } return Optional.empty(); } - private static Optional typeOfMap(BMap mapValue) { + private static Optional typeOfMap(Context cx, BMap mapValue) { int nFields = mapValue.size(); MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; Map.Entry[] entries = (Map.Entry[]) mapValue.entrySet().toArray(new Map.Entry[0]); for (int i = 0; i < nFields; i++) { String key = entries[i].getKey().toString(); Object value = entries[i].getValue(); - Optional valueType = typeOf(value); + Optional valueType = shapeOf(cx, value); if (valueType.isEmpty()) { return Optional.empty(); } @@ -325,11 +324,11 @@ private static Optional typeOfMap(BMap mapValue) { return Optional.of(md.defineMappingTypeWrapped(env, fields, neverType(), CELL_MUT_NONE)); } - private static Optional typeOfArray(BArray arrayValue) { + private static Optional typeOfArray(Context cx, BArray arrayValue) { int size = arrayValue.size(); SemType[] memberTypes = new SemType[size]; for (int i = 0; i < size; i++) { - Optional memberType = typeOf(arrayValue.get(i)); + Optional memberType = shapeOf(cx, arrayValue.get(i)); if (memberType.isEmpty()) { 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 d972ba5636f4..d19951542178 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 @@ -284,7 +284,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { return true; } SemType sourceSemType = Builder.from(cx, getType(sourceVal)); - return switch (isSubTypeInner(sourceVal, sourceSemType, targetSemType)) { + return switch (isSubTypeInner(cx, sourceVal, sourceSemType, targetSemType)) { case TRUE -> true; case FALSE -> false; case MAYBE -> FallbackTypeChecker.checkIsType(null, sourceVal, bTypePart(sourceSemType), @@ -567,7 +567,7 @@ private static TypeCheckResult isSubType(Context cx, Object sourceValue, Type so if (result != TypeCheckResult.FALSE || !source.isReadOnly()) { return result; } - return isSubTypeImmutableValue(sourceValue, Builder.from(cx, target)); + return isSubTypeImmutableValue(cx, sourceValue, Builder.from(cx, target)); } private static TypeCheckResult isSubType(Context cx, Type source, Type target) { @@ -580,17 +580,17 @@ private static TypeCheckResult isSubType(Context cx, Type source, Type target) { return isSubTypeInner(Builder.from(cx, source), Builder.from(cx, target)); } - private static TypeCheckResult isSubTypeInner(Object sourceValue, SemType source, SemType target) { + private static TypeCheckResult isSubTypeInner(Context cx, Object sourceValue, SemType source, SemType target) { TypeCheckResult result = isSubTypeInner(source, target); if (result != TypeCheckResult.FALSE || !Core.isSubType(context(), Core.intersect(source, SEMTYPE_TOP), Builder.readonlyType())) { return result; } - return isSubTypeImmutableValue(sourceValue, target); + return isSubTypeImmutableValue(cx, sourceValue, target); } - private static TypeCheckResult isSubTypeImmutableValue(Object sourceValue, SemType target) { - Optional sourceSingletonType = Builder.typeOf(sourceValue); + private static TypeCheckResult isSubTypeImmutableValue(Context cx, Object sourceValue, SemType target) { + Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); if (sourceSingletonType.isEmpty()) { return Core.containsBasicType(target, B_TYPE_TOP) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; } 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 c672f1582d4c..268f72eddb7f 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 @@ -206,6 +206,6 @@ public boolean equals(Object o) { @Override SemType createSemType(Context cx) { - return BTypeConverter.fromFiniteType(this); + return BTypeConverter.fromFiniteType(cx, this); } } 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 08d50e725d35..7667d277dbb0 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 @@ -32,6 +32,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BString; 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; @@ -48,7 +49,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BMapType extends BType implements MapType, PartialSemTypeSupplier { +public class BMapType extends BType implements MapType, PartialSemTypeSupplier, TypeWithShape { public static final MappingDefinition.Field[] EMPTY_FIELD_ARR = new MappingDefinition.Field[0]; private final Type constraint; @@ -203,6 +204,11 @@ public void resetSemTypeCache() { defn = null; } + @Override + public SemType shapeOf(Context cx, Object object) { + return get(cx); + } + private SemType getSemTypePart(SemType restType) { CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 04457a80480a..d117bda6c26a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -89,8 +89,8 @@ static SemType fromAnyType(BAnyType anyType) { return Core.union(ANY_SEMTYPE_PART, bTypePart); } - static SemType fromFiniteType(BFiniteType finiteType) { - BTypeParts parts = splitFiniteType(finiteType); + static SemType fromFiniteType(Context cx, BFiniteType finiteType) { + BTypeParts parts = splitFiniteType(cx, finiteType); if (parts.bTypeParts().isEmpty()) { return parts.semTypePart(); } @@ -126,7 +126,7 @@ private static BTypeParts split(Context cx, Type type) { } else if (type instanceof BReadonlyType readonlyType) { return splitReadonly(readonlyType); } else if (type instanceof BFiniteType finiteType) { - return splitFiniteType(finiteType); + return splitFiniteType(cx, finiteType); // FIXME: introduce a marker type for these } else if (type instanceof PartialSemTypeSupplier supplier) { return splitSemTypeSupplier(cx, supplier); @@ -156,12 +156,12 @@ private static BTypeParts splitAnyType(BAnyType anyType) { return new BTypeParts(semTypePart, List.of(anyType)); } - private static BTypeParts splitFiniteType(BFiniteType finiteType) { + private static BTypeParts splitFiniteType(Context cx, BFiniteType finiteType) { Set newValueSpace = new HashSet<>(finiteType.valueSpace.size()); SemType semTypePart = Builder.neverType(); for (var each : finiteType.valueSpace) { // TODO: lift this to Builder (Object) -> Type - Optional semType = Builder.typeOf(each); + Optional semType = Builder.shapeOf(cx, each); if (semType.isPresent()) { semTypePart = Core.union(semTypePart, semType.get()); } else { 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..93a87eecbf34 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java @@ -0,0 +1,28 @@ +/* + * 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; + +public interface TypeWithShape { + + SemType shapeOf(Context cx, Object object); +} From bbd564248e36df2a1c3958625fc97bc3c1403c7e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 12:06:13 +0530 Subject: [PATCH 051/178] Type with shape for mappings --- .../runtime/api/types/semtype/Builder.java | 18 +----- .../io/ballerina/runtime/api/values/BMap.java | 1 + .../internal/types/BIntersectionType.java | 11 +++- .../runtime/internal/types/BMapType.java | 27 +++++++- .../runtime/internal/types/BRecordType.java | 61 +++++++++++++++++-- .../internal/types/BTypeReferenceType.java | 11 +++- .../runtime/internal/types/TypeWithShape.java | 4 +- 7 files changed, 108 insertions(+), 25 deletions(-) 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 index d3ad01eb8430..17f41055b4cf 100644 --- 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; import io.ballerina.runtime.internal.types.semtype.BCellSubType; import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; @@ -307,21 +308,8 @@ public static Optional shapeOf(Context cx, Object object) { } private static Optional typeOfMap(Context cx, BMap mapValue) { - int nFields = mapValue.size(); - MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; - Map.Entry[] entries = (Map.Entry[]) mapValue.entrySet().toArray(new Map.Entry[0]); - for (int i = 0; i < nFields; i++) { - String key = entries[i].getKey().toString(); - Object value = entries[i].getValue(); - Optional valueType = shapeOf(cx, value); - if (valueType.isEmpty()) { - return Optional.empty(); - } - fields[i] = new MappingDefinition.Field(key, valueType.get(), true, false); - } - // TODO: cache this in the map value - MappingDefinition md = new MappingDefinition(); - return Optional.of(md.defineMappingTypeWrapped(env, fields, neverType(), CELL_MUT_NONE)); + TypeWithShape typeWithShape = (TypeWithShape) mapValue.getType(); + return typeWithShape.shapeOf(cx, mapValue); } private static Optional typeOfArray(Context cx, BArray arrayValue) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index 1eb65cc3fab8..3a45b69f5288 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Collection; import java.util.Map; 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 d3e7592e96c4..f7de30acef6c 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 @@ -40,7 +40,7 @@ * * @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 = "("; @@ -227,4 +227,13 @@ SemType createSemType(Context cx) { } return Builder.from(cx, effectiveType); } + + @Override + public Optional shapeOf(Context cx, Object object) { + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, object); + } + return Optional.empty(); + } } 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 7667d277dbb0..781c546a7acb 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 @@ -30,14 +30,17 @@ 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.values.BMap; import io.ballerina.runtime.api.values.BString; 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.Map; import java.util.Optional; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + /** * {@code BMapType} represents a type of a map in Ballerina. *

@@ -205,8 +208,26 @@ public void resetSemTypeCache() { } @Override - public SemType shapeOf(Context cx, Object object) { - return get(cx); + public Optional shapeOf(Context cx, Object object) { + if (!isReadOnly()) { + return Optional.of(get(cx)); + } + BMap value = (BMap) object; + int nFields = value.size(); + MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; + Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); + boolean hasBTypePart = false; + for (int i = 0; i < nFields; i++) { + Optional valueType = Builder.shapeOf(cx, entries[i].getValue()); + if (valueType.isEmpty()) { + return Optional.empty(); + } + SemType fieldType = valueType.get(); + fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); + } + // TODO: cache this in the map value + MappingDefinition md = new MappingDefinition(); + return Optional.of(md.defineMappingTypeWrapped(env, fields, Builder.neverType(), CELL_MUT_NONE)); } private SemType getSemTypePart(SemType restType) { 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 b9c7cad7a373..7533d95f5727 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 @@ -53,12 +53,16 @@ import java.util.Map; import java.util.Optional; +import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + /** * {@code BRecordType} represents a user defined record type in Ballerina. * * @since 0.995.0 */ -public class BRecordType extends BStructureType implements RecordType, PartialSemTypeSupplier { +public class BRecordType extends BStructureType implements RecordType, PartialSemTypeSupplier, TypeWithShape { private final String internalName; public boolean sealed; public Type restFieldType; @@ -246,7 +250,7 @@ synchronized SemType createSemType(Context cx) { boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); SemType fieldType = Builder.from(cx, field.getFieldType()); if (!isOptional && Core.isNever(fieldType)) { - return Builder.neverType(); + return neverType(); } else if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { hasBTypePart = true; fieldType = Core.intersect(fieldType, Core.SEMTYPE_TOP); @@ -254,9 +258,9 @@ synchronized SemType createSemType(Context cx) { mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY), isOptional); } - CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : Builder.neverType(); + SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : neverType(); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypePart = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); @@ -275,4 +279,53 @@ public void resetSemTypeCache() { super.resetSemTypeCache(); defn = null; } + + @Override + public Optional shapeOf(Context cx, Object object) { + BMap value = (BMap) object; + int nFields = value.size(); + MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; + Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); + for (int i = 0; i < nFields; i++) { + String fieldName = entries[i].getKey().toString(); + boolean readonlyField = fieldIsReadonly(fieldName); + Optional fieldType; + if (readonlyField) { + fieldType = Builder.shapeOf(cx, entries[i].getValue()); + } else { + SemType fieldSemType = Builder.from(cx, fieldType(fieldName)); + if (!Core.isNever(Core.intersect(fieldSemType, Core.B_TYPE_TOP))) { + return Optional.empty(); + } + fieldType = Optional.of(fieldSemType); + } + if (fieldType.isEmpty()) { + return Optional.empty(); + } + fields[i] = + new MappingDefinition.Field(entries[i].getKey().toString(), fieldType.get(), readonlyField, false); + } + MappingDefinition md = new MappingDefinition(); + SemType semTypePart; + if (isReadOnly()) { + semTypePart = md.defineMappingTypeWrapped(env, fields, neverType(), CELL_MUT_NONE); + } else { + SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : neverType(); + if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { + return Optional.empty(); + } + semTypePart = md.defineMappingTypeWrapped(env, fields, rest, CELL_MUT_LIMITED); + } + return Optional.of(semTypePart); + } + + 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); + } } 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 2e655c6077b7..f942b6ee958e 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 @@ -37,7 +37,7 @@ * * @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; @@ -138,4 +138,13 @@ SemType createSemType(Context cx) { } return Builder.from(cx, referredType); } + + @Override + public Optional shapeOf(Context cx, Object object) { + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, object); + } + return Optional.empty(); + } } 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 index 93a87eecbf34..53f867070308 100644 --- 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 @@ -22,7 +22,9 @@ import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.Optional; + public interface TypeWithShape { - SemType shapeOf(Context cx, Object object); + Optional shapeOf(Context cx, Object object); } From 7ce1b62f79a4330cc0d1e66820792a0d86425099 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 14:00:19 +0530 Subject: [PATCH 052/178] Use shape with lists --- .../runtime/api/types/semtype/Builder.java | 15 ++------- .../runtime/internal/types/BArrayType.java | 29 ++++++++++++++-- .../runtime/internal/types/BTupleType.java | 33 ++++++++++++++++--- 3 files changed, 58 insertions(+), 19 deletions(-) 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 index 17f41055b4cf..b53e83955907 100644 --- 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 @@ -313,19 +313,8 @@ private static Optional typeOfMap(Context cx, BMap mapValue) { } private static Optional typeOfArray(Context cx, BArray arrayValue) { - int size = arrayValue.size(); - SemType[] memberTypes = new SemType[size]; - for (int i = 0; i < size; i++) { - Optional memberType = shapeOf(cx, arrayValue.get(i)); - if (memberType.isEmpty()) { - return Optional.empty(); - } - memberTypes[i] = memberType.get(); - } - ListDefinition ld = new ListDefinition(); - // TODO: cache this in the array value - return Optional.of( - ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); + TypeWithShape typeWithShape = (TypeWithShape) arrayValue.getType(); + return typeWithShape.shapeOf(cx, arrayValue); } public static SemType roCellContaining(Env env, SemType ty) { 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 5bb22a33c8fa..baefb92bdf48 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 @@ -28,6 +28,7 @@ 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.values.BArray; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.values.ArrayValue; @@ -36,6 +37,9 @@ import java.util.Optional; +import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + /** * {@code BArrayType} represents a type of an arrays in Ballerina. *

@@ -47,7 +51,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BArrayType extends BType implements ArrayType, PartialSemTypeSupplier { +public class BArrayType extends BType implements ArrayType, PartialSemTypeSupplier, TypeWithShape { private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private Type elementType; @@ -244,7 +248,7 @@ private SemType getSemTypePart(SemType elementType) { return defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, elementType, mut); } else { SemType[] initial = {elementType}; - return defn.defineListTypeWrapped(env, initial, size, Builder.neverType(), mut); + return defn.defineListTypeWrapped(env, initial, size, neverType(), mut); } } @@ -253,4 +257,25 @@ public void resetSemTypeCache() { super.resetSemTypeCache(); defn = null; } + + @Override + public Optional shapeOf(Context cx, Object object) { + if (!isReadOnly()) { + return Optional.of(get(cx)); + } + BArray value = (BArray) object; + int size = value.size(); + SemType[] memberTypes = new SemType[size]; + for (int i = 0; i < size; i++) { + Optional memberType = Builder.shapeOf(cx, value.get(i)); + if (memberType.isEmpty()) { + return Optional.empty(); + } + memberTypes[i] = memberType.get(); + } + ListDefinition ld = new ListDefinition(); + // TODO: cache this in the array value + return Optional.of( + ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); + } } 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 5676e3457adf..20f63b356f87 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 @@ -30,6 +30,7 @@ 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.values.BArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TupleValueImpl; @@ -40,12 +41,15 @@ import java.util.Optional; import java.util.stream.Collectors; +import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + /** * {@code {@link BTupleType}} represents a tuple type in Ballerina. * * @since 0.995.0 */ -public class BTupleType extends BAnnotatableType implements TupleType, PartialSemTypeSupplier { +public class BTupleType extends BAnnotatableType implements TupleType, PartialSemTypeSupplier, TypeWithShape { private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private List tupleTypes; @@ -323,16 +327,16 @@ synchronized SemType createSemType(Context cx) { for (int i = 0; i < tupleTypes.size(); i++) { SemType memberType = Builder.from(cx, tupleTypes.get(i)); if (Core.isNever(memberType)) { - return Builder.neverType(); + return neverType(); } else if (!Core.isNever(Core.intersect(memberType, Core.B_TYPE_TOP))) { hasBTypePart = true; memberType = Core.intersect(memberType, Core.SEMTYPE_TOP); } memberTypes[i] = memberType; } - CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restType != null ? Builder.from(cx, restType) : Builder.neverType(); + SemType rest = restType != null ? Builder.from(cx, restType) : neverType(); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypePart = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); @@ -351,4 +355,25 @@ public void resetSemTypeCache() { super.resetSemTypeCache(); defn = null; } + + @Override + public Optional shapeOf(Context cx, Object object) { + if (!isReadOnly()) { + return Optional.of(get(cx)); + } + BArray value = (BArray) object; + int size = value.size(); + SemType[] memberTypes = new SemType[size]; + for (int i = 0; i < size; i++) { + Optional memberType = Builder.shapeOf(cx, value.get(i)); + if (memberType.isEmpty()) { + return Optional.empty(); + } + memberTypes[i] = memberType.get(); + } + ListDefinition ld = new ListDefinition(); + // TODO: cache this in the array value + return Optional.of( + ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); + } } From 3cd82d6975ed5020433b4e44fe380ee21193b58e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 14:16:21 +0530 Subject: [PATCH 053/178] Fix type checker not using shape correctly --- .../io/ballerina/runtime/internal/TypeChecker.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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 d19951542178..245b4cf209c6 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 @@ -564,10 +564,10 @@ private enum TypeCheckResult { private static TypeCheckResult isSubType(Context cx, Object sourceValue, Type source, Type target) { TypeCheckResult result = isSubType(cx, source, target); - if (result != TypeCheckResult.FALSE || !source.isReadOnly()) { + if (result != TypeCheckResult.FALSE) { return result; } - return isSubTypeImmutableValue(cx, sourceValue, Builder.from(cx, target)); + return isSubTypeWithShape(cx, sourceValue, Builder.from(cx, target)); } private static TypeCheckResult isSubType(Context cx, Type source, Type target) { @@ -582,14 +582,13 @@ private static TypeCheckResult isSubType(Context cx, Type source, Type target) { private static TypeCheckResult isSubTypeInner(Context cx, Object sourceValue, SemType source, SemType target) { TypeCheckResult result = isSubTypeInner(source, target); - if (result != TypeCheckResult.FALSE || - !Core.isSubType(context(), Core.intersect(source, SEMTYPE_TOP), Builder.readonlyType())) { + if (result != TypeCheckResult.FALSE) { return result; } - return isSubTypeImmutableValue(cx, sourceValue, target); + return isSubTypeWithShape(cx, sourceValue, target); } - private static TypeCheckResult isSubTypeImmutableValue(Context cx, Object sourceValue, SemType target) { + private static TypeCheckResult isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); if (sourceSingletonType.isEmpty()) { return Core.containsBasicType(target, B_TYPE_TOP) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; From 2fb29839a27e95654585b69d60e24799cbb31643 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 14:44:32 +0530 Subject: [PATCH 054/178] Fixed optional fields not been handled correctly in shape --- .../runtime/internal/types/BRecordType.java | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) 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 7533d95f5727..7749f5e8df27 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 @@ -45,6 +45,7 @@ import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +53,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import static io.ballerina.runtime.api.types.semtype.Builder.neverType; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; @@ -284,14 +286,19 @@ public void resetSemTypeCache() { public Optional shapeOf(Context cx, Object object) { BMap value = (BMap) object; int nFields = value.size(); - MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; + List fields = new ArrayList<>(nFields); Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); + Set handledFields = new HashSet<>(nFields); for (int i = 0; i < nFields; i++) { String fieldName = entries[i].getKey().toString(); + Object fieldValue = entries[i].getValue(); + handledFields.add(fieldName); boolean readonlyField = fieldIsReadonly(fieldName); + boolean optionalField = fieldIsOptional(fieldName); Optional fieldType; if (readonlyField) { - fieldType = Builder.shapeOf(cx, entries[i].getValue()); + optionalField = false; + fieldType = Builder.shapeOf(cx, fieldValue); } else { SemType fieldSemType = Builder.from(cx, fieldType(fieldName)); if (!Core.isNever(Core.intersect(fieldSemType, Core.B_TYPE_TOP))) { @@ -302,19 +309,33 @@ public Optional shapeOf(Context cx, Object object) { if (fieldType.isEmpty()) { return Optional.empty(); } - fields[i] = - new MappingDefinition.Field(entries[i].getKey().toString(), fieldType.get(), readonlyField, false); + fields.add(new MappingDefinition.Field(fieldName, fieldType.get(), readonlyField, + optionalField)); + } + for (var field : getFields().values()) { + String name = field.getFieldName(); + if (handledFields.contains(name)) { + continue; + } + boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + SemType fieldType = Builder.from(cx, field.getFieldType()); + if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { + return Optional.of(neverType()); + } + fields.add(new MappingDefinition.Field(field.getFieldName(), fieldType, + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY), isOptional)); } MappingDefinition md = new MappingDefinition(); SemType semTypePart; + MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); if (isReadOnly()) { - semTypePart = md.defineMappingTypeWrapped(env, fields, neverType(), CELL_MUT_NONE); + semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, neverType(), CELL_MUT_NONE); } else { SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : neverType(); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { return Optional.empty(); } - semTypePart = md.defineMappingTypeWrapped(env, fields, rest, CELL_MUT_LIMITED); + semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); } return Optional.of(semTypePart); } @@ -328,4 +349,9 @@ 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); + } } From 5dd68f230ad7f8b136de94b6f16737a8a81ae76e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 15:27:56 +0530 Subject: [PATCH 055/178] Fixed readonly values not being handled correctly --- .../java/io/ballerina/runtime/internal/types/BRecordType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7749f5e8df27..b81a43f759f5 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 @@ -296,7 +296,7 @@ public Optional shapeOf(Context cx, Object object) { boolean readonlyField = fieldIsReadonly(fieldName); boolean optionalField = fieldIsOptional(fieldName); Optional fieldType; - if (readonlyField) { + if (isReadOnly() || readonlyField) { optionalField = false; fieldType = Builder.shapeOf(cx, fieldValue); } else { From 22a12750cd7770f3a5bd06bc238538d440f809a3 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 15:56:31 +0530 Subject: [PATCH 056/178] Fix synchronizing bug --- .../io/ballerina/runtime/internal/types/BType.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 fb126ca971aa..5391a4e90853 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 @@ -255,9 +255,14 @@ SemType createSemType(Context cx) { @Override public final SemType get(Context cx) { if (cachedSemType == null) { - cachedSemType = createSemType(cx); - if (isReadOnly()) { - cachedSemType = Core.intersect(cachedSemType, READONLY_WITH_B_TYPE); + synchronized (this) { + if (cachedSemType != null) { + return cachedSemType; + } + cachedSemType = createSemType(cx); + if (isReadOnly()) { + cachedSemType = Core.intersect(cachedSemType, READONLY_WITH_B_TYPE); + } } } return cachedSemType; From 9bb161d550a4a141ebf466567381b007e72dd64d Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 16 Jun 2024 17:54:37 +0530 Subject: [PATCH 057/178] Implement caching for shape of map --- .../api/types/semtype/BddAllOrNothing.java | 2 +- .../io/ballerina/runtime/api/values/BMap.java | 9 +++++++++ .../runtime/internal/types/BMapType.java | 11 +++++++--- .../runtime/internal/types/BRecordType.java | 5 +++++ .../runtime/internal/types/BType.java | 20 ++++++++++--------- .../runtime/internal/values/MapValueImpl.java | 12 +++++++++++ 6 files changed, 46 insertions(+), 13 deletions(-) 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 index 5a7762763e46..9b5e84fda3f2 100644 --- 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 @@ -34,7 +34,7 @@ private BddAllOrNothing(boolean all) { @Override public int hashCode() { - return 0xa11084 + (this == ALL ? 1 : 0); + return this == ALL ? 1 : 0; } @Override diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index 3a45b69f5288..3face15744d8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -194,4 +194,13 @@ public interface BMap extends BRefValue, BCollection { Object merge(BMap v2, boolean checkMergeability); void populateInitialValue(K key, V value); + + // FIXME: lift this to collection + default SemType shapeOf() { + return null; + } + + default void cacheShape(SemType semType) { + + } } 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 781c546a7acb..e38c417b2e77 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 @@ -213,10 +213,14 @@ public Optional shapeOf(Context cx, Object object) { return Optional.of(get(cx)); } BMap value = (BMap) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + int nFields = value.size(); MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); - boolean hasBTypePart = false; for (int i = 0; i < nFields; i++) { Optional valueType = Builder.shapeOf(cx, entries[i].getValue()); if (valueType.isEmpty()) { @@ -225,9 +229,10 @@ public Optional shapeOf(Context cx, Object object) { SemType fieldType = valueType.get(); fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); } - // TODO: cache this in the map value MappingDefinition md = new MappingDefinition(); - return Optional.of(md.defineMappingTypeWrapped(env, fields, Builder.neverType(), CELL_MUT_NONE)); + SemType semType = md.defineMappingTypeWrapped(env, fields, Builder.neverType(), CELL_MUT_NONE); + value.cacheShape(semType); + return Optional.of(semType); } private SemType getSemTypePart(SemType restType) { 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 b81a43f759f5..6f26be13285f 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 @@ -285,6 +285,10 @@ public void resetSemTypeCache() { @Override public Optional shapeOf(Context cx, Object object) { BMap value = (BMap) object; + SemType cachedSemType = value.shapeOf(); + if (cachedSemType != null) { + return Optional.of(cachedSemType); + } int nFields = value.size(); List fields = new ArrayList<>(nFields); Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); @@ -337,6 +341,7 @@ public Optional shapeOf(Context cx, Object object) { } semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); } + value.cacheShape(semTypePart); return Optional.of(semTypePart); } 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 5391a4e90853..de82fc1631c1 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 @@ -52,7 +52,7 @@ public abstract class BType implements Type, SubTypeData, BSemTypeSupplier { private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; - SemType cachedSemType = null; + private volatile SemType cachedSemType = null; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -254,17 +254,19 @@ SemType createSemType(Context cx) { @Override public final SemType get(Context cx) { - if (cachedSemType == null) { + SemType semType = cachedSemType; + if (semType == null) { synchronized (this) { - if (cachedSemType != null) { - return cachedSemType; - } - cachedSemType = createSemType(cx); - if (isReadOnly()) { - cachedSemType = Core.intersect(cachedSemType, READONLY_WITH_B_TYPE); + semType = cachedSemType; + if (semType == null) { + semType = createSemType(cx); + if (isReadOnly()) { + semType = Core.intersect(semType, READONLY_WITH_B_TYPE); + } + cachedSemType = semType; } } } - return cachedSemType; + return semType; } } 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 53b0e46f92f2..26006609bedb 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -100,6 +101,7 @@ public class MapValueImpl extends LinkedHashMap implements RefValue, private Type referredType; private final Map nativeData = new HashMap<>(); private Type iteratorNextReturnType; + private SemType shape; public MapValueImpl(TypedescValue typedesc) { this(typedesc.getDescribingType()); @@ -722,4 +724,14 @@ 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; + } } From 4afb2106e10bf8ce3b1fb27ee46d9944841b0695 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 17 Jun 2024 06:26:20 +0530 Subject: [PATCH 058/178] Fix expected error message Fix runtime type clash Due to reasons that are not entierly clear to me when we run all the unit tests some how types from `record_project_closed_rec_equiv` to clashed with types defined in other unit tests. As a temperary workaround I introduced a new package name for this. --- .../test-src/expressions/typecast/type-casting.bal | 3 +-- .../record/record_project_closed_rec_equiv/Ballerina.toml | 2 +- .../closed_record_equivalency.bal | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) 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..6d28ca85a3e5 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 @@ -1022,8 +1022,7 @@ function testCastOfReadonlyRecordNegative() { Bar|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: '(Foo & readonly)' cannot be cast to 'Bar'"; assertEquality("{ballerina}TypeCastError", err.message()); assertEquality(errMsg, checkpanic err.detail()["message"]); } 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; From 1a1279ede1bb24ef1d530e934627035823ba71d9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 20 Jun 2024 09:11:54 +0530 Subject: [PATCH 059/178] Refactor MappingAtomicType --- .../runtime/api/types/semtype/Builder.java | 5 - .../runtime/api/types/semtype/Context.java | 6 +- .../runtime/api/types/semtype/FieldPair.java | 27 +++ .../runtime/api/types/semtype/FieldPairs.java | 165 ++++++++++++++++ .../api/types/semtype/MappingAtomicType.java | 23 +++ .../api/types/semtype/MappingProj.java | 5 - .../runtime/api/values/BCollection.java | 10 + .../io/ballerina/runtime/api/values/BMap.java | 10 - .../runtime/internal/types/BArrayType.java | 10 +- .../runtime/internal/types/BRecordType.java | 1 - .../internal/types/BStructureType.java | 1 - .../runtime/internal/types/BTupleType.java | 9 +- .../runtime/internal/types/BType.java | 1 - .../internal/types/BTypeConverter.java | 1 - .../internal/types/semtype/BMappingProj.java | 79 +------- .../types/semtype/BMappingSubType.java | 183 +----------------- .../types/semtype/BStringSubType.java | 60 +++--- .../internal/types/semtype/Common.java | 3 +- .../runtime/internal/values/ArrayValue.java | 3 - .../internal/values/ArrayValueImpl.java | 15 +- .../internal/values/TupleValueImpl.java | 12 ++ 21 files changed, 302 insertions(+), 327 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java 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 index b53e83955907..41ab486b657c 100644 --- 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 @@ -40,7 +40,6 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; @@ -170,10 +169,6 @@ public static SemType cell() { return from(BT_CELL); } - protected static SemType cellSemTypeInner() { - return CELL_SEMTYPE_INNER; - } - public static SemType inner() { return INNER; } 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 index a6e7ed070f44..163c2923e99b 100644 --- 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 @@ -136,11 +136,13 @@ public void emptyProvisionalTypes(int startingSize) { if (startingSize != 0) { return; } - // FIXME: reset all (if we have cycles we will reset the top one as well) if (resetProvisionalTypes) { + BType head = provisionalTypes.get(0); for (int i = 1; i < provisionalTypes.size(); i++) { BType type = provisionalTypes.get(i); - // TODO: we should be able to be more selective about resetting the cache + if (type == head) { + continue; + } type.resetSemTypeCache(); } } 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..646310006938 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java @@ -0,0 +1,27 @@ +/* + * 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; + +public record FieldPair(String name, SemType type1, SemType type2, Integer index1, Integer index2) { + + public static FieldPair create(String name, SemType type1, SemType type2, Integer index1, + Integer index2) { + return new FieldPair(name, type1, type2, index1, 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..2c49b855cb38 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java @@ -0,0 +1,165 @@ +/* + * 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 java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; + +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 = 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 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/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java index 2c1b27343941..1f7e31947db5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java @@ -19,11 +19,34 @@ package io.ballerina.runtime.api.types.semtype; +import java.util.ArrayList; +import java.util.Collection; + import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER_RO; +import static io.ballerina.runtime.api.types.semtype.Core.cellInner; +import static io.ballerina.runtime.api.types.semtype.Core.intersectMemberSemTypes; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; public record MappingAtomicType(String[] names, SemType[] types, SemType rest) implements AtomicType { public static final MappingAtomicType MAPPING_ATOMIC_RO = new MappingAtomicType( new String[]{}, new SemType[]{}, CELL_SEMTYPE_INNER_RO ); + + 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 = intersectMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + if (isNever(cellInner(fieldPair.type1()))) { + return null; + + } + types.add(t); + } + SemType rest = intersectMemberSemTypes(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/api/types/semtype/MappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java index 4ee596fa52e1..7ed7e47bed96 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -21,8 +21,6 @@ import io.ballerina.runtime.internal.types.semtype.BMappingProj; -import static io.ballerina.runtime.api.types.semtype.Core.diff; - public final class MappingProj { private MappingProj() { @@ -32,7 +30,4 @@ public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k return BMappingProj.mappingMemberTypeInnerVal(cx, t, k); } - public static SemType mappingMemberType(Context cx, SemType t, SemType k) { - return BMappingProj.mappingMemberType(cx, t, k); - } } 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/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index 3face15744d8..1eb65cc3fab8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -18,7 +18,6 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Collection; import java.util.Map; @@ -194,13 +193,4 @@ public interface BMap extends BRefValue, BCollection { Object merge(BMap v2, boolean checkMergeability); void populateInitialValue(K key, V value); - - // FIXME: lift this to collection - default SemType shapeOf() { - return null; - } - - default void cacheShape(SemType 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 baefb92bdf48..5ebb330ebb7a 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 @@ -264,6 +264,10 @@ public Optional shapeOf(Context cx, Object object) { return Optional.of(get(cx)); } BArray value = (BArray) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } int size = value.size(); SemType[] memberTypes = new SemType[size]; for (int i = 0; i < size; i++) { @@ -274,8 +278,8 @@ public Optional shapeOf(Context cx, Object object) { memberTypes[i] = memberType.get(); } ListDefinition ld = new ListDefinition(); - // TODO: cache this in the array value - return Optional.of( - ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); + SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE); + value.cacheShape(semType); + return Optional.of(semType); } } 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 6f26be13285f..9c953e88811e 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 @@ -46,7 +46,6 @@ import io.ballerina.runtime.internal.values.ReadOnlyUtils; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; 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 87d6ba82d1c6..3b5575b8a353 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 @@ -20,7 +20,6 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.StructureType; -import io.ballerina.runtime.api.types.semtype.SemType; import java.util.HashMap; import java.util.Map; 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 20f63b356f87..c249c02dd4a8 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 @@ -362,6 +362,10 @@ public Optional shapeOf(Context cx, Object object) { return Optional.of(get(cx)); } BArray value = (BArray) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } int size = value.size(); SemType[] memberTypes = new SemType[size]; for (int i = 0; i < size; i++) { @@ -373,7 +377,8 @@ public Optional shapeOf(Context cx, Object object) { } ListDefinition ld = new ListDefinition(); // TODO: cache this in the array value - return Optional.of( - ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE)); + SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE); + value.cacheShape(semType); + return Optional.of(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 de82fc1631c1..0e4b878aa0a9 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 @@ -31,7 +31,6 @@ import io.ballerina.runtime.internal.types.semtype.SubTypeData; import java.util.Objects; -import java.util.function.Supplier; /** * {@code BType} represents a type in Ballerina. diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index d117bda6c26a..4ab57607854c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -127,7 +127,6 @@ private static BTypeParts split(Context cx, Type type) { return splitReadonly(readonlyType); } else if (type instanceof BFiniteType finiteType) { return splitFiniteType(cx, finiteType); - // FIXME: introduce a marker type for these } else if (type instanceof PartialSemTypeSupplier supplier) { return splitSemTypeSupplier(cx, supplier); } else { 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 index 24ca59ebf065..39ca3732dd58 100644 --- 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 @@ -29,7 +29,6 @@ import io.ballerina.runtime.api.types.semtype.SemType; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; @@ -37,87 +36,12 @@ 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; -import static io.ballerina.runtime.internal.types.semtype.BStringSubType.stringSubtypeListCoverage; public final class BMappingProj { private BMappingProj() { } - // TODO: refactor things to avoid duplication (Bench method ref vs duplication) - public static SemType mappingMemberType(Context cx, SemType t, SemType k) { - // FIXME: cell containting undef - SemType result = diff(mappingMemberTypeAux(cx, t, k), Builder.undef()); - assert Core.isSubType(cx, result, Builder.cell()) : "map field type should be wrapped in a cell"; - return result; - } - - // Same as mappingMemberTypeInner except don't remove the cell - private static SemType mappingMemberTypeAux(Context cx, SemType t, SemType k) { - if (t.some() == 0) { - SemType res = (t.all() & Builder.mappingType().all()) != 0 ? (Builder.valType()) : Builder.undef(); - return Builder.cellContaining(cx.env, res); - } else { - SubTypeData keyData = stringSubtype(k); - if (isNothingSubtype(keyData)) { - return Builder.cellContaining(cx.env, Builder.undef()); - } - return bddMappingMemberType(cx, (Bdd) getComplexSubtypeData(t, BT_MAPPING), keyData, - Builder.cell()); - } - } - - static SemType bddMappingMemberType(Context cx, Bdd b, SubTypeData key, SemType accum) { - if (b instanceof BddAllOrNothing allOrNothing) { - return allOrNothing.isAll() ? accum : Builder.neverType(); - } else { - BddNode bdd = (BddNode) b; - return Core.union( - bddMappingMemberType(cx, bdd.left(), key, - Core.intersect(mappingAtomicMemberType(cx.mappingAtomType(bdd.atom()), key), - accum)), - Core.union(bddMappingMemberType(cx, bdd.middle(), key, accum), - bddMappingMemberType(cx, bdd.right(), key, accum))); - } - } - - static SemType mappingAtomicMemberType(MappingAtomicType atomic, SubTypeData key) { - SemType memberType = null; - for (SemType ty : mappingAtomicApplicableMemberTypes(atomic, key)) { - if (memberType == null) { - memberType = ty; - } else { - memberType = Core.union(memberType, ty); - } - } - // FIXME: wrap in cell - return memberType == null ? Builder.undef() : memberType; - } - - static List mappingAtomicApplicableMemberTypes(MappingAtomicType atomic, SubTypeData key) { - // FIXME: - List types = new ArrayList<>(atomic.types().length); - Collections.addAll(types, atomic.types()); - - List memberTypes = new ArrayList<>(); - SemType rest = atomic.rest(); - if (isAllSubtype(key)) { - memberTypes.addAll(types); - memberTypes.add(rest); - } else { - BStringSubType.StringSubtypeListCoverage coverage = - stringSubtypeListCoverage((BStringSubType.StringSubTypeData) key, - atomic.names()); - for (int index : coverage.indices()) { - memberTypes.add(types.get(index)); - } - if (!coverage.isSubType()) { - memberTypes.add(rest); - } - } - return memberTypes; - } - public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { return diff(mappingMemberTypeInner(cx, t, k), Builder.undef()); } @@ -177,8 +101,7 @@ static List mappingAtomicApplicableMemberTypesInner(MappingAtomicType a memberTypes.add(rest); } else { BStringSubType.StringSubtypeListCoverage coverage = - stringSubtypeListCoverage((BStringSubType.StringSubTypeData) key, - atomic.names()); + ((BStringSubType.StringSubTypeData) key).stringSubtypeListCoverage(atomic.names()); for (int index : coverage.indices()) { memberTypes.add(types.get(index)); } 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 index d86c9f5c36ba..f75da4f894bb 100644 --- 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 @@ -24,24 +24,17 @@ 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.FieldPair; +import io.ballerina.runtime.api.types.semtype.FieldPairs; import io.ballerina.runtime.api.types.semtype.MappingAtomicType; 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.Iterator; -import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; import static io.ballerina.runtime.api.types.semtype.Builder.MAPPING_ATOMIC_INNER; -import static io.ballerina.runtime.api.types.semtype.Core.cellInner; -import static io.ballerina.runtime.api.types.semtype.Core.intersectMemberSemTypes; -import static io.ballerina.runtime.api.types.semtype.Core.isNever; public class BMappingSubType extends SubType implements DelegatedSubType { @@ -107,7 +100,8 @@ private static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Co if (p == null) { break; } else { - MappingAtomicType m = intersectMapping(cx.env, combined, cx.mappingAtomType(p.atom())); + MappingAtomicType m = + combined.intersectMapping(cx.env, cx.mappingAtomType(p.atom())); if (m == null) { return true; } else { @@ -135,8 +129,7 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju if (!Core.isEmpty(cx, Core.diff(pos.rest(), neg.rest()))) { return mappingInhabited(cx, pos, negList.next()); } - FieldPairs pairing = new FieldPairs(pos, neg); - for (FieldPair fieldPair : pairing) { + for (FieldPair fieldPair : new FieldPairs(pos, neg)) { SemType d = Core.diff(fieldPair.type1(), fieldPair.type2()); if (!Core.isEmpty(cx, d)) { MappingAtomicType mt; @@ -144,7 +137,6 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju // the posType came from the rest type mt = insertField(pos, fieldPair.name(), d); } else { - // FIXME: if (Core.isSubType(cx, fieldPair.type1(), Builder.cellContaining(cx.env, Builder.undef()))) { continue; } @@ -188,24 +180,6 @@ private static String[] shallowCopyStrings(String[] v, int newLength) { return Arrays.copyOf(v, newLength); } - // FIXME: make this an instance method in mappingAtomicType - private static MappingAtomicType intersectMapping(Env env, MappingAtomicType m1, MappingAtomicType m2) { - int expectedSize = Integer.min(m1.types().length, m2.types().length); - List names = new ArrayList<>(expectedSize); - List types = new ArrayList<>(expectedSize); - FieldPairs pairing = new FieldPairs(m1, m2); - for (FieldPair fieldPair : pairing) { - names.add(fieldPair.name()); - SemType t = intersectMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); - if (isNever(cellInner(fieldPair.type1()))) { - return null; - } - types.add(t); - } - SemType rest = intersectMemberSemTypes(env, m1.rest(), m2.rest()); - return new MappingAtomicType(names.toArray(String[]::new), types.toArray(SemType[]::new), rest); - } - @Override public SubTypeData data() { throw new IllegalStateException("unimplemented"); @@ -235,151 +209,4 @@ public int hashCode() { return Objects.hashCode(inner); } - private static class FieldPairs implements Iterable { - - MappingAtomicType m1; - MappingAtomicType m2; - public MappingPairIterator itr; - - public FieldPairs(MappingAtomicType m1, MappingAtomicType m2) { - this.m1 = m1; - this.m2 = m2; - } - - @Override - public Iterator iterator() { - itr = new MappingPairIterator(m1, m2); - return itr; - } - } - - private record FieldPair(String name, SemType type1, SemType type2, Integer index1, Integer index2) { - - public static FieldPair create(String name, SemType type1, SemType type2, Integer index1, - Integer index2) { - return new FieldPair(name, type1, type2, index1, index2); - } - } - - // TODO: refact - private static 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 = 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 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/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 index ef8aab8dfdb6..493ba9f3fd91 100644 --- 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 @@ -164,38 +164,6 @@ public SubTypeData data() { return data; } - // FIXME: make this an instance method - // Returns a description of the relationship between a StringSubtype and a list of strings - // `values` must be ordered. - static StringSubtypeListCoverage stringSubtypeListCoverage(StringSubTypeData stringData, String[] values) { - List indices = new ArrayList<>(); - ValueData ch = stringData.chars(); - ValueData nonChar = stringData.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); - } - static void stringListIntersect(String[] values, String[] target, List indices) { int i1 = 0; int i2 = 0; @@ -237,6 +205,34 @@ private enum ComparisonResult { 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) { 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 index 7917a60eb69b..d4dda91cf99d 100644 --- 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 @@ -24,8 +24,7 @@ public final class Common { private Common() { } - // FIXME: not sure this is needed (java string comparision should support unicode) - static boolean codePointCompare(String s1, String s2) { + public static boolean codePointCompare(String s1, String s2) { if (s1.equals(s2)) { return false; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java index 492c8aa60d87..af4febe73476 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValue.java @@ -17,9 +17,6 @@ */ package io.ballerina.runtime.internal.values; -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.BArray; /** 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 a35e41c5a2d4..f5837b51f8ed 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,9 +24,6 @@ import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.ArrayType.ArrayState; 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.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; @@ -90,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) { @@ -1410,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/TupleValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java index 922b1ce59a21..b7a3c371c153 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.creators.ErrorCreator; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; +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; + } } From ff8e15f234f2f7730969697f817ffef804b3a01c Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 20 Jun 2024 17:33:31 +0530 Subject: [PATCH 060/178] Fix cyclic typing issue --- .../java/io/ballerina/runtime/api/types/semtype/Context.java | 4 ---- 1 file changed, 4 deletions(-) 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 index 163c2923e99b..699890713d6e 100644 --- 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 @@ -137,12 +137,8 @@ public void emptyProvisionalTypes(int startingSize) { return; } if (resetProvisionalTypes) { - BType head = provisionalTypes.get(0); for (int i = 1; i < provisionalTypes.size(); i++) { BType type = provisionalTypes.get(i); - if (type == head) { - continue; - } type.resetSemTypeCache(); } } From c79f1bcf7343532bed48ce550d7628b44c12d739 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sat, 22 Jun 2024 13:27:16 +0530 Subject: [PATCH 061/178] Switch to non sparse array to represent SubTypeData --- .../runtime/api/types/semtype/Builder.java | 11 ++-- .../runtime/api/types/semtype/Core.java | 61 ++++++++++++------- .../runtime/api/types/semtype/SemType.java | 9 +++ .../types/semtype/SubtypePairIterator.java | 20 +++--- 4 files changed, 62 insertions(+), 39 deletions(-) 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 index 41ab486b657c..2649b73f452d 100644 --- 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 @@ -227,9 +227,10 @@ public static SemType basicTypeUnion(int bitset) { public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { assert !(subType instanceof Bdd) : "BDD should always be wrapped with a delegate"; - SubType[] subTypes = initializeSubtypeArray(); - subTypes[basicTypeCode.code()] = subType; - return SemType.from(0, 1 << basicTypeCode.code(), subTypes); + int some = 1 << basicTypeCode.code(); + SubType[] subTypes = initializeSubtypeArray(some); + subTypes[0] = subType; + return SemType.from(0, some, subTypes); } public static SemType intConst(long value) { @@ -275,8 +276,8 @@ public static SemType stringConst(String value) { return basicSubType(BasicTypeCode.BT_STRING, subType); } - static SubType[] initializeSubtypeArray() { - return new SubType[CODE_B_TYPE + 2]; + static SubType[] initializeSubtypeArray(int some) { + return new SubType[Integer.bitCount(some)]; } public static Optional shapeOf(Context cx, Object object) { 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 index 39db9ab3ef28..209f46449432 100644 --- 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 @@ -24,6 +24,8 @@ import io.ballerina.runtime.internal.types.semtype.SubtypePair; import io.ballerina.runtime.internal.types.semtype.SubtypePairs; +import java.util.Arrays; +import java.util.Objects; import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_B_TYPE; @@ -80,7 +82,9 @@ public static SemType diff(SemType t1, SemType t2) { if (some == 0) { return SemType.from(all); } - SubType[] subtypes = Builder.initializeSubtypeArray(); + 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(); @@ -96,18 +100,21 @@ public static SemType diff(SemType t1, SemType t2) { if (data.isAll()) { all |= 1 << code; some &= ~(1 << code); + filterNulls = true; } else if (data.isNothing()) { some &= ~(1 << code); + filterNulls = true; } else { - subtypes[code] = data; + subtypes[i] = data; } + i++; } - return SemType.from(all, some, subtypes); + return SemType.from(all, some, filterNulls ? filterNulls(subtypes) : subtypes); } public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { assert (t.some() & (1 << code.code())) != 0; - SubType subType = t.subTypeData()[code.code()]; + SubType subType = t.subTypeByCode(code.code()); if (subType instanceof DelegatedSubType wrapper) { return wrapper.inner(); } @@ -148,7 +155,9 @@ public static SemType union(SemType t1, SemType t2) { if (some == 0) { return Builder.basicTypeUnion(all); } - SubType[] subtypes = Builder.initializeSubtypeArray(); + 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(); @@ -162,16 +171,22 @@ public static SemType union(SemType t1, SemType t2) { data = data1.union(data2); } if (data.isAll()) { + filterNulls = true; all |= 1 << code; some &= ~(1 << code); } else { - subtypes[code] = data; + subtypes[i] = data; } + i++; } if (some == 0) { return SemType.from(all); } - return SemType.from(all, some, subtypes); + return SemType.from(all, some, filterNulls ? filterNulls(subtypes) : subtypes); + } + + private static SubType[] filterNulls(SubType[] subtypes) { + return Arrays.stream(subtypes).filter(Objects::nonNull).toArray(SubType[]::new); } public static SemType intersect(SemType t1, SemType t2) { @@ -207,7 +222,9 @@ public static SemType intersect(SemType t1, SemType t2) { return SemType.from(all); } - SubType[] subtypes = Builder.initializeSubtypeArray(); + 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(); @@ -223,15 +240,17 @@ public static SemType intersect(SemType t1, SemType t2) { } if (!data.isNothing()) { - subtypes[code] = data; + subtypes[i] = data; } else { some &= ~(1 << code); + filterNulls = true; } + i++; } if (some == 0) { return SemType.from(all); } - return SemType.from(all, some, subtypes); + return SemType.from(all, some, filterNulls ? filterNulls(subtypes) : subtypes); } public static boolean isEmpty(Context cx, SemType t) { @@ -242,9 +261,7 @@ public static boolean isEmpty(Context cx, SemType t) { return false; } for (SubType subType : t.subTypeData()) { - if (subType == null) { - continue; - } + assert subType != null : "subtype array must not be sparse"; if (!subType.isEmpty(cx)) { return false; } @@ -252,8 +269,8 @@ public static boolean isEmpty(Context cx, SemType t) { return true; } - public static SemType complement(SemType t1) { - throw new IllegalStateException("Unimplemented"); + public static SemType complement(SemType t) { + return diff(Builder.valType(), t); } public static boolean isNever(SemType t) { @@ -262,12 +279,12 @@ public static boolean isNever(SemType t) { public static boolean isSubType(Context cx, SemType t1, SemType t2) { // IF t1 and t2 are not pure semtypes calling this is an undefined - SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); - if (cached != SemType.CachedResult.NOT_FOUND) { - return cached == SemType.CachedResult.TRUE; - } +// SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); +// if (cached != SemType.CachedResult.NOT_FOUND) { +// return cached == SemType.CachedResult.TRUE; +// } boolean result = isEmpty(cx, diff(t1, t2)); - t1.cacheSubTypeRelation(t2, result); +// t1.cacheSubTypeRelation(t2, result); return result; } @@ -298,7 +315,9 @@ public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { if (s.some == 0) { return AllOrNothing.NOTHING; } - return s.subTypeData()[code.code()].data(); + SubType subType = s.subTypeByCode(code.code()); + assert subType != null; + return subType.data(); } public static boolean containsBasicType(SemType t1, SemType t2) { 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 index 66802bf2be7e..be6836d51d7f 100644 --- 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 @@ -169,4 +169,13 @@ private CachedResult getCachedResult(int tid) { return cachedData ? CachedResult.TRUE : CachedResult.FALSE; } } + + 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)]; + } } 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 index 0676803fcaf1..0acffd7f3a7f 100644 --- 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 @@ -34,12 +34,12 @@ 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_B_TYPE + 1; - private final int bits; + private final int some; private final SemType t1; private final SemType t2; - SubtypePairIterator(SemType t1, SemType t2, int bits) { - this.bits = bits; + SubtypePairIterator(SemType t1, SemType t2, int some) { + this.some = some; this.t1 = t1; this.t2 = t2; incrementIndex(); @@ -51,22 +51,16 @@ public boolean hasNext() { } private void incrementIndex() { - int rest = bits >> index; + int rest = some >> index; int offset = Integer.numberOfTrailingZeros(rest); index += offset; } - private SubType subTypeAtIndex(SemType t, int index) { - if ((t.some() & (1 << index)) != 0) { - return t.subTypeData()[index]; - } - return null; - } - @Override public SubtypePair next() { - SubType subType1 = subTypeAtIndex(t1, index); - SubType subType2 = subTypeAtIndex(t2, index); + SubType subType1 = t1.subTypeByCode(index); + SubType subType2 = t2.subTypeByCode(index); + assert (subType1 == null || subType2 == null) || (subType1.getClass().equals(subType2.getClass())); int typeCode = index; index++; incrementIndex(); From 1f3711a3c5eac69a521a3d4a45785f80465081cb Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 24 Jun 2024 06:55:23 +0530 Subject: [PATCH 062/178] Refact: introduce explicit empty class for TypeCheckCache --- .../runtime/api/types/semtype/SemType.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) 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 index be6836d51d7f..30514e807e8b 100644 --- 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 @@ -56,7 +56,7 @@ protected SemType(int all, int some, SubType[] subTypeData) { this.resultCache = new TypeCheckResultCache(); } else { useCache = false; - this.resultCache = null; + this.resultCache = TypeCheckResultCache.EMPTY; } } @@ -151,17 +151,18 @@ void cacheSubTypeRelation(SemType other, boolean result) { } } - private static final class TypeCheckResultCache { + private static sealed class TypeCheckResultCache { + private static final TypeCheckResultCache EMPTY = new EmptyTypeCheckResultCache(); private static final int CACHE_LIMIT = 100; // See if we can use an identity hashmap on semtypes instead of tid private Map cache = new HashMap<>(); - private void cacheResult(int tid, boolean result) { + protected void cacheResult(int tid, boolean result) { cache.put((long) tid, result); } - private CachedResult getCachedResult(int tid) { + protected CachedResult getCachedResult(int tid) { Boolean cachedData = cache.get((long) tid); if (cachedData == null) { return CachedResult.NOT_FOUND; @@ -170,6 +171,19 @@ private CachedResult getCachedResult(int tid) { } } + private static final class EmptyTypeCheckResultCache extends TypeCheckResultCache { + + @Override + public void cacheResult(int tid, boolean result) { + throw new UnsupportedOperationException("Empty cache"); + } + + @Override + public CachedResult getCachedResult(int tid) { + throw new UnsupportedOperationException("Empty cache"); + } + } + public final SubType subTypeByCode(int code) { if ((some() & (1 << code)) == 0) { return null; From cdab9f7f5231ad148cc995ed9da418cee82c7bc5 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 17 Jul 2024 13:44:19 +0530 Subject: [PATCH 063/178] Implement function semtype --- .../runtime/api/types/semtype/AtomicType.java | 2 +- .../runtime/api/types/semtype/Builder.java | 10 ++ .../runtime/api/types/semtype/Context.java | 9 + .../runtime/api/types/semtype/Env.java | 41 ++++- .../api/types/semtype/FunctionAtomicType.java | 23 +++ .../runtime/api/types/semtype/RecAtom.java | 2 +- .../runtime/internal/TypeChecker.java | 4 +- .../runtime/internal/types/BFunctionType.java | 104 ++++++++++- .../internal/types/BTypeConverter.java | 2 +- .../types/semtype/BFunctionSubType.java | 168 ++++++++++++++++++ .../types/semtype/FunctionDefinition.java | 66 +++++++ .../types/semtype/FunctionQualifiers.java | 73 ++++++++ .../runtime/internal/values/FPValue.java | 8 + .../port/test/RuntimeSemTypeResolver.java | 49 +++++ .../semtype/port/test/SemTypeTest.java | 11 -- 15 files changed, 555 insertions(+), 17 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java 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 index ed193ee129c8..f2286e2fd61d 100644 --- 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 @@ -23,6 +23,6 @@ * * @since 2201.10.0 */ -public interface AtomicType { +public sealed interface AtomicType permits CellAtomicType, FunctionAtomicType, ListAtomicType, MappingAtomicType { } 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 index 2649b73f452d..713bf1d19951 100644 --- 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 @@ -36,6 +36,7 @@ import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.DecimalValue; +import io.ballerina.runtime.internal.values.FPValue; import java.math.BigDecimal; import java.util.ArrayList; @@ -43,6 +44,7 @@ import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FUNCTION; 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.CODE_B_TYPE; @@ -70,6 +72,7 @@ public final class Builder { valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED ); public static final SemType MAPPING = from(BT_MAPPING); + public static final SemType FUNCTION = from(BT_FUNCTION); static final TypeAtom ATOM_CELL_VAL = createTypeAtom(0, CELL_ATOMIC_VAL); static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED @@ -299,6 +302,9 @@ public static Optional shapeOf(Context cx, Object object) { return typeOfArray(cx, arrayValue); } else if (object instanceof BMap mapValue) { return typeOfMap(cx, mapValue); + } else if (object instanceof FPValue fpValue) { + // TODO: this is a hack to support partial function types, remove when semtypes are fully implemented + return Optional.of(from(cx, fpValue.getType())); } return Optional.empty(); } @@ -349,6 +355,10 @@ public static SemType mappingType() { return MAPPING; } + public static SemType functionType() { + return FUNCTION; + } + public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; if (memo != null) { 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 index 699890713d6e..0842e8809e65 100644 --- 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 @@ -39,6 +39,7 @@ public final class Context { public final Env env; public final Map listMemo = new HashMap<>(); public final Map mappingMemo = new HashMap<>(); + public final Map functionMemo = new HashMap<>(); private final List provisionalTypes = new ArrayList<>(); private boolean resetProvisionalTypes = false; @@ -122,6 +123,14 @@ public MappingAtomicType mappingAtomType(Atom atom) { } } + public FunctionAtomicType functionAtomicType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } + public int addProvisionalType(BType type) { int currentSize = provisionalTypes.size(); provisionalTypes.add(type); 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 index 7210d873e2ba..90ec5ff34cf1 100644 --- 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 @@ -50,13 +50,18 @@ public final class Env { private final ReadWriteLock recMapLock = new ReentrantReadWriteLock(); private final List recMappingAtoms; + private final ReadWriteLock recFunctionLock = new ReentrantReadWriteLock(); + private final List recFunctionAtoms; + private final Map cellTypeCache = new ConcurrentHashMap<>(); private Env() { this.atomTable = new HashMap<>(); this.recListAtoms = new ArrayList<>(); - recListAtoms.add(LIST_ATOMIC_RO); this.recMappingAtoms = new ArrayList<>(); + this.recFunctionAtoms = new ArrayList<>(); + + recListAtoms.add(LIST_ATOMIC_RO); recMappingAtoms.add(MAPPING_ATOMIC_RO); this.cellAtom(Builder.CELL_ATOMIC_VAL); @@ -180,6 +185,40 @@ public MappingAtomicType getRecMappingAtomType(RecAtom recAtom) { } } + 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.writeLock().lock(); + try { + this.recFunctionAtoms.set(rec.index(), atomicType); + } finally { + recFunctionLock.writeLock().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) { } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java new file mode 100644 index 000000000000..423f9a7c083f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java @@ -0,0 +1,23 @@ +/* + * 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; + +public record FunctionAtomicType(SemType paramType, SemType retType, SemType qualifiers) implements AtomicType { + +} 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 index a15f7caccd62..836b89bc50f2 100644 --- 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 @@ -24,7 +24,7 @@ * * @since 2201.10.0 */ -public class RecAtom implements Atom { +public final class RecAtom implements Atom { public final int index; private static final int BDD_REC_ATOM_READONLY = 0; 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 245b4cf209c6..479b2da38f96 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 @@ -58,6 +58,7 @@ import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.ErrorValue; +import io.ballerina.runtime.internal.values.FPValue; import io.ballerina.runtime.internal.values.HandleValue; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.RegExpValue; @@ -591,7 +592,8 @@ private static TypeCheckResult isSubTypeInner(Context cx, Object sourceValue, Se private static TypeCheckResult isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); if (sourceSingletonType.isEmpty()) { - return Core.containsBasicType(target, B_TYPE_TOP) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; + return Core.containsBasicType(target, B_TYPE_TOP) && !(sourceValue instanceof FPValue) ? + TypeCheckResult.MAYBE : TypeCheckResult.FALSE; } SemType singletonType = sourceSingletonType.get(); return isSubTypeInner(singletonType, target); 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 9916e0ba4da4..348e924890cd 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,6 +25,15 @@ import io.ballerina.runtime.api.types.FunctionType; import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import java.util.Arrays; @@ -33,12 +42,16 @@ * * @since 0.995.0 */ -public class BFunctionType extends BAnnotatableType implements FunctionType { +public class BFunctionType extends BAnnotatableType implements FunctionType, PartialSemTypeSupplier { public Type restType; 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 FunctionDefinition defn; public BFunctionType(Module pkg) { super("function ()", pkg, Object.class); @@ -220,4 +233,93 @@ public Type getReturnType() { public long getFlags() { return flags; } + + private static SemType createIsolatedTop(Env env) { + FunctionDefinition fd = new FunctionDefinition(); + SemType ret = Builder.valType(); + // FIXME: add a comment explaining why we are using neverType here + return fd.define(env, Builder.neverType(), ret, FunctionQualifiers.create(true, false)); + } + + @Override + synchronized SemType createSemType(Context cx) { + if (isFunctionTop()) { + SemType topType = getTopType(); + return Core.union(topType, BTypeConverter.wrapAsPureBType(this)); + } + if (defn != null) { + return defn.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + this.defn = fd; + SemType[] params = new SemType[parameters.length]; + boolean hasBType = false; + for (int i = 0; i < parameters.length; i++) { + var result = getSemType(cx, parameters[i].type); + hasBType = hasBType || result.hasBTypePart; + params[i] = result.pureSemTypePart; + } + SemType rest; + if (restType instanceof BArrayType arrayType) { + var result = getSemType(cx, arrayType.getElementType()); + hasBType = hasBType || result.hasBTypePart; + rest = result.pureSemTypePart; + } else { + rest = Builder.neverType(); + } + + SemType returnType; + if (retType != null) { + var result = getSemType(cx, retType); + hasBType = hasBType || result.hasBTypePart; + returnType = result.pureSemTypePart; + } else { + returnType = Builder.nilType(); + } + ListDefinition paramListDefinition = new ListDefinition(); + SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, + CellAtomicType.CellMutability.CELL_MUT_NONE); + SemType result = fd.define(env, paramType, returnType, getQualifiers()); + if (hasBType) { + cx.markProvisionTypeReset(); + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + return Core.union(result, bTypePart); + } + return result; + } + + private SemType getTopType() { + if (SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED)) { + return ISOLATED_TOP; + } + return Builder.functionType(); + } + + private record SemTypeResult(boolean hasBTypePart, SemType pureSemTypePart) { + + } + + private FunctionQualifiers getQualifiers() { + return FunctionQualifiers.create(SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED), + SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); + } + + // FIXME: consider moving this to builder + private static SemTypeResult getSemType(Context cx, Type type) { + SemType semType = Builder.from(cx, type); + if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { + return new SemTypeResult(true, Core.intersect(semType, Core.SEMTYPE_TOP)); + } + return new SemTypeResult(false, semType); + } + + private boolean isFunctionTop() { + return parameters == null && restType == null && retType == null; + } + + @Override + public void resetSemTypeCache() { + super.resetSemTypeCache(); + defn = null; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 4ab57607854c..981fc4e453d8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -47,7 +47,7 @@ private BTypeConverter() { private static final SemType implementedTypes = unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType(), - Builder.mappingType()); + Builder.mappingType(), Builder.functionType()); private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); 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..be0617fb4fd9 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java @@ -0,0 +1,168 @@ +/* + * 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.FunctionAtomicType; +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; + +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, null, null, 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.valType(); + } + 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.neverType(); + } + 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.neverType(); + } + 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/FunctionDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java new file mode 100644 index 000000000000..381e6fde53a0 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java @@ -0,0 +1,66 @@ +/* + * 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.Env; +import io.ballerina.runtime.api.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +public class FunctionDefinition implements Definition { + + private RecAtom rec; + private 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); + } + return this.createSemType(atom); + } + +} 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..f62fece7667f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.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.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +public final class FunctionQualifiers { + + private final static 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.booleanConst(true) : Builder.booleanType(), + transactional ? Builder.booleanType() : Builder.booleanConst(false) + }; + semType = ld.defineListTypeWrapped(env, members, 2, Builder.neverType(), + 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/values/FPValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java index beedcc984d45..be7da5375695 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,6 +20,9 @@ import io.ballerina.runtime.api.async.StrandMetadata; import io.ballerina.runtime.api.constants.RuntimeConstants; 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.BFunctionPointer; import io.ballerina.runtime.api.values.BFuture; import io.ballerina.runtime.api.values.BLink; @@ -128,4 +131,9 @@ public BTypedesc getTypedesc() { public String toString() { return RuntimeConstants.EMPTY; } + + @Override + public SemType widenedType(Context cx) { + return Builder.functionType(); + } } 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 index a050f904a5a6..6853ff4f2c77 100644 --- 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 @@ -24,6 +24,8 @@ import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.semtype.Definition; +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 io.ballerina.runtime.internal.types.semtype.MappingDefinition; import org.ballerinalang.model.elements.Flag; @@ -38,6 +40,7 @@ import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; 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.BLangRecordTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; @@ -49,6 +52,7 @@ import java.math.BigDecimal; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -62,6 +66,7 @@ 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.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; class RuntimeSemTypeResolver extends SemTypeResolver { @@ -122,10 +127,54 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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); default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); }; } + 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.neverType(), Builder.valType(), + FunctionQualifiers.create( + td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL))); + } + return Builder.functionType(); + } + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(td, fd); + List params = + td.params.stream().map(param -> resolveTypeDesc(cx, mod, defn, depth + 1, param.typeNode)) + .toList(); + SemType rest; + if (td.restParam == null) { + rest = Builder.neverType(); + } else { + BLangArrayType restArrayType = (BLangArrayType) td.restParam.typeNode; + rest = resolveTypeDesc(cx, mod, defn, depth + 1, restArrayType.elemtype); + } + SemType returnType = td.returnTypeNode != null ? resolveTypeDesc(cx, mod, defn, depth + 1, td.returnTypeNode) : + Builder.nilType(); + 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 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(); 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 index fded9d1e4887..5167363c4b54 100644 --- 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 @@ -232,16 +232,6 @@ public Object[] runtimeFileNameProviderFunc() { "table-readonly-t.bal", "table-t.bal" )); - Predicate functionFilter = createRuntimeFileNameFilter(Set.of( - "function2-tv.bal", - "function-intersection-tv.bal", - "function-param-tv.bal", - "function-rec-tv.bal", - "function-rest-tv.bal", - "function-tv.bal", - "function-union-tv.bal", - "func-quals-tv.bal" - )); Predicate xmlFilter = createRuntimeFileNameFilter(Set.of( "xml-complex-ro-tv.bal", "xml-complex-rw-tv.bal", @@ -259,7 +249,6 @@ public Object[] runtimeFileNameProviderFunc() { )); return balFiles.stream() .filter(tableFilter) - .filter(functionFilter) .filter(xmlFilter) .filter(objectFilter) .map(File::getAbsolutePath).toArray(); From 7cc8cb6f8595e855393d9934ab453ac9e77cdb87 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 23 Jul 2024 13:22:43 +0530 Subject: [PATCH 064/178] Implement object semtype --- .../runtime/api/types/FunctionType.java | 4 + .../runtime/api/types/MethodType.java | 2 + .../api/types/semtype/BasicTypeCode.java | 1 + .../runtime/api/types/semtype/Bdd.java | 22 +- .../runtime/api/types/semtype/Builder.java | 151 +++-- .../api/types/semtype/CellAtomicType.java | 4 + .../runtime/api/types/semtype/Core.java | 16 +- .../runtime/api/types/semtype/Env.java | 29 +- .../api/types/semtype/LazyContainer.java | 45 ++ .../api/types/semtype/MappingAtomicType.java | 5 - .../api/types/semtype/PredefinedTypeEnv.java | 586 ++++++++++++++++++ .../runtime/api/types/semtype/RecAtom.java | 8 +- .../runtime/internal/TypeChecker.java | 5 + .../runtime/internal/types/BFunctionType.java | 3 +- .../runtime/internal/types/BMethodType.java | 5 + .../internal/types/BNetworkObjectType.java | 16 + .../runtime/internal/types/BObjectType.java | 293 ++++++++- .../internal/types/BTypeConverter.java | 2 +- .../internal/types/semtype/BListSubType.java | 3 +- .../types/semtype/BMappingSubType.java | 7 +- .../types/semtype/BObjectSubType.java | 108 ++++ .../types/semtype/MappingDefinition.java | 15 +- .../internal/types/semtype/Member.java | 66 ++ .../types/semtype/ObjectDefinition.java | 114 ++++ .../types/semtype/ObjectQualifiers.java | 69 +++ .../internal/values/AbstractObjectValue.java | 10 + .../src/test/resources/testng.xml | 2 +- .../langlib/error/StackTrace.java | 34 +- .../port/test/RuntimeSemTypeResolver.java | 112 +++- .../semtype/port/test/SemTypeTest.java | 5 - 30 files changed, 1613 insertions(+), 129 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java index 94410bea3242..398dc7231183 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.api.types; +import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; + /** * {@code FunctionType} represents a function type in ballerina. * @@ -40,4 +42,6 @@ public interface FunctionType extends AnnotatableType { Type getRestType(); Parameter[] getParameters(); + + FunctionQualifiers getQualifiers(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java index 1f416679836e..7850e83d3303 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java @@ -34,4 +34,6 @@ public interface MethodType extends FunctionType { * @return true if {@link MethodType} method is isolated otherwise false. */ boolean isIsolated(); + + String name(); } 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 index 5a19f210ff8a..8026f3ff15c1 100644 --- 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 @@ -46,6 +46,7 @@ public final class BasicTypeCode { public static final int CODE_UNDEF = 0x12; public static final int CODE_B_TYPE = 0x13; + // TODO: see if we can turn this class to an enum with a value // Inherently immutable public static final BasicTypeCode BT_NIL = from(CODE_NIL); public static final BasicTypeCode BT_BOOLEAN = from(CODE_BOOLEAN); 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 index 43073e7f1e10..22ddf305a307 100644 --- 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 @@ -28,7 +28,7 @@ * * @since 2201.10.0 */ -public abstract sealed class Bdd extends SubType permits BddAllOrNothing, BddNode { +public abstract sealed class Bdd extends SubType implements SubTypeData permits BddAllOrNothing, BddNode { Bdd(boolean all, boolean nothing) { super(all, nothing); @@ -229,4 +229,24 @@ public static boolean bddEvery(Context cx, Bdd b, Conjunction pos, Conjunction n && 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); + } + } 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 index 713bf1d19951..956a40023aec 100644 --- 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 @@ -31,12 +31,15 @@ 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.BObjectSubType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.values.AbstractObjectValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.FPValue; +import io.ballerina.runtime.internal.values.ObjectValue; import java.math.BigDecimal; import java.util.ArrayList; @@ -47,6 +50,7 @@ import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FUNCTION; 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.CODE_B_TYPE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; @@ -54,7 +58,6 @@ import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; import static io.ballerina.runtime.api.types.semtype.Core.union; -import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; /** * Utility class for creating semtypes. @@ -64,71 +67,45 @@ public final class Builder { private static final String[] EMPTY_STRING_ARR = new String[0]; - private static final SemType NEVER = SemType.from(0); private static final SemType VAL = SemType.from(VT_MASK); - private static final SemType UNDEF = from(BasicTypeCode.BT_UNDEF); - private static final SemType INNER = basicTypeUnion(VAL.all | UNDEF.all); - static final CellAtomicType CELL_ATOMIC_VAL = new CellAtomicType( - valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED - ); - public static final SemType MAPPING = from(BT_MAPPING); - public static final SemType FUNCTION = from(BT_FUNCTION); - static final TypeAtom ATOM_CELL_VAL = createTypeAtom(0, CELL_ATOMIC_VAL); - static final CellAtomicType CELL_ATOMIC_NEVER = new CellAtomicType( - neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED - ); + private static final SemType OBJECT = from(BT_OBJECT); - public static final int BDD_REC_ATOM_READONLY = 0; - // represents both readonly & map and readonly & readonly[] - private static final BddNode BDD_SUBTYPE_RO = bddAtom(RecAtom.createRecAtom(BDD_REC_ATOM_READONLY)); - public static final SemType MAPPING_RO = basicSubType(BT_MAPPING, BMappingSubType.createDelegate(BDD_SUBTYPE_RO)); - public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING_RO = - new CellAtomicType(union(MAPPING_RO, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - - static final CellAtomicType CELL_ATOMIC_INNER = new CellAtomicType( - inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - static final SemType CELL_SEMTYPE_INNER = basicSubType(BT_CELL, - BCellSubType.createDelegate(bddAtom(createTypeAtom(2, CELL_ATOMIC_INNER)))); - public static final ListAtomicType LIST_ATOMIC_INNER = new ListAtomicType( - FixedLengthArray.empty(), CELL_SEMTYPE_INNER); - private static final SemType VAL_READONLY = unionOf(SemType.from(VT_INHERENTLY_IMMUTABLE), - basicSubType(BT_LIST, BListSubType.createDelegate(BDD_SUBTYPE_RO)), - basicSubType(BT_MAPPING, BMappingSubType.createDelegate(BDD_SUBTYPE_RO)) - ); - public static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); - public static final CellAtomicType CELL_ATOMIC_INNER_RO - = new CellAtomicType(INNER_READONLY, CELL_MUT_NONE); - public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING - = new CellAtomicType(union(MAPPING, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - static final SemType CELL_SEMTYPE_INNER_MAPPING = basicSubType( - BT_CELL, BCellSubType.createDelegate(bddAtom(createTypeAtom(3, CELL_ATOMIC_INNER_MAPPING)))); - public static final ListAtomicType LIST_ATOMIC_MAPPING = - new ListAtomicType(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING); - - static final SemType CELL_SEMTYPE_INNER_MAPPING_RO = basicSubType( - BT_CELL, - BCellSubType.createDelegate(bddAtom(createTypeAtom(5, CELL_ATOMIC_INNER_MAPPING_RO)))); - public static final ListAtomicType LIST_ATOMIC_MAPPING_RO = - new ListAtomicType(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING_RO); - - private static final TypeAtom ATOM_CELL_INNER_RO = createTypeAtom(7, CELL_ATOMIC_INNER_RO); - static final SemType CELL_SEMTYPE_INNER_RO = basicSubType( - BT_CELL, BCellSubType.createDelegate(bddAtom(ATOM_CELL_INNER_RO))); - public static final ListAtomicType LIST_ATOMIC_RO = new ListAtomicType( - FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); + private static final SemType INNER = basicTypeUnion(VAL.all | from(BasicTypeCode.BT_UNDEF).all); private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); - public static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; - public static final MappingAtomicType MAPPING_ATOMIC_INNER = new MappingAtomicType( - EMPTY_STRING_ARR, EMPTY_TYPES_ARR, CELL_SEMTYPE_INNER); - - public static final SemType SIMPLE_OR_STRING = + private static final SemType 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())); - private static final Env env = Env.getInstance(); + + private static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; + + 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); + private static final ConcurrentLazyContainer READONLY_TYPE = new ConcurrentLazyContainer<>(() -> 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)) + )); + private static final ConcurrentLazyContainer MAPPING_RO = new ConcurrentLazyContainer<>(() -> + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())) + ); + private static final ConcurrentLazyContainer INNER_RO = + new ConcurrentLazyContainer<>(() -> union(readonlyType(), inner())); + + private static final ConcurrentLazyContainer LIST_ATOMIC_INNER = + new ConcurrentLazyContainer<>(() -> new ListAtomicType( + FixedLengthArray.empty(), PredefinedTypeEnv.getInstance().cellSemTypeInner())); + private static final ConcurrentLazyContainer MAPPING_ATOMIC_INNER = + new ConcurrentLazyContainer<>(() -> new MappingAtomicType( + EMPTY_STRING_ARR, EMPTY_TYPES_ARR, PredefinedTypeEnv.getInstance().cellSemTypeInner())); + + private static final PredefinedTypeEnv PREDEFINED_TYPE_ENV = PredefinedTypeEnv.getInstance(); private Builder() { } @@ -157,7 +134,7 @@ private static SemType fromBType(Context cx, BType innerType) { } public static SemType neverType() { - return basicTypeUnion(0); + return SemType.from(0); } public static SemType nilType() { @@ -165,7 +142,7 @@ public static SemType nilType() { } public static SemType undef() { - return UNDEF; + return from(BasicTypeCode.BT_UNDEF); } public static SemType cell() { @@ -209,12 +186,12 @@ public static SemType listType() { } public static SemType readonlyType() { - return VAL_READONLY; + return READONLY_TYPE.get(); } - public static SemType basicTypeUnion(int bitset) { + static SemType basicTypeUnion(int bitset) { return switch (bitset) { - case 0 -> NEVER; + case 0 -> neverType(); case VT_MASK -> VAL; default -> { if (Integer.bitCount(bitset) == 1) { @@ -230,12 +207,19 @@ public static SemType basicTypeUnion(int 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); 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 intConst(long value) { if (value >= IntTypeCache.CACHE_MIN_VALUE && value <= IntTypeCache.CACHE_MAX_VALUE) { return IntTypeCache.cache[(int) value - IntTypeCache.CACHE_MIN_VALUE]; @@ -305,6 +289,8 @@ public static Optional shapeOf(Context cx, Object object) { } else if (object instanceof FPValue fpValue) { // TODO: this is a hack to support partial function types, remove when semtypes are fully implemented return Optional.of(from(cx, fpValue.getType())); + } else if (object instanceof AbstractObjectValue objectValue) { + return typeOfObject(cx, objectValue); } return Optional.empty(); } @@ -314,6 +300,11 @@ private static Optional typeOfMap(Context cx, BMap mapValue) { return typeWithShape.shapeOf(cx, mapValue); } + private static Optional typeOfObject(Context cx, AbstractObjectValue objectValue) { + TypeWithShape typeWithShape = (TypeWithShape) objectValue.getType(); + return typeWithShape.shapeOf(cx, objectValue); + } + private static Optional typeOfArray(Context cx, BArray arrayValue) { TypeWithShape typeWithShape = (TypeWithShape) arrayValue.getType(); return typeWithShape.shapeOf(cx, arrayValue); @@ -352,11 +343,11 @@ public static SemType anyType() { } public static SemType mappingType() { - return MAPPING; + return from(BT_MAPPING); } public static SemType functionType() { - return FUNCTION; + return from(BT_FUNCTION); } public static SemType anyDataType(Context context) { @@ -375,10 +366,6 @@ public static SemType anyDataType(Context context) { return accum; } - private static SemType unionOf(SemType type1, SemType type2) { - return union(type1, type2); - } - private static SemType unionOf(SemType... types) { SemType accum = types[0]; for (int i = 1; i < types.length; i++) { @@ -387,6 +374,34 @@ private static SemType unionOf(SemType... types) { return accum; } + public static SemType objectType() { + return OBJECT; + } + + static SemType mappingRO() { + return MAPPING_RO.get(); + } + + static SemType innerReadOnly() { + return INNER_RO.get(); + } + + static CellAtomicType cellAtomicVal() { + return PREDEFINED_TYPE_ENV.cellAtomicVal(); + } + + private static BddNode bddSubtypeRo() { + return bddAtom(RecAtom.createRecAtom(0)); + } + + public static ListAtomicType listAtomicInner() { + return LIST_ATOMIC_INNER.get(); + } + + public static MappingAtomicType mappingAtomicInner() { + return MAPPING_ATOMIC_INNER.get(); + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index ad5e969e8620..5ef2f0282a40 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -31,6 +31,10 @@ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicTy assert ty != null; } + public static CellAtomicType from(SemType ty, CellMutability mut) { + return new CellAtomicType(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()); 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 index 209f46449432..a015217b8d94 100644 --- 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 @@ -19,6 +19,7 @@ package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.internal.types.semtype.AllOrNothing; +import io.ballerina.runtime.internal.types.semtype.BObjectSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; import io.ballerina.runtime.internal.types.semtype.SubTypeData; import io.ballerina.runtime.internal.types.semtype.SubtypePair; @@ -33,6 +34,7 @@ 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_OBJECT; 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.Builder.cellContaining; @@ -368,12 +370,12 @@ public static SemType intersectMemberSemTypes(Env env, SemType t1, SemType t2) { private static Optional cellAtomicType(SemType t) { SemType cell = Builder.cell(); if (t.some == 0) { - return cell.equals(t) ? Optional.of(Builder.CELL_ATOMIC_VAL) : Optional.empty(); + 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.CELL_ATOMIC_VAL); + return bddCellAtomicType((Bdd) getComplexSubtypeData(t, BT_CELL), Builder.cellAtomicVal()); } } @@ -398,4 +400,14 @@ public static SemType cellInner(SemType t) { return cat.ty(); } + public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { + if (bdd instanceof BddAllOrNothing) { + return bdd.isAll() ? Builder.from(typeCode) : Builder.neverType(); + } + SubType subType = switch (typeCode.code()) { + case CODE_OBJECT -> BObjectSubType.createDelegate(bdd); + default -> throw new IllegalArgumentException("Unexpected type code: " + typeCode); + }; + return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); + } } 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 index 90ec5ff34cf1..3ffd01b7d4ca 100644 --- 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 @@ -24,12 +24,10 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static io.ballerina.runtime.api.types.semtype.Builder.LIST_ATOMIC_RO; -import static io.ballerina.runtime.api.types.semtype.MappingAtomicType.MAPPING_ATOMIC_RO; - /** * 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. @@ -45,36 +43,25 @@ public final class Env { private final ReadWriteLock atomTableLock = new ReentrantReadWriteLock(); private final ReadWriteLock recListLock = new ReentrantReadWriteLock(); - private final List recListAtoms; + final List recListAtoms; private final ReadWriteLock recMapLock = new ReentrantReadWriteLock(); - private final List recMappingAtoms; + 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 HashMap<>(); this.recListAtoms = new ArrayList<>(); this.recMappingAtoms = new ArrayList<>(); this.recFunctionAtoms = new ArrayList<>(); - recListAtoms.add(LIST_ATOMIC_RO); - recMappingAtoms.add(MAPPING_ATOMIC_RO); - - this.cellAtom(Builder.CELL_ATOMIC_VAL); - this.cellAtom(Builder.CELL_ATOMIC_NEVER); - - this.cellAtom(Builder.CELL_ATOMIC_INNER); - - this.cellAtom(Builder.CELL_ATOMIC_INNER_MAPPING); - this.listAtom(Builder.LIST_ATOMIC_MAPPING); - - this.cellAtom(Builder.CELL_ATOMIC_INNER_MAPPING_RO); - this.listAtom(Builder.LIST_ATOMIC_MAPPING_RO); - this.cellAtom(Builder.CELL_ATOMIC_INNER_RO); + PredefinedTypeEnv.getInstance().initializeEnv(this); } public static Env getInstance() { @@ -222,4 +209,8 @@ public Atom functionAtom(FunctionAtomicType atomicType) { private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { } + + public int distinctAtomCountGetAndIncrement() { + return this.distinctAtomCount.getAndIncrement(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java new file mode 100644 index 000000000000..9cde32b1f7ff --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.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.runtime.api.types.semtype; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +class ConcurrentLazyContainer implements Supplier { + + private Supplier initializer; + private final AtomicReference value = new AtomicReference<>(); + + ConcurrentLazyContainer(Supplier initializer) { + this.initializer = initializer; + } + + @Override + public E get() { + E result = value.get(); + if (result == null) { + result = initializer.get(); + if (!value.compareAndSet(null, result)) { + result = value.get(); + } + initializer = null; + } + return result; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java index 1f7e31947db5..76e9804760d1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java @@ -22,17 +22,12 @@ import java.util.ArrayList; import java.util.Collection; -import static io.ballerina.runtime.api.types.semtype.Builder.CELL_SEMTYPE_INNER_RO; import static io.ballerina.runtime.api.types.semtype.Core.cellInner; import static io.ballerina.runtime.api.types.semtype.Core.intersectMemberSemTypes; import static io.ballerina.runtime.api.types.semtype.Core.isNever; public record MappingAtomicType(String[] names, SemType[] types, SemType rest) implements AtomicType { - public static final MappingAtomicType MAPPING_ATOMIC_RO = new MappingAtomicType( - new String[]{}, new SemType[]{}, CELL_SEMTYPE_INNER_RO - ); - public MappingAtomicType intersectMapping(Env env, MappingAtomicType other) { int expectedSize = Integer.min(types().length, other.types().length); Collection names = new ArrayList<>(expectedSize); 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..469bf0198d28 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java @@ -0,0 +1,586 @@ +/* + * 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.BMappingSubType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +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.Builder.stringConst; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; + +final class PredefinedTypeEnv { + + private PredefinedTypeEnv() { + } + + private void initilizeEnv() { + // Initialize RecAtoms + mappingAtomicRO(); + listAtomicRO(); + mappingAtomicObjectRO(); + + // initialize atomic types + cellAtomicVal(); + cellAtomicNever(); + cellAtomicInner(); + cellAtomicInnerMapping(); + listAtomicMapping(); + cellAtomicInner(); + listAtomicMappingRO(); + cellAtomicInnerRO(); + } + + private static PredefinedTypeEnv INSTANCE; + + public synchronized static PredefinedTypeEnv getInstance() { + if (INSTANCE == null) { + INSTANCE = new PredefinedTypeEnv(); + INSTANCE.initilizeEnv(); + } + 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); + + // FIXME: instead use enums and enum map + // 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; + + // 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; + + 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 atomMappingObject; + private TypeAtom atomMappingObjectMember; + private TypeAtom atomMappingObjectMemberRO; + + 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(Builder.valType(), 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(Builder.neverType(), 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(Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(callAtomicInner); + } + return callAtomicInner; + } + + synchronized TypeAtom atomCellInner() { + if (atomCellInner == null) { + CellAtomicType cellAtomicInner = this.cellAtomicInner(); + atomCellInner = createTypeAtom(cellAtomIndex(cellAtomicInner), cellAtomicInner); + } + return atomCellInner; + } + + synchronized CellAtomicType cellAtomicInnerMapping() { + if (cellAtomicInnerMapping == null) { + cellAtomicInnerMapping = + CellAtomicType.from(union(Builder.mappingType(), Builder.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(Builder.mappingRO(), Builder.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 = new ListAtomicType( + FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMapping()))) + ); + 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 = new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, + BCellSubType.createDelegate(bddAtom(atomCellInnerMappingRO())))); + 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(Builder.innerReadOnly(), 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(Builder.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 CellAtomicType cellAtomicValRO() { + if (cellAtomicValRO == null) { + cellAtomicValRO = CellAtomicType.from( + Builder.readonlyType(), 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 = new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeValRO(), + cellSemTypeObjectMemberVisibility()}, + cellSemTypeUndef()); + 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( + mappingSemTypeObjectMemberRO(), 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( + 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( + 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 = new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeVal(), + cellSemTypeObjectMemberVisibility()}, + cellSemTypeUndef()); + ; + 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( + mappingSemTypeObjectMember(), 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 = new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, + cellSemTypeObjectMember() + ); + 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 = new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO()); + initializedRecListAtoms.add(listAtomicRO); + } + return listAtomicRO; + } + + synchronized MappingAtomicType mappingAtomicRO() { + if (mappingAtomicRO == null) { + mappingAtomicRO = new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO()); + initializedRecMappingAtoms.add(mappingAtomicRO); + } + return mappingAtomicRO; + } + + synchronized MappingAtomicType mappingAtomicObjectRO() { + if (mappingAtomicObjectRO == null) { + mappingAtomicObjectRO = new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, + cellSemTypeObjectMemberRO() + ); + initializedRecMappingAtoms.add(mappingAtomicObjectRO); + } + return mappingAtomicObjectRO; + } + + // 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); + } + } + } + + private int reservedRecAtomCount() { + return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); + } + + // FIXME: avoid creating these multiple times + private SemType cellSemTypeObjectMemberKind() { + return Builder.basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())) + ); + } + + private SemType cellSemTypeValRO() { + return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellValRO()))); + } + + private SemType cellSemTypeObjectMemberVisibility() { + return basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberVisibility())) + ); + } + + private SemType cellSemTypeUndef() { + return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellUndef()))); + } + + private SemType mappingSemTypeObjectMemberRO() { + return basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMemberRO()))); + } + + private SemType cellSemTypeVal() { + return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal()))); + } + + private SemType mappingSemTypeObjectMember() { + return basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMember()))); + } + + private SemType cellSemTypeObjectMember() { + return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMember()))); + } + + private SemType cellSemTypeObjectMemberRO() { + return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberRO()))); + } + + SemType cellSemTypeInner() { + return basicSubType(BT_CELL, + BCellSubType.createDelegate(bddAtom(atomCellInner()))); + } + + private SemType cellSemTypeInnerRO() { + return basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO()))); + } + + 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 index 836b89bc50f2..5e224575818f 100644 --- 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 @@ -26,9 +26,9 @@ */ public final class RecAtom implements Atom { - public final int index; + private final int index; private static final int BDD_REC_ATOM_READONLY = 0; - public static final RecAtom ZERO = new RecAtom(BDD_REC_ATOM_READONLY); + private static final RecAtom ZERO = new RecAtom(BDD_REC_ATOM_READONLY); private RecAtom(int index) { this.index = index; @@ -41,6 +41,10 @@ public static RecAtom createRecAtom(int index) { return new RecAtom(index); } + public static RecAtom createDistinctRecAtom(int index) { + return new RecAtom(index); + } + @Override public int index() { return index; 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 479b2da38f96..c7a553bb3f21 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 @@ -605,6 +605,11 @@ private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { return Core.isSubType(cx, source, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; } if (!Core.containsBasicType(target, B_TYPE_TOP)) { + if (Core.containsBasicType(source, Builder.objectType())) { + // This is a hack but since target defines the minimal it is fine + SemType sourcePureSemType = Core.intersect(source, SEMTYPE_TOP); + return Core.isSubType(cx, sourcePureSemType, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; + } return TypeCheckResult.FALSE; } SemType sourcePureSemType = Core.intersect(source, SEMTYPE_TOP); 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 348e924890cd..224aab24547c 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 @@ -299,7 +299,8 @@ private record SemTypeResult(boolean hasBTypePart, SemType pureSemTypePart) { } - private FunctionQualifiers getQualifiers() { + @Override + public FunctionQualifiers getQualifiers() { return FunctionQualifiers.create(SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED), SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); } 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..4e08a64a2e91 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,9 @@ public MethodType duplicate() { public boolean isIsolated() { return SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED); } + + @Override + 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 a0666927a833..ee3cacde96f4 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 @@ -22,8 +22,13 @@ import io.ballerina.runtime.api.types.NetworkObjectType; import io.ballerina.runtime.api.types.RemoteMethodType; import io.ballerina.runtime.api.types.ResourceMethodType; +import io.ballerina.runtime.api.types.semtype.Context; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Stream; /** * {@code BNetworkObjectType} represents a network object in Ballerina. @@ -80,4 +85,15 @@ private RemoteMethodType[] getRemoteMethods(MethodType[] methodTypes) { public ResourceMethodType[] getResourceMethods() { return resourceMethods; } + + @Override + protected Collection allMethods(Context cx) { + Stream methodStream = Arrays.stream(getMethods()).map(method -> MethodData.fromMethod(cx, method)); + Stream remoteMethodStream = + Arrays.stream(getRemoteMethods()).map(method -> MethodData.fromMethod(cx, method)); + Stream resoucrMethodStream = + Arrays.stream(getResourceMethods()).map(method -> MethodData.fromResourceMethod(cx, + (BResourceMethodType) method)); + return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resoucrMethodStream)).toList(); + } } 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 a4e609158587..865002acd0f4 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 @@ -23,24 +23,48 @@ 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.TypeId; import io.ballerina.runtime.api.types.TypeIdSet; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; +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.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.ValueUtils; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; +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.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.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.stream.Stream; import static io.ballerina.runtime.api.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, PartialSemTypeSupplier, TypeWithShape { private MethodType[] methodTypes; private MethodType initMethod; @@ -62,6 +86,11 @@ public class BObjectType extends BStructureType implements ObjectType { private String cachedToString; private boolean resolving; + private ObjectDefinition od; + private final Env env = Env.getInstance(); + // FIXME: better name + private SemType softSemTypeCache; + private final DistinctIdSupplier distinctIdSupplier; /** * Create a {@code BObjectType} which represents the user defined struct type. @@ -73,6 +102,7 @@ public class BObjectType extends BStructureType implements ObjectType { public BObjectType(String typeName, Module pkg, long flags) { super(typeName, pkg, flags, Object.class); this.readonly = SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY); + this.distinctIdSupplier = new DistinctIdSupplier(env); } @Override @@ -245,4 +275,265 @@ public boolean hasAnnotations() { public TypeIdSet getTypeIdSet() { return new BTypeIdSet(new ArrayList<>(typeIdSet.ids)); } + + @Override + synchronized SemType createSemType(Context cx) { + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) + .reduce(semTypeInner(cx), Core::intersect); + } + + private static boolean skipField(Set seen, String name) { + if (name.startsWith("$")) { + return true; + } + return !seen.add(name); + } + + private SemType semTypeInner(Context cx) { + if (softSemTypeCache != null) { + return softSemTypeCache; + } + if (od != null) { + return od.getSemType(env); + } + od = new ObjectDefinition(); + ObjectQualifiers qualifiers = getObjectQualifiers(); + List members = new ArrayList<>(); + boolean hasBTypes = false; + Set seen = new HashSet<>(fields.size() + methodTypes.length); + 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); + SemType ty = Builder.from(cx, field.getFieldType()); + SemType pureBTypePart = Core.intersect(ty, Core.B_TYPE_TOP); + if (!Core.isNever(pureBTypePart)) { + hasBTypes = true; + ty = Core.intersect(ty, Core.SEMTYPE_TOP); + } + members.add(new Member(name, ty, Member.Kind.Field, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); + } + for (MethodData method : allMethods(cx)) { + String name = method.name(); + if (skipField(seen, name)) { + continue; + } + boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); + SemType semType = method.semType(); + SemType pureBTypePart = Core.intersect(semType, Core.B_TYPE_TOP); + if (!Core.isNever(pureBTypePart)) { + hasBTypes = true; + semType = Core.intersect(semType, Core.SEMTYPE_TOP); + } + members.add(new Member(name, semType, Member.Kind.Method, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); + } + SemType semTypePart = od.define(env, qualifiers, members); + if (hasBTypes || members.isEmpty()) { + cx.markProvisionTypeReset(); + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + softSemTypeCache = Core.union(semTypePart, bTypePart); + return softSemTypeCache; + } + return semTypePart; + } + + 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 shapeOf(Context cx, Object object) { + AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; + SemType cachedShape = abstractObjectValue.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce( + valueShape(cx, abstractObjectValue), Core::intersect); + abstractObjectValue.cacheShape(shape); + return Optional.of(shape); + } + + private SemType valueShape(Context cx, AbstractObjectValue object) { + ObjectDefinition od = new ObjectDefinition(); + List members = new ArrayList<>(); + Set seen = new HashSet<>(fields.size() + methodTypes.length); + ObjectQualifiers qualifiers = getObjectQualifiers(); + boolean hasBTypes = false; + 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); + SemType ty = fieldShape(cx, field, object, isImmutable); + SemType pureBTypePart = Core.intersect(ty, Core.B_TYPE_TOP); + if (!Core.isNever(pureBTypePart)) { + hasBTypes = true; + ty = Core.intersect(ty, Core.SEMTYPE_TOP); + } + members.add(new Member(name, ty, Member.Kind.Field, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); + } + for (MethodData method : allMethods(cx)) { + String name = method.name(); + if (skipField(seen, name)) { + continue; + } + boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); + SemType semType = method.semType(); + SemType pureBTypePart = Core.intersect(semType, Core.B_TYPE_TOP); + if (!Core.isNever(pureBTypePart)) { + hasBTypes = true; + semType = Core.intersect(semType, Core.SEMTYPE_TOP); + } + members.add(new Member(name, semType, Member.Kind.Method, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); + } + SemType semTypePart = od.define(env, qualifiers, members); + if (hasBTypes) { + SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + return Core.union(semTypePart, bTypePart); + } + return semTypePart; + } + + private static SemType fieldShape(Context cx, Field field, AbstractObjectValue objectValue, boolean isImmutable) { + if (!isImmutable) { + return Builder.from(cx, field.getFieldType()); + } + BString fieldName = StringUtils.fromString(field.getFieldName()); + Optional shape = Builder.shapeOf(cx, objectValue.get(fieldName)); + assert !shape.isEmpty(); + return shape.get(); + } + + @Override + public void resetSemTypeCache() { + super.resetSemTypeCache(); + od = null; + } + + private final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map allocatedIds = new ConcurrentHashMap<>(); + private final Env env; + + private DistinctIdSupplier(Env env) { + this.env = env; + } + + public synchronized Collection get() { + if (ids != null) { + return ids; + } + if (typeIdSet == null) { + return List.of(); + } + ids = typeIdSet.getIds().stream().map(typeId -> allocatedIds.computeIfAbsent(typeId, + ignored -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + } + + protected Collection allMethods(Context cx) { + return Arrays.stream(methodTypes).map(method -> MethodData.fromMethod(cx, method)).toList(); + } + + protected record MethodData(String name, long flags, SemType semType) { + + static MethodData fromMethod(Context cx, MethodType method) { + return new MethodData(method.getName(), method.getFlags(), + Builder.from(cx, method.getType())); + } + + static MethodData fromResourceMethod(Context cx, 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<>(); + boolean hasBTypes = false; + for (Type part : pathSegmentTypes) { + if (part == null) { + paramTypes.add(Builder.anyType()); + } else { + SemType semType = Builder.from(cx, part); + if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { + hasBTypes = true; + paramTypes.add(Core.intersect(semType, Core.SEMTYPE_TOP)); + } else { + paramTypes.add(semType); + } + } + } + for (Parameter paramType : innerFn.getParameters()) { + SemType semType = Builder.from(cx, paramType.type); + if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { + hasBTypes = true; + paramTypes.add(Core.intersect(semType, Core.SEMTYPE_TOP)); + } else { + paramTypes.add(semType); + } + } + SemType rest; + Type restType = innerFn.getRestType(); + if (restType instanceof BArrayType arrayType) { + rest = Builder.from(cx, arrayType.getElementType()); + if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { + hasBTypes = true; + rest = Core.intersect(rest, Core.SEMTYPE_TOP); + } + } else { + rest = Builder.neverType(); + } + + SemType returnType; + if (innerFn.getReturnType() != null) { + returnType = Builder.from(cx, innerFn.getReturnType()); + if (!Core.isNever(Core.intersect(returnType, Core.B_TYPE_TOP))) { + hasBTypes = true; + returnType = Core.intersect(returnType, Core.SEMTYPE_TOP); + } + } else { + returnType = Builder.nilType(); + } + ListDefinition paramListDefinition = new ListDefinition(); + Env env = cx.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, innerFn.getQualifiers()); + if (hasBTypes) { + semType = Core.union(semType, BTypeConverter.wrapAsPureBType((BType) innerFn)); + } + return new MethodData(methodName, method.getFlags(), semType); + } + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 981fc4e453d8..cd666bd5dfc9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -47,7 +47,7 @@ private BTypeConverter() { private static final SemType implementedTypes = unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType(), - Builder.mappingType(), Builder.functionType()); + Builder.mappingType(), Builder.functionType(), Builder.objectType()); private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); 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 index fad698f6db94..be51941947c3 100644 --- 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 @@ -43,7 +43,6 @@ 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.intersectMemberSemTypes; -import static io.ballerina.runtime.api.types.semtype.Builder.LIST_ATOMIC_INNER; import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; // TODO: this has lot of common code with cell (and future mapping), consider refactoring (problem is createDelegate) @@ -106,7 +105,7 @@ private static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjuncti FixedLengthArray members; SemType rest; if (pos == null) { - ListAtomicType atom = LIST_ATOMIC_INNER; + ListAtomicType atom = Builder.listAtomicInner(); members = atom.members(); rest = atom.rest(); } else { 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 index f75da4f894bb..447f7ba2a904 100644 --- 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 @@ -34,7 +34,6 @@ import java.util.Objects; import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; -import static io.ballerina.runtime.api.types.semtype.Builder.MAPPING_ATOMIC_INNER; public class BMappingSubType extends SubType implements DelegatedSubType { @@ -88,10 +87,10 @@ public boolean isEmpty(Context cx) { (context, bdd) -> bddEvery(context, bdd, null, null, BMappingSubType::mappingFormulaIsEmpty), inner); } - private static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { MappingAtomicType combined; if (posList == null) { - combined = MAPPING_ATOMIC_INNER; + combined = Builder.mappingAtomicInner(); } else { // combine all the positive atoms using intersection combined = cx.mappingAtomType(posList.atom()); @@ -182,7 +181,7 @@ private static String[] shallowCopyStrings(String[] v, int newLength) { @Override public SubTypeData data() { - throw new IllegalStateException("unimplemented"); + return inner(); } @Override 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..49db4dd05f07 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java @@ -0,0 +1,108 @@ +/* + * 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; + +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/MappingDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java index 2a83d7376e33..82515b53f64d 100644 --- 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 @@ -66,17 +66,15 @@ public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, C BCellField[] cellFields = new BCellField[fields.length]; for (int i = 0; i < fields.length; i++) { Field field = fields[i]; - SemType type = field.ty; - SemType cellType = cellContaining(env, field.optional ? union(type, undef()) : type, - field.readonly ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); - cellFields[i] = new BCellField(field.name, cellType); + BCellField cellField = BCellField.from(env, field, mut); + cellFields[i] = cellField; } SemType restCell = cellContaining(env, union(rest, undef()), isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); return define(env, cellFields, restCell); } - private SemType define(Env env, BCellField[] cellFields, SemType rest) { + SemType define(Env env, BCellField[] cellFields, SemType rest) { String[] names = new String[cellFields.length]; SemType[] types = new SemType[cellFields.length]; sortAndSplitFields(cellFields, names, types); @@ -107,5 +105,12 @@ 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 = cellContaining(env, field.optional ? union(type, undef()) : 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..bd84d06e6dee --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java @@ -0,0 +1,66 @@ +/* + * 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.booleanConst; +import static io.ballerina.runtime.api.types.semtype.Builder.stringConst; + +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", stringConst("field"), true, false); + private static final MappingDefinition.Field METHOD = + new MappingDefinition.Field("kind", stringConst("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 = stringConst("public"); + private static final MappingDefinition.Field PUBLIC = + new MappingDefinition.Field("visibility", PUBLIC_TAG, true, false); + private static final SemType PRIVATE_TAG = stringConst("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/ObjectDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java new file mode 100644 index 000000000000..0683e8cf4cb7 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java @@ -0,0 +1,114 @@ +/* + * 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.CellAtomicType; +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.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.Builder.cellContaining; +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; + +public class ObjectDefinition implements 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 = qualifiers.readonly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + 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())); + return objectContaining(mappingType); + } + + 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.readonlyType() : Builder.valType(), + readonly, false), + Member.Kind.Field.field(), + Member.Visibility.ALL + }, + Builder.neverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + MappingDefinition methodDefn = new MappingDefinition(); + + SemType methodMemberType = methodDefn.defineMappingTypeWrapped( + env, + new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", Builder.functionType(), true, false), + Member.Kind.Method.field(), + Member.Visibility.ALL + }, + Builder.neverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + return cellContaining(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.neverType(), + 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..086e490c429f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java @@ -0,0 +1,69 @@ +/* + * 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.CellAtomicType; +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.booleanConst; +import static io.ballerina.runtime.api.types.semtype.Builder.stringConst; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +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 ? booleanConst(true) : Builder.booleanType(), + true, false); + MappingDefinition.Field networkField = networkQualifier.field(); + SemType ty = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{isolatedField, networkField}, + Builder.neverType(), 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 = stringConst("client"); + private static final MappingDefinition.Field CLIENT = + new MappingDefinition.Field("network", CLIENT_TAG, true, false); + + private static final SemType SERVICE_TAG = stringConst("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/values/AbstractObjectValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java index 07d20399f30d..51ada6d07807 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,7 @@ 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.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -61,6 +62,7 @@ public abstract class AbstractObjectValue implements ObjectValue { private BTypedesc typedesc; private final BObjectType objectType; private final Type type; + private SemType shape; private final HashMap nativeData = new HashMap<>(); @@ -233,4 +235,12 @@ 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; + } } diff --git a/bvm/ballerina-runtime/src/test/resources/testng.xml b/bvm/ballerina-runtime/src/test/resources/testng.xml index 605dbab4dee3..ea13455011ff 100644 --- a/bvm/ballerina-runtime/src/test/resources/testng.xml +++ b/bvm/ballerina-runtime/src/test/resources/testng.xml @@ -20,7 +20,7 @@ - + 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 f5acb449a94a..ecfb7c9149d5 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.creators.ValueCreator; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ObjectType; +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/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 index 6853ff4f2c77..64ab37b0a4d7 100644 --- 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 @@ -19,6 +19,7 @@ package io.ballerina.semtype.port.test; import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; @@ -28,9 +29,15 @@ import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; 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 org.ballerinalang.model.elements.Flag; 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.wso2.ballerinalang.compiler.tree.BLangFunction; import org.wso2.ballerinalang.compiler.tree.BLangNode; import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; @@ -42,6 +49,7 @@ 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.BLangTupleTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangType; @@ -55,6 +63,8 @@ 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; @@ -73,7 +83,7 @@ class RuntimeSemTypeResolver extends SemTypeResolver { private static final SemType[] EMPTY_SEMTYPE_ARR = {}; Map attachedSemType = new HashMap<>(); Map semTypeMemo = new HashMap<>(); - Map attachedDefinitions = new HashMap<>(); + Map attachedDefinitions = new HashMap<>(); @Override public void resolveTypeDefn(TypeTestContext cx, Map modTable, @@ -112,7 +122,7 @@ private SemType resolveTypeDefnRec(TypeTestContext cx, Map cx, Map mod, BLangTypeDefinition defn, - int depth, BLangType td) { + int depth, TypeNode td) { if (td == null) { return null; } @@ -128,10 +138,94 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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); default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); }; } + 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); + } + + 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); + SemType[] params = functionType.getParameters().stream() + .map(paramVar -> resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode)).toArray(SemType[]::new); + SemType rest; + if (functionType.getRestParameters() == null) { + rest = Builder.neverType(); + } else { + ArrayTypeNode arrayType = (ArrayTypeNode) functionType.getRestParameters().getTypeNode(); + rest = resolveTypeDesc(cx, mod, defn, depth + 1, arrayType.getElementType()); + } + SemType returnType = functionType.getReturnTypeNode() != null ? + resolveTypeDesc(cx, mod, defn, depth + 1, functionType.getReturnTypeNode()) : Builder.nilType(); + 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 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(); @@ -363,7 +457,11 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp } if (moduleLevelDef.getKind() == NodeKind.TYPE_DEFINITION) { - return resolveTypeDefnRec(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + SemType ty = resolveTypeDefnRec(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctSemType(cx, ty); + } + return ty; } else if (moduleLevelDef.getKind() == NodeKind.CONSTANT) { BLangConstant constant = (BLangConstant) moduleLevelDef; return resolveTypeDefnRec(cx, mod, constant.associatedTypeDefinition, depth); @@ -372,6 +470,14 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp } } + private SemType getDistinctSemType(TypeTestContext cx, SemType innerType) { + Env env = (Env) cx.getInnerEnv(); + if (Core.isSubtypeSimple(innerType, Builder.objectType())) { + return getDistinctObjectType(env, innerType); + } + throw new IllegalArgumentException("Distinct type not supported for: " + innerType); + } + private SemType resolveIntSubtype(String name) { // TODO: support MAX_VALUE return switch (name) { 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 index 5167363c4b54..64c873718281 100644 --- 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 @@ -241,11 +241,6 @@ public Object[] runtimeFileNameProviderFunc() { "xml-te.bal" )); Predicate objectFilter = createRuntimeFileNameFilter(Set.of( - "object-binaryops-tv.bal", - "object-qulifiers-tv.bal", - "object-rec-tv.bal", - "object-simple-tv.bal", - "object-distinct-tv.bal" )); return balFiles.stream() .filter(tableFilter) From 7adb3fc37eab46b408ec08eccf4264a09570409b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 28 Jul 2024 20:12:10 +0530 Subject: [PATCH 065/178] Fix record shape --- .../runtime/internal/types/BRecordType.java | 12 ++++++++++-- .../internal/types/semtype/BMappingSubType.java | 3 --- 2 files changed, 10 insertions(+), 5 deletions(-) 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 9c953e88811e..8badc79fbb6b 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 @@ -256,8 +256,12 @@ synchronized SemType createSemType(Context cx) { hasBTypePart = true; fieldType = Core.intersect(fieldType, Core.SEMTYPE_TOP); } + boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + if (Core.isNever(fieldType)) { + isReadonly = true; + } mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, - SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY), isOptional); + isReadonly, isOptional); } CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; @@ -321,12 +325,16 @@ public Optional shapeOf(Context cx, Object object) { continue; } boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); SemType fieldType = Builder.from(cx, field.getFieldType()); + if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { + fieldType = Builder.undef(); + } if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { return Optional.of(neverType()); } fields.add(new MappingDefinition.Field(field.getFieldName(), fieldType, - SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY), isOptional)); + isReadonly, isOptional)); } MappingDefinition md = new MappingDefinition(); SemType semTypePart; 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 index 447f7ba2a904..da267c1ef694 100644 --- 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 @@ -136,9 +136,6 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju // the posType came from the rest type mt = insertField(pos, fieldPair.name(), d); } else { - if (Core.isSubType(cx, fieldPair.type1(), Builder.cellContaining(cx.env, Builder.undef()))) { - continue; - } SemType[] posTypes = pos.types().clone(); posTypes[fieldPair.index1()] = d; mt = new MappingAtomicType(pos.names(), posTypes, pos.rest()); From 794b55273c54573689d7bcf5c462aacf5869e7c7 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 29 Jul 2024 10:08:34 +0530 Subject: [PATCH 066/178] Implement error semtype Impelement xml semtype --- .../runtime/api/types/semtype/Bdd.java | 2 + .../api/types/semtype/BddAllOrNothing.java | 5 + .../runtime/api/types/semtype/BddNode.java | 5 + .../runtime/api/types/semtype/Builder.java | 49 +++++- .../runtime/api/types/semtype/Core.java | 1 + .../api/types/semtype/PredefinedTypeEnv.java | 15 +- .../runtime/internal/TypeChecker.java | 27 ++- .../runtime/internal/types/BErrorType.java | 69 +++++++- .../runtime/internal/types/BFunctionType.java | 3 +- .../internal/types/BIntersectionType.java | 17 +- .../runtime/internal/types/BMapType.java | 6 +- .../internal/types/BNetworkObjectType.java | 1 - .../runtime/internal/types/BObjectType.java | 42 ++--- .../internal/types/BTypeConverter.java | 19 ++- .../runtime/internal/types/BXmlType.java | 103 ++++++++++- .../internal/types/DistinctIdSupplier.java | 75 ++++++++ .../internal/types/semtype/BErrorSubType.java | 116 +++++++++++++ .../internal/types/semtype/BXmlSubType.java | 160 ++++++++++++++++++ .../types/semtype/DelegatedSubType.java | 6 +- .../internal/types/semtype/ErrorUtils.java | 60 +++++++ .../types/semtype/FunctionQualifiers.java | 2 +- .../internal/types/semtype/Member.java | 1 - .../internal/types/semtype/XmlUtils.java | 141 +++++++++++++++ .../runtime/internal/values/ErrorValue.java | 3 +- .../internal/values/ReadOnlyUtils.java | 6 +- .../function_invocation/Dependencies.toml | 18 ++ .../port/test/RuntimeSemTypeResolver.java | 52 ++++++ .../semtype/port/test/SemTypeTest.java | 12 -- .../resources/test-src/type-rel/error1-tv.bal | 9 + .../test/query/XMLQueryExpressionTest.java | 6 + 30 files changed, 951 insertions(+), 80 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java create mode 100644 tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/Dependencies.toml create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal 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 index 22ddf305a307..a438b359afc2 100644 --- 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 @@ -249,4 +249,6 @@ private static Conjunction andIfPositive(Atom atom, Conjunction next) { return and(atom, next); } + public abstract boolean posMaybeEmpty(); + } 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 index 9b5e84fda3f2..f12f3c12f48b 100644 --- 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 @@ -41,4 +41,9 @@ public int hashCode() { 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/BddNode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java index 6f4770a9ae3b..ec0991eb66be 100644 --- 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 @@ -97,4 +97,9 @@ boolean isSimple() { return left.equals(BddAllOrNothing.ALL) && middle.equals(BddAllOrNothing.NOTHING) && right.equals(BddAllOrNothing.NOTHING); } + + @Override + public boolean posMaybeEmpty() { + return middle.posMaybeEmpty() || right.posMaybeEmpty(); + } } 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 index 956a40023aec..8ea78414eceb 100644 --- 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 @@ -20,6 +20,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BType; @@ -36,10 +37,11 @@ import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; import io.ballerina.runtime.internal.values.AbstractObjectValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.FPValue; -import io.ballerina.runtime.internal.values.ObjectValue; +import io.ballerina.runtime.internal.values.XmlValue; import java.math.BigDecimal; import java.util.ArrayList; @@ -47,10 +49,12 @@ import java.util.Optional; 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_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_XML; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; @@ -90,7 +94,8 @@ public final class Builder { 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_OBJECT, BObjectSubType.createDelegate(MAPPING_SUBTYPE_OBJECT_RO)), + basicSubType(BT_XML, XmlUtils.XML_SUBTYPE_RO) )); private static final ConcurrentLazyContainer MAPPING_RO = new ConcurrentLazyContainer<>(() -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())) @@ -289,12 +294,26 @@ public static Optional shapeOf(Context cx, Object object) { } else if (object instanceof FPValue fpValue) { // TODO: this is a hack to support partial function types, remove when semtypes are fully implemented return Optional.of(from(cx, fpValue.getType())); + } else if (object instanceof BError errorValue) { + return typeOfError(cx, errorValue); } else if (object instanceof AbstractObjectValue objectValue) { return typeOfObject(cx, objectValue); + } else if (object instanceof XmlValue xmlValue) { + return typeOfXml(cx, xmlValue); } return Optional.empty(); } + private static Optional typeOfXml(Context cx, XmlValue xmlValue) { + TypeWithShape typeWithShape = (TypeWithShape) xmlValue.getType(); + return typeWithShape.shapeOf(cx, xmlValue); + } + + private static Optional typeOfError(Context cx, BError errorValue) { + TypeWithShape typeWithShape = (TypeWithShape) errorValue.getType(); + return typeWithShape.shapeOf(cx, errorValue); + } + private static Optional typeOfMap(Context cx, BMap mapValue) { TypeWithShape typeWithShape = (TypeWithShape) mapValue.getType(); return typeWithShape.shapeOf(cx, mapValue); @@ -350,6 +369,30 @@ public static SemType functionType() { return from(BT_FUNCTION); } + public static SemType errorType() { + return from(BT_ERROR); + } + + public static SemType xmlType() { + return from(BT_XML); + } + + public static SemType xmlElementType() { + return XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_ELEMENT_RO | XmlUtils.XML_PRIMITIVE_ELEMENT_RW); + } + + public static SemType xmlCommentType() { + return XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_COMMENT_RO | XmlUtils.XML_PRIMITIVE_COMMENT_RW); + } + + public static SemType xmlTextType() { + return XmlUtils.xmlSequence(XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_TEXT)); + } + + public static SemType xmlPIType() { + return XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW); + } + public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; if (memo != null) { @@ -390,7 +433,7 @@ static CellAtomicType cellAtomicVal() { return PREDEFINED_TYPE_ENV.cellAtomicVal(); } - private static BddNode bddSubtypeRo() { + public static BddNode bddSubtypeRo() { return bddAtom(RecAtom.createRecAtom(0)); } 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 index a015217b8d94..4d990b6b88e5 100644 --- 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 @@ -114,6 +114,7 @@ public static SemType diff(SemType t1, SemType t2) { return SemType.from(all, some, filterNulls ? filterNulls(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()); 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 index 469bf0198d28..8d2b27fa4f18 100644 --- 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 @@ -57,14 +57,14 @@ private void initilizeEnv() { cellAtomicInnerRO(); } - private static PredefinedTypeEnv INSTANCE; + private static PredefinedTypeEnv instance; - public synchronized static PredefinedTypeEnv getInstance() { - if (INSTANCE == null) { - INSTANCE = new PredefinedTypeEnv(); - INSTANCE.initilizeEnv(); + public static synchronized PredefinedTypeEnv getInstance() { + if (instance == null) { + instance = new PredefinedTypeEnv(); + instance.initilizeEnv(); } - return INSTANCE; + return instance; } private final List> initializedCellAtoms = new ArrayList<>(); @@ -74,7 +74,6 @@ public synchronized static PredefinedTypeEnv getInstance() { private final List initializedRecMappingAtoms = new ArrayList<>(); private final AtomicInteger nextAtomIndex = new AtomicInteger(0); - // FIXME: instead use enums and enum map // This is to avoid passing down env argument when doing cell type operations. // Please refer to the cellSubtypeDataEnsureProper() in cell.bal private CellAtomicType cellAtomicVal; @@ -529,7 +528,7 @@ private int reservedRecAtomCount() { return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); } - // FIXME: avoid creating these multiple times + // TODO: avoid creating these multiple times private SemType cellSemTypeObjectMemberKind() { return Builder.basicSubType( BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())) 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 c7a553bb3f21..d22e2250ba2d 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 @@ -568,7 +568,19 @@ private static TypeCheckResult isSubType(Context cx, Object sourceValue, Type so if (result != TypeCheckResult.FALSE) { return result; } - return isSubTypeWithShape(cx, sourceValue, Builder.from(cx, target)); + return isSubTypeWithShape(cx, sourceValue, Builder.from(cx, source), Builder.from(cx, target)); + } + + private static TypeCheckResult isSubTypeWithShape(Context cx, Object sourceValue, SemType source, SemType target) { + TypeCheckResult result; + result = isSubTypeWithShapeInner(cx, sourceValue, target); + if (result == TypeCheckResult.MAYBE) { + if (Core.containsBasicType(source, B_TYPE_TOP)) { + return TypeCheckResult.MAYBE; + } + return TypeCheckResult.FALSE; + } + return result; } private static TypeCheckResult isSubType(Context cx, Type source, Type target) { @@ -586,19 +598,26 @@ private static TypeCheckResult isSubTypeInner(Context cx, Object sourceValue, Se if (result != TypeCheckResult.FALSE) { return result; } - return isSubTypeWithShape(cx, sourceValue, target); + return isSubTypeWithShape(cx, sourceValue, source, target); } - private static TypeCheckResult isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { + private static TypeCheckResult isSubTypeWithShapeInner(Context cx, Object sourceValue, SemType target) { Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); if (sourceSingletonType.isEmpty()) { - return Core.containsBasicType(target, B_TYPE_TOP) && !(sourceValue instanceof FPValue) ? + return fallbackToBTypeWithoutShape(sourceValue, target) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; } SemType singletonType = sourceSingletonType.get(); return isSubTypeInner(singletonType, target); } + private static boolean fallbackToBTypeWithoutShape(Object sourceValue, SemType target) { + if (!Core.containsBasicType(target, B_TYPE_TOP)) { + return false; + } + return !(sourceValue instanceof FPValue); + } + private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { Context cx = context(); if (!Core.containsBasicType(source, B_TYPE_TOP)) { 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 59cf32500a3d..dda7e62f3cf5 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,31 @@ import io.ballerina.runtime.api.types.ErrorType; 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.api.values.BError; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.values.ErrorValue; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; /** * {@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, PartialSemTypeSupplier, TypeWithShape { - public Type detailType = PredefinedTypes.TYPE_ERROR_DETAIL; + public Type detailType = PredefinedTypes.TYPE_DETAIL; public BTypeIdSet typeIdSet; private IntersectionType intersectionType = null; + private DistinctIdSupplier distinctIdSupplier; + private static final AtomicInteger nextId = new AtomicInteger(0); + private final int id = nextId.getAndIncrement(); public BErrorType(String typeName, Module pkg, Type detailType) { super(typeName, pkg, ErrorValue.class); @@ -50,6 +61,7 @@ public BErrorType(String typeName, Module pkg) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; + this.distinctIdSupplier = null; } @Override @@ -113,4 +125,57 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + synchronized SemType createSemType(Context cx) { + boolean hasBType = false; + SemType err; + if (detailType == null || isTopType()) { + err = Builder.errorType(); + hasBType = true; + } else { + SemType detailType = Builder.from(cx, getDetailType()); + if (!Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP))) { + hasBType = true; + detailType = Core.intersect(detailType, Core.SEMTYPE_TOP); + } + err = ErrorUtils.errorDetail(detailType); + } + + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(cx.env, getTypeIdSet()); + } + SemType pureSemType = + distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); + if (hasBType) { + return Core.union(pureSemType, BTypeConverter.wrapAsPureBType(this)); + } + return pureSemType; + } + + private boolean isTopType() { + return detailType == PredefinedTypes.TYPE_DETAIL; + } + + @Override + public Optional shapeOf(Context cx, Object object) { + BError errorValue = (BError) object; + Object details = errorValue.getDetails(); + if (!(details instanceof BMap errorDetails)) { + return Optional.empty(); + } + SemType detailType = Builder.from(cx, errorDetails.getType()); + boolean hasBType = !Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP)); + return BMapType.readonlyShape(cx, errorDetails) + .map(ErrorUtils::errorDetail) + .map(err -> distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct) + .reduce(err, Core::intersect)) + .map(semType -> { + if (hasBType) { + return Core.union(semType, BTypeConverter.wrapAsPureBType(this)); + } else { + return semType; + } + }); + } } 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 224aab24547c..0c65113a5728 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 @@ -237,7 +237,6 @@ public long getFlags() { private static SemType createIsolatedTop(Env env) { FunctionDefinition fd = new FunctionDefinition(); SemType ret = Builder.valType(); - // FIXME: add a comment explaining why we are using neverType here return fd.define(env, Builder.neverType(), ret, FunctionQualifiers.create(true, false)); } @@ -305,7 +304,7 @@ public FunctionQualifiers getQualifiers() { SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); } - // FIXME: consider moving this to builder + // TODO: consider moving this to builder private static SemTypeResult getSemType(Context cx, Type type) { SemType semType = Builder.from(cx, type); if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { 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 f7de30acef6c..449f2e84ba2c 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 @@ -26,6 +26,7 @@ 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 java.util.ArrayList; @@ -225,7 +226,21 @@ SemType createSemType(Context cx) { if (effectiveType instanceof SemType semType) { return semType; } - return Builder.from(cx, effectiveType); + if (constituentTypes.isEmpty()) { + return Builder.neverType(); + } + SemType result = Builder.from(cx, constituentTypes.get(0)); + boolean hasBType = Core.containsBasicType(Builder.from(cx, effectiveType), Builder.bType()); + result = Core.intersect(result, Core.SEMTYPE_TOP); + for (int i = 1; i < constituentTypes.size(); i++) { + SemType memberType = Builder.from(cx, constituentTypes.get(i)); +// hasBType |= Core.containsBasicType(memberType, Builder.bType()); + result = Core.intersect(result, memberType); + } + if (hasBType) { + return Core.union(result, BTypeConverter.wrapAsPureBType((BType) effectiveType)); + } + return result; } @Override 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 e38c417b2e77..30de625d412b 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 @@ -218,6 +218,10 @@ public Optional shapeOf(Context cx, Object object) { return Optional.of(cachedShape); } + return readonlyShape(cx, value); + } + + static Optional readonlyShape(Context cx, BMap value) { int nFields = value.size(); MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); @@ -230,7 +234,7 @@ public Optional shapeOf(Context cx, Object object) { fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); } MappingDefinition md = new MappingDefinition(); - SemType semType = md.defineMappingTypeWrapped(env, fields, Builder.neverType(), CELL_MUT_NONE); + SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.neverType(), CELL_MUT_NONE); value.cacheShape(semType); return Optional.of(semType); } 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 ee3cacde96f4..774964095558 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 @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.stream.Stream; /** 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 865002acd0f4..72ea9c27bba7 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 @@ -30,7 +30,6 @@ 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.TypeId; import io.ballerina.runtime.api.types.TypeIdSet; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; @@ -62,9 +61,6 @@ import java.util.Optional; import java.util.Set; import java.util.StringJoiner; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; -import java.util.stream.Stream; import static io.ballerina.runtime.api.TypeTags.SERVICE_TAG; @@ -88,9 +84,9 @@ public class BObjectType extends BStructureType implements ObjectType, PartialSe private boolean resolving; private ObjectDefinition od; private final Env env = Env.getInstance(); - // FIXME: better name + // TODO: better name private SemType softSemTypeCache; - private final DistinctIdSupplier distinctIdSupplier; + private DistinctIdSupplier distinctIdSupplier; /** * Create a {@code BObjectType} which represents the user defined struct type. @@ -102,7 +98,6 @@ public class BObjectType extends BStructureType implements ObjectType, PartialSe public BObjectType(String typeName, Module pkg, long flags) { super(typeName, pkg, flags, Object.class); this.readonly = SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY); - this.distinctIdSupplier = new DistinctIdSupplier(env); } @Override @@ -245,6 +240,7 @@ public void setIntersectionType(IntersectionType intersectionType) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; + this.distinctIdSupplier = null; } public BObjectType duplicate() { @@ -278,6 +274,9 @@ public TypeIdSet getTypeIdSet() { @Override synchronized SemType createSemType(Context cx) { + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); + } return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) .reduce(semTypeInner(cx), Core::intersect); } @@ -358,12 +357,15 @@ private ObjectQualifiers getObjectQualifiers() { } @Override - public Optional shapeOf(Context cx, Object object) { + public synchronized Optional shapeOf(Context cx, Object object) { AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; SemType cachedShape = abstractObjectValue.shapeOf(); if (cachedShape != null) { return Optional.of(cachedShape); } + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); + } SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce( valueShape(cx, abstractObjectValue), Core::intersect); abstractObjectValue.cacheShape(shape); @@ -433,30 +435,6 @@ public void resetSemTypeCache() { od = null; } - private final class DistinctIdSupplier implements Supplier> { - - private List ids = null; - private static final Map allocatedIds = new ConcurrentHashMap<>(); - private final Env env; - - private DistinctIdSupplier(Env env) { - this.env = env; - } - - public synchronized Collection get() { - if (ids != null) { - return ids; - } - if (typeIdSet == null) { - return List.of(); - } - ids = typeIdSet.getIds().stream().map(typeId -> allocatedIds.computeIfAbsent(typeId, - ignored -> env.distinctAtomCountGetAndIncrement())) - .toList(); - return ids; - } - } - protected Collection allMethods(Context cx) { return Arrays.stream(methodTypes).map(method -> MethodData.fromMethod(cx, method)).toList(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index cd666bd5dfc9..40c7b72263ca 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -47,7 +47,8 @@ private BTypeConverter() { private static final SemType implementedTypes = unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType(), - Builder.mappingType(), Builder.functionType(), Builder.objectType()); + Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), + Builder.xmlType()); private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); @@ -115,6 +116,9 @@ private record BTypeParts(SemType semTypePart, List bTypeParts) { private static BTypeParts split(Context cx, Type type) { if (type instanceof SemType) { return new BTypeParts(from(cx, type), Collections.emptyList()); + // TODO: + } else if (type instanceof BXmlType) { + return new BTypeParts(from(cx, type), Collections.emptyList()); } else if (type instanceof BUnionType unionType) { return splitUnion(cx, unionType); } else if (type instanceof BAnyType anyType) { @@ -122,7 +126,7 @@ private static BTypeParts split(Context cx, Type type) { } else if (type instanceof BTypeReferenceType referenceType) { return split(cx, referenceType.getReferredType()); } else if (type instanceof BIntersectionType intersectionType) { - return split(cx, intersectionType.getEffectiveType()); + return splitIntersection(cx, intersectionType); } else if (type instanceof BReadonlyType readonlyType) { return splitReadonly(readonlyType); } else if (type instanceof BFiniteType finiteType) { @@ -134,6 +138,17 @@ private static BTypeParts split(Context cx, Type type) { } } + private static BTypeParts splitIntersection(Context cx, BIntersectionType intersectionType) { + List members = Collections.unmodifiableList(intersectionType.getConstituentTypes()); + SemType semTypePart = Builder.valType(); + for (Type member : members) { + BTypeParts memberParts = split(cx, member); + semTypePart = Core.intersect(memberParts.semTypePart(), semTypePart); + } + BTypeParts effectiveTypeParts = split(cx, intersectionType.getEffectiveType()); + return new BTypeParts(semTypePart, effectiveTypeParts.bTypeParts()); + } + private static BTypeParts splitSemTypeSupplier(Context cx, PartialSemTypeSupplier supplier) { int startingIndex = cx.addProvisionalType((BType) supplier); SemType semtype = supplier.get(cx); 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 c48c9d085594..3fc5342ab6ce 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 @@ -21,10 +21,20 @@ import io.ballerina.runtime.api.TypeTags; 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.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,90 @@ public Optional getIntersectionType() { return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); } + // TODO: this class must also be a semtype class + @Override + SemType createSemType(Context cx) { + SemType semType; + if (constraint == null) { + semType = pickTopType(); + } else { + SemType contraintSemtype; + if (constraint instanceof ParameterizedType parameterizedType) { + contraintSemtype = Builder.from(cx, parameterizedType.getParamValueType()); + } else { + contraintSemtype = Builder.from(cx, constraint); + } + assert !Core.containsBasicType(contraintSemtype, Core.B_TYPE_TOP) : "XML is a pure semtype"; + semType = XmlUtils.xmlSequence(contraintSemtype); + } + return isReadOnly() ? Core.intersect(Builder.readonlyType(), semType) : semType; + } + + private SemType pickTopType() { + return switch (tag) { + case TypeTags.XML_TAG -> Builder.xmlType(); + case TypeTags.XML_ELEMENT_TAG -> Builder.xmlElementType(); + case TypeTags.XML_COMMENT_TAG -> Builder.xmlCommentType(); + case TypeTags.XML_PI_TAG -> Builder.xmlPIType(); + case TypeTags.XML_TEXT_TAG -> Builder.xmlTextType(); + default -> throw new IllegalStateException("Unexpected value: " + tag); + }; + } + @Override public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public Optional shapeOf(Context cx, Object object) { + XmlValue xmlValue = (XmlValue) object; + if (!isReadOnly(xmlValue)) { + return Optional.of(get(cx)); + } + return readonlyShapeOf(object); + } + + 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.xmlTextType()); + } else if (object instanceof XmlItem xml) { + return getSemType(xml, Builder.xmlElementType()); + } else if (object instanceof XmlComment xml) { + return getSemType(xml, Builder.xmlCommentType()); + } else if (object instanceof XmlPi xml) { + return getSemType(xml, Builder.xmlPIType()); + } + 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.readonlyType())); + } + 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..3ea468ab9bb3 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java @@ -0,0 +1,75 @@ +/* + * 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; + +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, BTypeIdSet 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/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..b0fe9de0fc28 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java @@ -0,0 +1,116 @@ +/* + * 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; + +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(errorSubtypeComplement()); + } + + private SubType errorSubtypeComplement() { + return Builder.bddSubtypeRo().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.bddSubtypeRo()) : 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/BXmlSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java new file mode 100644 index 000000000000..8d3ed726169b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java @@ -0,0 +1,160 @@ +/* + * 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; + +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, null, null, 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/DelegatedSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java index 3a145f5f44c0..8f0de442efce 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java @@ -18,9 +18,9 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.SubType; -public interface DelegatedSubType { +public interface DelegatedSubType extends SubTypeData { - Bdd inner(); + SubType inner(); } 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..f54380d95459 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java @@ -0,0 +1,60 @@ +/* + * 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; + +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.errorType(); + } else if (data == AllOrNothing.NOTHING) { + return Builder.neverType(); + } + + assert data instanceof Bdd; + SubType sd = ((Bdd) data).intersect(Builder.bddSubtypeRo()); + if (sd.equals(Builder.bddSubtypeRo())) { + return Builder.errorType(); + } + 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/FunctionQualifiers.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java index f62fece7667f..850b6131466d 100644 --- 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 @@ -25,7 +25,7 @@ public final class FunctionQualifiers { - private final static FunctionQualifiers DEFAULT = new FunctionQualifiers(false, false); + private static final FunctionQualifiers DEFAULT = new FunctionQualifiers(false, false); private final boolean isolated; private final boolean transactional; private SemType semType; 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 index bd84d06e6dee..8d96cfcc543c 100644 --- 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 @@ -21,7 +21,6 @@ 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.booleanConst; import static io.ballerina.runtime.api.types.semtype.Builder.stringConst; public record Member(String name, SemType valueTy, Kind kind, Visibility visibility, boolean immutable) { 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..4b25b22e2d0e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java @@ -0,0 +1,141 @@ +/* + * 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; + +// TODO: this should be a part of the public API +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.xmlType() : Builder.neverType(); + } + 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.xmlType()) : + "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.xmlType(), 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/values/ErrorValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java index dd534b85352e..c6daa4f91d6b 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 @@ -46,7 +46,6 @@ import java.util.Set; import java.util.StringJoiner; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_MAP; import static io.ballerina.runtime.api.constants.RuntimeConstants.BLANG_SRC_FILE_SUFFIX; import static io.ballerina.runtime.api.constants.RuntimeConstants.DOT; import static io.ballerina.runtime.api.constants.RuntimeConstants.MODULE_INIT_CLASS_NAME; @@ -83,7 +82,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)); } 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 819cca998c51..59626879b9ea 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 @@ -184,9 +184,9 @@ private static BIntersectionType setImmutableIntersectionType(Type type, Set cx, Map 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); default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); }; } + 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.errorType(); + } 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); @@ -309,11 +337,18 @@ private SemType resolveConstrainedTypeDesc(TypeTestContext cx, Map resolveMapTypeDesc(cx, mod, defn, depth, td); + case XML -> resolveXmlTypeDesc(cx, mod, defn, depth, td); default -> throw new UnsupportedOperationException( "Constrained type not implemented: " + refTypeNode.typeKind); }; } + 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(); @@ -449,6 +484,8 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp return resolveIntSubtype(name); } else if (td.pkgAlias.value.equals("string") && name.equals("Char")) { return Builder.charType(); + } else if (td.pkgAlias.value.equals("xml")) { + return resolveXmlSubType(name); } BLangNode moduleLevelDef = mod.get(name); @@ -470,10 +507,22 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp } } + private SemType resolveXmlSubType(String name) { + return switch (name) { + case "Element" -> Builder.xmlElementType(); + case "Comment" -> Builder.xmlCommentType(); + case "Text" -> Builder.xmlTextType(); + case "ProcessingInstruction" -> Builder.xmlPIType(); + 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.objectType())) { return getDistinctObjectType(env, innerType); + } else if (Core.isSubtypeSimple(innerType, Builder.errorType())) { + return getDistinctErrorType(env, innerType); } throw new IllegalArgumentException("Distinct type not supported for: " + innerType); } @@ -504,6 +553,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTy private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { return switch (td.typeKind) { case NEVER -> Builder.neverType(); + case XML -> Builder.xmlType(); default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); }; } @@ -520,6 +570,8 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) case READONLY -> Builder.readonlyType(); case ANY -> Builder.anyType(); case ANYDATA -> Builder.anyDataType((Context) cx.getInnerContext()); + case ERROR -> Builder.errorType(); + case XML -> Builder.xmlType(); default -> throw new IllegalStateException("Unknown type: " + td); }; } 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 index 64c873718281..96595192fa18 100644 --- 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 @@ -232,20 +232,8 @@ public Object[] runtimeFileNameProviderFunc() { "table-readonly-t.bal", "table-t.bal" )); - Predicate xmlFilter = createRuntimeFileNameFilter(Set.of( - "xml-complex-ro-tv.bal", - "xml-complex-rw-tv.bal", - "xml-never-tv.bal", - "xml-readonly-tv.bal", - "xml-sequence-tv.bal", - "xml-te.bal" - )); - Predicate objectFilter = createRuntimeFileNameFilter(Set.of( - )); return balFiles.stream() .filter(tableFilter) - .filter(xmlFilter) - .filter(objectFilter) .map(File::getAbsolutePath).toArray(); } 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-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java index 6fe2729ab2d9..52b93920e74a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java @@ -112,6 +112,12 @@ public void testSimpleQueryExprForXML4() { BRunUtil.invoke(result, "testSimpleQueryExprForXML4"); } + @Test + public void test() { + Object restult = BRunUtil.invoke(result, "simpleQueryExprForXML5"); + assert restult == null; + } + @Test(description = "Test simple query expression with limit clause for XMLs") public void testQueryExprWithLimitForXML() { Object returnValues = BRunUtil.invoke(result, "testQueryExprWithLimitForXML"); From 519e5ceb52284532dd8a3507d5526e2c256e3e62 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 2 Aug 2024 11:28:18 +0530 Subject: [PATCH 067/178] Fix unit tests --- .../expressions/binaryoperations/negative-type-test-expr.bal | 4 ++-- .../test-src/expressions/binaryoperations/type-test-expr.bal | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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..f0bab53f6e60 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 @@ -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 8073e3b11f14..df59a1f51636 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 @@ -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(){ From e6ea5e65692bcf308ff6cda8e5b96e5e73dd30e6 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 5 Aug 2024 13:22:13 +0530 Subject: [PATCH 068/178] Add workaround to value converter --- .../runtime/internal/TypeChecker.java | 2 +- .../runtime/internal/ValueConverter.java | 26 +++++++++++++++++++ .../test/resources/test-src/valuelib_test.bal | 4 +-- 3 files changed, 29 insertions(+), 3 deletions(-) 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 d22e2250ba2d..ceb223c1f674 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 @@ -150,7 +150,7 @@ public static Object checkCast(Object sourceVal, Type targetType) { throw createTypeCastError(sourceVal, targetType, errors); } - private static Context context() { + static Context context() { // We are pinning each context to thread. This depends on the assumption physical thread is not going to // get switched while type checking. Also for the same reason we don't need to synchronize this method. Thread currentThread = Thread.currentThread(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java index d29f432d9712..11d8034e04d4 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java @@ -31,6 +31,10 @@ import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; 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; @@ -55,6 +59,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; @@ -144,6 +149,7 @@ private static Object convert(Object value, Type targetType, Set if (matchingType.isReadOnly()) { newValue = CloneUtils.cloneReadOnly(newValue); } + newValue = xmlSequenceHack(newValue, matchingType); break; } @@ -169,6 +175,26 @@ 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(); + SemType targetSemType = Builder.from(cx, targetType); + List list = new ArrayList<>(); + for (BXml child : xmlSequence.getChildrenList()) { + SemType childType = Builder.from(cx, child.getType()); + boolean isReadonly = Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.readonlyType()); + 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/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal index 4ea468dd3ef0..585a44071e74 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 @@ -4705,8 +4705,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"); } From f923661c7f4445ba0ae632f4b48ecc42c4e16820 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 4 Aug 2024 08:26:39 +0530 Subject: [PATCH 069/178] Use lazy containers to xml types in Builder --- .../runtime/api/types/semtype/Builder.java | 24 ++++++++++++++----- .../api/types/semtype/LazyContainer.java | 15 +++++++----- .../internal/types/semtype/BIntSubType.java | 1 + 3 files changed, 28 insertions(+), 12 deletions(-) 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 index 8ea78414eceb..41784718b5b1 100644 --- 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 @@ -110,6 +110,15 @@ public final class Builder { new ConcurrentLazyContainer<>(() -> new MappingAtomicType( EMPTY_STRING_ARR, EMPTY_TYPES_ARR, PredefinedTypeEnv.getInstance().cellSemTypeInner())); + private static final ConcurrentLazyContainer XML_ELEMENT = new ConcurrentLazyContainer<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_ELEMENT_RO | XmlUtils.XML_PRIMITIVE_ELEMENT_RW)); + private static final ConcurrentLazyContainer XML_COMMENT = new ConcurrentLazyContainer<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_COMMENT_RO | XmlUtils.XML_PRIMITIVE_COMMENT_RW)); + private static final ConcurrentLazyContainer XML_TEXT = new ConcurrentLazyContainer<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_TEXT)); + private static final ConcurrentLazyContainer XML_PI = new ConcurrentLazyContainer<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW)); + private static final PredefinedTypeEnv PREDEFINED_TYPE_ENV = PredefinedTypeEnv.getInstance(); private Builder() { @@ -201,6 +210,7 @@ static SemType basicTypeUnion(int bitset) { default -> { if (Integer.bitCount(bitset) == 1) { int code = Integer.numberOfTrailingZeros(bitset); + // FIXME: this should always be true if (BasicTypeCache.isCached(code)) { yield BasicTypeCache.cache[code]; } @@ -272,6 +282,7 @@ static SubType[] initializeSubtypeArray(int some) { return new SubType[Integer.bitCount(some)]; } + // TODO: factor this to a separate class public static Optional shapeOf(Context cx, Object object) { if (object == null) { return Optional.of(nilType()); @@ -304,6 +315,7 @@ public static Optional shapeOf(Context cx, Object object) { return Optional.empty(); } + // Combine these methods maybe introduce a marker interface private static Optional typeOfXml(Context cx, XmlValue xmlValue) { TypeWithShape typeWithShape = (TypeWithShape) xmlValue.getType(); return typeWithShape.shapeOf(cx, xmlValue); @@ -347,6 +359,7 @@ public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMut } private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { + // FIXME: cache these when the semtype only has basic types CellAtomicType atomicCell = new CellAtomicType(ty, mut); TypeAtom atom = env.cellAtom(atomicCell); BddNode bdd = bddAtom(atom); @@ -378,19 +391,19 @@ public static SemType xmlType() { } public static SemType xmlElementType() { - return XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_ELEMENT_RO | XmlUtils.XML_PRIMITIVE_ELEMENT_RW); + return XML_ELEMENT.get(); } public static SemType xmlCommentType() { - return XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_COMMENT_RO | XmlUtils.XML_PRIMITIVE_COMMENT_RW); + return XML_COMMENT.get(); } public static SemType xmlTextType() { - return XmlUtils.xmlSequence(XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_TEXT)); + return XML_TEXT.get(); } public static SemType xmlPIType() { - return XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW); + return XML_PI.get(); } public static SemType anyDataType(Context context) { @@ -401,8 +414,7 @@ public static SemType anyDataType(Context context) { Env env = context.env; ListDefinition listDef = new ListDefinition(); MappingDefinition mapDef = new MappingDefinition(); - // TODO: add table, xml - SemType accum = unionOf(SIMPLE_OR_STRING, listDef.getSemType(env), mapDef.getSemType(env)); + SemType accum = unionOf(SIMPLE_OR_STRING, xmlType(), listDef.getSemType(env), mapDef.getSemType(env)); listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); context.anydataMemo = accum; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java index 9cde32b1f7ff..62539ca2bd5c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java @@ -24,7 +24,7 @@ class ConcurrentLazyContainer implements Supplier { private Supplier initializer; - private final AtomicReference value = new AtomicReference<>(); + private E value = null; ConcurrentLazyContainer(Supplier initializer) { this.initializer = initializer; @@ -32,13 +32,16 @@ class ConcurrentLazyContainer implements Supplier { @Override public E get() { - E result = value.get(); + E result = value; if (result == null) { - result = initializer.get(); - if (!value.compareAndSet(null, result)) { - result = value.get(); + synchronized (this) { + result = value; + if (result == null) { + result = initializer.get(); + value = result; + initializer = null; + } } - initializer = null; } return result; } 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 index 4c0e7d0f46e4..ff89c9360d37 100644 --- 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 @@ -65,6 +65,7 @@ public static BIntSubType createIntSubType(List values) { } 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)); From e5fb35fec3359517f15a3842527c8b560935676f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 4 Aug 2024 08:52:23 +0530 Subject: [PATCH 070/178] Use lazy suppliers for PredefinedEnv WIP --- .../api/types/semtype/LazyContainer.java | 33 ++ .../api/types/semtype/PredefinedTypeEnv.java | 492 ++++++++---------- 2 files changed, 253 insertions(+), 272 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java index 62539ca2bd5c..c4d114a92142 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java @@ -19,6 +19,7 @@ package io.ballerina.runtime.api.types.semtype; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Supplier; class ConcurrentLazyContainer implements Supplier { @@ -38,6 +39,7 @@ public E get() { result = value; if (result == null) { result = initializer.get(); + assert result != null; value = result; initializer = null; } @@ -46,3 +48,34 @@ public E get() { return result; } } + +class ConcurrentLazyContainerWithCallback implements Supplier { + + private E value = null; + private Supplier initializer; + private Consumer callback; + + ConcurrentLazyContainerWithCallback(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; + } +} \ No newline at end of file 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 index 8d2b27fa4f18..9d4b0675adf4 100644 --- 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 @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.List; 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_MAPPING; @@ -76,53 +77,157 @@ public static synchronized PredefinedTypeEnv getInstance() { // 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; + private final Supplier cellAtomicVal = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier cellAtomicNever = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); // Represent the typeAtom required to construct equivalent subtypes of map and (any|error)[]. - private CellAtomicType callAtomicInner; + private final Supplier cellAtomicInner = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); // TypeAtoms related to (map)[]. This is to avoid passing down env argument when doing // tableSubtypeComplement operation. - private CellAtomicType cellAtomicInnerMapping; - private ListAtomicType listAtomicMapping; + private final Supplier cellAtomicInnerMapping = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(union(Builder.mappingType(), Builder.undef()), + CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier listAtomicMapping = new ConcurrentLazyContainerWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMapping())))), + this::addInitializedListAtom + ); // 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 final Supplier cellAtomicInnerMappingRO = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(union(Builder.mappingRO(), Builder.undef()), + CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier listAtomicMappingRO = new ConcurrentLazyContainerWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMappingRO())))), + this::addInitializedListAtom + ); + private final Supplier cellAtomicInnerRO = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(Builder.innerReadOnly(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom + ); // 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; - - 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 final Supplier cellAtomicUndef = new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from(Builder.undef(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom + ); + + private final Supplier cellAtomicObjectMember = + new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from( + mappingSemTypeObjectMember(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED), + this::addInitializedCellAtom); + + private final Supplier cellAtomicObjectMemberKind = + new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from( + union(stringConst("field"), stringConst("method")), + CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + + private final Supplier cellAtomicObjectMemberRO = + new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from( + mappingSemTypeObjectMemberRO(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier cellAtomicObjectMemberVisibility = + new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from( + union(stringConst("public"), stringConst("private")), + CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier cellAtomicValRO = + new ConcurrentLazyContainerWithCallback<>( + () -> CellAtomicType.from( + Builder.readonlyType(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier listAtomicRO = new ConcurrentLazyContainerWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO()), + // FIXME: create a method to do this + this.initializedRecListAtoms::add + ); + private final Supplier mappingAtomicObject = new ConcurrentLazyContainerWithCallback<>( + () -> new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, + cellSemTypeObjectMember() + ), + this::addInitializedMapAtom + ); + private final Supplier mappingAtomicObjectMember = new ConcurrentLazyContainerWithCallback<>( + () -> new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeVal(), + cellSemTypeObjectMemberVisibility()}, + cellSemTypeUndef()), + this::addInitializedMapAtom + ); + private final Supplier mappingAtomicObjectMemberRO = new ConcurrentLazyContainerWithCallback<>( + () -> new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeValRO(), + cellSemTypeObjectMemberVisibility()}, + cellSemTypeUndef()), + this::addInitializedMapAtom + ); + private final Supplier mappingAtomicObjectRO = new ConcurrentLazyContainerWithCallback<>( + () -> new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, + cellSemTypeObjectMemberRO() + ), + // FIXME: + initializedRecMappingAtoms::add + ); + private final Supplier mappingAtomicRO = new ConcurrentLazyContainerWithCallback<>( + () -> new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO()), + // FIXME: + initializedRecMappingAtoms::add + ); + private final Supplier atomCellInner = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInner, this::cellAtomIndex); + + private final Supplier atomCellInnerMapping = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMapping, this::cellAtomIndex); + private final Supplier atomCellInnerMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMappingRO, this::cellAtomIndex); + private final Supplier atomCellInnerRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerRO, this::cellAtomIndex); + private final Supplier atomCellNever = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicNever, this::cellAtomIndex); + private final Supplier atomCellObjectMember = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMember, this::cellAtomIndex); + + private final Supplier atomCellObjectMemberKind = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberKind, this::cellAtomIndex); + private final Supplier atomCellObjectMemberRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberRO, this::cellAtomIndex); + private final Supplier atomCellObjectMemberVisibility = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberVisibility, this::cellAtomIndex); + private final Supplier atomCellUndef = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicUndef, this::cellAtomIndex); + private final Supplier atomCellVal = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicVal, this::cellAtomIndex); + private final Supplier atomCellValRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicValRO, this::cellAtomIndex); + private final Supplier atomListMapping = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMapping, this::listAtomIndex); private TypeAtom atomListMappingRO; private TypeAtom atomMappingObject; private TypeAtom atomMappingObjectMember; @@ -165,118 +270,56 @@ private int atomIndex(List> initia throw new IndexOutOfBoundsException(); } - synchronized CellAtomicType cellAtomicVal() { - if (cellAtomicVal == null) { - cellAtomicVal = CellAtomicType.from(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - addInitializedCellAtom(cellAtomicVal); - } - return cellAtomicVal; + CellAtomicType cellAtomicVal() { + return cellAtomicVal.get(); } - synchronized TypeAtom atomCellVal() { - if (atomCellVal == null) { - CellAtomicType cellAtomicVal = cellAtomicVal(); - atomCellVal = createTypeAtom(cellAtomIndex(cellAtomicVal), cellAtomicVal); - } - return atomCellVal; + TypeAtom atomCellVal() { + return atomCellVal.get(); } - synchronized CellAtomicType cellAtomicNever() { - if (cellAtomicNever == null) { - cellAtomicNever = CellAtomicType.from(Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - addInitializedCellAtom(cellAtomicNever); - } - return cellAtomicNever; + CellAtomicType cellAtomicNever() { + return cellAtomicNever.get(); } - synchronized TypeAtom atomCellNever() { - if (atomCellNever == null) { - CellAtomicType cellAtomicNever = cellAtomicNever(); - atomCellNever = createTypeAtom(cellAtomIndex(cellAtomicNever), cellAtomicNever); - } - return atomCellNever; + TypeAtom atomCellNever() { + return atomCellNever.get(); } - synchronized CellAtomicType cellAtomicInner() { - if (callAtomicInner == null) { - callAtomicInner = CellAtomicType.from(Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - addInitializedCellAtom(callAtomicInner); - } - return callAtomicInner; + CellAtomicType cellAtomicInner() { + return cellAtomicInner.get(); } - synchronized TypeAtom atomCellInner() { - if (atomCellInner == null) { - CellAtomicType cellAtomicInner = this.cellAtomicInner(); - atomCellInner = createTypeAtom(cellAtomIndex(cellAtomicInner), cellAtomicInner); - } - return atomCellInner; + TypeAtom atomCellInner() { + return atomCellInner.get(); } - synchronized CellAtomicType cellAtomicInnerMapping() { - if (cellAtomicInnerMapping == null) { - cellAtomicInnerMapping = - CellAtomicType.from(union(Builder.mappingType(), Builder.undef()), - CellAtomicType.CellMutability.CELL_MUT_LIMITED); - addInitializedCellAtom(cellAtomicInnerMapping); - } - return cellAtomicInnerMapping; + CellAtomicType cellAtomicInnerMapping() { + return cellAtomicInnerMapping.get(); } - synchronized TypeAtom atomCellInnerMapping() { - if (atomCellInnerMapping == null) { - CellAtomicType cellAtomicInnerMapping = cellAtomicInnerMapping(); - atomCellInnerMapping = createTypeAtom(cellAtomIndex(cellAtomicInnerMapping), cellAtomicInnerMapping); - } - return atomCellInnerMapping; + TypeAtom atomCellInnerMapping() { + return atomCellInnerMapping.get(); } - synchronized CellAtomicType cellAtomicInnerMappingRO() { - if (cellAtomicInnerMappingRO == null) { - cellAtomicInnerMappingRO = - CellAtomicType.from(union(Builder.mappingRO(), Builder.undef()), - CellAtomicType.CellMutability.CELL_MUT_LIMITED); - addInitializedCellAtom(cellAtomicInnerMappingRO); - } - return cellAtomicInnerMappingRO; + CellAtomicType cellAtomicInnerMappingRO() { + return cellAtomicInnerMappingRO.get(); } - synchronized TypeAtom atomCellInnerMappingRO() { - if (atomCellInnerMappingRO == null) { - CellAtomicType cellAtomicInnerMappingRO = cellAtomicInnerMappingRO(); - atomCellInnerMappingRO = - createTypeAtom(cellAtomIndex(cellAtomicInnerMappingRO), cellAtomicInnerMappingRO); - } - return atomCellInnerMappingRO; + TypeAtom atomCellInnerMappingRO() { + return atomCellInnerMappingRO.get(); } - synchronized ListAtomicType listAtomicMapping() { - if (listAtomicMapping == null) { - listAtomicMapping = new ListAtomicType( - FixedLengthArray.empty(), basicSubType( - BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMapping()))) - ); - addInitializedListAtom(listAtomicMapping); - } - return listAtomicMapping; + ListAtomicType listAtomicMapping() { + return listAtomicMapping.get(); } - synchronized TypeAtom atomListMapping() { - if (atomListMapping == null) { - ListAtomicType listAtomicMapping = listAtomicMapping(); - atomListMapping = createTypeAtom(listAtomIndex(listAtomicMapping), listAtomicMapping); - } - return atomListMapping; + TypeAtom atomListMapping() { + return atomListMapping.get(); } - synchronized ListAtomicType listAtomicMappingRO() { - if (listAtomicMappingRO == null) { - listAtomicMappingRO = new ListAtomicType(FixedLengthArray.empty(), basicSubType( - BT_CELL, - BCellSubType.createDelegate(bddAtom(atomCellInnerMappingRO())))); - addInitializedListAtom(listAtomicMappingRO); - } - return listAtomicMappingRO; + ListAtomicType listAtomicMappingRO() { + return listAtomicMappingRO.get(); } synchronized TypeAtom atomListMappingRO() { @@ -287,67 +330,32 @@ synchronized TypeAtom atomListMappingRO() { return atomListMappingRO; } - synchronized CellAtomicType cellAtomicInnerRO() { - if (cellAtomicInnerRO == null) { - cellAtomicInnerRO = - CellAtomicType.from(Builder.innerReadOnly(), CellAtomicType.CellMutability.CELL_MUT_NONE); - addInitializedCellAtom(cellAtomicInnerRO); - } - return cellAtomicInnerRO; + CellAtomicType cellAtomicInnerRO() { + return cellAtomicInnerRO.get(); } - synchronized TypeAtom atomCellInnerRO() { - if (atomCellInnerRO == null) { - CellAtomicType cellAtomicInnerRO = cellAtomicInnerRO(); - atomCellInnerRO = createTypeAtom(cellAtomIndex(cellAtomicInnerRO), cellAtomicInnerRO); - } - return atomCellInnerRO; + TypeAtom atomCellInnerRO() { + return atomCellInnerRO.get(); } - synchronized CellAtomicType cellAtomicUndef() { - if (cellAtomicUndef == null) { - cellAtomicUndef = CellAtomicType.from(Builder.undef(), CellAtomicType.CellMutability.CELL_MUT_NONE); - addInitializedCellAtom(cellAtomicUndef); - } - return cellAtomicUndef; + CellAtomicType cellAtomicUndef() { + return cellAtomicUndef.get(); } - synchronized TypeAtom atomCellUndef() { - if (atomCellUndef == null) { - CellAtomicType cellAtomicUndef = cellAtomicUndef(); - atomCellUndef = createTypeAtom(cellAtomIndex(cellAtomicUndef), cellAtomicUndef); - } - return atomCellUndef; + TypeAtom atomCellUndef() { + return atomCellUndef.get(); } - synchronized CellAtomicType cellAtomicValRO() { - if (cellAtomicValRO == null) { - cellAtomicValRO = CellAtomicType.from( - Builder.readonlyType(), CellAtomicType.CellMutability.CELL_MUT_NONE - ); - addInitializedCellAtom(cellAtomicValRO); - } - return cellAtomicValRO; + CellAtomicType cellAtomicValRO() { + return cellAtomicValRO.get(); } - synchronized TypeAtom atomCellValRO() { - if (atomCellValRO == null) { - CellAtomicType cellAtomicValRO = cellAtomicValRO(); - atomCellValRO = createTypeAtom(cellAtomIndex(cellAtomicValRO), cellAtomicValRO); - } - return atomCellValRO; + TypeAtom atomCellValRO() { + return atomCellValRO.get(); } - synchronized MappingAtomicType mappingAtomicObjectMemberRO() { - if (mappingAtomicObjectMemberRO == null) { - mappingAtomicObjectMemberRO = new MappingAtomicType( - new String[]{"kind", "value", "visibility"}, - new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeValRO(), - cellSemTypeObjectMemberVisibility()}, - cellSemTypeUndef()); - addInitializedMapAtom(mappingAtomicObjectMemberRO); - } - return mappingAtomicObjectMemberRO; + MappingAtomicType mappingAtomicObjectMemberRO() { + return mappingAtomicObjectMemberRO.get(); } synchronized TypeAtom atomMappingObjectMemberRO() { @@ -359,75 +367,32 @@ synchronized TypeAtom atomMappingObjectMemberRO() { return atomMappingObjectMemberRO; } - synchronized CellAtomicType cellAtomicObjectMemberRO() { - if (cellAtomicObjectMemberRO == null) { - cellAtomicObjectMemberRO = CellAtomicType.from( - mappingSemTypeObjectMemberRO(), CellAtomicType.CellMutability.CELL_MUT_NONE - ); - addInitializedCellAtom(cellAtomicObjectMemberRO); - } - return cellAtomicObjectMemberRO; + CellAtomicType cellAtomicObjectMemberRO() { + return cellAtomicObjectMemberRO.get(); } - synchronized TypeAtom atomCellObjectMemberRO() { - if (atomCellObjectMemberRO == null) { - CellAtomicType cellAtomicObjectMemberRO = cellAtomicObjectMemberRO(); - atomCellObjectMemberRO = createTypeAtom(cellAtomIndex(cellAtomicObjectMemberRO), cellAtomicObjectMemberRO); - } - return atomCellObjectMemberRO; + TypeAtom atomCellObjectMemberRO() { + return atomCellObjectMemberRO.get(); } - synchronized CellAtomicType cellAtomicObjectMemberKind() { - if (cellAtomicObjectMemberKind == null) { - cellAtomicObjectMemberKind = CellAtomicType.from( - union(stringConst("field"), stringConst("method")), - CellAtomicType.CellMutability.CELL_MUT_NONE - ); - addInitializedCellAtom(cellAtomicObjectMemberKind); - } - return cellAtomicObjectMemberKind; + CellAtomicType cellAtomicObjectMemberKind() { + return cellAtomicObjectMemberKind.get(); } - synchronized TypeAtom atomCellObjectMemberKind() { - if (atomCellObjectMemberKind == null) { - CellAtomicType cellAtomicObjectMemberKind = cellAtomicObjectMemberKind(); - atomCellObjectMemberKind = - createTypeAtom(cellAtomIndex(cellAtomicObjectMemberKind), cellAtomicObjectMemberKind); - } - return atomCellObjectMemberKind; + TypeAtom atomCellObjectMemberKind() { + return atomCellObjectMemberKind.get(); } - synchronized CellAtomicType cellAtomicObjectMemberVisibility() { - if (cellAtomicObjectMemberVisibility == null) { - cellAtomicObjectMemberVisibility = CellAtomicType.from( - union(stringConst("public"), stringConst("private")), - CellAtomicType.CellMutability.CELL_MUT_NONE - ); - addInitializedCellAtom(cellAtomicObjectMemberVisibility); - } - return cellAtomicObjectMemberVisibility; + CellAtomicType cellAtomicObjectMemberVisibility() { + return cellAtomicObjectMemberVisibility.get(); } - synchronized TypeAtom atomCellObjectMemberVisibility() { - if (atomCellObjectMemberVisibility == null) { - CellAtomicType cellAtomicObjectMemberVisibility = cellAtomicObjectMemberVisibility(); - atomCellObjectMemberVisibility = createTypeAtom(cellAtomIndex(cellAtomicObjectMemberVisibility), - cellAtomicObjectMemberVisibility); - } - return atomCellObjectMemberVisibility; + TypeAtom atomCellObjectMemberVisibility() { + return atomCellObjectMemberVisibility.get(); } - synchronized MappingAtomicType mappingAtomicObjectMember() { - if (mappingAtomicObjectMember == null) { - mappingAtomicObjectMember = new MappingAtomicType( - new String[]{"kind", "value", "visibility"}, - new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeVal(), - cellSemTypeObjectMemberVisibility()}, - cellSemTypeUndef()); - ; - addInitializedMapAtom(mappingAtomicObjectMember); - } - return mappingAtomicObjectMember; + MappingAtomicType mappingAtomicObjectMember() { + return mappingAtomicObjectMember.get(); } synchronized TypeAtom atomMappingObjectMember() { @@ -439,33 +404,16 @@ synchronized TypeAtom atomMappingObjectMember() { return atomMappingObjectMember; } - synchronized CellAtomicType cellAtomicObjectMember() { - if (cellAtomicObjectMember == null) { - cellAtomicObjectMember = CellAtomicType.from( - mappingSemTypeObjectMember(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED - ); - addInitializedCellAtom(cellAtomicObjectMember); - } - return cellAtomicObjectMember; + CellAtomicType cellAtomicObjectMember() { + return cellAtomicObjectMember.get(); } - synchronized TypeAtom atomCellObjectMember() { - if (atomCellObjectMember == null) { - CellAtomicType cellAtomicObjectMember = cellAtomicObjectMember(); - atomCellObjectMember = createTypeAtom(cellAtomIndex(cellAtomicObjectMember), cellAtomicObjectMember); - } - return atomCellObjectMember; + TypeAtom atomCellObjectMember() { + return atomCellObjectMember.get(); } - synchronized MappingAtomicType mappingAtomicObject() { - if (mappingAtomicObject == null) { - mappingAtomicObject = new MappingAtomicType( - new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, - cellSemTypeObjectMember() - ); - addInitializedMapAtom(mappingAtomicObject); - } - return mappingAtomicObject; + MappingAtomicType mappingAtomicObject() { + return mappingAtomicObject.get(); } synchronized TypeAtom atomMappingObject() { @@ -476,31 +424,16 @@ synchronized TypeAtom atomMappingObject() { return atomMappingObject; } - synchronized ListAtomicType listAtomicRO() { - if (listAtomicRO == null) { - listAtomicRO = new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO()); - initializedRecListAtoms.add(listAtomicRO); - } - return listAtomicRO; + ListAtomicType listAtomicRO() { + return listAtomicRO.get(); } synchronized MappingAtomicType mappingAtomicRO() { - if (mappingAtomicRO == null) { - mappingAtomicRO = new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO()); - initializedRecMappingAtoms.add(mappingAtomicRO); - } - return mappingAtomicRO; + return mappingAtomicRO.get(); } - synchronized MappingAtomicType mappingAtomicObjectRO() { - if (mappingAtomicObjectRO == null) { - mappingAtomicObjectRO = new MappingAtomicType( - new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, - cellSemTypeObjectMemberRO() - ); - initializedRecMappingAtoms.add(mappingAtomicObjectRO); - } - return mappingAtomicObjectRO; + MappingAtomicType mappingAtomicObjectRO() { + return mappingAtomicObjectRO.get(); } // Due to some reason SpotBug thinks this method is overrideable if we don't put final here as well. @@ -582,4 +515,19 @@ private SemType cellSemTypeInnerRO() { private record InitializedTypeAtom(E atomicType, int index) { } + + private static Supplier createTypeAtomSupplierFromCellAtomicSupplier( + Supplier atomicTypeSupplier, IndexSupplier indexSupplier) { + return new ConcurrentLazyContainer<>(() -> { + E atomicType = atomicTypeSupplier.get(); + int index = indexSupplier.get(atomicType); + return createTypeAtom(index, atomicType); + }); + } + + @FunctionalInterface + private interface IndexSupplier { + + int get(E atomicType); + } } From 355ebc987cd7be5ce4cef0077f79b8aea06003d5 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 4 Aug 2024 16:07:35 +0530 Subject: [PATCH 071/178] Rename Lazy containers to lazy suppliers --- .../runtime/api/types/semtype/Builder.java | 26 +++---- .../{LazyContainer.java => LazySupplier.java} | 9 ++- .../api/types/semtype/PredefinedTypeEnv.java | 70 +++++++++---------- 3 files changed, 49 insertions(+), 56 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/{LazyContainer.java => LazySupplier.java} (86%) 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 index 41784718b5b1..81e734d92dfc 100644 --- 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 @@ -90,33 +90,33 @@ public final class Builder { 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); - private static final ConcurrentLazyContainer READONLY_TYPE = new ConcurrentLazyContainer<>(() -> unionOf( + private static final ConcurrentLazySupplier READONLY_TYPE = 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_XML, XmlUtils.XML_SUBTYPE_RO) )); - private static final ConcurrentLazyContainer MAPPING_RO = new ConcurrentLazyContainer<>(() -> + private static final ConcurrentLazySupplier MAPPING_RO = new ConcurrentLazySupplier<>(() -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())) ); - private static final ConcurrentLazyContainer INNER_RO = - new ConcurrentLazyContainer<>(() -> union(readonlyType(), inner())); + private static final ConcurrentLazySupplier INNER_RO = + new ConcurrentLazySupplier<>(() -> union(readonlyType(), inner())); - private static final ConcurrentLazyContainer LIST_ATOMIC_INNER = - new ConcurrentLazyContainer<>(() -> new ListAtomicType( + private static final ConcurrentLazySupplier LIST_ATOMIC_INNER = + new ConcurrentLazySupplier<>(() -> new ListAtomicType( FixedLengthArray.empty(), PredefinedTypeEnv.getInstance().cellSemTypeInner())); - private static final ConcurrentLazyContainer MAPPING_ATOMIC_INNER = - new ConcurrentLazyContainer<>(() -> new MappingAtomicType( + private static final ConcurrentLazySupplier MAPPING_ATOMIC_INNER = + new ConcurrentLazySupplier<>(() -> new MappingAtomicType( EMPTY_STRING_ARR, EMPTY_TYPES_ARR, PredefinedTypeEnv.getInstance().cellSemTypeInner())); - private static final ConcurrentLazyContainer XML_ELEMENT = new ConcurrentLazyContainer<>(() -> + private static final ConcurrentLazySupplier XML_ELEMENT = new ConcurrentLazySupplier<>(() -> XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_ELEMENT_RO | XmlUtils.XML_PRIMITIVE_ELEMENT_RW)); - private static final ConcurrentLazyContainer XML_COMMENT = new ConcurrentLazyContainer<>(() -> + private static final ConcurrentLazySupplier XML_COMMENT = new ConcurrentLazySupplier<>(() -> XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_COMMENT_RO | XmlUtils.XML_PRIMITIVE_COMMENT_RW)); - private static final ConcurrentLazyContainer XML_TEXT = new ConcurrentLazyContainer<>(() -> + private static final ConcurrentLazySupplier XML_TEXT = new ConcurrentLazySupplier<>(() -> XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_TEXT)); - private static final ConcurrentLazyContainer XML_PI = new ConcurrentLazyContainer<>(() -> + private static final ConcurrentLazySupplier XML_PI = new ConcurrentLazySupplier<>(() -> XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW)); private static final PredefinedTypeEnv PREDEFINED_TYPE_ENV = PredefinedTypeEnv.getInstance(); @@ -210,7 +210,7 @@ static SemType basicTypeUnion(int bitset) { default -> { if (Integer.bitCount(bitset) == 1) { int code = Integer.numberOfTrailingZeros(bitset); - // FIXME: this should always be true + // TODO: what are the others? if (BasicTypeCache.isCached(code)) { yield BasicTypeCache.cache[code]; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java similarity index 86% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java index c4d114a92142..2d81dabe7eff 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazyContainer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java @@ -18,16 +18,15 @@ package io.ballerina.runtime.api.types.semtype; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; -class ConcurrentLazyContainer implements Supplier { +class ConcurrentLazySupplier implements Supplier { private Supplier initializer; private E value = null; - ConcurrentLazyContainer(Supplier initializer) { + ConcurrentLazySupplier(Supplier initializer) { this.initializer = initializer; } @@ -49,13 +48,13 @@ public E get() { } } -class ConcurrentLazyContainerWithCallback implements Supplier { +class ConcurrentLazySupplierWithCallback implements Supplier { private E value = null; private Supplier initializer; private Consumer callback; - ConcurrentLazyContainerWithCallback(Supplier initializer, Consumer callback) { + ConcurrentLazySupplierWithCallback(Supplier initializer, Consumer callback) { this.initializer = initializer; this.callback = callback; } 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 index 9d4b0675adf4..31f98d150b06 100644 --- 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 @@ -77,29 +77,29 @@ public static synchronized PredefinedTypeEnv getInstance() { // 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 ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicVal = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); - private final Supplier cellAtomicNever = new ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicNever = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); // Represent the typeAtom required to construct equivalent subtypes of map and (any|error)[]. - private final Supplier cellAtomicInner = new ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicInner = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); // TypeAtoms related to (map)[]. This is to avoid passing down env argument when doing // tableSubtypeComplement operation. - private final Supplier cellAtomicInnerMapping = new ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicInnerMapping = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(union(Builder.mappingType(), Builder.undef()), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); - private final Supplier listAtomicMapping = new ConcurrentLazyContainerWithCallback<>( + private final Supplier listAtomicMapping = new ConcurrentLazySupplierWithCallback<>( () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMapping())))), this::addInitializedListAtom @@ -108,70 +108,70 @@ public static synchronized PredefinedTypeEnv getInstance() { // 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 ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicInnerMappingRO = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(union(Builder.mappingRO(), Builder.undef()), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); - private final Supplier listAtomicMappingRO = new ConcurrentLazyContainerWithCallback<>( + private final Supplier listAtomicMappingRO = new ConcurrentLazySupplierWithCallback<>( () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMappingRO())))), this::addInitializedListAtom ); - private final Supplier cellAtomicInnerRO = new ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicInnerRO = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.innerReadOnly(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom ); // TypeAtoms related to [any|error, any|error]. This is to avoid passing down env argument when doing // streamSubtypeComplement operation. - private final Supplier cellAtomicUndef = new ConcurrentLazyContainerWithCallback<>( + private final Supplier cellAtomicUndef = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.undef(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom ); private final Supplier cellAtomicObjectMember = - new ConcurrentLazyContainerWithCallback<>( + new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( mappingSemTypeObjectMember(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED), this::addInitializedCellAtom); private final Supplier cellAtomicObjectMemberKind = - new ConcurrentLazyContainerWithCallback<>( + new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( union(stringConst("field"), stringConst("method")), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); private final Supplier cellAtomicObjectMemberRO = - new ConcurrentLazyContainerWithCallback<>( + new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( mappingSemTypeObjectMemberRO(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); private final Supplier cellAtomicObjectMemberVisibility = - new ConcurrentLazyContainerWithCallback<>( + new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( union(stringConst("public"), stringConst("private")), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); private final Supplier cellAtomicValRO = - new ConcurrentLazyContainerWithCallback<>( + new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( Builder.readonlyType(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); - private final Supplier listAtomicRO = new ConcurrentLazyContainerWithCallback<>( + private final Supplier listAtomicRO = new ConcurrentLazySupplierWithCallback<>( () -> new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO()), // FIXME: create a method to do this this.initializedRecListAtoms::add ); - private final Supplier mappingAtomicObject = new ConcurrentLazyContainerWithCallback<>( + private final Supplier mappingAtomicObject = new ConcurrentLazySupplierWithCallback<>( () -> new MappingAtomicType( new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, cellSemTypeObjectMember() ), this::addInitializedMapAtom ); - private final Supplier mappingAtomicObjectMember = new ConcurrentLazyContainerWithCallback<>( + private final Supplier mappingAtomicObjectMember = new ConcurrentLazySupplierWithCallback<>( () -> new MappingAtomicType( new String[]{"kind", "value", "visibility"}, new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeVal(), @@ -179,7 +179,7 @@ public static synchronized PredefinedTypeEnv getInstance() { cellSemTypeUndef()), this::addInitializedMapAtom ); - private final Supplier mappingAtomicObjectMemberRO = new ConcurrentLazyContainerWithCallback<>( + private final Supplier mappingAtomicObjectMemberRO = new ConcurrentLazySupplierWithCallback<>( () -> new MappingAtomicType( new String[]{"kind", "value", "visibility"}, new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeValRO(), @@ -187,7 +187,7 @@ public static synchronized PredefinedTypeEnv getInstance() { cellSemTypeUndef()), this::addInitializedMapAtom ); - private final Supplier mappingAtomicObjectRO = new ConcurrentLazyContainerWithCallback<>( + private final Supplier mappingAtomicObjectRO = new ConcurrentLazySupplierWithCallback<>( () -> new MappingAtomicType( new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal()}, cellSemTypeObjectMemberRO() @@ -195,7 +195,7 @@ public static synchronized PredefinedTypeEnv getInstance() { // FIXME: initializedRecMappingAtoms::add ); - private final Supplier mappingAtomicRO = new ConcurrentLazyContainerWithCallback<>( + private final Supplier mappingAtomicRO = new ConcurrentLazySupplierWithCallback<>( () -> new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO()), // FIXME: initializedRecMappingAtoms::add @@ -228,8 +228,10 @@ public static synchronized PredefinedTypeEnv getInstance() { createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicValRO, this::cellAtomIndex); private final Supplier atomListMapping = createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMapping, this::listAtomIndex); - private TypeAtom atomListMappingRO; - private TypeAtom atomMappingObject; + private final Supplier atomListMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); + private final Supplier atomMappingObject = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObject, this::mappingAtomIndex); private TypeAtom atomMappingObjectMember; private TypeAtom atomMappingObjectMemberRO; @@ -322,12 +324,8 @@ ListAtomicType listAtomicMappingRO() { return listAtomicMappingRO.get(); } - synchronized TypeAtom atomListMappingRO() { - if (atomListMappingRO == null) { - ListAtomicType listAtomicMappingRO = listAtomicMappingRO(); - atomListMappingRO = createTypeAtom(listAtomIndex(listAtomicMappingRO), listAtomicMappingRO); - } - return atomListMappingRO; + TypeAtom atomListMappingRO() { + return atomListMappingRO.get(); } CellAtomicType cellAtomicInnerRO() { @@ -358,7 +356,7 @@ MappingAtomicType mappingAtomicObjectMemberRO() { return mappingAtomicObjectMemberRO.get(); } - synchronized TypeAtom atomMappingObjectMemberRO() { + TypeAtom atomMappingObjectMemberRO() { if (atomMappingObjectMemberRO == null) { MappingAtomicType mappingAtomicObjectMemberRO = mappingAtomicObjectMemberRO(); atomMappingObjectMemberRO = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMemberRO), @@ -395,7 +393,7 @@ MappingAtomicType mappingAtomicObjectMember() { return mappingAtomicObjectMember.get(); } - synchronized TypeAtom atomMappingObjectMember() { + TypeAtom atomMappingObjectMember() { if (atomMappingObjectMember == null) { MappingAtomicType mappingAtomicObjectMember = mappingAtomicObjectMember(); atomMappingObjectMember = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMember), @@ -416,19 +414,15 @@ MappingAtomicType mappingAtomicObject() { return mappingAtomicObject.get(); } - synchronized TypeAtom atomMappingObject() { - if (atomMappingObject == null) { - MappingAtomicType mappingAtomicObject = mappingAtomicObject(); - atomMappingObject = createTypeAtom(mappingAtomIndex(mappingAtomicObject), mappingAtomicObject); - } - return atomMappingObject; + TypeAtom atomMappingObject() { + return atomMappingObject.get(); } ListAtomicType listAtomicRO() { return listAtomicRO.get(); } - synchronized MappingAtomicType mappingAtomicRO() { + MappingAtomicType mappingAtomicRO() { return mappingAtomicRO.get(); } @@ -518,7 +512,7 @@ private record InitializedTypeAtom(E atomicType, int index private static Supplier createTypeAtomSupplierFromCellAtomicSupplier( Supplier atomicTypeSupplier, IndexSupplier indexSupplier) { - return new ConcurrentLazyContainer<>(() -> { + return new ConcurrentLazySupplier<>(() -> { E atomicType = atomicTypeSupplier.get(); int index = indexSupplier.get(atomicType); return createTypeAtom(index, atomicType); From 3538323e0a8239d575e2590166671b4362534b7d Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 4 Aug 2024 17:37:14 +0530 Subject: [PATCH 072/178] Simplify context supplying --- .../io/ballerina/runtime/internal/TypeChecker.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) 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 ceb223c1f674..79ca961db51e 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 @@ -121,7 +121,8 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; - private static final Map contexts = new HashMap<>(10); + private final static ThreadLocal threadContext = + ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); public static Object checkCast(Object sourceVal, Type targetType) { @@ -153,15 +154,7 @@ public static Object checkCast(Object sourceVal, Type targetType) { static Context context() { // We are pinning each context to thread. This depends on the assumption physical thread is not going to // get switched while type checking. Also for the same reason we don't need to synchronize this method. - Thread currentThread = Thread.currentThread(); - long threadID = currentThread.getId(); - Context cx = contexts.get(threadID); - if (cx != null) { - return cx; - } - cx = Context.from(Env.getInstance()); - contexts.put(threadID, cx); - return cx; + return threadContext.get(); } public static long anyToInt(Object sourceVal) { From 95e1515f31fba441e87a3102decf2227c9274649 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 4 Aug 2024 18:09:57 +0530 Subject: [PATCH 073/178] More lazy initialization in predefined env --- .../api/types/semtype/PredefinedTypeEnv.java | 76 +++++++++++-------- 1 file changed, 43 insertions(+), 33 deletions(-) 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 index 31f98d150b06..1345527a7771 100644 --- 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 @@ -213,7 +213,6 @@ public static synchronized PredefinedTypeEnv getInstance() { createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicNever, this::cellAtomIndex); private final Supplier atomCellObjectMember = createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMember, this::cellAtomIndex); - private final Supplier atomCellObjectMemberKind = createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberKind, this::cellAtomIndex); private final Supplier atomCellObjectMemberRO = @@ -232,8 +231,36 @@ public static synchronized PredefinedTypeEnv getInstance() { createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); private final Supplier atomMappingObject = createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObject, this::mappingAtomIndex); - private TypeAtom atomMappingObjectMember; - private TypeAtom atomMappingObjectMemberRO; + + private final Supplier atomMappingObjectMember = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMember, this::mappingAtomIndex); + private final Supplier atomMappingObjectMemberRO = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMemberRO, this::mappingAtomIndex); + + // NOTE: it is okay for these to be not thread safe + private final Supplier cellSemTypeObjectMemberVisibility = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberVisibility())))); + private final Supplier cellSemTypeValRo = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellValRO())))); + private final Supplier cellSemTypeObjectMemberKind = new ConcurrentLazySupplier<>( + () -> Builder.basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())))); + private final Supplier cellSemTypeUndef = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellUndef.get())))); + private final Supplier mappingSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMemberRO())))); + private final Supplier cellSemTypeVal = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal())))); + private final Supplier mappingSemTypeObjectMember = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMember())))); + private final Supplier cellSemTypeObjectMember = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMember())))); + private final Supplier cellSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberRO())))); + private final Supplier cellSemTypeInner = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInner())))); + + private final Supplier cellSemTypeInnerRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO())))); private void addInitializedCellAtom(CellAtomicType atom) { addInitializedAtom(initializedCellAtoms, atom); @@ -357,12 +384,7 @@ MappingAtomicType mappingAtomicObjectMemberRO() { } TypeAtom atomMappingObjectMemberRO() { - if (atomMappingObjectMemberRO == null) { - MappingAtomicType mappingAtomicObjectMemberRO = mappingAtomicObjectMemberRO(); - atomMappingObjectMemberRO = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMemberRO), - mappingAtomicObjectMemberRO); - } - return atomMappingObjectMemberRO; + return atomMappingObjectMemberRO.get(); } CellAtomicType cellAtomicObjectMemberRO() { @@ -394,12 +416,7 @@ MappingAtomicType mappingAtomicObjectMember() { } TypeAtom atomMappingObjectMember() { - if (atomMappingObjectMember == null) { - MappingAtomicType mappingAtomicObjectMember = mappingAtomicObjectMember(); - atomMappingObjectMember = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMember), - mappingAtomicObjectMember); - } - return atomMappingObjectMember; + return atomMappingObjectMember.get(); } CellAtomicType cellAtomicObjectMember() { @@ -455,55 +472,48 @@ private int reservedRecAtomCount() { return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); } - // TODO: avoid creating these multiple times private SemType cellSemTypeObjectMemberKind() { - return Builder.basicSubType( - BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())) - ); + return cellSemTypeObjectMemberKind.get(); } private SemType cellSemTypeValRO() { - return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellValRO()))); + return cellSemTypeValRo.get(); } private SemType cellSemTypeObjectMemberVisibility() { - return basicSubType( - BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberVisibility())) - ); + return cellSemTypeObjectMemberVisibility.get(); } private SemType cellSemTypeUndef() { - return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellUndef()))); + return cellSemTypeUndef.get(); } private SemType mappingSemTypeObjectMemberRO() { - return basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMemberRO()))); + return mappingSemTypeObjectMemberRO.get(); } private SemType cellSemTypeVal() { - return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal()))); + return cellSemTypeVal.get(); } private SemType mappingSemTypeObjectMember() { - return basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMember()))); + return mappingSemTypeObjectMember.get(); } private SemType cellSemTypeObjectMember() { - return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMember()))); + return cellSemTypeObjectMember.get(); } private SemType cellSemTypeObjectMemberRO() { - return basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberRO()))); + return cellSemTypeObjectMemberRO.get(); } SemType cellSemTypeInner() { - return basicSubType(BT_CELL, - BCellSubType.createDelegate(bddAtom(atomCellInner()))); + return cellSemTypeInner.get(); } private SemType cellSemTypeInnerRO() { - return basicSubType( - BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO()))); + return cellSemTypeInnerRO.get(); } private record InitializedTypeAtom(E atomicType, int index) { From d9301b9d5cd3d079483e1de96bc7d46982d2d6ed Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 4 Aug 2024 18:25:42 +0530 Subject: [PATCH 074/178] Get rid of unwanted private methods and rearrange the code --- .../api/types/semtype/PredefinedTypeEnv.java | 313 +++++++----------- 1 file changed, 128 insertions(+), 185 deletions(-) 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 index 1345527a7771..89140d1c47a1 100644 --- 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 @@ -38,60 +38,38 @@ final class PredefinedTypeEnv { - private PredefinedTypeEnv() { - } - - private void initilizeEnv() { - // Initialize RecAtoms - mappingAtomicRO(); - listAtomicRO(); - mappingAtomicObjectRO(); - - // initialize atomic types - cellAtomicVal(); - cellAtomicNever(); - cellAtomicInner(); - cellAtomicInnerMapping(); - listAtomicMapping(); - cellAtomicInner(); - listAtomicMappingRO(); - cellAtomicInnerRO(); - } - private static PredefinedTypeEnv instance; - - public static synchronized PredefinedTypeEnv getInstance() { - if (instance == null) { - instance = new PredefinedTypeEnv(); - instance.initilizeEnv(); - } - 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 final Supplier cellAtomicVal = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.valType(), 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(Builder.neverType(), 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 Supplier cellAtomicInner = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.inner(), 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<>( @@ -99,12 +77,15 @@ public static synchronized PredefinedTypeEnv getInstance() { 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 @@ -113,154 +94,165 @@ public static synchronized PredefinedTypeEnv getInstance() { 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 atomListMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); private final Supplier cellAtomicInnerRO = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.innerReadOnly(), 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())))); + 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 cellAtomicUndef = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from(Builder.undef(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom ); - - private final Supplier cellAtomicObjectMember = - new ConcurrentLazySupplierWithCallback<>( - () -> CellAtomicType.from( - mappingSemTypeObjectMember(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED), - 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 cellAtomicObjectMemberKind = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( union(stringConst("field"), stringConst("method")), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); - - private final Supplier cellAtomicObjectMemberRO = - new ConcurrentLazySupplierWithCallback<>( - () -> CellAtomicType.from( - mappingSemTypeObjectMemberRO(), 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(stringConst("public"), stringConst("private")), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); - private final Supplier cellAtomicValRO = + 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( - Builder.readonlyType(), CellAtomicType.CellMutability.CELL_MUT_NONE), + mappingSemTypeObjectMember.get(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED), this::addInitializedCellAtom); - private final Supplier listAtomicRO = new ConcurrentLazySupplierWithCallback<>( - () -> new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO()), - // FIXME: create a method to do this - this.initializedRecListAtoms::add - ); + 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()}, - cellSemTypeObjectMember() + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal.get()}, + cellSemTypeObjectMember.get() ), this::addInitializedMapAtom ); - private final Supplier mappingAtomicObjectMember = new ConcurrentLazySupplierWithCallback<>( - () -> new MappingAtomicType( - new String[]{"kind", "value", "visibility"}, - new SemType[]{cellSemTypeObjectMemberKind(), cellSemTypeVal(), - cellSemTypeObjectMemberVisibility()}, - cellSemTypeUndef()), - this::addInitializedMapAtom - ); + private final Supplier atomMappingObject = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObject, this::mappingAtomIndex); + private final Supplier cellAtomicValRO = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + Builder.readonlyType(), 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(), cellSemTypeValRO(), - cellSemTypeObjectMemberVisibility()}, - cellSemTypeUndef()), + 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()}, - cellSemTypeObjectMemberRO() + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal.get()}, + cellSemTypeObjectMemberRO.get() ), - // FIXME: initializedRecMappingAtoms::add ); - private final Supplier mappingAtomicRO = new ConcurrentLazySupplierWithCallback<>( - () -> new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO()), - // FIXME: - initializedRecMappingAtoms::add - ); - private final Supplier atomCellInner = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInner, this::cellAtomIndex); - private final Supplier atomCellInnerMapping = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMapping, this::cellAtomIndex); - private final Supplier atomCellInnerMappingRO = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMappingRO, this::cellAtomIndex); - private final Supplier atomCellInnerRO = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerRO, this::cellAtomIndex); - private final Supplier atomCellNever = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicNever, this::cellAtomIndex); - private final Supplier atomCellObjectMember = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMember, this::cellAtomIndex); - private final Supplier atomCellObjectMemberKind = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberKind, this::cellAtomIndex); - private final Supplier atomCellObjectMemberRO = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberRO, this::cellAtomIndex); - private final Supplier atomCellObjectMemberVisibility = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberVisibility, this::cellAtomIndex); - private final Supplier atomCellUndef = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicUndef, this::cellAtomIndex); - private final Supplier atomCellVal = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicVal, this::cellAtomIndex); - private final Supplier atomCellValRO = - createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicValRO, this::cellAtomIndex); - private final Supplier atomListMapping = - createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMapping, this::listAtomIndex); - private final Supplier atomListMappingRO = - createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); - private final Supplier atomMappingObject = - createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObject, this::mappingAtomIndex); + private PredefinedTypeEnv() { + } - private final Supplier atomMappingObjectMember = - createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMember, this::mappingAtomIndex); - private final Supplier atomMappingObjectMemberRO = - createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMemberRO, this::mappingAtomIndex); + public static synchronized PredefinedTypeEnv getInstance() { + if (instance == null) { + instance = new PredefinedTypeEnv(); + instance.initilizeEnv(); + } + return instance; + } - // NOTE: it is okay for these to be not thread safe - private final Supplier cellSemTypeObjectMemberVisibility = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberVisibility())))); - private final Supplier cellSemTypeValRo = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellValRO())))); - private final Supplier cellSemTypeObjectMemberKind = new ConcurrentLazySupplier<>( - () -> Builder.basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())))); - private final Supplier cellSemTypeUndef = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellUndef.get())))); - private final Supplier mappingSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMemberRO())))); - private final Supplier cellSemTypeVal = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal())))); - private final Supplier mappingSemTypeObjectMember = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMember())))); - private final Supplier cellSemTypeObjectMember = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMember())))); - private final Supplier cellSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberRO())))); - private final Supplier cellSemTypeInner = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInner())))); + 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 final Supplier cellSemTypeInnerRO = new ConcurrentLazySupplier<>( - () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO())))); + private void initilizeEnv() { + // Initialize RecAtoms + mappingAtomicRO(); + listAtomicRO(); + mappingAtomicObjectRO(); + + // initialize atomic types + cellAtomicVal(); + cellAtomicNever(); + cellAtomicInner(); + cellAtomicInnerMapping(); + listAtomicMapping(); + cellAtomicInner(); + listAtomicMappingRO(); + cellAtomicInnerRO(); + } private void addInitializedCellAtom(CellAtomicType atom) { addInitializedAtom(initializedCellAtoms, atom); @@ -472,66 +464,17 @@ private int reservedRecAtomCount() { return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); } - private SemType cellSemTypeObjectMemberKind() { - return cellSemTypeObjectMemberKind.get(); - } - - private SemType cellSemTypeValRO() { - return cellSemTypeValRo.get(); - } - - private SemType cellSemTypeObjectMemberVisibility() { - return cellSemTypeObjectMemberVisibility.get(); - } - - private SemType cellSemTypeUndef() { - return cellSemTypeUndef.get(); - } - - private SemType mappingSemTypeObjectMemberRO() { - return mappingSemTypeObjectMemberRO.get(); - } - - private SemType cellSemTypeVal() { - return cellSemTypeVal.get(); - } - - private SemType mappingSemTypeObjectMember() { - return mappingSemTypeObjectMember.get(); - } - - private SemType cellSemTypeObjectMember() { - return cellSemTypeObjectMember.get(); - } - - private SemType cellSemTypeObjectMemberRO() { - return cellSemTypeObjectMemberRO.get(); - } - SemType cellSemTypeInner() { return cellSemTypeInner.get(); } - private SemType cellSemTypeInnerRO() { - return cellSemTypeInnerRO.get(); - } - - private record InitializedTypeAtom(E atomicType, int index) { - - } - - private static Supplier createTypeAtomSupplierFromCellAtomicSupplier( - Supplier atomicTypeSupplier, IndexSupplier indexSupplier) { - return new ConcurrentLazySupplier<>(() -> { - E atomicType = atomicTypeSupplier.get(); - int index = indexSupplier.get(atomicType); - return createTypeAtom(index, atomicType); - }); - } - @FunctionalInterface private interface IndexSupplier { int get(E atomicType); } + + private record InitializedTypeAtom(E atomicType, int index) { + + } } From ff36b2ab257721109338e4b02c1b35f030ab1799 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 6 Aug 2024 08:10:12 +0530 Subject: [PATCH 075/178] Impmlement handle semtype --- .../runtime/api/types/semtype/Builder.java | 5 ++ .../runtime/internal/types/BHandleType.java | 61 +++++++++++++------ .../internal/types/BTypeConverter.java | 3 +- .../port/test/RuntimeSemTypeResolver.java | 2 + 4 files changed, 49 insertions(+), 22 deletions(-) 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 index 81e734d92dfc..05de75b038d0 100644 --- 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 @@ -51,6 +51,7 @@ 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_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; @@ -406,6 +407,10 @@ public static SemType xmlPIType() { return XML_PI.get(); } + public static SemType handleType() { + return from(BT_HANDLE); + } + public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; if (memo != null) { 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 89b5482936bf..b3f1d30d2f43 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,65 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.HandleType; +import io.ballerina.runtime.api.types.semtype.Builder; 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(BHandleTypeImpl.create(typeName, pkg), Builder.handleType()); } - @Override - public V getZeroValue() { - return null; - } + private 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/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java index 40c7b72263ca..6b7f6a307d65 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java @@ -48,7 +48,7 @@ private BTypeConverter() { unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType(), Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), - Builder.xmlType()); + Builder.xmlType(), Builder.handleType()); private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); @@ -116,7 +116,6 @@ private record BTypeParts(SemType semTypePart, List bTypeParts) { private static BTypeParts split(Context cx, Type type) { if (type instanceof SemType) { return new BTypeParts(from(cx, type), Collections.emptyList()); - // TODO: } else if (type instanceof BXmlType) { return new BTypeParts(from(cx, type), Collections.emptyList()); } else if (type instanceof BUnionType unionType) { 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 index c96be67a15f4..21a5b438cf3d 100644 --- 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 @@ -461,6 +461,7 @@ private Optional resolveSingletonType(Object value, TypeKind targetType yield Optional.of(Builder.decimalConst(new BigDecimal(repr))); } case STRING -> Optional.of(Builder.stringConst((String) value)); + case HANDLE -> Optional.of(Builder.handleType()); default -> Optional.empty(); }; } @@ -572,6 +573,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) case ANYDATA -> Builder.anyDataType((Context) cx.getInnerContext()); case ERROR -> Builder.errorType(); case XML -> Builder.xmlType(); + case HANDLE -> Builder.handleType(); default -> throw new IllegalStateException("Unknown type: " + td); }; } From b601d2f1feea17db01fe29a0cc251c317234922c Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 6 Aug 2024 08:10:21 +0530 Subject: [PATCH 076/178] Cleanup BSemTypeWrapper --- .../ballerina/runtime/api/types/semtype/SemType.java | 10 +++++++--- .../runtime/internal/types/BSemTypeWrapper.java | 4 ++++ .../runtime/internal/types/semtype/PureSemType.java | 5 ----- 3 files changed, 11 insertions(+), 8 deletions(-) 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 index 30514e807e8b..9c79ff31445c 100644 --- 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 @@ -38,14 +38,18 @@ public abstract sealed class SemType implements BasicTypeBitSet permits BSemType public final int all; public final int some; + // TODO: this is messing up allignment either fill it or may be get rid of this + private final boolean useCache; private final SubType[] subTypeData; private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + // TODO: use a lazy supplier instead private Integer hashCode; - private static volatile AtomicInteger nextId = new AtomicInteger(1); - private final Integer typeID = nextId.getAndIncrement(); private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); private final TypeCheckResultCache resultCache; - private final boolean useCache; + + // TODO: this is for debug purposes get rid of this + private static volatile AtomicInteger nextId = new AtomicInteger(1); + private final Integer typeID = nextId.getAndIncrement(); protected SemType(int all, int some, SubType[] subTypeData) { this.all = all; 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 index 29fbc4bb134a..c8dabe3a150c 100644 --- 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 @@ -23,6 +23,8 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.SemType; +// TODO: make this a sealed class with clearly defined extensions + /** * 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. @@ -106,6 +108,7 @@ public boolean isNative() { return bType.isNative(); } + // FIXME: use semtype @Override public boolean isAnydata() { return bType.isAnydata(); @@ -116,6 +119,7 @@ public boolean isPureType() { return bType.isPureType(); } + // FIXME: use semtype @Override public boolean isReadOnly() { return bType.isReadOnly(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index 7fe00d5fd286..ec878f4bb183 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -28,16 +28,11 @@ */ public final class PureSemType extends SemType { - private final int index; - private static int nextIndex; - public PureSemType(int all, int some, SubType[] subTypeData) { super(all, some, subTypeData); - index = nextIndex++; } public PureSemType(int all) { super(all); - index = nextIndex++; } } From 048005756b77c5c314cef29f916e2425e7df0634 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 6 Aug 2024 09:23:26 +0530 Subject: [PATCH 077/178] Re-ennable type check cache Fix checkstyle violations --- .../runtime/api/types/semtype/Builder.java | 1 - .../runtime/api/types/semtype/Core.java | 10 +- .../api/types/semtype/LazySupplier.java | 2 +- .../runtime/api/types/semtype/SemType.java | 101 +++++++++++------- .../runtime/internal/TypeChecker.java | 4 +- .../internal/types/BSemTypeWrapper.java | 4 +- 6 files changed, 71 insertions(+), 51 deletions(-) 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 index 05de75b038d0..7df8f626f97b 100644 --- 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 @@ -360,7 +360,6 @@ public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMut } private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { - // FIXME: cache these when the semtype only has basic types CellAtomicType atomicCell = new CellAtomicType(ty, mut); TypeAtom atom = env.cellAtom(atomicCell); BddNode bdd = bddAtom(atom); 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 index 4d990b6b88e5..771a16f601cb 100644 --- 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 @@ -282,12 +282,12 @@ public static boolean isNever(SemType t) { public static boolean isSubType(Context cx, SemType t1, SemType t2) { // IF t1 and t2 are not pure semtypes calling this is an undefined -// SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); -// if (cached != SemType.CachedResult.NOT_FOUND) { -// return cached == SemType.CachedResult.TRUE; -// } + SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); + if (cached != SemType.CachedResult.NOT_FOUND) { + return cached == SemType.CachedResult.TRUE; + } boolean result = isEmpty(cx, diff(t1, t2)); -// t1.cacheSubTypeRelation(t2, result); + t1.cacheSubTypeRelation(t2, result); return result; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java index 2d81dabe7eff..59d2e5ff0955 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java @@ -77,4 +77,4 @@ public E get() { } return result; } -} \ No newline at end of file +} 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 index 9c79ff31445c..4dbb3e4d60d6 100644 --- 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 @@ -21,11 +21,12 @@ import io.ballerina.runtime.internal.types.BSemTypeWrapper; import io.ballerina.runtime.internal.types.semtype.PureSemType; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.WeakHashMap; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; @@ -36,30 +37,24 @@ */ public abstract sealed class SemType implements BasicTypeBitSet permits BSemTypeWrapper, PureSemType { + private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); + public final int all; public final int some; - // TODO: this is messing up allignment either fill it or may be get rid of this - private final boolean useCache; private final SubType[] subTypeData; - private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; - // TODO: use a lazy supplier instead + private Integer hashCode; - private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); - private final TypeCheckResultCache resultCache; - // TODO: this is for debug purposes get rid of this - private static volatile AtomicInteger nextId = new AtomicInteger(1); - private final Integer typeID = nextId.getAndIncrement(); + private final TypeCheckResultCache resultCache; protected SemType(int all, int some, SubType[] subTypeData) { this.all = all; this.some = some; this.subTypeData = subTypeData; if ((some & CACHEABLE_TYPE_MASK) != 0) { - useCache = true; this.resultCache = new TypeCheckResultCache(); } else { - useCache = false; this.resultCache = TypeCheckResultCache.EMPTY; } } @@ -126,6 +121,10 @@ private int computeHashCode() { return Objects.hash(all, some, Arrays.hashCode(subTypeData)); } + private boolean shouldCache() { + return (some & CACHEABLE_TYPE_MASK) != 0; + } + enum CachedResult { TRUE, FALSE, @@ -133,41 +132,49 @@ enum CachedResult { } CachedResult cachedSubTypeRelation(SemType other) { - if (!useCache) { + if (!shouldCache()) { return CachedResult.NOT_FOUND; } - int tid = other.typeID; - if (tid == typeID) { + if (this == other) { return CachedResult.TRUE; } - return resultCache.getCachedResult(tid); + return resultCache.getCachedResult(other); } void cacheSubTypeRelation(SemType other, boolean result) { - if (useCache) { - resultCache.cacheResult(other.typeID, result); + if (shouldCache()) { + resultCache.cacheResult(other, result); + assert isValidCacheState(other, result) : "Invalid cache state"; + } + } - CachedResult cachedResult = cachedSubTypeRelation(other); - if (cachedResult != CachedResult.NOT_FOUND && - cachedResult != (result ? CachedResult.TRUE : CachedResult.FALSE)) { - throw new IllegalStateException("Inconsistent cache state"); - } + private boolean isValidCacheState(SemType other, boolean result) { + CachedResult cachedResult = cachedSubTypeRelation(other); + return cachedResult == CachedResult.NOT_FOUND || + cachedResult == (result ? CachedResult.TRUE : CachedResult.FALSE); + } + + 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)]; } private static sealed class TypeCheckResultCache { private static final TypeCheckResultCache EMPTY = new EmptyTypeCheckResultCache(); - private static final int CACHE_LIMIT = 100; - // See if we can use an identity hashmap on semtypes instead of tid - private Map cache = new HashMap<>(); + // make this an int + private final Map cache = new WeakHashMap<>(); - protected void cacheResult(int tid, boolean result) { - cache.put((long) tid, result); + public void cacheResult(SemType semType, boolean result) { + cache.put(TypeCheckCacheKey.from(semType), result); } - protected CachedResult getCachedResult(int tid) { - Boolean cachedData = cache.get((long) tid); + public CachedResult getCachedResult(SemType semType) { + Boolean cachedData = cache.get(TypeCheckCacheKey.from(semType)); if (cachedData == null) { return CachedResult.NOT_FOUND; } @@ -178,22 +185,38 @@ protected CachedResult getCachedResult(int tid) { private static final class EmptyTypeCheckResultCache extends TypeCheckResultCache { @Override - public void cacheResult(int tid, boolean result) { + public void cacheResult(SemType semType, boolean result) { throw new UnsupportedOperationException("Empty cache"); } @Override - public CachedResult getCachedResult(int tid) { + public CachedResult getCachedResult(SemType semType) { throw new UnsupportedOperationException("Empty cache"); } } - public final SubType subTypeByCode(int code) { - if ((some() & (1 << code)) == 0) { - return null; + private record TypeCheckCacheKey(Reference semtype) { + + static TypeCheckCacheKey from(SemType semType) { + return new TypeCheckCacheKey(new WeakReference<>(semType)); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TypeCheckCacheKey other)) { + return false; + } + SemType thisSemType = semtype.get(); + SemType otherSemType = other.semtype.get(); + if (thisSemType == null || otherSemType == null) { + return false; + } + return thisSemType == otherSemType; + } + + @Override + public int hashCode() { + return System.identityHashCode(semtype.get()); } - int someMask = (1 << code) - 1; - int some = some() & someMask; - return subTypeData()[Integer.bitCount(some)]; } } 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 79ca961db51e..991be67f2b77 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 @@ -74,11 +74,9 @@ import io.ballerina.runtime.internal.values.XmlValue; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; @@ -121,7 +119,7 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; - private final static ThreadLocal threadContext = + private static final ThreadLocal threadContext = ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); public static Object checkCast(Object sourceVal, Type targetType) { 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 index c8dabe3a150c..26c9ee079424 100644 --- 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 @@ -108,7 +108,7 @@ public boolean isNative() { return bType.isNative(); } - // FIXME: use semtype + // TODO: use semtype @Override public boolean isAnydata() { return bType.isAnydata(); @@ -119,7 +119,7 @@ public boolean isPureType() { return bType.isPureType(); } - // FIXME: use semtype + // TODO: use semtype @Override public boolean isReadOnly() { return bType.isReadOnly(); From c7543d9c78d865cbd67ccc0c4a002c9e08b784b1 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 6 Aug 2024 10:32:31 +0530 Subject: [PATCH 078/178] Encapsulate access to all and some --- .../runtime/api/types/semtype/Builder.java | 2 +- .../runtime/api/types/semtype/Core.java | 46 +++++++++---------- .../runtime/api/types/semtype/SemType.java | 11 +++-- .../internal/types/semtype/BListProj.java | 2 +- 4 files changed, 31 insertions(+), 30 deletions(-) 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 index 7df8f626f97b..79392b8de086 100644 --- 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 @@ -75,7 +75,7 @@ public final class Builder { private static final SemType VAL = SemType.from(VT_MASK); private static final SemType OBJECT = from(BT_OBJECT); - private static final SemType INNER = basicTypeUnion(VAL.all | from(BasicTypeCode.BT_UNDEF).all); + private static final SemType INNER = basicTypeUnion(VAL.all() | from(BasicTypeCode.BT_UNDEF).all()); private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); private static final SemType SIMPLE_OR_STRING = basicTypeUnion((1 << BasicTypeCode.BT_NIL.code()) 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 index 771a16f601cb..f4790c716ef7 100644 --- 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 @@ -59,10 +59,10 @@ 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; + int all1 = t1.all(); + int all2 = t2.all(); + int some1 = t1.some(); + int some2 = t2.some(); if (some1 == 0) { if (some2 == 0) { return Builder.basicTypeUnion(all1 & ~all2); @@ -130,8 +130,8 @@ public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { // 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 & listType().all) != 0 ? Builder.valType() : Builder.neverType(); + if (t.some() == 0) { + return (t.all() & listType().all()) != 0 ? Builder.valType() : Builder.neverType(); } else { SubTypeData keyData = intSubtype(k); if (isNothingSubtype(keyData)) { @@ -194,10 +194,10 @@ private static SubType[] filterNulls(SubType[] subtypes) { 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; + 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); @@ -257,10 +257,10 @@ public static SemType intersect(SemType t1, SemType t2) { } public static boolean isEmpty(Context cx, SemType t) { - if (t.some == 0) { - return t.all == 0; + if (t.some() == 0) { + return t.all() == 0; } - if (t.all != 0) { + if (t.all() != 0) { return false; } for (SubType subType : t.subTypeData()) { @@ -277,7 +277,7 @@ public static SemType complement(SemType t) { } public static boolean isNever(SemType t) { - return t.all == 0 && t.some == 0; + return t.all() == 0 && t.some() == 0; } public static boolean isSubType(Context cx, SemType t1, SemType t2) { @@ -293,7 +293,7 @@ public static boolean isSubType(Context cx, SemType t1, SemType t2) { public static boolean isSubtypeSimple(SemType t1, SemType t2) { assert t1 != null && t2 != null; - int bits = t1.all | t1.some; + int bits = t1.all() | t1.some(); return (bits & ~t2.all()) == 0; } @@ -312,10 +312,10 @@ public static SubTypeData stringSubtype(SemType t) { } public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { - if ((s.all & (1 << code.code())) != 0) { + if ((s.all() & (1 << code.code())) != 0) { return AllOrNothing.ALL; } - if (s.some == 0) { + if (s.some() == 0) { return AllOrNothing.NOTHING; } SubType subType = s.subTypeByCode(code.code()); @@ -324,8 +324,8 @@ public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { } public static boolean containsBasicType(SemType t1, SemType t2) { - int bits = t1.all | t1.some; - return (bits & t2.all) != 0; + int bits = t1.all() | t1.some(); + return (bits & t2.all()) != 0; } public static boolean isSameType(Context cx, SemType t1, SemType t2) { @@ -333,7 +333,7 @@ public static boolean isSameType(Context cx, SemType t1, SemType t2) { } public static SemType widenToBasicTypes(SemType t) { - int all = t.all | t.some; + int all = t.all() | t.some(); if (cardinality(all) > 1) { throw new IllegalStateException("Cannot widen to basic type for a type with multiple basic types"); } @@ -345,10 +345,10 @@ private static int cardinality(int bitset) { } public static SemType widenToBasicTypeUnion(SemType t) { - if (t.some == 0) { + if (t.some() == 0) { return t; } - int all = t.all | t.some; + int all = t.all() | t.some(); return Builder.basicTypeUnion(all); } @@ -370,7 +370,7 @@ public static SemType intersectMemberSemTypes(Env env, SemType t1, SemType t2) { private static Optional cellAtomicType(SemType t) { SemType cell = Builder.cell(); - if (t.some == 0) { + if (t.some() == 0) { return cell.equals(t) ? Optional.of(Builder.cellAtomicVal()) : Optional.empty(); } else { if (!isSubtypeSimple(t, cell)) { 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 index 4dbb3e4d60d6..9fee8ef2b27b 100644 --- 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 @@ -40,8 +40,8 @@ public abstract sealed class SemType implements BasicTypeBitSet permits BSemType private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); - public final int all; - public final int some; + private final int all; + private final int some; private final SubType[] subTypeData; private Integer hashCode; @@ -100,7 +100,8 @@ public boolean equals(Object o) { if (!(o instanceof SemType semType)) { return false; } - return all == semType.all && some == semType.some && Objects.deepEquals(subTypeData, semType.subTypeData); + return all() == semType.all() && some() == semType.some() && + Objects.deepEquals(subTypeData, semType.subTypeData); } @Override @@ -118,11 +119,11 @@ public int hashCode() { } private int computeHashCode() { - return Objects.hash(all, some, Arrays.hashCode(subTypeData)); + return Objects.hash(all(), some(), Arrays.hashCode(subTypeData)); } private boolean shouldCache() { - return (some & CACHEABLE_TYPE_MASK) != 0; + return (some() & CACHEABLE_TYPE_MASK) != 0; } enum CachedResult { 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 index 961cfab65649..c349e140bb9f 100644 --- 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 @@ -65,7 +65,7 @@ private BListProj() { } public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { - if (t.some == 0) { + if (t.some() == 0) { return t == Builder.listType() ? Builder.valType() : Builder.neverType(); } else { SubTypeData keyData = Core.intSubtype(k); From 5fa7e07743a1c1cd1acb17a4b2e5af813dd18fe8 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 6 Aug 2024 10:49:58 +0530 Subject: [PATCH 079/178] Refactor the semtype class hierachy --- .../api/types/semtype/ImmutableSemType.java | 215 ++++++++++++++++++ .../runtime/api/types/semtype/SemType.java | 197 +--------------- .../internal/types/BSemTypeWrapper.java | 3 +- .../internal/types/semtype/PureSemType.java | 4 +- 4 files changed, 231 insertions(+), 188 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java new file mode 100644 index 000000000000..dce1a5ef5a4a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java @@ -0,0 +1,215 @@ +/* + * 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.BSemTypeWrapper; +import io.ballerina.runtime.internal.types.semtype.PureSemType; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; + +/** + * Runtime representation of SemType. + * + * @since 2201.10.0 + */ +public abstract sealed class ImmutableSemType implements SemType permits BSemTypeWrapper, PureSemType { + + private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); + + private final int all; + private final int some; + private final SubType[] subTypeData; + + private Integer hashCode; + + private final TypeCheckResultCache resultCache; + + protected ImmutableSemType(int all, int some, SubType[] subTypeData) { + this.all = all; + this.some = some; + this.subTypeData = subTypeData; + if ((some & CACHEABLE_TYPE_MASK) != 0) { + this.resultCache = new TypeCheckResultCache(); + } else { + this.resultCache = TypeCheckResultCache.EMPTY; + } + } + + protected ImmutableSemType(int all) { + this(all, 0, EMPTY_SUBTYPE_DATA); + } + + protected ImmutableSemType(SemType semType) { + this(semType.all(), semType.some(), semType.subTypeData()); + } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } + + @Override + public final int all() { + return all; + } + + @Override + public final int some() { + return some; + } + + @Override + public final SubType[] subTypeData() { + return 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(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)); + } + + private boolean shouldCache() { + return (some() & CACHEABLE_TYPE_MASK) != 0; + } + + @Override + public CachedResult cachedSubTypeRelation(SemType other) { + if (!shouldCache()) { + return CachedResult.NOT_FOUND; + } + if (this == other) { + return CachedResult.TRUE; + } + return resultCache.getCachedResult(other); + } + + @Override + public void cacheSubTypeRelation(SemType other, boolean result) { + if (shouldCache()) { + resultCache.cacheResult(other, result); + assert isValidCacheState(other, result) : "Invalid cache state"; + } + } + + private boolean isValidCacheState(SemType other, boolean result) { + CachedResult cachedResult = cachedSubTypeRelation(other); + return cachedResult == CachedResult.NOT_FOUND || + cachedResult == (result ? CachedResult.TRUE : CachedResult.FALSE); + } + + @Override + 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)]; + } + + private static sealed class TypeCheckResultCache { + + private static final TypeCheckResultCache EMPTY = new EmptyTypeCheckResultCache(); + // make this an int + private final Map cache = new WeakHashMap<>(); + + public void cacheResult(SemType semType, boolean result) { + cache.put(TypeCheckCacheKey.from(semType), result); + } + + public CachedResult getCachedResult(SemType semType) { + Boolean cachedData = cache.get(TypeCheckCacheKey.from(semType)); + if (cachedData == null) { + return CachedResult.NOT_FOUND; + } + return cachedData ? CachedResult.TRUE : CachedResult.FALSE; + } + } + + private static final class EmptyTypeCheckResultCache extends TypeCheckResultCache { + + @Override + public void cacheResult(SemType semType, boolean result) { + throw new UnsupportedOperationException("Empty cache"); + } + + @Override + public CachedResult getCachedResult(SemType semType) { + throw new UnsupportedOperationException("Empty cache"); + } + } + + private record TypeCheckCacheKey(Reference semtype) { + + static TypeCheckCacheKey from(SemType semType) { + return new TypeCheckCacheKey(new WeakReference<>(semType)); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TypeCheckCacheKey other)) { + return false; + } + SemType thisSemType = semtype.get(); + SemType otherSemType = other.semtype.get(); + if (thisSemType == null || otherSemType == null) { + return false; + } + return thisSemType == otherSemType; + } + + @Override + public int hashCode() { + return System.identityHashCode(semtype.get()); + } + } +} 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 index 9fee8ef2b27b..ce6cf9d091c4 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * 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 @@ -11,213 +11,40 @@ * 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.runtime.api.types.semtype; -import io.ballerina.runtime.internal.types.BSemTypeWrapper; import io.ballerina.runtime.internal.types.semtype.PureSemType; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.Map; -import java.util.Objects; -import java.util.WeakHashMap; +public interface SemType extends BasicTypeBitSet { -import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; - -/** - * Runtime representation of SemType. - * - * @since 2201.10.0 - */ -public abstract sealed class SemType implements BasicTypeBitSet permits BSemTypeWrapper, PureSemType { - - private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; - private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); - - private final int all; - private final int some; - private final SubType[] subTypeData; - - private Integer hashCode; - - private final TypeCheckResultCache resultCache; - - protected SemType(int all, int some, SubType[] subTypeData) { - this.all = all; - this.some = some; - this.subTypeData = subTypeData; - if ((some & CACHEABLE_TYPE_MASK) != 0) { - this.resultCache = new TypeCheckResultCache(); - } else { - this.resultCache = TypeCheckResultCache.EMPTY; - } - } - - protected SemType(int all) { - this(all, 0, EMPTY_SUBTYPE_DATA); - } - - protected SemType(SemType semType) { - this(semType.all(), semType.some(), semType.subTypeData()); - } - - public static SemType from(int all, int some, SubType[] subTypeData) { + static SemType from(int all, int some, SubType[] subTypeData) { return new PureSemType(all, some, subTypeData); } - public static SemType from(int all) { + static SemType from(int all) { return new PureSemType(all); } - @Override - public String toString() { - return SemTypeHelper.stringRepr(this); - } + int all(); - public final int all() { - return all; - } + int some(); - public final int some() { - return some; - } + SubType[] subTypeData(); - public final SubType[] subTypeData() { - return subTypeData; - } + CachedResult cachedSubTypeRelation(SemType other); - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SemType semType)) { - return false; - } - return all() == semType.all() && some() == semType.some() && - Objects.deepEquals(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)); - } + void cacheSubTypeRelation(SemType other, boolean result); - private boolean shouldCache() { - return (some() & CACHEABLE_TYPE_MASK) != 0; - } + SubType subTypeByCode(int code); - enum CachedResult { + public enum CachedResult { TRUE, FALSE, NOT_FOUND } - - CachedResult cachedSubTypeRelation(SemType other) { - if (!shouldCache()) { - return CachedResult.NOT_FOUND; - } - if (this == other) { - return CachedResult.TRUE; - } - return resultCache.getCachedResult(other); - } - - void cacheSubTypeRelation(SemType other, boolean result) { - if (shouldCache()) { - resultCache.cacheResult(other, result); - assert isValidCacheState(other, result) : "Invalid cache state"; - } - } - - private boolean isValidCacheState(SemType other, boolean result) { - CachedResult cachedResult = cachedSubTypeRelation(other); - return cachedResult == CachedResult.NOT_FOUND || - cachedResult == (result ? CachedResult.TRUE : CachedResult.FALSE); - } - - 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)]; - } - - private static sealed class TypeCheckResultCache { - - private static final TypeCheckResultCache EMPTY = new EmptyTypeCheckResultCache(); - // make this an int - private final Map cache = new WeakHashMap<>(); - - public void cacheResult(SemType semType, boolean result) { - cache.put(TypeCheckCacheKey.from(semType), result); - } - - public CachedResult getCachedResult(SemType semType) { - Boolean cachedData = cache.get(TypeCheckCacheKey.from(semType)); - if (cachedData == null) { - return CachedResult.NOT_FOUND; - } - return cachedData ? CachedResult.TRUE : CachedResult.FALSE; - } - } - - private static final class EmptyTypeCheckResultCache extends TypeCheckResultCache { - - @Override - public void cacheResult(SemType semType, boolean result) { - throw new UnsupportedOperationException("Empty cache"); - } - - @Override - public CachedResult getCachedResult(SemType semType) { - throw new UnsupportedOperationException("Empty cache"); - } - } - - private record TypeCheckCacheKey(Reference semtype) { - - static TypeCheckCacheKey from(SemType semType) { - return new TypeCheckCacheKey(new WeakReference<>(semType)); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TypeCheckCacheKey other)) { - return false; - } - SemType thisSemType = semtype.get(); - SemType otherSemType = other.semtype.get(); - if (thisSemType == null || otherSemType == null) { - return false; - } - return thisSemType == otherSemType; - } - - @Override - public int hashCode() { - return System.identityHashCode(semtype.get()); - } - } } 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 index 26c9ee079424..d1434e70aaa5 100644 --- 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 @@ -21,6 +21,7 @@ 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.ImmutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; // TODO: make this a sealed class with clearly defined extensions @@ -31,7 +32,7 @@ * * @since 2201.10.0 */ -public non-sealed class BSemTypeWrapper extends SemType implements Type { +public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { private final BType bType; protected final String typeName; // Debugger uses this field to show the type name diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index ec878f4bb183..75fe89a50ddf 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -18,7 +18,7 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ImmutableSemType; import io.ballerina.runtime.api.types.semtype.SubType; /** @@ -26,7 +26,7 @@ * * @since 2201.10.0 */ -public final class PureSemType extends SemType { +public final class PureSemType extends ImmutableSemType { public PureSemType(int all, int some, SubType[] subTypeData) { super(all, some, subTypeData); From b678c1e907af972d221beef15c48d556626468ed Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 6 Aug 2024 10:56:56 +0530 Subject: [PATCH 080/178] Move immutable semtype class to internals --- .../runtime/api/types/semtype/BasicTypeCode.java | 2 +- .../runtime/internal/types/BSemTypeWrapper.java | 2 +- .../types/semtype/ImmutableSemType.java | 14 ++++++++------ .../internal/types/semtype/PureSemType.java | 1 - .../types/semtype/SemTypeHelper.java | 8 +++++--- 5 files changed, 15 insertions(+), 12 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/ImmutableSemType.java (93%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/SemTypeHelper.java (94%) 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 index 8026f3ff15c1..487421b5dc6e 100644 --- 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 @@ -77,7 +77,7 @@ public final class BasicTypeCode { // Helper bit fields (does not represent basic type tag) static final int VT_COUNT = CODE_OBJECT + 1; - static final int BASIC_TYPE_MASK = (1 << (CODE_STRING + 1)) - 1; + public static final int BASIC_TYPE_MASK = (1 << (CODE_STRING + 1)) - 1; static final int VT_MASK = (1 << VT_COUNT) - 1; static final int VT_COUNT_INHERENTLY_IMMUTABLE = 0x0A; 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 index d1434e70aaa5..ea8a88242ac9 100644 --- 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 @@ -21,7 +21,7 @@ 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.ImmutableSemType; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; // TODO: make this a sealed class with clearly defined extensions diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java similarity index 93% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java index dce1a5ef5a4a..760eca1426f7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ImmutableSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * 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 @@ -11,15 +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.runtime.api.types.semtype; +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 io.ballerina.runtime.internal.types.BSemTypeWrapper; -import io.ballerina.runtime.internal.types.semtype.PureSemType; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -48,7 +50,7 @@ public abstract sealed class ImmutableSemType implements SemType permits BSemTyp private final TypeCheckResultCache resultCache; - protected ImmutableSemType(int all, int some, SubType[] subTypeData) { + ImmutableSemType(int all, int some, SubType[] subTypeData) { this.all = all; this.some = some; this.subTypeData = subTypeData; @@ -59,7 +61,7 @@ protected ImmutableSemType(int all, int some, SubType[] subTypeData) { } } - protected ImmutableSemType(int all) { + ImmutableSemType(int all) { this(all, 0, EMPTY_SUBTYPE_DATA); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index 75fe89a50ddf..92b794b0c19e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -18,7 +18,6 @@ package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.api.types.semtype.ImmutableSemType; import io.ballerina.runtime.api.types.semtype.SubType; /** diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java similarity index 94% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java index 2f8376db1a4f..ba0fee4be013 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemTypeHelper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * 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 @@ -11,12 +11,14 @@ * 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.runtime.api.types.semtype; +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_B_TYPE; From b777083a30088133a8c415b26a79a803ca955974 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 7 Aug 2024 07:54:55 +0530 Subject: [PATCH 081/178] Make BTypes proper semtypes --- .../runtime/api/types/semtype/Builder.java | 15 +- .../runtime/api/types/semtype/Context.java | 29 --- .../runtime/api/types/semtype/Core.java | 19 ++ .../types/semtype/MutableSemType.java} | 12 +- .../MutableSemTypeDependencyManager.java | 60 +++++ .../runtime/internal/TypeChecker.java | 2 +- .../runtime/internal/types/BAnyType.java | 7 +- .../runtime/internal/types/BArrayType.java | 29 +-- .../runtime/internal/types/BErrorType.java | 13 +- .../runtime/internal/types/BFiniteType.java | 23 +- .../runtime/internal/types/BFunctionType.java | 24 +- .../internal/types/BIntersectionType.java | 15 +- .../runtime/internal/types/BMapType.java | 25 ++- .../internal/types/BNetworkObjectType.java | 14 +- .../runtime/internal/types/BNullType.java | 3 +- .../runtime/internal/types/BObjectType.java | 62 +++--- .../runtime/internal/types/BReadonlyType.java | 7 +- .../runtime/internal/types/BRecordType.java | 29 +-- .../internal/types/BStructureType.java | 2 +- .../runtime/internal/types/BTupleType.java | 27 +-- .../runtime/internal/types/BType.java | 66 ++++-- .../internal/types/BTypeConverter.java | 207 ------------------ .../internal/types/BTypeReferenceType.java | 8 +- .../runtime/internal/types/BUnionType.java | 24 +- .../runtime/internal/types/BXmlType.java | 10 +- .../test/resources/test-src/valuelib_test.bal | 4 +- 26 files changed, 326 insertions(+), 410 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{internal/types/PartialSemTypeSupplier.java => api/types/semtype/MutableSemType.java} (66%) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java 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 index 79392b8de086..31da7968e5b3 100644 --- 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 @@ -34,6 +34,7 @@ import io.ballerina.runtime.internal.types.semtype.BMappingSubType; import io.ballerina.runtime.internal.types.semtype.BObjectSubType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import io.ballerina.runtime.internal.types.semtype.BSubType; import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; @@ -132,22 +133,14 @@ public static SemType from(BasicTypeCode typeCode) { return SemType.from(1 << typeCode.code()); } + // FIXME: remove this method public static SemType from(Context cx, Type type) { if (type instanceof SemType semType) { return semType; - } else if (type instanceof BType bType) { - return fromBType(cx, bType); } throw new IllegalArgumentException("Unsupported type: " + type); } - private static SemType fromBType(Context cx, BType innerType) { - int staringSize = cx.addProvisionalType(innerType); - SemType result = innerType.get(cx); - cx.emptyProvisionalTypes(staringSize); - return result; - } - public static SemType neverType() { return SemType.from(0); } @@ -461,6 +454,10 @@ public static MappingAtomicType mappingAtomicInner() { return MAPPING_ATOMIC_INNER.get(); } + public static SemType wrapAsPureBType(BType bType) { + return basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(bType)); + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; 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 index 0842e8809e65..944ec1bd0cf8 100644 --- 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 @@ -18,8 +18,6 @@ package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.internal.types.BType; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -41,9 +39,6 @@ public final class Context { public final Map mappingMemo = new HashMap<>(); public final Map functionMemo = new HashMap<>(); - private final List provisionalTypes = new ArrayList<>(); - private boolean resetProvisionalTypes = false; - SemType anydataMemo; private Context(Env env) { this.env = env; @@ -130,28 +125,4 @@ public FunctionAtomicType functionAtomicType(Atom atom) { return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); } } - - public int addProvisionalType(BType type) { - int currentSize = provisionalTypes.size(); - provisionalTypes.add(type); - return currentSize; - } - - public void markProvisionTypeReset() { - resetProvisionalTypes = true; - } - - public void emptyProvisionalTypes(int startingSize) { - if (startingSize != 0) { - return; - } - if (resetProvisionalTypes) { - for (int i = 1; i < provisionalTypes.size(); i++) { - BType type = provisionalTypes.get(i); - type.resetSemTypeCache(); - } - } - provisionalTypes.clear(); - resetProvisionalTypes = false; - } } 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 index f4790c716ef7..21598c4bcdd6 100644 --- 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 @@ -20,6 +20,7 @@ import io.ballerina.runtime.internal.types.semtype.AllOrNothing; import io.ballerina.runtime.internal.types.semtype.BObjectSubType; +import io.ballerina.runtime.internal.types.semtype.BSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; import io.ballerina.runtime.internal.types.semtype.SubTypeData; import io.ballerina.runtime.internal.types.semtype.SubtypePair; @@ -54,6 +55,13 @@ public final class Core { public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); public static final SemType B_TYPE_TOP = SemType.from(1 << BT_B_TYPE.code()); + private static final SemType implementedTypes = + unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), + Builder.floatType(), Builder.decimalType(), Builder.stringType(), listType(), + Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), + Builder.xmlType(), Builder.handleType()); + public static final SemType ANY_SEMTYPE_PART = intersect(implementedTypes, Builder.anyType()); + public static final SemType READONLY_SEMTYPE_PART = intersect(implementedTypes, Builder.readonlyType()); private Core() { } @@ -265,6 +273,9 @@ public static boolean isEmpty(Context cx, SemType t) { } for (SubType subType : t.subTypeData()) { assert subType != null : "subtype array must not be sparse"; + if (subType instanceof BSubType) { + continue; + } if (!subType.isEmpty(cx)) { return false; } @@ -411,4 +422,12 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { }; return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); } + + private static SemType unionOf(SemType... semTypes) { + SemType result = Builder.neverType(); + for (SemType semType : semTypes) { + result = union(result, semType); + } + return result; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java similarity index 66% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java index d517776cbcfd..b28883f506df 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/PartialSemTypeSupplier.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * 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 @@ -11,14 +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.runtime.internal.types; +package io.ballerina.runtime.api.types.semtype; + +public interface MutableSemType extends SemType { -public interface PartialSemTypeSupplier extends BSemTypeSupplier { + SemType createSemType(); + void resetSemType(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java new file mode 100644 index 000000000000..7df57345dc95 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.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.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.TypeChecker; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +// TODO: consider moving this to internal package +public final class MutableSemTypeDependencyManager { + + private static final MutableSemTypeDependencyManager INSTANCE = new MutableSemTypeDependencyManager(); + private final Map> dependencies = new IdentityHashMap<>(); + + public static MutableSemTypeDependencyManager getInstance() { + return INSTANCE; + } + + private MutableSemTypeDependencyManager() { + } + + public synchronized void notifyDependenciesToReset(MutableSemType semType) { + List mutableSemTypes = dependencies.get(semType); + if (mutableSemTypes != null) { + dependencies.remove(semType); + for (MutableSemType mutableSemType : mutableSemTypes) { + mutableSemType.resetSemType(); + } + } + } + + public synchronized SemType getSemType(Type target, MutableSemType self) { + if (target instanceof MutableSemType mutableTarget) { + List dependencies = + this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); + dependencies.add(self); + } + return Builder.from(TypeChecker.context(), target); + } +} 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 991be67f2b77..d95fa4e863f0 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 @@ -149,7 +149,7 @@ public static Object checkCast(Object sourceVal, Type targetType) { throw createTypeCastError(sourceVal, targetType, errors); } - static Context context() { + public static Context context() { // We are pinning each context to thread. This depends on the assumption physical thread is not going to // get switched while type checking. Also for the same reason we don't need to synchronize this method. return threadContext.get(); 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 7a6328955cf2..c63fbf7c8cbc 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,7 +25,8 @@ import io.ballerina.runtime.api.types.AnyType; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.Context; +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; @@ -104,7 +105,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType(Context cx) { - return BTypeConverter.fromAnyType(this); + public SemType createSemType() { + return Core.union(Core.ANY_SEMTYPE_PART, Builder.wrapAsPureBType(this)); } } 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 5ebb330ebb7a..b8d9a16747ae 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 @@ -51,7 +51,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BArrayType extends BType implements ArrayType, PartialSemTypeSupplier, TypeWithShape { +public class BArrayType extends BType implements ArrayType, TypeWithShape { private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private Type elementType; @@ -103,7 +103,7 @@ public void setElementType(Type elementType, int dimensions, boolean elementRO) this.elementType = readonly && !elementRO ? ReadOnlyUtils.getReadOnlyType(elementType) : elementType; this.dimensions = dimensions; defn = null; - resetSemTypeCache(); + resetSemType(); } private void setFlagsBasedOnElementType() { @@ -223,26 +223,27 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } - defn = new ListDefinition(); - SemType elementType = Builder.from(cx, getElementType()); + ListDefinition ld = new ListDefinition(); + defn = ld; + SemType elementType = mutableSemTypeDependencyManager.getSemType(getElementType(), this); SemType pureBTypePart = Core.intersect(elementType, Core.B_TYPE_TOP); if (!Core.isNever(pureBTypePart)) { - cx.markProvisionTypeReset(); SemType pureSemTypePart = Core.intersect(elementType, Core.SEMTYPE_TOP); - SemType semTypePart = getSemTypePart(pureSemTypePart); - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType semTypePart = getSemTypePart(ld, isReadOnly(), size, pureSemTypePart); + SemType bTypePart = Builder.wrapAsPureBType(this); + resetSemType(); return Core.union(semTypePart, bTypePart); } - return getSemTypePart(elementType); + return getSemTypePart(ld, isReadOnly(), size, elementType); } - private SemType getSemTypePart(SemType elementType) { - CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + private SemType getSemTypePart(ListDefinition defn, boolean isReadOnly, int size, SemType elementType) { + CellAtomicType.CellMutability mut = isReadOnly ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; if (size == -1) { return defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, elementType, mut); @@ -253,15 +254,15 @@ private SemType getSemTypePart(SemType elementType) { } @Override - public void resetSemTypeCache() { - super.resetSemTypeCache(); + public synchronized void resetSemType() { defn = null; + super.resetSemType(); } @Override public Optional shapeOf(Context cx, Object object) { if (!isReadOnly()) { - return Optional.of(get(cx)); + return Optional.of(getSemType()); } BArray value = (BArray) object; SemType cachedShape = value.shapeOf(); 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 dda7e62f3cf5..c401f0871dee 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 @@ -30,6 +30,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.values.ErrorValue; @@ -41,7 +42,7 @@ * * @since 0.995.0 */ -public class BErrorType extends BAnnotatableType implements ErrorType, PartialSemTypeSupplier, TypeWithShape { +public class BErrorType extends BAnnotatableType implements ErrorType, TypeWithShape { public Type detailType = PredefinedTypes.TYPE_DETAIL; public BTypeIdSet typeIdSet; @@ -127,14 +128,14 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { boolean hasBType = false; SemType err; if (detailType == null || isTopType()) { err = Builder.errorType(); hasBType = true; } else { - SemType detailType = Builder.from(cx, getDetailType()); + SemType detailType = mutableSemTypeDependencyManager.getSemType(getDetailType(), this); if (!Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP))) { hasBType = true; detailType = Core.intersect(detailType, Core.SEMTYPE_TOP); @@ -143,12 +144,12 @@ synchronized SemType createSemType(Context cx) { } if (distinctIdSupplier == null) { - distinctIdSupplier = new DistinctIdSupplier(cx.env, getTypeIdSet()); + distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); } SemType pureSemType = distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); if (hasBType) { - return Core.union(pureSemType, BTypeConverter.wrapAsPureBType(this)); + return Core.union(pureSemType, Builder.wrapAsPureBType(this)); } return pureSemType; } @@ -172,7 +173,7 @@ public Optional shapeOf(Context cx, Object object) { .reduce(err, Core::intersect)) .map(semType -> { if (hasBType) { - return Core.union(semType, BTypeConverter.wrapAsPureBType(this)); + return Core.union(semType, Builder.wrapAsPureBType(this)); } else { return semType; } 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 268f72eddb7f..e598cb168ddf 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,13 +21,16 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; -import io.ballerina.runtime.api.types.semtype.Context; +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.TypeChecker; import io.ballerina.runtime.internal.values.RefValue; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; @@ -205,7 +208,21 @@ public boolean equals(Object o) { } @Override - SemType createSemType(Context cx) { - return BTypeConverter.fromFiniteType(cx, this); + public SemType createSemType() { + Set bTypeValueSpace = new HashSet<>(); + SemType result = Builder.neverType(); + for (Object each : this.valueSpace) { + Optional semType = Builder.shapeOf(TypeChecker.context(), each); + if (semType.isPresent()) { + result = Core.union(result, semType.get()); + } else { + bTypeValueSpace.add(each); + } + } + if (bTypeValueSpace.isEmpty()) { + return result; + } + BFiniteType newFiniteType = this.cloneWithValueSpace(bTypeValueSpace); + return Core.union(result, Builder.wrapAsPureBType(newFiniteType)); } } 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 0c65113a5728..20a033a1cb5c 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 @@ -27,7 +27,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; -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; @@ -42,7 +41,7 @@ * * @since 0.995.0 */ -public class BFunctionType extends BAnnotatableType implements FunctionType, PartialSemTypeSupplier { +public class BFunctionType extends BAnnotatableType implements FunctionType { public Type restType; public Type retType; @@ -241,10 +240,10 @@ private static SemType createIsolatedTop(Env env) { } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { if (isFunctionTop()) { SemType topType = getTopType(); - return Core.union(topType, BTypeConverter.wrapAsPureBType(this)); + return Core.union(topType, Builder.wrapAsPureBType(this)); } if (defn != null) { return defn.getSemType(env); @@ -254,13 +253,13 @@ synchronized SemType createSemType(Context cx) { SemType[] params = new SemType[parameters.length]; boolean hasBType = false; for (int i = 0; i < parameters.length; i++) { - var result = getSemType(cx, parameters[i].type); + var result = getSemType(parameters[i].type); hasBType = hasBType || result.hasBTypePart; params[i] = result.pureSemTypePart; } SemType rest; if (restType instanceof BArrayType arrayType) { - var result = getSemType(cx, arrayType.getElementType()); + var result = getSemType(arrayType.getElementType()); hasBType = hasBType || result.hasBTypePart; rest = result.pureSemTypePart; } else { @@ -269,7 +268,7 @@ synchronized SemType createSemType(Context cx) { SemType returnType; if (retType != null) { - var result = getSemType(cx, retType); + var result = getSemType(retType); hasBType = hasBType || result.hasBTypePart; returnType = result.pureSemTypePart; } else { @@ -280,8 +279,7 @@ synchronized SemType createSemType(Context cx) { CellAtomicType.CellMutability.CELL_MUT_NONE); SemType result = fd.define(env, paramType, returnType, getQualifiers()); if (hasBType) { - cx.markProvisionTypeReset(); - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType bTypePart = Builder.wrapAsPureBType(this); return Core.union(result, bTypePart); } return result; @@ -305,8 +303,8 @@ public FunctionQualifiers getQualifiers() { } // TODO: consider moving this to builder - private static SemTypeResult getSemType(Context cx, Type type) { - SemType semType = Builder.from(cx, type); + private SemTypeResult getSemType(Type type) { + SemType semType = mutableSemTypeDependencyManager.getSemType(type, this); if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { return new SemTypeResult(true, Core.intersect(semType, Core.SEMTYPE_TOP)); } @@ -318,8 +316,8 @@ private boolean isFunctionTop() { } @Override - public void resetSemTypeCache() { - super.resetSemTypeCache(); + public synchronized void resetSemType() { defn = null; + super.resetSemType(); } } 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 449f2e84ba2c..8a244c459236 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 @@ -221,24 +221,21 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType(Context cx) { + public SemType createSemType() { Type effectiveType = getEffectiveType(); - if (effectiveType instanceof SemType semType) { - return semType; - } if (constituentTypes.isEmpty()) { return Builder.neverType(); } - SemType result = Builder.from(cx, constituentTypes.get(0)); - boolean hasBType = Core.containsBasicType(Builder.from(cx, effectiveType), Builder.bType()); + SemType result = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(0), this); + boolean hasBType = Core.containsBasicType(mutableSemTypeDependencyManager.getSemType(effectiveType, this), + Builder.bType()); result = Core.intersect(result, Core.SEMTYPE_TOP); for (int i = 1; i < constituentTypes.size(); i++) { - SemType memberType = Builder.from(cx, constituentTypes.get(i)); -// hasBType |= Core.containsBasicType(memberType, Builder.bType()); + SemType memberType = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(i), this); result = Core.intersect(result, memberType); } if (hasBType) { - return Core.union(result, BTypeConverter.wrapAsPureBType((BType) effectiveType)); + return Core.union(result, Builder.wrapAsPureBType((BType) effectiveType)); } return result; } 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 30de625d412b..f9ab58edd964 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 @@ -52,7 +52,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BMapType extends BType implements MapType, PartialSemTypeSupplier, TypeWithShape { +public class BMapType extends BType implements MapType, TypeWithShape { public static final MappingDefinition.Field[] EMPTY_FIELD_ARR = new MappingDefinition.Field[0]; private final Type constraint; @@ -184,33 +184,34 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } - defn = new MappingDefinition(); - SemType restType = Builder.from(cx, getConstrainedType()); + MappingDefinition md = new MappingDefinition(); + defn = md; + SemType restType = mutableSemTypeDependencyManager.getSemType(getConstrainedType(), this); SemType pureBTypePart = Core.intersect(restType, Core.B_TYPE_TOP); if (!Core.isNever(pureBTypePart)) { - cx.markProvisionTypeReset(); SemType pureSemTypePart = Core.intersect(restType, Core.SEMTYPE_TOP); - SemType semTypePart = getSemTypePart(pureSemTypePart); - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType semTypePart = getSemTypePart(md, pureSemTypePart); + SemType bTypePart = Builder.wrapAsPureBType(this); + resetSemType(); return Core.union(semTypePart, bTypePart); } - return getSemTypePart(restType); + return getSemTypePart(md, restType); } @Override - public void resetSemTypeCache() { - super.resetSemTypeCache(); + public synchronized void resetSemType() { defn = null; + super.resetSemType(); } @Override public Optional shapeOf(Context cx, Object object) { if (!isReadOnly()) { - return Optional.of(get(cx)); + return Optional.of(getSemType()); } BMap value = (BMap) object; SemType cachedShape = value.shapeOf(); @@ -239,7 +240,7 @@ static Optional readonlyShape(Context cx, BMap value) { return Optional.of(semType); } - private SemType getSemTypePart(SemType restType) { + private SemType getSemTypePart(MappingDefinition defn, SemType restType) { CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; return defn.defineMappingTypeWrapped(env, EMPTY_FIELD_ARR, restType, mut); 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 774964095558..784d5118ac3e 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 @@ -22,7 +22,6 @@ import io.ballerina.runtime.api.types.NetworkObjectType; import io.ballerina.runtime.api.types.RemoteMethodType; import io.ballerina.runtime.api.types.ResourceMethodType; -import io.ballerina.runtime.api.types.semtype.Context; import java.util.ArrayList; import java.util.Arrays; @@ -86,13 +85,16 @@ public ResourceMethodType[] getResourceMethods() { } @Override - protected Collection allMethods(Context cx) { - Stream methodStream = Arrays.stream(getMethods()).map(method -> MethodData.fromMethod(cx, method)); + protected Collection allMethods() { + Stream methodStream = Arrays.stream(getMethods()) + .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)); Stream remoteMethodStream = - Arrays.stream(getRemoteMethods()).map(method -> MethodData.fromMethod(cx, method)); + Arrays.stream(getRemoteMethods()) + .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)); Stream resoucrMethodStream = - Arrays.stream(getResourceMethods()).map(method -> MethodData.fromResourceMethod(cx, - (BResourceMethodType) method)); + Arrays.stream(getResourceMethods()) + .map(method -> MethodData.fromResourceMethod(mutableSemTypeDependencyManager, this, + (BResourceMethodType) method)); return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resoucrMethodStream)).toList(); } } 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 65e702da9b4b..c272a5d24ee1 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 @@ -21,7 +21,6 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.NullType; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; /** @@ -81,7 +80,7 @@ public boolean isReadOnly() { } @Override - SemType createSemType(Context cx) { + public SemType createSemType() { return Builder.nilType(); } } 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 72ea9c27bba7..bd9e22375d9a 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 @@ -36,10 +36,13 @@ 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.MutableSemType; +import io.ballerina.runtime.api.types.semtype.MutableSemTypeDependencyManager; import io.ballerina.runtime.api.types.semtype.SemType; 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.ValueUtils; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; @@ -69,7 +72,7 @@ * * @since 0.995.0 */ -public class BObjectType extends BStructureType implements ObjectType, PartialSemTypeSupplier, TypeWithShape { +public class BObjectType extends BStructureType implements ObjectType, TypeWithShape { private MethodType[] methodTypes; private MethodType initMethod; @@ -82,7 +85,7 @@ public class BObjectType extends BStructureType implements ObjectType, PartialSe private String cachedToString; private boolean resolving; - private ObjectDefinition od; + private ObjectDefinition defn; private final Env env = Env.getInstance(); // TODO: better name private SemType softSemTypeCache; @@ -273,12 +276,12 @@ public TypeIdSet getTypeIdSet() { } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { if (distinctIdSupplier == null) { distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); } return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) - .reduce(semTypeInner(cx), Core::intersect); + .reduce(semTypeInner(), Core::intersect); } private static boolean skipField(Set seen, String name) { @@ -288,14 +291,15 @@ private static boolean skipField(Set seen, String name) { return !seen.add(name); } - private SemType semTypeInner(Context cx) { + private SemType semTypeInner() { if (softSemTypeCache != null) { return softSemTypeCache; } - if (od != null) { - return od.getSemType(env); + if (defn != null) { + return defn.getSemType(env); } - od = new ObjectDefinition(); + ObjectDefinition od = new ObjectDefinition(); + defn = od; ObjectQualifiers qualifiers = getObjectQualifiers(); List members = new ArrayList<>(); boolean hasBTypes = false; @@ -308,7 +312,7 @@ private SemType semTypeInner(Context cx) { Field field = entry.getValue(); boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - SemType ty = Builder.from(cx, field.getFieldType()); + SemType ty = mutableSemTypeDependencyManager.getSemType(field.getFieldType(), this); SemType pureBTypePart = Core.intersect(ty, Core.B_TYPE_TOP); if (!Core.isNever(pureBTypePart)) { hasBTypes = true; @@ -317,7 +321,7 @@ private SemType semTypeInner(Context cx) { members.add(new Member(name, ty, Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } - for (MethodData method : allMethods(cx)) { + for (MethodData method : allMethods()) { String name = method.name(); if (skipField(seen, name)) { continue; @@ -334,8 +338,7 @@ private SemType semTypeInner(Context cx) { } SemType semTypePart = od.define(env, qualifiers, members); if (hasBTypes || members.isEmpty()) { - cx.markProvisionTypeReset(); - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType bTypePart = Builder.wrapAsPureBType(this); softSemTypeCache = Core.union(semTypePart, bTypePart); return softSemTypeCache; } @@ -396,7 +399,7 @@ private SemType valueShape(Context cx, AbstractObjectValue object) { members.add(new Member(name, ty, Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } - for (MethodData method : allMethods(cx)) { + for (MethodData method : allMethods()) { String name = method.name(); if (skipField(seen, name)) { continue; @@ -413,7 +416,7 @@ private SemType valueShape(Context cx, AbstractObjectValue object) { } SemType semTypePart = od.define(env, qualifiers, members); if (hasBTypes) { - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType bTypePart = Builder.wrapAsPureBType(this); return Core.union(semTypePart, bTypePart); } return semTypePart; @@ -430,23 +433,26 @@ private static SemType fieldShape(Context cx, Field field, AbstractObjectValue o } @Override - public void resetSemTypeCache() { - super.resetSemTypeCache(); - od = null; + public synchronized void resetSemType() { + defn = null; + super.resetSemType(); } - protected Collection allMethods(Context cx) { - return Arrays.stream(methodTypes).map(method -> MethodData.fromMethod(cx, method)).toList(); + protected Collection allMethods() { + return Arrays.stream(methodTypes) + .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)).toList(); } protected record MethodData(String name, long flags, SemType semType) { - static MethodData fromMethod(Context cx, MethodType method) { + static MethodData fromMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, + MethodType method) { return new MethodData(method.getName(), method.getFlags(), - Builder.from(cx, method.getType())); + dependencyManager.getSemType(method.getType(), parent)); } - static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { + static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, + BResourceMethodType method) { StringBuilder sb = new StringBuilder(); sb.append(method.getAccessor()); for (var each : method.getResourcePath()) { @@ -462,7 +468,7 @@ static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { if (part == null) { paramTypes.add(Builder.anyType()); } else { - SemType semType = Builder.from(cx, part); + SemType semType = dependencyManager.getSemType(part, parent); if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { hasBTypes = true; paramTypes.add(Core.intersect(semType, Core.SEMTYPE_TOP)); @@ -472,7 +478,7 @@ static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { } } for (Parameter paramType : innerFn.getParameters()) { - SemType semType = Builder.from(cx, paramType.type); + SemType semType = dependencyManager.getSemType(paramType.type, parent); if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { hasBTypes = true; paramTypes.add(Core.intersect(semType, Core.SEMTYPE_TOP)); @@ -483,7 +489,7 @@ static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { SemType rest; Type restType = innerFn.getRestType(); if (restType instanceof BArrayType arrayType) { - rest = Builder.from(cx, arrayType.getElementType()); + rest = dependencyManager.getSemType(arrayType.getElementType(), parent); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypes = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); @@ -494,7 +500,7 @@ static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { SemType returnType; if (innerFn.getReturnType() != null) { - returnType = Builder.from(cx, innerFn.getReturnType()); + returnType = dependencyManager.getSemType(innerFn.getReturnType(), parent); if (!Core.isNever(Core.intersect(returnType, Core.B_TYPE_TOP))) { hasBTypes = true; returnType = Core.intersect(returnType, Core.SEMTYPE_TOP); @@ -503,13 +509,13 @@ static MethodData fromResourceMethod(Context cx, BResourceMethodType method) { returnType = Builder.nilType(); } ListDefinition paramListDefinition = new ListDefinition(); - Env env = cx.env; + 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, innerFn.getQualifiers()); if (hasBTypes) { - semType = Core.union(semType, BTypeConverter.wrapAsPureBType((BType) innerFn)); + semType = Core.union(semType, Builder.wrapAsPureBType((BType) innerFn)); } return new MethodData(methodName, method.getFlags(), semType); } 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 7280c10fcf7b..76db2e38631e 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,7 +20,8 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ReadonlyType; -import io.ballerina.runtime.api.types.semtype.Context; +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; @@ -61,7 +62,7 @@ public boolean isReadOnly() { } @Override - SemType createSemType(Context cx) { - return BTypeConverter.fromReadonly(this); + public SemType createSemType() { + return Core.union(Core.READONLY_SEMTYPE_PART, Builder.wrapAsPureBType(this)); } } 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 8badc79fbb6b..a1f7bbecb3aa 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 @@ -29,7 +29,7 @@ import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; @@ -63,7 +63,7 @@ * * @since 0.995.0 */ -public class BRecordType extends BStructureType implements RecordType, PartialSemTypeSupplier, TypeWithShape { +public class BRecordType extends BStructureType implements RecordType, TypeWithShape { private final String internalName; public boolean sealed; public Type restFieldType; @@ -238,18 +238,19 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } - defn = new MappingDefinition(); + MappingDefinition md = new MappingDefinition(); + defn = md; Field[] fields = getFields().values().toArray(Field[]::new); MappingDefinition.Field[] mappingFields = new MappingDefinition.Field[fields.length]; boolean hasBTypePart = false; for (int i = 0; i < fields.length; i++) { Field field = fields[i]; boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); - SemType fieldType = Builder.from(cx, field.getFieldType()); + SemType fieldType = mutableSemTypeDependencyManager.getSemType(field.getFieldType(), this); if (!isOptional && Core.isNever(fieldType)) { return neverType(); } else if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { @@ -263,26 +264,26 @@ synchronized SemType createSemType(Context cx) { mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, isReadonly, isOptional); } - CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : - CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : neverType(); + CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; + SemType rest = + restFieldType != null ? mutableSemTypeDependencyManager.getSemType(restFieldType, this) : neverType(); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypePart = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); } if (hasBTypePart) { - cx.markProvisionTypeReset(); - SemType semTypePart = defn.defineMappingTypeWrapped(env, mappingFields, rest, mut); - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType semTypePart = md.defineMappingTypeWrapped(env, mappingFields, rest, mut); + SemType bTypePart = Builder.wrapAsPureBType(this); + resetSemType(); return Core.union(semTypePart, bTypePart); } - return defn.defineMappingTypeWrapped(env, mappingFields, rest, mut); + return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); } @Override - public void resetSemTypeCache() { - super.resetSemTypeCache(); + public synchronized void resetSemType() { defn = null; + super.resetSemType(); } @Override 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 3b5575b8a353..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,7 +72,7 @@ public Map getFields() { @Override public void setFields(Map fields) { this.fields = fields; - resetSemTypeCache(); + resetSemType(); } @Override 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 c249c02dd4a8..9b9ccbdfd954 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 @@ -49,7 +49,7 @@ * * @since 0.995.0 */ -public class BTupleType extends BAnnotatableType implements TupleType, PartialSemTypeSupplier, TypeWithShape { +public class BTupleType extends BAnnotatableType implements TupleType, TypeWithShape { private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private List tupleTypes; @@ -168,6 +168,7 @@ public void setCyclic(boolean isCyclic) { } public void setMemberTypes(List members, Type restType) { + resetSemType(); if (members == null) { return; } @@ -182,7 +183,6 @@ public void setMemberTypes(List members, Type restType) { } checkAllMembers(); defn = null; - resetSemTypeCache(); } @Override @@ -317,15 +317,16 @@ public String getAnnotationKey() { } @Override - synchronized SemType createSemType(Context cx) { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } - defn = new ListDefinition(); + ListDefinition ld = new ListDefinition(); + defn = ld; SemType[] memberTypes = new SemType[tupleTypes.size()]; boolean hasBTypePart = false; for (int i = 0; i < tupleTypes.size(); i++) { - SemType memberType = Builder.from(cx, tupleTypes.get(i)); + SemType memberType = mutableSemTypeDependencyManager.getSemType(tupleTypes.get(i), this); if (Core.isNever(memberType)) { return neverType(); } else if (!Core.isNever(Core.intersect(memberType, Core.B_TYPE_TOP))) { @@ -336,30 +337,30 @@ synchronized SemType createSemType(Context cx) { } CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restType != null ? Builder.from(cx, restType) : neverType(); + SemType rest = restType != null ? mutableSemTypeDependencyManager.getSemType(restType, this) : neverType(); if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { hasBTypePart = true; rest = Core.intersect(rest, Core.SEMTYPE_TOP); } if (hasBTypePart) { - cx.markProvisionTypeReset(); - SemType semTypePart = defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); - SemType bTypePart = BTypeConverter.wrapAsPureBType(this); + SemType semTypePart = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); + SemType bTypePart = Builder.wrapAsPureBType(this); + resetSemType(); return Core.union(semTypePart, bTypePart); } - return defn.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); + return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } @Override - public void resetSemTypeCache() { - super.resetSemTypeCache(); + public synchronized void resetSemType() { defn = null; + super.resetSemType(); } @Override public Optional shapeOf(Context cx, Object object) { if (!isReadOnly()) { - return Optional.of(get(cx)); + return Optional.of(getSemType()); } BArray value = (BArray) object; SemType cachedShape = value.shapeOf(); 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 0e4b878aa0a9..0de948373b73 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 @@ -23,9 +23,11 @@ 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.MutableSemType; +import io.ballerina.runtime.api.types.semtype.MutableSemTypeDependencyManager; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.SubTypeData; @@ -42,7 +44,7 @@ * * @since 0.995.0 */ -public abstract class BType implements Type, SubTypeData, BSemTypeSupplier { +public abstract class BType implements Type, SubTypeData, MutableSemType { private static final SemType READONLY_WITH_B_TYPE = Core.union(Builder.readonlyType(), Core.B_TYPE_TOP); protected String typeName; @@ -52,6 +54,8 @@ public abstract class BType implements Type, SubTypeData, BSemTypeSupplier { private Type cachedReferredType = null; private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; + protected MutableSemTypeDependencyManager mutableSemTypeDependencyManager = + MutableSemTypeDependencyManager.getInstance(); protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -240,25 +244,19 @@ public Type getCachedImpliedType() { return this.cachedImpliedType; } - // If any child class allow mutation that will affect the SemType, it must call this method. - // TODO: update this comment to mention what context does - public void resetSemTypeCache() { - cachedSemType = null; - } - - // If any child class partially implement SemType it must override this method. - SemType createSemType(Context cx) { - return BTypeConverter.wrapAsPureBType(this); + // TODO: do better + @Override + public SemType createSemType() { + return Builder.wrapAsPureBType(this); } - @Override - public final SemType get(Context cx) { + protected SemType getSemType() { SemType semType = cachedSemType; if (semType == null) { synchronized (this) { semType = cachedSemType; if (semType == null) { - semType = createSemType(cx); + semType = createSemType(); if (isReadOnly()) { semType = Core.intersect(semType, READONLY_WITH_B_TYPE); } @@ -268,4 +266,44 @@ public final SemType get(Context cx) { } return semType; } + + @Override + public int all() { + getSemType(); + return cachedSemType.all(); + } + + @Override + public int some() { + getSemType(); + return cachedSemType.some(); + } + + @Override + public SubType[] subTypeData() { + getSemType(); + return cachedSemType.subTypeData(); + } + + @Override + public CachedResult cachedSubTypeRelation(SemType other) { + return CachedResult.NOT_FOUND; + } + + @Override + public void cacheSubTypeRelation(SemType other, boolean result) { + + } + + @Override + public SubType subTypeByCode(int code) { + getSemType(); + return cachedSemType.subTypeByCode(code); + } + + @Override + public synchronized void resetSemType() { + cachedSemType = null; + mutableSemTypeDependencyManager.notifyDependenciesToReset(this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java deleted file mode 100644 index 6b7f6a307d65..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeConverter.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.types.Type; -import io.ballerina.runtime.api.types.semtype.BasicTypeCode; -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.BSubType; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -/** - * This is a utility class for {@code Builder} class so that BTypes don't need to expose their internal structure as - * public to create semtypes from them. - * - * @since 2201.10.0 - */ -final class BTypeConverter { - - private BTypeConverter() { - } - - private static final SemType implementedTypes = - unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), - Builder.floatType(), Builder.decimalType(), Builder.stringType(), Builder.listType(), - Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), - Builder.xmlType(), Builder.handleType()); - private static final SemType READONLY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.readonlyType()); - private static final SemType ANY_SEMTYPE_PART = Core.intersect(implementedTypes, Builder.anyType()); - - private static SemType unionOf(SemType... semTypes) { - SemType result = Builder.neverType(); - for (SemType semType : semTypes) { - result = Core.union(result, semType); - } - return result; - } - - private static SemType from(Context cx, Type type) { - if (type instanceof SemType semType) { - return semType; - } else if (type instanceof BType bType) { - return fromBType(cx, bType); - } - throw new IllegalArgumentException("Unsupported type: " + type); - } - - private static SemType fromBType(Context cx, BType innerType) { - int staringSize = cx.addProvisionalType(innerType); - SemType res = innerType.get(cx); - cx.emptyProvisionalTypes(staringSize); - return res; - } - - static SemType fromReadonly(BReadonlyType readonlyType) { - SemType bTypePart = wrapAsPureBType(readonlyType); - return Core.union(READONLY_SEMTYPE_PART, bTypePart); - } - - static SemType wrapAsPureBType(BType bType) { - return Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(bType)); - } - - static SemType fromAnyType(BAnyType anyType) { - SemType bTypePart = wrapAsPureBType(anyType); - return Core.union(ANY_SEMTYPE_PART, bTypePart); - } - - static SemType fromFiniteType(Context cx, BFiniteType finiteType) { - BTypeParts parts = splitFiniteType(cx, finiteType); - if (parts.bTypeParts().isEmpty()) { - return parts.semTypePart(); - } - BType newFiniteType = (BType) parts.bTypeParts().get(0); - SemType bTypePart = wrapAsPureBType(newFiniteType); - return Core.union(parts.semTypePart(), bTypePart); - } - - static SemType fromUnionType(Context cx, BUnionType unionType) { - BTypeParts parts = splitUnion(cx, unionType); - if (parts.bTypeParts().isEmpty()) { - return parts.semTypePart(); - } - SemType bTypePart = Builder.basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(unionType)); - return Core.union(parts.semTypePart(), bTypePart); - } - - private record BTypeParts(SemType semTypePart, List bTypeParts) { - - } - - private static BTypeParts split(Context cx, Type type) { - if (type instanceof SemType) { - return new BTypeParts(from(cx, type), Collections.emptyList()); - } else if (type instanceof BXmlType) { - return new BTypeParts(from(cx, type), Collections.emptyList()); - } else if (type instanceof BUnionType unionType) { - return splitUnion(cx, unionType); - } else if (type instanceof BAnyType anyType) { - return splitAnyType(anyType); - } else if (type instanceof BTypeReferenceType referenceType) { - return split(cx, referenceType.getReferredType()); - } else if (type instanceof BIntersectionType intersectionType) { - return splitIntersection(cx, intersectionType); - } else if (type instanceof BReadonlyType readonlyType) { - return splitReadonly(readonlyType); - } else if (type instanceof BFiniteType finiteType) { - return splitFiniteType(cx, finiteType); - } else if (type instanceof PartialSemTypeSupplier supplier) { - return splitSemTypeSupplier(cx, supplier); - } else { - return new BTypeParts(Builder.neverType(), List.of(type)); - } - } - - private static BTypeParts splitIntersection(Context cx, BIntersectionType intersectionType) { - List members = Collections.unmodifiableList(intersectionType.getConstituentTypes()); - SemType semTypePart = Builder.valType(); - for (Type member : members) { - BTypeParts memberParts = split(cx, member); - semTypePart = Core.intersect(memberParts.semTypePart(), semTypePart); - } - BTypeParts effectiveTypeParts = split(cx, intersectionType.getEffectiveType()); - return new BTypeParts(semTypePart, effectiveTypeParts.bTypeParts()); - } - - private static BTypeParts splitSemTypeSupplier(Context cx, PartialSemTypeSupplier supplier) { - int startingIndex = cx.addProvisionalType((BType) supplier); - SemType semtype = supplier.get(cx); - cx.emptyProvisionalTypes(startingIndex); - SemType bBTypePart = Core.intersect(semtype, Core.B_TYPE_TOP); - if (Core.isNever(bBTypePart)) { - return new BTypeParts(semtype, Collections.emptyList()); - } - SemType pureSemTypePart = Core.intersect(semtype, Core.SEMTYPE_TOP); - BType bType = (BType) Core.subTypeData(semtype, BasicTypeCode.BT_B_TYPE); - return new BTypeParts(pureSemTypePart, List.of(bType)); - } - - private static BTypeParts splitAnyType(BAnyType anyType) { - SemType semTypePart = ANY_SEMTYPE_PART; - if (anyType.isReadOnly()) { - semTypePart = Core.intersect(semTypePart, Builder.readonlyType()); - } - return new BTypeParts(semTypePart, List.of(anyType)); - } - - private static BTypeParts splitFiniteType(Context cx, BFiniteType finiteType) { - Set newValueSpace = new HashSet<>(finiteType.valueSpace.size()); - SemType semTypePart = Builder.neverType(); - for (var each : finiteType.valueSpace) { - // TODO: lift this to Builder (Object) -> Type - Optional semType = Builder.shapeOf(cx, each); - if (semType.isPresent()) { - semTypePart = Core.union(semTypePart, semType.get()); - } else { - newValueSpace.add(each); - } - } - if (newValueSpace.isEmpty()) { - return new BTypeParts(semTypePart, List.of()); - } - BFiniteType newFiniteType = finiteType.cloneWithValueSpace(newValueSpace); - return new BTypeParts(semTypePart, List.of(newFiniteType)); - } - - private static BTypeParts splitReadonly(BReadonlyType readonlyType) { - // TODO: this is not exactly correct - return new BTypeParts(READONLY_SEMTYPE_PART, List.of(readonlyType)); - } - - private static BTypeParts splitUnion(Context cx, BUnionType unionType) { - List members = Collections.unmodifiableList(unionType.getMemberTypes()); - List bTypeMembers = new ArrayList<>(members.size()); - SemType semTypePart = Builder.neverType(); - for (Type member : members) { - BTypeParts memberParts = split(cx, member); - semTypePart = Core.union(memberParts.semTypePart(), semTypePart); - bTypeMembers.addAll(memberParts.bTypeParts()); - } - return new BTypeParts(semTypePart, Collections.unmodifiableList(bTypeMembers)); - } -} 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 f942b6ee958e..db976686c264 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,7 +25,6 @@ import io.ballerina.runtime.api.types.IntersectableReferenceType; 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.SemType; @@ -131,12 +130,9 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType(Context cx) { + public SemType createSemType() { Type referredType = getReferredType(); - if (referredType instanceof SemType semType) { - return semType; - } - return Builder.from(cx, referredType); + return mutableSemTypeDependencyManager.getSemType(referredType, this); } @Override 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 72568dc7641e..dc0b39f01311 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 @@ -25,7 +25,8 @@ import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; -import io.ballerina.runtime.api.types.semtype.Context; +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.internal.TypeChecker; @@ -169,7 +170,7 @@ public void setMemberTypes(Type[] members) { } this.memberTypes = readonly ? getReadOnlyTypes(members) : Arrays.asList(members); setFlagsBasedOnMembers(); - resetSemTypeCache(); + resetSemType(); } public void setOriginalMemberTypes(Type[] originalMemberTypes) { @@ -185,6 +186,7 @@ private void setMemberTypes(List members) { } private void setMemberTypes(List members, List originalMembers) { + resetSemType(); if (members == null) { return; } @@ -196,7 +198,6 @@ private void setMemberTypes(List members, List originalMembers) { this.memberTypes = readonly ? getReadOnlyTypes(members, new HashSet<>(members.size())) : members; this.resolvingReadonly = false; setFlagsBasedOnMembers(); - setOriginalMemberTypes(originalMembers); } @@ -545,7 +546,20 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - SemType createSemType(Context cx) { - return BTypeConverter.fromUnionType(cx, this); + public SemType createSemType() { + SemType result = Builder.neverType(); + boolean hasBType = false; + for (Type each : memberTypes) { + SemType eachSemType = mutableSemTypeDependencyManager.getSemType(each, this); + if (Core.containsBasicType(eachSemType, Builder.bType())) { + hasBType = true; + eachSemType = Core.intersect(eachSemType, Core.SEMTYPE_TOP); + } + result = Core.union(result, eachSemType); + } + if (hasBType) { + return Core.union(result, Builder.wrapAsPureBType(this)); + } + return result; } } 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 3fc5342ab6ce..e553bf13434a 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 @@ -155,18 +155,18 @@ public Optional getIntersectionType() { return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); } - // TODO: this class must also be a semtype class @Override - SemType createSemType(Context cx) { + public SemType createSemType() { SemType semType; if (constraint == null) { semType = pickTopType(); } else { SemType contraintSemtype; if (constraint instanceof ParameterizedType parameterizedType) { - contraintSemtype = Builder.from(cx, parameterizedType.getParamValueType()); + contraintSemtype = + mutableSemTypeDependencyManager.getSemType(parameterizedType.getParamValueType(), this); } else { - contraintSemtype = Builder.from(cx, constraint); + contraintSemtype = mutableSemTypeDependencyManager.getSemType(constraint, this); } assert !Core.containsBasicType(contraintSemtype, Core.B_TYPE_TOP) : "XML is a pure semtype"; semType = XmlUtils.xmlSequence(contraintSemtype); @@ -194,7 +194,7 @@ public void setIntersectionType(IntersectionType intersectionType) { public Optional shapeOf(Context cx, Object object) { XmlValue xmlValue = (XmlValue) object; if (!isReadOnly(xmlValue)) { - return Optional.of(get(cx)); + return Optional.of(getSemType()); } return readonlyShapeOf(object); } 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 585a44071e74..4ea468dd3ef0 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 @@ -4705,8 +4705,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; + string errorMsgPrefix = "incompatible types: 'map<(json & readonly)> & readonly' cannot be cast to 'Factory': "; + string errorMsg = errorMsgPrefix + errorMsgContent; assert(checkpanic err.detail()["message"], errorMsg); assert(err.message(), "{ballerina}TypeCastError"); } From 8c4607e8684c813b52aebab5a9bf20cf3f5ca7a8 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 7 Aug 2024 07:55:17 +0530 Subject: [PATCH 082/178] Patch unit tests with clashing type names --- .../QueryExprWithQueryConstructTypeTest.java | 4 +- .../negative-type-test-expr.bal | 42 +- .../binaryoperations/type-test-expr.bal | 42 +- .../query/multiple-order-by-clauses.bal | 337 ++-- .../test-src/query/order-by-clause.bal | 473 +++--- .../query-expr-with-query-construct-type.bal | 1422 +++++++++-------- .../query/simple-query-with-defined-type.bal | 493 +++--- .../record/readonly_record_fields.bal | 38 +- .../test_selectively_immutable_type.bal | 184 +-- 9 files changed, 1615 insertions(+), 1420 deletions(-) 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 7e7d36c784f3..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 @@ -362,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"); } @@ -370,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"); } 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 f0bab53f6e60..8f62f33845a2 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 @@ -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 ========================== 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 df59a1f51636..4fbee0c9e7fc 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 @@ -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 { 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 7913b3e5e775..e05c61303270 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/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/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()); From 92249d4c29209797d49d19687bb0e8fdd2b59115 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 7 Aug 2024 10:10:45 +0530 Subject: [PATCH 083/178] Implement future semtype --- .../runtime/api/types/semtype/Builder.java | 5 + .../runtime/api/types/semtype/Core.java | 5 +- .../runtime/internal/types/BArrayType.java | 4 +- .../runtime/internal/types/BFutureType.java | 32 +++++- .../runtime/internal/types/BMapType.java | 4 +- .../runtime/internal/types/BObjectType.java | 4 +- .../runtime/internal/types/BRecordType.java | 4 +- .../runtime/internal/types/BTupleType.java | 4 +- .../runtime/internal/types/BType.java | 21 ++-- .../types/semtype/BFutureSubType.java | 98 +++++++++++++++++++ .../internal/types/semtype/FutureUtils.java | 49 ++++++++++ .../port/test/CompilerSemTypeResolver.java | 1 + .../port/test/RuntimeSemTypeResolver.java | 9 ++ .../resources/test-src/type-rel/future-tv.bal | 7 ++ 14 files changed, 221 insertions(+), 26 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal 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 index 31da7968e5b3..beaa26830c9f 100644 --- 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 @@ -52,6 +52,7 @@ 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; @@ -403,6 +404,10 @@ public static SemType handleType() { return from(BT_HANDLE); } + public static SemType futureType() { + return from(BT_FUTURE); + } + public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; if (memo != null) { 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 index 21598c4bcdd6..9a27f1329640 100644 --- 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 @@ -19,6 +19,7 @@ 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.BObjectSubType; import io.ballerina.runtime.internal.types.semtype.BSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; @@ -35,6 +36,7 @@ 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_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; @@ -59,7 +61,7 @@ public final class Core { unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), listType(), Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), - Builder.xmlType(), Builder.handleType()); + Builder.xmlType(), Builder.handleType(), Builder.futureType()); public static final SemType ANY_SEMTYPE_PART = intersect(implementedTypes, Builder.anyType()); public static final SemType READONLY_SEMTYPE_PART = intersect(implementedTypes, Builder.readonlyType()); @@ -418,6 +420,7 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { } SubType subType = switch (typeCode.code()) { case CODE_OBJECT -> BObjectSubType.createDelegate(bdd); + case CODE_FUTURE -> BFutureSubType.createDelegate(bdd); default -> throw new IllegalArgumentException("Unexpected type code: " + typeCode); }; return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); 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 b8d9a16747ae..305be34ad6e3 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 @@ -223,7 +223,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } @@ -254,7 +254,7 @@ private SemType getSemTypePart(ListDefinition defn, boolean isReadOnly, int size } @Override - public synchronized void resetSemType() { + public void resetSemType() { defn = null; super.resetSemType(); } 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 df747fc1865c..57b4096d8d6c 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,16 +22,23 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.FutureType; 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.FutureUtils; + +import java.util.Optional; /** * {@code BFutureType} represents a future value in Ballerina. * * @since 0.995.0 */ -public class BFutureType extends BType implements FutureType { +public class BFutureType extends BType implements FutureType, TypeWithShape { - private Type constraint; + private final Type constraint; /** * Create a {@code {@link BFutureType}} which represents the future value. @@ -41,6 +48,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) { @@ -93,4 +101,24 @@ public String toString() { private String getConstraintString() { return constraint != null ? "<" + constraint + ">" : ""; } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.futureType(); + } + SemType constraintSemType = mutableSemTypeDependencyManager.getSemType(constraint, this); + Context cx = TypeChecker.context(); + if (Core.containsBasicType(constraintSemType, Builder.bType())) { + constraintSemType = Core.intersect(constraintSemType, Core.SEMTYPE_TOP); + SemType pureSemType = FutureUtils.futureContaining(cx.env, constraintSemType); + return Core.union(pureSemType, Builder.wrapAsPureBType(this)); + } + return FutureUtils.futureContaining(cx.env, constraintSemType); + } + + @Override + public Optional shapeOf(Context cx, Object object) { + throw new UnsupportedOperationException(); + } } 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 f9ab58edd964..3a8f2f148f77 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 @@ -184,7 +184,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } @@ -203,7 +203,7 @@ public synchronized SemType createSemType() { } @Override - public synchronized void resetSemType() { + public void resetSemType() { defn = null; super.resetSemType(); } 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 bd9e22375d9a..4036dbc0acbe 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 @@ -276,7 +276,7 @@ public TypeIdSet getTypeIdSet() { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (distinctIdSupplier == null) { distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); } @@ -433,7 +433,7 @@ private static SemType fieldShape(Context cx, Field field, AbstractObjectValue o } @Override - public synchronized void resetSemType() { + public void resetSemType() { defn = null; super.resetSemType(); } 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 a1f7bbecb3aa..ff04401c0aa8 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 @@ -238,7 +238,7 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } @@ -281,7 +281,7 @@ public synchronized SemType createSemType() { } @Override - public synchronized void resetSemType() { + public void resetSemType() { defn = null; super.resetSemType(); } 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 9b9ccbdfd954..8dec1f9ae83f 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 @@ -317,7 +317,7 @@ public String getAnnotationKey() { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } @@ -352,7 +352,7 @@ public synchronized SemType createSemType() { } @Override - public synchronized void resetSemType() { + public void resetSemType() { defn = null; super.resetSemType(); } 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 0de948373b73..05daf454cb63 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 @@ -244,7 +244,6 @@ public Type getCachedImpliedType() { return this.cachedImpliedType; } - // TODO: do better @Override public SemType createSemType() { return Builder.wrapAsPureBType(this); @@ -252,18 +251,14 @@ public SemType createSemType() { protected SemType getSemType() { SemType semType = cachedSemType; - if (semType == null) { - synchronized (this) { - semType = cachedSemType; - if (semType == null) { - semType = createSemType(); - if (isReadOnly()) { - semType = Core.intersect(semType, READONLY_WITH_B_TYPE); - } - cachedSemType = semType; - } - } + if (semType != null) { + return semType; + } + semType = createSemType(); + if (isReadOnly()) { + semType = Core.intersect(semType, READONLY_WITH_B_TYPE); } + cachedSemType = semType; return semType; } @@ -302,7 +297,7 @@ public SubType subTypeByCode(int code) { } @Override - public synchronized void resetSemType() { + public void resetSemType() { cachedSemType = null; mutableSemTypeDependencyManager.notifyDependenciesToReset(this); } 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..1d225a03042f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java @@ -0,0 +1,98 @@ +/* + * 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; + +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, null, null, 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/FutureUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java new file mode 100644 index 000000000000..312ec8c3e581 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.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.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.CellAtomicType; +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; + +// TODO: this should be part of the public API +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.valType()) { + return Builder.futureType(); + } + 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/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 index 2929936eb974..08aaa060679a 100644 --- 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 @@ -486,6 +486,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangBuiltInRefType 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); }; } 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 index 21a5b438cf3d..4773578aafd1 100644 --- 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 @@ -28,6 +28,7 @@ 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; @@ -337,12 +338,19 @@ private SemType resolveConstrainedTypeDesc(TypeTestContext cx, Map resolveMapTypeDesc(cx, mod, defn, depth, td); + case FUTURE -> resolveFutureTypeDesc(cx, mod, defn, depth, td); case XML -> resolveXmlTypeDesc(cx, mod, defn, depth, td); default -> throw new UnsupportedOperationException( "Constrained type not implemented: " + refTypeNode.typeKind); }; } + 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); @@ -555,6 +563,7 @@ private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { return switch (td.typeKind) { case NEVER -> Builder.neverType(); case XML -> Builder.xmlType(); + case FUTURE -> Builder.futureType(); default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); }; } 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; From c4d12972e9d0055e2271c8aa5c014d470b8cdc1b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 7 Aug 2024 12:20:57 +0530 Subject: [PATCH 084/178] Implement regex type --- .../api/types/semtype/BasicTypeCode.java | 24 +++++++------ .../runtime/api/types/semtype/Builder.java | 9 +++++ .../runtime/api/types/semtype/Core.java | 2 +- .../runtime/api/values/BRegexpValue.java | 5 +++ .../runtime/internal/TypeChecker.java | 1 + .../runtime/internal/types/BAnyType.java | 6 +++- .../runtime/internal/types/BAnydataType.java | 19 ++++++++-- .../internal/types/BIntersectionType.java | 1 + .../runtime/internal/types/BReadonlyType.java | 3 +- .../internal/types/semtype/RegexUtils.java | 36 +++++++++++++++++++ .../internal/types/semtype/SemTypeHelper.java | 2 ++ .../runtime/internal/values/RegExpValue.java | 17 +++++++++ .../port/test/RuntimeSemTypeResolver.java | 2 ++ 13 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java 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 index 487421b5dc6e..1b1f8b734eb8 100644 --- 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 @@ -35,16 +35,17 @@ public final class BasicTypeCode { 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_FUTURE = 0x0A; - public static final int CODE_STREAM = 0x0B; - public static final int CODE_LIST = 0x0C; - public static final int CODE_MAPPING = 0x0D; - public static final int CODE_TABLE = 0x0E; - public static final int CODE_XML = 0x0F; - public static final int CODE_OBJECT = 0x10; - public static final int CODE_CELL = 0x11; - public static final int CODE_UNDEF = 0x12; - public static final int CODE_B_TYPE = 0x13; + 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; + public static final int CODE_B_TYPE = 0x14; // TODO: see if we can turn this class to an enum with a value // Inherently immutable @@ -58,6 +59,7 @@ public final class BasicTypeCode { public static final BasicTypeCode BT_TYPEDESC = from(CODE_TYPEDESC); public static final BasicTypeCode BT_HANDLE = from(CODE_HANDLE); public static final BasicTypeCode BT_FUNCTION = from(CODE_FUNCTION); + public static final BasicTypeCode BT_REGEXP = from(CODE_REGEXP); // Inherently mutable public static final BasicTypeCode BT_FUTURE = from(CODE_FUTURE); @@ -80,7 +82,7 @@ public final class BasicTypeCode { public static final int BASIC_TYPE_MASK = (1 << (CODE_STRING + 1)) - 1; static final int VT_MASK = (1 << VT_COUNT) - 1; - static final int VT_COUNT_INHERENTLY_IMMUTABLE = 0x0A; + static final int VT_COUNT_INHERENTLY_IMMUTABLE = CODE_FUTURE; public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; private int code; 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 index beaa26830c9f..fc1b9716e11c 100644 --- 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BRegexpValue; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.TypeWithShape; @@ -57,6 +58,7 @@ 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_XML; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_B_TYPE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; @@ -85,6 +87,7 @@ public final class Builder { | (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]; @@ -306,6 +309,8 @@ public static Optional shapeOf(Context cx, Object object) { return typeOfObject(cx, objectValue); } else if (object instanceof XmlValue xmlValue) { return typeOfXml(cx, xmlValue); + } else if (object instanceof BRegexpValue regexpValue) { + return regexpValue.shapeOf(); } return Optional.empty(); } @@ -408,6 +413,10 @@ public static SemType futureType() { return from(BT_FUTURE); } + public static SemType regexType() { + return from(BT_REGEXP); + } + public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; if (memo != null) { 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 index 9a27f1329640..4b50e173b9f4 100644 --- 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 @@ -61,7 +61,7 @@ public final class Core { unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), listType(), Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), - Builder.xmlType(), Builder.handleType(), Builder.futureType()); + Builder.xmlType(), Builder.handleType(), Builder.futureType(), Builder.regexType()); public static final SemType ANY_SEMTYPE_PART = intersect(implementedTypes, Builder.anyType()); public static final SemType READONLY_SEMTYPE_PART = intersect(implementedTypes, Builder.readonlyType()); 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/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index d95fa4e863f0..d0fe1dbffa01 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 @@ -125,6 +125,7 @@ public final class TypeChecker { public static Object checkCast(Object sourceVal, Type targetType) { List errors = new ArrayList<>(); + // TODO: we don't need to do this like this see checkIsType(Object, Type) Type sourceType = getImpliedType(getType(sourceVal)); if (checkIsType(errors, sourceVal, sourceType, targetType)) { return sourceVal; 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 c63fbf7c8cbc..fcc27e134e58 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 @@ -106,6 +106,10 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { - return Core.union(Core.ANY_SEMTYPE_PART, Builder.wrapAsPureBType(this)); + SemType semType = Builder.anyType(); + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.readonlyType()); + } + return Core.union(semType, Builder.wrapAsPureBType(this)); } } 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 7d006df8e080..5e51dd886e61 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,11 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.AnydataType; 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.values.RefValue; /** @@ -31,8 +36,7 @@ * * @since 0.995.0 */ -public class BAnydataType extends BUnionType implements AnydataType { - +public class BAnydataType extends BUnionType implements AnydataType, SemType { /** * Create a {@code BAnydataType} which represents the anydata type. * @@ -86,4 +90,15 @@ public String toString() { } return super.toString(); } + + // TODO: this type don't have mutable parts so this should be a immutable semtype + @Override + public SemType createSemType() { + Context cx = TypeChecker.context(); + SemType semType = Builder.anyDataType(cx); + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.readonlyType()); + } + return Core.union(semType, Builder.wrapAsPureBType(this)); + } } 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 8a244c459236..091ffbe5aaa0 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 @@ -232,6 +232,7 @@ public SemType createSemType() { result = Core.intersect(result, Core.SEMTYPE_TOP); for (int i = 1; i < constituentTypes.size(); i++) { SemType memberType = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(i), this); + memberType = Core.intersect(memberType, Core.SEMTYPE_TOP); result = Core.intersect(result, memberType); } if (hasBType) { 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 76db2e38631e..5aa2d9840977 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 @@ -61,8 +61,9 @@ public boolean isReadOnly() { return true; } + // TODO: this must be immutable semtype as well @Override public SemType createSemType() { - return Core.union(Core.READONLY_SEMTYPE_PART, Builder.wrapAsPureBType(this)); + return Core.union(Builder.readonlyType(), Builder.wrapAsPureBType(this)); } } 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..9728e8429d81 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java @@ -0,0 +1,36 @@ +/* + * 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; + +public final class RegexUtils { + + private RegexUtils() { + + } + + public static SemType regexShape(String value) { + SemType stringSubtype = Builder.stringConst(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 index ba0fee4be013..53552bf33bda 100644 --- 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 @@ -34,6 +34,7 @@ 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; @@ -66,6 +67,7 @@ private static String bitSetRepr(int bits) { 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"); 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 da45d3856a1e..fb3eeb851004 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.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.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 @@ -123,4 +130,14 @@ public boolean equals(Object o, Set visitedValues) { } return this.stringValue(null).equals(rhsRegExpValue.stringValue(null)); } + + @Override + public SemType widenedType(Context cx) { + return Builder.regexType(); + } + + @Override + public Optional shapeOf() { + return Optional.of(this.shape); + } } 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 index 4773578aafd1..ffe88e0dfa0b 100644 --- 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 @@ -495,6 +495,8 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp return Builder.charType(); } else if (td.pkgAlias.value.equals("xml")) { return resolveXmlSubType(name); + } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { + return Builder.regexType(); } BLangNode moduleLevelDef = mod.get(name); From 554e819ca0cf3e1271a52c681effb25685b7a507 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 9 Aug 2024 08:31:22 +0530 Subject: [PATCH 085/178] Implement typedesc semtype --- .../runtime/api/types/semtype/Builder.java | 8 +- .../runtime/api/types/semtype/Core.java | 10 +- .../internal/types/BSemTypeWrapper.java | 2 +- .../runtime/internal/types/BTypedescType.java | 25 +++- .../internal/types/semtype/BErrorSubType.java | 6 +- .../types/semtype/BTypedescSubType.java | 108 ++++++++++++++++++ .../internal/types/semtype/TypedescUtils.java | 50 ++++++++ .../port/test/RuntimeSemTypeResolver.java | 9 ++ .../test-src/type-rel/typedesc-tv.bal | 10 ++ 9 files changed, 213 insertions(+), 15 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal 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 index fc1b9716e11c..f4ad909bfb40 100644 --- 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 @@ -59,6 +59,7 @@ 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_B_TYPE; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; @@ -137,7 +138,7 @@ public static SemType from(BasicTypeCode typeCode) { return SemType.from(1 << typeCode.code()); } - // FIXME: remove this method + // TODO: remove this method public static SemType from(Context cx, Type type) { if (type instanceof SemType semType) { return semType; @@ -417,6 +418,11 @@ public static SemType regexType() { return from(BT_REGEXP); } + public static SemType typeDescType() { + return from(BT_TYPEDESC); + } + + public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; if (memo != null) { 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 index 4b50e173b9f4..4de66c570d96 100644 --- 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.internal.types.semtype.BFutureSubType; import io.ballerina.runtime.internal.types.semtype.BObjectSubType; import io.ballerina.runtime.internal.types.semtype.BSubType; +import io.ballerina.runtime.internal.types.semtype.BTypedescSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; import io.ballerina.runtime.internal.types.semtype.SubTypeData; import io.ballerina.runtime.internal.types.semtype.SubtypePair; @@ -38,6 +39,7 @@ 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_TYPEDESC; 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.Builder.cellContaining; @@ -57,13 +59,6 @@ public final class Core { public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); public static final SemType B_TYPE_TOP = SemType.from(1 << BT_B_TYPE.code()); - private static final SemType implementedTypes = - unionOf(Builder.neverType(), Builder.nilType(), Builder.booleanType(), Builder.intType(), - Builder.floatType(), Builder.decimalType(), Builder.stringType(), listType(), - Builder.mappingType(), Builder.functionType(), Builder.objectType(), Builder.errorType(), - Builder.xmlType(), Builder.handleType(), Builder.futureType(), Builder.regexType()); - public static final SemType ANY_SEMTYPE_PART = intersect(implementedTypes, Builder.anyType()); - public static final SemType READONLY_SEMTYPE_PART = intersect(implementedTypes, Builder.readonlyType()); private Core() { } @@ -421,6 +416,7 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { SubType subType = switch (typeCode.code()) { case CODE_OBJECT -> BObjectSubType.createDelegate(bdd); case CODE_FUTURE -> BFutureSubType.createDelegate(bdd); + case CODE_TYPEDESC -> BTypedescSubType.createDelegate(bdd); default -> throw new IllegalArgumentException("Unexpected type code: " + typeCode); }; return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); 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 index ea8a88242ac9..c9830732f780 100644 --- 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 @@ -21,8 +21,8 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; // TODO: make this a sealed class with clearly defined extensions 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 a6988bc263cb..16616c81d5a1 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,6 +24,12 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.Type; 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.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.TypedescUtils; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; @@ -33,10 +39,12 @@ * @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 +92,19 @@ public boolean isReadOnly() { public String toString() { return "typedesc" + "<" + constraint.toString() + ">"; } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.typeDescType(); + } + SemType constraint = mutableSemTypeDependencyManager.getSemType(getConstraint(), this); + Context cx = TypeChecker.context(); + if (Core.containsBasicType(constraint, Builder.bType())) { + constraint = Core.intersect(constraint, Core.SEMTYPE_TOP); + SemType pureSemType = TypedescUtils.typedescContaining(cx.env, constraint); + return Core.union(pureSemType, Builder.wrapAsPureBType(this)); + } + return TypedescUtils.typedescContaining(cx.env, constraint); + } } 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 index b0fe9de0fc28..4cf4342f9542 100644 --- 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 @@ -66,11 +66,7 @@ public SubType intersect(SubType other) { @Override public SubType complement() { - return createDelegate(errorSubtypeComplement()); - } - - private SubType errorSubtypeComplement() { - return Builder.bddSubtypeRo().diff(inner); + return createDelegate(Builder.bddSubtypeRo().diff(inner)); } @Override 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..66162fc911f9 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java @@ -0,0 +1,108 @@ +/* + * 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; + +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.bddSubtypeRo().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.bddSubtypeRo()) : 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/TypedescUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java new file mode 100644 index 000000000000..9d9b8cab5551 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java @@ -0,0 +1,50 @@ +/* + * 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.CellAtomicType; +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; + +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.valType()) { + return Builder.typeDescType(); + } + 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/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 index ffe88e0dfa0b..0574541a8714 100644 --- 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 @@ -34,6 +34,7 @@ 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.TypedescUtils; import io.ballerina.runtime.internal.types.semtype.XmlUtils; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.tree.NodeKind; @@ -340,11 +341,18 @@ private SemType resolveConstrainedTypeDesc(TypeTestContext cx, 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); @@ -585,6 +593,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) case ERROR -> Builder.errorType(); case XML -> Builder.xmlType(); case HANDLE -> Builder.handleType(); + case TYPEDESC -> Builder.typeDescType(); default -> throw new IllegalStateException("Unknown type: " + td); }; } 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; From 5a9fa45d2855522ee541f3db7399677a380cd480 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 9 Aug 2024 11:27:16 +0530 Subject: [PATCH 086/178] Implement table and stream types --- .../runtime/api/types/semtype/Builder.java | 85 +++-- .../runtime/api/types/semtype/Core.java | 48 ++- .../types/semtype/Definition.java | 5 +- .../runtime/api/types/semtype/Env.java | 8 + .../MutableSemTypeDependencyManager.java | 1 + .../api/types/semtype/PredefinedTypeEnv.java | 154 +++++++- .../ballerina/runtime/api/values/BArray.java | 8 +- .../ballerina/runtime/api/values/BError.java | 8 +- .../io/ballerina/runtime/api/values/BMap.java | 8 +- .../api/values/PatternMatchableValue.java | 28 ++ .../runtime/api/values/RecursiveValue.java | 12 + .../runtime/internal/TypeChecker.java | 17 +- .../runtime/internal/types/BAnyType.java | 2 +- .../runtime/internal/types/BAnydataType.java | 2 +- .../runtime/internal/types/BArrayType.java | 44 ++- .../runtime/internal/types/BErrorType.java | 43 +-- .../runtime/internal/types/BFiniteType.java | 7 +- .../runtime/internal/types/BFunctionType.java | 30 +- .../runtime/internal/types/BFutureType.java | 13 +- .../internal/types/BIntersectionType.java | 34 +- .../runtime/internal/types/BMapType.java | 37 +- .../internal/types/BNetworkObjectType.java | 4 +- .../runtime/internal/types/BObjectType.java | 104 ++---- .../internal/types/BParameterizedType.java | 7 + .../runtime/internal/types/BReadonlyType.java | 2 +- .../runtime/internal/types/BRecordType.java | 100 ++--- .../runtime/internal/types/BStreamType.java | 29 +- .../runtime/internal/types/BTableType.java | 64 +++- .../runtime/internal/types/BTupleType.java | 50 +-- .../runtime/internal/types/BType.java | 2 +- .../internal/types/BTypeReferenceType.java | 13 +- .../runtime/internal/types/BTypedescType.java | 6 +- .../runtime/internal/types/BUnionType.java | 9 +- .../runtime/internal/types/BXmlType.java | 7 +- .../internal/types/DistinctIdSupplier.java | 2 +- .../runtime/internal/types/ShapeSupplier.java | 31 ++ .../runtime/internal/types/TypeWithShape.java | 5 +- .../internal/types/semtype/BListSubType.java | 4 +- .../internal/types/semtype/BMappingProj.java | 2 +- .../types/semtype/BStreamSubType.java | 90 +++++ .../internal/types/semtype/BTableSubType.java | 105 ++++++ .../types/semtype/FixedLengthArray.java | 2 +- .../types/semtype/FunctionDefinition.java | 1 + .../types/semtype/ListDefinition.java | 1 + .../types/semtype/MappingDefinition.java | 1 + .../types/semtype/ObjectDefinition.java | 1 + .../internal/types/semtype/PureSemType.java | 2 + .../types/semtype/StreamDefinition.java | 57 +++ .../internal/types/semtype/TableUtils.java | 99 +++++ .../internal/values/AbstractArrayValue.java | 18 + .../runtime/internal/values/MapValueImpl.java | 18 + .../port/test/RuntimeSemTypeResolver.java | 48 ++- .../semtype/port/test/SemTypeTest.java | 8 - .../test-src/type-rel/stream-recursive-tv.bal | 8 + .../test-src/type-rel/stream-subtype-tv.bal | 10 + .../test-src/type-rel/stream-subtype2-tv.bal | 19 + .../jvm/tests/VariableReturnType.java | 10 +- .../DependentlyTypedFunctionsBalaTest.java | 2 +- .../NativeConversionNegativeTest.java | 4 +- .../DependentlyTypedFunctionsTest.java | 4 +- .../negative-type-test-expr.bal | 106 +++--- .../binaryoperations/type-test-expr.bal | 154 ++++---- .../list_constructor_infer_type.bal | 42 ++- .../dependently_typed_functions_bir_test.bal | 68 ++-- .../dependently_typed_functions_test.bal | 305 +++++++-------- ...erred_dependently_typed_func_signature.bal | 168 +++++---- .../query/simple-query-with-var-type.bal | 14 +- .../matchstmt/list-match-pattern.bal | 350 +++++++++--------- .../matchstmt/match-stmt-type-narrow.bal | 16 +- .../structured_record_match_patterns.bal | 58 +-- 70 files changed, 1873 insertions(+), 951 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{internal => api}/types/semtype/Definition.java (82%) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal 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 index f4ad909bfb40..b648c0b0d737 100644 --- 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 @@ -24,7 +24,9 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BRegexpValue; import io.ballerina.runtime.api.values.BString; -import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.api.values.BTable; +import io.ballerina.runtime.api.values.BValue; +import io.ballerina.runtime.api.values.PatternMatchableValue; import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; import io.ballerina.runtime.internal.types.semtype.BCellSubType; @@ -33,12 +35,11 @@ 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.BObjectSubType; import io.ballerina.runtime.internal.types.semtype.BStringSubType; -import io.ballerina.runtime.internal.types.semtype.BSubType; import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; import io.ballerina.runtime.internal.types.semtype.ListDefinition; 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 io.ballerina.runtime.internal.values.AbstractObjectValue; import io.ballerina.runtime.internal.values.DecimalValue; @@ -62,7 +63,6 @@ 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_B_TYPE; -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.CellAtomicType.CellMutability.CELL_MUT_LIMITED; @@ -93,17 +93,6 @@ public final class Builder { private static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; - 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); - private static final ConcurrentLazySupplier READONLY_TYPE = 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_XML, XmlUtils.XML_SUBTYPE_RO) - )); private static final ConcurrentLazySupplier MAPPING_RO = new ConcurrentLazySupplier<>(() -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())) ); @@ -127,6 +116,9 @@ public final class Builder { XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW)); 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() { } @@ -199,7 +191,7 @@ public static SemType listType() { } public static SemType readonlyType() { - return READONLY_TYPE.get(); + return PREDEFINED_TYPE_ENV.readonlyType(); } static SemType basicTypeUnion(int bitset) { @@ -281,6 +273,32 @@ static SubType[] initializeSubtypeArray(int some) { return new SubType[Integer.bitCount(some)]; } + public static Optional readonlyShapeOf(Context cx, Object object) { + if (object == null) { + return Optional.of(nilType()); + } else if (object instanceof DecimalValue decimalValue) { + return Optional.of(decimalConst(decimalValue.value())); + } else if (object instanceof Double doubleValue) { + return Optional.of(floatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(intConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(booleanConst(booleanValue)); + } else if (object instanceof BString stringValue) { + return Optional.of(stringConst(stringValue.getValue())); + } else if (object instanceof BValue bValue) { + Type type = bValue.getType(); + if (type instanceof TypeWithShape typeWithShape) { + return typeWithShape.readonlyShapeOf(cx, Builder::readonlyShapeOf, object); + } else { + return Optional.empty(); + } + } + return Optional.empty(); + } + // TODO: factor this to a separate class public static Optional shapeOf(Context cx, Object object) { if (object == null) { @@ -312,34 +330,41 @@ public static Optional shapeOf(Context cx, Object object) { return typeOfXml(cx, xmlValue); } else if (object instanceof BRegexpValue regexpValue) { return regexpValue.shapeOf(); + } else if (object instanceof BTable table) { + return typeOfTable(cx, table); } return Optional.empty(); } + private static Optional typeOfTable(Context cx, BTable table) { + TypeWithShape typeWithShape = (TypeWithShape) table.getType(); + return typeWithShape.shapeOf(cx, Builder::shapeOf, table); + } + // Combine these methods maybe introduce a marker interface private static Optional typeOfXml(Context cx, XmlValue xmlValue) { TypeWithShape typeWithShape = (TypeWithShape) xmlValue.getType(); - return typeWithShape.shapeOf(cx, xmlValue); + return typeWithShape.shapeOf(cx, Builder::shapeOf, xmlValue); } private static Optional typeOfError(Context cx, BError errorValue) { TypeWithShape typeWithShape = (TypeWithShape) errorValue.getType(); - return typeWithShape.shapeOf(cx, errorValue); + return typeWithShape.shapeOf(cx, Builder::shapeOf, errorValue); } private static Optional typeOfMap(Context cx, BMap mapValue) { TypeWithShape typeWithShape = (TypeWithShape) mapValue.getType(); - return typeWithShape.shapeOf(cx, mapValue); + return typeWithShape.shapeOf(cx, Builder::shapeOf, mapValue); } private static Optional typeOfObject(Context cx, AbstractObjectValue objectValue) { TypeWithShape typeWithShape = (TypeWithShape) objectValue.getType(); - return typeWithShape.shapeOf(cx, objectValue); + return typeWithShape.shapeOf(cx, Builder::shapeOf, objectValue); } private static Optional typeOfArray(Context cx, BArray arrayValue) { TypeWithShape typeWithShape = (TypeWithShape) arrayValue.getType(); - return typeWithShape.shapeOf(cx, arrayValue); + return typeWithShape.shapeOf(cx, Builder::shapeOf, arrayValue); } public static SemType roCellContaining(Env env, SemType ty) { @@ -422,6 +447,9 @@ public static SemType typeDescType() { return from(BT_TYPEDESC); } + public static SemType streamType() { + return from(BasicTypeCode.BT_STREAM); + } public static SemType anyDataType(Context context) { SemType memo = context.anydataMemo; @@ -431,7 +459,8 @@ public static SemType anyDataType(Context context) { Env env = context.env; ListDefinition listDef = new ListDefinition(); MappingDefinition mapDef = new MappingDefinition(); - SemType accum = unionOf(SIMPLE_OR_STRING, xmlType(), listDef.getSemType(env), mapDef.getSemType(env)); + SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); + SemType accum = unionOf(SIMPLE_OR_STRING, xmlType(), listDef.getSemType(env), mapDef.getSemType(env), tableTy); listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); context.anydataMemo = accum; @@ -474,8 +503,16 @@ public static MappingAtomicType mappingAtomicInner() { return MAPPING_ATOMIC_INNER.get(); } - public static SemType wrapAsPureBType(BType bType) { - return basicSubType(BasicTypeCode.BT_B_TYPE, BSubType.wrap(bType)); + public static BddNode listSubtypeThreeElement() { + return LIST_SUBTYPE_THREE_ELEMENT; + } + + public static BddNode listSubtypeThreeElementRO() { + return LIST_SUBTYPE_THREE_ELEMENT_RO; + } + + public static BddNode listSubtypeTwoElement() { + return LIST_SUBTYPE_TWO_ELEMENT; } private static final class IntTypeCache { 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 index 4de66c570d96..33e28f5d6212 100644 --- 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 @@ -21,7 +21,9 @@ import io.ballerina.runtime.internal.types.semtype.AllOrNothing; import io.ballerina.runtime.internal.types.semtype.BFutureSubType; import io.ballerina.runtime.internal.types.semtype.BObjectSubType; +import io.ballerina.runtime.internal.types.semtype.BStreamSubType; import io.ballerina.runtime.internal.types.semtype.BSubType; +import io.ballerina.runtime.internal.types.semtype.BTableSubType; import io.ballerina.runtime.internal.types.semtype.BTypedescSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; import io.ballerina.runtime.internal.types.semtype.SubTypeData; @@ -39,6 +41,8 @@ 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.CODE_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; @@ -49,6 +53,7 @@ import static io.ballerina.runtime.api.types.semtype.CellAtomicType.intersectCellAtomicType; import static io.ballerina.runtime.internal.types.semtype.BCellSubType.cellAtomType; import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; +import static io.ballerina.runtime.internal.types.semtype.BMappingProj.mappingMemberTypeInner; /** * Contain functions defined in `core.bal` file. @@ -291,11 +296,11 @@ public static boolean isNever(SemType t) { public static boolean isSubType(Context cx, SemType t1, SemType t2) { // IF t1 and t2 are not pure semtypes calling this is an undefined SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); - if (cached != SemType.CachedResult.NOT_FOUND) { - return cached == SemType.CachedResult.TRUE; - } +// if (cached != SemType.CachedResult.NOT_FOUND) { +// return cached == SemType.CachedResult.TRUE; +// } boolean result = isEmpty(cx, diff(t1, t2)); - t1.cacheSubTypeRelation(t2, result); +// t1.cacheSubTypeRelation(t2, result); return result; } @@ -376,7 +381,7 @@ public static SemType intersectMemberSemTypes(Env env, SemType t1, SemType t2) { return cellContaining(env, atomicType.ty(), undef().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); } - private static Optional cellAtomicType(SemType t) { + public static Optional cellAtomicType(SemType t) { SemType cell = Builder.cell(); if (t.some() == 0) { return cell.equals(t) ? Optional.of(Builder.cellAtomicVal()) : Optional.empty(); @@ -417,6 +422,8 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { 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}); @@ -429,4 +436,33 @@ private static SemType unionOf(SemType... semTypes) { } return result; } -} + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), Builder.undef()); + } + + public static Optional listAtomicType(Context cx, SemType t) { + ListAtomicType listAtomicInner = Builder.listAtomicInner(); + if (t.some() == 0) { + return Core.isSubtypeSimple(t, Builder.listType()) ? Optional.ofNullable(listAtomicInner) : + Optional.empty(); + } + Env env = cx.env; + if (!isSubtypeSimple(t, Builder.listType())) { + return Optional.empty(); + } + return bddListAtomicType(env, (Bdd) getComplexSubtypeData(t, BT_LIST), listAtomicInner); + } + + 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(); + } +} \ No newline at end of file diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java similarity index 82% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java index 0e144cc1fab9..cd47a8302b3c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Definition.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java @@ -16,10 +16,7 @@ * under the License. */ -package io.ballerina.runtime.internal.types.semtype; - -import io.ballerina.runtime.api.types.semtype.Env; -import io.ballerina.runtime.api.types.semtype.SemType; +package io.ballerina.runtime.api.types.semtype; // NOTE: definitions are not thread safe public interface Definition { 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 index 3ffd01b7d4ca..fe735c3e21d3 100644 --- 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 @@ -138,6 +138,14 @@ public ListAtomicType getRecListAtomType(RecAtom ra) { } } + 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 { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java index 7df57345dc95..545981ad8688 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java @@ -50,6 +50,7 @@ public synchronized void notifyDependenciesToReset(MutableSemType semType) { } public synchronized SemType getSemType(Type target, MutableSemType self) { + assert target != null; if (target instanceof MutableSemType mutableTarget) { List dependencies = this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); 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 index 89140d1c47a1..2a22d1013e91 100644 --- 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 @@ -19,8 +19,12 @@ 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.FixedLengthArray; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; import java.util.ArrayList; import java.util.Collection; @@ -29,9 +33,17 @@ 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.basicTypeUnion; +import static io.ballerina.runtime.api.types.semtype.Builder.from; import static io.ballerina.runtime.api.types.semtype.Builder.stringConst; import static io.ballerina.runtime.api.types.semtype.Core.union; import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; @@ -39,6 +51,10 @@ final class PredefinedTypeEnv { private static PredefinedTypeEnv instance; + 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<>(); @@ -48,7 +64,7 @@ final class PredefinedTypeEnv { // 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(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + () -> CellAtomicType.from(basicTypeUnion(VT_MASK), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); private final Supplier atomCellVal = @@ -56,14 +72,17 @@ final class PredefinedTypeEnv { private final Supplier cellSemTypeVal = new ConcurrentLazySupplier<>( () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal())))); private final Supplier cellAtomicNever = new ConcurrentLazySupplierWithCallback<>( - () -> CellAtomicType.from(Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + () -> 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(Builder.inner(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + () -> CellAtomicType.from(inner.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); private final Supplier atomCellInner = @@ -101,16 +120,87 @@ final class PredefinedTypeEnv { 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.undef(), 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(Builder.innerReadOnly(), CellAtomicType.CellMutability.CELL_MUT_NONE), + () -> 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())))); + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO.get())))); private final Supplier listAtomicRO = new ConcurrentLazySupplierWithCallback<>( () -> new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO.get()), this.initializedRecListAtoms::add @@ -121,14 +211,6 @@ final class PredefinedTypeEnv { ); // TypeAtoms related to [any|error, any|error]. This is to avoid passing down env argument when doing // streamSubtypeComplement operation. - private final Supplier cellAtomicUndef = new ConcurrentLazySupplierWithCallback<>( - () -> CellAtomicType.from(Builder.undef(), 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 cellAtomicObjectMemberKind = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( @@ -182,7 +264,7 @@ final class PredefinedTypeEnv { private final Supplier cellAtomicValRO = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( - Builder.readonlyType(), CellAtomicType.CellMutability.CELL_MUT_NONE), + readonlyType.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); private final Supplier atomCellValRO = createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicValRO, this::cellAtomIndex); @@ -217,13 +299,37 @@ final class PredefinedTypeEnv { 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.initilizeEnv(); + instance.initilize(); } return instance; } @@ -237,7 +343,7 @@ private static Supplier createTypeAtomSupplierF }); } - private void initilizeEnv() { + private void initilize() { // Initialize RecAtoms mappingAtomicRO(); listAtomicRO(); @@ -439,6 +545,18 @@ 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) { fillRecAtoms(env.recListAtoms, initializedRecListAtoms); @@ -468,6 +586,10 @@ SemType cellSemTypeInner() { return cellSemTypeInner.get(); } + public Atom atomListTwoElement() { + return atomListTwoElement.get(); + } + @FunctionalInterface private interface IndexSupplier { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java index efc1e70cb995..7ffe41b38cd1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.types.TypeWithShape; /** *

@@ -26,7 +27,7 @@ * * @since 1.1.0 */ -public interface BArray extends BRefValue, BCollection { +public interface BArray extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { /** * Get value in the given array index. @@ -236,4 +237,9 @@ public interface BArray extends BRefValue, BCollection { void setLength(long i); long getLength(); + + @Override + default TypeWithShape getTypeWithShape() { + return (TypeWithShape) getType(); + } } 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..47f4e4a6b8bf 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,6 +17,8 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.internal.types.TypeWithShape; + import java.io.PrintWriter; import java.util.List; @@ -27,7 +29,7 @@ * * @since 1.1.0 */ -public abstract class BError extends RuntimeException implements BValue { +public abstract class BError extends RuntimeException implements BValue, PatternMatchableValue { public static final String ERROR_PRINT_PREFIX = "error: "; @@ -83,4 +85,8 @@ public void printStackTrace(PrintWriter printWriter) { */ public abstract List getCallStack(); + @Override + public TypeWithShape getTypeWithShape() { + return (TypeWithShape) getType(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index 1eb65cc3fab8..f331fa06dd8c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.types.TypeWithShape; import java.util.Collection; import java.util.Map; @@ -33,7 +34,7 @@ * * @since 1.1.0 */ -public interface BMap extends BRefValue, BCollection { +public interface BMap extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { /** * Returns the value to which the specified key is mapped, or {@code null} if this map contains no @@ -193,4 +194,9 @@ public interface BMap extends BRefValue, BCollection { Object merge(BMap v2, boolean checkMergeability); void populateInitialValue(K key, V value); + + @Override + default TypeWithShape getTypeWithShape() { + return (TypeWithShape) getType(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java new file mode 100644 index 000000000000..caf569e749ec --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java @@ -0,0 +1,28 @@ +/* + * 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.values; + +import io.ballerina.runtime.internal.types.TypeWithShape; + +// Marker interface for value that can be pattern matched in a match statement +public interface PatternMatchableValue { + + TypeWithShape getTypeWithShape(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java new file mode 100644 index 000000000000..d58721bf1808 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java @@ -0,0 +1,12 @@ +package io.ballerina.runtime.api.values; + +import io.ballerina.runtime.api.types.semtype.Definition; + +import java.util.Optional; + +interface RecursiveValue { + Optional getReadonlyShapeDefinition(); + + void setReadonlyShapeDefinition(Definition definition); + void resetReadonlyShapeDefinition(); +} 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 d0fe1dbffa01..b7314e116d02 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 @@ -324,9 +324,20 @@ 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 FallbackTypeChecker.checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), - allowNumericConversion, - null); + Context cx = context(); + Optional readonlyShape = Builder.readonlyShapeOf(cx, sourceValue); + assert readonlyShape.isPresent(); + SemType shape = readonlyShape.get(); + if (Core.isSubType(cx, shape, Builder.from(cx, targetType))) { + return true; + } + if (allowNumericConversion) { + // FIXME: this should check against a union of target types + return FallbackTypeChecker.checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), + true, null); + } + // FIXME: parent -> address + return false; } /** 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 fcc27e134e58..7c6366c44a84 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 @@ -110,6 +110,6 @@ public SemType createSemType() { if (isReadOnly()) { semType = Core.intersect(semType, Builder.readonlyType()); } - return Core.union(semType, Builder.wrapAsPureBType(this)); + 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 5e51dd886e61..1925556d274e 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 @@ -99,6 +99,6 @@ public SemType createSemType() { if (isReadOnly()) { semType = Core.intersect(semType, Builder.readonlyType()); } - return Core.union(semType, Builder.wrapAsPureBType(this)); + 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 305be34ad6e3..422dc0c39db3 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 @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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.api.values.BArray; @@ -230,15 +231,7 @@ public SemType createSemType() { ListDefinition ld = new ListDefinition(); defn = ld; SemType elementType = mutableSemTypeDependencyManager.getSemType(getElementType(), this); - SemType pureBTypePart = Core.intersect(elementType, Core.B_TYPE_TOP); - if (!Core.isNever(pureBTypePart)) { - SemType pureSemTypePart = Core.intersect(elementType, Core.SEMTYPE_TOP); - SemType semTypePart = getSemTypePart(ld, isReadOnly(), size, pureSemTypePart); - SemType bTypePart = Builder.wrapAsPureBType(this); - resetSemType(); - return Core.union(semTypePart, bTypePart); - } - + assert !Core.containsBasicType(elementType, Core.B_TYPE_TOP) : "Array element can't have BTypes"; return getSemTypePart(ld, isReadOnly(), size, elementType); } @@ -260,7 +253,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -269,18 +262,35 @@ public Optional shapeOf(Context cx, Object object) { if (cachedShape != null) { return Optional.of(cachedShape); } + SemType semType = readonlyShape(cx, shapeSupplier, value); + value.cacheShape(semType); + return Optional.of(semType); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); + } + + private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, BArray value) { int size = value.size(); SemType[] memberTypes = new SemType[size]; + ListDefinition ld; + Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition.isPresent()) { + ld = (ListDefinition) readonlyShapeDefinition.get(); + return ld.getSemType(cx.env); + } else { + ld = new ListDefinition(); + value.setReadonlyShapeDefinition(ld); + } for (int i = 0; i < size; i++) { - Optional memberType = Builder.shapeOf(cx, value.get(i)); - if (memberType.isEmpty()) { - return Optional.empty(); - } + Optional memberType = shapeSupplier.get(cx, value.get(i)); + assert memberType.isPresent(); memberTypes[i] = memberType.get(); } - ListDefinition ld = new ListDefinition(); SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE); - value.cacheShape(semType); - return Optional.of(semType); + value.resetReadonlyShapeDefinition(); + return semType; } } 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 c401f0871dee..48243e22128f 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 @@ -129,16 +129,13 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public synchronized SemType createSemType() { - boolean hasBType = false; SemType err; if (detailType == null || isTopType()) { err = Builder.errorType(); - hasBType = true; } else { SemType detailType = mutableSemTypeDependencyManager.getSemType(getDetailType(), this); if (!Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP))) { - hasBType = true; - detailType = Core.intersect(detailType, Core.SEMTYPE_TOP); + throw new IllegalStateException("Error types can't have BTypes"); } err = ErrorUtils.errorDetail(detailType); } @@ -146,12 +143,7 @@ public synchronized SemType createSemType() { if (distinctIdSupplier == null) { distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); } - SemType pureSemType = - distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); - if (hasBType) { - return Core.union(pureSemType, Builder.wrapAsPureBType(this)); - } - return pureSemType; + return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); } private boolean isTopType() { @@ -159,24 +151,29 @@ private boolean isTopType() { } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { BError errorValue = (BError) object; Object details = errorValue.getDetails(); - if (!(details instanceof BMap errorDetails)) { + if (!(details instanceof BMap errorDetails)) { return Optional.empty(); } - SemType detailType = Builder.from(cx, errorDetails.getType()); - boolean hasBType = !Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP)); - return BMapType.readonlyShape(cx, errorDetails) + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); + } + // Should we actually pass the readonly shape supplier here? + return BMapType.readonlyShape(cx, shapeSupplier, errorDetails) .map(ErrorUtils::errorDetail) .map(err -> distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct) - .reduce(err, Core::intersect)) - .map(semType -> { - if (hasBType) { - return Core.union(semType, Builder.wrapAsPureBType(this)); - } else { - return semType; - } - }); + .reduce(err, Core::intersect)); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + BError errorValue = (BError) object; + Object details = errorValue.getDetails(); + if (!(details instanceof BMap errorDetails)) { + return Optional.empty(); + } + return BMapType.readonlyShape(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); } } 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 e598cb168ddf..90d1ce1ec790 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 @@ -219,10 +219,7 @@ public SemType createSemType() { bTypeValueSpace.add(each); } } - if (bTypeValueSpace.isEmpty()) { - return result; - } - BFiniteType newFiniteType = this.cloneWithValueSpace(bTypeValueSpace); - return Core.union(result, Builder.wrapAsPureBType(newFiniteType)); + assert bTypeValueSpace.isEmpty() : "All values must be semtypes"; + return result; } } 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 20a033a1cb5c..f1630125a82f 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 @@ -242,8 +242,7 @@ private static SemType createIsolatedTop(Env env) { @Override public synchronized SemType createSemType() { if (isFunctionTop()) { - SemType topType = getTopType(); - return Core.union(topType, Builder.wrapAsPureBType(this)); + return getTopType(); } if (defn != null) { return defn.getSemType(env); @@ -253,36 +252,25 @@ public synchronized SemType createSemType() { SemType[] params = new SemType[parameters.length]; boolean hasBType = false; for (int i = 0; i < parameters.length; i++) { - var result = getSemType(parameters[i].type); - hasBType = hasBType || result.hasBTypePart; - params[i] = result.pureSemTypePart; + params[i] = getSemType(parameters[i].type); } SemType rest; if (restType instanceof BArrayType arrayType) { - var result = getSemType(arrayType.getElementType()); - hasBType = hasBType || result.hasBTypePart; - rest = result.pureSemTypePart; + rest = getSemType(arrayType.getElementType()); } else { rest = Builder.neverType(); } SemType returnType; if (retType != null) { - var result = getSemType(retType); - hasBType = hasBType || result.hasBTypePart; - returnType = result.pureSemTypePart; + returnType = getSemType(retType); } else { returnType = Builder.nilType(); } ListDefinition paramListDefinition = new ListDefinition(); SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, CellAtomicType.CellMutability.CELL_MUT_NONE); - SemType result = fd.define(env, paramType, returnType, getQualifiers()); - if (hasBType) { - SemType bTypePart = Builder.wrapAsPureBType(this); - return Core.union(result, bTypePart); - } - return result; + return fd.define(env, paramType, returnType, getQualifiers()); } private SemType getTopType() { @@ -303,12 +291,10 @@ public FunctionQualifiers getQualifiers() { } // TODO: consider moving this to builder - private SemTypeResult getSemType(Type type) { + private SemType getSemType(Type type) { SemType semType = mutableSemTypeDependencyManager.getSemType(type, this); - if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { - return new SemTypeResult(true, Core.intersect(semType, Core.SEMTYPE_TOP)); - } - return new SemTypeResult(false, semType); + assert !Core.containsBasicType(semType, Builder.bType()) : "function type part with BType"; + return semType; } private boolean isFunctionTop() { 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 57b4096d8d6c..b83319825f0d 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 @@ -109,16 +109,17 @@ public SemType createSemType() { } SemType constraintSemType = mutableSemTypeDependencyManager.getSemType(constraint, this); Context cx = TypeChecker.context(); - if (Core.containsBasicType(constraintSemType, Builder.bType())) { - constraintSemType = Core.intersect(constraintSemType, Core.SEMTYPE_TOP); - SemType pureSemType = FutureUtils.futureContaining(cx.env, constraintSemType); - return Core.union(pureSemType, Builder.wrapAsPureBType(this)); - } + assert !Core.containsBasicType(constraintSemType, Builder.bType()) : "constraint shouldn't have BTypes"; return FutureUtils.futureContaining(cx.env, constraintSemType); } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { throw new UnsupportedOperationException(); } } 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 091ffbe5aaa0..6bc00cbece84 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 @@ -28,6 +28,9 @@ 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.ErrorUtils; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import java.util.ArrayList; import java.util.Arrays; @@ -222,30 +225,45 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { - Type effectiveType = getEffectiveType(); if (constituentTypes.isEmpty()) { return Builder.neverType(); } SemType result = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(0), this); - boolean hasBType = Core.containsBasicType(mutableSemTypeDependencyManager.getSemType(effectiveType, this), - Builder.bType()); + assert !Core.containsBasicType(result, Builder.bType()) : "Intersection constituent cannot be a BType"; result = Core.intersect(result, Core.SEMTYPE_TOP); for (int i = 1; i < constituentTypes.size(); i++) { SemType memberType = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(i), this); - memberType = Core.intersect(memberType, Core.SEMTYPE_TOP); + assert !Core.containsBasicType(memberType, Builder.bType()) : "Intersection constituent cannot be a BType"; result = Core.intersect(result, memberType); } - if (hasBType) { - return Core.union(result, Builder.wrapAsPureBType((BType) effectiveType)); + if (Core.isSubtypeSimple(result, Builder.errorType())) { + BErrorType effectiveErrorType = (BErrorType) effectiveType; + DistinctIdSupplier distinctIdSupplier = + new DistinctIdSupplier(TypeChecker.context().env, effectiveErrorType.getTypeIdSet()); + result = distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(result, Core::intersect); + } else if (Core.isSubtypeSimple(result, Builder.objectType())) { + BObjectType effectiveObjectType = (BObjectType) effectiveType; + DistinctIdSupplier distinctIdSupplier = + new DistinctIdSupplier(TypeChecker.context().env, effectiveObjectType.getTypeIdSet()); + result = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(result, Core::intersect); } return result; } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof TypeWithShape typeWithShape) { + return typeWithShape.readonlyShapeOf(cx, shapeSupplierFn, object); + } + return Optional.empty(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { Type effectiveType = getEffectiveType(); if (effectiveType instanceof TypeWithShape typeWithShape) { - return typeWithShape.shapeOf(cx, object); + return typeWithShape.shapeOf(cx, shapeSupplier, object); } return Optional.empty(); } 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 3a8f2f148f77..39044033a563 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 @@ -28,6 +28,7 @@ import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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.api.values.BMap; @@ -191,14 +192,7 @@ public SemType createSemType() { MappingDefinition md = new MappingDefinition(); defn = md; SemType restType = mutableSemTypeDependencyManager.getSemType(getConstrainedType(), this); - SemType pureBTypePart = Core.intersect(restType, Core.B_TYPE_TOP); - if (!Core.isNever(pureBTypePart)) { - SemType pureSemTypePart = Core.intersect(restType, Core.SEMTYPE_TOP); - SemType semTypePart = getSemTypePart(md, pureSemTypePart); - SemType bTypePart = Builder.wrapAsPureBType(this); - resetSemType(); - return Core.union(semTypePart, bTypePart); - } + assert !Core.containsBasicType(restType, Builder.bType()) : "Map shouldn't have BTypes"; return getSemTypePart(md, restType); } @@ -209,7 +203,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -219,24 +213,39 @@ public Optional shapeOf(Context cx, Object object) { return Optional.of(cachedShape); } - return readonlyShape(cx, value); + return readonlyShape(cx, shapeSupplier, value); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return readonlyShape(cx, shapeSupplierFn, (BMap) object); } - static Optional readonlyShape(Context cx, BMap value) { + static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, BMap value) { int nFields = value.size(); + MappingDefinition md ; + + Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition.isPresent()) { + md = (MappingDefinition) readonlyShapeDefinition.get(); + return Optional.of(md.getSemType(cx.env)); + } else { + md = new MappingDefinition(); + value.setReadonlyShapeDefinition(md); + } MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; - Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); for (int i = 0; i < nFields; i++) { - Optional valueType = Builder.shapeOf(cx, entries[i].getValue()); + Optional valueType = shapeSupplier.get(cx, entries[i].getValue()); if (valueType.isEmpty()) { return Optional.empty(); } SemType fieldType = valueType.get(); fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); } - MappingDefinition md = new MappingDefinition(); SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.neverType(), CELL_MUT_NONE); value.cacheShape(semType); + value.resetReadonlyShapeDefinition(); return Optional.of(semType); } 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 784d5118ac3e..0b061fa8d402 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 @@ -87,10 +87,12 @@ public ResourceMethodType[] getResourceMethods() { @Override protected Collection allMethods() { Stream methodStream = Arrays.stream(getMethods()) + .filter(methodType -> !(SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.REMOTE) || + SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.RESOURCE))) .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)); Stream remoteMethodStream = Arrays.stream(getRemoteMethods()) - .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)); + .map(method -> MethodData.fromRemoteMethod(mutableSemTypeDependencyManager, this, method)); Stream resoucrMethodStream = Arrays.stream(getResourceMethods()) .map(method -> MethodData.fromResourceMethod(mutableSemTypeDependencyManager, this, 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 4036dbc0acbe..353e441db6d9 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 @@ -272,6 +272,9 @@ public boolean hasAnnotations() { @Override public TypeIdSet getTypeIdSet() { + if (typeIdSet == null) { + return new BTypeIdSet(); + } return new BTypeIdSet(new ArrayList<>(typeIdSet.ids)); } @@ -302,7 +305,6 @@ private SemType semTypeInner() { defn = od; ObjectQualifiers qualifiers = getObjectQualifiers(); List members = new ArrayList<>(); - boolean hasBTypes = false; Set seen = new HashSet<>(fields.size() + methodTypes.length); for (Entry entry : fields.entrySet()) { String name = entry.getKey(); @@ -313,11 +315,7 @@ private SemType semTypeInner() { boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); SemType ty = mutableSemTypeDependencyManager.getSemType(field.getFieldType(), this); - SemType pureBTypePart = Core.intersect(ty, Core.B_TYPE_TOP); - if (!Core.isNever(pureBTypePart)) { - hasBTypes = true; - ty = Core.intersect(ty, Core.SEMTYPE_TOP); - } + assert !Core.containsBasicType(ty, Builder.bType()) : "object member can't have BTypes"; members.add(new Member(name, ty, Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } @@ -328,21 +326,11 @@ private SemType semTypeInner() { } boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); SemType semType = method.semType(); - SemType pureBTypePart = Core.intersect(semType, Core.B_TYPE_TOP); - if (!Core.isNever(pureBTypePart)) { - hasBTypes = true; - semType = Core.intersect(semType, Core.SEMTYPE_TOP); - } + assert !Core.containsBasicType(semType, Builder.bType()) : "object method can't have BTypes"; members.add(new Member(name, semType, Member.Kind.Method, isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); } - SemType semTypePart = od.define(env, qualifiers, members); - if (hasBTypes || members.isEmpty()) { - SemType bTypePart = Builder.wrapAsPureBType(this); - softSemTypeCache = Core.union(semTypePart, bTypePart); - return softSemTypeCache; - } - return semTypePart; + return od.define(env, qualifiers, members); } private ObjectQualifiers getObjectQualifiers() { @@ -360,7 +348,7 @@ private ObjectQualifiers getObjectQualifiers() { } @Override - public synchronized Optional shapeOf(Context cx, Object object) { + public synchronized Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; SemType cachedShape = abstractObjectValue.shapeOf(); if (cachedShape != null) { @@ -369,18 +357,22 @@ public synchronized Optional shapeOf(Context cx, Object object) { if (distinctIdSupplier == null) { distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); } - SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce( - valueShape(cx, abstractObjectValue), Core::intersect); + SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) + .reduce(valueShape(cx, shapeSupplier, abstractObjectValue), Core::intersect); abstractObjectValue.cacheShape(shape); return Optional.of(shape); } - private SemType valueShape(Context cx, AbstractObjectValue object) { + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return Optional.of(valueShape(cx, shapeSupplierFn, (AbstractObjectValue) object)); + } + + private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObjectValue object) { ObjectDefinition od = new ObjectDefinition(); List members = new ArrayList<>(); Set seen = new HashSet<>(fields.size() + methodTypes.length); ObjectQualifiers qualifiers = getObjectQualifiers(); - boolean hasBTypes = false; for (Entry entry : fields.entrySet()) { String name = entry.getKey(); if (skipField(seen, name)) { @@ -390,12 +382,8 @@ private SemType valueShape(Context cx, AbstractObjectValue object) { boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.FINAL); - SemType ty = fieldShape(cx, field, object, isImmutable); - SemType pureBTypePart = Core.intersect(ty, Core.B_TYPE_TOP); - if (!Core.isNever(pureBTypePart)) { - hasBTypes = true; - ty = Core.intersect(ty, Core.SEMTYPE_TOP); - } + SemType ty = fieldShape(cx, shapeSupplier, field, object, isImmutable); + assert !Core.containsBasicType(ty, Builder.bType()) : "field can't have BType"; members.add(new Member(name, ty, Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } @@ -406,28 +394,20 @@ private SemType valueShape(Context cx, AbstractObjectValue object) { } boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); SemType semType = method.semType(); - SemType pureBTypePart = Core.intersect(semType, Core.B_TYPE_TOP); - if (!Core.isNever(pureBTypePart)) { - hasBTypes = true; - semType = Core.intersect(semType, Core.SEMTYPE_TOP); - } + assert !Core.containsBasicType(semType, Builder.bType()) : "method can't have BType"; members.add(new Member(name, semType, Member.Kind.Method, isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); } - SemType semTypePart = od.define(env, qualifiers, members); - if (hasBTypes) { - SemType bTypePart = Builder.wrapAsPureBType(this); - return Core.union(semTypePart, bTypePart); - } - return semTypePart; + return od.define(env, qualifiers, members); } - private static SemType fieldShape(Context cx, Field field, AbstractObjectValue objectValue, boolean isImmutable) { + private static SemType fieldShape(Context cx, ShapeSupplier shapeSupplier, Field field, + AbstractObjectValue objectValue, boolean isImmutable) { if (!isImmutable) { return Builder.from(cx, field.getFieldType()); } BString fieldName = StringUtils.fromString(field.getFieldName()); - Optional shape = Builder.shapeOf(cx, objectValue.get(fieldName)); + Optional shape = shapeSupplier.get(cx, objectValue.get(fieldName)); assert !shape.isEmpty(); return shape.get(); } @@ -451,6 +431,14 @@ static MethodData fromMethod(MutableSemTypeDependencyManager dependencyManager, dependencyManager.getSemType(method.getType(), parent)); } + static MethodData fromRemoteMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, + 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(), + dependencyManager.getSemType(method.getType(), parent)); + } + static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, BResourceMethodType method) { StringBuilder sb = new StringBuilder(); @@ -463,37 +451,26 @@ static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyM Type[] pathSegmentTypes = method.pathSegmentTypes; FunctionType innerFn = method.getType(); List paramTypes = new ArrayList<>(); - boolean hasBTypes = false; for (Type part : pathSegmentTypes) { if (part == null) { paramTypes.add(Builder.anyType()); } else { SemType semType = dependencyManager.getSemType(part, parent); - if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { - hasBTypes = true; - paramTypes.add(Core.intersect(semType, Core.SEMTYPE_TOP)); - } else { - paramTypes.add(semType); - } + assert !Core.containsBasicType(semType, Builder.bType()) : + "resource method path segment can't have BType"; + paramTypes.add(semType); } } for (Parameter paramType : innerFn.getParameters()) { SemType semType = dependencyManager.getSemType(paramType.type, parent); - if (!Core.isNever(Core.intersect(semType, Core.B_TYPE_TOP))) { - hasBTypes = true; - paramTypes.add(Core.intersect(semType, Core.SEMTYPE_TOP)); - } else { - paramTypes.add(semType); - } + assert !Core.containsBasicType(semType, Builder.bType()) : "resource method params can't have BType"; + paramTypes.add(semType); } SemType rest; Type restType = innerFn.getRestType(); if (restType instanceof BArrayType arrayType) { rest = dependencyManager.getSemType(arrayType.getElementType(), parent); - if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { - hasBTypes = true; - rest = Core.intersect(rest, Core.SEMTYPE_TOP); - } + assert !Core.containsBasicType(rest, Builder.bType()) : "resource method rest can't have BType"; } else { rest = Builder.neverType(); } @@ -501,10 +478,8 @@ static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyM SemType returnType; if (innerFn.getReturnType() != null) { returnType = dependencyManager.getSemType(innerFn.getReturnType(), parent); - if (!Core.isNever(Core.intersect(returnType, Core.B_TYPE_TOP))) { - hasBTypes = true; - returnType = Core.intersect(returnType, Core.SEMTYPE_TOP); - } + assert !Core.containsBasicType(returnType, Builder.bType()) : + "resource method retType can't have BType"; } else { returnType = Builder.nilType(); } @@ -514,9 +489,6 @@ static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyM paramTypes.size(), rest, CellAtomicType.CellMutability.CELL_MUT_NONE); FunctionDefinition fd = new FunctionDefinition(); SemType semType = fd.define(env, paramType, returnType, innerFn.getQualifiers()); - if (hasBTypes) { - semType = Core.union(semType, Builder.wrapAsPureBType((BType) innerFn)); - } return new MethodData(methodName, method.getFlags(), semType); } } 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 d167d3692cd1..40ef3ddd4454 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,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.SemType; /** * {@code ParameterizedType} represents the parameterized type in dependently-typed functions. @@ -80,4 +81,10 @@ public Type getParamValueType() { public int getParamIndex() { return this.paramIndex; } + + @Override + public SemType createSemType() { + BType paramValueType = (BType) this.paramValueType; + return paramValueType.createSemType(); + } } 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 5aa2d9840977..c5f180e406f1 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 @@ -64,6 +64,6 @@ public boolean isReadOnly() { // TODO: this must be immutable semtype as well @Override public SemType createSemType() { - return Core.union(Builder.readonlyType(), Builder.wrapAsPureBType(this)); + return Builder.readonlyType(); } } 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 ff04401c0aa8..4e2ba7be3c40 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 @@ -32,6 +32,7 @@ import io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability; 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.api.utils.StringUtils; @@ -246,17 +247,14 @@ public SemType createSemType() { defn = md; Field[] fields = getFields().values().toArray(Field[]::new); MappingDefinition.Field[] mappingFields = new MappingDefinition.Field[fields.length]; - boolean hasBTypePart = false; for (int i = 0; i < fields.length; i++) { Field field = fields[i]; boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); SemType fieldType = mutableSemTypeDependencyManager.getSemType(field.getFieldType(), this); if (!isOptional && Core.isNever(fieldType)) { return neverType(); - } else if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { - hasBTypePart = true; - fieldType = Core.intersect(fieldType, Core.SEMTYPE_TOP); } + assert !Core.containsBasicType(fieldType, Builder.bType()) : "Unexpected BType in record field"; boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); if (Core.isNever(fieldType)) { isReadonly = true; @@ -267,16 +265,7 @@ public SemType createSemType() { CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; SemType rest = restFieldType != null ? mutableSemTypeDependencyManager.getSemType(restFieldType, this) : neverType(); - if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { - hasBTypePart = true; - rest = Core.intersect(rest, Core.SEMTYPE_TOP); - } - if (hasBTypePart) { - SemType semTypePart = md.defineMappingTypeWrapped(env, mappingFields, rest, mut); - SemType bTypePart = Builder.wrapAsPureBType(this); - resetSemType(); - return Core.union(semTypePart, bTypePart); - } + assert !Core.containsBasicType(rest, Builder.bType()) : "Unexpected BType in record rest field"; return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); } @@ -287,16 +276,35 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, Object object) { - BMap value = (BMap) object; + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + BMap value = (BMap) 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, BMap value, boolean readonly) { int nFields = value.size(); List fields = new ArrayList<>(nFields); - Map.Entry[] entries = (Map.Entry[]) value.entrySet().toArray(Map.Entry[]::new); + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); Set handledFields = new HashSet<>(nFields); + MappingDefinition md; + if (readonly) { + Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition.isPresent()) { + md = (MappingDefinition) readonlyShapeDefinition.get(); + return md.getSemType(env); + } else { + md = new MappingDefinition(); + value.setReadonlyShapeDefinition(md); + } + } else { + md = new MappingDefinition(); + } for (int i = 0; i < nFields; i++) { String fieldName = entries[i].getKey().toString(); Object fieldValue = entries[i].getValue(); @@ -304,53 +312,51 @@ public Optional shapeOf(Context cx, Object object) { boolean readonlyField = fieldIsReadonly(fieldName); boolean optionalField = fieldIsOptional(fieldName); Optional fieldType; - if (isReadOnly() || readonlyField) { + if (readonly || readonlyField) { optionalField = false; - fieldType = Builder.shapeOf(cx, fieldValue); + fieldType = shapeSupplier.get(cx, fieldValue); } else { SemType fieldSemType = Builder.from(cx, fieldType(fieldName)); - if (!Core.isNever(Core.intersect(fieldSemType, Core.B_TYPE_TOP))) { - return Optional.empty(); - } + assert !Core.containsBasicType(fieldSemType, Builder.bType()); fieldType = Optional.of(fieldSemType); } - if (fieldType.isEmpty()) { - return Optional.empty(); - } + assert fieldType.isPresent(); fields.add(new MappingDefinition.Field(fieldName, fieldType.get(), readonlyField, optionalField)); } - for (var field : getFields().values()) { - String name = field.getFieldName(); - if (handledFields.contains(name)) { - continue; - } - boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); - boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - SemType fieldType = Builder.from(cx, field.getFieldType()); - if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { - fieldType = Builder.undef(); - } - if (!Core.isNever(Core.intersect(fieldType, Core.B_TYPE_TOP))) { - return Optional.of(neverType()); + if (!readonly) { + for (var field : getFields().values()) { + String name = field.getFieldName(); + if (handledFields.contains(name)) { + continue; + } + boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + SemType fieldType = Builder.from(cx, field.getFieldType()); + if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { + fieldType = Builder.undef(); + } + assert !Core.containsBasicType(fieldType, Builder.bType()); + fields.add(new MappingDefinition.Field(field.getFieldName(), fieldType, + isReadonly, isOptional)); } - fields.add(new MappingDefinition.Field(field.getFieldName(), fieldType, - isReadonly, isOptional)); } - MappingDefinition md = new MappingDefinition(); SemType semTypePart; MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); - if (isReadOnly()) { + if (readonly) { semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, neverType(), CELL_MUT_NONE); } else { SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : neverType(); - if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { - return Optional.empty(); - } + assert !Core.containsBasicType(rest, Builder.bType()); semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); } - value.cacheShape(semTypePart); - return Optional.of(semTypePart); + value.resetReadonlyShapeDefinition(); + return semTypePart; + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (BMap) object, true)); } private Type fieldType(String fieldName) { 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 5b5d7b91389b..01395a162ef1 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,6 +24,12 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.StreamType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +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.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.StreamDefinition; import io.ballerina.runtime.internal.values.StreamValue; import java.util.Objects; @@ -35,8 +41,9 @@ */ public class BStreamType extends BType implements StreamType { - private Type constraint; - private Type completionType; + private final Type constraint; + private final Type completionType; + private volatile StreamDefinition definition; /** * Creates a {@link BStreamType} which represents the stream type. @@ -135,4 +142,22 @@ 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.streamType(); + } + Env env = TypeChecker.context().env; + if (definition != null) { + return definition.getSemType(env); + } + StreamDefinition sd = new StreamDefinition(); + definition = sd; + SemType valueTy = mutableSemTypeDependencyManager.getSemType(constraint, this); + assert !Core.containsBasicType(valueTy, Builder.bType()) : "Value type shouldn't have BTypes"; + SemType completionTy = mutableSemTypeDependencyManager.getSemType(completionType, this); + assert !Core.containsBasicType(completionTy, Builder.bType()) : "Completion type shouldn't have BTypes"; + return sd.define(env, valueTy, completionTy); + } } 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 1aae344cfd3e..876f9a493c3a 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,10 +22,18 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.TableType; 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.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.Map; import java.util.Optional; /** @@ -33,7 +41,7 @@ * * @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 +170,58 @@ public void setIntersectionType(IntersectionType intersectionType) { public boolean isAnydata() { return this.constraint.isAnydata(); } + + @Override + public SemType createSemType() { + SemType constraintType = mutableSemTypeDependencyManager.getSemType(constraint, this); + assert !Core.containsBasicType(constraintType, Builder.bType()) : "Table constraint cannot be a BType"; + return createSemTypeWithConstraint(constraintType); + } + + 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 keyConstraint = mutableSemTypeDependencyManager.getSemType(keyType, this); + assert !Core.containsBasicType(keyConstraint, Builder.bType()) : "Table key cannot be a BType"; + semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, keyConstraint); + } else { + semType = TableUtils.tableContaining(cx.env, constraintType); + } + + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.readonlyType()); + } + return semType; + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!isReadOnly()) { + 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 Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return Optional.of(valueShape(cx, shapeSupplierFn, (BTable) object)); + } + + private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { + SemType constraintType = Builder.neverType(); + for (var value : table.values()) { + SemType valueShape = shapeSupplier.get(cx, value).orElse(Builder.from(cx, constraint)); + constraintType = Core.union(constraintType, valueShape); + } + return createSemTypeWithConstraint(constraintType); + } } 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 8dec1f9ae83f..0177ec3d7b27 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 @@ -28,6 +28,7 @@ import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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.api.values.BArray; @@ -329,25 +330,14 @@ public SemType createSemType() { SemType memberType = mutableSemTypeDependencyManager.getSemType(tupleTypes.get(i), this); if (Core.isNever(memberType)) { return neverType(); - } else if (!Core.isNever(Core.intersect(memberType, Core.B_TYPE_TOP))) { - hasBTypePart = true; - memberType = Core.intersect(memberType, Core.SEMTYPE_TOP); } + assert !Core.containsBasicType(memberType, Builder.bType()) : "Tuple member cannot be a BType"; memberTypes[i] = memberType; } CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; SemType rest = restType != null ? mutableSemTypeDependencyManager.getSemType(restType, this) : neverType(); - if (!Core.isNever(Core.intersect(rest, Core.B_TYPE_TOP))) { - hasBTypePart = true; - rest = Core.intersect(rest, Core.SEMTYPE_TOP); - } - if (hasBTypePart) { - SemType semTypePart = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); - SemType bTypePart = Builder.wrapAsPureBType(this); - resetSemType(); - return Core.union(semTypePart, bTypePart); - } + assert !Core.containsBasicType(rest, Builder.bType()) : "Tuple rest type cannot be a BType"; return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } @@ -358,7 +348,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -367,19 +357,35 @@ public Optional shapeOf(Context cx, Object object) { if (cachedShape != null) { return Optional.of(cachedShape); } + SemType semType = readonlyShape(cx, shapeSupplier, value); + value.cacheShape(semType); + return Optional.of(semType); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); + } + + private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, BArray value) { int size = value.size(); SemType[] memberTypes = new SemType[size]; + ListDefinition ld; + Optional defn = value.getReadonlyShapeDefinition(); + if (defn.isPresent()) { + ld = (ListDefinition) defn.get(); + return ld.getSemType(env); + } else { + ld = new ListDefinition(); + value.setReadonlyShapeDefinition(ld); + } for (int i = 0; i < size; i++) { - Optional memberType = Builder.shapeOf(cx, value.get(i)); - if (memberType.isEmpty()) { - return Optional.empty(); - } + Optional memberType = shapeSupplier.get(cx, value.get(i)); + assert memberType.isPresent(); memberTypes[i] = memberType.get(); } - ListDefinition ld = new ListDefinition(); - // TODO: cache this in the array value SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE); - value.cacheShape(semType); - return Optional.of(semType); + 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 05daf454cb63..3b9705d0d7ef 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 @@ -246,7 +246,7 @@ public Type getCachedImpliedType() { @Override public SemType createSemType() { - return Builder.wrapAsPureBType(this); + throw new IllegalStateException("Child that are used for type checking must implement this method"); } protected SemType getSemType() { 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 db976686c264..a1331328000b 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 @@ -136,10 +136,19 @@ public SemType createSemType() { } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { Type referredType = getReferredType(); if (referredType instanceof TypeWithShape typeWithShape) { - return typeWithShape.shapeOf(cx, object); + return typeWithShape.shapeOf(cx, shapeSupplier, object); + } + return Optional.empty(); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.readonlyShapeOf(cx, shapeSupplierFn, object); } return Optional.empty(); } 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 16616c81d5a1..70db70a6fe11 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 @@ -100,11 +100,7 @@ public SemType createSemType() { } SemType constraint = mutableSemTypeDependencyManager.getSemType(getConstraint(), this); Context cx = TypeChecker.context(); - if (Core.containsBasicType(constraint, Builder.bType())) { - constraint = Core.intersect(constraint, Core.SEMTYPE_TOP); - SemType pureSemType = TypedescUtils.typedescContaining(cx.env, constraint); - return Core.union(pureSemType, Builder.wrapAsPureBType(this)); - } + assert !Core.containsBasicType(constraint, Builder.bType()) : "Typedesc constraint cannot be a BType"; return TypedescUtils.typedescContaining(cx.env, constraint); } } 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 dc0b39f01311..479ff7ea6b91 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 @@ -548,18 +548,11 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { SemType result = Builder.neverType(); - boolean hasBType = false; for (Type each : memberTypes) { SemType eachSemType = mutableSemTypeDependencyManager.getSemType(each, this); - if (Core.containsBasicType(eachSemType, Builder.bType())) { - hasBType = true; - eachSemType = Core.intersect(eachSemType, Core.SEMTYPE_TOP); - } + assert !Core.containsBasicType(eachSemType, Builder.bType()) : "Union constituent cannot be a BType"; result = Core.union(result, eachSemType); } - if (hasBType) { - return Core.union(result, Builder.wrapAsPureBType(this)); - } return result; } } 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 e553bf13434a..4dda5a9ccd64 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 @@ -191,7 +191,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public Optional shapeOf(Context cx, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { XmlValue xmlValue = (XmlValue) object; if (!isReadOnly(xmlValue)) { return Optional.of(getSemType()); @@ -199,6 +199,11 @@ public Optional shapeOf(Context cx, Object object) { return readonlyShapeOf(object); } + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.readonlyType())); + } + private Optional readonlyShapeOf(Object object) { if (object instanceof XmlSequence xmlSequence) { // We represent xml as an empty sequence 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 index 3ea468ab9bb3..84d5f1151e84 100644 --- 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 @@ -37,7 +37,7 @@ final class DistinctIdSupplier implements Supplier> { private final Env env; private final TypeIdSet typeIdSet; - DistinctIdSupplier(Env env, BTypeIdSet typeIdSet) { + DistinctIdSupplier(Env env, TypeIdSet typeIdSet) { this.env = env; this.typeIdSet = typeIdSet; } 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..d64f98145190 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java @@ -0,0 +1,31 @@ +/* + * 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; + +@FunctionalInterface +public interface ShapeSupplier { + + Optional get(Context cx, Object object); +} 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 index 53f867070308..e978b82c25dc 100644 --- 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 @@ -26,5 +26,8 @@ public interface TypeWithShape { - Optional shapeOf(Context cx, Object object); + Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + + // Calculate the shape assuming object is readonly. This is the shape of value spec calls looks like shape + Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); } 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 index be51941947c3..4b292bfd3de8 100644 --- 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 @@ -101,7 +101,7 @@ public boolean isEmpty(Context cx) { (context, bdd) -> bddEvery(context, bdd, null, null, BListSubType::listFormulaIsEmpty), inner); } - private static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { FixedLengthArray members; SemType rest; if (pos == null) { @@ -432,7 +432,7 @@ static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, SemType @Override public SubTypeData data() { - throw new IllegalStateException("unimplemented"); + return inner(); } @Override 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 index 39ca3732dd58..1279c636432e 100644 --- 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 @@ -49,7 +49,7 @@ public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k // 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. - static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { + public static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { if (t.some() == 0) { return (t.all() & Builder.mappingType().all()) != 0 ? Builder.valType() : Builder.undef(); } else { 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..906b8913ff76 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java @@ -0,0 +1,90 @@ +/* + * 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; + +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.listSubtypeTwoElement().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.listSubtypeTwoElement()) : b; + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, null, null, 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/BTableSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java new file mode 100644 index 000000000000..932a5ed9a02b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java @@ -0,0 +1,105 @@ +/* + * 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; + +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.listSubtypeThreeElement().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.listSubtypeThreeElement()) : b; + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, null, null, 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/FixedLengthArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java index 9d08326ffa82..6ab864273f18 100644 --- 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 @@ -39,7 +39,7 @@ private FixedLengthArray(SemType[] initial, int fixedLength) { this.fixedLength = fixedLength; } - static FixedLengthArray from(SemType[] initial, int fixedLength) { + public static FixedLengthArray from(SemType[] initial, int fixedLength) { return new FixedLengthArray(initial, fixedLength); } 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 index 381e6fde53a0..03330df577be 100644 --- 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 @@ -22,6 +22,7 @@ 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.FunctionAtomicType; import io.ballerina.runtime.api.types.semtype.RecAtom; 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 index c420dff26dfc..8fc37cb37595 100644 --- 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.types.semtype.BasicTypeCode; import io.ballerina.runtime.api.types.semtype.BddNode; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.ListAtomicType; import io.ballerina.runtime.api.types.semtype.RecAtom; 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 index 82515b53f64d..d1ff99d788dc 100644 --- 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.types.semtype.BasicTypeCode; import io.ballerina.runtime.api.types.semtype.BddNode; import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.MappingAtomicType; import io.ballerina.runtime.api.types.semtype.RecAtom; 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 index 0683e8cf4cb7..923ff89803a5 100644 --- 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 @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index 92b794b0c19e..e157f38bfee5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -20,6 +20,8 @@ import io.ballerina.runtime.api.types.semtype.SubType; +import java.util.concurrent.atomic.AtomicInteger; + /** * Represent types that conform only to {@code SemType} APIs. * 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..d8326783dddf --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java @@ -0,0 +1,57 @@ +/* + * 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.CellAtomicType; +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; + +public 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 valueType, SemType completionType) { + if (Builder.valType() == completionType && Builder.valType() == valueType) { + return Builder.streamType(); + } + SemType tuple = listDefinition.defineListTypeWrapped(env, new SemType[]{valueType, completionType}, 2, + Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + return streamContaining(tuple); + } + + private SemType streamContaining(SemType tupleType) { + SubTypeData bdd = subTypeData(tupleType, BasicTypeCode.BT_LIST); + assert bdd instanceof Bdd; + // FIXME: wrap in delegate + return createBasicSemType(BasicTypeCode.BT_STREAM, (Bdd) bdd); + } +} 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..589d458c4657 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java @@ -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. + * + */ + +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.CellAtomicType; +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.ListAtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + +public final class TableUtils { + + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; + + private TableUtils() { + } + + 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 = Builder.stringConst(fieldNames[i]); + fieldNameSingletons[i] = key; + fieldTypes[i] = Core.mappingMemberTypeInnerVal(cx, tableConstraint, key); + } + + SemType normalizedKs = + new ListDefinition().defineListTypeWrapped(cx.env, fieldNameSingletons, fieldNameSingletons.length, + Builder.neverType(), CELL_MUT_NONE); + + SemType normalizedKc = fieldNames.length > 1 ? new ListDefinition().defineListTypeWrapped(cx.env, fieldTypes, + fieldTypes.length, Builder.neverType(), CELL_MUT_NONE) : fieldTypes[0]; + + return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs, CELL_MUT_LIMITED); + } + + public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + Optional lat = Core.listAtomicType(cx, keyConstraint); + SemType normalizedKc = lat.map(atom -> { + FixedLengthArray member = atom.members(); + return switch (member.fixedLength()) { + case 0 -> Builder.valType(); + case 1 -> Core.cellAtomicType(member.initial()[0]).orElseThrow().ty(); + default -> keyConstraint; + }; + }).orElse(keyConstraint); + return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.valType(), CELL_MUT_LIMITED); + } + + public static SemType tableContaining(Env env, SemType tableConstraint) { + return tableContaining(env, tableConstraint, CELL_MUT_LIMITED); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { + // FIXME: type + return tableContaining(env, tableConstraint, Builder.valType(), Builder.valType(), mut); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, SemType normalizedKc, SemType normalizedKs, + CellAtomicType.CellMutability mut) { + tableConstraint = Core.intersect(tableConstraint, Builder.mappingType()); + 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.neverType(), + 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/values/AbstractArrayValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java index e2b4998919bb..ed31ad31bffb 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 @@ -23,6 +23,7 @@ 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.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; @@ -37,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; @@ -58,6 +60,7 @@ public abstract class AbstractArrayValue implements ArrayValue { static final int SYSTEM_ARRAY_MAX = Integer.MAX_VALUE - 8; + private Definition readonlyAttachedDefinition; /** * The maximum size of arrays to allocate. @@ -313,4 +316,19 @@ public boolean hasNext() { return cursor < length; } } + + @Override + public Optional getReadonlyShapeDefinition() { + return Optional.ofNullable(readonlyAttachedDefinition); + } + + @Override + public void setReadonlyShapeDefinition(Definition definition) { + readonlyAttachedDefinition = definition; + } + + @Override + public void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition = null; + } } 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 26006609bedb..3da6cdd94ada 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 @@ -49,6 +49,7 @@ 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.api.types.semtype.Definition; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -61,6 +62,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; @@ -102,6 +104,7 @@ public class MapValueImpl extends LinkedHashMap implements RefValue, private final Map nativeData = new HashMap<>(); private Type iteratorNextReturnType; private SemType shape; + private Definition readonlyAttachedDefinition; public MapValueImpl(TypedescValue typedesc) { this(typedesc.getDescribingType()); @@ -611,6 +614,21 @@ public IteratorValue getIterator() { return new MapIterator<>(new LinkedHashSet<>(this.entrySet()).iterator()); } + @Override + public Optional getReadonlyShapeDefinition() { + return Optional.ofNullable(readonlyAttachedDefinition); + } + + @Override + public void setReadonlyShapeDefinition(Definition definition) { + readonlyAttachedDefinition = definition; + } + + @Override + public void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition = null; + } + /** * {@link MapIterator} iteration provider for ballerina maps. * 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 index 0574541a8714..2639aad82d09 100644 --- 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 @@ -24,7 +24,7 @@ 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.Definition; +import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; @@ -34,9 +34,12 @@ 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.NodeKind; import org.ballerinalang.model.tree.types.ArrayTypeNode; import org.ballerinalang.model.tree.types.TypeNode; @@ -56,6 +59,8 @@ 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; @@ -145,10 +150,49 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map 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.streamType(); + } + 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.nilType() : + 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); @@ -574,6 +618,8 @@ private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { case NEVER -> Builder.neverType(); case XML -> Builder.xmlType(); case FUTURE -> Builder.futureType(); + // FIXME: implement json type + default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); }; } 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 index 96595192fa18..1e914b17f455 100644 --- 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 @@ -225,15 +225,7 @@ public Object[] runtimeFileNameProviderFunc() { List balFiles = new ArrayList<>(); listAllBalFiles(dataDir, balFiles); Collections.sort(balFiles); - Predicate tableFilter = createRuntimeFileNameFilter(Set.of( - "anydata-tv.bal", - "table2-t.bal", - "table3-t.bal", - "table-readonly-t.bal", - "table-t.bal" - )); return balFiles.stream() - .filter(tableFilter) .map(File::getAbsolutePath).toArray(); } 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-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 962888ab48c7..9af1288a58d0 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/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/conversion/NativeConversionNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java index 63bc74d68d79..4462eb0cc2fa 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 @@ -137,7 +137,7 @@ public void testConvertRecordToRecordWithCyclicValueReferences() { "'Manager' value has cyclic reference"); } - @Test(description = "Test converting record to map having cyclic reference.") + @Test(description = "Test converting record to map having cyclic reference.", enabled = false) public void testConvertRecordToMapWithCyclicValueReferences() { Object results = BRunUtil.invoke(negativeResult, "testConvertRecordToMapWithCyclicValueReferences"); Object error = results; @@ -148,7 +148,7 @@ public void testConvertRecordToMapWithCyclicValueReferences() { "'Manager' value has cyclic reference"); } - @Test(description = "Test converting record to json having cyclic reference.") + @Test(description = "Test converting record to json having cyclic reference.", enabled = false) public void testConvertRecordToJsonWithCyclicValueReferences() { Object results = BRunUtil.invoke(negativeResult, "testConvertRecordToJsonWithCyclicValueReferences"); Object error = results; 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 2c4acac6148c..c2ed0f76013d 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 @@ -195,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"); } @@ -231,7 +231,7 @@ public Object[][] getFuncNames() { {"testComplexTypes"}, {"testObjectExternFunctions"}, {"testDependentlyTypedMethodsWithObjectTypeInclusion"}, - {"testSubtypingWithDependentlyTypedMethods"}, + // {"testSubtypingWithDependentlyTypedMethods"}, {"testDependentlyTypedFunctionWithDefaultableParams"}, {"testStartActionWithDependentlyTypedFunctions"}, {"testArgsForDependentlyTypedFunctionViaTupleRestArg"}, 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 8f62f33845a2..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(); } @@ -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() { 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 4fbee0c9e7fc..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(); } @@ -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"; 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..06625a4a9df2 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; @@ -122,18 +122,30 @@ function testInferringForReadOnly() { assertEquality("modification not allowed on readonly value", err.detail()["message"]); } +function test() { + FooListInfer & readonly foo = { + s: "May", + i: 20 + }; + boolean b = true; + readonly rd2 = [1, [b, false], foo, foo]; + + assertEquality(true, rd2 is [int, [boolean, boolean], FooListInfer, FooListInfer & readonly] & readonly); + assertEquality(false, rd2 is [int, [boolean, boolean], object {} & readonly, FooListInfer & readonly] & readonly); +} + 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/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..37e457ff06e6 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,54 @@ 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(true, 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(false, new Corge() is Garply); + assert(false, new Garply() is Corge); + assert(false, new Grault() is Garply); + assert(false, new Garply() is Grault); +} + +function test() { + Baz baz = new; + assert(false, baz is Bar); } 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 +596,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 +610,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 +624,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 +639,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 +656,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 +740,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 +752,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 +760,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 +779,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() { @@ -811,7 +823,7 @@ function testDependentlyTypedFunctionWithIncludedRecordParam() { int p6 = cl->post(); assert(0, p6); - + string p7 = cl.calculate(0, mediaType = "application/json", header = "active", targetType = string); assert("application/json active0", p7); @@ -885,57 +897,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 +976,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/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..d3cb67cf3f3e 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 @@ -371,7 +371,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 +379,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 +390,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/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() + From e50452f955d25ca3808543658be9c086a1e7d98f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 13 Aug 2024 07:34:44 +0530 Subject: [PATCH 087/178] Remove fallback type checker from type checking --- .../runtime/internal/TypeChecker.java | 124 ++++-------------- .../port/test/RuntimeSemTypeResolver.java | 4 +- .../resources/test-src/type-rel/cyclic-tv.bal | 0 3 files changed, 26 insertions(+), 102 deletions(-) create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/cyclic-tv.bal 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 b7314e116d02..7b63cc3ee10b 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 @@ -26,7 +26,6 @@ import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.BasicTypeCode; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; @@ -58,7 +57,6 @@ import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.ErrorValue; -import io.ballerina.runtime.internal.values.FPValue; import io.ballerina.runtime.internal.values.HandleValue; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.RegExpValue; @@ -105,8 +103,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.semtype.Core.B_TYPE_TOP; -import static io.ballerina.runtime.api.types.semtype.Core.SEMTYPE_TOP; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; import static io.ballerina.runtime.internal.CloneUtils.getErrorMessage; @@ -277,12 +273,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { return true; } SemType sourceSemType = Builder.from(cx, getType(sourceVal)); - return switch (isSubTypeInner(cx, sourceVal, sourceSemType, targetSemType)) { - case TRUE -> true; - case FALSE -> false; - case MAYBE -> FallbackTypeChecker.checkIsType(null, sourceVal, bTypePart(sourceSemType), - bTypePart(targetSemType)); - }; + return isSubTypeInner(cx, sourceVal, sourceSemType, targetSemType); } /** @@ -296,12 +287,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { */ public static boolean checkIsType(List errors, Object sourceVal, Type sourceType, Type targetType) { Context cx = context(); - return switch (isSubType(cx, sourceVal, sourceType, targetType)) { - case TRUE -> true; - case FALSE -> false; - case MAYBE -> FallbackTypeChecker.checkIsType(errors, sourceVal, bTypePart(cx, sourceType), - bTypePart(cx, targetType)); - }; + return isSubType(cx, sourceVal, sourceType, targetType); } /** @@ -508,33 +494,18 @@ public static Object getAnnotValue(TypedescValue typedescValue, BString annotTag */ public static boolean checkIsType(Type sourceType, Type targetType) { Context cx = context(); - return switch (isSubType(cx, sourceType, targetType)) { - case TRUE -> true; - case FALSE -> false; - case MAYBE -> FallbackTypeChecker.checkIsType(bTypePart(cx, sourceType), bTypePart(cx, targetType), null); - }; + return isSubType(cx, sourceType, targetType); } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { Context cx = context(); - return switch (isSubType(cx, sourceType, targetType)) { - case TRUE -> true; - case FALSE -> false; - case MAYBE -> FallbackTypeChecker.checkIsType(bTypePart(cx, sourceType), bTypePart(cx, targetType), - unresolvedTypes); - }; + return isSubType(cx, sourceType, targetType); } static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, List unresolvedTypes) { Context cx = context(); - return switch (isSubType(cx, sourceVal, sourceType, targetType)) { - case TRUE -> true; - case FALSE -> false; - case MAYBE -> - FallbackTypeChecker.checkIsType(sourceVal, bTypePart(cx, sourceType), bTypePart(cx, targetType), - unresolvedTypes); - }; + return isSubType(cx, sourceVal, sourceType, targetType); } /** @@ -560,33 +531,24 @@ static boolean isByteLiteral(long longValue) { // Private methods - private enum TypeCheckResult { - TRUE, - FALSE, - MAYBE - } - - private static TypeCheckResult isSubType(Context cx, Object sourceValue, Type source, Type target) { - TypeCheckResult result = isSubType(cx, source, target); - if (result != TypeCheckResult.FALSE) { - return result; + private static boolean isSubType(Context cx, Object sourceValue, Type source, Type target) { + boolean result = isSubType(cx, source, target); + if (!result) { + return isSubTypeWithShape(cx, sourceValue, Builder.from(cx, source), Builder.from(cx, target)); } - return isSubTypeWithShape(cx, sourceValue, Builder.from(cx, source), Builder.from(cx, target)); + return result; } - private static TypeCheckResult isSubTypeWithShape(Context cx, Object sourceValue, SemType source, SemType target) { - TypeCheckResult result; - result = isSubTypeWithShapeInner(cx, sourceValue, target); - if (result == TypeCheckResult.MAYBE) { - if (Core.containsBasicType(source, B_TYPE_TOP)) { - return TypeCheckResult.MAYBE; - } - return TypeCheckResult.FALSE; + private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemType source, SemType target) { + Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); + if (sourceSingletonType.isEmpty()) { + return false; } - return result; + SemType singletonType = sourceSingletonType.get(); + return isSubTypeInner(singletonType, target); } - private static TypeCheckResult isSubType(Context cx, Type source, Type target) { + private static boolean isSubType(Context cx, Type source, Type target) { if (source instanceof ParameterizedType sourceParamType) { if (target instanceof ParameterizedType targetParamType) { return isSubType(cx, sourceParamType.getParamValueType(), targetParamType.getParamValueType()); @@ -596,47 +558,17 @@ private static TypeCheckResult isSubType(Context cx, Type source, Type target) { return isSubTypeInner(Builder.from(cx, source), Builder.from(cx, target)); } - private static TypeCheckResult isSubTypeInner(Context cx, Object sourceValue, SemType source, SemType target) { - TypeCheckResult result = isSubTypeInner(source, target); - if (result != TypeCheckResult.FALSE) { - return result; - } - return isSubTypeWithShape(cx, sourceValue, source, target); - } - - private static TypeCheckResult isSubTypeWithShapeInner(Context cx, Object sourceValue, SemType target) { - Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); - if (sourceSingletonType.isEmpty()) { - return fallbackToBTypeWithoutShape(sourceValue, target) ? - TypeCheckResult.MAYBE : TypeCheckResult.FALSE; - } - SemType singletonType = sourceSingletonType.get(); - return isSubTypeInner(singletonType, target); - } - - private static boolean fallbackToBTypeWithoutShape(Object sourceValue, SemType target) { - if (!Core.containsBasicType(target, B_TYPE_TOP)) { - return false; + private static boolean isSubTypeInner(Context cx, Object sourceValue, SemType source, SemType target) { + boolean result = isSubTypeInner(source, target); + if (!result) { + return isSubTypeWithShape(cx, sourceValue, source, target); } - return !(sourceValue instanceof FPValue); + return true; } - private static TypeCheckResult isSubTypeInner(SemType source, SemType target) { + private static boolean isSubTypeInner(SemType source, SemType target) { Context cx = context(); - if (!Core.containsBasicType(source, B_TYPE_TOP)) { - return Core.isSubType(cx, source, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; - } - if (!Core.containsBasicType(target, B_TYPE_TOP)) { - if (Core.containsBasicType(source, Builder.objectType())) { - // This is a hack but since target defines the minimal it is fine - SemType sourcePureSemType = Core.intersect(source, SEMTYPE_TOP); - return Core.isSubType(cx, sourcePureSemType, target) ? TypeCheckResult.TRUE : TypeCheckResult.FALSE; - } - return TypeCheckResult.FALSE; - } - SemType sourcePureSemType = Core.intersect(source, SEMTYPE_TOP); - SemType targetPureSemType = Core.intersect(target, SEMTYPE_TOP); - return Core.isSubType(cx, sourcePureSemType, targetPureSemType) ? TypeCheckResult.MAYBE : TypeCheckResult.FALSE; + return Core.isSubType(cx, source, target); } private static SemType widenedType(Context cx, Object value) { @@ -657,14 +589,6 @@ private static SemType widenedType(Context cx, Object value) { } } - private static BType bTypePart(Context cx, Type t) { - return bTypePart(Builder.from(cx, t)); - } - - private static BType bTypePart(SemType t) { - return (BType) Core.subTypeData(t, BasicTypeCode.BT_B_TYPE); - } - public static boolean isInherentlyImmutableType(Type sourceType) { sourceType = getImpliedType(sourceType); if (FallbackTypeChecker.isSimpleBasicType(sourceType)) { 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 index 2639aad82d09..2fede7beb4ec 100644 --- 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 @@ -22,9 +22,9 @@ import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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.api.types.semtype.Definition; import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; @@ -618,7 +618,7 @@ private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { case NEVER -> Builder.neverType(); case XML -> Builder.xmlType(); case FUTURE -> Builder.futureType(); - // FIXME: implement json type + // TODO: implement json type default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); }; 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 From 824e579275ce0873d2f30b18ac968e99e78ee31a Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 13 Aug 2024 09:22:47 +0530 Subject: [PATCH 088/178] Implement isSimpleBasicType with SemTypes --- .../runtime/api/types/semtype/Builder.java | 9 ++- .../runtime/internal/FallbackTypeChecker.java | 13 +---- .../runtime/internal/TypeChecker.java | 58 +++++++++++++++++-- .../runtime/internal/TypeConverter.java | 4 +- 4 files changed, 64 insertions(+), 20 deletions(-) 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 index b648c0b0d737..8668baf415a4 100644 --- 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 @@ -26,7 +26,6 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BTable; import io.ballerina.runtime.api.values.BValue; -import io.ballerina.runtime.api.values.PatternMatchableValue; import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; import io.ballerina.runtime.internal.types.semtype.BCellSubType; @@ -320,7 +319,6 @@ public static Optional shapeOf(Context cx, Object object) { } else if (object instanceof BMap mapValue) { return typeOfMap(cx, mapValue); } else if (object instanceof FPValue fpValue) { - // TODO: this is a hack to support partial function types, remove when semtypes are fully implemented return Optional.of(from(cx, fpValue.getType())); } else if (object instanceof BError errorValue) { return typeOfError(cx, errorValue); @@ -460,7 +458,8 @@ public static SemType anyDataType(Context context) { ListDefinition listDef = new ListDefinition(); MappingDefinition mapDef = new MappingDefinition(); SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); - SemType accum = unionOf(SIMPLE_OR_STRING, xmlType(), listDef.getSemType(env), mapDef.getSemType(env), tableTy); + SemType accum = + unionOf(simpleOrStringType(), xmlType(), listDef.getSemType(env), mapDef.getSemType(env), tableTy); listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); context.anydataMemo = accum; @@ -515,6 +514,10 @@ public static BddNode listSubtypeTwoElement() { return LIST_SUBTYPE_TWO_ELEMENT; } + public static SemType simpleOrStringType() { + return SIMPLE_OR_STRING; + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java index 98e711207b88..ab1726eb963c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java @@ -263,16 +263,6 @@ static boolean checkIsType(Object sourceVal, BType sourceBType, BType targetBTyp }; } - /** - * Checks if the given decimal number is a real number. - * - * @param decimalValue The decimal value being checked - * @return True if the decimal value is a real number. - */ - static boolean isDecimalRealNumber(DecimalValue decimalValue) { - return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; - } - static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { for (Object bValue : sourceType.valueSpace) { if (!TypeChecker.checkIsType(bValue, targetType)) { @@ -1256,7 +1246,8 @@ private static boolean checkFiniteTypeAssignable(Object sourceValue, Type source boolean allowNumericConversion) { if (targetType.valueSpace.size() == 1) { Type valueType = getImpliedType(TypeChecker.getType(targetType.valueSpace.iterator().next())); - if (!isSimpleBasicType(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { + if (!TypeChecker.belongToSingleBasicTypeOrString(valueType) && + valueType.getTag() != TypeTags.NULL_TAG) { return checkIsLikeOnValue(null, sourceValue, sourceType, valueType, unresolvedValues, allowNumericConversion, null); } 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 7b63cc3ee10b..fd699263ea69 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 @@ -77,6 +77,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BOOLEAN; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BYTE; @@ -117,6 +118,7 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; private static final ThreadLocal threadContext = ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); + private static final SemType SIMPLE_BASIC_TYPE = createSimpleBasicType(); public static Object checkCast(Object sourceVal, Type targetType) { @@ -380,10 +382,20 @@ public static boolean isEqual(Object lhsValue, Object rhsValue) { */ public static boolean checkDecimalExactEqual(DecimalValue lhsValue, DecimalValue rhsValue) { - return FallbackTypeChecker.isDecimalRealNumber(lhsValue) && FallbackTypeChecker.isDecimalRealNumber(rhsValue) + return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && lhsValue.decimalValue().equals(rhsValue.decimalValue()); } + /** + * Checks if the given decimal number is a real number. + * + * @param decimalValue The decimal value being checked + * @return True if the decimal value is a real number. + */ + static boolean isDecimalRealNumber(DecimalValue decimalValue) { + return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; + } + /** * Reference equality check for values. If both the values are simple basic types, returns the same * result as {@link #isEqual(Object, Object, Set)} @@ -450,6 +462,23 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { }; } + private static boolean isReferenceEqualNew(Object lhsValue, Object rhsValue) { + if (lhsValue == rhsValue) { + return true; + } + + // if one is null, the other also needs to be null to be true + if (lhsValue == null || rhsValue == null) { + return false; + } + + Context cx = context(); + Optional lhsShape = Builder.shapeOf(cx, lhsValue); + Optional rhsShape = Builder.shapeOf(cx, rhsValue); + assert lhsShape.isPresent() && rhsShape.isPresent(); + return true; + } + /** * Get the typedesc of a value. * @@ -461,7 +490,7 @@ public static TypedescValue getTypedesc(Object value) { if (type == null) { return null; } - if (FallbackTypeChecker.isSimpleBasicType(type)) { + if (belongToSingleBasicTypeOrString(type)) { return new TypedescValueImpl(new BFiniteType(value.toString(), Set.of(value), 0)); } if (value instanceof BRefValue bRefValue) { @@ -516,7 +545,7 @@ static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, L * @return True if values are equal, else false. */ public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsValue) { - return FallbackTypeChecker.isDecimalRealNumber(lhsValue) && FallbackTypeChecker.isDecimalRealNumber(rhsValue) && + return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; } @@ -591,7 +620,7 @@ private static SemType widenedType(Context cx, Object value) { public static boolean isInherentlyImmutableType(Type sourceType) { sourceType = getImpliedType(sourceType); - if (FallbackTypeChecker.isSimpleBasicType(sourceType)) { + if (belongToSingleBasicTypeOrString(sourceType)) { return true; } @@ -1173,6 +1202,27 @@ private static BError createTypeCastError(Object value, Type targetType, List Date: Tue, 13 Aug 2024 10:50:10 +0530 Subject: [PATCH 089/178] Implement isReferenceEqual with semtypes --- .../runtime/internal/FallbackTypeChecker.java | 347 ------------------ .../runtime/internal/TypeChecker.java | 156 +++++--- 2 files changed, 100 insertions(+), 403 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java index ab1726eb963c..bdf9b514a2b7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java @@ -34,8 +34,6 @@ import io.ballerina.runtime.api.types.XmlNodeType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; -import io.ballerina.runtime.api.values.BRefValue; -import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BXml; import io.ballerina.runtime.internal.commons.TypeValuePair; import io.ballerina.runtime.internal.types.BArrayType; @@ -64,7 +62,6 @@ import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.ErrorValue; -import io.ballerina.runtime.internal.values.MapValue; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.StreamValue; import io.ballerina.runtime.internal.values.TableValueImpl; @@ -88,7 +85,6 @@ import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANY; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANYDATA; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_JSON; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_READONLY_JSON; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; import static io.ballerina.runtime.internal.TypeChecker.isEqual; @@ -105,27 +101,6 @@ final class FallbackTypeChecker { private FallbackTypeChecker() { } - static boolean checkIsType(List errors, Object sourceVal, BType sourceType, BType targetType) { - if (TypeChecker.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); - } - } - - if (isMutable(sourceVal, sourceType)) { - return false; - } - - return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, - null); - } - @Deprecated static boolean checkIsType(BType sourceType, BType targetType, List unresolvedTypes) { // First check whether both types are the same. @@ -221,48 +196,6 @@ static boolean checkIsType(BType sourceType, BType targetType, List unresolvedTypes) { - Type sourceType = getImpliedType(sourceBType); - Type targetType = getImpliedType(targetBType); - - 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 TypeChecker.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 -> TypeChecker.isInherentlyImmutableType(sourceType) || sourceType.isReadOnly(); - default -> checkIsRecursiveTypeOnValue(sourceVal, sourceType, targetType, sourceTypeTag, - targetTypeTag, unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - }; - } - static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { for (Object bValue : sourceType.valueSpace) { if (!TypeChecker.checkIsType(bValue, targetType)) { @@ -566,17 +499,6 @@ static boolean checkIsServiceType(Type sourceType, Type targetType, List visitedTypeSet = new HashSet<>(); return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(type, visitedTypeSet); @@ -1431,38 +1353,6 @@ static boolean checkIsRecursiveType(Type sourceType, Type targetType, List unresolvedTypes) { - switch (targetTypeTag) { - case TypeTags.ANYDATA_TAG: - if (sourceTypeTag == TypeTags.OBJECT_TYPE_TAG) { - return false; - } - return checkRecordBelongsToAnydataType((MapValue) sourceVal, (BRecordType) sourceType, unresolvedTypes); - case TypeTags.MAP_TAG: - return checkIsMapType(sourceVal, sourceType, (BMapType) targetType, unresolvedTypes); - case TypeTags.JSON_TAG: - return checkIsMapType(sourceVal, sourceType, - new BMapType(targetType.isReadOnly() ? TYPE_READONLY_JSON : - TYPE_JSON), unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - return checkIsRecordType(sourceVal, sourceType, (BRecordType) targetType, unresolvedTypes); - case TypeTags.UNION_TAG: - for (Type type : ((BUnionType) targetType).getMemberTypes()) { - if (TypeChecker.checkIsType(sourceVal, sourceType, type, unresolvedTypes)) { - return true; - } - } - return false; - case TypeTags.OBJECT_TYPE_TAG: - return checkObjectEquivalency(sourceVal, sourceType, (BObjectType) targetType, - unresolvedTypes); - default: - return false; - } - } - private static boolean checkIsUnionType(Type sourceType, BUnionType targetType, List unresolvedTypes) { // If we encounter two types that we are still resolving, then skip it. @@ -1509,51 +1399,6 @@ private static boolean checkIsMapType(Type sourceType, BMapType targetType, } } - private static boolean checkIsMapType(Object sourceVal, 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: - return checkIsMapType((MapValue) sourceVal, (BRecordType) sourceType, unresolvedTypes, - targetConstrainedType); - default: - return false; - } - } - - 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 (!TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceVal.get(name), targetConstrainedType)) { - return false; - } - } - - if (sourceType.sealed) { - return true; - } - - return TypeChecker.checkIsType(sourceType.restFieldType, targetConstrainedType, unresolvedTypes); - } - private static boolean checkIsXMLType(Type sourceType, Type targetType, List unresolvedTypes) { sourceType = getImpliedType(sourceType); @@ -1886,198 +1731,6 @@ private static boolean checkIsRecordType(BMapType sourceType, BRecordType target return TypeChecker.checkIsType(constraintType, targetType.restFieldType, unresolvedTypes); } - private static boolean checkRecordBelongsToAnydataType(MapValue sourceVal, BRecordType recordType, - List unresolvedTypes) { - Type targetType = TYPE_ANYDATA; - TypeChecker.TypePair pair = new TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceVal.get(fieldNameBString), targetType)) { - return false; - } - } else { - if (!TypeChecker.checkIsType(field.getFieldType(), targetType, unresolvedTypes)) { - return false; - } - } - } - - if (recordType.sealed) { - return true; - } - - return TypeChecker.checkIsType(recordType.restFieldType, targetType, unresolvedTypes); - } - - private static boolean checkIsRecordType(Object sourceVal, Type sourceType, BRecordType targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { - case TypeTags.RECORD_TYPE_TAG: - return checkIsRecordType((MapValue) sourceVal, (BRecordType) sourceType, targetType, unresolvedTypes); - case TypeTags.MAP_TAG: - return checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); - default: - return false; - } - } - - private static boolean checkIsRecordType(MapValue sourceRecordValue, BRecordType sourceRecordType, - BRecordType targetType, List unresolvedTypes) { - TypeChecker.TypePair pair = new TypeChecker.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 && - !TypeChecker.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 && - !TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceRecordValue.get(fieldNameBString), targetField.getFieldType())) { - return false; - } - } else { - if (!optionalTargetField && optionalSourceField) { - return false; - } - - if (!TypeChecker.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 (!TypeChecker.checkIsLikeType(sourceRecordValue.get(StringUtils.fromString(fieldName)), - targetType.restFieldType)) { - return false; - } - } else if (!TypeChecker.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 checkIsArrayType(BArrayType sourceType, BArrayType targetType, List unresolvedTypes) { switch (sourceType.getState()) { 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 fd699263ea69..d7956c004c83 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 @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.XmlNodeType; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; @@ -37,6 +38,7 @@ import io.ballerina.runtime.api.values.BRefValue; 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.types.BAnnotatableType; import io.ballerina.runtime.internal.types.BArrayType; import io.ballerina.runtime.internal.types.BBooleanType; @@ -77,6 +79,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Stream; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_BOOLEAN; @@ -119,6 +122,7 @@ public final class TypeChecker { private static final ThreadLocal threadContext = ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); private static final SemType SIMPLE_BASIC_TYPE = createSimpleBasicType(); + private static final byte MAX_TYPECAST_ERROR_COUNT = 20; public static Object checkCast(Object sourceVal, Type targetType) { @@ -415,68 +419,103 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { return false; } - Type lhsType = getImpliedType(getType(lhsValue)); - Type rhsType = getImpliedType(getType(rhsValue)); + 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.stringType())) { + return isEqual(lhsValue, rhsValue); + } + if (basicTypePredicate.test(Builder.xmlType())) { + return isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); + } + if (basicTypePredicate.test(Builder.handleType())) { + return isHandleValueRefEqual(lhsValue, rhsValue); + } + if (basicTypePredicate.test(Builder.functionType())) { + return isFunctionPointerEqual(getImpliedType(getType(lhsValue)), getImpliedType(getType(rhsValue))); + } + if (basicTypePredicate.test(Builder.regexType())) { + 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; + } - 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 FallbackTypeChecker.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; + static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { + boolean isLhsXmlSequence = lhsValue.getNodeType() == XmlNodeType.SEQUENCE; + boolean isRhsXmlSequence = rhsValue.getNodeType() == XmlNodeType.SEQUENCE; + + if (isLhsXmlSequence && isRhsXmlSequence) { + return isXMLSequenceRefEqual((XmlSequence) lhsValue, (XmlSequence) rhsValue); + } + if (isLhsXmlSequence && lhsValue.isSingleton()) { + return ((XmlSequence) lhsValue).getChildrenList().get(0) == rhsValue; + } + if (isRhsXmlSequence && rhsValue.isSingleton()) { + return ((XmlSequence) rhsValue).getChildrenList().get(0) == lhsValue; + } + if (lhsValue.getNodeType() != rhsValue.getNodeType()) { + return false; + } + if (lhsValue.getNodeType() == XmlNodeType.TEXT && rhsValue.getNodeType() == XmlNodeType.TEXT) { + return isEqual(lhsValue, rhsValue); + } + return false; + } + + private static boolean isXMLSequenceRefEqual(XmlSequence lhsValue, XmlSequence rhsValue) { + Iterator lhsIter = lhsValue.getChildrenList().iterator(); + Iterator rhsIter = rhsValue.getChildrenList().iterator(); + while (lhsIter.hasNext() && rhsIter.hasNext()) { + BXml l = lhsIter.next(); + BXml r = rhsIter.next(); + if (!(l == r || isXMLValueRefEqual((XmlValue) l, (XmlValue) r))) { + return false; } - }; + } + // lhs hasNext = false & rhs hasNext = false -> empty sequences, hence ref equal + // lhs hasNext = true & rhs hasNext = true would never reach here + // only one hasNext method returns true means sequences are of different sizes, hence not ref equal + return lhsIter.hasNext() == rhsIter.hasNext(); } - private static boolean isReferenceEqualNew(Object lhsValue, Object rhsValue) { - if (lhsValue == rhsValue) { - return true; + 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; } - // if one is null, the other also needs to be null to be true - if (lhsValue == null || rhsValue == null) { + SemType v2Ty = widenedType(cx, v2); + if (!isSimpleBasicSemType(v2Ty)) { return false; } - Context cx = context(); - Optional lhsShape = Builder.shapeOf(cx, lhsValue); - Optional rhsShape = Builder.shapeOf(cx, rhsValue); - assert lhsShape.isPresent() && rhsShape.isPresent(); - return true; + if (!Core.isSameType(cx, v1Ty, v2Ty)) { + return false; + } + + if (Core.isSubType(cx, v1Ty, Builder.decimalType())) { + return checkDecimalExactEqual((DecimalValue) v1, (DecimalValue) v2); + } + if (Core.isSubType(cx, v1Ty, Builder.intType())) { + Number n1 = (Number) v1; + Number n2 = (Number) v2; + return n1.longValue() == n2.longValue(); + } + return v1.equals(v2); } /** @@ -1197,8 +1236,7 @@ private static BError createTypeCastError(Object value, Type targetType, List Date: Tue, 13 Aug 2024 11:04:01 +0530 Subject: [PATCH 090/178] Re ennable type result caching --- .../io/ballerina/runtime/api/types/semtype/Core.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 index 33e28f5d6212..477239b82781 100644 --- 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 @@ -294,13 +294,15 @@ public static boolean isNever(SemType t) { } public static boolean isSubType(Context cx, SemType t1, SemType t2) { - // IF t1 and t2 are not pure semtypes calling this is an undefined + if (t1.equals(t2)) { + return true; + } SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); -// if (cached != SemType.CachedResult.NOT_FOUND) { -// return cached == SemType.CachedResult.TRUE; -// } + if (cached != SemType.CachedResult.NOT_FOUND) { + return cached == SemType.CachedResult.TRUE; + } boolean result = isEmpty(cx, diff(t1, t2)); -// t1.cacheSubTypeRelation(t2, result); + t1.cacheSubTypeRelation(t2, result); return result; } From c78b28575b065916dd06e671d81f0cab2ace8fbc Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 13 Aug 2024 12:41:26 +0530 Subject: [PATCH 091/178] Implement value conversion --- .../runtime/api/types/semtype/Core.java | 93 +++++++++++++++++++ .../runtime/internal/TypeChecker.java | 22 +++-- .../types/semtype/BDecimalSubType.java | 4 +- .../internal/types/semtype/BFloatSubType.java | 4 +- .../internal/types/semtype/BIntSubType.java | 12 ++- .../types/semtype/BStringSubType.java | 4 +- .../types/semtype/EnumerableSubtypeData.java | 6 +- 7 files changed, 127 insertions(+), 18 deletions(-) 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 index 477239b82781..a09502e79102 100644 --- 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 @@ -20,22 +20,28 @@ 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.BSubType; import io.ballerina.runtime.internal.types.semtype.BTableSubType; import io.ballerina.runtime.internal.types.semtype.BTypedescSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; +import io.ballerina.runtime.internal.types.semtype.EnumerableSubtypeData; 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.Objects; import java.util.Optional; +import java.util.function.Function; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_B_TYPE; 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; @@ -456,6 +462,93 @@ public static Optional listAtomicType(Context cx, SemType t) { return bddListAtomicType(env, (Bdd) getComplexSubtypeData(t, BT_LIST), listAtomicInner); } + public static SemType floatToInt(SemType t) { + if (!containsBasicType(t, Builder.floatType())) { + return Builder.neverType(); + } + return convertEnumerableNumericType(t, BT_FLOAT, Builder.intType(), + (floatValue) -> ((Double) floatValue).longValue(), + Builder::intConst); + } + + public static SemType floatToDecimal(SemType t) { + if (!containsBasicType(t, Builder.floatType())) { + return Builder.neverType(); + } + return convertEnumerableNumericType(t, BT_FLOAT, Builder.decimalType(), + (floatValue) -> BigDecimal.valueOf((Double) floatValue), + Builder::decimalConst); + } + + public static SemType decimalToInt(SemType t) { + if (!containsBasicType(t, Builder.decimalType())) { + return Builder.neverType(); + } + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.intType(), + (decimalVal) -> ((BigDecimal) decimalVal).longValue(), + Builder::intConst); + } + + public static SemType decimalToFloat(SemType t) { + if (!containsBasicType(t, Builder.decimalType())) { + return Builder.neverType(); + } + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.floatType(), + (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), + Builder::floatConst); + } + + public static SemType intToFloat(SemType t) { + if (!containsBasicType(t, Builder.intType())) { + return Builder.neverType(); + } + SubTypeData subTypeData = subTypeData(t, BT_INT); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.neverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return Builder.floatType(); + } + BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; + return intSubTypeData.values().stream().map(Builder::floatConst).reduce(Builder.neverType(), Core::union); + } + + public static SemType intToDecimal(SemType t) { + if (!containsBasicType(t, Builder.intType())) { + return Builder.neverType(); + } + SubTypeData subTypeData = subTypeData(t, BT_INT); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.neverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return Builder.decimalType(); + } + BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; + return intSubTypeData.values().stream().map(BigDecimal::new).map(Builder::decimalConst) + .reduce(Builder.neverType(), 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.neverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return topType; + } + assert subTypeData instanceof EnumerableSubtypeData; + EnumerableSubtypeData enumerableSubtypeData = (EnumerableSubtypeData) subTypeData; + SemType posType = Arrays.stream(enumerableSubtypeData.values()).map(valueConverter).distinct() + .map(semTypeCreator).reduce(Builder.neverType(), 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)) { 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 d7956c004c83..59188cae07f1 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 @@ -320,16 +320,22 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole Optional readonlyShape = Builder.readonlyShapeOf(cx, sourceValue); assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); - if (Core.isSubType(cx, shape, Builder.from(cx, targetType))) { - return true; - } + SemType targetSemType = Builder.from(cx, targetType); if (allowNumericConversion) { - // FIXME: this should check against a union of target types - return FallbackTypeChecker.checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), - true, null); + targetSemType = appendNumericConversionTypes(targetSemType); } - // FIXME: parent -> address - return false; + return Core.isSubType(cx, shape, targetSemType); + } + + private static SemType appendNumericConversionTypes(SemType semType) { + SemType result = semType; + result = Core.union(result, Core.intToFloat(semType)); + result = Core.union(result, Core.intToDecimal(semType)); + 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; } /** 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 index d534ff842e42..a25f5639478f 100644 --- 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 @@ -156,12 +156,12 @@ private DecimalSubTypeData(boolean allowed, BigDecimal[] values) { } @Override - boolean allowed() { + public boolean allowed() { return allowed; } @Override - BigDecimal[] values() { + public BigDecimal[] values() { return values; } } 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 index b2a786ddf450..3bc9e533abe3 100644 --- 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 @@ -168,12 +168,12 @@ private static boolean isSame(double f1, double f2) { } @Override - boolean allowed() { + public boolean allowed() { return allowed; } @Override - Double[] values() { + public Double[] values() { return values; } } 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 index ff89c9360d37..423c7bef51d7 100644 --- 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 @@ -155,7 +155,7 @@ public static boolean intSubtypeContains(SubTypeData d, long n) { return intSubTypeData.contains(n); } - static final class IntSubTypeData implements SubTypeData { + public static final class IntSubTypeData implements SubTypeData { final Range[] ranges; @@ -167,6 +167,16 @@ 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; } 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 index 493ba9f3fd91..6a2af0654a89 100644 --- 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 @@ -251,12 +251,12 @@ private ValueData(boolean allowed, String[] values) { } @Override - boolean allowed() { + public boolean allowed() { return allowed; } @Override - String[] values() { + public String[] values() { return values; } } 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 index 1b01d52aa5a1..a7831a541c12 100644 --- 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 @@ -31,11 +31,11 @@ * @param type individual value in the subset * @since 2201.10.0 */ -abstract class EnumerableSubtypeData> { +public abstract class EnumerableSubtypeData> { - abstract boolean allowed(); + public abstract boolean allowed(); - abstract E[] values(); + public abstract E[] values(); @Override public boolean equals(Object obj) { From d18e8b9b97e3f39312de44be6fe87bcad1c8c3d9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 13 Aug 2024 13:22:44 +0530 Subject: [PATCH 092/178] Remove fallback type checker --- .../runtime/internal/TypeChecker.java | 63 +++++++++++++++++-- .../runtime/internal/TypeConverter.java | 4 +- 2 files changed, 60 insertions(+), 7 deletions(-) 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 59188cae07f1..e3225ae2b46f 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 @@ -577,11 +577,6 @@ public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - Context cx = context(); - return isSubType(cx, sourceVal, sourceType, targetType); - } - /** * Check if two decimal values are equal in value. * @@ -955,6 +950,64 @@ static boolean isStructuredType(Type type) { }; } + static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object valueSpaceItem, + boolean allowNumericConversion) { + Type valueSpaceItemType = getType(valueSpaceItem); + int sourceTypeTag = getImpliedType(sourceType).getTag(); + int valueSpaceItemTypeTag = getImpliedType(valueSpaceItemType).getTag(); + if (valueSpaceItemTypeTag > TypeTags.DECIMAL_TAG) { + return valueSpaceItemTypeTag == sourceTypeTag && + (valueSpaceItem == sourceValue || valueSpaceItem.equals(sourceValue)); + } + + switch (sourceTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + switch (valueSpaceItemTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue(); + case TypeTags.FLOAT_TAG: + return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue() && + allowNumericConversion; + case TypeTags.DECIMAL_TAG: + return ((Number) sourceValue).longValue() == ((DecimalValue) valueSpaceItem).intValue() && + allowNumericConversion; + } + case TypeTags.FLOAT_TAG: + switch (valueSpaceItemTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + return ((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() + && allowNumericConversion; + case TypeTags.FLOAT_TAG: + return (((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() || + (Double.isNaN((Double) sourceValue) && Double.isNaN((Double) valueSpaceItem))); + case TypeTags.DECIMAL_TAG: + return ((Number) sourceValue).doubleValue() == ((DecimalValue) valueSpaceItem).floatValue() + && allowNumericConversion; + } + case TypeTags.DECIMAL_TAG: + switch (valueSpaceItemTypeTag) { + case TypeTags.BYTE_TAG: + case TypeTags.INT_TAG: + return checkDecimalEqual((DecimalValue) sourceValue, + DecimalValue.valueOf(((Number) valueSpaceItem).longValue())) && allowNumericConversion; + case TypeTags.FLOAT_TAG: + return checkDecimalEqual((DecimalValue) sourceValue, + DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && + allowNumericConversion; + case TypeTags.DECIMAL_TAG: + return checkDecimalEqual((DecimalValue) sourceValue, (DecimalValue) valueSpaceItem); + } + default: + if (sourceTypeTag != valueSpaceItemTypeTag) { + return false; + } + return valueSpaceItem.equals(sourceValue); + } + } + /** * Type vector of size two, to hold the source and the target types. * diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index d4131ba5e7ba..ce4121a8741a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -397,7 +397,7 @@ public static Type getConvertibleFiniteType(Object inputValue, BFiniteType targe if (inputValue == valueSpaceItem) { return inputValueType; } - if (FallbackTypeChecker.isFiniteTypeValue(inputValue, inputValueType, valueSpaceItem, false)) { + if (TypeChecker.isFiniteTypeValue(inputValue, inputValueType, valueSpaceItem, false)) { return TypeChecker.getType(valueSpaceItem); } } @@ -407,7 +407,7 @@ public static Type getConvertibleFiniteType(Object inputValue, BFiniteType targe // if not we check whether it can be converted into a member of the finite type for (Object valueSpaceItem : finiteTypeValueSpace) { - if (FallbackTypeChecker.isFiniteTypeValue(inputValue, inputValueType, valueSpaceItem, true)) { + if (TypeChecker.isFiniteTypeValue(inputValue, inputValueType, valueSpaceItem, true)) { return TypeChecker.getType(valueSpaceItem); } } From 0807969fedf655baa5010ce88937fb3f07445e73 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 13 Aug 2024 14:26:15 +0530 Subject: [PATCH 093/178] Make semtype part of the top type --- .../io/ballerina/runtime/api/types/Type.java | 3 +- .../runtime/api/types/semtype/Builder.java | 10 +-- .../MutableSemTypeDependencyManager.java | 3 +- .../ballerina/runtime/api/values/BValue.java | 3 +- .../runtime/internal/TypeChecker.java | 75 ++++++------------- .../runtime/internal/ValueConverter.java | 5 +- .../runtime/internal/types/BObjectType.java | 2 +- .../runtime/internal/types/BRecordType.java | 6 +- .../runtime/internal/types/BTableType.java | 3 +- .../internal/values/AbstractArrayValue.java | 2 +- .../runtime/internal/values/MapValue.java | 2 +- 11 files changed, 36 insertions(+), 78 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java index 40d87dbe9175..66e3ad4ad3a4 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java @@ -18,6 +18,7 @@ package io.ballerina.runtime.api.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.types.semtype.SemType; /** * {@code Type} represents a type in Ballerina. @@ -29,7 +30,7 @@ * * @since 2.0.0 */ -public interface Type { +public interface Type extends SemType { // TODO: remove default implementations when standard library types are updated /** 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 index 8668baf415a4..b84450d9a9bf 100644 --- 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 @@ -129,14 +129,6 @@ public static SemType from(BasicTypeCode typeCode) { return SemType.from(1 << typeCode.code()); } - // TODO: remove this method - public static SemType from(Context cx, Type type) { - if (type instanceof SemType semType) { - return semType; - } - throw new IllegalArgumentException("Unsupported type: " + type); - } - public static SemType neverType() { return SemType.from(0); } @@ -319,7 +311,7 @@ public static Optional shapeOf(Context cx, Object object) { } else if (object instanceof BMap mapValue) { return typeOfMap(cx, mapValue); } else if (object instanceof FPValue fpValue) { - return Optional.of(from(cx, fpValue.getType())); + return Optional.of(fpValue.getType()); } else if (object instanceof BError errorValue) { return typeOfError(cx, errorValue); } else if (object instanceof AbstractObjectValue objectValue) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java index 545981ad8688..4478d5917d37 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java @@ -19,7 +19,6 @@ package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.internal.TypeChecker; import java.util.ArrayList; import java.util.IdentityHashMap; @@ -56,6 +55,6 @@ public synchronized SemType getSemType(Type target, MutableSemType self) { this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); dependencies.add(self); } - return Builder.from(TypeChecker.context(), target); + return target; } } 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 accc36c2bdfb..4dfb25e75c31 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,7 +18,6 @@ package io.ballerina.runtime.api.values; 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; @@ -63,6 +62,6 @@ default String informalStringValue(BLink parent) { Type getType(); default SemType widenedType(Context cx) { - return Builder.from(cx, getType()); + return getType(); } } 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 e3225ae2b46f..ef0c79e12746 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 @@ -269,17 +269,16 @@ public static boolean anyToJBoolean(Object sourceVal) { */ public static boolean checkIsType(Object sourceVal, Type targetType) { Context cx = context(); - SemType targetSemType = Builder.from(cx, targetType); - SemType targetBasicTypeUnion = Core.widenToBasicTypeUnion(targetSemType); + SemType targetBasicTypeUnion = Core.widenToBasicTypeUnion(targetType); SemType valueBasicType = widenedType(cx, sourceVal); if (!Core.isSubtypeSimple(valueBasicType, targetBasicTypeUnion)) { return false; } - if (targetBasicTypeUnion == targetSemType) { + if (targetBasicTypeUnion == targetType) { return true; } - SemType sourceSemType = Builder.from(cx, getType(sourceVal)); - return isSubTypeInner(cx, sourceVal, sourceSemType, targetSemType); + SemType sourceSemType = getType(sourceVal); + return Core.isSubType(context(), sourceSemType, targetType) || isSubTypeWithShape(cx, sourceVal, targetType); } /** @@ -292,8 +291,7 @@ 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) { - Context cx = context(); - return isSubType(cx, sourceVal, sourceType, targetType); + return isSubType(sourceType, targetType) || isSubTypeWithShape(context(), sourceVal, targetType); } /** @@ -320,7 +318,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole Optional readonlyShape = Builder.readonlyShapeOf(cx, sourceValue); assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); - SemType targetSemType = Builder.from(cx, targetType); + SemType targetSemType = targetType; if (allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); } @@ -346,6 +344,7 @@ private static SemType appendNumericConversionTypes(SemType semType) { * @return true if the two types are same; false otherwise */ public static boolean isSameType(Type sourceType, Type targetType) { + // FIXME: return sourceType == targetType || sourceType.equals(targetType); } @@ -567,14 +566,12 @@ 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) { - Context cx = context(); - return isSubType(cx, sourceType, targetType); + return isSubType(sourceType, targetType); } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - Context cx = context(); - return isSubType(cx, sourceType, targetType); + return isSubType(sourceType, targetType); } /** @@ -590,6 +587,7 @@ public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsV } public static boolean isNumericType(Type type) { + // FIXME: type = getImpliedType(type); return type.getTag() < TypeTags.STRING_TAG || TypeTags.isIntegerTypeTag(type.getTag()); } @@ -600,44 +598,20 @@ static boolean isByteLiteral(long longValue) { // Private methods - private static boolean isSubType(Context cx, Object sourceValue, Type source, Type target) { - boolean result = isSubType(cx, source, target); - if (!result) { - return isSubTypeWithShape(cx, sourceValue, Builder.from(cx, source), Builder.from(cx, target)); - } - return result; - } - - private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemType source, SemType target) { - Optional sourceSingletonType = Builder.shapeOf(cx, sourceValue); - if (sourceSingletonType.isEmpty()) { - return false; - } - SemType singletonType = sourceSingletonType.get(); - return isSubTypeInner(singletonType, target); + private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { + return Builder.shapeOf(cx, sourceValue) + .map(source -> Core.isSubType(context(), source, target)) + .orElse(false); } - private static boolean isSubType(Context cx, Type source, Type target) { + private static boolean isSubType(Type source, Type target) { if (source instanceof ParameterizedType sourceParamType) { if (target instanceof ParameterizedType targetParamType) { - return isSubType(cx, sourceParamType.getParamValueType(), targetParamType.getParamValueType()); + return isSubType(sourceParamType.getParamValueType(), targetParamType.getParamValueType()); } - return isSubType(cx, sourceParamType.getParamValueType(), target); + return isSubType(sourceParamType.getParamValueType(), target); } - return isSubTypeInner(Builder.from(cx, source), Builder.from(cx, target)); - } - - private static boolean isSubTypeInner(Context cx, Object sourceValue, SemType source, SemType target) { - boolean result = isSubTypeInner(source, target); - if (!result) { - return isSubTypeWithShape(cx, sourceValue, source, target); - } - return true; - } - - private static boolean isSubTypeInner(SemType source, SemType target) { - Context cx = context(); - return Core.isSubType(cx, source, target); + return Core.isSubType(context(), source, target); } private static SemType widenedType(Context cx, Object value) { @@ -658,6 +632,7 @@ private static SemType widenedType(Context cx, Object value) { } } + // FIXME: public static boolean isInherentlyImmutableType(Type sourceType) { sourceType = getImpliedType(sourceType); if (belongToSingleBasicTypeOrString(sourceType)) { @@ -687,6 +662,7 @@ public static boolean isInherentlyImmutableType(Type sourceType) { } } + // FIXME: public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { if (!unresolvedTypes.add(type)) { return true; @@ -1304,12 +1280,6 @@ private static SemType createSimpleBasicType() { Builder.decimalType()).reduce(Builder.neverType(), Core::union); } - static boolean isSimpleBasicType(Type type) { - Context cx = context(); - SemType semtype = Builder.from(cx, type); - return isSimpleBasicSemType(semtype); - } - static boolean isSimpleBasicSemType(SemType semType) { Context cx = context(); return Core.isSubType(cx, semType, SIMPLE_BASIC_TYPE); @@ -1317,9 +1287,8 @@ static boolean isSimpleBasicSemType(SemType semType) { static boolean belongToSingleBasicTypeOrString(Type type) { Context cx = context(); - SemType semType = Builder.from(cx, type); - return isSingleBasicType(semType) && Core.isSubType(cx, semType, Builder.simpleOrStringType()) && - !Core.isSubType(cx, semType, Builder.nilType()); + return isSingleBasicType(type) && Core.isSubType(cx, type, Builder.simpleOrStringType()) && + !Core.isSubType(cx, type, Builder.nilType()); } private static boolean isSingleBasicType(SemType semType) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java index 11d8034e04d4..a271ff313efc 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java @@ -181,11 +181,10 @@ private static Object xmlSequenceHack(Object value, Type targetType) { return value; } Context cx = TypeChecker.context(); - SemType targetSemType = Builder.from(cx, targetType); List list = new ArrayList<>(); for (BXml child : xmlSequence.getChildrenList()) { - SemType childType = Builder.from(cx, child.getType()); - boolean isReadonly = Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.readonlyType()); + SemType childType = child.getType(); + boolean isReadonly = Core.isSubType(cx, Core.intersect(childType, targetType), Builder.readonlyType()); if (isReadonly) { list.add((BXml) CloneUtils.cloneReadOnly(child)); } else { 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 353e441db6d9..97a9eab4ab40 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 @@ -404,7 +404,7 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje private static SemType fieldShape(Context cx, ShapeSupplier shapeSupplier, Field field, AbstractObjectValue objectValue, boolean isImmutable) { if (!isImmutable) { - return Builder.from(cx, field.getFieldType()); + return field.getFieldType(); } BString fieldName = StringUtils.fromString(field.getFieldName()); Optional shape = shapeSupplier.get(cx, objectValue.get(fieldName)); 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 4e2ba7be3c40..44f257aa1e97 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 @@ -316,7 +316,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap optionalField = false; fieldType = shapeSupplier.get(cx, fieldValue); } else { - SemType fieldSemType = Builder.from(cx, fieldType(fieldName)); + SemType fieldSemType = fieldType(fieldName); assert !Core.containsBasicType(fieldSemType, Builder.bType()); fieldType = Optional.of(fieldSemType); } @@ -332,7 +332,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap } boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - SemType fieldType = Builder.from(cx, field.getFieldType()); + SemType fieldType = field.getFieldType(); if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { fieldType = Builder.undef(); } @@ -346,7 +346,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap if (readonly) { semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, neverType(), CELL_MUT_NONE); } else { - SemType rest = restFieldType != null ? Builder.from(cx, restFieldType) : neverType(); + SemType rest = restFieldType != null ? restFieldType : neverType(); assert !Core.containsBasicType(rest, Builder.bType()); semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); } 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 876f9a493c3a..d81971cbb713 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 @@ -33,7 +33,6 @@ import io.ballerina.runtime.internal.values.TableValue; import io.ballerina.runtime.internal.values.TableValueImpl; -import java.util.Map; import java.util.Optional; /** @@ -219,7 +218,7 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { SemType constraintType = Builder.neverType(); for (var value : table.values()) { - SemType valueShape = shapeSupplier.get(cx, value).orElse(Builder.from(cx, constraint)); + SemType valueShape = shapeSupplier.get(cx, value).orElse(constraint); constraintType = Core.union(constraintType, valueShape); } return createSemTypeWithConstraint(constraintType); 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 ed31ad31bffb..ea7c3fbe5cff 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 @@ -283,7 +283,7 @@ protected void prepareForAddForcefully(int intIndex, int currentArraySize) { @Override public SemType widenedType(Context cx) { - SemType semType = Builder.from(cx, getType()); + SemType semType = getType(); return Core.intersect(semType, Builder.listType()); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java index 77752b5f1887..506f8e3413ef 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java @@ -40,7 +40,7 @@ public interface MapValue extends RefValue, CollectionValue, BMap { @Override default SemType widenedType(Context cx) { - SemType semType = Builder.from(cx, getType()); + SemType semType = getType(); return Core.intersect(semType, Builder.mappingType()); } } From ec258728d1947ad15ef63f33b7cf8e4a542c4651 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 13 Aug 2024 15:12:04 +0530 Subject: [PATCH 094/178] Refactor isSameType --- .../runtime/api/types/semtype/Builder.java | 7 +- .../runtime/internal/TypeChecker.java | 79 ++++++++----------- 2 files changed, 40 insertions(+), 46 deletions(-) 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 index b84450d9a9bf..4926635d2d07 100644 --- 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 @@ -113,7 +113,8 @@ public final class Builder { 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()); @@ -417,6 +418,10 @@ public static SemType xmlTextType() { return XML_TEXT.get(); } + public static SemType xmlNeverType() { + return XML_NEVER.get(); + } + public static SemType xmlPIType() { return XML_PI.get(); } 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 ef0c79e12746..fd4eab2cdb2b 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 @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.FunctionType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ParameterizedType; +import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; import io.ballerina.runtime.api.types.semtype.Builder; @@ -55,7 +56,6 @@ import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.BTypeReferenceType; import io.ballerina.runtime.internal.types.BUnionType; -import io.ballerina.runtime.internal.types.BXmlType; import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.ErrorValue; @@ -122,6 +122,8 @@ public final class TypeChecker { private static final ThreadLocal threadContext = ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); private static final SemType SIMPLE_BASIC_TYPE = createSimpleBasicType(); + private static final SemType NUMERIC_TYPE = createNumericType(); + private static final SemType INHERENTLY_IMMUTABLE_TYPE = createInherentlyImmutableType(); private static final byte MAX_TYPECAST_ERROR_COUNT = 20; public static Object checkCast(Object sourceVal, Type targetType) { @@ -344,33 +346,34 @@ private static SemType appendNumericConversionTypes(SemType semType) { * @return true if the two types are same; false otherwise */ public static boolean isSameType(Type sourceType, Type targetType) { - // FIXME: - return sourceType == targetType || sourceType.equals(targetType); + return Core.isSameType(context(), sourceType, targetType); } public static Type getType(Object value) { if (value == null) { return TYPE_NULL; } else if (value instanceof Number number) { - if (value instanceof Double) { - return BFloatType.singletonType(number.doubleValue()); - } - long numberValue = - number instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : number.longValue(); - if (value instanceof Long) { - return BIntegerType.singletonType(numberValue); - } else if (value instanceof Integer || value instanceof Byte) { - return BByteType.singletonType(numberValue); - } + return getNumberType(number); } else if (value instanceof Boolean booleanValue) { return BBooleanType.singletonType(booleanValue); } else if (value instanceof BObject bObject) { return bObject.getOriginalType(); } - return ((BValue) value).getType(); } + private static Type getNumberType(Number number) { + if (number instanceof Double) { + return BFloatType.singletonType(number.doubleValue()); + } + long numberValue = + number instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : number.longValue(); + if (number instanceof Integer || number instanceof Byte) { + return BByteType.singletonType(numberValue); + } + return BIntegerType.singletonType(numberValue); + } + /** * Deep value equality check for anydata. * @@ -586,10 +589,13 @@ public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsV lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; } + private static SemType createNumericType() { + return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType()) + .reduce(Builder.neverType(), Core::union); + } + public static boolean isNumericType(Type type) { - // FIXME: - type = getImpliedType(type); - return type.getTag() < TypeTags.STRING_TAG || TypeTags.isIntegerTypeTag(type.getTag()); + return Core.isSubType(context(), type, NUMERIC_TYPE); } static boolean isByteLiteral(long longValue) { @@ -632,37 +638,19 @@ private static SemType widenedType(Context cx, Object value) { } } - // FIXME: - public static boolean isInherentlyImmutableType(Type sourceType) { - sourceType = getImpliedType(sourceType); - if (belongToSingleBasicTypeOrString(sourceType)) { - return true; - } + private static SemType createInherentlyImmutableType() { + return Stream.of(createSimpleBasicType(), Builder.stringType(), Builder.errorType(), Builder.functionType(), + Builder.typeDescType(), Builder.handleType(), Builder.xmlTextType(), Builder.xmlNeverType(), + Builder.regexType()) + .reduce(Builder.neverType(), Core::union); + } - switch (sourceType.getTag()) { - case TypeTags.XML_TEXT_TAG: - case TypeTags.FINITE_TYPE_TAG: // Assuming a finite type will only have members from simple basic types. - case TypeTags.READONLY_TAG: - case TypeTags.NULL_TAG: - case TypeTags.NEVER_TAG: - case TypeTags.ERROR_TAG: - case TypeTags.INVOKABLE_TAG: - case TypeTags.SERVICE_TAG: - case TypeTags.TYPEDESC_TAG: - case TypeTags.FUNCTION_POINTER_TAG: - case TypeTags.HANDLE_TAG: - case TypeTags.REG_EXP_TYPE_TAG: - return true; - case TypeTags.XML_TAG: - return ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return isInherentlyImmutableType(((BTypeReferenceType) sourceType).getReferredType()); - default: - return false; - } + public static boolean isInherentlyImmutableType(Type sourceType) { + // readonly part is there to match to old API + return + Core.isSubType(context(), sourceType, INHERENTLY_IMMUTABLE_TYPE) || sourceType instanceof ReadonlyType; } - // FIXME: public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { if (!unresolvedTypes.add(type)) { return true; @@ -676,6 +664,7 @@ public static boolean isSelectivelyImmutableType(Type type, Set unresolved 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: Type elementType = ((BArrayType) type).getElementType(); From 12abdbe8e18fe9a518eab99f6cfef95018277f42 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 14 Aug 2024 08:22:56 +0530 Subject: [PATCH 095/178] Optimize convertible type for int subtypes --- .../java/io/ballerina/runtime/internal/TypeChecker.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 fd4eab2cdb2b..94bcd60e4a7d 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 @@ -321,7 +321,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); SemType targetSemType = targetType; - if (allowNumericConversion) { + if (Core.isSubType(context(), shape, NUMERIC_TYPE) && allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); } return Core.isSubType(cx, shape, targetSemType); @@ -329,8 +329,11 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole private static SemType appendNumericConversionTypes(SemType semType) { SemType result = semType; - result = Core.union(result, Core.intToFloat(semType)); - result = Core.union(result, Core.intToDecimal(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.intType())) { + result = Core.union(Core.union(Builder.decimalType(), Builder.floatType()), result); + } result = Core.union(result, Core.floatToInt(semType)); result = Core.union(result, Core.floatToDecimal(semType)); result = Core.union(result, Core.decimalToInt(semType)); From b9e2d7714b0f1f17c1bf9aecb2c9999e937e8028 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 14 Aug 2024 08:55:46 +0530 Subject: [PATCH 096/178] Introduce SimpleBdd --- .../runtime/api/types/semtype/Bdd.java | 2 +- .../runtime/api/types/semtype/BddNode.java | 76 +++++-------------- .../api/types/semtype/BddNodeImpl.java | 60 +++++++++++++++ .../api/types/semtype/BddNodeSimple.java | 31 ++++++++ 4 files changed, 110 insertions(+), 59 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java 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 index a438b359afc2..8d5a4aa55442 100644 --- 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 @@ -205,7 +205,7 @@ private Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { return left.bddUnion(right); } - return new BddNode(atom, left, middle, right); + return new BddNodeImpl(atom, left, middle, right); } @Override 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 index ec0991eb66be..c5a45ffe89e5 100644 --- 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 @@ -1,63 +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.runtime.api.types.semtype; -/** - * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. - * - * @since 2201.10.0 - */ -public final class BddNode extends Bdd { +public abstract sealed class BddNode extends Bdd permits BddNodeImpl, BddNodeSimple { - private final Atom atom; - private final Bdd left; - private final Bdd middle; - private final Bdd right; private volatile Integer hashCode = null; - BddNode(Atom atom, Bdd left, Bdd middle, Bdd right) { - super(false, false); - this.atom = atom; - this.left = left; - this.middle = middle; - this.right = right; + protected BddNode(boolean all, boolean nothing) { + super(all, nothing); } public static BddNode bddAtom(Atom atom) { - return new BddNode(atom, BddAllOrNothing.ALL, BddAllOrNothing.NOTHING, BddAllOrNothing.NOTHING); + return new BddNodeSimple(atom); } - public Atom atom() { - return atom; + boolean isSimple() { + return this instanceof BddNodeSimple; } - public Bdd left() { - return left; - } + abstract public Atom atom(); - public Bdd middle() { - return middle; - } + abstract public Bdd left(); - public Bdd right() { - return right; - } + abstract public Bdd middle(); + + abstract public Bdd right(); @Override public boolean equals(Object obj) { @@ -67,8 +32,8 @@ public boolean equals(Object obj) { if (!(obj instanceof BddNode other)) { return false; } - return atom.equals(other.atom) && left.equals(other.left) && middle.equals(other.middle) && - right.equals(other.right); + return atom().equals(other.atom()) && left().equals(other.left()) && middle().equals(other.middle()) && + right().equals(other.right()); } @Override @@ -86,20 +51,15 @@ public int hashCode() { } private int computeHashCode() { - int result = atom.hashCode(); - result = 31 * result + left.hashCode(); - result = 31 * result + middle.hashCode(); - result = 31 * result + right.hashCode(); + int result = atom().hashCode(); + result = 31 * result + left().hashCode(); + result = 31 * result + middle().hashCode(); + result = 31 * result + right().hashCode(); return result; } - boolean isSimple() { - return left.equals(BddAllOrNothing.ALL) && middle.equals(BddAllOrNothing.NOTHING) && - right.equals(BddAllOrNothing.NOTHING); - } - @Override public boolean posMaybeEmpty() { - return middle.posMaybeEmpty() || right.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..1f6239bbef60 --- /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; + +/** + * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + * + * @since 2201.10.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..10b31a5d754b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java @@ -0,0 +1,31 @@ +package io.ballerina.runtime.api.types.semtype; + +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; + } +} From 355526bc2f6ea4977b3f2281e9ab34af7b3c8061 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 14 Aug 2024 09:04:50 +0530 Subject: [PATCH 097/178] Move MutableSemTypeDependencyManager to internal --- .../io/ballerina/runtime/internal/types/BObjectType.java | 2 +- .../main/java/io/ballerina/runtime/internal/types/BType.java | 2 +- .../types/semtype/MutableSemTypeDependencyManager.java | 5 +++-- bvm/ballerina-runtime/src/main/java/module-info.java | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/MutableSemTypeDependencyManager.java (92%) 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 97a9eab4ab40..922f96f00c3e 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 @@ -37,7 +37,6 @@ import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.MutableSemType; -import io.ballerina.runtime.api.types.semtype.MutableSemTypeDependencyManager; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; @@ -49,6 +48,7 @@ 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.MutableSemTypeDependencyManager; import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; import io.ballerina.runtime.internal.values.AbstractObjectValue; 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 3b9705d0d7ef..e41999c1c161 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 @@ -25,11 +25,11 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.MutableSemType; -import io.ballerina.runtime.api.types.semtype.MutableSemTypeDependencyManager; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.MutableSemTypeDependencyManager; import io.ballerina.runtime.internal.types.semtype.SubTypeData; import java.util.Objects; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java similarity index 92% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java index 4478d5917d37..dd1aafa0420b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java @@ -16,16 +16,17 @@ * under the License. */ -package io.ballerina.runtime.api.types.semtype; +package io.ballerina.runtime.internal.types.semtype; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.MutableSemType; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -// TODO: consider moving this to internal package public final class MutableSemTypeDependencyManager { private static final MutableSemTypeDependencyManager INSTANCE = new MutableSemTypeDependencyManager(); diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index 56c5b94c127d..bc9149193727 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -74,4 +74,5 @@ exports io.ballerina.runtime.observability.tracer.noop; exports io.ballerina.runtime.internal.regexp; exports io.ballerina.runtime.internal.configurable.providers to org.ballerinalang.debugadapter.runtime; + exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.internal.types; } From aa4379cb1ef10b512d39da3a8dcbb1cab2496db4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 14 Aug 2024 11:35:25 +0530 Subject: [PATCH 098/178] Make any and readonly types immutable semtypes --- .../runtime/api/types/semtype/BddNode.java | 8 +- .../runtime/api/types/semtype/Core.java | 28 ++--- .../runtime/internal/types/BAnyType.java | 112 +++++++++++------- .../runtime/internal/types/BArrayType.java | 1 - .../runtime/internal/types/BErrorType.java | 7 +- .../runtime/internal/types/BMapType.java | 11 +- .../internal/types/BParameterizedType.java | 7 +- .../runtime/internal/types/BReadonlyType.java | 54 ++++----- .../runtime/internal/types/BRecordType.java | 2 +- .../internal/types/BSemTypeSupplier.java | 29 ----- .../internal/types/BSemTypeWrapper.java | 3 +- .../runtime/internal/types/ShapeSupplier.java | 2 +- .../internal/types/semtype/PureSemType.java | 2 - .../types/semtype/StreamDefinition.java | 1 - .../runtime/internal/values/MapValueImpl.java | 2 +- 15 files changed, 126 insertions(+), 143 deletions(-) delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java 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 index c5a45ffe89e5..b8411879a24e 100644 --- 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 @@ -16,13 +16,13 @@ boolean isSimple() { return this instanceof BddNodeSimple; } - abstract public Atom atom(); + public abstract Atom atom(); - abstract public Bdd left(); + public abstract Bdd left(); - abstract public Bdd middle(); + public abstract Bdd middle(); - abstract public Bdd right(); + public abstract Bdd right(); @Override public boolean equals(Object obj) { 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 index a09502e79102..986c2038e425 100644 --- 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 @@ -467,8 +467,7 @@ public static SemType floatToInt(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_FLOAT, Builder.intType(), - (floatValue) -> ((Double) floatValue).longValue(), - Builder::intConst); + (floatValue) -> ((Double) floatValue).longValue(), Builder::intConst); } public static SemType floatToDecimal(SemType t) { @@ -476,8 +475,7 @@ public static SemType floatToDecimal(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_FLOAT, Builder.decimalType(), - (floatValue) -> BigDecimal.valueOf((Double) floatValue), - Builder::decimalConst); + (floatValue) -> BigDecimal.valueOf((Double) floatValue), Builder::decimalConst); } public static SemType decimalToInt(SemType t) { @@ -485,8 +483,7 @@ public static SemType decimalToInt(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_DECIMAL, Builder.intType(), - (decimalVal) -> ((BigDecimal) decimalVal).longValue(), - Builder::intConst); + (decimalVal) -> ((BigDecimal) decimalVal).longValue(), Builder::intConst); } public static SemType decimalToFloat(SemType t) { @@ -494,8 +491,7 @@ public static SemType decimalToFloat(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_DECIMAL, Builder.floatType(), - (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), - Builder::floatConst); + (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), Builder::floatConst); } public static SemType intToFloat(SemType t) { @@ -530,8 +526,8 @@ public static SemType intToDecimal(SemType t) { } private static , T extends Comparable> SemType convertEnumerableNumericType( - SemType source, BasicTypeCode targetTypeCode, SemType topType, - Function valueConverter, Function semTypeCreator) { + SemType source, BasicTypeCode targetTypeCode, SemType topType, Function valueConverter, + Function semTypeCreator) { SubTypeData subTypeData = subTypeData(source, targetTypeCode); if (subTypeData == AllOrNothing.NOTHING) { return Builder.neverType(); @@ -539,18 +535,18 @@ private static , T extends Comparable> SemType conver if (subTypeData == AllOrNothing.ALL) { return topType; } - assert subTypeData instanceof EnumerableSubtypeData; + //noinspection unchecked - it's a enumerable type EnumerableSubtypeData enumerableSubtypeData = (EnumerableSubtypeData) subTypeData; - SemType posType = Arrays.stream(enumerableSubtypeData.values()).map(valueConverter).distinct() - .map(semTypeCreator).reduce(Builder.neverType(), Core::union); + SemType posType = + Arrays.stream(enumerableSubtypeData.values()).map(valueConverter).distinct().map(semTypeCreator) + .reduce(Builder.neverType(), Core::union); if (enumerableSubtypeData.allowed()) { return posType; } return diff(topType, posType); } - private static Optional bddListAtomicType(Env env, Bdd bdd, - ListAtomicType top) { + private static Optional bddListAtomicType(Env env, Bdd bdd, ListAtomicType top) { if (!(bdd instanceof BddNode bddNode)) { if (bdd.isAll()) { return Optional.ofNullable(top); @@ -560,4 +556,4 @@ private static Optional bddListAtomicType(Env env, Bdd bdd, } return bddNode.isSimple() ? Optional.of(env.listAtomType(bddNode.atom())) : Optional.empty(); } -} \ No newline at end of file +} 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 7c6366c44a84..c3040bbab3d6 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 @@ -37,11 +37,7 @@ * * @since 0.995.0 */ -public class BAnyType extends BType implements AnyType { - - private final boolean readonly; - private IntersectionType immutableType; - private IntersectionType intersectionType = null; +public class BAnyType extends BSemTypeWrapper implements AnyType { /** * Create a {@code BAnyType} which represents the any type. @@ -49,65 +45,95 @@ 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 BAnyTypeImpl(typeName, pkg, readonly), pickSemType(readonly)); } @Override - public V getZeroValue() { - return null; + public Optional getIntersectionType() { + return ((BAnyTypeImpl) this.bType).getIntersectionType(); } @Override - public V getEmptyValue() { - return null; + public void setIntersectionType(IntersectionType intersectionType) { + ((BAnyTypeImpl) this.bType).setIntersectionType(intersectionType); } @Override - public int getTag() { - return TypeTags.ANY_TAG; + public Type getReferredType() { + return ((BAnyTypeImpl) this.bType).getReferredType(); } @Override - public boolean isNilable() { - return true; + public IntersectionType getImmutableType() { + return ((BAnyTypeImpl) this.bType).getImmutableType(); } - @Override - public boolean isReadOnly() { - return this.readonly; - } + private 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; - @Override - public Optional getIntersectionType() { - return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); - } + 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 void setIntersectionType(IntersectionType intersectionType) { - this.intersectionType = intersectionType; } - @Override - public SemType createSemType() { + private static SemType pickSemType(boolean readonly) { SemType semType = Builder.anyType(); - if (isReadOnly()) { + if (readonly) { semType = Core.intersect(semType, Builder.readonlyType()); } 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 422dc0c39db3..ed6db9b37937 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,7 +22,6 @@ import io.ballerina.runtime.api.types.ArrayType; 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; 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 48243e22128f..a1e268caa9ec 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 @@ -35,7 +35,6 @@ import io.ballerina.runtime.internal.values.ErrorValue; import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; /** * {@code BErrorType} represents error type in Ballerina. @@ -48,8 +47,6 @@ public class BErrorType extends BAnnotatableType implements ErrorType, TypeWithS public BTypeIdSet typeIdSet; private IntersectionType intersectionType = null; private DistinctIdSupplier distinctIdSupplier; - private static final AtomicInteger nextId = new AtomicInteger(0); - private final int id = nextId.getAndIncrement(); public BErrorType(String typeName, Module pkg, Type detailType) { super(typeName, pkg, ErrorValue.class); @@ -134,9 +131,7 @@ public synchronized SemType createSemType() { err = Builder.errorType(); } else { SemType detailType = mutableSemTypeDependencyManager.getSemType(getDetailType(), this); - if (!Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP))) { - throw new IllegalStateException("Error types can't have BTypes"); - } + assert (!Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP))); err = ErrorUtils.errorDetail(detailType); } 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 39044033a563..b6d577285a81 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 @@ -221,9 +221,9 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier return readonlyShape(cx, shapeSupplierFn, (BMap) object); } - static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, BMap value) { + static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, BMap value) { int nFields = value.size(); - MappingDefinition md ; + MappingDefinition md; Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); if (readonlyShapeDefinition.isPresent()) { @@ -234,13 +234,10 @@ static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, value.setReadonlyShapeDefinition(md); } MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; - Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); for (int i = 0; i < nFields; i++) { Optional valueType = shapeSupplier.get(cx, entries[i].getValue()); - if (valueType.isEmpty()) { - return Optional.empty(); - } - SemType fieldType = valueType.get(); + SemType fieldType = valueType.orElseThrow(); fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); } SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.neverType(), CELL_MUT_NONE); 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 40ef3ddd4454..42fa4a8153be 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 @@ -84,7 +84,10 @@ public int getParamIndex() { @Override public SemType createSemType() { - BType paramValueType = (BType) this.paramValueType; - return paramValueType.createSemType(); + Type paramValueType = this.paramValueType; + if (paramValueType instanceof BType bType) { + return bType.createSemType(); + } + return paramValueType; } } 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 c5f180e406f1..baa048df7248 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 @@ -21,8 +21,6 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ReadonlyType; 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; /** @@ -30,40 +28,40 @@ * * @since 1.3.0 */ -public class BReadonlyType extends BType implements ReadonlyType { +public class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(typeName, pkg, RefValue.class); + super(new BReadonlyTypeImpl(typeName, pkg), Builder.readonlyType()); } - @Override - public V getZeroValue() { - return null; - } + private 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 boolean isReadOnly() { - return true; - } + @Override + public int getTag() { + return TypeTags.READONLY_TAG; + } + + public boolean isNilable() { + return true; + } - // TODO: this must be immutable semtype as well - @Override - public SemType createSemType() { - return Builder.readonlyType(); + @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 44f257aa1e97..baf93922625f 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 @@ -290,7 +290,7 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap value, boolean readonly) { int nFields = value.size(); List fields = new ArrayList<>(nFields); - Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); Set handledFields = new HashSet<>(nFields); MappingDefinition md; if (readonly) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java deleted file mode 100644 index a6695b51c75f..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeSupplier.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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; - -@FunctionalInterface -public interface BSemTypeSupplier { - - SemType get(Context cx); -} 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 index c9830732f780..5703cb486983 100644 --- 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 @@ -34,7 +34,8 @@ */ public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { - private final BType bType; + // FIXME: turn this to a lazy supplier to avoid intialization if not needed + protected final BType bType; protected final String typeName; // Debugger uses this field to show the type name BSemTypeWrapper(BType bType, SemType semType) { 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 index d64f98145190..2f19564dd5c7 100644 --- 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 @@ -25,7 +25,7 @@ import java.util.Optional; @FunctionalInterface -public interface ShapeSupplier { +interface ShapeSupplier { Optional get(Context cx, Object object); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java index e157f38bfee5..92b794b0c19e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java @@ -20,8 +20,6 @@ import io.ballerina.runtime.api.types.semtype.SubType; -import java.util.concurrent.atomic.AtomicInteger; - /** * Represent types that conform only to {@code SemType} APIs. * 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 index d8326783dddf..f0c7c103b1dd 100644 --- 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 @@ -51,7 +51,6 @@ public SemType define(Env env, SemType valueType, SemType completionType) { private SemType streamContaining(SemType tupleType) { SubTypeData bdd = subTypeData(tupleType, BasicTypeCode.BT_LIST); assert bdd instanceof Bdd; - // FIXME: wrap in delegate return createBasicSemType(BasicTypeCode.BT_STREAM, (Bdd) bdd); } } 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 3da6cdd94ada..fd76f51682aa 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; @@ -49,7 +50,6 @@ 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.api.types.semtype.Definition; import java.io.ByteArrayOutputStream; import java.io.IOException; From e5629e565668cfa5ae19e1c16369a381480b4051 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 14 Aug 2024 13:22:34 +0530 Subject: [PATCH 099/178] Lazy initialize inner BTypes --- .../runtime/internal/types/BAnyType.java | 14 ++-- .../runtime/internal/types/BBooleanType.java | 20 +++--- .../runtime/internal/types/BByteType.java | 24 ++++--- .../runtime/internal/types/BDecimalType.java | 23 ++++--- .../runtime/internal/types/BErrorType.java | 2 +- .../runtime/internal/types/BFloatType.java | 15 ++-- .../runtime/internal/types/BHandleType.java | 6 +- .../runtime/internal/types/BIntegerType.java | 26 ++++--- .../runtime/internal/types/BNullType.java | 16 +++-- .../runtime/internal/types/BReadonlyType.java | 6 +- .../internal/types/BSemTypeWrapper.java | 68 +++++++++++-------- .../runtime/internal/types/BStringType.java | 26 ++++--- .../runtime/internal/types/ShapeSupplier.java | 2 +- 13 files changed, 140 insertions(+), 108 deletions(-) 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 c3040bbab3d6..66a173b44bbe 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 @@ -37,7 +37,7 @@ * * @since 0.995.0 */ -public class BAnyType extends BSemTypeWrapper implements AnyType { +public class BAnyType extends BSemTypeWrapper implements AnyType { /** * Create a {@code BAnyType} which represents the any type. @@ -45,30 +45,30 @@ public class BAnyType extends BSemTypeWrapper implements AnyType { * @param typeName string name of the type */ public BAnyType(String typeName, Module pkg, boolean readonly) { - super(new BAnyTypeImpl(typeName, pkg, readonly), pickSemType(readonly)); + super(() -> new BAnyTypeImpl(typeName, pkg, readonly), typeName, pickSemType(readonly)); } @Override public Optional getIntersectionType() { - return ((BAnyTypeImpl) this.bType).getIntersectionType(); + return this.getbType().getIntersectionType(); } @Override public void setIntersectionType(IntersectionType intersectionType) { - ((BAnyTypeImpl) this.bType).setIntersectionType(intersectionType); + this.getbType().setIntersectionType(intersectionType); } @Override public Type getReferredType() { - return ((BAnyTypeImpl) this.bType).getReferredType(); + return this.getbType().getReferredType(); } @Override public IntersectionType getImmutableType() { - return ((BAnyTypeImpl) this.bType).getImmutableType(); + return this.getbType().getImmutableType(); } - private static final class BAnyTypeImpl extends BType implements AnyType { + protected static final class BAnyTypeImpl extends BType implements AnyType { private final boolean readonly; private IntersectionType immutableType; 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 bf2cd2c44826..74fcd797e2bc 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 @@ -25,19 +25,21 @@ import io.ballerina.runtime.api.types.semtype.Builder; 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 final class BBooleanType extends BSemTypeWrapper 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), - Builder.booleanConst(true)); + new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + TypeConstants.BOOLEAN_TNAME, Builder.booleanConst(true)); private static final BBooleanType FALSE = - new BBooleanType(new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), - Builder.booleanConst(false)); + new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + TypeConstants.BOOLEAN_TNAME, Builder.booleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. @@ -45,18 +47,18 @@ public final class BBooleanType extends BSemTypeWrapper implements BooleanType { * @param typeName string name of the type */ public BBooleanType(String typeName, Module pkg) { - this(new BBooleanTypeImpl(typeName, pkg), Builder.booleanType()); + this(() -> new BBooleanTypeImpl(typeName, pkg), typeName, Builder.booleanType()); } public static BBooleanType singletonType(boolean value) { return value ? TRUE : FALSE; } - private BBooleanType(BBooleanTypeImpl bType, SemType semType) { - super(bType, semType); + private BBooleanType(Supplier bTypeSupplier, String typeName, SemType semType) { + super(bTypeSupplier, typeName, semType); } - private static final class BBooleanTypeImpl extends BType implements BooleanType { + protected static final class BBooleanTypeImpl extends BType implements BooleanType { private BBooleanTypeImpl(String typeName, Module pkg) { super(typeName, pkg, Boolean.class); 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 bb4fc8ae5045..f1d2f40763d1 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 @@ -25,6 +25,8 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.function.Supplier; + import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; @@ -33,7 +35,7 @@ * * @since 0.995.0 */ -public final class BByteType extends BSemTypeWrapper implements ByteType { +public final class BByteType extends BSemTypeWrapper implements ByteType { private static final BByteTypeImpl DEFAULT_B_TYPE = new BByteTypeImpl(TypeConstants.BYTE_TNAME, EMPTY_MODULE); @@ -43,22 +45,24 @@ public final class BByteType extends BSemTypeWrapper implements ByteType { * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { - this(new BByteTypeImpl(typeName, pkg), Builder.intRange(0, UNSIGNED8_MAX_VALUE)); + this(() -> new BByteTypeImpl(typeName, pkg), typeName, Builder.intRange(0, UNSIGNED8_MAX_VALUE)); } - private BByteType(BByteTypeImpl bType, SemType semType) { - super(bType, semType); + private BByteType(Supplier bTypeSupplier, String typeName, SemType semType) { + super(bTypeSupplier, typeName, semType); } public static BByteType singletonType(long value) { - try { - return new BByteType((BByteTypeImpl) DEFAULT_B_TYPE.clone(), Builder.intConst(value)); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } + return new BByteType(() -> { + try { + return (BByteTypeImpl) DEFAULT_B_TYPE.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + }, TypeConstants.BYTE_TNAME, Builder.intConst(value)); } - private static final class BByteTypeImpl extends BType implements ByteType, Cloneable { + protected static final class BByteTypeImpl extends BType implements ByteType, Cloneable { private BByteTypeImpl(String typeName, Module pkg) { super(typeName, pkg, Integer.class); 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 ca66357278c6..9863ef70b323 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 @@ -27,6 +27,7 @@ import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; +import java.util.function.Supplier; import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; @@ -36,7 +37,7 @@ * * @since 0.995.0 */ -public final class BDecimalType extends BSemTypeWrapper implements DecimalType { +public final class BDecimalType extends BSemTypeWrapper implements DecimalType { private static final BDecimalTypeImpl DEFAULT_B_TYPE = new BDecimalTypeImpl(TypeConstants.DECIMAL_TNAME, EMPTY_MODULE); @@ -47,22 +48,24 @@ public final class BDecimalType extends BSemTypeWrapper implements DecimalType { * @param typeName string name of the type */ public BDecimalType(String typeName, Module pkg) { - this(new BDecimalTypeImpl(typeName, pkg), Builder.decimalType()); + this(() -> new BDecimalTypeImpl(typeName, pkg), typeName, Builder.decimalType()); } public static BDecimalType singletonType(BigDecimal value) { - try { - return new BDecimalType((BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), Builder.decimalConst(value)); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } + return new BDecimalType(() -> { + try { + return (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + }, TypeConstants.DECIMAL_TNAME, Builder.decimalConst(value)); } - private BDecimalType(BDecimalTypeImpl bType, SemType semType) { - super(bType, semType); + private BDecimalType(Supplier bType, String typeName, SemType semType) { + super(bType, typeName, semType); } - private static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { + protected static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { private BDecimalTypeImpl(String typeName, Module pkg) { super(typeName, pkg, DecimalValue.class); 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 a1e268caa9ec..02dc9f336872 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 @@ -131,7 +131,7 @@ public synchronized SemType createSemType() { err = Builder.errorType(); } else { SemType detailType = mutableSemTypeDependencyManager.getSemType(getDetailType(), this); - assert (!Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP))); + assert Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP)); err = ErrorUtils.errorDetail(detailType); } 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 15de4aea83ab..6565df54c6f1 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 @@ -24,6 +24,8 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.function.Supplier; + import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; /** @@ -33,7 +35,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BFloatType extends BSemTypeWrapper implements FloatType { +public class BFloatType extends BSemTypeWrapper implements FloatType { /** * Create a {@code BFloatType} which represents the boolean type. @@ -41,18 +43,19 @@ public class BFloatType extends BSemTypeWrapper implements FloatType { * @param typeName string name of the type */ public BFloatType(String typeName, Module pkg) { - this(new BFloatTypeImpl(typeName, pkg), Builder.floatType()); + this(() -> new BFloatTypeImpl(typeName, pkg), typeName, Builder.floatType()); } - private BFloatType(BFloatTypeImpl bType, SemType semType) { - super(bType, semType); + private BFloatType(Supplier bType, String typeName, SemType semType) { + super(bType, typeName, semType); } public static BFloatType singletonType(Double value) { - return new BFloatType(new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), Builder.floatConst(value)); + return new BFloatType(() -> new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), + TypeConstants.FLOAT_TNAME, Builder.floatConst(value)); } - private static final class BFloatTypeImpl extends BType implements FloatType { + protected static final class BFloatTypeImpl extends BType implements FloatType { private BFloatTypeImpl(String typeName, Module pkg) { super(typeName, pkg, Double.class); 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 b3f1d30d2f43..688e4d79f565 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 @@ -31,7 +31,7 @@ * * @since 1.0.0 */ -public final class BHandleType extends BSemTypeWrapper implements HandleType { +public final class BHandleType extends BSemTypeWrapper implements HandleType { /** * Create a {@code BHandleType} which represents the handle type. @@ -39,10 +39,10 @@ public final class BHandleType extends BSemTypeWrapper implements HandleType { * @param typeName string name of the type */ public BHandleType(String typeName, Module pkg) { - super(BHandleTypeImpl.create(typeName, pkg), Builder.handleType()); + super(() -> BHandleTypeImpl.create(typeName, pkg), typeName, Builder.handleType()); } - private static final class BHandleTypeImpl extends BType implements HandleType { + protected static final class BHandleTypeImpl extends BType implements HandleType { private static final BHandleTypeImpl DEFAULT = new BHandleTypeImpl(TypeConstants.HANDLE_TNAME, PredefinedTypes.EMPTY_MODULE); 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 6a2db9bc2b78..fc662806b36f 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 @@ -25,6 +25,8 @@ import io.ballerina.runtime.api.types.semtype.Builder; 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; @@ -41,7 +43,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public final class BIntegerType extends BSemTypeWrapper implements IntegerType { +public final class BIntegerType extends BSemTypeWrapper implements IntegerType { private static final BIntegerTypeImpl DEFAULT_B_TYPE = new BIntegerTypeImpl(TypeConstants.INT_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.INT_TAG); @@ -52,15 +54,15 @@ public final class BIntegerType extends BSemTypeWrapper implements IntegerType { * @param typeName string name of the type */ public BIntegerType(String typeName, Module pkg) { - this(new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), Builder.intType()); + this(() -> new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, Builder.intType()); } public BIntegerType(String typeName, Module pkg, int tag) { - this(new BIntegerTypeImpl(typeName, pkg, tag), pickSemType(tag)); + this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, pickSemType(tag)); } - private BIntegerType(BIntegerTypeImpl bType, SemType semType) { - super(bType, semType); + private BIntegerType(Supplier bType, String typeName, SemType semType) { + super(bType, typeName, semType); } private static SemType pickSemType(int tag) { @@ -84,14 +86,16 @@ public static BIntegerType singletonType(long value) { } private static BIntegerType createSingletonType(long value) { - try { - return new BIntegerType((BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), Builder.intConst(value)); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } + return new BIntegerType(() -> { + try { + return (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + }, TypeConstants.INT_TNAME, Builder.intConst(value)); } - private static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { + protected static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { private final int 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 c272a5d24ee1..d9ea188612f7 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 @@ -23,12 +23,14 @@ import io.ballerina.runtime.api.types.semtype.Builder; 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 BSemTypeWrapper implements NullType { +public class BNullType extends BSemTypeWrapper implements NullType { /** * Create a {@code BNullType} represents the type of a {@code NullLiteral}. @@ -37,18 +39,18 @@ public class BNullType extends BSemTypeWrapper implements NullType { * @param pkg package path */ public BNullType(String typeName, Module pkg) { - this(new BNullTypeImpl(typeName, pkg), Builder.nilType()); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, Builder.nilType()); } - BNullType(String typeName, Module pkg, SemType semType) { - this(new BNullTypeImpl(typeName, pkg), semType); + protected BNullType(String typeName, Module pkg, SemType semType) { + this(() -> new BNullTypeImpl(typeName, pkg), typeName, semType); } - private BNullType(BNullTypeImpl bNullType, SemType semType) { - super(bNullType, semType); + private BNullType(Supplier bNullType, String typeName, SemType semType) { + super(bNullType, typeName, semType); } - private static final class BNullTypeImpl extends BType implements NullType { + protected static final class BNullTypeImpl extends BType implements NullType { private BNullTypeImpl(String typeName, Module pkg) { super(typeName, pkg, null); 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 baa048df7248..b7cc7deb7c86 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 @@ -28,13 +28,13 @@ * * @since 1.3.0 */ -public class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { +public class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(new BReadonlyTypeImpl(typeName, pkg), Builder.readonlyType()); + super(() -> new BReadonlyTypeImpl(typeName, pkg), typeName, Builder.readonlyType()); } - private static final class BReadonlyTypeImpl extends BType implements ReadonlyType { + protected static final class BReadonlyTypeImpl extends BType implements ReadonlyType { private BReadonlyTypeImpl(String typeName, Module pkg) { super(typeName, pkg, RefValue.class); 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 index 5703cb486983..f0d09c501f0b 100644 --- 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 @@ -24,6 +24,8 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; +import java.util.function.Supplier; + // TODO: make this a sealed class with clearly defined extensions /** @@ -32,39 +34,40 @@ * * @since 2201.10.0 */ -public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { +public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { // FIXME: turn this to a lazy supplier to avoid intialization if not needed - protected final BType bType; + private E bType; + private final Supplier bTypeSupplier; protected final String typeName; // Debugger uses this field to show the type name - BSemTypeWrapper(BType bType, SemType semType) { + BSemTypeWrapper(Supplier bTypeSupplier, String typeName, SemType semType) { super(semType); - this.bType = bType; - this.typeName = bType.typeName; + this.bTypeSupplier = bTypeSupplier; + this.typeName = typeName; } public Class getValueClass() { - return bType.getValueClass(); + return getbType().getValueClass(); } public V getZeroValue() { - return bType.getZeroValue(); + return getbType().getZeroValue(); } @Override public V getEmptyValue() { - return bType.getEmptyValue(); + return getbType().getEmptyValue(); } @Override public int getTag() { - return bType.getTag(); + return getbType().getTag(); } @Override public String toString() { - return bType.toString(); + return getbType().toString(); } @Override @@ -72,98 +75,105 @@ public boolean equals(Object obj) { if (!(obj instanceof BSemTypeWrapper other)) { return false; } - return bType.equals(other.bType); + return getbType().equals(other.getbType()); } @Override public boolean isNilable() { - return bType.isNilable(); + return getbType().isNilable(); } @Override public int hashCode() { - return bType.hashCode(); + return getbType().hashCode(); } @Override public String getName() { - return bType.getName(); + return getbType().getName(); } @Override public String getQualifiedName() { - return bType.getQualifiedName(); + return getbType().getQualifiedName(); } @Override public Module getPackage() { - return bType.getPackage(); + return getbType().getPackage(); } @Override public boolean isPublic() { - return bType.isPublic(); + return getbType().isPublic(); } @Override public boolean isNative() { - return bType.isNative(); + return getbType().isNative(); } // TODO: use semtype @Override public boolean isAnydata() { - return bType.isAnydata(); + return getbType().isAnydata(); } @Override public boolean isPureType() { - return bType.isPureType(); + return getbType().isPureType(); } // TODO: use semtype @Override public boolean isReadOnly() { - return bType.isReadOnly(); + return getbType().isReadOnly(); } @Override public Type getImmutableType() { - return bType.getImmutableType(); + return getbType().getImmutableType(); } @Override public void setImmutableType(IntersectionType immutableType) { - bType.setImmutableType(immutableType); + getbType().setImmutableType(immutableType); } @Override public Module getPkg() { - return bType.getPkg(); + return getbType().getPkg(); } @Override public long getFlags() { - return bType.getFlags(); + return getbType().getFlags(); } @Override public void setCachedReferredType(Type type) { - bType.setCachedReferredType(type); + getbType().setCachedReferredType(type); } @Override public Type getCachedReferredType() { - return bType.getCachedReferredType(); + return getbType().getCachedReferredType(); } @Override public void setCachedImpliedType(Type type) { - bType.setCachedImpliedType(type); + getbType().setCachedImpliedType(type); } @Override public Type getCachedImpliedType() { - return bType.getCachedImpliedType(); + return getbType().getCachedImpliedType(); + } + + protected synchronized E getbType() { + if (bType == null) { + bType = bTypeSupplier.get(); + } + return bType; } } 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 20a7f8d1edbb..7cf561053de3 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 @@ -25,13 +25,15 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.function.Supplier; + /** * {@code BStringType} represents a String type in ballerina. * * @since 0.995.0 */ @SuppressWarnings("unchecked") -public final class BStringType extends BSemTypeWrapper implements StringType { +public final class BStringType extends BSemTypeWrapper implements StringType { // We are creating separate empty module instead of reusing PredefinedTypes.EMPTY_MODULE to avoid cyclic // dependencies. @@ -44,23 +46,25 @@ public final class BStringType extends BSemTypeWrapper implements StringType { * @param typeName string name of the type */ public BStringType(String typeName, Module pkg) { - this(new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), Builder.stringType()); + this(() -> new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, Builder.stringType()); } public BStringType(String typeName, Module pkg, int tag) { - this(new BStringTypeImpl(typeName, pkg, tag), pickSemtype(tag)); + this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, pickSemtype(tag)); } - private BStringType(BStringTypeImpl bType, SemType semType) { - super(bType, semType); + private BStringType(Supplier bType, String typeName, SemType semType) { + super(bType, typeName, semType); } public static BStringType singletonType(String value) { - try { - return new BStringType((BStringTypeImpl) DEFAULT_B_TYPE.clone(), Builder.stringConst(value)); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } + return new BStringType(() -> { + try { + return (BStringTypeImpl) DEFAULT_B_TYPE.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + }, TypeConstants.STRING_TNAME, Builder.stringConst(value)); } private static SemType pickSemtype(int tag) { @@ -71,7 +75,7 @@ private static SemType pickSemtype(int tag) { }; } - private static final class BStringTypeImpl extends BType implements StringType, Cloneable { + protected static final class BStringTypeImpl extends BType implements StringType, Cloneable { private final int tag; 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 index 2f19564dd5c7..d64f98145190 100644 --- 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 @@ -25,7 +25,7 @@ import java.util.Optional; @FunctionalInterface -interface ShapeSupplier { +public interface ShapeSupplier { Optional get(Context cx, Object object); } From 30c867f2aa03934ec5a6f7c9c7f70c72119337ef Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 14 Aug 2024 13:44:11 +0530 Subject: [PATCH 100/178] Cache cell atom creation --- .../runtime/api/types/semtype/Builder.java | 2 +- .../api/types/semtype/CellAtomicType.java | 28 +++++++++++++++++-- .../internal/types/semtype/BCellSubType.java | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) 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 index 4926635d2d07..68f2d12fece5 100644 --- 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 @@ -376,7 +376,7 @@ public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMut } private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { - CellAtomicType atomicCell = new CellAtomicType(ty, mut); + CellAtomicType atomicCell = CellAtomicType.from(ty, mut); TypeAtom atom = env.cellAtom(atomicCell); BddNode bdd = bddAtom(atom); return basicSubType(BT_CELL, BCellSubType.createDelegate(bdd)); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 5ef2f0282a40..dc92f76874b6 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -18,6 +18,9 @@ package io.ballerina.runtime.api.types.semtype; +import java.util.HashMap; +import java.util.Map; + /** * CellAtomicType node. * @@ -32,13 +35,13 @@ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicTy } public static CellAtomicType from(SemType ty, CellMutability mut) { - return new CellAtomicType(ty, 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 new CellAtomicType(ty, mut); + return CellAtomicType.from(ty, mut); } private static CellMutability min(CellMutability m1, @@ -51,4 +54,25 @@ public enum CellMutability { CELL_MUT_LIMITED, CELL_MUT_UNLIMITED } + + private static final class CellAtomCache { + + private final static Map NONE_CACHE = new HashMap<>(); + private final static Map LIMITED_CACHE = new HashMap<>(); + private final static 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/BCellSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java index 951a0d97cb3b..f992a0079dd4 100644 --- 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 @@ -107,7 +107,7 @@ public SubTypeData data() { private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { CellAtomicType combined; if (posList == null) { - combined = new CellAtomicType(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + combined = CellAtomicType.from(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); } else { combined = cellAtomType(posList.atom()); Conjunction p = posList.next(); From e6698d7b1ab627c52aa40aaa4293a374e2bc5c55 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 07:07:35 +0530 Subject: [PATCH 101/178] Optimize mapping subtype check --- .../runtime/internal/types/semtype/BMappingSubType.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 index da267c1ef694..0b3d1503cc89 100644 --- 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 @@ -129,6 +129,15 @@ private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conju 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; From 8910490d24b6feadaa585f982e6501ed7c7045ba Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 08:59:37 +0530 Subject: [PATCH 102/178] Use semtypes for valuesEquals --- .../runtime/api/types/semtype/Builder.java | 4 + .../runtime/internal/TypeChecker.java | 120 +++++------------- 2 files changed, 39 insertions(+), 85 deletions(-) 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 index 68f2d12fece5..3fb4b53977e0 100644 --- 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 @@ -515,6 +515,10 @@ public static SemType simpleOrStringType() { return SIMPLE_OR_STRING; } + public static SemType tableType() { + return from(BasicTypeCode.BT_TABLE); + } + private static final class IntTypeCache { private static final int CACHE_MAX_VALUE = 127; 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 94bcd60e4a7d..6e7b33ecfe01 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 @@ -56,21 +56,14 @@ import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.BTypeReferenceType; import io.ballerina.runtime.internal.types.BUnionType; -import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; -import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.internal.values.HandleValue; -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.TableValueImpl; 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; @@ -124,6 +117,7 @@ public final class TypeChecker { private static final SemType SIMPLE_BASIC_TYPE = createSimpleBasicType(); private static final SemType NUMERIC_TYPE = createNumericType(); private static final SemType INHERENTLY_IMMUTABLE_TYPE = createInherentlyImmutableType(); + private static final SemType REF_TYPE_MASK = createRefValueMask(); private static final byte MAX_TYPECAST_ERROR_COUNT = 20; public static Object checkCast(Object sourceVal, Type targetType) { @@ -436,6 +430,7 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { if (isSimpleBasicSemType(lhsType)) { return isSimpleBasicValuesEqual(lhsValue, rhsValue); } + // Use belong to basic type Predicate basicTypePredicate = (basicType) -> Core.isSubType(cx, lhsType, basicType) && Core.isSubType(cx, rhsType, basicType); if (basicTypePredicate.test(Builder.stringType())) { @@ -808,87 +803,42 @@ public static boolean isEqual(Object lhsValue, Object rhsValue, Set c return false; } - return checkValueEquals(lhsValue, rhsValue, checkedValues, getType(lhsValue), getType(rhsValue)); + return checkValueEqual(lhsValue, rhsValue, new HashSet<>(checkedValues)); } - 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 SemType createRefValueMask() { + return Stream.of(Builder.xmlType(), Builder.mappingType(), Builder.listType(), Builder.errorType(), + Builder.tableType(), Builder.regexType()) + .reduce(Builder.neverType(), Core::union); } - private static boolean isMappingType(int typeTag) { - return typeTag == TypeTags.MAP_TAG || typeTag == TypeTags.RECORD_TYPE_TAG || typeTag == TypeTags.JSON_TAG; + private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { + Context cx = context(); + SemType lhsShape = Builder.shapeOf(cx, lhsValue).orElseThrow(); + SemType rhsShape = Builder.shapeOf(cx, rhsValue).orElseThrow(); + Predicate belongToSameBasicType = (basicType) -> Core.containsBasicType(lhsShape, basicType) && + Core.containsBasicType(rhsShape, basicType); + if (belongToSameBasicType.test(Builder.stringType()) || belongToSameBasicType.test(Builder.booleanType())) { + return lhsValue.equals(rhsValue); + } + if (belongToSameBasicType.test(Builder.intType())) { + // 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.floatType())) { + 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.decimalType())) { + return checkDecimalEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); + } + if (belongToSameBasicType.test(REF_TYPE_MASK)) { + RefValue lhs = (RefValue) lhsValue; + return lhs.equals(rhsValue, checkedValues); + } + return false; } public static boolean isRegExpType(Type targetType) { From 7a693bf472c13574f871b1300038d7bd7f737bcb Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 11:22:55 +0530 Subject: [PATCH 103/178] Use semtypes for casting --- .../runtime/internal/TypeChecker.java | 38 ++++++---- .../runtime/internal/TypeConverter.java | 74 ++++++++++++++----- 2 files changed, 79 insertions(+), 33 deletions(-) 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 6e7b33ecfe01..4f11ed8e5a6e 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 @@ -118,33 +118,32 @@ public final class TypeChecker { private static final SemType NUMERIC_TYPE = createNumericType(); private static final SemType INHERENTLY_IMMUTABLE_TYPE = createInherentlyImmutableType(); private static final SemType REF_TYPE_MASK = createRefValueMask(); + private static final SemType CONVERTIBLE_CAST_MASK = createConvertibleCastMask(); private static final byte MAX_TYPECAST_ERROR_COUNT = 20; public static Object checkCast(Object sourceVal, Type targetType) { List errors = new ArrayList<>(); // TODO: we don't need to do this like this see checkIsType(Object, Type) - 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(sourceType, CONVERTIBLE_CAST_MASK) && + Core.containsBasicType(targetType, 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); } @@ -649,6 +648,7 @@ public static boolean isInherentlyImmutableType(Type sourceType) { Core.isSubType(context(), sourceType, INHERENTLY_IMMUTABLE_TYPE) || sourceType instanceof ReadonlyType; } + // FIXME: public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { if (!unresolvedTypes.add(type)) { return true; @@ -812,6 +812,12 @@ private static SemType createRefValueMask() { .reduce(Builder.neverType(), Core::union); } + private static SemType createConvertibleCastMask() { + return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), + Builder.booleanType()) + .reduce(Builder.neverType(), Core::union); + } + private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { Context cx = context(); SemType lhsShape = Builder.shapeOf(cx, lhsValue).orElseThrow(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index ce4121a8741a..9932157f0b9b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -28,6 +28,10 @@ import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.Type; 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.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.utils.XmlUtils; @@ -43,6 +47,7 @@ import io.ballerina.runtime.internal.errors.ErrorReasons; import io.ballerina.runtime.internal.regexp.RegExpFactory; import io.ballerina.runtime.internal.types.BArrayType; +import io.ballerina.runtime.internal.types.BByteType; import io.ballerina.runtime.internal.types.BFiniteType; import io.ballerina.runtime.internal.types.BIntersectionType; import io.ballerina.runtime.internal.types.BMapType; @@ -65,6 +70,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; import java.util.function.Supplier; import static io.ballerina.runtime.api.PredefinedTypes.TYPE_STRING; @@ -132,28 +138,62 @@ public static Object convertValues(Type targetType, Object inputValue) { }; } + private static Object castValueToInt(SemType targetType, Object inputValue) { + Context cx = TypeChecker.context(); + assert Core.isSubType(cx, targetType, Builder.intType()); + if (targetType instanceof BByteType) { + return anyToByteCast(inputValue, () -> + ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); + } + Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, 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(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.intType())) { + return castValueToInt(targetType, inputValue); + } + if (Core.isSubType(cx, targetType, Builder.decimalType())) { + return anyToDecimalCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_DECIMAL)); - case TypeTags.FLOAT_TAG -> anyToFloatCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.floatType())) { + return anyToFloatCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_FLOAT)); - case TypeTags.STRING_TAG -> anyToStringCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.stringType())) { + return anyToStringCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_STRING)); - case TypeTags.BOOLEAN_TAG -> anyToBooleanCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.booleanType())) { + 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) { From bdf91aa22971bea34708d3a9fe1c12a545e202c9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 12:34:57 +0530 Subject: [PATCH 104/178] Refact shapeOf to support inlining --- .../runtime/api/types/semtype/Builder.java | 62 +------------------ .../ballerina/runtime/api/values/BError.java | 10 +++ .../ballerina/runtime/api/values/BValue.java | 5 ++ .../internal/values/AbstractArrayValue.java | 7 +++ .../internal/values/AbstractObjectValue.java | 10 +++ .../runtime/internal/values/DecimalValue.java | 9 +++ .../runtime/internal/values/FPValue.java | 6 ++ .../runtime/internal/values/MapValueImpl.java | 9 +++ .../runtime/internal/values/RegExpValue.java | 5 ++ .../runtime/internal/values/StringValue.java | 8 +++ .../internal/values/TableValueImpl.java | 11 ++++ .../runtime/internal/values/XmlValue.java | 11 +++- 12 files changed, 93 insertions(+), 60 deletions(-) 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 index 3fb4b53977e0..aafa32a4b762 100644 --- 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 @@ -19,12 +19,7 @@ package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.values.BArray; -import io.ballerina.runtime.api.values.BError; -import io.ballerina.runtime.api.values.BMap; -import io.ballerina.runtime.api.values.BRegexpValue; import io.ballerina.runtime.api.values.BString; -import io.ballerina.runtime.api.values.BTable; import io.ballerina.runtime.api.values.BValue; import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; @@ -40,10 +35,7 @@ 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 io.ballerina.runtime.internal.values.AbstractObjectValue; import io.ballerina.runtime.internal.values.DecimalValue; -import io.ballerina.runtime.internal.values.FPValue; -import io.ballerina.runtime.internal.values.XmlValue; import java.math.BigDecimal; import java.util.ArrayList; @@ -293,10 +285,11 @@ public static Optional readonlyShapeOf(Context cx, Object object) { // TODO: factor this to a separate class public static Optional shapeOf(Context cx, Object object) { + if (object instanceof BValue bValue) { + return bValue.shapeOf(cx); + } if (object == null) { return Optional.of(nilType()); - } else if (object instanceof DecimalValue decimalValue) { - return Optional.of(decimalConst(decimalValue.value())); } else if (object instanceof Double doubleValue) { return Optional.of(floatConst(doubleValue)); } else if (object instanceof Number intValue) { @@ -305,59 +298,10 @@ public static Optional shapeOf(Context cx, Object object) { return Optional.of(intConst(value)); } else if (object instanceof Boolean booleanValue) { return Optional.of(booleanConst(booleanValue)); - } else if (object instanceof BString stringValue) { - return Optional.of(stringConst(stringValue.getValue())); - } else if (object instanceof BArray arrayValue) { - return typeOfArray(cx, arrayValue); - } else if (object instanceof BMap mapValue) { - return typeOfMap(cx, mapValue); - } else if (object instanceof FPValue fpValue) { - return Optional.of(fpValue.getType()); - } else if (object instanceof BError errorValue) { - return typeOfError(cx, errorValue); - } else if (object instanceof AbstractObjectValue objectValue) { - return typeOfObject(cx, objectValue); - } else if (object instanceof XmlValue xmlValue) { - return typeOfXml(cx, xmlValue); - } else if (object instanceof BRegexpValue regexpValue) { - return regexpValue.shapeOf(); - } else if (object instanceof BTable table) { - return typeOfTable(cx, table); } return Optional.empty(); } - private static Optional typeOfTable(Context cx, BTable table) { - TypeWithShape typeWithShape = (TypeWithShape) table.getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, table); - } - - // Combine these methods maybe introduce a marker interface - private static Optional typeOfXml(Context cx, XmlValue xmlValue) { - TypeWithShape typeWithShape = (TypeWithShape) xmlValue.getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, xmlValue); - } - - private static Optional typeOfError(Context cx, BError errorValue) { - TypeWithShape typeWithShape = (TypeWithShape) errorValue.getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, errorValue); - } - - private static Optional typeOfMap(Context cx, BMap mapValue) { - TypeWithShape typeWithShape = (TypeWithShape) mapValue.getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, mapValue); - } - - private static Optional typeOfObject(Context cx, AbstractObjectValue objectValue) { - TypeWithShape typeWithShape = (TypeWithShape) objectValue.getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, objectValue); - } - - private static Optional typeOfArray(Context cx, BArray arrayValue) { - TypeWithShape typeWithShape = (TypeWithShape) arrayValue.getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, arrayValue); - } - public static SemType roCellContaining(Env env, SemType ty) { return cellContaining(env, ty, CELL_MUT_NONE); } 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 47f4e4a6b8bf..e98885565cd0 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,10 +17,14 @@ */ package io.ballerina.runtime.api.values; +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.types.TypeWithShape; import java.io.PrintWriter; import java.util.List; +import java.util.Optional; /** *

@@ -89,4 +93,10 @@ public void printStackTrace(PrintWriter printWriter) { public TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } + + @Override + public Optional shapeOf(Context cx) { + TypeWithShape type = getTypeWithShape(); + return type.shapeOf(cx, Builder::shapeOf, this); + } } 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 4dfb25e75c31..53f353f89346 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Map; +import java.util.Optional; /** *

@@ -64,4 +65,8 @@ default String informalStringValue(BLink parent) { default SemType widenedType(Context cx) { return getType(); } + + default Optional shapeOf(Context cx) { + return Optional.empty(); + } } 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 ea7c3fbe5cff..b52e17203598 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 @@ -32,6 +32,7 @@ import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -331,4 +332,10 @@ public void setReadonlyShapeDefinition(Definition definition) { public void resetReadonlyShapeDefinition() { readonlyAttachedDefinition = null; } + + @Override + public Optional shapeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) getType(); + return typeWithShape.shapeOf(cx, Builder::shapeOf, 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 51ada6d07807..58668e8773e2 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,8 @@ 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.Builder; +import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; @@ -36,10 +38,12 @@ 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 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; @@ -243,4 +247,10 @@ public final SemType shapeOf() { public final void cacheShape(SemType semType) { this.shape = semType; } + + @Override + public Optional shapeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) getType(); + return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + } } 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 436adf97e5eb..96a611d060a0 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,6 +22,9 @@ import io.ballerina.runtime.api.constants.RuntimeConstants; import io.ballerina.runtime.api.creators.ErrorCreator; 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.DecimalValueKind; @@ -35,6 +38,7 @@ import java.math.MathContext; import java.math.RoundingMode; import java.util.Map; +import java.util.Optional; /** *

@@ -484,4 +488,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 shapeOf(Context cx) { + return Optional.of(Builder.decimalConst(value)); + } } 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 be7da5375695..3ea123c0553e 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 @@ -31,6 +31,7 @@ import io.ballerina.runtime.internal.scheduling.Scheduler; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; @@ -136,4 +137,9 @@ public String toString() { public SemType widenedType(Context cx) { return Builder.functionType(); } + + @Override + public Optional shapeOf(Context cx) { + return Optional.of(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 fd76f51682aa..a605880562cb 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 @@ -22,6 +22,8 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Field; 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.Definition; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; @@ -50,6 +52,7 @@ 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 java.io.ByteArrayOutputStream; import java.io.IOException; @@ -752,4 +755,10 @@ public void cacheShape(SemType semType) { public SemType shapeOf() { return shape; } + + @Override + public Optional shapeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + } } 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 fb3eeb851004..d82385580e42 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 @@ -140,4 +140,9 @@ public SemType widenedType(Context cx) { public Optional shapeOf() { return Optional.of(this.shape); } + + @Override + public Optional shapeOf(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 160cb9e69072..778f61452780 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 @@ -18,11 +18,15 @@ package io.ballerina.runtime.internal.values; 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. @@ -102,4 +106,8 @@ public boolean equals(Object str) { return false; } + @Override + public Optional shapeOf(Context cx) { + return Optional.of(Builder.stringConst(getValue())); + } } 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 540f6371e222..ba5d4d40a75c 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 @@ -23,6 +23,9 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.TableType; 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.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -46,6 +49,7 @@ 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 java.util.AbstractMap; import java.util.ArrayList; @@ -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; @@ -906,4 +911,10 @@ public BObject getObjectValue(BString key) { public BArray getArrayValue(BString key) { return (BArray) get(key); } + + @Override + public Optional shapeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + } } 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 23443629ca47..d92dc7feedf8 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.creators.ErrorCreator; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; +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.utils.StringUtils; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BMap; @@ -29,11 +32,12 @@ import io.ballerina.runtime.api.values.BXmlQName; import io.ballerina.runtime.internal.BallerinaXmlSerializer; import io.ballerina.runtime.internal.IteratorUtils; +import io.ballerina.runtime.internal.types.TypeWithShape; import java.io.OutputStream; import java.util.List; import java.util.Map; - +import java.util.Optional; import javax.xml.namespace.QName; import static io.ballerina.runtime.internal.ValueUtils.getTypedescValue; @@ -273,4 +277,9 @@ public Type getIteratorNextReturnType() { return iteratorNextReturnType; } + @Override + public Optional shapeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + } } From 3940add20ad744f0652586ba3600695f2e02bd68 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 19:16:49 +0530 Subject: [PATCH 105/178] Refactor memoSubtypeIsEmpty to make it inline --- .../runtime/api/types/semtype/BddMemo.java | 13 ++++ .../runtime/api/types/semtype/Context.java | 70 +++++++------------ 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java index 30beff46fdb2..71ab2280e617 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java @@ -19,6 +19,7 @@ package io.ballerina.runtime.api.types.semtype; import java.util.Objects; +import java.util.Optional; // TODO: consider moving this to inner as well public final class BddMemo { @@ -53,4 +54,16 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hashCode(isEmpty); } + + public Optional isEmpty() { + return switch (isEmpty) { + case TRUE, CYCLIC -> Optional.of(true); + case FALSE -> Optional.of(false); + case LOOP, PROVISIONAL -> { + isEmpty = Status.LOOP; + yield Optional.of(true); + } + case NULL -> Optional.empty(); + }; + } } 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 index 944ec1bd0cf8..6d9fdec1329e 100644 --- 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 @@ -49,59 +49,43 @@ public static Context from(Env env) { } public boolean memoSubtypeIsEmpty(Map memoTable, BddIsEmptyPredicate isEmptyPredicate, Bdd bdd) { - BddMemo mm = memoTable.get(bdd); - BddMemo m; - if (mm != null) { - switch (mm.isEmpty) { - 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 mm.isEmpty == BddMemo.Status.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.Status.LOOP; - return true; - default: - throw new AssertionError("Unexpected memo status: " + mm.isEmpty); - } - } else { - m = new BddMemo(); - memoTable.put(bdd, m); - } + 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) { 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 (!isEmpty || initStackDepth == 0) { - 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) { - memoStack.get(i).isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.NULL; - } - } - if (memoStack.size() > initStackDepth) { - memoStack.subList(initStackDepth, 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.Status.CYCLIC; - } else { - m.isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.FALSE; - } + 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) { + memoStack.get(i).isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.NULL; + } + } + if (memoStack.size() > initStackDepth) { + memoStack.subList(initStackDepth, 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.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); From 9161308fc647be27756bc83bd7c2153fdde779c7 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 19:17:18 +0530 Subject: [PATCH 106/178] Fix thread safty issue in createSemType --- .../io/ballerina/runtime/internal/types/BArrayType.java | 2 +- .../java/io/ballerina/runtime/internal/types/BMapType.java | 2 +- .../io/ballerina/runtime/internal/types/BNullType.java | 5 ----- .../io/ballerina/runtime/internal/types/BObjectType.java | 7 +------ .../io/ballerina/runtime/internal/types/BRecordType.java | 2 +- .../io/ballerina/runtime/internal/types/BStreamType.java | 2 +- .../io/ballerina/runtime/internal/types/BTupleType.java | 2 +- 7 files changed, 6 insertions(+), 16 deletions(-) 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 ed6db9b37937..8071d218e29d 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 @@ -223,7 +223,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType() { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 b6d577285a81..84175056ff36 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 @@ -185,7 +185,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public SemType createSemType() { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 d9ea188612f7..8598c28169a7 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 @@ -80,10 +80,5 @@ public boolean isNilable() { public boolean isReadOnly() { return true; } - - @Override - public SemType createSemType() { - return Builder.nilType(); - } } } 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 922f96f00c3e..50f1445fb74c 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 @@ -87,8 +87,6 @@ public class BObjectType extends BStructureType implements ObjectType, TypeWithS private boolean resolving; private ObjectDefinition defn; private final Env env = Env.getInstance(); - // TODO: better name - private SemType softSemTypeCache; private DistinctIdSupplier distinctIdSupplier; /** @@ -294,10 +292,7 @@ private static boolean skipField(Set seen, String name) { return !seen.add(name); } - private SemType semTypeInner() { - if (softSemTypeCache != null) { - return softSemTypeCache; - } + private synchronized SemType semTypeInner() { if (defn != null) { return defn.getSemType(env); } 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 baf93922625f..1c2d260afe5b 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 @@ -239,7 +239,7 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul } @Override - public SemType createSemType() { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 01395a162ef1..5f2d74892d56 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 @@ -144,7 +144,7 @@ public boolean equals(Object obj) { } @Override - public SemType createSemType() { + public synchronized SemType createSemType() { if (constraint == null) { return Builder.streamType(); } 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 0177ec3d7b27..d65f598f19d1 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 @@ -318,7 +318,7 @@ public String getAnnotationKey() { } @Override - public SemType createSemType() { + public synchronized SemType createSemType() { if (defn != null) { return defn.getSemType(env); } From 85982c1015965e45ee67bffdfb5fba05a32fff38 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 19:17:28 +0530 Subject: [PATCH 107/178] Cache BType results as well --- .../java/io/ballerina/runtime/internal/types/BType.java | 7 +++++-- .../runtime/internal/types/semtype/ImmutableSemType.java | 3 --- 2 files changed, 5 insertions(+), 5 deletions(-) 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 e41999c1c161..7d8b9d743c98 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 @@ -32,7 +32,9 @@ import io.ballerina.runtime.internal.types.semtype.MutableSemTypeDependencyManager; import io.ballerina.runtime.internal.types.semtype.SubTypeData; +import java.util.Map; import java.util.Objects; +import java.util.WeakHashMap; /** * {@code BType} represents a type in Ballerina. @@ -56,6 +58,7 @@ public abstract class BType implements Type, SubTypeData, MutableSemType { private volatile SemType cachedSemType = null; protected MutableSemTypeDependencyManager mutableSemTypeDependencyManager = MutableSemTypeDependencyManager.getInstance(); + private Map cachedResults = new WeakHashMap<>(); protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -282,12 +285,12 @@ public SubType[] subTypeData() { @Override public CachedResult cachedSubTypeRelation(SemType other) { - return CachedResult.NOT_FOUND; + return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); } @Override public void cacheSubTypeRelation(SemType other, boolean result) { - + cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); } @Override 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 index 760eca1426f7..d8a0df76d62e 100644 --- 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 @@ -128,9 +128,6 @@ public CachedResult cachedSubTypeRelation(SemType other) { if (!shouldCache()) { return CachedResult.NOT_FOUND; } - if (this == other) { - return CachedResult.TRUE; - } return resultCache.getCachedResult(other); } From a9be2110076b2265070383790a31dec75828921f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 15 Aug 2024 19:17:47 +0530 Subject: [PATCH 108/178] Only hold weak references in the mutable semtype dependency manager --- .../MutableSemTypeDependencyManager.java | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java index dd1aafa0420b..269de8fd57b2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java @@ -22,15 +22,17 @@ import io.ballerina.runtime.api.types.semtype.MutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.IdentityHashMap; +import java.util.HashMap; import java.util.List; import java.util.Map; public final class MutableSemTypeDependencyManager { private static final MutableSemTypeDependencyManager INSTANCE = new MutableSemTypeDependencyManager(); - private final Map> dependencies = new IdentityHashMap<>(); + private final Map>> dependencies = new HashMap<>(); public static MutableSemTypeDependencyManager getInstance() { return INSTANCE; @@ -40,11 +42,15 @@ private MutableSemTypeDependencyManager() { } public synchronized void notifyDependenciesToReset(MutableSemType semType) { - List mutableSemTypes = dependencies.get(semType); + MutableSemTypeKey key = MutableSemTypeKey.from(semType); + List> mutableSemTypes = dependencies.get(key); if (mutableSemTypes != null) { - dependencies.remove(semType); - for (MutableSemType mutableSemType : mutableSemTypes) { - mutableSemType.resetSemType(); + dependencies.remove(key); + for (Reference mutableSemType : mutableSemTypes) { + MutableSemType dependent = mutableSemType.get(); + if (dependent != null) { + dependent.resetSemType(); + } } } } @@ -52,10 +58,34 @@ public synchronized void notifyDependenciesToReset(MutableSemType semType) { public synchronized SemType getSemType(Type target, MutableSemType self) { assert target != null; if (target instanceof MutableSemType mutableTarget) { - List dependencies = - this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); - dependencies.add(self); + MutableSemTypeKey key = MutableSemTypeKey.from(mutableTarget); + List> dependencies = + this.dependencies.computeIfAbsent(key, (ignored) -> new ArrayList<>()); + dependencies.add(new WeakReference<>(self)); } return target; } + + private record MutableSemTypeKey(WeakReference semTypeRef) { + + private static MutableSemTypeKey from(MutableSemType semType) { + return new MutableSemTypeKey(new WeakReference<>(semType)); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableSemTypeKey that) { + if (semTypeRef.get() == null || that.semTypeRef().get() == null) { + return false; + } + return semTypeRef.get() == that.semTypeRef.get(); + } + return false; + } + + @Override + public int hashCode() { + return System.identityHashCode(semTypeRef.get()); + } + } } From f3160ad03afade4d28f3ffdbf2cb48daffb23272 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 16 Aug 2024 06:24:56 +0530 Subject: [PATCH 109/178] Make all context memo tables weak hash maps --- .../io/ballerina/runtime/api/types/semtype/Context.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index 6d9fdec1329e..056b36ce12ae 100644 --- 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 @@ -19,10 +19,10 @@ package io.ballerina.runtime.api.types.semtype; import java.util.ArrayList; -import java.util.HashMap; 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, requiring external @@ -35,9 +35,9 @@ 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 HashMap<>(); - public final Map mappingMemo = new HashMap<>(); - public final Map functionMemo = new HashMap<>(); + public final Map listMemo = new WeakHashMap<>(); + public final Map mappingMemo = new WeakHashMap<>(); + public final Map functionMemo = new WeakHashMap<>(); SemType anydataMemo; private Context(Env env) { From b9ec8e946070895a3e9d044027e789b5ac290a54 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 16 Aug 2024 13:20:44 +0530 Subject: [PATCH 110/178] Make atom table hold only weak references --- .../runtime/api/types/semtype/Env.java | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) 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 index fe735c3e21d3..9ce8730169c1 100644 --- 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 @@ -18,11 +18,13 @@ package io.ballerina.runtime.api.types.semtype; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.HashMap; 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; @@ -39,8 +41,7 @@ public final class Env { private static final Env INSTANCE = new Env(); - private final Map atomTable; - private final ReadWriteLock atomTableLock = new ReentrantReadWriteLock(); + private final Map> atomTable; private final ReadWriteLock recListLock = new ReentrantReadWriteLock(); final List recListAtoms; @@ -56,7 +57,7 @@ public final class Env { private final AtomicInteger distinctAtomCount = new AtomicInteger(0); private Env() { - this.atomTable = new HashMap<>(); + this.atomTable = new WeakHashMap<>(); this.recListAtoms = new ArrayList<>(); this.recMappingAtoms = new ArrayList<>(); this.recFunctionAtoms = new ArrayList<>(); @@ -73,29 +74,17 @@ public TypeAtom cellAtom(CellAtomicType atomicType) { } private TypeAtom typeAtom(AtomicType atomicType) { - atomTableLock.readLock().lock(); - try { - TypeAtom ta = this.atomTable.get(atomicType); - if (ta != null) { - return ta; + synchronized (this.atomTable) { + Reference ref = this.atomTable.get(atomicType); + if (ref != null) { + TypeAtom atom = ref.get(); + if (atom != null) { + return atom; + } } - } finally { - atomTableLock.readLock().unlock(); - } - - atomTableLock.writeLock().lock(); - try { - // we are double-checking since there may be 2 trying to add at the same time - TypeAtom ta = this.atomTable.get(atomicType); - if (ta != null) { - return ta; - } else { - TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size() + 1, atomicType); - this.atomTable.put(result.atomicType(), result); - return result; - } - } finally { - atomTableLock.writeLock().unlock(); + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + this.atomTable.put(result.atomicType(), new WeakReference<>(result)); + return result; } } From 3678eb15ccf7e8500fcb0d965cb0c27febea3c2f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 16 Aug 2024 13:26:41 +0530 Subject: [PATCH 111/178] Cache repeated semtype creation --- .../runtime/api/types/semtype/Context.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 index 056b36ce12ae..e171f0a40758 100644 --- 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 @@ -18,10 +18,16 @@ package io.ballerina.runtime.api.types.semtype; +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.internal.types.BFunctionType; +import io.ballerina.runtime.internal.types.BType; +import io.ballerina.runtime.internal.types.semtype.PureSemType; + import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.WeakHashMap; /** @@ -38,6 +44,7 @@ public final class Context { public final Map listMemo = new WeakHashMap<>(); public final Map mappingMemo = new WeakHashMap<>(); public final Map functionMemo = new WeakHashMap<>(); + private final Map computedSemTypes = new WeakHashMap<>(); SemType anydataMemo; private Context(Env env) { @@ -109,4 +116,32 @@ public FunctionAtomicType functionAtomicType(Atom atom) { return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); } } + + public SemType cachedSemType(BType bType) { + return BTypeMemoKey.from(bType) + .map(computedSemTypes::get) + .orElse(null); + } + + public void cacheSemType(BType bType, SemType semType) { + if (semType instanceof PureSemType) { + BTypeMemoKey.from(bType).ifPresent(key -> computedSemTypes.put(key, semType)); + } + } + + private record BTypeMemoKey(String typeName, Module pkg) { + + static Optional from(BType bType) { + // Can have different function (isolation flag etc, with the same name, pkg combination) + if (bType instanceof BFunctionType) { + return Optional.empty(); + } + String name = bType.getName(); + Module module = bType.getPkg(); + if (name == null || name.isEmpty() || module == null) { + return Optional.empty(); + } + return Optional.of(new BTypeMemoKey(bType.getName(), bType.getPkg())); + } + } } From af65f460ee9296972c11f9329989d8f54e4b3b64 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 10:49:50 +0530 Subject: [PATCH 112/178] Introduce simplified cell subtype Cache BddNode for simple cell types Avoid unnecessarily creating Bdds with simple cells --- .../api/types/semtype/BasicTypeCode.java | 2 +- .../runtime/api/types/semtype/BddNode.java | 2 +- .../api/types/semtype/CellAtomicType.java | 4 + .../runtime/api/types/semtype/Core.java | 6 +- .../api/types/semtype/LazySupplier.java | 4 +- .../api/types/semtype/PredefinedTypeEnv.java | 8 +- .../runtime/internal/TypeChecker.java | 4 + .../internal/types/semtype/BCellSubType.java | 210 +++--------------- .../types/semtype/BCellSubTypeImpl.java | 191 ++++++++++++++++ .../types/semtype/BCellSubTypeSimple.java | 133 +++++++++++ .../types/semtype/BMappingSubType.java | 8 +- .../types/semtype/SubtypePairIterator.java | 2 +- 12 files changed, 375 insertions(+), 199 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java 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 index 1b1f8b734eb8..7aebd0dbd9a4 100644 --- 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 @@ -80,7 +80,7 @@ public final class BasicTypeCode { // 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; - static final int VT_MASK = (1 << VT_COUNT) - 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; 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 index b8411879a24e..89139415106f 100644 --- 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 @@ -12,7 +12,7 @@ public static BddNode bddAtom(Atom atom) { return new BddNodeSimple(atom); } - boolean isSimple() { + public boolean isSimple() { return this instanceof BddNodeSimple; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index dc92f76874b6..3bbd7725b3ae 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -49,6 +49,10 @@ private static CellMutability min(CellMutability m1, 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, 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 index 986c2038e425..8935f3a0d058 100644 --- 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 @@ -56,8 +56,8 @@ import static io.ballerina.runtime.api.types.semtype.Builder.listType; import static io.ballerina.runtime.api.types.semtype.Builder.undef; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.api.types.semtype.CellAtomicType.cellAtomType; import static io.ballerina.runtime.api.types.semtype.CellAtomicType.intersectCellAtomicType; -import static io.ballerina.runtime.internal.types.semtype.BCellSubType.cellAtomType; import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; import static io.ballerina.runtime.internal.types.semtype.BMappingProj.mappingMemberTypeInner; @@ -281,9 +281,7 @@ public static boolean isEmpty(Context cx, SemType t) { } for (SubType subType : t.subTypeData()) { assert subType != null : "subtype array must not be sparse"; - if (subType instanceof BSubType) { - continue; - } + assert !(subType instanceof BSubType) : "expect pure semtype"; if (!subType.isEmpty(cx)) { return false; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java index 59d2e5ff0955..76f00e752a98 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java @@ -24,7 +24,7 @@ class ConcurrentLazySupplier implements Supplier { private Supplier initializer; - private E value = null; + private volatile E value = null; ConcurrentLazySupplier(Supplier initializer) { this.initializer = initializer; @@ -50,7 +50,7 @@ public E get() { class ConcurrentLazySupplierWithCallback implements Supplier { - private E value = null; + private volatile E value = null; private Supplier initializer; private Consumer callback; 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 index 2a22d1013e91..48f6027f347a 100644 --- 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 @@ -29,6 +29,7 @@ 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; @@ -51,6 +52,7 @@ 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); @@ -329,7 +331,7 @@ private static BddNode bddSubtypeRo() { public static synchronized PredefinedTypeEnv getInstance() { if (instance == null) { instance = new PredefinedTypeEnv(); - instance.initilize(); + instance.initialize(); } return instance; } @@ -343,7 +345,7 @@ private static Supplier createTypeAtomSupplierF }); } - private void initilize() { + private void initialize() { // Initialize RecAtoms mappingAtomicRO(); listAtomicRO(); @@ -358,6 +360,7 @@ private void initilize() { cellAtomicInner(); listAtomicMappingRO(); cellAtomicInnerRO(); + initialized.set(true); } private void addInitializedCellAtom(CellAtomicType atom) { @@ -559,6 +562,7 @@ SemType readonlyType() { // 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())); 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 4f11ed8e5a6e..2d814801694a 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 @@ -932,6 +932,10 @@ static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object val } } + public static Env getEnv() { + return Env.getInstance(); + } + /** * Type vector of size two, to hold the source and the target types. * 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 index f992a0079dd4..7d191780b1f5 100644 --- 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 @@ -1,206 +1,48 @@ -/* - * 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.Bdd; +import io.ballerina.runtime.api.types.semtype.BddNode; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; -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 io.ballerina.runtime.api.types.semtype.TypeAtom; -import java.util.Objects; -import java.util.function.Predicate; - -// TODO: would making this a child class of say BddNode be faster than making this a delegate -// -- Problem with this is modeling type operations (union, intersect, complement) since parent must return a Cell -// as well - -/** - * Runtime representation of CellSubType. - * - * @since 2201.10.0 - */ -public final class BCellSubType extends SubType implements DelegatedSubType { +public abstract sealed class BCellSubType extends SubType implements DelegatedSubType + permits BCellSubTypeImpl, BCellSubTypeSimple { - public final Bdd inner; - - private BCellSubType(Bdd inner) { - super(inner.isAll(), inner.isNothing()); - this.inner = inner; + public BCellSubType(boolean all, boolean nothing) { + super(all, nothing); } public static BCellSubType createDelegate(SubType inner) { - if (inner instanceof Bdd bdd) { - return new BCellSubType(bdd); - } else if (inner instanceof BCellSubType bCell) { - return new BCellSubType(bCell.inner); - } - throw new IllegalArgumentException("Unexpected inner type"); - } - - public static CellAtomicType cellAtomType(Atom atom) { - return (CellAtomicType) ((TypeAtom) atom).atomicType(); - } - - @Override - public SubType union(SubType other) { - if (!(other instanceof BCellSubType otherCell)) { - throw new IllegalArgumentException("union of different subtypes"); - } - return createDelegate(inner.union(otherCell.inner)); - } - - @Override - public SubType intersect(SubType other) { - if (!(other instanceof BCellSubType otherCell)) { - throw new IllegalArgumentException("intersect of different subtypes"); - } - return createDelegate(inner.intersect(otherCell.inner)); - } - - @Override - public SubType complement() { - return createDelegate(inner.complement()); - } - - @Override - public boolean isEmpty(Context cx) { - return Bdd.bddEvery(cx, inner, null, null, BCellSubType::cellFormulaIsEmpty); - } - - @Override - public SubType diff(SubType other) { - if (!(other instanceof BCellSubType otherCell)) { - throw new IllegalArgumentException("diff of different subtypes"); - } - - return createDelegate(inner.diff(otherCell.inner)); - } - - @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.valType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + 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 { - combined = cellAtomType(posList.atom()); - Conjunction p = posList.next(); - while (p != null) { - combined = CellAtomicType.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; + throw new IllegalArgumentException("Unexpected inner type"); } - 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 (cellAtomType(neg.atom()).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && - Core.isSameType(cx, Builder.valType(), cellAtomType(neg.atom()).ty())) { - return false; - } - neg = neg.next(); + if (!(bdd instanceof BddNode bddNode && bddNode.isSimple())) { + return new BCellSubTypeImpl(bdd); } - SemType negListUnionResult = filteredCellListUnion(negList, - conjunction -> 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; + Atom atom = bddNode.atom(); + if (!(atom instanceof TypeAtom typeAtom)) { + return new BCellSubTypeImpl(bdd); } - 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; + 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.undef()) || ty.some() != 0 || + atomicType.mut() != CellAtomicType.CellMutability.CELL_MUT_LIMITED) { + return new BCellSubTypeImpl(bdd); } - 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.neverType(); - Conjunction neg = negList; - while (neg != null) { - if (predicate.test(neg)) { - negUnion = Core.union(negUnion, 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 BCellSubType other)) { - return false; - } - return Objects.equals(inner, other.inner); - } - - @Override - public int hashCode() { - return Objects.hashCode(inner); + 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..08fbec00b26c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java @@ -0,0 +1,191 @@ +/* + * 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.CellAtomicType; +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; + +// TODO: would making this a child class of say BddNode be faster than making this a delegate +// -- Problem with this is modeling type operations (union, intersect, complement) since parent must return a Cell +// as well + +/** + * Runtime representation of CellSubType. + * + * @since 2201.10.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, null, null, 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.valType(), 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.valType(), 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.neverType(); + 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..03111f388f24 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java @@ -0,0 +1,133 @@ +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.CellAtomicType; +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; + +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.valType(), 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.neverType(), 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/BMappingSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java index 0b3d1503cc89..6ce68e43c76c 100644 --- 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 @@ -62,18 +62,18 @@ public Bdd inner() { @Override public SubType union(SubType other) { - if (!(other instanceof BMappingSubType otherList)) { + if (!(other instanceof BMappingSubType otherMapping)) { throw new IllegalArgumentException("union of different subtypes"); } - return createDelegate(inner.union(otherList.inner)); + return createDelegate(inner.union(otherMapping.inner)); } @Override public SubType intersect(SubType other) { - if (!(other instanceof BMappingSubType otherList)) { + if (!(other instanceof BMappingSubType otherMapping)) { throw new IllegalArgumentException("intersect of different subtypes"); } - return createDelegate(inner.intersect(otherList.inner)); + return createDelegate(inner.intersect(otherMapping.inner)); } @Override 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 index 0acffd7f3a7f..53f24cac960a 100644 --- 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 @@ -60,7 +60,7 @@ private void incrementIndex() { public SubtypePair next() { SubType subType1 = t1.subTypeByCode(index); SubType subType2 = t2.subTypeByCode(index); - assert (subType1 == null || subType2 == null) || (subType1.getClass().equals(subType2.getClass())); + assert (!(subType1 == null && subType2 == null)); int typeCode = index; index++; incrementIndex(); From 8448eefcc8dd44f28263020175dba36ea81869d4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 11:47:58 +0530 Subject: [PATCH 113/178] Avoid caching cell semtypes for complex types --- .../java/io/ballerina/runtime/api/types/semtype/Env.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 index 9ce8730169c1..57e09dec1557 100644 --- 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 @@ -89,10 +89,16 @@ private TypeAtom typeAtom(AtomicType atomicType) { } Optional getCachedCellType(SemType ty, CellAtomicType.CellMutability mut) { + if (ty.some() != 0) { + return Optional.empty(); + } return Optional.ofNullable(this.cellTypeCache.get(new CellSemTypeCacheKey(ty, mut))); } void cacheCellType(SemType ty, CellAtomicType.CellMutability mut, SemType semType) { + if (ty.some() != 0) { + return; + } this.cellTypeCache.put(new CellSemTypeCacheKey(ty, mut), semType); } From d4d768cbf3c23195ba88be22600159486d299a0f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 14:38:12 +0530 Subject: [PATCH 114/178] Avoid unnecessarily checking for shape --- .../runtime/internal/TypeChecker.java | 19 +++++++++++++++++-- .../runtime/internal/types/BArrayType.java | 5 +++++ .../runtime/internal/types/BErrorType.java | 6 ++++++ .../runtime/internal/types/BFutureType.java | 5 +++++ .../internal/types/BIntersectionType.java | 5 +++++ .../runtime/internal/types/BMapType.java | 5 +++++ .../runtime/internal/types/BObjectType.java | 10 ++++++++++ .../runtime/internal/types/BRecordType.java | 8 ++++++++ .../runtime/internal/types/BTableType.java | 5 +++++ .../runtime/internal/types/BTupleType.java | 5 +++++ .../internal/types/BTypeReferenceType.java | 5 +++++ .../runtime/internal/types/BXmlType.java | 5 +++++ .../runtime/internal/types/TypeWithShape.java | 2 ++ 13 files changed, 83 insertions(+), 2 deletions(-) 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 2d814801694a..aab1e141a3e7 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 @@ -56,6 +56,7 @@ import io.ballerina.runtime.internal.types.BType; 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.values.DecimalValue; import io.ballerina.runtime.internal.values.HandleValue; import io.ballerina.runtime.internal.values.RefValue; @@ -273,7 +274,10 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { return true; } SemType sourceSemType = getType(sourceVal); - return Core.isSubType(context(), sourceSemType, targetType) || isSubTypeWithShape(cx, sourceVal, targetType); + if (Core.isSubType(cx, sourceSemType, targetType)) { + return true; + } + return couldShapeBeDifferent(sourceSemType) && isSubTypeWithShape(cx, sourceVal, targetType); } /** @@ -286,7 +290,18 @@ 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) { - return isSubType(sourceType, targetType) || isSubTypeWithShape(context(), sourceVal, targetType); + if (checkIsType(sourceVal, targetType)) { + return true; + } + return couldShapeBeDifferent(sourceType) && isSubTypeWithShape(context(), sourceVal, targetType); + } + + // This is just an optimization since shapes are not cached, when in doubt return false + private static boolean couldShapeBeDifferent(SemType type) { + if (type instanceof TypeWithShape typeWithShape) { + return typeWithShape.couldShapeBeDifferent(); + } + return true; } /** 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 8071d218e29d..92f2c676478e 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 @@ -266,6 +266,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return Optional.of(semType); } + @Override + public boolean couldShapeBeDifferent() { + return isReadOnly(); + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); 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 02dc9f336872..8f00fb529e57 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 @@ -171,4 +171,10 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier } return BMapType.readonlyShape(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); } + + @Override + public boolean couldShapeBeDifferent() { + // TODO: consider properly handling this + return true; + } } 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 b83319825f0d..9c1d4d62b8c7 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 @@ -122,4 +122,9 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { throw new UnsupportedOperationException(); } + + @Override + public boolean couldShapeBeDifferent() { + return false; + } } 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 6bc00cbece84..d6855beadd34 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 @@ -267,4 +267,9 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } return Optional.empty(); } + + @Override + public boolean couldShapeBeDifferent() { + 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 84175056ff36..ccde2e1d99f4 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 @@ -216,6 +216,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return readonlyShape(cx, shapeSupplier, value); } + @Override + public boolean couldShapeBeDifferent() { + return isReadOnly(); + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return readonlyShape(cx, shapeSupplierFn, (BMap) object); 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 50f1445fb74c..60d759864598 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 @@ -363,6 +363,16 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier return Optional.of(valueShape(cx, shapeSupplierFn, (AbstractObjectValue) object)); } + @Override + public boolean couldShapeBeDifferent() { + 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 od = new ObjectDefinition(); List members = new ArrayList<>(); 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 1c2d260afe5b..fd2504a12eeb 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 @@ -354,6 +354,14 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap return semTypePart; } + @Override + public boolean couldShapeBeDifferent() { + if (isReadOnly()) { + return true; + } + return fields.values().stream().anyMatch(field -> SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)); + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { return Optional.of(shapeOfInner(cx, shapeSupplier, (BMap) object, true)); 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 d81971cbb713..6fd9fa80d955 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 @@ -210,6 +210,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return Optional.of(semtype); } + @Override + public boolean couldShapeBeDifferent() { + return isReadOnly(); + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return Optional.of(valueShape(cx, shapeSupplierFn, (BTable) object)); 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 d65f598f19d1..9ee90e75245d 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 @@ -362,6 +362,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return Optional.of(semType); } + @Override + public boolean couldShapeBeDifferent() { + return isReadOnly(); + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); 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 a1331328000b..2eee3dfbde3e 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 @@ -144,6 +144,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return Optional.empty(); } + @Override + public boolean couldShapeBeDifferent() { + return true; + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { Type referredType = getReferredType(); 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 4dda5a9ccd64..5e4d597554cf 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 @@ -199,6 +199,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return readonlyShapeOf(object); } + @Override + public boolean couldShapeBeDifferent() { + return true; + } + @Override public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.readonlyType())); 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 index e978b82c25dc..0a16c7d204c5 100644 --- 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 @@ -30,4 +30,6 @@ public interface TypeWithShape { // Calculate the shape assuming object is readonly. This is the shape of value spec calls looks like shape Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + + boolean couldShapeBeDifferent(); } From 711bf85532c55093e20bedae7823befb81d87988 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 14:40:04 +0530 Subject: [PATCH 115/178] Cache type check results for user defined types --- .../runtime/internal/types/BType.java | 8 +- .../types/semtype/ImmutableSemType.java | 74 +------------------ 2 files changed, 3 insertions(+), 79 deletions(-) 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 7d8b9d743c98..726545b3ee09 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,8 +22,6 @@ import io.ballerina.runtime.api.creators.ErrorCreator; 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.Core; import io.ballerina.runtime.api.types.semtype.MutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; @@ -48,7 +46,6 @@ */ public abstract class BType implements Type, SubTypeData, MutableSemType { - private static final SemType READONLY_WITH_B_TYPE = Core.union(Builder.readonlyType(), Core.B_TYPE_TOP); protected String typeName; protected Module pkg; protected Class valueClass; @@ -58,7 +55,7 @@ public abstract class BType implements Type, SubTypeData, MutableSemType { private volatile SemType cachedSemType = null; protected MutableSemTypeDependencyManager mutableSemTypeDependencyManager = MutableSemTypeDependencyManager.getInstance(); - private Map cachedResults = new WeakHashMap<>(); + private final Map cachedResults = new WeakHashMap<>(); protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -258,9 +255,6 @@ protected SemType getSemType() { return semType; } semType = createSemType(); - if (isReadOnly()) { - semType = Core.intersect(semType, READONLY_WITH_B_TYPE); - } cachedSemType = semType; return semType; } 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 index d8a0df76d62e..16a3135869be 100644 --- 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 @@ -48,17 +48,10 @@ public abstract sealed class ImmutableSemType implements SemType permits BSemTyp private Integer hashCode; - private final TypeCheckResultCache resultCache; - ImmutableSemType(int all, int some, SubType[] subTypeData) { this.all = all; this.some = some; this.subTypeData = subTypeData; - if ((some & CACHEABLE_TYPE_MASK) != 0) { - this.resultCache = new TypeCheckResultCache(); - } else { - this.resultCache = TypeCheckResultCache.EMPTY; - } } ImmutableSemType(int all) { @@ -125,18 +118,12 @@ private boolean shouldCache() { @Override public CachedResult cachedSubTypeRelation(SemType other) { - if (!shouldCache()) { - return CachedResult.NOT_FOUND; - } - return resultCache.getCachedResult(other); + return CachedResult.NOT_FOUND; } @Override public void cacheSubTypeRelation(SemType other, boolean result) { - if (shouldCache()) { - resultCache.cacheResult(other, result); - assert isValidCacheState(other, result) : "Invalid cache state"; - } + return; } private boolean isValidCacheState(SemType other, boolean result) { @@ -154,61 +141,4 @@ public final SubType subTypeByCode(int code) { int some = some() & someMask; return subTypeData()[Integer.bitCount(some)]; } - - private static sealed class TypeCheckResultCache { - - private static final TypeCheckResultCache EMPTY = new EmptyTypeCheckResultCache(); - // make this an int - private final Map cache = new WeakHashMap<>(); - - public void cacheResult(SemType semType, boolean result) { - cache.put(TypeCheckCacheKey.from(semType), result); - } - - public CachedResult getCachedResult(SemType semType) { - Boolean cachedData = cache.get(TypeCheckCacheKey.from(semType)); - if (cachedData == null) { - return CachedResult.NOT_FOUND; - } - return cachedData ? CachedResult.TRUE : CachedResult.FALSE; - } - } - - private static final class EmptyTypeCheckResultCache extends TypeCheckResultCache { - - @Override - public void cacheResult(SemType semType, boolean result) { - throw new UnsupportedOperationException("Empty cache"); - } - - @Override - public CachedResult getCachedResult(SemType semType) { - throw new UnsupportedOperationException("Empty cache"); - } - } - - private record TypeCheckCacheKey(Reference semtype) { - - static TypeCheckCacheKey from(SemType semType) { - return new TypeCheckCacheKey(new WeakReference<>(semType)); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TypeCheckCacheKey other)) { - return false; - } - SemType thisSemType = semtype.get(); - SemType otherSemType = other.semtype.get(); - if (thisSemType == null || otherSemType == null) { - return false; - } - return thisSemType == otherSemType; - } - - @Override - public int hashCode() { - return System.identityHashCode(semtype.get()); - } - } } From 0f1b631ce8fca4c58f5361c25a9c4e8a0d25b272 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 14:41:53 +0530 Subject: [PATCH 116/178] Cache runtime type creation --- .../runtime/api/creators/TypeCreator.java | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) 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..f2edc75fc683 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.WeakHashMap; /** * Class @{@link TypeCreator} provides APIs to create ballerina type instances. @@ -58,6 +59,15 @@ */ public final class TypeCreator { + private static final Map tupleTypeMemo = new WeakHashMap<>(100); + private static final Map mapTypeMemo = new WeakHashMap<>(100); + private static final Map recordTypeMemo = new WeakHashMap<>(100); + private static final Map objectTypeMemo = new WeakHashMap<>(100); + private static final Map streamTypeMemo = new WeakHashMap<>(100); + private static final Map unionTypeMemo = new WeakHashMap<>(100); + private static final Map errorTypeMemo = new WeakHashMap<>(100); + private static final Map xmlTypeMemo = new WeakHashMap<>(100); + private static final Map jsonTypeMemo = new WeakHashMap<>(100); /** * Creates a new array type with given element type. * @@ -163,7 +173,9 @@ public static TupleType createTupleType(List typeList, Type restType, */ public static TupleType createTupleType(String name, Module pkg, int typeFlags, boolean isCyclic, boolean readonly) { - return new BTupleType(name, pkg, typeFlags, isCyclic, readonly); + TypeMemoKey key = new TypeMemoKey(name, pkg); + return tupleTypeMemo.computeIfAbsent(key, + (ignored) -> new BTupleType(name, pkg, typeFlags, isCyclic, readonly)); } /** @@ -196,7 +208,8 @@ public static MapType createMapType(Type constraint, boolean readonly) { * @return the new map type */ public static MapType createMapType(String typeName, Type constraint, Module module) { - return new BMapType(typeName, constraint, module); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return mapTypeMemo.computeIfAbsent(key, (ignored) -> new BMapType(typeName, constraint, module)); } /** @@ -209,7 +222,8 @@ public static MapType createMapType(String typeName, Type constraint, Module mod * @return the new map type */ public static MapType createMapType(String typeName, Type constraint, Module module, boolean readonly) { - return new BMapType(typeName, constraint, module, readonly); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return mapTypeMemo.computeIfAbsent(key, (ignored) -> new BMapType(typeName, constraint, module, readonly)); } /** @@ -224,7 +238,9 @@ public static MapType createMapType(String typeName, Type constraint, Module mod */ public static RecordType createRecordType(String typeName, Module module, long flags, boolean sealed, int typeFlags) { - return new BRecordType(typeName, typeName, module, flags, sealed, typeFlags); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return recordTypeMemo.computeIfAbsent(key, + (ignored) -> new BRecordType(typeName, typeName, module, flags, sealed, typeFlags)); } /** @@ -242,7 +258,9 @@ public static RecordType createRecordType(String typeName, Module module, long f public static RecordType createRecordType(String typeName, Module module, long flags, Map fields, Type restFieldType, boolean sealed, int typeFlags) { - return new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return recordTypeMemo.computeIfAbsent(key, + (ignored) -> new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags)); } /** @@ -254,7 +272,8 @@ public static RecordType createRecordType(String typeName, Module module, long f * @return the new object type */ public static ObjectType createObjectType(String typeName, Module module, long flags) { - return new BObjectType(typeName, module, flags); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return objectTypeMemo.computeIfAbsent(key, (ignored) -> new BObjectType(typeName, module, flags)); } /** @@ -279,7 +298,9 @@ public static StreamType createStreamType(Type constraint, Type completionType) */ public static StreamType createStreamType(String typeName, Type constraint, Type completionType, Module modulePath) { - return new BStreamType(typeName, constraint, completionType, modulePath); + TypeMemoKey key = new TypeMemoKey(typeName, modulePath); + return streamTypeMemo.computeIfAbsent(key, + (ignored) -> new BStreamType(typeName, constraint, completionType, modulePath)); } /** @@ -305,7 +326,9 @@ public static StreamType createStreamType(Type constraint) { */ @Deprecated public static StreamType createStreamType(String typeName, Type completionType, Module modulePath) { - return new BStreamType(typeName, completionType, modulePath); + TypeMemoKey key = new TypeMemoKey(typeName, modulePath); + return streamTypeMemo.computeIfAbsent(key, + (ignored) -> new BStreamType(typeName, completionType, modulePath)); } /** @@ -375,7 +398,9 @@ public static UnionType createUnionType(List memberTypes, int typeFlags, b */ public static UnionType createUnionType(List memberTypes, String name, Module pkg, int typeFlags, boolean isCyclic, long flags) { - return new BUnionType(memberTypes, name, pkg, typeFlags, isCyclic, flags); + TypeMemoKey key = new TypeMemoKey(name, pkg); + return unionTypeMemo.computeIfAbsent(key, + (ignored) -> new BUnionType(memberTypes, name, pkg, typeFlags, isCyclic, flags)); } /** @@ -386,7 +411,8 @@ public static UnionType createUnionType(List memberTypes, String name, Mod * @return the new error type */ public static ErrorType createErrorType(String typeName, Module module) { - return new BErrorType(typeName, module); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return errorTypeMemo.computeIfAbsent(key, (ignored) -> new BErrorType(typeName, module)); } /** @@ -398,7 +424,8 @@ public static ErrorType createErrorType(String typeName, Module module) { * @return the new error type */ public static ErrorType createErrorType(String typeName, Module module, Type detailType) { - return new BErrorType(typeName, module, detailType); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return errorTypeMemo.computeIfAbsent(key, (ignored) -> new BErrorType(typeName, module, detailType)); } /** @@ -457,7 +484,8 @@ public static TableType createTableType(Type constraint, boolean readonly) { * @return new xml type */ public static XmlType createXMLType(String typeName, Type constraint, Module module) { - return new BXmlType(typeName, constraint, module); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return xmlTypeMemo.computeIfAbsent(key, (ignored) -> new BXmlType(typeName, constraint, module)); } /** @@ -470,7 +498,8 @@ public static XmlType createXMLType(String typeName, Type constraint, Module mod * @return new xml type */ public static XmlType createXMLType(String typeName, Module module, int tag, boolean readonly) { - return new BXmlType(typeName, module, tag, readonly); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return xmlTypeMemo.computeIfAbsent(key, (ignored) -> new BXmlType(typeName, module, tag, readonly)); } /** @@ -493,7 +522,8 @@ public static XmlType createXMLType(Type constraint, boolean readonly) { * @return new xml type */ public static JsonType createJSONType(String typeName, Module module, boolean readonly) { - return new BJsonType(typeName, module, readonly); + TypeMemoKey key = new TypeMemoKey(typeName, module); + return jsonTypeMemo.computeIfAbsent(key, (ignored) -> new BJsonType(typeName, module, readonly)); } /** @@ -520,4 +550,8 @@ public static FiniteType createFiniteType(String typeName, Set values, i private TypeCreator() { } + + private record TypeMemoKey(String typeName, Module module) { + + } } From 4ff0e2fd4599ea304e4c3106e447d58a8d2fa36a Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 14:43:50 +0530 Subject: [PATCH 117/178] Remove unwanted caching logic --- .../runtime/api/types/semtype/Context.java | 35 ------------------- 1 file changed, 35 deletions(-) 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 index e171f0a40758..056b36ce12ae 100644 --- 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 @@ -18,16 +18,10 @@ package io.ballerina.runtime.api.types.semtype; -import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.internal.types.BFunctionType; -import io.ballerina.runtime.internal.types.BType; -import io.ballerina.runtime.internal.types.semtype.PureSemType; - import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.WeakHashMap; /** @@ -44,7 +38,6 @@ public final class Context { public final Map listMemo = new WeakHashMap<>(); public final Map mappingMemo = new WeakHashMap<>(); public final Map functionMemo = new WeakHashMap<>(); - private final Map computedSemTypes = new WeakHashMap<>(); SemType anydataMemo; private Context(Env env) { @@ -116,32 +109,4 @@ public FunctionAtomicType functionAtomicType(Atom atom) { return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); } } - - public SemType cachedSemType(BType bType) { - return BTypeMemoKey.from(bType) - .map(computedSemTypes::get) - .orElse(null); - } - - public void cacheSemType(BType bType, SemType semType) { - if (semType instanceof PureSemType) { - BTypeMemoKey.from(bType).ifPresent(key -> computedSemTypes.put(key, semType)); - } - } - - private record BTypeMemoKey(String typeName, Module pkg) { - - static Optional from(BType bType) { - // Can have different function (isolation flag etc, with the same name, pkg combination) - if (bType instanceof BFunctionType) { - return Optional.empty(); - } - String name = bType.getName(); - Module module = bType.getPkg(); - if (name == null || name.isEmpty() || module == null) { - return Optional.empty(); - } - return Optional.of(new BTypeMemoKey(bType.getName(), bType.getPkg())); - } - } } From 1f5141de0c4e13e7484db3ff96c9bea4898d3f64 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 16:33:15 +0530 Subject: [PATCH 118/178] Cleanup access to context --- .../java/io/ballerina/runtime/internal/TypeChecker.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 aab1e141a3e7..074947ea94f2 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 @@ -290,10 +290,7 @@ 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, targetType)) { - return true; - } - return couldShapeBeDifferent(sourceType) && isSubTypeWithShape(context(), sourceVal, targetType); + return checkIsType(sourceVal, targetType); } // This is just an optimization since shapes are not cached, when in doubt return false @@ -329,7 +326,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); SemType targetSemType = targetType; - if (Core.isSubType(context(), shape, NUMERIC_TYPE) && allowNumericConversion) { + if (Core.isSubType(cx, shape, NUMERIC_TYPE) && allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); } return Core.isSubType(cx, shape, targetSemType); @@ -618,7 +615,7 @@ static boolean isByteLiteral(long longValue) { private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { return Builder.shapeOf(cx, sourceValue) - .map(source -> Core.isSubType(context(), source, target)) + .map(source -> Core.isSubType(cx, source, target)) .orElse(false); } From 3787c4916fb46db2b46c60b4001e188fae8b8892 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 18 Aug 2024 17:43:34 +0530 Subject: [PATCH 119/178] Refactor anydata type creation --- .../runtime/api/types/semtype/Builder.java | 31 ++++++++++--------- .../runtime/internal/types/BAnydataType.java | 2 +- .../port/test/RuntimeSemTypeResolver.java | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) 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 index aafa32a4b762..4fafce406ae0 100644 --- 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 @@ -87,6 +87,20 @@ public final class Builder { private static final ConcurrentLazySupplier MAPPING_RO = new ConcurrentLazySupplier<>(() -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())) ); + 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 = + unionOf(simpleOrStringType(), xmlType(), listDef.getSemType(env), mapDef.getSemType(env), + tableTy); + 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(readonlyType(), inner())); @@ -390,21 +404,8 @@ public static SemType streamType() { return from(BasicTypeCode.BT_STREAM); } - public static SemType anyDataType(Context context) { - SemType memo = context.anydataMemo; - if (memo != null) { - return memo; - } - Env env = context.env; - ListDefinition listDef = new ListDefinition(); - MappingDefinition mapDef = new MappingDefinition(); - SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); - SemType accum = - unionOf(simpleOrStringType(), xmlType(), listDef.getSemType(env), mapDef.getSemType(env), tableTy); - listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); - mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); - context.anydataMemo = accum; - return accum; + public static SemType anyDataType() { + return ANYDATA.get(); } private static SemType unionOf(SemType... types) { 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 1925556d274e..707106ce617b 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 @@ -95,7 +95,7 @@ public String toString() { @Override public SemType createSemType() { Context cx = TypeChecker.context(); - SemType semType = Builder.anyDataType(cx); + SemType semType = Builder.anyDataType(); if (isReadOnly()) { semType = Core.intersect(semType, Builder.readonlyType()); } 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 index 2fede7beb4ec..d3e9124f0e51 100644 --- 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 @@ -368,7 +368,7 @@ private SemType resolveRecordTypeDesc(TypeTestContext cx, Map cx, BLangValueType td) case STRING -> Builder.stringType(); case READONLY -> Builder.readonlyType(); case ANY -> Builder.anyType(); - case ANYDATA -> Builder.anyDataType((Context) cx.getInnerContext()); + case ANYDATA -> Builder.anyDataType(); case ERROR -> Builder.errorType(); case XML -> Builder.xmlType(); case HANDLE -> Builder.handleType(); From b572b4cc2e73d6b5e4ba06354853655e9b2496a4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 19 Aug 2024 08:36:53 +0530 Subject: [PATCH 120/178] Use a simple weak map for dependency manager --- .../runtime/internal/types/BArrayType.java | 5 ++- .../runtime/internal/types/BFiniteType.java | 15 ++++++- .../runtime/internal/types/BUnionType.java | 4 +- .../MutableSemTypeDependencyManager.java | 43 ++++--------------- 4 files changed, 29 insertions(+), 38 deletions(-) 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 92f2c676478e..45600f24287d 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 @@ -100,10 +100,11 @@ 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; - defn = null; - resetSemType(); } private void setFlagsBasedOnElementType() { 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 90d1ce1ec790..27e20a38c92c 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 @@ -204,7 +204,20 @@ 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 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 479ff7ea6b91..2f797dbf38c6 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 @@ -186,7 +186,9 @@ private void setMemberTypes(List members) { } private void setMemberTypes(List members, List originalMembers) { - resetSemType(); + if (memberTypes != null) { + resetSemType(); + } if (members == null) { return; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java index 269de8fd57b2..ace9d9165530 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java @@ -25,14 +25,14 @@ import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; public final class MutableSemTypeDependencyManager { private static final MutableSemTypeDependencyManager INSTANCE = new MutableSemTypeDependencyManager(); - private final Map>> dependencies = new HashMap<>(); + private final Map>> dependencies = new WeakHashMap<>(); public static MutableSemTypeDependencyManager getInstance() { return INSTANCE; @@ -42,14 +42,13 @@ private MutableSemTypeDependencyManager() { } public synchronized void notifyDependenciesToReset(MutableSemType semType) { - MutableSemTypeKey key = MutableSemTypeKey.from(semType); - List> mutableSemTypes = dependencies.get(key); + List> mutableSemTypes = dependencies.get(semType); if (mutableSemTypes != null) { - dependencies.remove(key); - for (Reference mutableSemType : mutableSemTypes) { - MutableSemType dependent = mutableSemType.get(); - if (dependent != null) { - dependent.resetSemType(); + dependencies.remove(semType); + for (var dependent : mutableSemTypes) { + MutableSemType dependentSemType = dependent.get(); + if (dependentSemType != null) { + dependentSemType.resetSemType(); } } } @@ -58,34 +57,10 @@ public synchronized void notifyDependenciesToReset(MutableSemType semType) { public synchronized SemType getSemType(Type target, MutableSemType self) { assert target != null; if (target instanceof MutableSemType mutableTarget) { - MutableSemTypeKey key = MutableSemTypeKey.from(mutableTarget); List> dependencies = - this.dependencies.computeIfAbsent(key, (ignored) -> new ArrayList<>()); + this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); dependencies.add(new WeakReference<>(self)); } return target; } - - private record MutableSemTypeKey(WeakReference semTypeRef) { - - private static MutableSemTypeKey from(MutableSemType semType) { - return new MutableSemTypeKey(new WeakReference<>(semType)); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof MutableSemTypeKey that) { - if (semTypeRef.get() == null || that.semTypeRef().get() == null) { - return false; - } - return semTypeRef.get() == that.semTypeRef.get(); - } - return false; - } - - @Override - public int hashCode() { - return System.identityHashCode(semTypeRef.get()); - } - } } From 904aa455c926a6e5f958a4c301f4e41829178014 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 19 Aug 2024 09:24:22 +0530 Subject: [PATCH 121/178] Introduce copy of write BMapType --- .../runtime/api/creators/TypeCreator.java | 7 +- .../runtime/internal/JsonInternalUtils.java | 3 +- .../ballerina/runtime/internal/MapUtils.java | 6 +- .../runtime/internal/TypeChecker.java | 4 +- .../runtime/internal/TypeConverter.java | 5 +- .../runtime/internal/types/BByteType.java | 16 +- .../runtime/internal/types/BDecimalType.java | 16 +- .../runtime/internal/types/BIntegerType.java | 16 +- .../runtime/internal/types/BMapType.java | 9 +- .../runtime/internal/types/BStringType.java | 16 +- .../runtime/internal/types/BType.java | 22 ++- .../runtime/internal/types/BUnionType.java | 5 +- .../types/CopyOnWriteBMapWrapper.java | 166 ++++++++++++++++++ .../runtime/internal/values/MapValueImpl.java | 8 +- .../internal/values/ReadOnlyUtils.java | 3 +- .../internal/values/TableValueImpl.java | 6 +- .../langlib/test/LangLibMapTest.java | 8 +- .../langlib/test/LangLibRecordTest.java | 8 +- .../debugadapter/runtime/VariableUtils.java | 4 +- .../jvm/runtime/api/tests/TypeReference.java | 4 +- .../AnydataStampInbuiltFunctionTest.java | 11 +- .../stamp/ArrayStampInbuiltFunctionTest.java | 9 +- 22 files changed, 258 insertions(+), 94 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java 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 f2edc75fc683..11ca73e2052c 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 @@ -45,8 +45,10 @@ import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BUnionType; import io.ballerina.runtime.internal.types.BXmlType; +import io.ballerina.runtime.internal.types.CopyOnWriteBMapWrapper; import java.util.Arrays; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -68,6 +70,8 @@ public final class TypeCreator { private static final Map errorTypeMemo = new WeakHashMap<>(100); private static final Map xmlTypeMemo = new WeakHashMap<>(100); private static final Map jsonTypeMemo = new WeakHashMap<>(100); + + private static final Map mapTypeWrapperMemo = new IdentityHashMap<>(); /** * Creates a new array type with given element type. * @@ -185,7 +189,8 @@ public static TupleType createTupleType(String name, Module pkg, * @return the new map type */ public static MapType createMapType(Type constraint) { - return new BMapType(constraint); + return mapTypeWrapperMemo.computeIfAbsent(constraint, + (ignored) -> new CopyOnWriteBMapWrapper(new BMapType(constraint))); } /** diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java index a508ac4b5991..5d0c3f84aba3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java @@ -41,7 +41,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; @@ -362,7 +361,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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java index 2b062de290e8..59bc71da1be9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java @@ -21,13 +21,13 @@ 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.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BString; 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; @@ -55,7 +55,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; } @@ -151,7 +151,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/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 074947ea94f2..f88898b1bba9 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 @@ -23,6 +23,7 @@ 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.MapType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.ReadonlyType; @@ -48,7 +49,6 @@ import io.ballerina.runtime.internal.types.BFloatType; import io.ballerina.runtime.internal.types.BIntegerType; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BObjectType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTableType; @@ -730,7 +730,7 @@ public static boolean isSelectivelyImmutableType(Type type, Set unresolved } return true; case TypeTags.MAP_TAG: - Type constraintType = ((BMapType) type).getConstrainedType(); + Type constraintType = ((MapType) type).getConstrainedType(); return isInherentlyImmutableType(constraintType) || isSelectivelyImmutableType(constraintType, unresolvedTypes); case TypeTags.TABLE_TAG: diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 9932157f0b9b..4262def93ff7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.FiniteType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; @@ -305,7 +306,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; } @@ -616,7 +617,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/types/BByteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java index f1d2f40763d1..661f1aac6844 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 @@ -53,13 +53,8 @@ private BByteType(Supplier bTypeSupplier, String typeName, SemTyp } public static BByteType singletonType(long value) { - return new BByteType(() -> { - try { - return (BByteTypeImpl) DEFAULT_B_TYPE.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - }, TypeConstants.BYTE_TNAME, Builder.intConst(value)); + return new BByteType(() -> (BByteTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.BYTE_TNAME, + Builder.intConst(value)); } protected static final class BByteTypeImpl extends BType implements ByteType, Cloneable { @@ -91,11 +86,8 @@ public boolean isReadOnly() { } @Override - protected Object clone() throws CloneNotSupportedException { - BType bType = (BType) super.clone(); - bType.setCachedImpliedType(null); - bType.setCachedReferredType(null); - return bType; + 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 9863ef70b323..44925f50eaef 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 @@ -52,13 +52,8 @@ public BDecimalType(String typeName, Module pkg) { } public static BDecimalType singletonType(BigDecimal value) { - return new BDecimalType(() -> { - try { - return (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - }, TypeConstants.DECIMAL_TNAME, Builder.decimalConst(value)); + return new BDecimalType(() -> (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.DECIMAL_TNAME, + Builder.decimalConst(value)); } private BDecimalType(Supplier bType, String typeName, SemType semType) { @@ -94,11 +89,8 @@ public boolean isReadOnly() { } @Override - protected Object clone() throws CloneNotSupportedException { - BType bType = (BType) super.clone(); - bType.setCachedImpliedType(null); - bType.setCachedReferredType(null); - return bType; + public BType clone() { + return super.clone(); } } } 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 fc662806b36f..4c305a3d63a2 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 @@ -86,13 +86,8 @@ public static BIntegerType singletonType(long value) { } private static BIntegerType createSingletonType(long value) { - return new BIntegerType(() -> { - try { - return (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - }, TypeConstants.INT_TNAME, Builder.intConst(value)); + return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, + Builder.intConst(value)); } protected static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { @@ -125,11 +120,8 @@ public boolean isReadOnly() { } @Override - protected Object clone() throws CloneNotSupportedException { - BType bType = (BType) super.clone(); - bType.setCachedImpliedType(null); - bType.setCachedReferredType(null); - return bType; + public BType clone() { + return super.clone(); } } 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 ccde2e1d99f4..9b59a18c0dbe 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 @@ -53,7 +53,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BMapType extends BType implements MapType, TypeWithShape { +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; @@ -256,4 +256,11 @@ private SemType getSemTypePart(MappingDefinition defn, SemType restType) { CellAtomicType.CellMutability.CELL_MUT_LIMITED; return defn.defineMappingTypeWrapped(env, EMPTY_FIELD_ARR, restType, mut); } + + @Override + public BMapType clone() { + BMapType clone = (BMapType) super.clone(); + clone.defn = null; + return clone; + } } 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 7cf561053de3..0b7d6a9eeebb 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 @@ -58,13 +58,8 @@ private BStringType(Supplier bType, String typeName, SemType se } public static BStringType singletonType(String value) { - return new BStringType(() -> { - try { - return (BStringTypeImpl) DEFAULT_B_TYPE.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - }, TypeConstants.STRING_TNAME, Builder.stringConst(value)); + return new BStringType(() -> (BStringTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.STRING_TNAME, + Builder.stringConst(value)); } private static SemType pickSemtype(int tag) { @@ -105,11 +100,8 @@ public boolean isReadOnly() { } @Override - protected Object clone() throws CloneNotSupportedException { - BType bType = (BType) super.clone(); - bType.setCachedImpliedType(null); - bType.setCachedReferredType(null); - return bType; + public BType clone() { + return super.clone(); } } } 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 726545b3ee09..bda057298c4f 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 @@ -44,18 +44,18 @@ * * @since 0.995.0 */ -public abstract class BType implements Type, SubTypeData, MutableSemType { +public abstract class BType implements Type, SubTypeData, MutableSemType, Cloneable { protected String typeName; protected Module pkg; protected Class valueClass; + protected MutableSemTypeDependencyManager mutableSemTypeDependencyManager = + MutableSemTypeDependencyManager.getInstance(); private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; - protected MutableSemTypeDependencyManager mutableSemTypeDependencyManager = - MutableSemTypeDependencyManager.getInstance(); - private final Map cachedResults = new WeakHashMap<>(); + private Map cachedResults = new WeakHashMap<>(); protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -298,4 +298,18 @@ public void resetSemType() { cachedSemType = null; mutableSemTypeDependencyManager.notifyDependenciesToReset(this); } + + @Override + public BType clone() { + try { + BType clone = (BType) super.clone(); + clone.cachedResults = null; + clone.cachedSemType = null; + clone.setCachedImpliedType(null); + clone.setCachedReferredType(null); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } } 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 2f797dbf38c6..31424c18751d 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 @@ -22,6 +22,7 @@ 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.UnionType; @@ -452,7 +453,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); @@ -463,7 +464,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, diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java new file mode 100644 index 000000000000..80c69a93ef96 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java @@ -0,0 +1,166 @@ +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.MapType; +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.types.semtype.SubType; + +import java.util.Optional; + +public final class CopyOnWriteBMapWrapper implements MapType, TypeWithShape { + + private BMapType inner; + + public CopyOnWriteBMapWrapper(BMapType inner) { + this.inner = inner; + } + + private void copyOnWrite() { + inner = inner.clone(); + } + + @Override + public Type getConstrainedType() { + return inner.getConstrainedType(); + } + + @Override + public V getZeroValue() { + return inner.getZeroValue(); + } + + @Override + public V getEmptyValue() { + return inner.getEmptyValue(); + } + + @Override + public int getTag() { + return inner.getTag(); + } + + @Override + public boolean isNilable() { + return inner.isNilable(); + } + + @Override + public String getName() { + return inner.getName(); + } + + @Override + public String getQualifiedName() { + return inner.getQualifiedName(); + } + + @Override + public Module getPackage() { + return inner.getPackage(); + } + + @Override + public boolean isPublic() { + return inner.isPublic(); + } + + @Override + public boolean isNative() { + return inner.isNative(); + } + + @Override + public boolean isAnydata() { + return inner.isAnydata(); + } + + @Override + public boolean isPureType() { + return inner.isPureType(); + } + + @Override + public boolean isReadOnly() { + return inner.isReadOnly(); + } + + @Override + public long getFlags() { + return inner.getFlags(); + } + + @Override + public IntersectionType getImmutableType() { + return inner.getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + copyOnWrite(); + inner.setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return inner.getPkg(); + } + + @Override + public Optional getIntersectionType() { + return inner.getIntersectionType(); + } + + @Override + public void setIntersectionType(IntersectionType intersectionType) { + copyOnWrite(); + inner.setImmutableType(intersectionType); + } + + @Override + public int all() { + return inner.all(); + } + + @Override + public int some() { + return inner.some(); + } + + @Override + public SubType[] subTypeData() { + return inner.subTypeData(); + } + + @Override + public CachedResult cachedSubTypeRelation(SemType other) { + return inner.cachedSubTypeRelation(other); + } + + @Override + public void cacheSubTypeRelation(SemType other, boolean result) { + inner.cacheSubTypeRelation(other, result); + } + + @Override + public SubType subTypeByCode(int code) { + return inner.subTypeByCode(code); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return inner.shapeOf(cx, shapeSupplierFn, object); + } + + @Override + public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return inner.readonlyShapeOf(cx, shapeSupplierFn, object); + } + + @Override + public boolean couldShapeBeDifferent() { + return inner.couldShapeBeDifferent(); + } +} 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 a605880562cb..f32e0ba70f66 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 @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.TypeTags; 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.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Context; @@ -48,7 +49,6 @@ import io.ballerina.runtime.internal.errors.ErrorHelper; 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; @@ -241,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)) { @@ -355,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; @@ -711,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; 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 59626879b9ea..bfe5230ac39d 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 @@ -28,6 +28,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.ReferenceType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; import io.ballerina.runtime.api.types.Type; @@ -212,7 +213,7 @@ private static BIntersectionType setImmutableIntersectionType(Type type, Set 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); @@ -795,7 +795,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); 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 dc67b87400f6..bdd684dc420e 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 @@ -21,11 +21,11 @@ import io.ballerina.runtime.api.TypeTags; 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.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 cc007f3cdb9e..f049d8220958 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 @@ -19,13 +19,13 @@ package org.ballerinalang.langlib.test; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; 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.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/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/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 7062413ebe9b..7fd3fa4c893d 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 @@ -19,6 +19,7 @@ import io.ballerina.runtime.api.TypeTags; 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/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java index af90b284548c..6be94125a519 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 @@ -18,6 +18,7 @@ package org.ballerinalang.test.expressions.stamp; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BMap; @@ -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 d7ba05008d41..779ed550d946 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 @@ -18,6 +18,7 @@ package org.ballerinalang.test.expressions.stamp; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BMap; @@ -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 From 1bc0cfda263ddd0c2b760077a4cfabecfb7a6021 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 19 Aug 2024 10:26:07 +0530 Subject: [PATCH 122/178] Simplify getType --- .../runtime/api/types/semtype/Core.java | 2 +- .../runtime/internal/TypeChecker.java | 18 +++++++++++------- .../runtime/internal/values/DecimalValue.java | 5 +++++ 3 files changed, 17 insertions(+), 8 deletions(-) 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 index 8935f3a0d058..98a558224d13 100644 --- 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 @@ -368,7 +368,7 @@ public static SemType widenToBasicTypeUnion(SemType t) { return t; } int all = t.all() | t.some(); - return Builder.basicTypeUnion(all); + return SemType.from(all); } public static SemType cellContainingInnerVal(Env env, SemType t) { 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 f88898b1bba9..7514c5b36d8f 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 @@ -358,16 +358,20 @@ public static boolean isSameType(Type sourceType, Type targetType) { } public static Type getType(Object value) { + if (value instanceof BValue bValue) { + if (!(value instanceof BObject bObject)) { + return bValue.getType(); + } + 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); - } else if (value instanceof BObject bObject) { - return bObject.getOriginalType(); } - return ((BValue) value).getType(); + throw new IllegalArgumentException("unexpected value type"); } private static Type getNumberType(Number number) { @@ -630,6 +634,9 @@ private static boolean isSubType(Type source, Type target) { } private static SemType widenedType(Context cx, Object value) { + if (value instanceof BValue bValue) { + return bValue.widenedType(cx); + } if (value == null) { return Builder.nilType(); } else if (value instanceof Double) { @@ -640,11 +647,8 @@ private static SemType widenedType(Context cx, Object value) { return Builder.stringType(); } else if (value instanceof Boolean) { return Builder.booleanType(); - } else if (value instanceof DecimalValue) { - return Builder.decimalType(); - } else { - return ((BValue) value).widenedType(cx); } + throw new IllegalArgumentException("Unexpected object type"); } private static SemType createInherentlyImmutableType() { 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 96a611d060a0..a6dbe32a0e38 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 @@ -493,4 +493,9 @@ public static DecimalValue valueOfJ(BigDecimal value) { public Optional shapeOf(Context cx) { return Optional.of(Builder.decimalConst(value)); } + + @Override + public SemType widenedType(Context cx) { + return Builder.decimalType(); + } } From f06b55291a09d2be8326fbde658bd5f35bb67d0f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 19 Aug 2024 14:19:25 +0530 Subject: [PATCH 123/178] Remove unwanted optimizations --- .../io/ballerina/runtime/api/types/semtype/Core.java | 3 --- .../java/io/ballerina/runtime/internal/TypeChecker.java | 8 -------- .../runtime/internal/values/AbstractArrayValue.java | 7 ------- .../io/ballerina/runtime/internal/values/MapValue.java | 9 --------- 4 files changed, 27 deletions(-) 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 index 98a558224d13..2d932427ce6b 100644 --- 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 @@ -298,9 +298,6 @@ public static boolean isNever(SemType t) { } public static boolean isSubType(Context cx, SemType t1, SemType t2) { - if (t1.equals(t2)) { - return true; - } SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); if (cached != SemType.CachedResult.NOT_FOUND) { return cached == SemType.CachedResult.TRUE; 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 7514c5b36d8f..93956608a4d2 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 @@ -265,14 +265,6 @@ public static boolean anyToJBoolean(Object sourceVal) { */ public static boolean checkIsType(Object sourceVal, Type targetType) { Context cx = context(); - SemType targetBasicTypeUnion = Core.widenToBasicTypeUnion(targetType); - SemType valueBasicType = widenedType(cx, sourceVal); - if (!Core.isSubtypeSimple(valueBasicType, targetBasicTypeUnion)) { - return false; - } - if (targetBasicTypeUnion == targetType) { - return true; - } SemType sourceSemType = getType(sourceVal); if (Core.isSubType(cx, sourceSemType, targetType)) { return true; 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 b52e17203598..f2e7e0b77d1d 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 @@ -22,7 +22,6 @@ 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.Definition; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; @@ -282,12 +281,6 @@ protected void prepareForAddForcefully(int intIndex, int currentArraySize) { resetSize(intIndex); } - @Override - public SemType widenedType(Context cx) { - SemType semType = getType(); - return Core.intersect(semType, Builder.listType()); - } - /** * {@code {@link ArrayIterator}} provides iterator implementation for Ballerina array values. * diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java index 506f8e3413ef..262173eedb23 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValue.java @@ -17,10 +17,6 @@ */ package io.ballerina.runtime.internal.values; -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.BMap; /** @@ -38,9 +34,4 @@ */ public interface MapValue extends RefValue, CollectionValue, BMap { - @Override - default SemType widenedType(Context cx) { - SemType semType = getType(); - return Core.intersect(semType, Builder.mappingType()); - } } From 7dbb8175e26fb3f8776a84777a903c91607d8ab4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 19 Aug 2024 15:03:10 +0530 Subject: [PATCH 124/178] Fix result caching for futures --- .../ballerina/runtime/internal/MapUtils.java | 1 + .../runtime/internal/types/BFutureType.java | 2 +- .../query/simple-query-with-var-type.bal | 289 +++++++++--------- 3 files changed, 148 insertions(+), 144 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java index 59bc71da1be9..00d2d50d97ef 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java @@ -117,6 +117,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { + // FIXME: type = TypeUtils.getImpliedType(type); int tag = type.getTag(); if (tag == TypeTags.UNION_TAG) { 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 9c1d4d62b8c7..01de16492337 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 @@ -90,7 +90,7 @@ public boolean equals(Object obj) { return true; } - return TypeChecker.checkIsType(constraint, other.constraint); + return TypeChecker.isSameType(constraint, other.constraint); } @Override 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 d3cb67cf3f3e..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; From e7856ba52d21c2c953d6dea2fcfc8bf6ff4e98f8 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 19 Aug 2024 18:31:32 +0530 Subject: [PATCH 125/178] Fix Union type not getting reset correctly --- .../java/io/ballerina/runtime/internal/types/BUnionType.java | 2 ++ 1 file changed, 2 insertions(+) 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 31424c18751d..4391a1ebe084 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 @@ -238,12 +238,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)); From fb30a7c9c3cde9951394c0caffadde03ce35c992 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 20 Aug 2024 11:58:09 +0530 Subject: [PATCH 126/178] Reduce the overhead for calculating couldShapeBeDifferent --- .../ballerina/runtime/internal/types/BRecordType.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 fd2504a12eeb..0ed1eb098e33 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 @@ -74,6 +74,7 @@ public class BRecordType extends BStructureType implements RecordType, TypeWithS private IntersectionType intersectionType = null; private MappingDefinition defn; private final Env env = Env.getInstance(); + private byte couldShapeBeDifferentCache = 0; private final Map> defaultValues = new LinkedHashMap<>(); @@ -356,6 +357,15 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap @Override public boolean couldShapeBeDifferent() { + if (couldShapeBeDifferentCache != 0) { + return couldShapeBeDifferentCache == 1; + } + boolean result = couldShapeBeDifferentInner(); + couldShapeBeDifferentCache = (byte) (result ? 1 : 2); + return result; + } + + private boolean couldShapeBeDifferentInner() { if (isReadOnly()) { return true; } From 48d3d9ae6dba8d8643ccabe18dbd0bae777fe860 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 20 Aug 2024 14:37:37 +0530 Subject: [PATCH 127/178] Be lazy about calculating the flags of tuple types --- .../runtime/internal/types/BTupleType.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 9ee90e75245d..dc55aa8ab9c3 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 @@ -52,11 +52,11 @@ */ public class BTupleType extends BAnnotatableType implements TupleType, TypeWithShape { - private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private List tupleTypes; private Type restType; private int typeFlags; private final boolean readonly; + private boolean flagsPoisoned = false; private IntersectionType immutableType; private IntersectionType intersectionType = null; public boolean isCyclic = false; @@ -75,7 +75,7 @@ public BTupleType(List typeList) { super(null, null, Object.class); this.tupleTypes = typeList; this.restType = null; - checkAllMembers(); + this.flagsPoisoned = true; this.readonly = false; } @@ -182,7 +182,7 @@ public void setMemberTypes(List members, Type restType) { this.tupleTypes = members; this.restType = restType; } - checkAllMembers(); + flagsPoisoned = true; defn = null; } @@ -271,16 +271,20 @@ 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) { + checkAllMembers(); + flagsPoisoned = false; + } return this.typeFlags; } From 06cd5854494ad05bb204d5bab4fe1d98a784d559 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 20 Aug 2024 14:37:59 +0530 Subject: [PATCH 128/178] Be lazy when creating the semtype result cache --- .../java/io/ballerina/runtime/internal/types/BType.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 bda057298c4f..42da8f5b1ab6 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 @@ -55,7 +55,7 @@ public abstract class BType implements Type, SubTypeData, MutableSemType, Clonea private Type cachedReferredType = null; private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; - private Map cachedResults = new WeakHashMap<>(); + private Map cachedResults; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -279,11 +279,15 @@ public SubType[] subTypeData() { @Override public CachedResult cachedSubTypeRelation(SemType other) { + if (cachedResults == null) { + cachedResults = new WeakHashMap<>(); + } return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); } @Override public void cacheSubTypeRelation(SemType other, boolean result) { + // we always check of the result before caching so there will always be a map cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); } From 6126610555d9a3ab4ec0c5d41317b0d5678c1bd3 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 20 Aug 2024 14:38:15 +0530 Subject: [PATCH 129/178] Get rid of the stream when filtering nulls --- .../runtime/api/types/semtype/Core.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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 index 2d932427ce6b..25c4e700150f 100644 --- 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 @@ -34,7 +34,6 @@ import java.math.BigDecimal; import java.util.Arrays; -import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -124,10 +123,10 @@ public static SemType diff(SemType t1, SemType t2) { filterNulls = true; } else { subtypes[i] = data; + i++; } - i++; } - return SemType.from(all, some, filterNulls ? filterNulls(subtypes) : subtypes); + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); } // TODO: this should return SubTypeData not subtype @@ -195,17 +194,20 @@ public static SemType union(SemType t1, SemType t2) { some &= ~(1 << code); } else { subtypes[i] = data; + i++; } - i++; } if (some == 0) { return SemType.from(all); } - return SemType.from(all, some, filterNulls ? filterNulls(subtypes) : subtypes); + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); } - private static SubType[] filterNulls(SubType[] subtypes) { - return Arrays.stream(subtypes).filter(Objects::nonNull).toArray(SubType[]::new); + 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) { @@ -260,16 +262,16 @@ public static SemType intersect(SemType t1, SemType t2) { if (!data.isNothing()) { subtypes[i] = data; + i++; } else { some &= ~(1 << code); filterNulls = true; } - i++; } if (some == 0) { return SemType.from(all); } - return SemType.from(all, some, filterNulls ? filterNulls(subtypes) : subtypes); + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); } public static boolean isEmpty(Context cx, SemType t) { From 2fa358e8d3588daeb0f78f061e30ac3025d9a76e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 21 Aug 2024 08:46:15 +0530 Subject: [PATCH 130/178] Use concurrent lazy suppliers for BSemTypeWrappers --- .../types/semtype/ConcurrentLazySupplier.java | 30 +++++++++++++++++++ ...> ConcurrentLazySupplierWithCallback.java} | 29 +----------------- .../runtime/api/types/semtype/Context.java | 1 - .../runtime/internal/types/BAnyType.java | 4 ++- .../runtime/internal/types/BBooleanType.java | 3 +- .../runtime/internal/types/BByteType.java | 3 +- .../runtime/internal/types/BDecimalType.java | 3 +- .../runtime/internal/types/BFloatType.java | 3 +- .../runtime/internal/types/BHandleType.java | 4 ++- .../runtime/internal/types/BIntegerType.java | 5 ++-- .../runtime/internal/types/BNullType.java | 5 ++-- .../runtime/internal/types/BReadonlyType.java | 4 ++- .../internal/types/BSemTypeWrapper.java | 9 ++---- .../runtime/internal/types/BStringType.java | 5 ++-- 14 files changed, 59 insertions(+), 49 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/{LazySupplier.java => ConcurrentLazySupplierWithCallback.java} (68%) 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..eaf7036cee5d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java @@ -0,0 +1,30 @@ +package io.ballerina.runtime.api.types.semtype; + +import java.util.function.Supplier; + +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/LazySupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java similarity index 68% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java index 76f00e752a98..798717b164a3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/LazySupplier.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java @@ -21,34 +21,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; -class ConcurrentLazySupplier implements Supplier { - - private Supplier initializer; - private volatile E value = null; - - 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; - } -} - -class ConcurrentLazySupplierWithCallback implements Supplier { +public class ConcurrentLazySupplierWithCallback implements Supplier { private volatile E value = null; private Supplier initializer; 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 index 056b36ce12ae..bf355a225a6d 100644 --- 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 @@ -39,7 +39,6 @@ public final class Context { public final Map mappingMemo = new WeakHashMap<>(); public final Map functionMemo = new WeakHashMap<>(); - SemType anydataMemo; private Context(Env env) { this.env = env; } 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 66a173b44bbe..98314793cb49 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 @@ -26,6 +26,7 @@ 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.ConcurrentLazySupplier; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; @@ -45,7 +46,8 @@ public class BAnyType extends BSemTypeWrapper implements * @param typeName string name of the type */ public BAnyType(String typeName, Module pkg, boolean readonly) { - super(() -> new BAnyTypeImpl(typeName, pkg, readonly), typeName, pickSemType(readonly)); + super(new ConcurrentLazySupplier<>(() -> new BAnyTypeImpl(typeName, pkg, readonly)), + typeName, pickSemType(readonly)); } @Override 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 74fcd797e2bc..42c83507fa9a 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.BooleanType; 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; @@ -55,7 +56,7 @@ public static BBooleanType singletonType(boolean value) { } private BBooleanType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(bTypeSupplier, typeName, semType); + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, semType); } protected static final class BBooleanTypeImpl extends BType implements BooleanType { 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 661f1aac6844..72fda2ddef85 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.ByteType; 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; @@ -49,7 +50,7 @@ public BByteType(String typeName, Module pkg) { } private BByteType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(bTypeSupplier, typeName, semType); + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, semType); } public static BByteType singletonType(long value) { 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 44925f50eaef..3ada48c25779 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.DecimalType; 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; @@ -57,7 +58,7 @@ public static BDecimalType singletonType(BigDecimal value) { } private BDecimalType(Supplier bType, String typeName, SemType semType) { - super(bType, typeName, semType); + super(new ConcurrentLazySupplier<>(bType), typeName, semType); } protected static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { 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 6565df54c6f1..3a46b3102c34 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 @@ -22,6 +22,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.FloatType; 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; @@ -47,7 +48,7 @@ public BFloatType(String typeName, Module pkg) { } private BFloatType(Supplier bType, String typeName, SemType semType) { - super(bType, typeName, semType); + super(new ConcurrentLazySupplier<>(bType), typeName, semType); } public static BFloatType singletonType(Double value) { 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 688e4d79f565..0742e63b9689 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.HandleType; import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.internal.values.RefValue; /** @@ -39,7 +40,8 @@ public final class BHandleType extends BSemTypeWrapper BHandleTypeImpl.create(typeName, pkg), typeName, Builder.handleType()); + super(new ConcurrentLazySupplier<> + (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, Builder.handleType()); } protected static final class BHandleTypeImpl extends BType implements HandleType { 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 4c305a3d63a2..62840f725b67 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntegerType; 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; @@ -61,8 +62,8 @@ public BIntegerType(String typeName, Module pkg, int tag) { this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, pickSemType(tag)); } - private BIntegerType(Supplier bType, String typeName, SemType semType) { - super(bType, typeName, semType); + private BIntegerType(Supplier bIntegerTypeSupplier, String typeName, SemType semType) { + super(new ConcurrentLazySupplier<>(bIntegerTypeSupplier), typeName, semType); } private static SemType pickSemType(int 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 8598c28169a7..1abc907780df 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 @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.NullType; 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; @@ -46,8 +47,8 @@ protected BNullType(String typeName, Module pkg, SemType semType) { this(() -> new BNullTypeImpl(typeName, pkg), typeName, semType); } - private BNullType(Supplier bNullType, String typeName, SemType semType) { - super(bNullType, typeName, semType); + private BNullType(Supplier bNullTypeSupplier, String typeName, SemType semType) { + super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, semType); } protected static final class BNullTypeImpl extends BType implements NullType { 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 b7cc7deb7c86..bc324e1f8b31 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 @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.internal.values.RefValue; /** @@ -31,7 +32,8 @@ public class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(() -> new BReadonlyTypeImpl(typeName, pkg), typeName, Builder.readonlyType()); + super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, + Builder.readonlyType()); } protected static final class BReadonlyTypeImpl extends BType implements ReadonlyType { 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 index f0d09c501f0b..68737b334bb6 100644 --- 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 @@ -36,8 +36,6 @@ */ public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { - // FIXME: turn this to a lazy supplier to avoid intialization if not needed - private E bType; private final Supplier bTypeSupplier; protected final String typeName; // Debugger uses this field to show the type name @@ -170,10 +168,7 @@ public Type getCachedImpliedType() { return getbType().getCachedImpliedType(); } - protected synchronized E getbType() { - if (bType == null) { - bType = bTypeSupplier.get(); - } - return bType; + protected E getbType() { + return bTypeSupplier.get(); } } 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 0b7d6a9eeebb..29af4accb9da 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 @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.StringType; 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; @@ -53,8 +54,8 @@ public BStringType(String typeName, Module pkg, int tag) { this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, pickSemtype(tag)); } - private BStringType(Supplier bType, String typeName, SemType semType) { - super(bType, typeName, semType); + private BStringType(Supplier bTypeSupplier, String typeName, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, semType); } public static BStringType singletonType(String value) { From ed5fc41e19c122948c129d7bfbe1aaae53f38ba2 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 21 Aug 2024 10:07:08 +0530 Subject: [PATCH 131/178] Reduce contention in dependency manager --- .../MutableSemTypeDependencyManager.java | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java index ace9d9165530..8f5aa2d85663 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java @@ -32,7 +32,9 @@ public final class MutableSemTypeDependencyManager { private static final MutableSemTypeDependencyManager INSTANCE = new MutableSemTypeDependencyManager(); + private static final int GC_THRESHOLD = 100; private final Map>> dependencies = new WeakHashMap<>(); + private final Map accessLocks = new WeakHashMap<>(); public static MutableSemTypeDependencyManager getInstance() { return INSTANCE; @@ -41,26 +43,45 @@ public static MutableSemTypeDependencyManager getInstance() { private MutableSemTypeDependencyManager() { } - public synchronized void notifyDependenciesToReset(MutableSemType semType) { - List> mutableSemTypes = dependencies.get(semType); - if (mutableSemTypes != null) { - dependencies.remove(semType); - for (var dependent : mutableSemTypes) { - MutableSemType dependentSemType = dependent.get(); - if (dependentSemType != null) { - dependentSemType.resetSemType(); + public void notifyDependenciesToReset(MutableSemType semType) { + Object lock = getLock(semType); + synchronized (lock) { + List> mutableSemTypes = dependencies.get(semType); + if (mutableSemTypes != null) { + dependencies.remove(semType); + for (var dependent : mutableSemTypes) { + MutableSemType dependentSemType = dependent.get(); + if (dependentSemType != null) { + dependentSemType.resetSemType(); + } } } } } - public synchronized SemType getSemType(Type target, MutableSemType self) { + public SemType getSemType(Type target, MutableSemType self) { assert target != null; if (target instanceof MutableSemType mutableTarget) { + addDependency(self, mutableTarget); + } + return target; + } + + private void addDependency(MutableSemType self, MutableSemType mutableTarget) { + Object lock = getLock(mutableTarget); + synchronized (lock) { List> dependencies = this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); + // garbage collect these dependencies since the actual target may never mutate, triggering the cleanup + // of the list + if (dependencies.size() > GC_THRESHOLD) { + dependencies.removeIf((ref) -> ref.get() == null); + } dependencies.add(new WeakReference<>(self)); } - return target; + } + + private synchronized Object getLock(MutableSemType semType) { + return accessLocks.computeIfAbsent(semType, (ignored) -> new Object()); } } From 4e527a359ea04259489aae1f9e4fde6ba510169b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 23 Aug 2024 09:42:28 +0530 Subject: [PATCH 132/178] Fix runtime semtype resolver --- .../port/test/RuntimeSemTypeResolver.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) 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 index d3e9124f0e51..0881b5359219 100644 --- 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 @@ -46,6 +46,7 @@ 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.expressions.BLangConstant; @@ -68,6 +69,7 @@ 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; @@ -278,8 +280,7 @@ private SemType resolveFunctionType(TypeTestContext cx, Map resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode)).toArray(SemType[]::new); + SemType[] params = getParameters(cx, mod, defn, depth, functionType); SemType rest; if (functionType.getRestParameters() == null) { rest = Builder.neverType(); @@ -296,6 +297,21 @@ private SemType resolveFunctionType(TypeTestContext cx, Map cx, Map mod, BLangTypeDefinition defn, + int depth, BLangFunction functionType) { + List params = new ArrayList<>(); + if (functionType instanceof BLangResourceFunction resourceFunctionType) { + params.add(Builder.stringConst(resourceFunctionType.methodName.value)); + for (var each : resourceFunctionType.resourcePathSegments) { + params.add(resolveTypeDesc(cx, mod, defn, depth + 1, each.typeNode)); + } + } + functionType.getParameters().stream() + .map(paramVar -> resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode)) + .forEach(params::add); + return params.toArray(SemType[]::new); + } + private SemType getDistinctObjectType(Env env, SemType innerType) { return Core.intersect(ObjectDefinition.distinct(env.distinctAtomCountGetAndIncrement()), innerType); } From 7f976b379ec63e3c1282672d3972d69d3bde7a7d Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 23 Aug 2024 10:42:47 +0530 Subject: [PATCH 133/178] Fix BFunction Equal Also fix more unit tests with clashing type names --- .../runtime/internal/types/BFunctionType.java | 22 +---- ...-match-pattern-with-rest-match-pattern.bal | 90 +++++++++++++------ 2 files changed, 64 insertions(+), 48 deletions(-) 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 f1630125a82f..bd2854eb94e5 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 @@ -35,6 +35,7 @@ import io.ballerina.runtime.internal.types.semtype.ListDefinition; import java.util.Arrays; +import java.util.Objects; /** * {@code {@link BFunctionType }} represents a function type in ballerina. @@ -134,31 +135,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 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]); From 5c7b4c4e1bb8c4f110b87d687a9b217e570ddaaa Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 23 Aug 2024 11:05:29 +0530 Subject: [PATCH 134/178] Add workarounds to make libraries compile Fix NPE --- .../runtime/api/types/FunctionType.java | 3 -- .../runtime/api/types/MethodType.java | 33 ++++++++++++++++++- .../runtime/internal/types/BFunctionType.java | 1 - .../runtime/internal/types/BMethodType.java | 1 - .../runtime/internal/types/BObjectType.java | 7 ++-- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java index 398dc7231183..dcd676f3675e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java @@ -17,8 +17,6 @@ */ package io.ballerina.runtime.api.types; -import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; - /** * {@code FunctionType} represents a function type in ballerina. * @@ -43,5 +41,4 @@ public interface FunctionType extends AnnotatableType { Parameter[] getParameters(); - FunctionQualifiers getQualifiers(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java index 7850e83d3303..52071061ff2f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java @@ -17,6 +17,9 @@ */ package io.ballerina.runtime.api.types; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + /** * {@code MethodType} represents a function type in ballerina. * @@ -35,5 +38,33 @@ public interface MethodType extends FunctionType { */ boolean isIsolated(); - String name(); + @Override + default int all() { + throw new UnsupportedOperationException(); + } + + @Override + default int some() { + throw new UnsupportedOperationException(); + } + + @Override + default SubType[] subTypeData() { + throw new UnsupportedOperationException(); + } + + @Override + default CachedResult cachedSubTypeRelation(SemType other) { + throw new UnsupportedOperationException(); + } + + @Override + default void cacheSubTypeRelation(SemType other, boolean result) { + throw new UnsupportedOperationException(); + } + + @Override + default SubType subTypeByCode(int code) { + throw new UnsupportedOperationException(); + } } 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 bd2854eb94e5..5afc3e4b5d79 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 @@ -268,7 +268,6 @@ private record SemTypeResult(boolean hasBTypePart, SemType pureSemTypePart) { } - @Override public FunctionQualifiers getQualifiers() { return FunctionQualifiers.create(SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED), SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); 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 4e08a64a2e91..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 @@ -85,7 +85,6 @@ public boolean isIsolated() { return SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED); } - @Override public String name() { return funcName; } 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 60d759864598..c4249d3afd10 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 @@ -300,7 +300,7 @@ private synchronized SemType semTypeInner() { defn = od; ObjectQualifiers qualifiers = getObjectQualifiers(); List members = new ArrayList<>(); - Set seen = new HashSet<>(fields.size() + methodTypes.length); + Set seen = new HashSet<>(); for (Entry entry : fields.entrySet()) { String name = entry.getKey(); if (skipField(seen, name)) { @@ -424,6 +424,9 @@ public void resetSemType() { } protected Collection allMethods() { + if (methodTypes == null) { + return List.of(); + } return Arrays.stream(methodTypes) .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)).toList(); } @@ -493,7 +496,7 @@ static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyM 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, innerFn.getQualifiers()); + SemType semType = fd.define(env, paramType, returnType, ((BFunctionType) innerFn).getQualifiers()); return new MethodData(methodName, method.getFlags(), semType); } } From 8fc2f5c05c760df9ab4cd43bb15f7d7008f5da18 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 25 Aug 2024 08:05:42 +0530 Subject: [PATCH 135/178] Better caching of the type creator --- .../runtime/api/creators/TypeCreator.java | 50 +++++++++++++------ .../runtime/internal/types/BRecordType.java | 9 ++++ .../runtime/internal/types/BType.java | 6 +++ 3 files changed, 50 insertions(+), 15 deletions(-) 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 11ca73e2052c..d2157db229ae 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 @@ -43,6 +43,7 @@ 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.BUnionType; import io.ballerina.runtime.internal.types.BXmlType; import io.ballerina.runtime.internal.types.CopyOnWriteBMapWrapper; @@ -53,6 +54,8 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.function.Function; +import java.util.function.Supplier; /** * Class @{@link TypeCreator} provides APIs to create ballerina type instances. @@ -179,7 +182,7 @@ public static TupleType createTupleType(String name, Module pkg, int typeFlags, boolean isCyclic, boolean readonly) { TypeMemoKey key = new TypeMemoKey(name, pkg); return tupleTypeMemo.computeIfAbsent(key, - (ignored) -> new BTupleType(name, pkg, typeFlags, isCyclic, readonly)); + createMappingFn(() -> new BTupleType(name, pkg, typeFlags, isCyclic, readonly))); } /** @@ -214,7 +217,8 @@ public static MapType createMapType(Type constraint, boolean readonly) { */ public static MapType createMapType(String typeName, Type constraint, Module module) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return mapTypeMemo.computeIfAbsent(key, (ignored) -> new BMapType(typeName, constraint, module)); + return mapTypeMemo.computeIfAbsent(key, + createMappingFn(() -> new BMapType(typeName, constraint, module))); } /** @@ -228,7 +232,8 @@ public static MapType createMapType(String typeName, Type constraint, Module mod */ public static MapType createMapType(String typeName, Type constraint, Module module, boolean readonly) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return mapTypeMemo.computeIfAbsent(key, (ignored) -> new BMapType(typeName, constraint, module, readonly)); + return mapTypeMemo.computeIfAbsent(key, + createMappingFn(() -> new BMapType(typeName, constraint, module, readonly))); } /** @@ -245,7 +250,7 @@ public static RecordType createRecordType(String typeName, Module module, long f int typeFlags) { TypeMemoKey key = new TypeMemoKey(typeName, module); return recordTypeMemo.computeIfAbsent(key, - (ignored) -> new BRecordType(typeName, typeName, module, flags, sealed, typeFlags)); + createMappingFn(() -> new BRecordType(typeName, typeName, module, flags, sealed, typeFlags))); } /** @@ -265,7 +270,8 @@ public static RecordType createRecordType(String typeName, Module module, long f boolean sealed, int typeFlags) { TypeMemoKey key = new TypeMemoKey(typeName, module); return recordTypeMemo.computeIfAbsent(key, - (ignored) -> new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags)); + createMappingFn( + () -> new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags))); } /** @@ -278,7 +284,7 @@ public static RecordType createRecordType(String typeName, Module module, long f */ public static ObjectType createObjectType(String typeName, Module module, long flags) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return objectTypeMemo.computeIfAbsent(key, (ignored) -> new BObjectType(typeName, module, flags)); + return objectTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BObjectType(typeName, module, flags))); } /** @@ -305,7 +311,7 @@ public static StreamType createStreamType(String typeName, Type constraint, Type completionType, Module modulePath) { TypeMemoKey key = new TypeMemoKey(typeName, modulePath); return streamTypeMemo.computeIfAbsent(key, - (ignored) -> new BStreamType(typeName, constraint, completionType, modulePath)); + createMappingFn(() -> new BStreamType(typeName, constraint, completionType, modulePath))); } /** @@ -333,7 +339,7 @@ public static StreamType createStreamType(Type constraint) { public static StreamType createStreamType(String typeName, Type completionType, Module modulePath) { TypeMemoKey key = new TypeMemoKey(typeName, modulePath); return streamTypeMemo.computeIfAbsent(key, - (ignored) -> new BStreamType(typeName, completionType, modulePath)); + createMappingFn(() -> new BStreamType(typeName, completionType, modulePath))); } /** @@ -405,7 +411,7 @@ public static UnionType createUnionType(List memberTypes, String name, Mod boolean isCyclic, long flags) { TypeMemoKey key = new TypeMemoKey(name, pkg); return unionTypeMemo.computeIfAbsent(key, - (ignored) -> new BUnionType(memberTypes, name, pkg, typeFlags, isCyclic, flags)); + createMappingFn(() -> new BUnionType(memberTypes, name, pkg, typeFlags, isCyclic, flags))); } /** @@ -417,7 +423,7 @@ public static UnionType createUnionType(List memberTypes, String name, Mod */ public static ErrorType createErrorType(String typeName, Module module) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return errorTypeMemo.computeIfAbsent(key, (ignored) -> new BErrorType(typeName, module)); + return errorTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BErrorType(typeName, module))); } /** @@ -430,7 +436,7 @@ public static ErrorType createErrorType(String typeName, Module module) { */ public static ErrorType createErrorType(String typeName, Module module, Type detailType) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return errorTypeMemo.computeIfAbsent(key, (ignored) -> new BErrorType(typeName, module, detailType)); + return errorTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BErrorType(typeName, module, detailType))); } /** @@ -490,7 +496,7 @@ public static TableType createTableType(Type constraint, boolean readonly) { */ public static XmlType createXMLType(String typeName, Type constraint, Module module) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return xmlTypeMemo.computeIfAbsent(key, (ignored) -> new BXmlType(typeName, constraint, module)); + return xmlTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BXmlType(typeName, constraint, module))); } /** @@ -504,7 +510,7 @@ public static XmlType createXMLType(String typeName, Type constraint, Module mod */ public static XmlType createXMLType(String typeName, Module module, int tag, boolean readonly) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return xmlTypeMemo.computeIfAbsent(key, (ignored) -> new BXmlType(typeName, module, tag, readonly)); + return xmlTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BXmlType(typeName, module, tag, readonly))); } /** @@ -528,7 +534,7 @@ public static XmlType createXMLType(Type constraint, boolean readonly) { */ public static JsonType createJSONType(String typeName, Module module, boolean readonly) { TypeMemoKey key = new TypeMemoKey(typeName, module); - return jsonTypeMemo.computeIfAbsent(key, (ignored) -> new BJsonType(typeName, module, readonly)); + return jsonTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BJsonType(typeName, module, readonly))); } /** @@ -556,7 +562,21 @@ public static FiniteType createFiniteType(String typeName, Set values, i private TypeCreator() { } - private record TypeMemoKey(String typeName, Module module) { + public record TypeMemoKey(String typeName, Module module) { } + + public static void registerRecordType(String typeName, Module module, BRecordType recordType) { + TypeMemoKey key = new TypeMemoKey(typeName, module); + recordTypeMemo.put(key, recordType); + recordType.setLookupKey(key); + } + + private static Function createMappingFn(Supplier innerSuppler) { + return (key) -> { + E bType = innerSuppler.get(); + bType.setLookupKey(key); + return bType; + }; + } } 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 0ed1eb098e33..446ba233a414 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 @@ -21,6 +21,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; +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; @@ -93,6 +94,7 @@ public BRecordType(String typeName, String internalName, Module pkg, long flags, this.sealed = sealed; this.typeFlags = typeFlags; this.readonly = SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY); + registerWithTypeCreator(); } /** @@ -122,6 +124,13 @@ public BRecordType(String typeName, Module pkg, long flags, Map f this.fields = fields; } this.internalName = typeName; + registerWithTypeCreator(); + } + + private void registerWithTypeCreator() { + if (this.typeName != null && this.pkg != null) { + TypeCreator.registerRecordType(this.typeName, this.pkg, this); + } } private Map getReadOnlyFields(Map fields) { 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 42da8f5b1ab6..7576d1c82cc4 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 @@ -20,6 +20,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.MutableSemType; @@ -55,6 +56,7 @@ public abstract class BType implements Type, SubTypeData, MutableSemType, Clonea private Type cachedReferredType = null; private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; + private TypeCreator.TypeMemoKey lookupKey = null; private Map cachedResults; protected BType(String typeName, Module pkg, Class valueClass) { @@ -316,4 +318,8 @@ public BType clone() { throw new AssertionError(); } } + + public void setLookupKey(TypeCreator.TypeMemoKey lookupKey) { + this.lookupKey = lookupKey; + } } From 65243248acf73f0d62b2a7f5da4e5be6478546b9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 25 Aug 2024 10:30:55 +0530 Subject: [PATCH 136/178] Make shape calculation thread safe --- .../internal/values/AbstractArrayValue.java | 14 +++++++------- .../runtime/internal/values/MapValueImpl.java | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) 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 f2e7e0b77d1d..240a8106b6e7 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 @@ -60,7 +60,7 @@ public abstract class AbstractArrayValue implements ArrayValue { static final int SYSTEM_ARRAY_MAX = Integer.MAX_VALUE - 8; - private Definition readonlyAttachedDefinition; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); /** * The maximum size of arrays to allocate. @@ -312,18 +312,18 @@ public boolean hasNext() { } @Override - public Optional getReadonlyShapeDefinition() { - return Optional.ofNullable(readonlyAttachedDefinition); + public synchronized Optional getReadonlyShapeDefinition() { + return Optional.ofNullable(readonlyAttachedDefinition.get()); } @Override - public void setReadonlyShapeDefinition(Definition definition) { - readonlyAttachedDefinition = definition; + public synchronized void setReadonlyShapeDefinition(Definition definition) { + readonlyAttachedDefinition.set(definition); } @Override - public void resetReadonlyShapeDefinition() { - readonlyAttachedDefinition = null; + public synchronized void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); } @Override 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 f32e0ba70f66..3472956da61d 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 @@ -107,7 +107,7 @@ public class MapValueImpl extends LinkedHashMap implements RefValue, private final Map nativeData = new HashMap<>(); private Type iteratorNextReturnType; private SemType shape; - private Definition readonlyAttachedDefinition; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); public MapValueImpl(TypedescValue typedesc) { this(typedesc.getDescribingType()); @@ -618,18 +618,18 @@ public IteratorValue getIterator() { } @Override - public Optional getReadonlyShapeDefinition() { - return Optional.ofNullable(readonlyAttachedDefinition); + public synchronized Optional getReadonlyShapeDefinition() { + return Optional.ofNullable(readonlyAttachedDefinition.get()); } @Override - public void setReadonlyShapeDefinition(Definition definition) { - readonlyAttachedDefinition = definition; + public synchronized void setReadonlyShapeDefinition(Definition definition) { + readonlyAttachedDefinition.set(definition); } @Override - public void resetReadonlyShapeDefinition() { - readonlyAttachedDefinition = null; + public synchronized void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); } /** From b0007271e1c140535f81e59561fa8bb225c6fda4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 25 Aug 2024 10:31:14 +0530 Subject: [PATCH 137/178] Add workaround to handle cyclic shapes --- .../main/java/io/ballerina/runtime/internal/TypeChecker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 93956608a4d2..971c5292787e 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 @@ -611,7 +611,7 @@ static boolean isByteLiteral(long longValue) { private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { return Builder.shapeOf(cx, sourceValue) - .map(source -> Core.isSubType(cx, source, target)) + .map(source -> !Core.isEmpty(cx, source) && Core.isSubType(cx, source, target)) .orElse(false); } From 70fa9a52c7a32eca22a8353cb7e929f356aaa313 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 25 Aug 2024 12:39:54 +0530 Subject: [PATCH 138/178] Add default implementations to make TestUtils work --- .../io/ballerina/runtime/api/types/Type.java | 32 +++++++++++++++++++ .../ballerina/runtime/api/values/BArray.java | 24 ++++++++++++++ .../io/ballerina/runtime/api/values/BMap.java | 23 +++++++++++++ 3 files changed, 79 insertions(+) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java index 66e3ad4ad3a4..35ed28d362fe 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java @@ -19,6 +19,7 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; /** * {@code Type} represents a type in Ballerina. @@ -75,6 +76,37 @@ default Type getCachedImpliedType() { return null; } + // Default implementations for SemTypes since some standard library test utils define there own anonymous classes + @Override + default int all() { + throw new UnsupportedOperationException(); + } + + @Override + default int some() { + throw new UnsupportedOperationException(); + } + + @Override + default SubType[] subTypeData() { + throw new UnsupportedOperationException(); + } + + @Override + default CachedResult cachedSubTypeRelation(SemType other) { + throw new UnsupportedOperationException(); + } + + @Override + default void cacheSubTypeRelation(SemType other, boolean result) { + throw new UnsupportedOperationException(); + } + + @Override + default SubType subTypeByCode(int code) { + throw new UnsupportedOperationException(); + } + /** * Get the default value of the type. This is the value of an uninitialized variable of this type. * For value types, this is same as the value get from {@code BType#getInitValue()}. diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java index 7ffe41b38cd1..2b98c3876148 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java @@ -18,8 +18,12 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.TypeWithShape; +import java.util.Optional; + /** *

* Represent an array in ballerina. @@ -242,4 +246,24 @@ public interface BArray extends BRefValue, BCollection, PatternMatchableValue, default TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } + + @Override + default void cacheShape(SemType semType) { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + default Optional getReadonlyShapeDefinition() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + default void setReadonlyShapeDefinition(Definition definition) { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + default void resetReadonlyShapeDefinition() { + throw new UnsupportedOperationException("Method not implemented"); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index f331fa06dd8c..f554830fbdc6 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -18,10 +18,13 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.TypeWithShape; import java.util.Collection; import java.util.Map; +import java.util.Optional; import java.util.Set; /** @@ -199,4 +202,24 @@ public interface BMap extends BRefValue, BCollection, PatternMatchableValu default TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } + + @Override + default void cacheShape(SemType semType) { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + default Optional getReadonlyShapeDefinition() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + default void setReadonlyShapeDefinition(Definition definition) { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + default void resetReadonlyShapeDefinition() { + throw new UnsupportedOperationException("Method not implemented"); + } } From 041aa1dd4b720f0040c1996ef244ec51cd3881aa Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 25 Aug 2024 12:40:05 +0530 Subject: [PATCH 139/178] Fix type casts in Intersection type --- .../runtime/internal/types/BIntersectionType.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 d6855beadd34..39a50038b1df 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 @@ -39,6 +39,8 @@ import java.util.Optional; import java.util.StringJoiner; +import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; + /** * {@code BIntersectionType} represents an intersection type in Ballerina. * @@ -237,12 +239,12 @@ public SemType createSemType() { result = Core.intersect(result, memberType); } if (Core.isSubtypeSimple(result, Builder.errorType())) { - BErrorType effectiveErrorType = (BErrorType) effectiveType; + BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, effectiveErrorType.getTypeIdSet()); result = distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(result, Core::intersect); } else if (Core.isSubtypeSimple(result, Builder.objectType())) { - BObjectType effectiveObjectType = (BObjectType) effectiveType; + BObjectType effectiveObjectType = (BObjectType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, effectiveObjectType.getTypeIdSet()); result = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(result, Core::intersect); @@ -272,4 +274,5 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object public boolean couldShapeBeDifferent() { return true; } + } From 74ce57173d1c5d19f2626c6bdebc2734815d03d2 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 25 Aug 2024 14:32:05 +0530 Subject: [PATCH 140/178] Fix error equals --- .../java/io/ballerina/runtime/internal/values/ErrorValue.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c6daa4f91d6b..666d85176086 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 @@ -459,7 +459,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); From 2d8324b46d3c0efd1913d40d773c95e618639522 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 26 Aug 2024 07:58:06 +0530 Subject: [PATCH 141/178] Add workaround to make SQL work Fix check style violations --- .../runtime/api/creators/TypeCreator.java | 8 ++------ .../api/types/semtype/CellAtomicType.java | 6 +++--- .../runtime/api/types/semtype/Core.java | 5 +++++ .../ballerina/runtime/internal/MapUtils.java | 18 +++--------------- .../runtime/internal/TypeChecker.java | 2 +- .../internal/types/BSemTypeWrapper.java | 1 + .../types/semtype/ImmutableSemType.java | 4 ---- .../internal/types/semtype/TableUtils.java | 1 - .../runtime/internal/values/XmlValue.java | 1 + 9 files changed, 16 insertions(+), 30 deletions(-) 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 d2157db229ae..e396fdae02e4 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 @@ -266,12 +266,8 @@ 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) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return recordTypeMemo.computeIfAbsent(key, - createMappingFn( - () -> new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags))); + Type restFieldType, boolean sealed, int typeFlags) { + return new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags); } /** diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java index 3bbd7725b3ae..e9b1beb72ce8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java @@ -61,9 +61,9 @@ public enum CellMutability { private static final class CellAtomCache { - private final static Map NONE_CACHE = new HashMap<>(); - private final static Map LIMITED_CACHE = new HashMap<>(); - private final static Map UNLIMITED_CACHE = new HashMap<>(); + 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) { 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 index 25c4e700150f..6391959da520 100644 --- 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 @@ -300,6 +300,11 @@ public static boolean isNever(SemType t) { } public static boolean isSubType(Context cx, SemType t1, SemType t2) { + // 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. + if (t1.equals(t2)) { + return true; + } SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); if (cached != SemType.CachedResult.NOT_FOUND) { return cached == SemType.CachedResult.TRUE; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java index 00d2d50d97ef..be744541529d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java @@ -23,6 +23,8 @@ 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.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BString; @@ -30,11 +32,8 @@ import io.ballerina.runtime.internal.errors.ErrorHelper; 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; @@ -117,18 +116,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { - // FIXME: - 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(type, Builder.nilType()); } public static BError createOpNotSupportedError(Type type, String op) { 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 971c5292787e..51c145fc770c 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 @@ -656,7 +656,7 @@ public static boolean isInherentlyImmutableType(Type sourceType) { Core.isSubType(context(), sourceType, INHERENTLY_IMMUTABLE_TYPE) || sourceType instanceof ReadonlyType; } - // FIXME: + // 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; 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 index 68737b334bb6..ba5e0f03a10f 100644 --- 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 @@ -32,6 +32,7 @@ * 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.10.0 */ public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { 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 index 16a3135869be..ffa73d986eeb 100644 --- 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 @@ -23,12 +23,8 @@ import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.internal.types.BSemTypeWrapper; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; import java.util.Arrays; -import java.util.Map; import java.util.Objects; -import java.util.WeakHashMap; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; 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 index 589d458c4657..c8dfa2fe6f9a 100644 --- 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 @@ -78,7 +78,6 @@ public static SemType tableContaining(Env env, SemType tableConstraint) { } private static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { - // FIXME: type return tableContaining(env, tableConstraint, Builder.valType(), Builder.valType(), mut); } 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 d92dc7feedf8..ae5fbd90e50d 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 @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; + import javax.xml.namespace.QName; import static io.ballerina.runtime.internal.ValueUtils.getTypedescValue; From e21c4d058e9b3faf4404b3feb4a3824124f5331f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 26 Aug 2024 11:38:58 +0530 Subject: [PATCH 142/178] Patch runtime resolver to handle dependtly typed func --- .../port/test/RuntimeSemTypeResolver.java | 106 +++++++++++++----- 1 file changed, 81 insertions(+), 25 deletions(-) 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 index 0881b5359219..237a0c8a4b97 100644 --- 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 @@ -40,7 +40,6 @@ 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.NodeKind; import org.ballerinalang.model.tree.types.ArrayTypeNode; import org.ballerinalang.model.tree.types.TypeNode; import org.ballerinalang.model.types.TypeKind; @@ -49,6 +48,7 @@ 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; @@ -280,7 +280,8 @@ private SemType resolveFunctionType(TypeTestContext cx, Map paramScope = new HashMap<>(); + SemType[] params = getParameters(cx, mod, paramScope, defn, depth, functionType); SemType rest; if (functionType.getRestParameters() == null) { rest = Builder.neverType(); @@ -288,8 +289,7 @@ private SemType resolveFunctionType(TypeTestContext cx, Map cx, Map cx, Map mod, BLangTypeDefinition defn, - int depth, BLangFunction functionType) { + 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.stringConst(resourceFunctionType.methodName.value)); @@ -306,9 +307,13 @@ private SemType[] getParameters(TypeTestContext cx, Map resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode)) - .forEach(params::add); + for (BLangSimpleVariable paramVar : functionType.getParameters()) { + SemType semType = resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode); + if (Core.isSubtypeSimple(semType, Builder.typeDescType())) { + paramScope.put(paramVar.name.value, paramVar); + } + params.add(semType); + } return params.toArray(SemType[]::new); } @@ -335,9 +340,16 @@ private SemType resolveFunctionTypeDesc(TypeTestContext cx, Map params = - td.params.stream().map(param -> resolveTypeDesc(cx, mod, defn, depth + 1, param.typeNode)) - .toList(); + + 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.typeDescType())) { + tdScope.put(param.name.value, param); + } + } SemType rest; if (td.restParam == null) { rest = Builder.neverType(); @@ -345,8 +357,7 @@ private SemType resolveFunctionTypeDesc(TypeTestContext cx, Map cx, Map cx, + Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, + int depth, BLangType returnTypeNode) { + if (returnTypeNode == null) { + return Builder.nilType(); + } + 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.booleanType() : Builder.booleanConst(true), + innerType}, 2, Builder.neverType(), + 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; } @@ -569,20 +617,28 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp BLangNode moduleLevelDef = mod.get(name); if (moduleLevelDef == null) { - throw new IllegalStateException("unknown type: " + name); + throw new IndexOutOfBoundsException("unknown type " + name); } - if (moduleLevelDef.getKind() == NodeKind.TYPE_DEFINITION) { - SemType ty = resolveTypeDefnRec(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); - if (td.flagSet.contains(Flag.DISTINCT)) { - return getDistinctSemType(cx, ty); + 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; } - return ty; - } else if (moduleLevelDef.getKind() == NodeKind.CONSTANT) { - BLangConstant constant = (BLangConstant) moduleLevelDef; - return resolveTypeDefnRec(cx, mod, constant.associatedTypeDefinition, depth); - } else { - throw new UnsupportedOperationException("constants and class defns not implemented"); + 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"); } } From 3b2e60111d27c3d4cef8332b5797e1ad05e7fb41 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 26 Aug 2024 13:15:42 +0530 Subject: [PATCH 143/178] Fix runtime test errors Fix error messages --- .../internal/types/CopyOnWriteBMapWrapper.java | 5 +++++ .../internal/values/AbstractArrayValue.java | 4 +++- .../runtime/test/config/TomlProviderTest.java | 2 +- .../test/resources/test-src/valuelib_test.bal | 18 +++++++----------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java index 80c69a93ef96..744b72205aad 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java @@ -163,4 +163,9 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier public boolean couldShapeBeDifferent() { return inner.couldShapeBeDifferent(); } + + @Override + public String toString() { + return inner.toString(); + } } 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 240a8106b6e7..98ee6eb92d49 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 @@ -84,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)) { @@ -92,7 +95,6 @@ public boolean equals(Object o, Set visitedValues) { } visitedValues.add(compValuePair); - ArrayValue arrayValue = (ArrayValue) o; if (arrayValue.size() != this.size()) { return false; } 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 3ab6dbf9311c..326dd4323a15 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/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal index 4ea468dd3ef0..84f69c53309c 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]; @@ -4705,8 +4701,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"); } From 79fb814693d3d5377304fac0a8e3c385107a4850 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 28 Aug 2024 09:43:46 +0530 Subject: [PATCH 144/178] Change synchronization of atom table --- .../runtime/api/types/semtype/Env.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) 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 index 57e09dec1557..bd1c5a17886c 100644 --- 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 @@ -37,10 +37,13 @@ */ 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 to have multiple instances of Env. + // 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(); @@ -74,7 +77,8 @@ public TypeAtom cellAtom(CellAtomicType atomicType) { } private TypeAtom typeAtom(AtomicType atomicType) { - synchronized (this.atomTable) { + atomLock.readLock().lock(); + try { Reference ref = this.atomTable.get(atomicType); if (ref != null) { TypeAtom atom = ref.get(); @@ -82,9 +86,16 @@ private TypeAtom typeAtom(AtomicType atomicType) { 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(); } } @@ -115,9 +126,14 @@ public RecAtom recListAtom() { } public void setRecListAtomType(RecAtom rec, ListAtomicType atomicType) { - synchronized (this.recListAtoms) { + // 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) { @@ -154,11 +170,11 @@ public RecAtom recMappingAtom() { } public void setRecMappingAtomType(RecAtom rec, MappingAtomicType atomicType) { - recMapLock.writeLock().lock(); + recMapLock.readLock().lock(); try { this.recMappingAtoms.set(rec.index(), atomicType); } finally { - recMapLock.writeLock().unlock(); + recMapLock.readLock().unlock(); } } @@ -188,11 +204,11 @@ public RecAtom recFunctionAtom() { } public void setRecFunctionAtomType(RecAtom rec, FunctionAtomicType atomicType) { - recFunctionLock.writeLock().lock(); + recFunctionLock.readLock().lock(); try { this.recFunctionAtoms.set(rec.index(), atomicType); } finally { - recFunctionLock.writeLock().unlock(); + recFunctionLock.readLock().unlock(); } } From 6bf8f5cd938488619e1678928fef06a06876403e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sat, 31 Aug 2024 11:14:17 +0530 Subject: [PATCH 145/178] Remove BasicTypeBitSet --- .../api/types/semtype/BasicTypeBitSet.java | 39 ------------------- .../runtime/api/types/semtype/SemType.java | 2 +- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java 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 deleted file mode 100644 index 47e72ce4cd06..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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; - -// SEMTYPE-TODO: revisit this after fully implementing semtypes. Added this to match nBallerina where this is just a -// type alias to int. Maybe not needed here due to the way we have modeled type hierarchy (need to check if doing -// instancof checks on this is faster than checking if some is 0) - -/** - * Represents a union of basic types. - * - * @since 2201.10.0 - */ -public interface BasicTypeBitSet { - - default int some() { - return 0; - } - - default SubType[] subTypeData() { - return new SubType[0]; - } -} 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 index ce6cf9d091c4..9a4bb5d9e3c3 100644 --- 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 @@ -20,7 +20,7 @@ import io.ballerina.runtime.internal.types.semtype.PureSemType; -public interface SemType extends BasicTypeBitSet { +public interface SemType { static SemType from(int all, int some, SubType[] subTypeData) { return new PureSemType(all, some, subTypeData); From 9db0a5116e48bce502f7ab4dcf73622cd6968373 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sat, 31 Aug 2024 15:06:00 +0530 Subject: [PATCH 146/178] Restructure semtype structure --- .../runtime/api/creators/TypeCreator.java | 7 +- .../runtime/api/types/MethodType.java | 33 ---- .../io/ballerina/runtime/api/types/Type.java | 35 +--- .../api/types/semtype/BasicTypeBitSet.java | 18 ++ .../runtime/api/types/semtype/Core.java | 2 - .../api/types/semtype/MutableSemType.java | 6 +- .../runtime/api/types/semtype/SemType.java | 82 ++++++--- .../ballerina/runtime/api/values/BArray.java | 6 - .../io/ballerina/runtime/api/values/BMap.java | 6 - .../ballerina/runtime/api/values/BValue.java | 2 +- .../ballerina/runtime/internal/MapUtils.java | 3 +- .../runtime/internal/TypeChecker.java | 34 ++-- .../runtime/internal/TypeConverter.java | 5 +- .../runtime/internal/ValueConverter.java | 6 +- .../runtime/internal/types/BAnydataType.java | 5 +- .../runtime/internal/types/BObjectType.java | 2 +- .../internal/types/BParameterizedType.java | 2 +- .../runtime/internal/types/BRecordType.java | 6 +- .../runtime/internal/types/BTableType.java | 2 +- .../runtime/internal/types/BType.java | 52 ++---- .../types/CopyOnWriteBMapWrapper.java | 171 ------------------ .../internal/types/semtype/BSubType.java | 74 -------- .../types/semtype/ImmutableSemType.java | 39 +--- .../MutableSemTypeDependencyManager.java | 5 +- .../runtime/internal/values/FPValue.java | 2 +- .../runtime/internal/values/RegExpValue.java | 5 - .../runtime/internal/values/XmlValue.java | 1 - 27 files changed, 139 insertions(+), 472 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java 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 e396fdae02e4..33da18d6857b 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 @@ -46,7 +46,6 @@ import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.BUnionType; import io.ballerina.runtime.internal.types.BXmlType; -import io.ballerina.runtime.internal.types.CopyOnWriteBMapWrapper; import java.util.Arrays; import java.util.IdentityHashMap; @@ -73,8 +72,7 @@ public final class TypeCreator { private static final Map errorTypeMemo = new WeakHashMap<>(100); private static final Map xmlTypeMemo = new WeakHashMap<>(100); private static final Map jsonTypeMemo = new WeakHashMap<>(100); - - private static final Map mapTypeWrapperMemo = new IdentityHashMap<>(); + private static final Map mapTypeCache = new IdentityHashMap<>(); /** * Creates a new array type with given element type. * @@ -192,8 +190,7 @@ public static TupleType createTupleType(String name, Module pkg, * @return the new map type */ public static MapType createMapType(Type constraint) { - return mapTypeWrapperMemo.computeIfAbsent(constraint, - (ignored) -> new CopyOnWriteBMapWrapper(new BMapType(constraint))); + return mapTypeCache.computeIfAbsent(constraint, (ignore) -> new BMapType(constraint)); } /** diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java index 52071061ff2f..1f416679836e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/MethodType.java @@ -17,9 +17,6 @@ */ package io.ballerina.runtime.api.types; -import io.ballerina.runtime.api.types.semtype.SemType; -import io.ballerina.runtime.api.types.semtype.SubType; - /** * {@code MethodType} represents a function type in ballerina. * @@ -37,34 +34,4 @@ public interface MethodType extends FunctionType { * @return true if {@link MethodType} method is isolated otherwise false. */ boolean isIsolated(); - - @Override - default int all() { - throw new UnsupportedOperationException(); - } - - @Override - default int some() { - throw new UnsupportedOperationException(); - } - - @Override - default SubType[] subTypeData() { - throw new UnsupportedOperationException(); - } - - @Override - default CachedResult cachedSubTypeRelation(SemType other) { - throw new UnsupportedOperationException(); - } - - @Override - default void cacheSubTypeRelation(SemType other, boolean result) { - throw new UnsupportedOperationException(); - } - - @Override - default SubType subTypeByCode(int code) { - throw new UnsupportedOperationException(); - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java index 35ed28d362fe..40d87dbe9175 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java @@ -18,8 +18,6 @@ package io.ballerina.runtime.api.types; import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.api.types.semtype.SemType; -import io.ballerina.runtime.api.types.semtype.SubType; /** * {@code Type} represents a type in Ballerina. @@ -31,7 +29,7 @@ * * @since 2.0.0 */ -public interface Type extends SemType { +public interface Type { // TODO: remove default implementations when standard library types are updated /** @@ -76,37 +74,6 @@ default Type getCachedImpliedType() { return null; } - // Default implementations for SemTypes since some standard library test utils define there own anonymous classes - @Override - default int all() { - throw new UnsupportedOperationException(); - } - - @Override - default int some() { - throw new UnsupportedOperationException(); - } - - @Override - default SubType[] subTypeData() { - throw new UnsupportedOperationException(); - } - - @Override - default CachedResult cachedSubTypeRelation(SemType other) { - throw new UnsupportedOperationException(); - } - - @Override - default void cacheSubTypeRelation(SemType other, boolean result) { - throw new UnsupportedOperationException(); - } - - @Override - default SubType subTypeByCode(int code) { - throw new UnsupportedOperationException(); - } - /** * Get the default value of the type. This is the value of an uninitialized variable of this type. * For value types, this is same as the value get from {@code BType#getInitValue()}. 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..97f64f378f8a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java @@ -0,0 +1,18 @@ +package io.ballerina.runtime.api.types.semtype; + +public 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() { + return all; + } +} 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 index 6391959da520..678f882d9cbc 100644 --- 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 @@ -23,7 +23,6 @@ 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.BSubType; import io.ballerina.runtime.internal.types.semtype.BTableSubType; import io.ballerina.runtime.internal.types.semtype.BTypedescSubType; import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; @@ -283,7 +282,6 @@ public static boolean isEmpty(Context cx, SemType t) { } for (SubType subType : t.subTypeData()) { assert subType != null : "subtype array must not be sparse"; - assert !(subType instanceof BSubType) : "expect pure semtype"; if (!subType.isEmpty(cx)) { return false; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java index b28883f506df..37b8cbcb65e3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java @@ -18,9 +18,13 @@ package io.ballerina.runtime.api.types.semtype; -public interface MutableSemType extends SemType { +import io.ballerina.runtime.internal.types.BType; + +public sealed interface MutableSemType permits BType { SemType createSemType(); void resetSemType(); + + void updateInnerSemTypeIfNeeded(); } 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 index 9a4bb5d9e3c3..09eb185fd16a 100644 --- 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 @@ -1,46 +1,68 @@ -/* - * 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.PureSemType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; + +public sealed class SemType extends BasicTypeBitSet + permits io.ballerina.runtime.internal.types.BType, ImmutableSemType { -public interface SemType { + private int some; + private SubType[] subTypeData; + + protected SemType(int all, int some, SubType[] subTypeData) { + super(all); + this.some = some; + this.subTypeData = subTypeData; + } - static SemType from(int all, int some, SubType[] subTypeData) { - return new PureSemType(all, some, subTypeData); + protected SemType() { + this(-1, -1, null); } - static SemType from(int all) { - return new PureSemType(all); + public static SemType from(int all) { + return new SemType(all, 0, null); } - int all(); + public static SemType from(int all, int some, SubType[] subTypes) { + return new SemType(all, some, subTypes); + } - int some(); + public final int some() { + return some; + } - SubType[] subTypeData(); + public final SubType[] subTypeData() { + return subTypeData; + } - CachedResult cachedSubTypeRelation(SemType other); + public CachedResult cachedSubTypeRelation(SemType other) { + return CachedResult.NOT_FOUND; + } - void cacheSubTypeRelation(SemType other, boolean result); + public void cacheSubTypeRelation(SemType other, boolean result) { - SubType subTypeByCode(int code); + } + + 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; + } public enum CachedResult { TRUE, diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java index 2b98c3876148..6597871f44f5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java @@ -19,7 +19,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Definition; -import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.TypeWithShape; import java.util.Optional; @@ -247,11 +246,6 @@ default TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } - @Override - default void cacheShape(SemType semType) { - throw new UnsupportedOperationException("Method not implemented"); - } - @Override default Optional getReadonlyShapeDefinition() { throw new UnsupportedOperationException("Method not implemented"); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index f554830fbdc6..5ebca7e87455 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -19,7 +19,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Definition; -import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.TypeWithShape; import java.util.Collection; @@ -203,11 +202,6 @@ default TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } - @Override - default void cacheShape(SemType semType) { - throw new UnsupportedOperationException("Method not implemented"); - } - @Override default Optional getReadonlyShapeDefinition() { throw new UnsupportedOperationException("Method not implemented"); 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 53f353f89346..b707293ee83b 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 @@ -63,7 +63,7 @@ default String informalStringValue(BLink parent) { Type getType(); default SemType widenedType(Context cx) { - return getType(); + return SemType.tryInto(getType()); } default Optional shapeOf(Context cx) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java index be744541529d..0415d8373f79 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.Type; 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; @@ -116,7 +117,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { - return Core.containsBasicType(type, Builder.nilType()); + return Core.containsBasicType(SemType.tryInto(type), Builder.nilType()); } public static BError createOpNotSupportedError(Type type, String op) { 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 51c145fc770c..b795e488a955 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 @@ -25,7 +25,6 @@ import io.ballerina.runtime.api.types.FunctionType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.MethodType; -import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; @@ -130,8 +129,8 @@ public static Object checkCast(Object sourceVal, Type targetType) { return sourceVal; } Type sourceType = getType(sourceVal); - if (Core.containsBasicType(sourceType, CONVERTIBLE_CAST_MASK) && - Core.containsBasicType(targetType, CONVERTIBLE_CAST_MASK)) { + if (Core.containsBasicType(SemType.tryInto(sourceType), CONVERTIBLE_CAST_MASK) && + Core.containsBasicType(SemType.tryInto(targetType), CONVERTIBLE_CAST_MASK)) { // We need to maintain order for these? if (targetType instanceof BUnionType unionType) { for (Type memberType : unionType.getMemberTypes()) { @@ -265,11 +264,12 @@ public static boolean anyToJBoolean(Object sourceVal) { */ public static boolean checkIsType(Object sourceVal, Type targetType) { Context cx = context(); - SemType sourceSemType = getType(sourceVal); - if (Core.isSubType(cx, sourceSemType, targetType)) { + SemType sourceSemType = SemType.tryInto(getType(sourceVal)); + SemType semTargetType = SemType.tryInto(targetType); + if (Core.isSubType(cx, sourceSemType, semTargetType)) { return true; } - return couldShapeBeDifferent(sourceSemType) && isSubTypeWithShape(cx, sourceVal, targetType); + return couldShapeBeDifferent(sourceSemType) && isSubTypeWithShape(cx, sourceVal, semTargetType); } /** @@ -317,7 +317,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole Optional readonlyShape = Builder.readonlyShapeOf(cx, sourceValue); assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); - SemType targetSemType = targetType; + SemType targetSemType = SemType.tryInto(targetType); if (Core.isSubType(cx, shape, NUMERIC_TYPE) && allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); } @@ -346,7 +346,7 @@ private static SemType appendNumericConversionTypes(SemType semType) { * @return true if the two types are same; false otherwise */ public static boolean isSameType(Type sourceType, Type targetType) { - return Core.isSameType(context(), sourceType, targetType); + return Core.isSameType(context(), SemType.tryInto(sourceType), SemType.tryInto(targetType)); } public static Type getType(Object value) { @@ -600,7 +600,7 @@ private static SemType createNumericType() { } public static boolean isNumericType(Type type) { - return Core.isSubType(context(), type, NUMERIC_TYPE); + return Core.isSubType(context(), SemType.tryInto(type), NUMERIC_TYPE); } static boolean isByteLiteral(long longValue) { @@ -616,13 +616,7 @@ private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemTyp } private static boolean isSubType(Type source, Type target) { - if (source instanceof ParameterizedType sourceParamType) { - if (target instanceof ParameterizedType targetParamType) { - return isSubType(sourceParamType.getParamValueType(), targetParamType.getParamValueType()); - } - return isSubType(sourceParamType.getParamValueType(), target); - } - return Core.isSubType(context(), source, target); + return Core.isSubType(context(), SemType.tryInto(source), SemType.tryInto(target)); } private static SemType widenedType(Context cx, Object value) { @@ -653,7 +647,8 @@ private static SemType createInherentlyImmutableType() { public static boolean isInherentlyImmutableType(Type sourceType) { // readonly part is there to match to old API return - Core.isSubType(context(), sourceType, INHERENTLY_IMMUTABLE_TYPE) || sourceType instanceof ReadonlyType; + Core.isSubType(context(), SemType.tryInto(sourceType), INHERENTLY_IMMUTABLE_TYPE) || + sourceType instanceof ReadonlyType; } // NOTE: this is not the same as selectively immutable as it stated in the spec @@ -1247,8 +1242,9 @@ static boolean isSimpleBasicSemType(SemType semType) { static boolean belongToSingleBasicTypeOrString(Type type) { Context cx = context(); - return isSingleBasicType(type) && Core.isSubType(cx, type, Builder.simpleOrStringType()) && - !Core.isSubType(cx, type, Builder.nilType()); + SemType semType = SemType.tryInto(type); + return isSingleBasicType(semType) && Core.isSubType(cx, semType, Builder.simpleOrStringType()) && + !Core.isSubType(cx, semType, Builder.nilType()); } private static boolean isSingleBasicType(SemType semType) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 4262def93ff7..7109627c2f50 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -146,7 +146,7 @@ private static Object castValueToInt(SemType targetType, Object inputValue) { return anyToByteCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); } - Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, subType); + Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, SemType.tryInto(subType)); if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_32)) { return anyToSigned32(inputValue); } @@ -170,7 +170,8 @@ private static Object castValueToInt(SemType targetType, Object inputValue) { } public static Object castValues(Type targetType, Object inputValue) { - return castValuesInner(targetType, inputValue, () -> ErrorUtils.createTypeCastError(inputValue, targetType)); + return castValuesInner(SemType.tryInto(targetType), inputValue, + () -> ErrorUtils.createTypeCastError(inputValue, targetType)); } static Object castValuesInner(SemType targetType, Object inputValue, Supplier errorSupplier) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java index a271ff313efc..248c851a1f4a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java @@ -182,9 +182,11 @@ private static Object xmlSequenceHack(Object value, Type targetType) { } Context cx = TypeChecker.context(); List list = new ArrayList<>(); + SemType targetSemType = SemType.tryInto(targetType); for (BXml child : xmlSequence.getChildrenList()) { - SemType childType = child.getType(); - boolean isReadonly = Core.isSubType(cx, Core.intersect(childType, targetType), Builder.readonlyType()); + SemType childType = SemType.tryInto(child.getType()); + boolean isReadonly = + Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.readonlyType()); if (isReadonly) { list.add((BXml) CloneUtils.cloneReadOnly(child)); } else { 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 707106ce617b..b0c21310d379 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 @@ -25,10 +25,8 @@ import io.ballerina.runtime.api.types.AnydataType; 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.values.RefValue; /** @@ -36,7 +34,7 @@ * * @since 0.995.0 */ -public class BAnydataType extends BUnionType implements AnydataType, SemType { +public class BAnydataType extends BUnionType implements AnydataType { /** * Create a {@code BAnydataType} which represents the anydata type. * @@ -94,7 +92,6 @@ public String toString() { // TODO: this type don't have mutable parts so this should be a immutable semtype @Override public SemType createSemType() { - Context cx = TypeChecker.context(); SemType semType = Builder.anyDataType(); if (isReadOnly()) { semType = Core.intersect(semType, Builder.readonlyType()); 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 c4249d3afd10..b89a8d00fa98 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 @@ -409,7 +409,7 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje private static SemType fieldShape(Context cx, ShapeSupplier shapeSupplier, Field field, AbstractObjectValue objectValue, boolean isImmutable) { if (!isImmutable) { - return field.getFieldType(); + return SemType.tryInto(field.getFieldType()); } BString fieldName = StringUtils.fromString(field.getFieldName()); Optional shape = shapeSupplier.get(cx, objectValue.get(fieldName)); 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 42fa4a8153be..a030272e44f2 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 @@ -88,6 +88,6 @@ public SemType createSemType() { if (paramValueType instanceof BType bType) { return bType.createSemType(); } - return paramValueType; + return SemType.tryInto(paramValueType); } } 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 446ba233a414..30b59fb74996 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 @@ -326,7 +326,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap optionalField = false; fieldType = shapeSupplier.get(cx, fieldValue); } else { - SemType fieldSemType = fieldType(fieldName); + SemType fieldSemType = SemType.tryInto(fieldType(fieldName)); assert !Core.containsBasicType(fieldSemType, Builder.bType()); fieldType = Optional.of(fieldSemType); } @@ -342,7 +342,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap } boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - SemType fieldType = field.getFieldType(); + SemType fieldType = SemType.tryInto(field.getFieldType()); if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { fieldType = Builder.undef(); } @@ -356,7 +356,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap if (readonly) { semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, neverType(), CELL_MUT_NONE); } else { - SemType rest = restFieldType != null ? restFieldType : neverType(); + SemType rest = restFieldType != null ? SemType.tryInto(restFieldType) : neverType(); assert !Core.containsBasicType(rest, Builder.bType()); semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); } 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 6fd9fa80d955..614ec058cb4b 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 @@ -223,7 +223,7 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { SemType constraintType = Builder.neverType(); for (var value : table.values()) { - SemType valueShape = shapeSupplier.get(cx, value).orElse(constraint); + SemType valueShape = shapeSupplier.get(cx, value).orElse(SemType.tryInto(constraint)); constraintType = Core.union(constraintType, valueShape); } return createSemTypeWithConstraint(constraintType); 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 7576d1c82cc4..606c36105d55 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 @@ -25,11 +25,9 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.MutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; -import io.ballerina.runtime.api.types.semtype.SubType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.MutableSemTypeDependencyManager; -import io.ballerina.runtime.internal.types.semtype.SubTypeData; import java.util.Map; import java.util.Objects; @@ -45,7 +43,7 @@ * * @since 0.995.0 */ -public abstract class BType implements Type, SubTypeData, MutableSemType, Cloneable { +public abstract non-sealed class BType extends SemType implements Type, MutableSemType, Cloneable { protected String typeName; protected Module pkg; @@ -251,32 +249,25 @@ public SemType createSemType() { throw new IllegalStateException("Child that are used for type checking must implement this method"); } - protected SemType getSemType() { + @Override + public void updateInnerSemTypeIfNeeded() { SemType semType = cachedSemType; - if (semType != null) { - return semType; + if (semType == null) { + synchronized (this) { + semType = cachedSemType; + if (semType == null) { + semType = createSemType(); + cachedSemType = semType; + setAll(cachedSemType.all()); + setSome(cachedSemType.some(), cachedSemType.subTypeData()); + } + } } - semType = createSemType(); - cachedSemType = semType; - return semType; - } - - @Override - public int all() { - getSemType(); - return cachedSemType.all(); } - @Override - public int some() { - getSemType(); - return cachedSemType.some(); - } - - @Override - public SubType[] subTypeData() { - getSemType(); - return cachedSemType.subTypeData(); + protected SemType getSemType() { + updateInnerSemTypeIfNeeded(); + return cachedSemType; } @Override @@ -293,16 +284,13 @@ public void cacheSubTypeRelation(SemType other, boolean result) { cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); } - @Override - public SubType subTypeByCode(int code) { - getSemType(); - return cachedSemType.subTypeByCode(code); - } - @Override public void resetSemType() { + boolean shouldResetDependencies = cachedSemType != null; cachedSemType = null; - mutableSemTypeDependencyManager.notifyDependenciesToReset(this); + if (shouldResetDependencies) { + mutableSemTypeDependencyManager.notifyDependenciesToReset(this); + } } @Override diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java deleted file mode 100644 index 744b72205aad..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CopyOnWriteBMapWrapper.java +++ /dev/null @@ -1,171 +0,0 @@ -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.MapType; -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.types.semtype.SubType; - -import java.util.Optional; - -public final class CopyOnWriteBMapWrapper implements MapType, TypeWithShape { - - private BMapType inner; - - public CopyOnWriteBMapWrapper(BMapType inner) { - this.inner = inner; - } - - private void copyOnWrite() { - inner = inner.clone(); - } - - @Override - public Type getConstrainedType() { - return inner.getConstrainedType(); - } - - @Override - public V getZeroValue() { - return inner.getZeroValue(); - } - - @Override - public V getEmptyValue() { - return inner.getEmptyValue(); - } - - @Override - public int getTag() { - return inner.getTag(); - } - - @Override - public boolean isNilable() { - return inner.isNilable(); - } - - @Override - public String getName() { - return inner.getName(); - } - - @Override - public String getQualifiedName() { - return inner.getQualifiedName(); - } - - @Override - public Module getPackage() { - return inner.getPackage(); - } - - @Override - public boolean isPublic() { - return inner.isPublic(); - } - - @Override - public boolean isNative() { - return inner.isNative(); - } - - @Override - public boolean isAnydata() { - return inner.isAnydata(); - } - - @Override - public boolean isPureType() { - return inner.isPureType(); - } - - @Override - public boolean isReadOnly() { - return inner.isReadOnly(); - } - - @Override - public long getFlags() { - return inner.getFlags(); - } - - @Override - public IntersectionType getImmutableType() { - return inner.getImmutableType(); - } - - @Override - public void setImmutableType(IntersectionType immutableType) { - copyOnWrite(); - inner.setImmutableType(immutableType); - } - - @Override - public Module getPkg() { - return inner.getPkg(); - } - - @Override - public Optional getIntersectionType() { - return inner.getIntersectionType(); - } - - @Override - public void setIntersectionType(IntersectionType intersectionType) { - copyOnWrite(); - inner.setImmutableType(intersectionType); - } - - @Override - public int all() { - return inner.all(); - } - - @Override - public int some() { - return inner.some(); - } - - @Override - public SubType[] subTypeData() { - return inner.subTypeData(); - } - - @Override - public CachedResult cachedSubTypeRelation(SemType other) { - return inner.cachedSubTypeRelation(other); - } - - @Override - public void cacheSubTypeRelation(SemType other, boolean result) { - inner.cacheSubTypeRelation(other, result); - } - - @Override - public SubType subTypeByCode(int code) { - return inner.subTypeByCode(code); - } - - @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { - return inner.shapeOf(cx, shapeSupplierFn, object); - } - - @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { - return inner.readonlyShapeOf(cx, shapeSupplierFn, object); - } - - @Override - public boolean couldShapeBeDifferent() { - return inner.couldShapeBeDifferent(); - } - - @Override - public String toString() { - return inner.toString(); - } -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java deleted file mode 100644 index f67f47b8a2f0..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BSubType.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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; -import io.ballerina.runtime.internal.types.BType; - -/** - * Runtime representation of BType part of a semtype. - * - * @since 2201.10.0 - */ -public class BSubType extends SubType { - - private final BType data; - - private BSubType(BType innerType) { - super(false, false); - data = innerType; - } - - public static BSubType wrap(BType innerType) { - return new BSubType(innerType); - } - - // NOTE: we are allowing isAll() and isNothing() (from the parent) so we can get the union of PureSemTypes and - // PureBTypes. All other operations are unsupported for BSubType - @Override - public SubType union(SubType other) { - throw new IllegalArgumentException("BSubType don't support semType operations"); - } - - @Override - public SubType intersect(SubType other) { - throw new IllegalArgumentException("BSubType don't support semType operations"); - } - - @Override - public SubType diff(SubType other) { - throw new IllegalArgumentException("BSubType don't support semType operations"); - } - - @Override - public SubType complement() { - throw new IllegalArgumentException("BSubType don't support semType operations"); - } - - @Override - public boolean isEmpty(Context cx) { - throw new IllegalArgumentException("BSubType don't support semType operations"); - } - - @Override - public SubTypeData data() { - return data; - } -} 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 index ffa73d986eeb..0dc366db47d6 100644 --- 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 @@ -33,21 +33,15 @@ * * @since 2201.10.0 */ -public abstract sealed class ImmutableSemType implements SemType permits BSemTypeWrapper, PureSemType { +public abstract sealed class ImmutableSemType extends SemType permits BSemTypeWrapper, PureSemType { private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); - private final int all; - private final int some; - private final SubType[] subTypeData; - private Integer hashCode; ImmutableSemType(int all, int some, SubType[] subTypeData) { - this.all = all; - this.some = some; - this.subTypeData = subTypeData; + super(all, some, subTypeData); } ImmutableSemType(int all) { @@ -63,21 +57,6 @@ public String toString() { return SemTypeHelper.stringRepr(this); } - @Override - public final int all() { - return all; - } - - @Override - public final int some() { - return some; - } - - @Override - public final SubType[] subTypeData() { - return subTypeData; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -87,7 +66,7 @@ public boolean equals(Object o) { return false; } return all() == semType.all() && some() == semType.some() && - Objects.deepEquals(subTypeData, semType.subTypeData); + Objects.deepEquals(this.subTypeData(), semType.subTypeData()); } @Override @@ -105,7 +84,7 @@ public int hashCode() { } private int computeHashCode() { - return Objects.hash(all(), some(), Arrays.hashCode(subTypeData)); + return Objects.hash(all(), some(), Arrays.hashCode(subTypeData())); } private boolean shouldCache() { @@ -127,14 +106,4 @@ private boolean isValidCacheState(SemType other, boolean result) { return cachedResult == CachedResult.NOT_FOUND || cachedResult == (result ? CachedResult.TRUE : CachedResult.FALSE); } - - @Override - 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)]; - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java index 8f5aa2d85663..372bbc760ef7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java @@ -47,15 +47,18 @@ public void notifyDependenciesToReset(MutableSemType semType) { Object lock = getLock(semType); synchronized (lock) { List> mutableSemTypes = dependencies.get(semType); + List toBeRecalculated = new ArrayList<>(); if (mutableSemTypes != null) { dependencies.remove(semType); for (var dependent : mutableSemTypes) { MutableSemType dependentSemType = dependent.get(); if (dependentSemType != null) { dependentSemType.resetSemType(); + toBeRecalculated.add(dependentSemType); } } } + toBeRecalculated.forEach(MutableSemType::updateInnerSemTypeIfNeeded); } } @@ -64,7 +67,7 @@ public SemType getSemType(Type target, MutableSemType self) { if (target instanceof MutableSemType mutableTarget) { addDependency(self, mutableTarget); } - return target; + return SemType.tryInto(target); } private void addDependency(MutableSemType self, MutableSemType mutableTarget) { 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 3ea123c0553e..fb3e117ddd15 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 @@ -140,6 +140,6 @@ public SemType widenedType(Context cx) { @Override public Optional shapeOf(Context cx) { - return Optional.of(getType()); + return Optional.of(SemType.tryInto(getType())); } } 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 d82385580e42..b8df500a3b8b 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 @@ -83,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) { 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 ae5fbd90e50d..d92dc7feedf8 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 @@ -38,7 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; - import javax.xml.namespace.QName; import static io.ballerina.runtime.internal.ValueUtils.getTypedescValue; From 1be568a26bd5dc5fa12c7a86875c4d5286d83858 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 1 Sep 2024 15:26:55 +0530 Subject: [PATCH 147/178] Make tag directly accesible --- .../runtime/internal/types/BAnyType.java | 4 +-- .../runtime/internal/types/BBooleanType.java | 2 +- .../runtime/internal/types/BByteType.java | 2 +- .../runtime/internal/types/BDecimalType.java | 2 +- .../runtime/internal/types/BFloatType.java | 4 +-- .../runtime/internal/types/BHandleType.java | 2 +- .../runtime/internal/types/BIntegerType.java | 11 ++++---- .../runtime/internal/types/BNeverType.java | 9 ++----- .../runtime/internal/types/BNullType.java | 12 ++++----- .../runtime/internal/types/BReadonlyType.java | 4 +-- .../internal/types/BSemTypeWrapper.java | 27 +++++++++++-------- .../runtime/internal/types/BStringType.java | 11 ++++---- 12 files changed, 46 insertions(+), 44 deletions(-) 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 98314793cb49..1bf7847bbf9d 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 @@ -38,7 +38,7 @@ * * @since 0.995.0 */ -public class BAnyType extends BSemTypeWrapper implements AnyType { +public final class BAnyType extends BSemTypeWrapper implements AnyType { /** * Create a {@code BAnyType} which represents the any type. @@ -47,7 +47,7 @@ public class BAnyType extends BSemTypeWrapper implements */ public BAnyType(String typeName, Module pkg, boolean readonly) { super(new ConcurrentLazySupplier<>(() -> new BAnyTypeImpl(typeName, pkg, readonly)), - typeName, pickSemType(readonly)); + typeName, TypeTags.ANY_TAG, pickSemType(readonly)); } @Override 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 42c83507fa9a..122e59317407 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 @@ -56,7 +56,7 @@ public static BBooleanType singletonType(boolean value) { } private BBooleanType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, semType); + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, TypeTags.BOOLEAN_TAG, semType); } protected static final class BBooleanTypeImpl extends BType implements BooleanType { 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 72fda2ddef85..50b6b3587ea1 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 @@ -50,7 +50,7 @@ public BByteType(String typeName, Module pkg) { } private BByteType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, semType); + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, TypeTags.BYTE_TAG, semType); } public static BByteType singletonType(long value) { 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 3ada48c25779..19d45d20ce60 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 @@ -58,7 +58,7 @@ public static BDecimalType singletonType(BigDecimal value) { } private BDecimalType(Supplier bType, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bType), typeName, semType); + super(new ConcurrentLazySupplier<>(bType), typeName, TypeTags.DECIMAL_TAG, semType); } protected static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { 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 3a46b3102c34..1be41a5fd4ea 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 @@ -36,7 +36,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BFloatType extends BSemTypeWrapper implements FloatType { +public final class BFloatType extends BSemTypeWrapper implements FloatType { /** * Create a {@code BFloatType} which represents the boolean type. @@ -48,7 +48,7 @@ public BFloatType(String typeName, Module pkg) { } private BFloatType(Supplier bType, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bType), typeName, semType); + super(new ConcurrentLazySupplier<>(bType), typeName, TypeTags.FLOAT_TAG, semType); } public static BFloatType singletonType(Double value) { 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 0742e63b9689..ba7e2472234c 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 @@ -41,7 +41,7 @@ public final class BHandleType extends BSemTypeWrapper - (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, Builder.handleType()); + (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, TypeTags.HANDLE_TAG, Builder.handleType()); } protected static final class BHandleTypeImpl extends BType implements HandleType { 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 62840f725b67..33ce5c3939a6 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 @@ -55,15 +55,16 @@ public final class BIntegerType extends BSemTypeWrapper new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, Builder.intType()); + this(() -> new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, TypeTags.INT_TAG, + Builder.intType()); } public BIntegerType(String typeName, Module pkg, int tag) { - this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, pickSemType(tag)); + this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, tag, pickSemType(tag)); } - private BIntegerType(Supplier bIntegerTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bIntegerTypeSupplier), typeName, semType); + private BIntegerType(Supplier bIntegerTypeSupplier, String typeName, int tag, SemType semType) { + super(new ConcurrentLazySupplier<>(bIntegerTypeSupplier), typeName, tag, semType); } private static SemType pickSemType(int tag) { @@ -88,7 +89,7 @@ public static BIntegerType singletonType(long value) { private static BIntegerType createSingletonType(long value) { return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, - Builder.intConst(value)); + TypeTags.INT_TAG, Builder.intConst(value)); } protected static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { 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 2667619f6887..ec5357559229 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 @@ -28,23 +28,18 @@ * * @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, Builder.neverType()); + super(TypeConstants.NEVER_TNAME, pkg, Builder.neverType(), 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 1abc907780df..331f4ddfa4ce 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 @@ -31,7 +31,7 @@ * * @since 0.995.0 */ -public class BNullType extends BSemTypeWrapper implements NullType { +public sealed class BNullType extends BSemTypeWrapper implements NullType permits BNeverType { /** * Create a {@code BNullType} represents the type of a {@code NullLiteral}. @@ -40,15 +40,15 @@ public class BNullType extends BSemTypeWrapper implemen * @param pkg package path */ public BNullType(String typeName, Module pkg) { - this(() -> new BNullTypeImpl(typeName, pkg), typeName, Builder.nilType()); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, TypeTags.NULL_TAG, Builder.nilType()); } - protected BNullType(String typeName, Module pkg, SemType semType) { - this(() -> new BNullTypeImpl(typeName, pkg), typeName, semType); + protected BNullType(String typeName, Module pkg, SemType semType, int tag) { + this(() -> new BNullTypeImpl(typeName, pkg), typeName, tag, semType); } - private BNullType(Supplier bNullTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, semType); + private BNullType(Supplier bNullTypeSupplier, String typeName, int tag, SemType semType) { + super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, tag, semType); } protected static final class BNullTypeImpl extends BType implements NullType { 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 bc324e1f8b31..9990dfe24a33 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 @@ -29,10 +29,10 @@ * * @since 1.3.0 */ -public class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { +public final class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, + super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, TypeTags.READONLY_TAG, Builder.readonlyType()); } 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 index ba5e0f03a10f..0ce96d4d752a 100644 --- 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 @@ -35,55 +35,60 @@ * @param The type of the {@code BType} that is being wrapped. * @since 2201.10.0 */ -public non-sealed class BSemTypeWrapper extends ImmutableSemType implements Type { +public sealed class BSemTypeWrapper extends ImmutableSemType implements Type + permits BAnyType, BBooleanType, BByteType, BDecimalType, BFloatType, BHandleType, BIntegerType, BNullType, + BReadonlyType, BStringType { private final Supplier bTypeSupplier; + private final int tag; protected final String typeName; // Debugger uses this field to show the type name - BSemTypeWrapper(Supplier bTypeSupplier, String typeName, SemType semType) { + protected BSemTypeWrapper(Supplier bTypeSupplier, String typeName, int tag, SemType semType) { super(semType); this.bTypeSupplier = bTypeSupplier; this.typeName = typeName; + this.tag = tag; } - public Class getValueClass() { + final public Class getValueClass() { return getbType().getValueClass(); } - public V getZeroValue() { + @Override + final public V getZeroValue() { return getbType().getZeroValue(); } @Override - public V getEmptyValue() { + final public V getEmptyValue() { return getbType().getEmptyValue(); } @Override - public int getTag() { - return getbType().getTag(); + final public int getTag() { + return tag; } @Override - public String toString() { + public final String toString() { return getbType().toString(); } @Override public boolean equals(Object obj) { - if (!(obj instanceof BSemTypeWrapper other)) { + if (!(obj instanceof BSemTypeWrapper other)) { return false; } return getbType().equals(other.getbType()); } @Override - public boolean isNilable() { + public final boolean isNilable() { return getbType().isNilable(); } @Override - public int hashCode() { + public final int hashCode() { return getbType().hashCode(); } 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 29af4accb9da..b85e3fd418a7 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 @@ -47,20 +47,21 @@ public final class BStringType extends BSemTypeWrapper new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, Builder.stringType()); + this(() -> new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, TypeTags.STRING_TAG, + Builder.stringType()); } public BStringType(String typeName, Module pkg, int tag) { - this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, pickSemtype(tag)); + this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, tag, pickSemtype(tag)); } - private BStringType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, semType); + private BStringType(Supplier bTypeSupplier, String typeName, int tag, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, tag, semType); } public static BStringType singletonType(String value) { return new BStringType(() -> (BStringTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.STRING_TNAME, - Builder.stringConst(value)); + TypeTags.STRING_TAG, Builder.stringConst(value)); } private static SemType pickSemtype(int tag) { From d6162eaffd855d0677ad7b56666c954c1b2a6a77 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 1 Sep 2024 15:40:52 +0530 Subject: [PATCH 148/178] Avoid creating BType when trying to calculate hash and equal for wrapper --- .../runtime/internal/types/BAnyType.java | 2 +- .../runtime/internal/types/BBooleanType.java | 10 +++++----- .../runtime/internal/types/BByteType.java | 8 ++++---- .../runtime/internal/types/BDecimalType.java | 8 ++++---- .../runtime/internal/types/BFloatType.java | 8 ++++---- .../runtime/internal/types/BHandleType.java | 3 ++- .../runtime/internal/types/BIntegerType.java | 15 ++++++++------- .../runtime/internal/types/BNullType.java | 9 +++++---- .../runtime/internal/types/BReadonlyType.java | 4 ++-- .../runtime/internal/types/BSemTypeWrapper.java | 9 ++++++--- .../runtime/internal/types/BStringType.java | 14 ++++++++------ 11 files changed, 49 insertions(+), 41 deletions(-) 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 1bf7847bbf9d..a907e33df01e 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 @@ -47,7 +47,7 @@ public final class BAnyType extends BSemTypeWrapper imple */ public BAnyType(String typeName, Module pkg, boolean readonly) { super(new ConcurrentLazySupplier<>(() -> new BAnyTypeImpl(typeName, pkg, readonly)), - typeName, TypeTags.ANY_TAG, pickSemType(readonly)); + typeName, pkg, TypeTags.ANY_TAG, pickSemType(readonly)); } @Override 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 122e59317407..888343f142a7 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 @@ -37,10 +37,10 @@ public final class BBooleanType extends BSemTypeWrapper new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), - TypeConstants.BOOLEAN_TNAME, Builder.booleanConst(true)); + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(true)); private static final BBooleanType FALSE = new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), - TypeConstants.BOOLEAN_TNAME, Builder.booleanConst(false)); + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. @@ -48,15 +48,15 @@ public final class BBooleanType extends BSemTypeWrapper new BBooleanTypeImpl(typeName, pkg), typeName, Builder.booleanType()); + this(() -> new BBooleanTypeImpl(typeName, pkg), typeName, pkg, Builder.booleanType()); } public static BBooleanType singletonType(boolean value) { return value ? TRUE : FALSE; } - private BBooleanType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, TypeTags.BOOLEAN_TAG, semType); + private BBooleanType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, TypeTags.BOOLEAN_TAG, semType); } protected static final class BBooleanTypeImpl extends BType implements BooleanType { 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 50b6b3587ea1..59f51aa10b39 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 @@ -46,15 +46,15 @@ public final class BByteType extends BSemTypeWrapper im * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { - this(() -> new BByteTypeImpl(typeName, pkg), typeName, Builder.intRange(0, UNSIGNED8_MAX_VALUE)); + this(() -> new BByteTypeImpl(typeName, pkg), typeName, EMPTY_MODULE, Builder.intRange(0, UNSIGNED8_MAX_VALUE)); } - private BByteType(Supplier bTypeSupplier, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, TypeTags.BYTE_TAG, semType); + private BByteType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, TypeTags.BYTE_TAG, semType); } public static BByteType singletonType(long value) { - return new BByteType(() -> (BByteTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.BYTE_TNAME, + return new BByteType(() -> (BByteTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.BYTE_TNAME, EMPTY_MODULE, Builder.intConst(value)); } 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 19d45d20ce60..9341bbe63c39 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 @@ -49,16 +49,16 @@ public final class BDecimalType extends BSemTypeWrapper new BDecimalTypeImpl(typeName, pkg), typeName, Builder.decimalType()); + this(() -> new BDecimalTypeImpl(typeName, pkg), typeName, pkg, Builder.decimalType()); } public static BDecimalType singletonType(BigDecimal value) { return new BDecimalType(() -> (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.DECIMAL_TNAME, - Builder.decimalConst(value)); + EMPTY_MODULE, Builder.decimalConst(value)); } - private BDecimalType(Supplier bType, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bType), typeName, TypeTags.DECIMAL_TAG, semType); + private BDecimalType(Supplier bType, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bType), typeName, pkg, TypeTags.DECIMAL_TAG, semType); } protected static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { 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 1be41a5fd4ea..8ab193e8d89a 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 @@ -44,16 +44,16 @@ public final class BFloatType extends BSemTypeWrapper * @param typeName string name of the type */ public BFloatType(String typeName, Module pkg) { - this(() -> new BFloatTypeImpl(typeName, pkg), typeName, Builder.floatType()); + this(() -> new BFloatTypeImpl(typeName, pkg), typeName, pkg, Builder.floatType()); } - private BFloatType(Supplier bType, String typeName, SemType semType) { - super(new ConcurrentLazySupplier<>(bType), typeName, TypeTags.FLOAT_TAG, semType); + private BFloatType(Supplier bType, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bType), typeName, pkg, TypeTags.FLOAT_TAG, semType); } public static BFloatType singletonType(Double value) { return new BFloatType(() -> new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), - TypeConstants.FLOAT_TNAME, Builder.floatConst(value)); + TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.floatConst(value)); } protected static final class BFloatTypeImpl extends BType implements FloatType { 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 ba7e2472234c..8ed70dbed88d 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 @@ -41,7 +41,8 @@ public final class BHandleType extends BSemTypeWrapper - (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, TypeTags.HANDLE_TAG, Builder.handleType()); + (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, pkg, TypeTags.HANDLE_TAG, + Builder.handleType()); } protected static final class BHandleTypeImpl extends BType implements HandleType { 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 33ce5c3939a6..d7ae1f17c97b 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 @@ -18,7 +18,6 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntegerType; @@ -28,6 +27,7 @@ import java.util.function.Supplier; +import static io.ballerina.runtime.api.PredefinedTypes.EMPTY_MODULE; 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; @@ -47,7 +47,7 @@ public final class BIntegerType extends BSemTypeWrapper implements IntegerType { private static final BIntegerTypeImpl DEFAULT_B_TYPE = - new BIntegerTypeImpl(TypeConstants.INT_TNAME, PredefinedTypes.EMPTY_MODULE, TypeTags.INT_TAG); + new BIntegerTypeImpl(TypeConstants.INT_TNAME, EMPTY_MODULE, TypeTags.INT_TAG); /** * Create a {@code BIntegerType} which represents the boolean type. @@ -55,16 +55,17 @@ public final class BIntegerType extends BSemTypeWrapper new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, TypeTags.INT_TAG, + this(() -> new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, pkg, TypeTags.INT_TAG, Builder.intType()); } public BIntegerType(String typeName, Module pkg, int tag) { - this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, tag, pickSemType(tag)); + this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, pkg, tag, pickSemType(tag)); } - private BIntegerType(Supplier bIntegerTypeSupplier, String typeName, int tag, SemType semType) { - super(new ConcurrentLazySupplier<>(bIntegerTypeSupplier), typeName, tag, semType); + 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) { @@ -88,7 +89,7 @@ public static BIntegerType singletonType(long value) { } private static BIntegerType createSingletonType(long value) { - return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, + return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, EMPTY_MODULE, TypeTags.INT_TAG, Builder.intConst(value)); } 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 331f4ddfa4ce..9bb1cc11affc 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 @@ -40,15 +40,16 @@ public sealed class BNullType extends BSemTypeWrapper i * @param pkg package path */ public BNullType(String typeName, Module pkg) { - this(() -> new BNullTypeImpl(typeName, pkg), typeName, TypeTags.NULL_TAG, Builder.nilType()); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, TypeTags.NULL_TAG, Builder.nilType()); } protected BNullType(String typeName, Module pkg, SemType semType, int tag) { - this(() -> new BNullTypeImpl(typeName, pkg), typeName, tag, semType); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, tag, semType); } - private BNullType(Supplier bNullTypeSupplier, String typeName, int tag, SemType semType) { - super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, tag, semType); + private BNullType(Supplier bNullTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, pkg, tag, semType); } protected static final class BNullTypeImpl extends BType implements NullType { 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 9990dfe24a33..710b72c25b8a 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 @@ -32,8 +32,8 @@ public final class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, TypeTags.READONLY_TAG, - Builder.readonlyType()); + super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, pkg, + TypeTags.READONLY_TAG, Builder.readonlyType()); } protected static final class BReadonlyTypeImpl extends BType implements ReadonlyType { 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 index 0ce96d4d752a..1246bb5ce80b 100644 --- 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 @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; +import java.util.Objects; import java.util.function.Supplier; // TODO: make this a sealed class with clearly defined extensions @@ -42,12 +43,14 @@ public sealed class BSemTypeWrapper extends ImmutableSemType im 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, int tag, SemType semType) { + 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; } final public Class getValueClass() { @@ -79,7 +82,7 @@ public boolean equals(Object obj) { if (!(obj instanceof BSemTypeWrapper other)) { return false; } - return getbType().equals(other.getbType()); + return Objects.equals(this.typeName, other.typeName) && Objects.equals(this.pkg, other.pkg); } @Override @@ -89,7 +92,7 @@ public final boolean isNilable() { @Override public final int hashCode() { - return getbType().hashCode(); + return Objects.hash(this.typeName, this.pkg); } @Override 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 b85e3fd418a7..dd29f9c16341 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 @@ -38,8 +38,9 @@ public final class BStringType extends BSemTypeWrapper new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, TypeTags.STRING_TAG, + this(() -> new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, pkg, TypeTags.STRING_TAG, Builder.stringType()); } public BStringType(String typeName, Module pkg, int tag) { - this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, tag, pickSemtype(tag)); + this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, pkg, tag, pickSemtype(tag)); } - private BStringType(Supplier bTypeSupplier, String typeName, int tag, SemType semType) { - super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, tag, semType); + private BStringType(Supplier bTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, tag, semType); } public static BStringType singletonType(String value) { return new BStringType(() -> (BStringTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.STRING_TNAME, - TypeTags.STRING_TAG, Builder.stringConst(value)); + DEFAULT_MODULE, TypeTags.STRING_TAG, Builder.stringConst(value)); } private static SemType pickSemtype(int tag) { From aa97891557d6604e5c210e886f6a9432c6c0cd5f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 1 Sep 2024 15:49:24 +0530 Subject: [PATCH 149/178] Use result caching with all semtypes Fix check style violations --- .../runtime/api/types/semtype/SemType.java | 17 ++++++++++---- .../internal/types/BSemTypeWrapper.java | 8 +++---- .../runtime/internal/types/BType.java | 15 ------------ .../types/semtype/ImmutableSemType.java | 23 ------------------- .../runtime/internal/values/XmlValue.java | 1 + 5 files changed, 18 insertions(+), 46 deletions(-) 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 index 09eb185fd16a..5fca6890958c 100644 --- 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 @@ -3,11 +3,15 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; +import java.util.Map; +import java.util.WeakHashMap; + public sealed class SemType extends BasicTypeBitSet permits io.ballerina.runtime.internal.types.BType, ImmutableSemType { private int some; private SubType[] subTypeData; + private Map cachedResults; protected SemType(int all, int some, SubType[] subTypeData) { super(all); @@ -35,12 +39,17 @@ public final SubType[] subTypeData() { return subTypeData; } - public CachedResult cachedSubTypeRelation(SemType other) { - return CachedResult.NOT_FOUND; + public final CachedResult cachedSubTypeRelation(SemType other) { + if (cachedResults == null) { + cachedResults = new WeakHashMap<>(); + return CachedResult.NOT_FOUND; + } + return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); } - public void cacheSubTypeRelation(SemType other, boolean result) { - + public final void cacheSubTypeRelation(SemType other, boolean result) { + // we always check of the result before caching so there will always be a map + cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); } public final SubType subTypeByCode(int code) { 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 index 1246bb5ce80b..8e24e97593ef 100644 --- 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 @@ -53,22 +53,22 @@ protected BSemTypeWrapper(Supplier bTypeSupplier, String typeName, Module pkg this.pkg = pkg; } - final public Class getValueClass() { + public final Class getValueClass() { return getbType().getValueClass(); } @Override - final public V getZeroValue() { + public final V getZeroValue() { return getbType().getZeroValue(); } @Override - final public V getEmptyValue() { + public final V getEmptyValue() { return getbType().getEmptyValue(); } @Override - final public int getTag() { + public final int getTag() { return tag; } 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 606c36105d55..e3f15f2e1f67 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 @@ -31,7 +31,6 @@ import java.util.Map; import java.util.Objects; -import java.util.WeakHashMap; /** * {@code BType} represents a type in Ballerina. @@ -270,20 +269,6 @@ protected SemType getSemType() { return cachedSemType; } - @Override - public CachedResult cachedSubTypeRelation(SemType other) { - if (cachedResults == null) { - cachedResults = new WeakHashMap<>(); - } - return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); - } - - @Override - public void cacheSubTypeRelation(SemType other, boolean result) { - // we always check of the result before caching so there will always be a map - cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); - } - @Override public void resetSemType() { boolean shouldResetDependencies = cachedSemType != null; 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 index 0dc366db47d6..b17d94360d7e 100644 --- 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 @@ -18,7 +18,6 @@ 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 io.ballerina.runtime.internal.types.BSemTypeWrapper; @@ -26,8 +25,6 @@ import java.util.Arrays; import java.util.Objects; -import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; - /** * Runtime representation of SemType. * @@ -36,7 +33,6 @@ public abstract sealed class ImmutableSemType extends SemType permits BSemTypeWrapper, PureSemType { private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; - private static final int CACHEABLE_TYPE_MASK = (~BasicTypeCode.BASIC_TYPE_MASK) & ((1 << (CODE_UNDEF + 1)) - 1); private Integer hashCode; @@ -87,23 +83,4 @@ private int computeHashCode() { return Objects.hash(all(), some(), Arrays.hashCode(subTypeData())); } - private boolean shouldCache() { - return (some() & CACHEABLE_TYPE_MASK) != 0; - } - - @Override - public CachedResult cachedSubTypeRelation(SemType other) { - return CachedResult.NOT_FOUND; - } - - @Override - public void cacheSubTypeRelation(SemType other, boolean result) { - return; - } - - private boolean isValidCacheState(SemType other, boolean result) { - CachedResult cachedResult = cachedSubTypeRelation(other); - return cachedResult == CachedResult.NOT_FOUND || - cachedResult == (result ? CachedResult.TRUE : CachedResult.FALSE); - } } 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 d92dc7feedf8..ae5fbd90e50d 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 @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; + import javax.xml.namespace.QName; import static io.ballerina.runtime.internal.ValueUtils.getTypedescValue; From fcdc9ec26b6272c50abbb5cc0ec7b334b993676f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 2 Sep 2024 08:48:03 +0530 Subject: [PATCH 150/178] Add fast path to hasFillerValue --- .../runtime/internal/TypeChecker.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) 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 b795e488a955..91579de3d187 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 @@ -120,6 +120,7 @@ public final class TypeChecker { private static final SemType REF_TYPE_MASK = createRefValueMask(); private static final SemType CONVERTIBLE_CAST_MASK = createConvertibleCastMask(); private static final byte MAX_TYPECAST_ERROR_COUNT = 20; + private static final SemType TOP_TYPES_WITH_ALWAYS_FILLING = createTopTypesWithFillerValues(); public static Object checkCast(Object sourceVal, Type targetType) { @@ -984,11 +985,40 @@ public static boolean hasFillerValue(Type type) { return hasFillerValue(type, new ArrayList<>()); } + private enum FillerValueResult { + TRUE, FALSE, MAYBE + } + + private static SemType createTopTypesWithFillerValues() { + return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), + Builder.booleanType(), Builder.nilType(), Builder.tableType(), Builder.mappingType(), + Builder.listType()).reduce(Builder.neverType(), Core::union); + } + + private static FillerValueResult hasFillerValueSemType(Context cx, SemType type) { + if (Core.containsBasicType(type, Builder.nilType())) { + 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, 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; @@ -1017,7 +1047,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; } From d3f52628d9dd886eb62b666634afbf9fc51970ee Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 2 Sep 2024 08:48:42 +0530 Subject: [PATCH 151/178] Refactor workarounds to isSubType --- .../runtime/api/types/semtype/Core.java | 5 ----- .../ballerina/runtime/internal/TypeChecker.java | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) 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 index 678f882d9cbc..779dd245c4ad 100644 --- 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 @@ -298,11 +298,6 @@ public static boolean isNever(SemType t) { } public static boolean isSubType(Context cx, SemType t1, SemType t2) { - // 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. - if (t1.equals(t2)) { - return true; - } SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); if (cached != SemType.CachedResult.NOT_FOUND) { return cached == SemType.CachedResult.TRUE; 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 91579de3d187..e6ac9073bf69 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 @@ -265,11 +265,12 @@ public static boolean anyToJBoolean(Object sourceVal) { */ public static boolean checkIsType(Object sourceVal, Type targetType) { Context cx = context(); - SemType sourceSemType = SemType.tryInto(getType(sourceVal)); - SemType semTargetType = SemType.tryInto(targetType); - if (Core.isSubType(cx, sourceSemType, semTargetType)) { + Type sourceType = getType(sourceVal); + if (isSubType(sourceType, targetType)) { return true; } + SemType sourceSemType = SemType.tryInto(sourceType); + SemType semTargetType = SemType.tryInto(targetType); return couldShapeBeDifferent(sourceSemType) && isSubTypeWithShape(cx, sourceVal, semTargetType); } @@ -617,7 +618,15 @@ private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemTyp } private static boolean isSubType(Type source, Type target) { - return Core.isSubType(context(), SemType.tryInto(source), SemType.tryInto(target)); + // 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. + if (source.equals(target)) { + return true; + } + Context cx = context(); + SemType sourceSemType = SemType.tryInto(source); + SemType targetSemType = SemType.tryInto(target); + return Core.isSubType(cx, sourceSemType, targetSemType); } private static SemType widenedType(Context cx, Object value) { From e5d32aaf36f0bdd77ae795d8f1d46ae5aff94dd0 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Mon, 2 Sep 2024 15:12:04 +0530 Subject: [PATCH 152/178] Remove MutableSemTypeDependencyManager --- .../runtime/internal/types/BArrayType.java | 2 +- .../runtime/internal/types/BErrorType.java | 2 +- .../runtime/internal/types/BFunctionType.java | 2 +- .../runtime/internal/types/BFutureType.java | 2 +- .../internal/types/BIntersectionType.java | 4 +- .../runtime/internal/types/BMapType.java | 2 +- .../internal/types/BNetworkObjectType.java | 6 +- .../runtime/internal/types/BObjectType.java | 27 +++--- .../runtime/internal/types/BRecordType.java | 6 +- .../runtime/internal/types/BStreamType.java | 4 +- .../runtime/internal/types/BTableType.java | 4 +- .../runtime/internal/types/BTupleType.java | 5 +- .../runtime/internal/types/BType.java | 7 -- .../internal/types/BTypeReferenceType.java | 2 +- .../runtime/internal/types/BTypedescType.java | 4 +- .../runtime/internal/types/BUnionType.java | 2 +- .../runtime/internal/types/BXmlType.java | 5 +- .../MutableSemTypeDependencyManager.java | 90 ------------------- 18 files changed, 35 insertions(+), 141 deletions(-) delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java 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 45600f24287d..9cb7595e11a6 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 @@ -230,7 +230,7 @@ public synchronized SemType createSemType() { } ListDefinition ld = new ListDefinition(); defn = ld; - SemType elementType = mutableSemTypeDependencyManager.getSemType(getElementType(), this); + SemType elementType = tryInto(getElementType()); assert !Core.containsBasicType(elementType, Core.B_TYPE_TOP) : "Array element can't have BTypes"; return getSemTypePart(ld, isReadOnly(), size, elementType); } 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 8f00fb529e57..f8283d59447d 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 @@ -130,7 +130,7 @@ public synchronized SemType createSemType() { if (detailType == null || isTopType()) { err = Builder.errorType(); } else { - SemType detailType = mutableSemTypeDependencyManager.getSemType(getDetailType(), this); + SemType detailType = tryInto(getDetailType()); assert Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP)); err = ErrorUtils.errorDetail(detailType); } 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 5afc3e4b5d79..f4cd4b50396d 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 @@ -275,7 +275,7 @@ public FunctionQualifiers getQualifiers() { // TODO: consider moving this to builder private SemType getSemType(Type type) { - SemType semType = mutableSemTypeDependencyManager.getSemType(type, this); + SemType semType = tryInto(type); assert !Core.containsBasicType(semType, Builder.bType()) : "function type part with BType"; return semType; } 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 01de16492337..8449b0134b4c 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 @@ -107,7 +107,7 @@ public SemType createSemType() { if (constraint == null) { return Builder.futureType(); } - SemType constraintSemType = mutableSemTypeDependencyManager.getSemType(constraint, this); + SemType constraintSemType = tryInto(constraint); Context cx = TypeChecker.context(); assert !Core.containsBasicType(constraintSemType, Builder.bType()) : "constraint shouldn't have BTypes"; return FutureUtils.futureContaining(cx.env, constraintSemType); 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 39a50038b1df..33ba3f622c5e 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 @@ -230,11 +230,11 @@ public SemType createSemType() { if (constituentTypes.isEmpty()) { return Builder.neverType(); } - SemType result = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(0), this); + SemType result = tryInto(constituentTypes.get(0)); assert !Core.containsBasicType(result, Builder.bType()) : "Intersection constituent cannot be a BType"; result = Core.intersect(result, Core.SEMTYPE_TOP); for (int i = 1; i < constituentTypes.size(); i++) { - SemType memberType = mutableSemTypeDependencyManager.getSemType(constituentTypes.get(i), this); + SemType memberType = tryInto(constituentTypes.get(i)); assert !Core.containsBasicType(memberType, Builder.bType()) : "Intersection constituent cannot be a BType"; result = Core.intersect(result, memberType); } 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 9b59a18c0dbe..216e855b9fb9 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 @@ -191,7 +191,7 @@ public synchronized SemType createSemType() { } MappingDefinition md = new MappingDefinition(); defn = md; - SemType restType = mutableSemTypeDependencyManager.getSemType(getConstrainedType(), this); + SemType restType = tryInto(getConstrainedType()); assert !Core.containsBasicType(restType, Builder.bType()) : "Map shouldn't have BTypes"; return getSemTypePart(md, restType); } 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 0b061fa8d402..c14c38cb686c 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 @@ -89,13 +89,13 @@ protected Collection allMethods() { Stream methodStream = Arrays.stream(getMethods()) .filter(methodType -> !(SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.REMOTE) || SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.RESOURCE))) - .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)); + .map(MethodData::fromMethod); Stream remoteMethodStream = Arrays.stream(getRemoteMethods()) - .map(method -> MethodData.fromRemoteMethod(mutableSemTypeDependencyManager, this, method)); + .map(MethodData::fromRemoteMethod); Stream resoucrMethodStream = Arrays.stream(getResourceMethods()) - .map(method -> MethodData.fromResourceMethod(mutableSemTypeDependencyManager, this, + .map(method -> MethodData.fromResourceMethod( (BResourceMethodType) method)); return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resoucrMethodStream)).toList(); } 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 b89a8d00fa98..1d0e06f2ad0c 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 @@ -36,7 +36,6 @@ 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.MutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; @@ -48,7 +47,6 @@ 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.MutableSemTypeDependencyManager; import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; import io.ballerina.runtime.internal.values.AbstractObjectValue; @@ -309,7 +307,7 @@ private synchronized SemType semTypeInner() { Field field = entry.getValue(); boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - SemType ty = mutableSemTypeDependencyManager.getSemType(field.getFieldType(), this); + SemType ty = tryInto(field.getFieldType()); assert !Core.containsBasicType(ty, Builder.bType()) : "object member can't have BTypes"; members.add(new Member(name, ty, Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); @@ -428,27 +426,24 @@ protected Collection allMethods() { return List.of(); } return Arrays.stream(methodTypes) - .map(method -> MethodData.fromMethod(mutableSemTypeDependencyManager, this, method)).toList(); + .map(MethodData::fromMethod).toList(); } protected record MethodData(String name, long flags, SemType semType) { - static MethodData fromMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, - MethodType method) { + static MethodData fromMethod(MethodType method) { return new MethodData(method.getName(), method.getFlags(), - dependencyManager.getSemType(method.getType(), parent)); + tryInto(method.getType())); } - static MethodData fromRemoteMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, - MethodType method) { + 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(), - dependencyManager.getSemType(method.getType(), parent)); + tryInto(method.getType())); } - static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyManager, MutableSemType parent, - BResourceMethodType method) { + static MethodData fromResourceMethod(BResourceMethodType method) { StringBuilder sb = new StringBuilder(); sb.append(method.getAccessor()); for (var each : method.getResourcePath()) { @@ -463,21 +458,21 @@ static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyM if (part == null) { paramTypes.add(Builder.anyType()); } else { - SemType semType = dependencyManager.getSemType(part, parent); + SemType semType = tryInto(part); assert !Core.containsBasicType(semType, Builder.bType()) : "resource method path segment can't have BType"; paramTypes.add(semType); } } for (Parameter paramType : innerFn.getParameters()) { - SemType semType = dependencyManager.getSemType(paramType.type, parent); + SemType semType = tryInto(paramType.type); assert !Core.containsBasicType(semType, Builder.bType()) : "resource method params can't have BType"; paramTypes.add(semType); } SemType rest; Type restType = innerFn.getRestType(); if (restType instanceof BArrayType arrayType) { - rest = dependencyManager.getSemType(arrayType.getElementType(), parent); + rest = tryInto(arrayType.getElementType()); assert !Core.containsBasicType(rest, Builder.bType()) : "resource method rest can't have BType"; } else { rest = Builder.neverType(); @@ -485,7 +480,7 @@ static MethodData fromResourceMethod(MutableSemTypeDependencyManager dependencyM SemType returnType; if (innerFn.getReturnType() != null) { - returnType = dependencyManager.getSemType(innerFn.getReturnType(), parent); + returnType = tryInto(innerFn.getReturnType()); assert !Core.containsBasicType(returnType, Builder.bType()) : "resource method retType can't have BType"; } else { 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 30b59fb74996..5efa40a03287 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 @@ -260,7 +260,7 @@ public synchronized SemType createSemType() { for (int i = 0; i < fields.length; i++) { Field field = fields[i]; boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); - SemType fieldType = mutableSemTypeDependencyManager.getSemType(field.getFieldType(), this); + SemType fieldType = tryInto(field.getFieldType()); if (!isOptional && Core.isNever(fieldType)) { return neverType(); } @@ -273,8 +273,8 @@ public synchronized SemType createSemType() { isReadonly, isOptional); } CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; - SemType rest = - restFieldType != null ? mutableSemTypeDependencyManager.getSemType(restFieldType, this) : neverType(); + SemType rest; + rest = restFieldType != null ? tryInto(restFieldType) : neverType(); assert !Core.containsBasicType(rest, Builder.bType()) : "Unexpected BType in record rest field"; return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); } 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 5f2d74892d56..6fbda4f6718c 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 @@ -154,9 +154,9 @@ public synchronized SemType createSemType() { } StreamDefinition sd = new StreamDefinition(); definition = sd; - SemType valueTy = mutableSemTypeDependencyManager.getSemType(constraint, this); + SemType valueTy = tryInto(constraint); assert !Core.containsBasicType(valueTy, Builder.bType()) : "Value type shouldn't have BTypes"; - SemType completionTy = mutableSemTypeDependencyManager.getSemType(completionType, this); + SemType completionTy = tryInto(completionType); assert !Core.containsBasicType(completionTy, Builder.bType()) : "Completion type shouldn't have BTypes"; return sd.define(env, valueTy, completionTy); } 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 614ec058cb4b..311b79f74054 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 @@ -172,7 +172,7 @@ public boolean isAnydata() { @Override public SemType createSemType() { - SemType constraintType = mutableSemTypeDependencyManager.getSemType(constraint, this); + SemType constraintType = tryInto(constraint); assert !Core.containsBasicType(constraintType, Builder.bType()) : "Table constraint cannot be a BType"; return createSemTypeWithConstraint(constraintType); } @@ -183,7 +183,7 @@ private SemType createSemTypeWithConstraint(SemType constraintType) { if (fieldNames.length > 0) { semType = TableUtils.tableContainingKeySpecifier(cx, constraintType, fieldNames); } else if (keyType != null) { - SemType keyConstraint = mutableSemTypeDependencyManager.getSemType(keyType, this); + SemType keyConstraint = tryInto(keyType); assert !Core.containsBasicType(keyConstraint, Builder.bType()) : "Table key cannot be a BType"; semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, keyConstraint); } else { 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 dc55aa8ab9c3..6b02ec3629df 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 @@ -329,9 +329,8 @@ public synchronized SemType createSemType() { ListDefinition ld = new ListDefinition(); defn = ld; SemType[] memberTypes = new SemType[tupleTypes.size()]; - boolean hasBTypePart = false; for (int i = 0; i < tupleTypes.size(); i++) { - SemType memberType = mutableSemTypeDependencyManager.getSemType(tupleTypes.get(i), this); + SemType memberType = tryInto(tupleTypes.get(i)); if (Core.isNever(memberType)) { return neverType(); } @@ -340,7 +339,7 @@ public synchronized SemType createSemType() { } CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restType != null ? mutableSemTypeDependencyManager.getSemType(restType, this) : neverType(); + SemType rest = restType != null ? tryInto(restType) : neverType(); assert !Core.containsBasicType(rest, Builder.bType()) : "Tuple rest type cannot be a BType"; return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } 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 e3f15f2e1f67..11f774de8f64 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 @@ -27,7 +27,6 @@ import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; -import io.ballerina.runtime.internal.types.semtype.MutableSemTypeDependencyManager; import java.util.Map; import java.util.Objects; @@ -47,8 +46,6 @@ public abstract non-sealed class BType extends SemType implements Type, MutableS protected String typeName; protected Module pkg; protected Class valueClass; - protected MutableSemTypeDependencyManager mutableSemTypeDependencyManager = - MutableSemTypeDependencyManager.getInstance(); private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; @@ -271,11 +268,7 @@ protected SemType getSemType() { @Override public void resetSemType() { - boolean shouldResetDependencies = cachedSemType != null; cachedSemType = null; - if (shouldResetDependencies) { - mutableSemTypeDependencyManager.notifyDependenciesToReset(this); - } } @Override 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 2eee3dfbde3e..67975db7dbad 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 @@ -132,7 +132,7 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { Type referredType = getReferredType(); - return mutableSemTypeDependencyManager.getSemType(referredType, this); + return tryInto(referredType); } @Override 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 70db70a6fe11..2f8d6374b68d 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 @@ -26,7 +26,6 @@ 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.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.TypedescUtils; @@ -98,9 +97,8 @@ public SemType createSemType() { if (constraint == null) { return Builder.typeDescType(); } - SemType constraint = mutableSemTypeDependencyManager.getSemType(getConstraint(), this); + SemType constraint = tryInto(getConstraint()); Context cx = TypeChecker.context(); - assert !Core.containsBasicType(constraint, Builder.bType()) : "Typedesc constraint cannot be a BType"; return TypedescUtils.typedescContaining(cx.env, constraint); } } 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 4391a1ebe084..a40594bf4617 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 @@ -554,7 +554,7 @@ public void setIntersectionType(IntersectionType intersectionType) { public SemType createSemType() { SemType result = Builder.neverType(); for (Type each : memberTypes) { - SemType eachSemType = mutableSemTypeDependencyManager.getSemType(each, this); + SemType eachSemType = tryInto(each); assert !Core.containsBasicType(eachSemType, Builder.bType()) : "Union constituent cannot be a BType"; result = Core.union(result, eachSemType); } 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 5e4d597554cf..ba3f1f0cb225 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 @@ -163,10 +163,9 @@ public SemType createSemType() { } else { SemType contraintSemtype; if (constraint instanceof ParameterizedType parameterizedType) { - contraintSemtype = - mutableSemTypeDependencyManager.getSemType(parameterizedType.getParamValueType(), this); + contraintSemtype = tryInto(parameterizedType.getParamValueType()); } else { - contraintSemtype = mutableSemTypeDependencyManager.getSemType(constraint, this); + contraintSemtype = tryInto(constraint); } assert !Core.containsBasicType(contraintSemtype, Core.B_TYPE_TOP) : "XML is a pure semtype"; semType = XmlUtils.xmlSequence(contraintSemtype); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java deleted file mode 100644 index 372bbc760ef7..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemTypeDependencyManager.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.Type; -import io.ballerina.runtime.api.types.semtype.MutableSemType; -import io.ballerina.runtime.api.types.semtype.SemType; - -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.WeakHashMap; - -public final class MutableSemTypeDependencyManager { - - private static final MutableSemTypeDependencyManager INSTANCE = new MutableSemTypeDependencyManager(); - private static final int GC_THRESHOLD = 100; - private final Map>> dependencies = new WeakHashMap<>(); - private final Map accessLocks = new WeakHashMap<>(); - - public static MutableSemTypeDependencyManager getInstance() { - return INSTANCE; - } - - private MutableSemTypeDependencyManager() { - } - - public void notifyDependenciesToReset(MutableSemType semType) { - Object lock = getLock(semType); - synchronized (lock) { - List> mutableSemTypes = dependencies.get(semType); - List toBeRecalculated = new ArrayList<>(); - if (mutableSemTypes != null) { - dependencies.remove(semType); - for (var dependent : mutableSemTypes) { - MutableSemType dependentSemType = dependent.get(); - if (dependentSemType != null) { - dependentSemType.resetSemType(); - toBeRecalculated.add(dependentSemType); - } - } - } - toBeRecalculated.forEach(MutableSemType::updateInnerSemTypeIfNeeded); - } - } - - public SemType getSemType(Type target, MutableSemType self) { - assert target != null; - if (target instanceof MutableSemType mutableTarget) { - addDependency(self, mutableTarget); - } - return SemType.tryInto(target); - } - - private void addDependency(MutableSemType self, MutableSemType mutableTarget) { - Object lock = getLock(mutableTarget); - synchronized (lock) { - List> dependencies = - this.dependencies.computeIfAbsent(mutableTarget, (ignored) -> new ArrayList<>()); - // garbage collect these dependencies since the actual target may never mutate, triggering the cleanup - // of the list - if (dependencies.size() > GC_THRESHOLD) { - dependencies.removeIf((ref) -> ref.get() == null); - } - dependencies.add(new WeakReference<>(self)); - } - } - - private synchronized Object getLock(MutableSemType semType) { - return accessLocks.computeIfAbsent(semType, (ignored) -> new Object()); - } -} From 8416880a168f2f8b8e0e18c1c53ec25e79061641 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 3 Sep 2024 08:32:13 +0530 Subject: [PATCH 153/178] Get rid of BType basic type --- .../api/types/semtype/BasicTypeCode.java | 6 ++-- .../runtime/api/types/semtype/Builder.java | 14 ++++------ .../runtime/api/types/semtype/Core.java | 5 ---- .../runtime/internal/types/BArrayType.java | 5 +--- .../runtime/internal/types/BErrorType.java | 4 +-- .../runtime/internal/types/BFunctionType.java | 6 +--- .../runtime/internal/types/BFutureType.java | 6 +--- .../internal/types/BIntersectionType.java | 10 ++----- .../runtime/internal/types/BMapType.java | 5 +--- .../runtime/internal/types/BObjectType.java | 28 ++++--------------- .../runtime/internal/types/BRecordType.java | 9 ++---- .../runtime/internal/types/BStreamType.java | 7 +---- .../runtime/internal/types/BTableType.java | 8 ++---- .../runtime/internal/types/BTupleType.java | 3 -- .../runtime/internal/types/BUnionType.java | 8 +----- .../runtime/internal/types/BXmlType.java | 1 - .../internal/types/semtype/SemTypeHelper.java | 4 +-- .../types/semtype/SubtypePairIterator.java | 2 +- 18 files changed, 28 insertions(+), 103 deletions(-) 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 index 7aebd0dbd9a4..be4871ccfc87 100644 --- 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 @@ -45,7 +45,6 @@ public final class BasicTypeCode { public static final int CODE_OBJECT = 0x11; public static final int CODE_CELL = 0x12; public static final int CODE_UNDEF = 0x13; - public static final int CODE_B_TYPE = 0x14; // TODO: see if we can turn this class to an enum with a value // Inherently immutable @@ -75,7 +74,6 @@ public final class BasicTypeCode { // Non-val public static final BasicTypeCode BT_CELL = from(CODE_CELL); public static final BasicTypeCode BT_UNDEF = from(CODE_UNDEF); - public static final BasicTypeCode BT_B_TYPE = from(CODE_B_TYPE); // Helper bit fields (does not represent basic type tag) static final int VT_COUNT = CODE_OBJECT + 1; @@ -122,8 +120,8 @@ private static final class BasicTypeCodeCache { private static final BasicTypeCode[] cache; static { - cache = new BasicTypeCode[CODE_B_TYPE + 2]; - for (int i = CODE_NIL; i < CODE_B_TYPE + 1; i++) { + cache = new BasicTypeCode[CODE_UNDEF + 2]; + for (int i = CODE_NIL; i < CODE_UNDEF + 1; i++) { cache[i] = new BasicTypeCode(i); } } 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 index 4fafce406ae0..7517e1b504d7 100644 --- 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 @@ -53,7 +53,7 @@ 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_B_TYPE; +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.CellAtomicType.CellMutability.CELL_MUT_LIMITED; @@ -160,10 +160,6 @@ public static SemType intType() { return from(BasicTypeCode.BT_INT); } - public static SemType bType() { - return from(BasicTypeCode.BT_B_TYPE); - } - public static SemType decimalType() { return from(BasicTypeCode.BT_DECIMAL); } @@ -501,19 +497,19 @@ private static final class BasicTypeCache { private static final SemType[] cache; static { - cache = new SemType[CODE_B_TYPE + 2]; - for (int i = 0; i < CODE_B_TYPE + 1; i++) { + 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_B_TYPE; + return 0 < i && i <= CODE_UNDEF; } private static boolean isCached(int code) { - return 0 < code && code <= CODE_B_TYPE; + return 0 < code && code <= CODE_UNDEF; } } } 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 index 779dd245c4ad..20ddc1bf500c 100644 --- 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 @@ -36,7 +36,6 @@ import java.util.Optional; import java.util.function.Function; -import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_B_TYPE; 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; @@ -48,7 +47,6 @@ 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.CODE_UNDEF; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.listType; @@ -66,9 +64,6 @@ */ public final class Core { - public static final SemType SEMTYPE_TOP = SemType.from((1 << (CODE_UNDEF + 1)) - 1); - public static final SemType B_TYPE_TOP = SemType.from(1 << BT_B_TYPE.code()); - private Core() { } 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 9cb7595e11a6..44eb9d88c2b2 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 @@ -24,7 +24,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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; @@ -230,9 +229,7 @@ public synchronized SemType createSemType() { } ListDefinition ld = new ListDefinition(); defn = ld; - SemType elementType = tryInto(getElementType()); - assert !Core.containsBasicType(elementType, Core.B_TYPE_TOP) : "Array element can't have BTypes"; - return getSemTypePart(ld, isReadOnly(), size, elementType); + return getSemTypePart(ld, isReadOnly(), size, tryInto(getElementType())); } private SemType getSemTypePart(ListDefinition defn, boolean isReadOnly, int size, SemType elementType) { 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 f8283d59447d..a50446be2253 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 @@ -130,9 +130,7 @@ public synchronized SemType createSemType() { if (detailType == null || isTopType()) { err = Builder.errorType(); } else { - SemType detailType = tryInto(getDetailType()); - assert Core.isNever(Core.intersect(detailType, Core.B_TYPE_TOP)); - err = ErrorUtils.errorDetail(detailType); + err = ErrorUtils.errorDetail(tryInto(getDetailType())); } if (distinctIdSupplier == null) { 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 f4cd4b50396d..9a7bc713a198 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 @@ -27,7 +27,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; -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.FunctionDefinition; @@ -273,11 +272,8 @@ public FunctionQualifiers getQualifiers() { SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); } - // TODO: consider moving this to builder private SemType getSemType(Type type) { - SemType semType = tryInto(type); - assert !Core.containsBasicType(semType, Builder.bType()) : "function type part with BType"; - return semType; + return tryInto(type); } private boolean isFunctionTop() { 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 8449b0134b4c..b17decbbd658 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 @@ -24,7 +24,6 @@ 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.FutureUtils; @@ -107,10 +106,7 @@ public SemType createSemType() { if (constraint == null) { return Builder.futureType(); } - SemType constraintSemType = tryInto(constraint); - Context cx = TypeChecker.context(); - assert !Core.containsBasicType(constraintSemType, Builder.bType()) : "constraint shouldn't have BTypes"; - return FutureUtils.futureContaining(cx.env, constraintSemType); + return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); } @Override 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 33ba3f622c5e..ef727944b999 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 @@ -230,14 +230,8 @@ public SemType createSemType() { if (constituentTypes.isEmpty()) { return Builder.neverType(); } - SemType result = tryInto(constituentTypes.get(0)); - assert !Core.containsBasicType(result, Builder.bType()) : "Intersection constituent cannot be a BType"; - result = Core.intersect(result, Core.SEMTYPE_TOP); - for (int i = 1; i < constituentTypes.size(); i++) { - SemType memberType = tryInto(constituentTypes.get(i)); - assert !Core.containsBasicType(memberType, Builder.bType()) : "Intersection constituent cannot be a BType"; - result = Core.intersect(result, memberType); - } + SemType result = constituentTypes.stream().map(SemType::tryInto).reduce(Core::intersect).orElseThrow(); + // FIXME: if (Core.isSubtypeSimple(result, Builder.errorType())) { BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = 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 216e855b9fb9..afae6f10bcc0 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 @@ -27,7 +27,6 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; 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; @@ -191,9 +190,7 @@ public synchronized SemType createSemType() { } MappingDefinition md = new MappingDefinition(); defn = md; - SemType restType = tryInto(getConstrainedType()); - assert !Core.containsBasicType(restType, Builder.bType()) : "Map shouldn't have BTypes"; - return getSemTypePart(md, restType); + return getSemTypePart(md, tryInto(getConstrainedType())); } @Override 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 1d0e06f2ad0c..36230a1ea7bf 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 @@ -307,9 +307,7 @@ private synchronized SemType semTypeInner() { Field field = entry.getValue(); boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - SemType ty = tryInto(field.getFieldType()); - assert !Core.containsBasicType(ty, Builder.bType()) : "object member can't have BTypes"; - members.add(new Member(name, ty, Member.Kind.Field, + members.add(new Member(name, tryInto(field.getFieldType()), Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } for (MethodData method : allMethods()) { @@ -318,9 +316,7 @@ private synchronized SemType semTypeInner() { continue; } boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); - SemType semType = method.semType(); - assert !Core.containsBasicType(semType, Builder.bType()) : "object method can't have BTypes"; - members.add(new Member(name, semType, Member.Kind.Method, + members.add(new Member(name, method.semType(), Member.Kind.Method, isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); } return od.define(env, qualifiers, members); @@ -385,9 +381,7 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.FINAL); - SemType ty = fieldShape(cx, shapeSupplier, field, object, isImmutable); - assert !Core.containsBasicType(ty, Builder.bType()) : "field can't have BType"; - members.add(new Member(name, ty, Member.Kind.Field, + 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()) { @@ -396,9 +390,7 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje continue; } boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); - SemType semType = method.semType(); - assert !Core.containsBasicType(semType, Builder.bType()) : "method can't have BType"; - members.add(new Member(name, semType, Member.Kind.Method, + members.add(new Member(name, method.semType(), Member.Kind.Method, isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); } return od.define(env, qualifiers, members); @@ -458,22 +450,16 @@ static MethodData fromResourceMethod(BResourceMethodType method) { if (part == null) { paramTypes.add(Builder.anyType()); } else { - SemType semType = tryInto(part); - assert !Core.containsBasicType(semType, Builder.bType()) : - "resource method path segment can't have BType"; - paramTypes.add(semType); + paramTypes.add(tryInto(part)); } } for (Parameter paramType : innerFn.getParameters()) { - SemType semType = tryInto(paramType.type); - assert !Core.containsBasicType(semType, Builder.bType()) : "resource method params can't have BType"; - paramTypes.add(semType); + paramTypes.add(tryInto(paramType.type)); } SemType rest; Type restType = innerFn.getRestType(); if (restType instanceof BArrayType arrayType) { rest = tryInto(arrayType.getElementType()); - assert !Core.containsBasicType(rest, Builder.bType()) : "resource method rest can't have BType"; } else { rest = Builder.neverType(); } @@ -481,8 +467,6 @@ static MethodData fromResourceMethod(BResourceMethodType method) { SemType returnType; if (innerFn.getReturnType() != null) { returnType = tryInto(innerFn.getReturnType()); - assert !Core.containsBasicType(returnType, Builder.bType()) : - "resource method retType can't have BType"; } else { returnType = Builder.nilType(); } 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 5efa40a03287..3614eec91930 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 @@ -264,7 +264,6 @@ public synchronized SemType createSemType() { if (!isOptional && Core.isNever(fieldType)) { return neverType(); } - assert !Core.containsBasicType(fieldType, Builder.bType()) : "Unexpected BType in record field"; boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); if (Core.isNever(fieldType)) { isReadonly = true; @@ -275,7 +274,6 @@ public synchronized SemType createSemType() { CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; SemType rest; rest = restFieldType != null ? tryInto(restFieldType) : neverType(); - assert !Core.containsBasicType(rest, Builder.bType()) : "Unexpected BType in record rest field"; return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); } @@ -326,9 +324,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap optionalField = false; fieldType = shapeSupplier.get(cx, fieldValue); } else { - SemType fieldSemType = SemType.tryInto(fieldType(fieldName)); - assert !Core.containsBasicType(fieldSemType, Builder.bType()); - fieldType = Optional.of(fieldSemType); + fieldType = Optional.of(SemType.tryInto(fieldType(fieldName))); } assert fieldType.isPresent(); fields.add(new MappingDefinition.Field(fieldName, fieldType.get(), readonlyField, @@ -342,11 +338,11 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap } boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + // FIXME: SemType fieldType = SemType.tryInto(field.getFieldType()); if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { fieldType = Builder.undef(); } - assert !Core.containsBasicType(fieldType, Builder.bType()); fields.add(new MappingDefinition.Field(field.getFieldName(), fieldType, isReadonly, isOptional)); } @@ -357,7 +353,6 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, neverType(), CELL_MUT_NONE); } else { SemType rest = restFieldType != null ? SemType.tryInto(restFieldType) : neverType(); - assert !Core.containsBasicType(rest, Builder.bType()); semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); } value.resetReadonlyShapeDefinition(); 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 6fbda4f6718c..f2761e7ae50a 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 @@ -25,7 +25,6 @@ import io.ballerina.runtime.api.types.StreamType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -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.TypeChecker; @@ -154,10 +153,6 @@ public synchronized SemType createSemType() { } StreamDefinition sd = new StreamDefinition(); definition = sd; - SemType valueTy = tryInto(constraint); - assert !Core.containsBasicType(valueTy, Builder.bType()) : "Value type shouldn't have BTypes"; - SemType completionTy = tryInto(completionType); - assert !Core.containsBasicType(completionTy, Builder.bType()) : "Completion type shouldn't have BTypes"; - return sd.define(env, valueTy, completionTy); + return sd.define(env, tryInto(constraint), tryInto(completionType)); } } 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 311b79f74054..bfb263d2d301 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 @@ -172,9 +172,7 @@ public boolean isAnydata() { @Override public SemType createSemType() { - SemType constraintType = tryInto(constraint); - assert !Core.containsBasicType(constraintType, Builder.bType()) : "Table constraint cannot be a BType"; - return createSemTypeWithConstraint(constraintType); + return createSemTypeWithConstraint(tryInto(constraint)); } private SemType createSemTypeWithConstraint(SemType constraintType) { @@ -183,9 +181,7 @@ private SemType createSemTypeWithConstraint(SemType constraintType) { if (fieldNames.length > 0) { semType = TableUtils.tableContainingKeySpecifier(cx, constraintType, fieldNames); } else if (keyType != null) { - SemType keyConstraint = tryInto(keyType); - assert !Core.containsBasicType(keyConstraint, Builder.bType()) : "Table key cannot be a BType"; - semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, keyConstraint); + semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, tryInto(keyType)); } else { semType = TableUtils.tableContaining(cx.env, constraintType); } 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 6b02ec3629df..a64d70030863 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,7 +24,6 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.api.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; @@ -334,13 +333,11 @@ public synchronized SemType createSemType() { if (Core.isNever(memberType)) { return neverType(); } - assert !Core.containsBasicType(memberType, Builder.bType()) : "Tuple member cannot be a BType"; memberTypes[i] = memberType; } CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; SemType rest = restType != null ? tryInto(restType) : neverType(); - assert !Core.containsBasicType(rest, Builder.bType()) : "Tuple rest type cannot be a BType"; return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } 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 a40594bf4617..e47094cbb4c3 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 @@ -552,12 +552,6 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { - SemType result = Builder.neverType(); - for (Type each : memberTypes) { - SemType eachSemType = tryInto(each); - assert !Core.containsBasicType(eachSemType, Builder.bType()) : "Union constituent cannot be a BType"; - result = Core.union(result, eachSemType); - } - return result; + return memberTypes.stream().map(SemType::tryInto).reduce(Builder.neverType(), 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 ba3f1f0cb225..594e60f58cde 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 @@ -167,7 +167,6 @@ public SemType createSemType() { } else { contraintSemtype = tryInto(constraint); } - assert !Core.containsBasicType(contraintSemtype, Core.B_TYPE_TOP) : "XML is a pure semtype"; semType = XmlUtils.xmlSequence(contraintSemtype); } return isReadOnly() ? Core.intersect(Builder.readonlyType(), semType) : semType; 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 index 53552bf33bda..58c5d00940a0 100644 --- 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 @@ -21,7 +21,6 @@ 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_B_TYPE; 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; @@ -56,7 +55,7 @@ public static String stringRepr(SemType ty) { return "all[" + bitSetRepr(ty.all()) + "] some [" + bitSetRepr(ty.some()) + "]"; } - private static String bitSetRepr(int bits) { + public static String bitSetRepr(int bits) { StringBuilder sb = new StringBuilder(); appendBitSetRepr(sb, bits, CODE_NIL, "NIL"); appendBitSetRepr(sb, bits, CODE_BOOLEAN, "BOOLEAN"); @@ -78,7 +77,6 @@ private static String bitSetRepr(int bits) { appendBitSetRepr(sb, bits, CODE_OBJECT, "OBJECT"); appendBitSetRepr(sb, bits, CODE_CELL, "CELL"); appendBitSetRepr(sb, bits, CODE_UNDEF, "UNDEF"); - appendBitSetRepr(sb, bits, CODE_B_TYPE, "B_TYPE"); return sb.toString(); } 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 index 53f24cac960a..a79e346495b3 100644 --- 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 @@ -33,7 +33,7 @@ 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_B_TYPE + 1; + private static final int maxIndex = BasicTypeCode.CODE_UNDEF + 1; private final int some; private final SemType t1; private final SemType t2; From f8ffbfced7adb655095e2bfdf369c42a0c6ddd8b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 3 Sep 2024 09:16:33 +0530 Subject: [PATCH 154/178] Refactor shape anlayzer --- .../runtime/api/types/semtype/Builder.java | 50 ---------------- .../api/types/semtype/ShapeAnalyzer.java | 59 +++++++++++++++++++ .../ballerina/runtime/api/values/BError.java | 4 +- .../runtime/internal/TypeChecker.java | 18 +++--- .../runtime/internal/types/BArrayType.java | 6 +- .../runtime/internal/types/BErrorType.java | 6 +- .../runtime/internal/types/BFiniteType.java | 3 +- .../runtime/internal/types/BFutureType.java | 6 +- .../internal/types/BIntersectionType.java | 10 ++-- .../runtime/internal/types/BMapType.java | 6 +- .../runtime/internal/types/BObjectType.java | 6 +- .../runtime/internal/types/BRecordType.java | 6 +- .../runtime/internal/types/BTableType.java | 6 +- .../runtime/internal/types/BTupleType.java | 6 +- .../internal/types/BTypeReferenceType.java | 10 ++-- .../runtime/internal/types/BXmlType.java | 6 +- .../runtime/internal/types/TypeWithShape.java | 7 +-- .../internal/values/AbstractArrayValue.java | 4 +- .../internal/values/AbstractObjectValue.java | 4 +- .../runtime/internal/values/MapValueImpl.java | 4 +- .../internal/values/TableValueImpl.java | 4 +- .../runtime/internal/values/XmlValue.java | 5 +- 22 files changed, 123 insertions(+), 113 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java 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 index 7517e1b504d7..0a56bdb5f2f2 100644 --- 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 @@ -18,10 +18,6 @@ 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.TypeWithShape; import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; import io.ballerina.runtime.internal.types.semtype.BCellSubType; import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; @@ -35,7 +31,6 @@ 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 io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; import java.util.ArrayList; @@ -267,51 +262,6 @@ static SubType[] initializeSubtypeArray(int some) { return new SubType[Integer.bitCount(some)]; } - public static Optional readonlyShapeOf(Context cx, Object object) { - if (object == null) { - return Optional.of(nilType()); - } else if (object instanceof DecimalValue decimalValue) { - return Optional.of(decimalConst(decimalValue.value())); - } else if (object instanceof Double doubleValue) { - return Optional.of(floatConst(doubleValue)); - } else if (object instanceof Number intValue) { - long value = - intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); - return Optional.of(intConst(value)); - } else if (object instanceof Boolean booleanValue) { - return Optional.of(booleanConst(booleanValue)); - } else if (object instanceof BString stringValue) { - return Optional.of(stringConst(stringValue.getValue())); - } else if (object instanceof BValue bValue) { - Type type = bValue.getType(); - if (type instanceof TypeWithShape typeWithShape) { - return typeWithShape.readonlyShapeOf(cx, Builder::readonlyShapeOf, object); - } else { - return Optional.empty(); - } - } - return Optional.empty(); - } - - // TODO: factor this to a separate class - public static Optional shapeOf(Context cx, Object object) { - if (object instanceof BValue bValue) { - return bValue.shapeOf(cx); - } - if (object == null) { - return Optional.of(nilType()); - } else if (object instanceof Double doubleValue) { - return Optional.of(floatConst(doubleValue)); - } else if (object instanceof Number intValue) { - long value = - intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); - return Optional.of(intConst(value)); - } else if (object instanceof Boolean booleanValue) { - return Optional.of(booleanConst(booleanValue)); - } - return Optional.empty(); - } - public static SemType roCellContaining(Env env, SemType ty) { return cellContaining(env, ty, CELL_MUT_NONE); } 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..242f7aa63b22 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java @@ -0,0 +1,59 @@ +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.TypeWithShape; +import io.ballerina.runtime.internal.values.DecimalValue; + +import java.util.Optional; + +public class ShapeAnalyzer { + + private ShapeAnalyzer() { + } + + public static Optional shapeOf(Context cx, Object object) { + if (object == null) { + return Optional.of(Builder.nilType()); + } else if (object instanceof DecimalValue decimalValue) { + return Optional.of(Builder.decimalConst(decimalValue.value())); + } else if (object instanceof Double doubleValue) { + return Optional.of(Builder.floatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(Builder.intConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(Builder.booleanConst(booleanValue)); + } else if (object instanceof BString stringValue) { + return Optional.of(Builder.stringConst(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.shapeOf(cx); + } + if (object == null) { + return Optional.of(Builder.nilType()); + } else if (object instanceof Double doubleValue) { + return Optional.of(Builder.floatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(Builder.intConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(Builder.booleanConst(booleanValue)); + } + return Optional.empty(); + } +} 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 e98885565cd0..e32190547840 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,9 +17,9 @@ */ package io.ballerina.runtime.api.values; -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.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.internal.types.TypeWithShape; import java.io.PrintWriter; @@ -97,6 +97,6 @@ public TypeWithShape getTypeWithShape() { @Override public Optional shapeOf(Context cx) { TypeWithShape type = getTypeWithShape(); - return type.shapeOf(cx, Builder::shapeOf, this); + return type.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } } 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 e6ac9073bf69..8eeda5b1f854 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 @@ -33,6 +33,7 @@ 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; @@ -271,7 +272,7 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { } SemType sourceSemType = SemType.tryInto(sourceType); SemType semTargetType = SemType.tryInto(targetType); - return couldShapeBeDifferent(sourceSemType) && isSubTypeWithShape(cx, sourceVal, semTargetType); + return couldInherentTypeBeDifferent(sourceSemType) && isSubTypeWithInherentType(cx, sourceVal, semTargetType); } /** @@ -288,9 +289,9 @@ public static boolean checkIsType(List errors, Object sourceVal, Type so } // This is just an optimization since shapes are not cached, when in doubt return false - private static boolean couldShapeBeDifferent(SemType type) { + private static boolean couldInherentTypeBeDifferent(SemType type) { if (type instanceof TypeWithShape typeWithShape) { - return typeWithShape.couldShapeBeDifferent(); + return typeWithShape.couldInherentTypeBeDifferent(); } return true; } @@ -316,7 +317,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType) { */ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boolean allowNumericConversion) { Context cx = context(); - Optional readonlyShape = Builder.readonlyShapeOf(cx, sourceValue); + Optional readonlyShape = ShapeAnalyzer.shapeOf(cx, sourceValue); assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); SemType targetSemType = SemType.tryInto(targetType); @@ -611,9 +612,10 @@ static boolean isByteLiteral(long longValue) { // Private methods - private static boolean isSubTypeWithShape(Context cx, Object sourceValue, SemType target) { - return Builder.shapeOf(cx, sourceValue) + 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); } @@ -833,8 +835,8 @@ private static SemType createConvertibleCastMask() { private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { Context cx = context(); - SemType lhsShape = Builder.shapeOf(cx, lhsValue).orElseThrow(); - SemType rhsShape = Builder.shapeOf(cx, rhsValue).orElseThrow(); + 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.stringType()) || belongToSameBasicType.test(Builder.booleanType())) { 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 44eb9d88c2b2..56bf6ada654d 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 @@ -250,7 +250,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -265,12 +265,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return isReadOnly(); } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); } 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 a50446be2253..13d132c73379 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 @@ -144,7 +144,7 @@ private boolean isTopType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { BError errorValue = (BError) object; Object details = errorValue.getDetails(); if (!(details instanceof BMap errorDetails)) { @@ -161,7 +161,7 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { BError errorValue = (BError) object; Object details = errorValue.getDetails(); if (!(details instanceof BMap errorDetails)) { @@ -171,7 +171,7 @@ public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier } @Override - public boolean couldShapeBeDifferent() { + 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 27e20a38c92c..4ced616ef111 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 @@ -24,6 +24,7 @@ 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.ShapeAnalyzer; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.RefValue; @@ -225,7 +226,7 @@ public SemType createSemType() { Set bTypeValueSpace = new HashSet<>(); SemType result = Builder.neverType(); for (Object each : this.valueSpace) { - Optional semType = Builder.shapeOf(TypeChecker.context(), each); + Optional semType = ShapeAnalyzer.inherentTypeOf(TypeChecker.context(), each); if (semType.isPresent()) { result = Core.union(result, semType.get()); } else { 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 b17decbbd658..2cf1df311543 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 @@ -110,17 +110,17 @@ public SemType createSemType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { throw new UnsupportedOperationException(); } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { throw new UnsupportedOperationException(); } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return false; } } 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 ef727944b999..ab48b524e129 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 @@ -247,25 +247,25 @@ public SemType createSemType() { } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { Type effectiveType = getEffectiveType(); if (effectiveType instanceof TypeWithShape typeWithShape) { - return typeWithShape.readonlyShapeOf(cx, shapeSupplierFn, object); + return typeWithShape.shapeOf(cx, shapeSupplierFn, object); } return Optional.empty(); } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { Type effectiveType = getEffectiveType(); if (effectiveType instanceof TypeWithShape typeWithShape) { - return typeWithShape.shapeOf(cx, shapeSupplier, object); + return typeWithShape.inherentTypeOf(cx, shapeSupplier, object); } return Optional.empty(); } @Override - public boolean couldShapeBeDifferent() { + 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 afae6f10bcc0..40dd07d8aacc 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 @@ -200,7 +200,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -214,12 +214,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return isReadOnly(); } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return readonlyShape(cx, shapeSupplierFn, (BMap) object); } 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 36230a1ea7bf..5c1c28470c34 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 @@ -337,7 +337,7 @@ private ObjectQualifiers getObjectQualifiers() { } @Override - public synchronized Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public synchronized Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; SemType cachedShape = abstractObjectValue.shapeOf(); if (cachedShape != null) { @@ -353,12 +353,12 @@ public synchronized Optional shapeOf(Context cx, ShapeSupplier shapeSup } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return Optional.of(valueShape(cx, shapeSupplierFn, (AbstractObjectValue) object)); } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY)) { 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 3614eec91930..98d59cf5b0d4 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 @@ -284,7 +284,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { BMap value = (BMap) object; SemType cachedSemType = value.shapeOf(); if (cachedSemType != null) { @@ -360,7 +360,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { if (couldShapeBeDifferentCache != 0) { return couldShapeBeDifferentCache == 1; } @@ -377,7 +377,7 @@ private boolean couldShapeBeDifferentInner() { } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { return Optional.of(shapeOfInner(cx, shapeSupplier, (BMap) object, true)); } 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 bfb263d2d301..dc26eeccfc23 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 @@ -193,7 +193,7 @@ private SemType createSemTypeWithConstraint(SemType constraintType) { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -207,12 +207,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return isReadOnly(); } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return Optional.of(valueShape(cx, shapeSupplierFn, (BTable) object)); } 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 a64d70030863..aabbad5b19ce 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 @@ -348,7 +348,7 @@ public void resetSemType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!isReadOnly()) { return Optional.of(getSemType()); } @@ -363,12 +363,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return isReadOnly(); } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); } 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 67975db7dbad..0d06f04d35bc 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 @@ -136,24 +136,24 @@ public SemType createSemType() { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { Type referredType = getReferredType(); if (referredType instanceof TypeWithShape typeWithShape) { - return typeWithShape.shapeOf(cx, shapeSupplier, object); + return typeWithShape.inherentTypeOf(cx, shapeSupplier, object); } return Optional.empty(); } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return true; } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { Type referredType = getReferredType(); if (referredType instanceof TypeWithShape typeWithShape) { - return typeWithShape.readonlyShapeOf(cx, shapeSupplierFn, object); + return typeWithShape.shapeOf(cx, shapeSupplierFn, object); } return Optional.empty(); } 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 594e60f58cde..c1f4d18d8d7b 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 @@ -189,7 +189,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { XmlValue xmlValue = (XmlValue) object; if (!isReadOnly(xmlValue)) { return Optional.of(getSemType()); @@ -198,12 +198,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object } @Override - public boolean couldShapeBeDifferent() { + public boolean couldInherentTypeBeDifferent() { return true; } @Override - public Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.readonlyType())); } 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 index 0a16c7d204c5..5dc6188a66cd 100644 --- 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 @@ -26,10 +26,9 @@ public interface TypeWithShape { - Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); - // Calculate the shape assuming object is readonly. This is the shape of value spec calls looks like shape - Optional readonlyShapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); - boolean couldShapeBeDifferent(); + boolean couldInherentTypeBeDifferent(); } 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 98ee6eb92d49..e6a7d504e7e4 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,10 +20,10 @@ import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; 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.Definition; 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.IteratorUtils; @@ -331,6 +331,6 @@ public synchronized void resetReadonlyShapeDefinition() { @Override public Optional shapeOf(Context cx) { TypeWithShape typeWithShape = (TypeWithShape) getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + 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 58668e8773e2..d6e4b05d4be9 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,9 +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.Builder; 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; @@ -251,6 +251,6 @@ public final void cacheShape(SemType semType) { @Override public Optional shapeOf(Context cx) { TypeWithShape typeWithShape = (TypeWithShape) getType(); - return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } } 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 3472956da61d..1b1d628f8023 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 @@ -23,10 +23,10 @@ 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.semtype.Builder; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Definition; 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; @@ -759,6 +759,6 @@ public SemType shapeOf() { @Override public Optional shapeOf(Context cx) { TypeWithShape typeWithShape = (TypeWithShape) type; - return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } } 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 784c4f1067d7..fc643242cd9b 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 @@ -24,9 +24,9 @@ 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.semtype.Builder; 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; @@ -915,6 +915,6 @@ public BArray getArrayValue(BString key) { @Override public Optional shapeOf(Context cx) { TypeWithShape typeWithShape = (TypeWithShape) type; - return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } } 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 ae5fbd90e50d..2af1bf15bd50 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,9 +20,9 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; -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.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BMap; @@ -38,7 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; - import javax.xml.namespace.QName; import static io.ballerina.runtime.internal.ValueUtils.getTypedescValue; @@ -281,6 +280,6 @@ public Type getIteratorNextReturnType() { @Override public Optional shapeOf(Context cx) { TypeWithShape typeWithShape = (TypeWithShape) type; - return typeWithShape.shapeOf(cx, Builder::shapeOf, this); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } } From 6e546b40652ec437910741c8f5f10893ecd94d56 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 3 Sep 2024 10:04:24 +0530 Subject: [PATCH 155/178] Avoid creating the inner BType with BSemTypeWrapper when possible --- .../api/types/semtype/BasicTypeBitSet.java | 1 + .../runtime/api/types/semtype/SemType.java | 1 + .../internal/types/BSemTypeWrapper.java | 41 ++++++++++++------- 3 files changed, 29 insertions(+), 14 deletions(-) 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 index 97f64f378f8a..8ee3a556a23c 100644 --- 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 @@ -13,6 +13,7 @@ protected void setAll(int 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/SemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java index 5fca6890958c..e9b8bc52d8d4 100644 --- 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 @@ -32,6 +32,7 @@ public static SemType from(int all, int some, SubType[] subTypes) { } public final int some() { + assert some != -1 : "SemType created by no arg constructor must be initialized with setSome"; return some; } 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 index 8e24e97593ef..5c66134d0098 100644 --- 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 @@ -21,7 +21,11 @@ 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; @@ -40,6 +44,9 @@ public sealed class BSemTypeWrapper extends ImmutableSemType im 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 @@ -87,7 +94,7 @@ public boolean equals(Object obj) { @Override public final boolean isNilable() { - return getbType().isNilable(); + return Core.containsBasicType(this, Builder.nilType()); } @Override @@ -97,17 +104,22 @@ public final int hashCode() { @Override public String getName() { - return getbType().getName(); + return typeName == null ? "" : typeName; } @Override public String getQualifiedName() { - return getbType().getQualifiedName(); + String name = getName(); + if (name.isEmpty()) { + return ""; + } + + return pkg == null ? name : pkg + ":" + name; } @Override public Module getPackage() { - return getbType().getPackage(); + return pkg; } @Override @@ -120,21 +132,22 @@ public boolean isNative() { return getbType().isNative(); } - // TODO: use semtype @Override public boolean isAnydata() { - return getbType().isAnydata(); + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.anyDataType()); } @Override public boolean isPureType() { - return getbType().isPureType(); + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.errorType()) || isAnydata(); } - // TODO: use semtype @Override public boolean isReadOnly() { - return getbType().isReadOnly(); + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.readonlyType()); } @Override @@ -149,7 +162,7 @@ public void setImmutableType(IntersectionType immutableType) { @Override public Module getPkg() { - return getbType().getPkg(); + return pkg; } @Override @@ -159,22 +172,22 @@ public long getFlags() { @Override public void setCachedReferredType(Type type) { - getbType().setCachedReferredType(type); + cachedReferredType = type; } @Override public Type getCachedReferredType() { - return getbType().getCachedReferredType(); + return cachedReferredType; } @Override public void setCachedImpliedType(Type type) { - getbType().setCachedImpliedType(type); + cachedImpliedType = type; } @Override public Type getCachedImpliedType() { - return getbType().getCachedImpliedType(); + return cachedImpliedType; } protected E getbType() { From 528545dfa58970ce2d71845e5a33e63070931962 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 3 Sep 2024 11:02:42 +0530 Subject: [PATCH 156/178] Fix failing windows errors Remove unwanted changes --- .../resources/testcases/operations.cast.json | 8 +++--- .../testcases/operations.equality.json | 12 ++++----- .../api/types/semtype/BasicTypeBitSet.java | 2 +- .../runtime/api/types/semtype/Core.java | 26 ------------------- .../internal/types/BIntersectionType.java | 2 +- .../runtime/internal/types/BRecordType.java | 2 +- .../runtime/internal/values/XmlValue.java | 1 + 7 files changed, 14 insertions(+), 39 deletions(-) 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/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 index 8ee3a556a23c..f3fd6f57048b 100644 --- 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 @@ -1,6 +1,6 @@ package io.ballerina.runtime.api.types.semtype; -public abstract sealed class BasicTypeBitSet permits SemType { +abstract sealed class BasicTypeBitSet permits SemType { private int all; 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 index 20ddc1bf500c..1dd89941d52a 100644 --- 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 @@ -312,12 +312,10 @@ public static boolean isNothingSubtype(SubTypeData data) { return data == AllOrNothing.NOTHING; } - // 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); } - // 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); } @@ -343,26 +341,10 @@ public static boolean isSameType(Context cx, SemType t1, SemType t2) { return isSubType(cx, t1, t2) && isSubType(cx, t2, t1); } - public static SemType widenToBasicTypes(SemType t) { - int all = t.all() | t.some(); - if (cardinality(all) > 1) { - throw new IllegalStateException("Cannot widen to basic type for a type with multiple basic types"); - } - return Builder.basicTypeUnion(all); - } - private static int cardinality(int bitset) { return Integer.bitCount(bitset); } - public static SemType widenToBasicTypeUnion(SemType t) { - if (t.some() == 0) { - return t; - } - int all = t.all() | t.some(); - return SemType.from(all); - } - public static SemType cellContainingInnerVal(Env env, SemType t) { CellAtomicType cat = cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); @@ -427,14 +409,6 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); } - private static SemType unionOf(SemType... semTypes) { - SemType result = Builder.neverType(); - for (SemType semType : semTypes) { - result = union(result, semType); - } - return result; - } - public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { return diff(mappingMemberTypeInner(cx, t, k), Builder.undef()); } 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 ab48b524e129..d7b287111433 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 @@ -231,7 +231,7 @@ public SemType createSemType() { return Builder.neverType(); } SemType result = constituentTypes.stream().map(SemType::tryInto).reduce(Core::intersect).orElseThrow(); - // FIXME: + // TODO:refactor this if (Core.isSubtypeSimple(result, Builder.errorType())) { BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = 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 98d59cf5b0d4..ea59c875fe15 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 @@ -338,7 +338,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap } boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - // FIXME: + // TODO: refactor this SemType fieldType = SemType.tryInto(field.getFieldType()); if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { fieldType = Builder.undef(); 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 2af1bf15bd50..5ab0511a018c 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 @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; + import javax.xml.namespace.QName; import static io.ballerina.runtime.internal.ValueUtils.getTypedescValue; From 846f34dadfb715d74467237406545052f3d193a2 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 4 Sep 2024 10:17:53 +0530 Subject: [PATCH 157/178] Fix string shape calculation --- .../io/ballerina/runtime/api/types/semtype/SemType.java | 6 ++++++ .../java/io/ballerina/runtime/internal/types/BType.java | 3 --- .../runtime/internal/types/semtype/ImmutableSemType.java | 5 ----- .../runtime/internal/types/semtype/SemTypeHelper.java | 2 +- .../io/ballerina/runtime/internal/values/StringValue.java | 4 +++- 5 files changed, 10 insertions(+), 10 deletions(-) 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 index e9b8bc52d8d4..090bc9b8ebf1 100644 --- 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 @@ -2,6 +2,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; +import io.ballerina.runtime.internal.types.semtype.SemTypeHelper; import java.util.Map; import java.util.WeakHashMap; @@ -79,4 +80,9 @@ public enum CachedResult { FALSE, NOT_FOUND } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } } 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 11f774de8f64..fd18d66c75e9 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 @@ -28,7 +28,6 @@ import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; -import java.util.Map; import java.util.Objects; /** @@ -51,7 +50,6 @@ public abstract non-sealed class BType extends SemType implements Type, MutableS private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; private TypeCreator.TypeMemoKey lookupKey = null; - private Map cachedResults; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -275,7 +273,6 @@ public void resetSemType() { public BType clone() { try { BType clone = (BType) super.clone(); - clone.cachedResults = null; clone.cachedSemType = null; clone.setCachedImpliedType(null); clone.setCachedReferredType(null); 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 index b17d94360d7e..e74d079f5c70 100644 --- 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 @@ -48,11 +48,6 @@ protected ImmutableSemType(SemType semType) { this(semType.all(), semType.some(), semType.subTypeData()); } - @Override - public String toString() { - return SemTypeHelper.stringRepr(this); - } - @Override public boolean equals(Object o) { if (this == o) { 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 index 58c5d00940a0..86bdcc2fe854 100644 --- 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 @@ -46,7 +46,7 @@ * * @since 2201.10.0 */ -final class SemTypeHelper { +public final class SemTypeHelper { private SemTypeHelper() { } 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 778f61452780..e8e508d8f659 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 @@ -38,11 +38,13 @@ 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.stringConst(value); } @Override @@ -108,6 +110,6 @@ public boolean equals(Object str) { @Override public Optional shapeOf(Context cx) { - return Optional.of(Builder.stringConst(getValue())); + return Optional.of(shape); } } From f02292d51237ebb211ae383e2a73a2a1da6cba61 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 4 Sep 2024 10:19:04 +0530 Subject: [PATCH 158/178] Avoid result caching for basic types --- .../ballerina/runtime/api/types/semtype/SemType.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 index 090bc9b8ebf1..53b510b7d32d 100644 --- 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 @@ -42,6 +42,9 @@ public final SubType[] subTypeData() { } public final CachedResult cachedSubTypeRelation(SemType other) { + if (skipCache()) { + return CachedResult.NOT_FOUND; + } if (cachedResults == null) { cachedResults = new WeakHashMap<>(); return CachedResult.NOT_FOUND; @@ -49,7 +52,14 @@ public final CachedResult cachedSubTypeRelation(SemType other) { return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); } + private boolean skipCache() { + return this.some() == 0; + } + public final void cacheSubTypeRelation(SemType other, boolean result) { + if (skipCache() || other.skipCache()) { + return; + } // we always check of the result before caching so there will always be a map cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); } From 98eb2b2949800852f0314000838685136627e6bf Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 4 Sep 2024 11:39:09 +0530 Subject: [PATCH 159/178] Avoid unnecessarily calculating inherent type --- .../runtime/internal/types/BArrayType.java | 2 +- .../runtime/internal/types/BErrorType.java | 3 +++ .../runtime/internal/types/BFutureType.java | 20 +------------------ .../runtime/internal/types/BMapType.java | 2 +- .../runtime/internal/types/BObjectType.java | 3 +++ .../runtime/internal/types/BRecordType.java | 11 ++++++---- .../runtime/internal/types/BTableType.java | 2 +- .../runtime/internal/types/BTupleType.java | 2 +- .../internal/types/BTypeReferenceType.java | 5 ++++- 9 files changed, 22 insertions(+), 28 deletions(-) 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 56bf6ada654d..a9b57dd565a5 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 @@ -251,7 +251,7 @@ public void resetSemType() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - if (!isReadOnly()) { + if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } BArray value = (BArray) object; 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 13d132c73379..a9a5297b5141 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 @@ -145,6 +145,9 @@ private boolean isTopType() { @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 BMap errorDetails)) { 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 2cf1df311543..5966b122096d 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 @@ -23,19 +23,16 @@ import io.ballerina.runtime.api.types.FutureType; 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.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.FutureUtils; -import java.util.Optional; - /** * {@code BFutureType} represents a future value in Ballerina. * * @since 0.995.0 */ -public class BFutureType extends BType implements FutureType, TypeWithShape { +public class BFutureType extends BType implements FutureType { private final Type constraint; @@ -108,19 +105,4 @@ public SemType createSemType() { } return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); } - - @Override - public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - throw new UnsupportedOperationException(); - } - - @Override - public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean couldInherentTypeBeDifferent() { - return false; - } } 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 40dd07d8aacc..56478dc8ec4a 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 @@ -201,7 +201,7 @@ public void resetSemType() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - if (!isReadOnly()) { + if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } BMap value = (BMap) object; 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 5c1c28470c34..f779099b16c5 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 @@ -338,6 +338,9 @@ private ObjectQualifiers getObjectQualifiers() { @Override public synchronized 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) { 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 ea59c875fe15..e14ea41d2811 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 @@ -75,7 +75,7 @@ public class BRecordType extends BStructureType implements RecordType, TypeWithS private IntersectionType intersectionType = null; private MappingDefinition defn; private final Env env = Env.getInstance(); - private byte couldShapeBeDifferentCache = 0; + private byte couldInhereTypeBeDifferentCache = 0; private final Map> defaultValues = new LinkedHashMap<>(); @@ -285,6 +285,9 @@ public void resetSemType() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } BMap value = (BMap) object; SemType cachedSemType = value.shapeOf(); if (cachedSemType != null) { @@ -361,11 +364,11 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap @Override public boolean couldInherentTypeBeDifferent() { - if (couldShapeBeDifferentCache != 0) { - return couldShapeBeDifferentCache == 1; + if (couldInhereTypeBeDifferentCache != 0) { + return couldInhereTypeBeDifferentCache == 1; } boolean result = couldShapeBeDifferentInner(); - couldShapeBeDifferentCache = (byte) (result ? 1 : 2); + couldInhereTypeBeDifferentCache = (byte) (result ? 1 : 2); return result; } 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 dc26eeccfc23..286a044256a6 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 @@ -194,7 +194,7 @@ private SemType createSemTypeWithConstraint(SemType constraintType) { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - if (!isReadOnly()) { + if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } BTable table = (BTable) object; 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 aabbad5b19ce..9b932dc08db4 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 @@ -349,7 +349,7 @@ public void resetSemType() { @Override public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - if (!isReadOnly()) { + if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } BArray value = (BArray) object; 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 0d06f04d35bc..18c22b722c2d 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 @@ -137,6 +137,9 @@ public SemType createSemType() { @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); @@ -146,7 +149,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, @Override public boolean couldInherentTypeBeDifferent() { - return true; + return referredType instanceof TypeWithShape typeWithShape && typeWithShape.couldInherentTypeBeDifferent(); } @Override From 0ebdd73ce19c2abf59df9088e098a02a3e8c53d4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 6 Sep 2024 14:07:26 +0530 Subject: [PATCH 160/178] Avoid unnecessarily creating singleton types --- .../io/ballerina/runtime/internal/TypeChecker.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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 8eeda5b1f854..702e4863ca60 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 @@ -44,10 +44,7 @@ import io.ballerina.runtime.internal.types.BAnnotatableType; import io.ballerina.runtime.internal.types.BArrayType; import io.ballerina.runtime.internal.types.BBooleanType; -import io.ballerina.runtime.internal.types.BByteType; import io.ballerina.runtime.internal.types.BFiniteType; -import io.ballerina.runtime.internal.types.BFloatType; -import io.ballerina.runtime.internal.types.BIntegerType; import io.ballerina.runtime.internal.types.BIntersectionType; import io.ballerina.runtime.internal.types.BObjectType; import io.ballerina.runtime.internal.types.BRecordType; @@ -371,14 +368,12 @@ public static Type getType(Object value) { private static Type getNumberType(Number number) { if (number instanceof Double) { - return BFloatType.singletonType(number.doubleValue()); + return TYPE_FLOAT; } - long numberValue = - number instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : number.longValue(); if (number instanceof Integer || number instanceof Byte) { - return BByteType.singletonType(numberValue); + return TYPE_BYTE; } - return BIntegerType.singletonType(numberValue); + return TYPE_INT; } /** From e2e04d22662cb6e6028e8d8c4884c8b7672cf80b Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 8 Sep 2024 09:19:31 +0530 Subject: [PATCH 161/178] Refactor atoms --- .../java/io/ballerina/runtime/api/types/semtype/Atom.java | 2 +- .../ballerina/runtime/api/types/semtype/AtomicType.java | 5 +++++ .../io/ballerina/runtime/api/types/semtype/Builder.java | 7 +++++-- .../io/ballerina/runtime/api/types/semtype/Context.java | 4 ++++ .../java/io/ballerina/runtime/api/types/semtype/Core.java | 8 +++++--- .../java/io/ballerina/runtime/api/types/semtype/Env.java | 5 +++++ .../ballerina/runtime/api/types/semtype/FieldPairs.java | 1 + .../runtime/api/types/semtype/PredefinedTypeEnv.java | 3 +++ .../io/ballerina/runtime/internal/types/BArrayType.java | 4 ++-- .../ballerina/runtime/internal/types/BFunctionType.java | 2 +- .../io/ballerina/runtime/internal/types/BMapType.java | 4 ++-- .../io/ballerina/runtime/internal/types/BObjectType.java | 2 +- .../io/ballerina/runtime/internal/types/BRecordType.java | 6 +++--- .../io/ballerina/runtime/internal/types/BTupleType.java | 4 ++-- .../runtime/internal/types/semtype/BCellSubType.java | 1 - .../runtime/internal/types/semtype/BCellSubTypeImpl.java | 1 - .../internal/types/semtype/BCellSubTypeSimple.java | 1 - .../runtime/internal/types/semtype/BFunctionSubType.java | 1 - .../runtime/internal/types/semtype/BListProj.java | 1 - .../runtime/internal/types/semtype/BListSubType.java | 1 - .../runtime/internal/types/semtype/BMappingProj.java | 1 - .../runtime/internal/types/semtype/BMappingSubType.java | 1 - .../{api => internal}/types/semtype/CellAtomicType.java | 8 +++++++- .../types/semtype/FunctionAtomicType.java | 5 ++++- .../internal/types/semtype/FunctionDefinition.java | 1 - .../internal/types/semtype/FunctionQualifiers.java | 1 - .../runtime/internal/types/semtype/FutureUtils.java | 1 - .../{api => internal}/types/semtype/ListAtomicType.java | 6 +++--- .../runtime/internal/types/semtype/ListDefinition.java | 4 +--- .../types/semtype/MappingAtomicType.java | 8 +++++++- .../runtime/internal/types/semtype/MappingDefinition.java | 2 -- .../runtime/internal/types/semtype/ObjectDefinition.java | 1 - .../runtime/internal/types/semtype/ObjectQualifiers.java | 1 - .../runtime/internal/types/semtype/StreamDefinition.java | 1 - .../runtime/internal/types/semtype/TableUtils.java | 6 ++---- .../runtime/internal/types/semtype/TypedescUtils.java | 1 - bvm/ballerina-runtime/src/main/java/module-info.java | 2 +- .../java/io/ballerina/runtime/test/semtype/CoreTests.java | 2 +- .../semtype/port/test/RuntimeSemTypeResolver.java | 6 +++--- 39 files changed, 69 insertions(+), 52 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/CellAtomicType.java (89%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/FunctionAtomicType.java (82%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/ListAtomicType.java (81%) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/MappingAtomicType.java (84%) 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 index 6d993b5f5bc8..4afe068ae55b 100644 --- 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 @@ -23,7 +23,7 @@ * * @since 2201.10.0 */ -public interface Atom { +public sealed interface Atom permits RecAtom, TypeAtom { 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 index f2286e2fd61d..7407bc97f39a 100644 --- 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 @@ -18,6 +18,11 @@ 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. * 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 index 0a56bdb5f2f2..d742d77fabe4 100644 --- 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 @@ -26,8 +26,11 @@ 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; @@ -51,9 +54,9 @@ 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.CellAtomicType.CellMutability.CELL_MUT_LIMITED; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; 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. 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 index bf355a225a6d..1ed454e89c44 100644 --- 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 @@ -18,6 +18,10 @@ package io.ballerina.runtime.api.types.semtype; +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.List; import java.util.Map; 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 index 1dd89941d52a..6234864e236e 100644 --- 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 @@ -25,8 +25,10 @@ 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; @@ -51,9 +53,9 @@ import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.listType; import static io.ballerina.runtime.api.types.semtype.Builder.undef; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.cellAtomType; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.intersectCellAtomicType; +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; import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; import static io.ballerina.runtime.internal.types.semtype.BMappingProj.mappingMemberTypeInner; 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 index bd1c5a17886c..9c2d8279b108 100644 --- 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 @@ -18,6 +18,11 @@ 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; 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 index 2c49b855cb38..36c37ce6ce14 100644 --- 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 @@ -19,6 +19,7 @@ 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; 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 index 48f6027f347a..5a8ac3ad047a 100644 --- 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 @@ -23,7 +23,10 @@ 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; 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 a9b57dd565a5..65a6956f6d49 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,7 +22,7 @@ import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; @@ -37,7 +37,7 @@ import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.Builder.neverType; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; /** * {@code BArrayType} represents a type of an arrays in Ballerina. 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 9a7bc713a198..7d70418c667b 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 @@ -26,7 +26,7 @@ import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; 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 56478dc8ec4a..9238d174f750 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 @@ -25,7 +25,7 @@ import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; @@ -39,7 +39,7 @@ import java.util.Map; import java.util.Optional; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; /** * {@code BMapType} represents a type of a map in Ballerina. 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 f779099b16c5..bb7ad87c9da3 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 @@ -32,7 +32,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeIdSet; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; 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 e14ea41d2811..14090cb76a34 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 @@ -30,7 +30,7 @@ import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Definition; @@ -57,8 +57,8 @@ import java.util.Set; import static io.ballerina.runtime.api.types.semtype.Builder.neverType; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +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; /** * {@code BRecordType} represents a user defined record type in Ballerina. 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 9b932dc08db4..1a3b0c699f75 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,7 +24,7 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Definition; @@ -42,7 +42,7 @@ import java.util.stream.Collectors; import static io.ballerina.runtime.api.types.semtype.Builder.neverType; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; /** * {@code {@link BTupleType}} represents a tuple type in Ballerina. 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 index 7d191780b1f5..b053edf15c2f 100644 --- 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 @@ -4,7 +4,6 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; 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 index 08fbec00b26c..4019189ecfa1 100644 --- 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 @@ -21,7 +21,6 @@ import io.ballerina.runtime.api.types.semtype.Bdd; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Conjunction; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; 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 index 03111f388f24..4b2ec6bf2ed2 100644 --- 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 @@ -5,7 +5,6 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; 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 index be0617fb4fd9..d695ecc189fe 100644 --- 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 @@ -23,7 +23,6 @@ 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.FunctionAtomicType; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; 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 index c349e140bb9f..c5a6bf910c12 100644 --- 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 @@ -27,7 +27,6 @@ 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.ListAtomicType; import io.ballerina.runtime.api.types.semtype.Pair; import io.ballerina.runtime.api.types.semtype.SemType; 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 index 4b292bfd3de8..747052dfa4fa 100644 --- 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 @@ -27,7 +27,6 @@ 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.ListAtomicType; import io.ballerina.runtime.api.types.semtype.Pair; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; 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 index 1279c636432e..436db85fd007 100644 --- 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 @@ -25,7 +25,6 @@ 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.MappingAtomicType; import io.ballerina.runtime.api.types.semtype.SemType; import java.util.ArrayList; 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 index 6ce68e43c76c..244504283129 100644 --- 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 @@ -26,7 +26,6 @@ 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.MappingAtomicType; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.SubType; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java similarity index 89% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java index e9b1beb72ce8..b83a655ea081 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CellAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java @@ -16,7 +16,13 @@ * under the License. */ -package io.ballerina.runtime.api.types.semtype; +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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java similarity index 82% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java index 423f9a7c083f..1a84adc9fa4b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FunctionAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java @@ -16,7 +16,10 @@ * under the License. */ -package io.ballerina.runtime.api.types.semtype; +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; 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 index 03330df577be..200787c2c09b 100644 --- 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 @@ -24,7 +24,6 @@ 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.FunctionAtomicType; import io.ballerina.runtime.api.types.semtype.RecAtom; import io.ballerina.runtime.api.types.semtype.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 index 850b6131466d..539575549ddd 100644 --- 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 @@ -19,7 +19,6 @@ package io.ballerina.runtime.internal.types.semtype; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; 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 index 312ec8c3e581..2b026265d5df 100644 --- 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 @@ -21,7 +21,6 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java similarity index 81% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java index 5210389264ed..22d605b2844f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java @@ -16,11 +16,11 @@ * under the License. */ -package io.ballerina.runtime.api.types.semtype; +package io.ballerina.runtime.internal.types.semtype; -import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; -// TODO: move this to internal along with cell atomic type 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 index 8fc37cb37595..852c31cc3605 100644 --- 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 @@ -21,10 +21,8 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; -import io.ballerina.runtime.api.types.semtype.ListAtomicType; import io.ballerina.runtime.api.types.semtype.RecAtom; import io.ballerina.runtime.api.types.semtype.SemType; @@ -32,7 +30,7 @@ import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.undef; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; import static io.ballerina.runtime.api.types.semtype.Core.isNever; import static io.ballerina.runtime.api.types.semtype.Core.union; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java similarity index 84% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java index 76e9804760d1..d700df9be6e3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingAtomicType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java @@ -17,7 +17,13 @@ * */ -package io.ballerina.runtime.api.types.semtype; +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; 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 index d1ff99d788dc..3bcf2c3976d3 100644 --- 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 @@ -22,10 +22,8 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; -import io.ballerina.runtime.api.types.semtype.MappingAtomicType; import io.ballerina.runtime.api.types.semtype.RecAtom; import io.ballerina.runtime.api.types.semtype.SemType; 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 index 923ff89803a5..7c764d08b8fc 100644 --- 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 @@ -22,7 +22,6 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; 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 index 086e490c429f..de05fcc5a1f7 100644 --- 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 @@ -19,7 +19,6 @@ package io.ballerina.runtime.internal.types.semtype; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; 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 index f0c7c103b1dd..ca8501f44b0c 100644 --- 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 @@ -22,7 +22,6 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; 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 index c8dfa2fe6f9a..93f3a8d699ef 100644 --- 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 @@ -22,17 +22,15 @@ 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.CellAtomicType; 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.ListAtomicType; import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Optional; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +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; public final class TableUtils { 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 index 9d9b8cab5551..66f7d5c7a7a9 100644 --- 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 @@ -22,7 +22,6 @@ 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.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Env; import io.ballerina.runtime.api.types.semtype.SemType; diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index bc9149193727..675da0d260b7 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -74,5 +74,5 @@ exports io.ballerina.runtime.observability.tracer.noop; exports io.ballerina.runtime.internal.regexp; exports io.ballerina.runtime.internal.configurable.providers to org.ballerinalang.debugadapter.runtime; - exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.internal.types; + exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.api.types.semtype; } 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 index 07d6d88114b1..1b50ec437b16 100644 --- 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 @@ -19,7 +19,7 @@ package io.ballerina.runtime.test.semtype; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; 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 index 237a0c8a4b97..8a3c59ce11eb 100644 --- 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 @@ -19,7 +19,7 @@ package io.ballerina.semtype.port.test; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.api.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Definition; @@ -87,8 +87,8 @@ 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.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; -import static io.ballerina.runtime.api.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +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; class RuntimeSemTypeResolver extends SemTypeResolver { From a78802c03dc168f1000689cee26c10b6f6611a63 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 6 Sep 2024 16:14:15 +0530 Subject: [PATCH 162/178] Refactor shape calculation --- .../ballerina/runtime/api/values/BArray.java | 20 +--------------- .../runtime/api/values/BArray.java.rej | 10 ++++++++ .../io/ballerina/runtime/api/values/BMap.java | 19 +-------------- .../runtime/api/values/RecursiveValue.java | 12 ---------- .../runtime/internal/types/BArrayType.java | 24 ++++++++----------- .../runtime/internal/types/BErrorType.java | 6 ++--- .../runtime/internal/types/BMapType.java | 24 +++++++------------ .../runtime/internal/types/BObjectType.java | 5 ++++ .../runtime/internal/types/BRecordType.java | 14 +++++------ .../runtime/internal/types/BTupleType.java | 24 ++++++++----------- .../runtime/internal/types/TypeWithShape.java | 6 +++++ .../internal/values/AbstractArrayValue.java | 12 +++++----- .../internal/values/AbstractObjectValue.java | 19 ++++++++++++++- .../runtime/internal/values/MapValueImpl.java | 12 +++++----- .../internal/values/RecursiveValue.java | 15 ++++++++++++ 15 files changed, 106 insertions(+), 116 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java index 6597871f44f5..4d92f07dd6dd 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java @@ -18,11 +18,8 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.internal.types.TypeWithShape; -import java.util.Optional; - /** *

* Represent an array in ballerina. @@ -30,7 +27,7 @@ * * @since 1.1.0 */ -public interface BArray extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { +public interface BArray extends BRefValue, BCollection, PatternMatchableValue { /** * Get value in the given array index. @@ -245,19 +242,4 @@ public interface BArray extends BRefValue, BCollection, PatternMatchableValue, default TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } - - @Override - default Optional getReadonlyShapeDefinition() { - throw new UnsupportedOperationException("Method not implemented"); - } - - @Override - default void setReadonlyShapeDefinition(Definition definition) { - throw new UnsupportedOperationException("Method not implemented"); - } - - @Override - default void resetReadonlyShapeDefinition() { - throw new UnsupportedOperationException("Method not implemented"); - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej new file mode 100644 index 000000000000..0ed99d03ea7b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej @@ -0,0 +1,10 @@ +diff a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java (rejected hunks) +@@ -30,7 +30,7 @@ import java.util.Optional; + * + * @since 1.1.0 + */ +-public interface BArray extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { ++public interface BArray extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { + + /** + * Get value in the given array index. diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index 5ebca7e87455..36dc6a246110 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -18,12 +18,10 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.internal.types.TypeWithShape; import java.util.Collection; import java.util.Map; -import java.util.Optional; import java.util.Set; /** @@ -36,7 +34,7 @@ * * @since 1.1.0 */ -public interface BMap extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { +public interface BMap extends BRefValue, BCollection, PatternMatchableValue { /** * Returns the value to which the specified key is mapped, or {@code null} if this map contains no @@ -201,19 +199,4 @@ public interface BMap extends BRefValue, BCollection, PatternMatchableValu default TypeWithShape getTypeWithShape() { return (TypeWithShape) getType(); } - - @Override - default Optional getReadonlyShapeDefinition() { - throw new UnsupportedOperationException("Method not implemented"); - } - - @Override - default void setReadonlyShapeDefinition(Definition definition) { - throw new UnsupportedOperationException("Method not implemented"); - } - - @Override - default void resetReadonlyShapeDefinition() { - throw new UnsupportedOperationException("Method not implemented"); - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java deleted file mode 100644 index d58721bf1808..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/RecursiveValue.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.ballerina.runtime.api.values; - -import io.ballerina.runtime.api.types.semtype.Definition; - -import java.util.Optional; - -interface RecursiveValue { - Optional getReadonlyShapeDefinition(); - - void setReadonlyShapeDefinition(Definition definition); - void resetReadonlyShapeDefinition(); -} 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 65a6956f6d49..fac6231648d2 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 @@ -24,12 +24,11 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; -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.api.values.BArray; import io.ballerina.runtime.internal.TypeChecker; 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; @@ -254,7 +253,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } - BArray value = (BArray) object; + AbstractArrayValue value = (AbstractArrayValue) object; SemType cachedShape = value.shapeOf(); if (cachedShape != null) { return Optional.of(cachedShape); @@ -271,21 +270,18 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); + return Optional.of(readonlyShape(cx, shapeSupplier, (AbstractArrayValue) object)); } - private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, BArray value) { + private SemType readonlyShape(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; - Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); - if (readonlyShapeDefinition.isPresent()) { - ld = (ListDefinition) readonlyShapeDefinition.get(); - return ld.getSemType(cx.env); - } else { - ld = new ListDefinition(); - value.setReadonlyShapeDefinition(ld); - } + 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(); 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 a9a5297b5141..5a7b658a6196 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 @@ -29,10 +29,10 @@ 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.api.values.BMap; 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; @@ -150,7 +150,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, } BError errorValue = (BError) object; Object details = errorValue.getDetails(); - if (!(details instanceof BMap errorDetails)) { + if (!(details instanceof MapValueImpl errorDetails)) { return Optional.empty(); } if (distinctIdSupplier == null) { @@ -167,7 +167,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { BError errorValue = (BError) object; Object details = errorValue.getDetails(); - if (!(details instanceof BMap errorDetails)) { + if (!(details instanceof MapValueImpl errorDetails)) { return Optional.empty(); } return BMapType.readonlyShape(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); 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 9238d174f750..b88d6139e803 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 @@ -27,10 +27,8 @@ import io.ballerina.runtime.api.types.semtype.Builder; import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; -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.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValueImpl; @@ -204,7 +202,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } - BMap value = (BMap) object; + MapValueImpl value = (MapValueImpl) object; SemType cachedShape = value.shapeOf(); if (cachedShape != null) { return Optional.of(cachedShape); @@ -220,21 +218,17 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { - return readonlyShape(cx, shapeSupplierFn, (BMap) object); + return readonlyShape(cx, shapeSupplierFn, (MapValueImpl) object); } - static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, BMap value) { - int nFields = value.size(); - MappingDefinition md; - - Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); - if (readonlyShapeDefinition.isPresent()) { - md = (MappingDefinition) readonlyShapeDefinition.get(); - return Optional.of(md.getSemType(cx.env)); - } else { - md = new MappingDefinition(); - value.setReadonlyShapeDefinition(md); + static Optional readonlyShape(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++) { 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 bb7ad87c9da3..535bbd7f7bbd 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 @@ -371,7 +371,12 @@ public boolean couldInherentTypeBeDifferent() { } 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(); 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 14090cb76a34..19ddc8887bfb 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 @@ -33,7 +33,6 @@ import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; 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.api.utils.StringUtils; @@ -288,7 +287,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } - BMap value = (BMap) object; + MapValueImpl value = (MapValueImpl) object; SemType cachedSemType = value.shapeOf(); if (cachedSemType != null) { return Optional.of(cachedSemType); @@ -298,17 +297,16 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, return Optional.of(semTypePart); } - private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, BMap value, boolean readonly) { + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value, boolean readonly) { int nFields = value.size(); List fields = new ArrayList<>(nFields); Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); Set handledFields = new HashSet<>(nFields); MappingDefinition md; if (readonly) { - Optional readonlyShapeDefinition = value.getReadonlyShapeDefinition(); - if (readonlyShapeDefinition.isPresent()) { - md = (MappingDefinition) readonlyShapeDefinition.get(); - return md.getSemType(env); + MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(env); } else { md = new MappingDefinition(); value.setReadonlyShapeDefinition(md); @@ -381,7 +379,7 @@ private boolean couldShapeBeDifferentInner() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - return Optional.of(shapeOfInner(cx, shapeSupplier, (BMap) object, true)); + return Optional.of(shapeOfInner(cx, shapeSupplier, (MapValueImpl) object, true)); } private Type fieldType(String fieldName) { 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 1a3b0c699f75..027160dca7f5 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 @@ -27,11 +27,10 @@ import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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.api.values.BArray; 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; @@ -352,7 +351,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } - BArray value = (BArray) object; + AbstractArrayValue value = (AbstractArrayValue) object; SemType cachedShape = value.shapeOf(); if (cachedShape != null) { return Optional.of(cachedShape); @@ -369,21 +368,18 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - return Optional.of(readonlyShape(cx, shapeSupplier, (BArray) object)); + return Optional.of(readonlyShape(cx, shapeSupplier, (AbstractArrayValue) object)); } - private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, BArray value) { + private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + ListDefinition defn = value.getReadonlyShapeDefinition(); + if (defn != null) { + return defn.getSemType(env); + } int size = value.size(); SemType[] memberTypes = new SemType[size]; - ListDefinition ld; - Optional defn = value.getReadonlyShapeDefinition(); - if (defn.isPresent()) { - ld = (ListDefinition) defn.get(); - return ld.getSemType(env); - } else { - ld = new ListDefinition(); - value.setReadonlyShapeDefinition(ld); - } + 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(); 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 index 5dc6188a66cd..4f2d3c2ff93b 100644 --- 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 @@ -24,6 +24,12 @@ 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}. + */ public interface TypeWithShape { Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); 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 e6a7d504e7e4..4c6c10cd496f 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 @@ -21,7 +21,6 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Context; -import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; @@ -32,6 +31,7 @@ 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 java.io.ByteArrayOutputStream; import java.io.IOException; @@ -57,10 +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<>(); + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); /** * The maximum size of arrays to allocate. @@ -314,12 +314,12 @@ public boolean hasNext() { } @Override - public synchronized Optional getReadonlyShapeDefinition() { - return Optional.ofNullable(readonlyAttachedDefinition.get()); + public synchronized ListDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); } @Override - public synchronized void setReadonlyShapeDefinition(Definition definition) { + public synchronized void setReadonlyShapeDefinition(ListDefinition definition) { readonlyAttachedDefinition.set(definition); } 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 d6e4b05d4be9..ffacb4890550 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 @@ -39,6 +39,7 @@ 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; @@ -62,11 +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<>(); @@ -253,4 +255,19 @@ public Optional shapeOf(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/MapValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java index 1b1d628f8023..84c3f7405987 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 @@ -24,7 +24,6 @@ import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Context; -import io.ballerina.runtime.api.types.semtype.Definition; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; @@ -53,6 +52,7 @@ 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 java.io.ByteArrayOutputStream; import java.io.IOException; @@ -99,7 +99,7 @@ * @since 0.995.0 */ public class MapValueImpl extends LinkedHashMap implements RefValue, CollectionValue, MapValue, - BMap { + BMap, RecursiveValue { private BTypedesc typedesc; private Type type; @@ -107,7 +107,7 @@ public class MapValueImpl extends LinkedHashMap implements RefValue, private final Map nativeData = new HashMap<>(); private Type iteratorNextReturnType; private SemType shape; - private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); public MapValueImpl(TypedescValue typedesc) { this(typedesc.getDescribingType()); @@ -618,12 +618,12 @@ public IteratorValue getIterator() { } @Override - public synchronized Optional getReadonlyShapeDefinition() { - return Optional.ofNullable(readonlyAttachedDefinition.get()); + public synchronized MappingDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); } @Override - public synchronized void setReadonlyShapeDefinition(Definition definition) { + public synchronized void setReadonlyShapeDefinition(MappingDefinition definition) { readonlyAttachedDefinition.set(definition); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java new file mode 100644 index 000000000000..80bc9f8aff67 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java @@ -0,0 +1,15 @@ +package io.ballerina.runtime.internal.values; + +import io.ballerina.runtime.api.types.semtype.Definition; + +/** + * Every value that can contain a recursive reference should implement this interface. + */ +interface RecursiveValue { + + E getReadonlyShapeDefinition(); + + void setReadonlyShapeDefinition(E definition); + + void resetReadonlyShapeDefinition(); +} From 716c1d74bf5100d3f82eae42ebcf935b4f27f31d Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 8 Sep 2024 10:39:57 +0530 Subject: [PATCH 163/178] Get rid of unwanted sychronizations --- .../runtime/api/types/semtype/Bdd.java | 25 +++++++ .../runtime/api/types/semtype/BddMemo.java | 69 ------------------- .../runtime/api/types/semtype/Context.java | 15 ++++ .../runtime/api/types/semtype/SemType.java | 8 ++- .../runtime/internal/types/BArrayType.java | 4 +- .../runtime/internal/types/BFunctionType.java | 2 +- .../runtime/internal/types/BMapType.java | 4 +- .../runtime/internal/types/BObjectType.java | 8 +-- .../runtime/internal/types/BRecordType.java | 4 +- .../runtime/internal/types/BTupleType.java | 4 +- .../types/semtype/BBooleanSubType.java | 14 +--- .../internal/values/RecursiveValue.java | 2 + .../runtime/test/semtype/CoreTests.java | 2 +- .../port/test/RuntimeSemTypeResolver.java | 2 +- .../resources/test-src/type-rel/test-tv.bal | 0 15 files changed, 65 insertions(+), 98 deletions(-) delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java create mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test-tv.bal 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 index 8d5a4aa55442..726a7bcfceaf 100644 --- 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 @@ -251,4 +251,29 @@ private static Conjunction andIfPositive(Atom atom, Conjunction 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/BddMemo.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java deleted file mode 100644 index 71ab2280e617..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddMemo.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 java.util.Objects; -import java.util.Optional; - -// TODO: consider moving this to inner as well -public final class BddMemo { - - public Status isEmpty; - - public BddMemo() { - this.isEmpty = Status.NULL; - } - - public enum Status { - TRUE, - FALSE, - LOOP, - CYCLIC, - PROVISIONAL, - 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) { - case TRUE, CYCLIC -> Optional.of(true); - case FALSE -> Optional.of(false); - case LOOP, PROVISIONAL -> { - isEmpty = Status.LOOP; - yield Optional.of(true); - } - case NULL -> Optional.empty(); - }; - } -} 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 index 1ed454e89c44..8a6ea6eab859 100644 --- 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 @@ -51,6 +51,21 @@ 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)); 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 index 53b510b7d32d..57c752e55689 100644 --- 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 @@ -12,7 +12,7 @@ public sealed class SemType extends BasicTypeBitSet private int some; private SubType[] subTypeData; - private Map cachedResults; + private volatile Map cachedResults; protected SemType(int all, int some, SubType[] subTypeData) { super(all); @@ -46,7 +46,11 @@ public final CachedResult cachedSubTypeRelation(SemType other) { return CachedResult.NOT_FOUND; } if (cachedResults == null) { - cachedResults = new WeakHashMap<>(); + synchronized (this) { + if (cachedResults == null) { + cachedResults = new WeakHashMap<>(); + } + } return CachedResult.NOT_FOUND; } return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); 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 fac6231648d2..6705ecc82956 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,11 +22,11 @@ import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ArrayValue; @@ -222,7 +222,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 7d70418c667b..675cd813233d 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 @@ -26,9 +26,9 @@ import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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.FunctionDefinition; import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; import io.ballerina.runtime.internal.types.semtype.ListDefinition; 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 b88d6139e803..1add6c4963d7 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 @@ -25,11 +25,11 @@ import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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.values.BString; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -182,7 +182,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 535bbd7f7bbd..1b2e2f639f14 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 @@ -32,7 +32,6 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeIdSet; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; @@ -44,6 +43,7 @@ import io.ballerina.runtime.internal.ValueUtils; 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.FunctionDefinition; import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.types.semtype.Member; @@ -275,7 +275,7 @@ public TypeIdSet getTypeIdSet() { } @Override - public SemType createSemType() { + public synchronized SemType createSemType() { if (distinctIdSupplier == null) { distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); } @@ -290,7 +290,7 @@ private static boolean skipField(Set seen, String name) { return !seen.add(name); } - private synchronized SemType semTypeInner() { + private SemType semTypeInner() { if (defn != null) { return defn.getSemType(env); } @@ -337,7 +337,7 @@ private ObjectQualifiers getObjectQualifiers() { } @Override - public synchronized Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { if (!couldInherentTypeBeDifferent()) { return Optional.of(getSemType()); } 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 19ddc8887bfb..8463e22f26c1 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 @@ -30,7 +30,6 @@ import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; import io.ballerina.runtime.api.types.semtype.Context; import io.ballerina.runtime.api.types.semtype.Core; import io.ballerina.runtime.api.types.semtype.Env; @@ -41,6 +40,7 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.ValueUtils; import io.ballerina.runtime.internal.scheduling.Scheduler; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValue; import io.ballerina.runtime.internal.values.MapValueImpl; @@ -248,7 +248,7 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 027160dca7f5..28b1b714f8e3 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,11 +24,11 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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 io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -320,7 +320,7 @@ public String getAnnotationKey() { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { if (defn != null) { return defn.getSemType(env); } 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 index 39619b0ee099..6cba94c8d23b 100644 --- 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 @@ -48,11 +48,8 @@ public SubType union(SubType otherSubtype) { if (!(otherSubtype instanceof BBooleanSubType other)) { throw new IllegalArgumentException("union of different subtypes"); } - if (this.isAll()) { - return this; - } - if (other.isAll()) { - return other; + if (this.isAll() || other.isAll()) { + return ALL; } if (this.isNothing()) { return other; @@ -101,9 +98,6 @@ public SubType diff(SubType otherSubtype) { return this; } if (this.isAll()) { - if (other.isNothing()) { - return this; - } return from(!other.data.value); } return this.data.value == other.data.value ? NOTHING : this; @@ -156,10 +150,6 @@ private record BBooleanSubTypeData(boolean isAll, boolean isNothing, boolean val private static final BBooleanSubTypeData TRUE = new BBooleanSubTypeData(false, false, true); private static final BBooleanSubTypeData FALSE = new BBooleanSubTypeData(false, false, false); - static BBooleanSubTypeData from(boolean value) { - return value ? TRUE : FALSE; - } - SubTypeData toData() { if (isAll()) { return AllOrNothing.ALL; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java index 80bc9f8aff67..d8f478a8f0ae 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java @@ -4,6 +4,8 @@ /** * Every value that can contain a recursive reference should implement this interface. + * + * @param Type of the definition */ interface RecursiveValue { 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 index 1b50ec437b16..687aa77e3da5 100644 --- 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 @@ -19,11 +19,11 @@ package io.ballerina.runtime.test.semtype; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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; 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 index 8a3c59ce11eb..9f1dc91637dd 100644 --- 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 @@ -19,12 +19,12 @@ package io.ballerina.semtype.port.test; import io.ballerina.runtime.api.types.semtype.Builder; -import io.ballerina.runtime.internal.types.semtype.CellAtomicType; 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; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test-tv.bal new file mode 100644 index 000000000000..e69de29bb2d1 From a36335fe7399b189885e5402a370bc9a2e877431 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 11 Sep 2024 08:47:55 +0530 Subject: [PATCH 164/178] Move BddMemo to inner --- .../runtime/api/types/semtype/Context.java | 1 + .../runtime/api/types/semtype/Env.java | 19 ++++++ .../internal/types/semtype/BddMemo.java | 68 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java 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 index 8a6ea6eab859..5fd6015eae75 100644 --- 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 @@ -18,6 +18,7 @@ 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; 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 index 9c2d8279b108..7661052c4d6e 100644 --- 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 @@ -237,4 +237,23 @@ 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/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..faee572d96c1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java @@ -0,0 +1,68 @@ +/* + * 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; + +public final class BddMemo { + + public Status isEmpty; + + public BddMemo() { + this.isEmpty = Status.NULL; + } + + public enum Status { + TRUE, + FALSE, + LOOP, + CYCLIC, + PROVISIONAL, + 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) { + case TRUE, CYCLIC -> Optional.of(true); + case FALSE -> Optional.of(false); + case LOOP, PROVISIONAL -> { + isEmpty = Status.LOOP; + yield Optional.of(true); + } + case NULL -> Optional.empty(); + }; + } +} From d40c8581c4d344f0c61561cb32e3f9e2ef343b2e Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 15 Sep 2024 08:52:29 +0530 Subject: [PATCH 165/178] Use accepted type for is likeShape --- .../api/types/semtype/ShapeAnalyzer.java | 7 +++ .../runtime/internal/TypeChecker.java | 2 +- .../runtime/internal/types/BArrayType.java | 36 ++++++++--- .../runtime/internal/types/BErrorType.java | 9 ++- .../internal/types/BIntersectionType.java | 13 +++- .../runtime/internal/types/BMapType.java | 36 ++++++++--- .../runtime/internal/types/BObjectType.java | 59 ++++++++++++++----- .../runtime/internal/types/BRecordType.java | 52 ++++++++++++---- .../runtime/internal/types/BTableType.java | 6 ++ .../runtime/internal/types/BTupleType.java | 42 ++++++++++--- .../internal/types/BTypeReferenceType.java | 10 ++++ .../runtime/internal/types/BXmlType.java | 5 ++ .../runtime/internal/types/TypeWithShape.java | 2 + .../types/semtype/ObjectDefinition.java | 4 +- .../port/test/RuntimeSemTypeResolver.java | 3 +- 15 files changed, 224 insertions(+), 62 deletions(-) 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 index 242f7aa63b22..be8e418a4282 100644 --- 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 @@ -13,6 +13,13 @@ public class ShapeAnalyzer { private ShapeAnalyzer() { } + public static Optional acceptedTypeOf(Context cx, Type typeDesc) { + if (typeDesc instanceof TypeWithShape typeWithShape) { + return typeWithShape.acceptedTypeOf(cx); + } + return Optional.of(SemType.tryInto(typeDesc)); + } + public static Optional shapeOf(Context cx, Object object) { if (object == null) { return Optional.of(Builder.nilType()); 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 702e4863ca60..d7aba47e97ff 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 @@ -317,7 +317,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole Optional readonlyShape = ShapeAnalyzer.shapeOf(cx, sourceValue); assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); - SemType targetSemType = SemType.tryInto(targetType); + SemType targetSemType = ShapeAnalyzer.acceptedTypeOf(cx, targetType).orElseThrow(); if (Core.isSubType(cx, shape, NUMERIC_TYPE) && allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); } 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 6705ecc82956..1cf6b859d5fb 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 @@ -25,6 +25,7 @@ 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.ListDefinition; @@ -36,7 +37,9 @@ import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +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. @@ -63,7 +66,7 @@ public class BArrayType extends BType implements ArrayType, TypeWithShape { private IntersectionType intersectionType = null; private int typeFlags; private ListDefinition defn; - private final Env env = Env.getInstance(); + private ListDefinition acceptedTypeDefn; public BArrayType(Type elementType) { this(elementType, false); } @@ -223,17 +226,19 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { + Env env = Env.getInstance(); if (defn != null) { return defn.getSemType(env); } ListDefinition ld = new ListDefinition(); defn = ld; - return getSemTypePart(ld, isReadOnly(), size, tryInto(getElementType())); + 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(ListDefinition defn, boolean isReadOnly, int size, SemType elementType) { - CellAtomicType.CellMutability mut = isReadOnly ? CellAtomicType.CellMutability.CELL_MUT_NONE : - CellAtomicType.CellMutability.CELL_MUT_LIMITED; + 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 { @@ -258,7 +263,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, if (cachedShape != null) { return Optional.of(cachedShape); } - SemType semType = readonlyShape(cx, shapeSupplier, value); + SemType semType = shapeOfInner(cx, shapeSupplier, value); value.cacheShape(semType); return Optional.of(semType); } @@ -270,10 +275,22 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - return Optional.of(readonlyShape(cx, shapeSupplier, (AbstractArrayValue) object)); + return Optional.of(shapeOfInner(cx, shapeSupplier, (AbstractArrayValue) object)); + } + + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn != null) { + return Optional.of(acceptedTypeDefn.getSemType(cx.env)); + } + ListDefinition ld = new ListDefinition(); + acceptedTypeDefn = ld; + SemType elementType = ShapeAnalyzer.acceptedTypeOf(cx, getElementType()).orElseThrow(); + return Optional.of(getSemTypePart(env, ld, size, elementType, CELL_MUT_UNLIMITED)); } - private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { ListDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); if (readonlyShapeDefinition != null) { return readonlyShapeDefinition.getSemType(cx.env); @@ -287,7 +304,8 @@ private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, AbstractA assert memberType.isPresent(); memberTypes[i] = memberType.get(); } - SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE); + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CELL_MUT_LIMITED; + SemType semType = ld.defineListTypeWrapped(cx.env, memberTypes, memberTypes.length, neverType(), mut); value.resetReadonlyShapeDefinition(); return semType; } 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 5a7b658a6196..d773328a7879 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 @@ -157,7 +157,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); } // Should we actually pass the readonly shape supplier here? - return BMapType.readonlyShape(cx, shapeSupplier, errorDetails) + return BMapType.shapeOfInner(cx, shapeSupplier, errorDetails) .map(ErrorUtils::errorDetail) .map(err -> distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct) .reduce(err, Core::intersect)); @@ -170,7 +170,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje if (!(details instanceof MapValueImpl errorDetails)) { return Optional.empty(); } - return BMapType.readonlyShape(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); + return BMapType.shapeOfInner(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(getSemType()); } @Override 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 d7b287111433..cf130fff5b6c 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 @@ -28,6 +28,7 @@ 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; @@ -38,6 +39,7 @@ import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; +import java.util.function.Function; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; @@ -227,10 +229,14 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { + return createSemTypeInner(SemType::tryInto); + } + + private SemType createSemTypeInner(Function semTypeFunction) { if (constituentTypes.isEmpty()) { return Builder.neverType(); } - SemType result = constituentTypes.stream().map(SemType::tryInto).reduce(Core::intersect).orElseThrow(); + SemType result = constituentTypes.stream().map(semTypeFunction).reduce(Core::intersect).orElseThrow(); // TODO:refactor this if (Core.isSubtypeSimple(result, Builder.errorType())) { BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); @@ -255,6 +261,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje 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(); 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 1add6c4963d7..2b61df8abc5e 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 @@ -28,6 +28,7 @@ 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.MappingDefinition; @@ -38,6 +39,7 @@ import java.util.Optional; 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. @@ -58,7 +60,7 @@ public class BMapType extends BType implements MapType, TypeWithShape, Cloneable private IntersectionType immutableType; private IntersectionType intersectionType = null; private MappingDefinition defn; - private final Env env = Env.getInstance(); + private MappingDefinition acceptedTypeDefn; public BMapType(Type constraint) { this(constraint, false); @@ -183,12 +185,15 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { + Env env = Env.getInstance(); if (defn != null) { return defn.getSemType(env); } MappingDefinition md = new MappingDefinition(); defn = md; - return getSemTypePart(md, tryInto(getConstrainedType())); + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + return createSemTypeInner(env, md, tryInto(getConstrainedType()), mut); } @Override @@ -208,7 +213,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, return Optional.of(cachedShape); } - return readonlyShape(cx, shapeSupplier, value); + return shapeOfInner(cx, shapeSupplier, value); } @Override @@ -218,10 +223,22 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { - return readonlyShape(cx, shapeSupplierFn, (MapValueImpl) object); + return shapeOfInner(cx, shapeSupplierFn, (MapValueImpl) object); + } + + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn != null) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + MappingDefinition md = new MappingDefinition(); + acceptedTypeDefn = md; + SemType elementType = ShapeAnalyzer.acceptedTypeOf(cx, getConstrainedType()).orElseThrow(); + return Optional.of(createSemTypeInner(env, md, elementType, CELL_MUT_UNLIMITED)); } - static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value) { + static Optional shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value) { MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); if (readonlyShapeDefinition != null) { return Optional.of(readonlyShapeDefinition.getSemType(cx.env)); @@ -236,15 +253,16 @@ static Optional readonlyShape(Context cx, ShapeSupplier shapeSupplier, SemType fieldType = valueType.orElseThrow(); fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); } - SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.neverType(), CELL_MUT_NONE); + CellAtomicType.CellMutability mut = value.getType().isReadOnly() ? CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.neverType(), mut); value.cacheShape(semType); value.resetReadonlyShapeDefinition(); return Optional.of(semType); } - private SemType getSemTypePart(MappingDefinition defn, SemType restType) { - CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : - CellAtomicType.CellMutability.CELL_MUT_LIMITED; + private SemType createSemTypeInner(Env env, MappingDefinition defn, SemType restType, + CellAtomicType.CellMutability mut) { return defn.defineMappingTypeWrapped(env, EMPTY_FIELD_ARR, restType, mut); } 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 1b2e2f639f14..622dbc5a0244 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 @@ -36,6 +36,7 @@ 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; @@ -62,6 +63,7 @@ import java.util.Optional; import java.util.Set; import java.util.StringJoiner; +import java.util.function.Function; import static io.ballerina.runtime.api.TypeTags.SERVICE_TAG; @@ -84,7 +86,7 @@ public class BObjectType extends BStructureType implements ObjectType, TypeWithS private String cachedToString; private boolean resolving; private ObjectDefinition defn; - private final Env env = Env.getInstance(); + private ObjectDefinition acceptedTypeDefn; private DistinctIdSupplier distinctIdSupplier; /** @@ -275,12 +277,23 @@ public TypeIdSet getTypeIdSet() { } @Override - public synchronized SemType createSemType() { + public SemType createSemType() { + Env env = Env.getInstance(); if (distinctIdSupplier == null) { distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); } - return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) - .reduce(semTypeInner(), Core::intersect); + CellAtomicType.CellMutability mut = + SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY) ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType innerType; + if (defn != null) { + innerType = defn.getSemType(env); + } else { + ObjectDefinition od = new ObjectDefinition(); + defn = od; + 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) { @@ -290,12 +303,9 @@ private static boolean skipField(Set seen, String name) { return !seen.add(name); } - private SemType semTypeInner() { - if (defn != null) { - return defn.getSemType(env); - } - ObjectDefinition od = new ObjectDefinition(); - defn = od; + 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<>(); @@ -307,7 +317,7 @@ private SemType semTypeInner() { 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, tryInto(field.getFieldType()), Member.Kind.Field, + members.add(new Member(name, semTypeSupplier.apply(field.getFieldType()), Member.Kind.Field, isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); } for (MethodData method : allMethods()) { @@ -319,7 +329,7 @@ private SemType semTypeInner() { members.add(new Member(name, method.semType(), Member.Kind.Method, isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); } - return od.define(env, qualifiers, members); + return od.define(env, qualifiers, members, mut); } private ObjectQualifiers getObjectQualifiers() { @@ -347,7 +357,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, return Optional.of(cachedShape); } if (distinctIdSupplier == null) { - distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); + distinctIdSupplier = new DistinctIdSupplier(cx.env, typeIdSet); } SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) .reduce(valueShape(cx, shapeSupplier, abstractObjectValue), Core::intersect); @@ -360,6 +370,25 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje return Optional.of(valueShape(cx, shapeSupplierFn, (AbstractObjectValue) object)); } + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = Env.getInstance(); + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); + } + CellAtomicType.CellMutability mut = CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + SemType innerType; + if (acceptedTypeDefn != null) { + innerType = acceptedTypeDefn.getSemType(env); + } else { + ObjectDefinition od = new ObjectDefinition(); + acceptedTypeDefn = od; + 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)) { @@ -401,7 +430,9 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObje members.add(new Member(name, method.semType(), Member.Kind.Method, isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); } - return od.define(env, qualifiers, members); + 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, 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 8463e22f26c1..2f6d3a6af672 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 @@ -34,6 +34,7 @@ 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; @@ -54,10 +55,11 @@ 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.neverType; -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 BRecordType} represents a user defined record type in Ballerina. @@ -73,7 +75,7 @@ public class BRecordType extends BStructureType implements RecordType, TypeWithS private IntersectionType immutableType; private IntersectionType intersectionType = null; private MappingDefinition defn; - private final Env env = Env.getInstance(); + private MappingDefinition acceptedTypeDefn; private byte couldInhereTypeBeDifferentCache = 0; private final Map> defaultValues = new LinkedHashMap<>(); @@ -249,17 +251,27 @@ public void setDefaultValue(String fieldName, BFunctionPointer defaul @Override public SemType createSemType() { + Env env = Env.getInstance(); if (defn != null) { return defn.getSemType(env); } MappingDefinition md = new MappingDefinition(); defn = md; + 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 = tryInto(field.getFieldType()); + SemType fieldType = semTypeFunction.apply(field.getFieldType()); if (!isOptional && Core.isNever(fieldType)) { return neverType(); } @@ -270,9 +282,8 @@ public SemType createSemType() { mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, isReadonly, isOptional); } - CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; SemType rest; - rest = restFieldType != null ? tryInto(restFieldType) : neverType(); + rest = restFieldType != null ? semTypeFunction.apply(restFieldType) : neverType(); return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); } @@ -297,13 +308,15 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, return Optional.of(semTypePart); } - private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value, boolean readonly) { + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value, + boolean takeFieldShape) { + Env env = cx.env; int nFields = value.size(); List fields = new ArrayList<>(nFields); Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); Set handledFields = new HashSet<>(nFields); MappingDefinition md; - if (readonly) { + if (takeFieldShape) { MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); if (readonlyShapeDefinition != null) { return readonlyShapeDefinition.getSemType(env); @@ -321,7 +334,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm boolean readonlyField = fieldIsReadonly(fieldName); boolean optionalField = fieldIsOptional(fieldName); Optional fieldType; - if (readonly || readonlyField) { + if (takeFieldShape || readonlyField) { optionalField = false; fieldType = shapeSupplier.get(cx, fieldValue); } else { @@ -331,7 +344,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm fields.add(new MappingDefinition.Field(fieldName, fieldType.get(), readonlyField, optionalField)); } - if (!readonly) { + if (!takeFieldShape) { for (var field : getFields().values()) { String name = field.getFieldName(); if (handledFields.contains(name)) { @@ -350,12 +363,13 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm } SemType semTypePart; MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); - if (readonly) { - semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, neverType(), CELL_MUT_NONE); + SemType rest; + if (takeFieldShape) { + rest = Builder.neverType(); } else { - SemType rest = restFieldType != null ? SemType.tryInto(restFieldType) : neverType(); - semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, CELL_MUT_LIMITED); + rest = restFieldType != null ? SemType.tryInto(restFieldType) : neverType(); } + semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, mut()); value.resetReadonlyShapeDefinition(); return semTypePart; } @@ -382,6 +396,18 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object return Optional.of(shapeOfInner(cx, shapeSupplier, (MapValueImpl) object, true)); } + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn != null) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + MappingDefinition md = new MappingDefinition(); + acceptedTypeDefn = md; + 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(); 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 286a044256a6..45226ee36d66 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 @@ -216,6 +216,12 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje return Optional.of(valueShape(cx, shapeSupplierFn, (BTable) object)); } + @Override + public Optional acceptedTypeOf(Context cx) { + // TODO: this is not correct but can we actually match tables? + return Optional.of(getSemType()); + } + private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { SemType constraintType = Builder.neverType(); for (var value : table.values()) { 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 28b1b714f8e3..7f743e220d83 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 @@ -28,6 +28,7 @@ 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.ListDefinition; import io.ballerina.runtime.internal.values.AbstractArrayValue; @@ -38,10 +39,12 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import static io.ballerina.runtime.api.types.semtype.Builder.neverType; 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. @@ -62,7 +65,7 @@ public class BTupleType extends BAnnotatableType implements TupleType, TypeWithS private boolean resolvingReadonly; private String cachedToString; private ListDefinition defn; - private final Env env = Env.getInstance(); + private ListDefinition acceptedTypeDefn; /** * Create a {@code BTupleType} which represents the tuple type. @@ -321,22 +324,30 @@ public String getAnnotationKey() { @Override public SemType createSemType() { + Env env = Env.getInstance(); if (defn != null) { return defn.getSemType(env); } ListDefinition ld = new ListDefinition(); defn = ld; + 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 = tryInto(tupleTypes.get(i)); + SemType memberType = semTypeFunction.apply(tupleTypes.get(i)); if (Core.isNever(memberType)) { return neverType(); } memberTypes[i] = memberType; } - CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : - CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType rest = restType != null ? tryInto(restType) : neverType(); + SemType rest = restType != null ? semTypeFunction.apply(restType) : neverType(); return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); } @@ -356,7 +367,7 @@ public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, if (cachedShape != null) { return Optional.of(cachedShape); } - SemType semType = readonlyShape(cx, shapeSupplier, value); + SemType semType = shapeOfInner(cx, shapeSupplier, value); value.cacheShape(semType); return Optional.of(semType); } @@ -368,10 +379,23 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { - return Optional.of(readonlyShape(cx, shapeSupplier, (AbstractArrayValue) object)); + return Optional.of(shapeOfInner(cx, shapeSupplier, (AbstractArrayValue) object)); + } + + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn != null) { + return Optional.ofNullable(acceptedTypeDefn.getSemType(env)); + } + ListDefinition ld = new ListDefinition(); + acceptedTypeDefn = ld; + return Optional.of(createSemTypeInner(env, ld, (type) -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow(), + CELL_MUT_UNLIMITED)); } - private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + Env env = cx.env; ListDefinition defn = value.getReadonlyShapeDefinition(); if (defn != null) { return defn.getSemType(env); @@ -385,7 +409,7 @@ private SemType readonlyShape(Context cx, ShapeSupplier shapeSupplier, AbstractA assert memberType.isPresent(); memberTypes[i] = memberType.get(); } - SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), CELL_MUT_NONE); + SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, neverType(), mut()); value.resetReadonlyShapeDefinition(); return semType; } 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 18c22b722c2d..331767a581cc 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 @@ -27,6 +27,7 @@ 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.types.semtype.ShapeAnalyzer; import java.util.Objects; import java.util.Optional; @@ -160,4 +161,13 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje } return Optional.empty(); } + + @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/BXmlType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java index c1f4d18d8d7b..7445fd376fd6 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 @@ -207,6 +207,11 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.readonlyType())); } + @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 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 index 4f2d3c2ff93b..650609221196 100644 --- 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 @@ -36,5 +36,7 @@ public interface TypeWithShape { Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + Optional acceptedTypeOf(Context cx); + boolean couldInherentTypeBeDifferent(); } 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 index 7c764d08b8fc..d3494eccca04 100644 --- 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 @@ -46,9 +46,7 @@ public SemType getSemType(Env env) { return objectContaining(mappingDefinition.getSemType(env)); } - public SemType define(Env env, ObjectQualifiers qualifiers, List members) { - CellAtomicType.CellMutability mut = qualifiers.readonly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : - CellAtomicType.CellMutability.CELL_MUT_LIMITED; + 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)); 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 index 9f1dc91637dd..f0929b83f4ee 100644 --- 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 @@ -252,7 +252,8 @@ private SemType resolveNonDistinctObject(TypeTestContext cx, Map members = Stream.concat(fieldStream, methodStream).toList(); ObjectQualifiers qualifiers = getQualifiers(td); - return od.define(env, qualifiers, members); + return od.define(env, qualifiers, members, qualifiers.readonly() ? CELL_MUT_NONE : + CELL_MUT_LIMITED); } private ObjectQualifiers getQualifiers(BLangObjectTypeNode td) { From e16eda7595dfa8a84351cf5b150be10de06303a9 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 18 Sep 2024 15:13:21 +0530 Subject: [PATCH 166/178] Introduce wrapper for bddEvery --- .../java/io/ballerina/runtime/api/types/semtype/Bdd.java | 6 +++++- .../runtime/internal/types/semtype/BCellSubTypeImpl.java | 2 +- .../runtime/internal/types/semtype/BFunctionSubType.java | 2 +- .../runtime/internal/types/semtype/BFutureSubType.java | 2 +- .../runtime/internal/types/semtype/BListSubType.java | 2 +- .../runtime/internal/types/semtype/BMappingSubType.java | 2 +- .../runtime/internal/types/semtype/BStreamSubType.java | 2 +- .../runtime/internal/types/semtype/BTableSubType.java | 2 +- .../runtime/internal/types/semtype/BXmlSubType.java | 2 +- 9 files changed, 13 insertions(+), 9 deletions(-) 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 index 726a7bcfceaf..7d9ef4060457 100644 --- 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 @@ -220,7 +220,11 @@ public SubTypeData data() { throw new IllegalStateException("Bdd don't support data"); } - public static boolean bddEvery(Context cx, Bdd b, Conjunction pos, Conjunction neg, BddPredicate predicate) { + 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); } 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 index 4019189ecfa1..4de1709ceb08 100644 --- 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 @@ -71,7 +71,7 @@ public SubType complement() { @Override public boolean isEmpty(Context cx) { - return Bdd.bddEvery(cx, inner, null, null, BCellSubTypeImpl::cellFormulaIsEmpty); + return Bdd.bddEvery(cx, inner, BCellSubTypeImpl::cellFormulaIsEmpty); } @Override 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 index d695ecc189fe..94eb87eb4101 100644 --- 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 @@ -74,7 +74,7 @@ public SubType complement() { @Override public boolean isEmpty(Context cx) { return cx.memoSubtypeIsEmpty(cx.functionMemo, - (context, bdd) -> bddEvery(context, bdd, null, null, BFunctionSubType::functionFormulaIsEmpty), inner); + (context, bdd) -> bddEvery(context, bdd, BFunctionSubType::functionFormulaIsEmpty), inner); } private static boolean functionFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { 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 index 1d225a03042f..06c6efa4403e 100644 --- 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 @@ -70,7 +70,7 @@ public SubType complement() { @Override public boolean isEmpty(Context cx) { return cx.memoSubtypeIsEmpty(cx.mappingMemo, - (context, bdd) -> bddEvery(context, bdd, null, null, BMappingSubType::mappingFormulaIsEmpty), inner); + (context, bdd) -> bddEvery(context, bdd, BMappingSubType::mappingFormulaIsEmpty), inner); } @Override 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 index 747052dfa4fa..c2757f1545d8 100644 --- 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 @@ -97,7 +97,7 @@ public SubType diff(SubType other) { @Override public boolean isEmpty(Context cx) { return cx.memoSubtypeIsEmpty(cx.listMemo, - (context, bdd) -> bddEvery(context, bdd, null, null, BListSubType::listFormulaIsEmpty), inner); + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), inner); } static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { 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 index 244504283129..5749f83f1e06 100644 --- 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 @@ -83,7 +83,7 @@ public SubType complement() { @Override public boolean isEmpty(Context cx) { return cx.memoSubtypeIsEmpty(cx.mappingMemo, - (context, bdd) -> bddEvery(context, bdd, null, null, BMappingSubType::mappingFormulaIsEmpty), inner); + (context, bdd) -> bddEvery(context, bdd, BMappingSubType::mappingFormulaIsEmpty), inner); } static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { 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 index 906b8913ff76..bc2ff8f6c018 100644 --- 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 @@ -75,7 +75,7 @@ public boolean isEmpty(Context cx) { // as `[any|error...]` rather than `[any|error, any|error]`. b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.listSubtypeTwoElement()) : b; return cx.memoSubtypeIsEmpty(cx.listMemo, - (context, bdd) -> bddEvery(context, bdd, null, null, BListSubType::listFormulaIsEmpty), b); + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); } @Override 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 index 932a5ed9a02b..3f3a3fd3ee26 100644 --- 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 @@ -77,7 +77,7 @@ public boolean isEmpty(Context cx) { // as `(any|error)[]` rather than `[(map)[], any|error, any|error]`. b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.listSubtypeThreeElement()) : b; return cx.memoSubtypeIsEmpty(cx.listMemo, - (context, bdd) -> bddEvery(context, bdd, null, null, BListSubType::listFormulaIsEmpty), b); + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); } @Override 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 index 8d3ed726169b..f6137905b67f 100644 --- 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 @@ -85,7 +85,7 @@ public boolean isEmpty(Context cx) { } private boolean xmlBddEmpty(Context cx) { - return Bdd.bddEvery(cx, inner, null, null, BXmlSubType::xmlFormulaIsEmpty); + return Bdd.bddEvery(cx, inner, BXmlSubType::xmlFormulaIsEmpty); } private static boolean xmlFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { From ddd17a859f7cc0b7332c4b19c46054754f126425 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 18 Sep 2024 16:09:26 +0530 Subject: [PATCH 167/178] Move mutable semtype to inner --- .../java/io/ballerina/runtime/api/types/semtype/SemType.java | 1 + .../main/java/io/ballerina/runtime/internal/types/BType.java | 2 +- .../{api => internal}/types/semtype/MutableSemType.java | 3 ++- bvm/ballerina-runtime/src/main/java/module-info.java | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{api => internal}/types/semtype/MutableSemType.java (88%) 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 index 57c752e55689..8606126d1d2e 100644 --- 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 @@ -2,6 +2,7 @@ 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; import java.util.Map; 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 fd18d66c75e9..01ac5e98905b 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 @@ -23,10 +23,10 @@ import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.semtype.MutableSemType; import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.MutableSemType; import java.util.Objects; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java similarity index 88% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java index 37b8cbcb65e3..01b223b66460 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MutableSemType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java @@ -16,8 +16,9 @@ * under the License. */ -package io.ballerina.runtime.api.types.semtype; +package io.ballerina.runtime.internal.types.semtype; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.types.BType; public sealed interface MutableSemType permits BType { diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index 675da0d260b7..d0ec0b6b4dd9 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -74,5 +74,5 @@ exports io.ballerina.runtime.observability.tracer.noop; exports io.ballerina.runtime.internal.regexp; exports io.ballerina.runtime.internal.configurable.providers to org.ballerinalang.debugadapter.runtime; - exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.api.types.semtype; + exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.api.types.semtype, io.ballerina.runtime.internal.types; } From c97b52c9220472844cc3138fd4baeda4850be84c Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 29 Sep 2024 15:54:04 +0530 Subject: [PATCH 168/178] Refactor method names --- .../api/types/semtype/BasicTypeCode.java | 45 +++++---- .../runtime/api/types/semtype/BddNode.java | 8 +- .../runtime/api/types/semtype/Builder.java | 96 +++++++++---------- .../runtime/api/types/semtype/Core.java | 43 +++++---- .../runtime/api/types/semtype/Env.java | 17 ++-- .../runtime/api/types/semtype/FieldPair.java | 4 - .../runtime/api/types/semtype/FieldPairs.java | 10 +- .../runtime/api/types/semtype/ListProj.java | 2 +- .../api/types/semtype/MappingProj.java | 5 + .../runtime/api/types/semtype/Pair.java | 9 ++ .../api/types/semtype/PredefinedTypeEnv.java | 12 +-- .../api/types/semtype/ShapeAnalyzer.java | 18 ++-- .../ballerina/runtime/api/values/BError.java | 2 +- .../ballerina/runtime/api/values/BValue.java | 9 +- .../runtime/internal/TypeChecker.java | 25 ++--- .../runtime/internal/types/BAnyType.java | 2 +- .../runtime/internal/types/BAnydataType.java | 2 +- .../runtime/internal/types/BBooleanType.java | 4 +- .../runtime/internal/types/BByteType.java | 5 +- .../runtime/internal/types/BDecimalType.java | 2 +- .../runtime/internal/types/BErrorType.java | 2 +- .../runtime/internal/types/BFloatType.java | 2 +- .../runtime/internal/types/BFunctionType.java | 4 +- .../runtime/internal/types/BFutureType.java | 2 +- .../runtime/internal/types/BHandleType.java | 2 +- .../runtime/internal/types/BIntegerType.java | 14 +-- .../internal/types/BIntersectionType.java | 4 +- .../runtime/internal/types/BObjectType.java | 2 +- .../internal/types/BSemTypeWrapper.java | 4 +- .../runtime/internal/types/BStreamType.java | 2 +- .../runtime/internal/types/BStringType.java | 2 +- .../runtime/internal/types/BTypedescType.java | 2 +- .../runtime/internal/types/BXmlType.java | 18 ++-- .../types/semtype/BCellSubTypeImpl.java | 4 +- .../types/semtype/BCellSubTypeSimple.java | 2 +- .../internal/types/semtype/BErrorSubType.java | 4 +- .../types/semtype/BFunctionSubType.java | 2 +- .../internal/types/semtype/BListProj.java | 11 +-- .../internal/types/semtype/BListSubType.java | 13 +-- .../internal/types/semtype/BMappingProj.java | 2 +- .../types/semtype/BMappingSubType.java | 2 +- .../types/semtype/BStreamSubType.java | 4 +- .../internal/types/semtype/BTableSubType.java | 4 +- .../types/semtype/BTypedescSubType.java | 4 +- .../internal/types/semtype/ErrorUtils.java | 8 +- .../types/semtype/FunctionQualifiers.java | 4 +- .../internal/types/semtype/FutureUtils.java | 4 +- .../types/semtype/ListDefinition.java | 8 +- .../types/semtype/MappingAtomicType.java | 6 +- .../types/semtype/MappingDefinition.java | 6 +- .../internal/types/semtype/Member.java | 10 +- .../types/semtype/ObjectDefinition.java | 53 ++++------ .../types/semtype/ObjectQualifiers.java | 10 +- .../internal/types/semtype/RegexUtils.java | 2 +- .../types/semtype/StreamDefinition.java | 4 +- .../internal/types/semtype/TableUtils.java | 10 +- .../internal/types/semtype/TypedescUtils.java | 4 +- .../internal/types/semtype/XmlUtils.java | 6 +- .../internal/values/AbstractArrayValue.java | 2 +- .../internal/values/AbstractObjectValue.java | 2 +- .../runtime/internal/values/DecimalValue.java | 6 +- .../runtime/internal/values/FPValue.java | 6 +- .../runtime/internal/values/MapValueImpl.java | 2 +- .../runtime/internal/values/RegExpValue.java | 6 +- .../runtime/internal/values/StringValue.java | 4 +- .../internal/values/TableValueImpl.java | 2 +- .../runtime/internal/values/XmlValue.java | 2 +- .../runtime/test/semtype/CoreTests.java | 8 +- .../port/test/RuntimeSemTypeResolver.java | 82 ++++++++-------- .../semtype/port/test/RuntimeTypeTestAPI.java | 4 +- .../resources/test-src/type-rel/test-tv.bal | 0 71 files changed, 340 insertions(+), 353 deletions(-) delete mode 100644 tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test-tv.bal 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 index be4871ccfc87..ba01d29eb71c 100644 --- 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 @@ -46,34 +46,33 @@ public final class BasicTypeCode { public static final int CODE_CELL = 0x12; public static final int CODE_UNDEF = 0x13; - // TODO: see if we can turn this class to an enum with a value // Inherently immutable - public static final BasicTypeCode BT_NIL = from(CODE_NIL); - public static final BasicTypeCode BT_BOOLEAN = from(CODE_BOOLEAN); - public static final BasicTypeCode BT_INT = from(CODE_INT); - public static final BasicTypeCode BT_FLOAT = from(CODE_FLOAT); - public static final BasicTypeCode BT_DECIMAL = from(CODE_DECIMAL); - public static final BasicTypeCode BT_STRING = from(CODE_STRING); - public static final BasicTypeCode BT_ERROR = from(CODE_ERROR); - public static final BasicTypeCode BT_TYPEDESC = from(CODE_TYPEDESC); - public static final BasicTypeCode BT_HANDLE = from(CODE_HANDLE); - public static final BasicTypeCode BT_FUNCTION = from(CODE_FUNCTION); - public static final BasicTypeCode BT_REGEXP = from(CODE_REGEXP); + 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 = from(CODE_FUTURE); - public static final BasicTypeCode BT_STREAM = from(CODE_STREAM); + 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 = from(CODE_LIST); - public static final BasicTypeCode BT_MAPPING = from(CODE_MAPPING); - public static final BasicTypeCode BT_TABLE = from(CODE_TABLE); - public static final BasicTypeCode BT_XML = from(CODE_XML); - public static final BasicTypeCode BT_OBJECT = from(CODE_OBJECT); + 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 = from(CODE_CELL); - public static final BasicTypeCode BT_UNDEF = from(CODE_UNDEF); + 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; @@ -83,13 +82,13 @@ public final class BasicTypeCode { static final int VT_COUNT_INHERENTLY_IMMUTABLE = CODE_FUTURE; public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; - private int code; + private final int code; private BasicTypeCode(int code) { this.code = code; } - public static BasicTypeCode from(int code) { + public static BasicTypeCode get(int code) { if (BasicTypeCodeCache.isCached(code)) { return BasicTypeCodeCache.cache[code]; } 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 index 89139415106f..949d36ed5b2e 100644 --- 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 @@ -40,12 +40,8 @@ public boolean equals(Object obj) { public int hashCode() { Integer result = hashCode; if (result == null) { - synchronized (this) { - result = hashCode; - if (result == null) { - hashCode = result = computeHashCode(); - } - } + // No need to synchronize this since {@code computeHashCode} is idempotent + hashCode = result = computeHashCode(); } return result; } 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 index d742d77fabe4..23d09ee3527e 100644 --- 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 @@ -38,7 +38,6 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_ERROR; @@ -69,10 +68,10 @@ public final class Builder { private static final SemType VAL = SemType.from(VT_MASK); private static final SemType OBJECT = from(BT_OBJECT); - private static final SemType INNER = basicTypeUnion(VAL.all() | from(BasicTypeCode.BT_UNDEF).all()); - private static final SemType ANY = basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); + 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 = - basicTypeUnion((1 << BasicTypeCode.BT_NIL.code()) + getBasicTypeUnion((1 << BasicTypeCode.BT_NIL.code()) | (1 << BasicTypeCode.BT_BOOLEAN.code()) | (1 << BasicTypeCode.BT_INT.code()) | (1 << BasicTypeCode.BT_FLOAT.code()) @@ -83,7 +82,7 @@ public final class Builder { private static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; private static final ConcurrentLazySupplier MAPPING_RO = new ConcurrentLazySupplier<>(() -> - basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())) + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(getBddSubtypeRo())) ); private static final ConcurrentLazySupplier ANYDATA = new ConcurrentLazySupplier<>( () -> { @@ -92,7 +91,7 @@ public final class Builder { MappingDefinition mapDef = new MappingDefinition(); SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); SemType accum = - unionOf(simpleOrStringType(), xmlType(), listDef.getSemType(env), mapDef.getSemType(env), + unionOf(getSimpleOrStringType(), getXmlType(), listDef.getSemType(env), mapDef.getSemType(env), tableTy); listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); @@ -186,7 +185,7 @@ public static SemType readonlyType() { return PREDEFINED_TYPE_ENV.readonlyType(); } - static SemType basicTypeUnion(int bitset) { + static SemType getBasicTypeUnion(int bitset) { return switch (bitset) { case 0 -> neverType(); case VT_MASK -> VAL; @@ -218,7 +217,7 @@ private static boolean checkDelegate(BasicTypeCode basicTypeCode, SubType subTyp && (basicTypeCode != BT_CELL || subType instanceof BCellSubType); } - public static SemType intConst(long value) { + 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]; } @@ -231,25 +230,25 @@ private static SemType createIntSingletonType(long value) { return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(values)); } - public static SemType booleanConst(boolean value) { + public static SemType getBooleanConst(boolean value) { return value ? BooleanTypeCache.TRUE : BooleanTypeCache.FALSE; } - public static SemType intRange(long min, long max) { + public static SemType createIntRange(long min, long max) { return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(min, max)); } - public static SemType decimalConst(BigDecimal value) { + public static SemType getDecimalConst(BigDecimal value) { BigDecimal[] values = {value}; return basicSubType(BasicTypeCode.BT_DECIMAL, BDecimalSubType.createDecimalSubType(true, values)); } - public static SemType floatConst(double value) { + public static SemType getFloatConst(double value) { Double[] values = {value}; return basicSubType(BasicTypeCode.BT_FLOAT, BFloatSubType.createFloatSubType(true, values)); } - public static SemType stringConst(String value) { + public static SemType getStringConst(String value) { BStringSubType subType; String[] values = {value}; String[] empty = EMPTY_STRING_ARR; @@ -265,21 +264,16 @@ static SubType[] initializeSubtypeArray(int some) { return new SubType[Integer.bitCount(some)]; } - public static SemType roCellContaining(Env env, SemType ty) { - return cellContaining(env, ty, CELL_MUT_NONE); + public static SemType getRoCellContaining(Env env, SemType ty) { + return getCellContaining(env, ty, CELL_MUT_NONE); } - public static SemType cellContaining(Env env, SemType ty) { - return cellContaining(env, ty, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + public static SemType getRwCellContaining(Env env, SemType ty) { + return getCellContaining(env, ty, CellAtomicType.CellMutability.CELL_MUT_LIMITED); } - public static SemType cellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { - Optional cachedSemType = env.getCachedCellType(ty, mut); - return cachedSemType.orElseGet(() -> { - SemType semType = createCellSemType(env, ty, mut); - env.cacheCellType(ty, mut, semType); - return semType; - }); + 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) { @@ -289,71 +283,71 @@ private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.Cel return basicSubType(BT_CELL, BCellSubType.createDelegate(bdd)); } - public static SemType valType() { - return basicTypeUnion(VT_MASK); + public static SemType getValType() { + return getBasicTypeUnion(VT_MASK); } - public static SemType anyType() { + public static SemType getAnyType() { return ANY; } - public static SemType mappingType() { + public static SemType getMappingType() { return from(BT_MAPPING); } - public static SemType functionType() { + public static SemType getFunctionType() { return from(BT_FUNCTION); } - public static SemType errorType() { + public static SemType getErrorType() { return from(BT_ERROR); } - public static SemType xmlType() { + public static SemType getXmlType() { return from(BT_XML); } - public static SemType xmlElementType() { + public static SemType getXmlElementType() { return XML_ELEMENT.get(); } - public static SemType xmlCommentType() { + public static SemType getXmlCommentType() { return XML_COMMENT.get(); } - public static SemType xmlTextType() { + public static SemType getXmlTextType() { return XML_TEXT.get(); } - public static SemType xmlNeverType() { + public static SemType getXmlNeverType() { return XML_NEVER.get(); } - public static SemType xmlPIType() { + public static SemType getXmlPIType() { return XML_PI.get(); } - public static SemType handleType() { + public static SemType getHandleType() { return from(BT_HANDLE); } - public static SemType futureType() { + public static SemType getFutureType() { return from(BT_FUTURE); } - public static SemType regexType() { + public static SemType getRegexType() { return from(BT_REGEXP); } - public static SemType typeDescType() { + public static SemType getTypeDescType() { return from(BT_TYPEDESC); } - public static SemType streamType() { + public static SemType getStreamType() { return from(BasicTypeCode.BT_STREAM); } - public static SemType anyDataType() { + public static SemType getAnyDataType() { return ANYDATA.get(); } @@ -365,7 +359,7 @@ private static SemType unionOf(SemType... types) { return accum; } - public static SemType objectType() { + public static SemType getObjectType() { return OBJECT; } @@ -381,35 +375,35 @@ static CellAtomicType cellAtomicVal() { return PREDEFINED_TYPE_ENV.cellAtomicVal(); } - public static BddNode bddSubtypeRo() { + public static BddNode getBddSubtypeRo() { return bddAtom(RecAtom.createRecAtom(0)); } - public static ListAtomicType listAtomicInner() { + public static ListAtomicType getListAtomicInner() { return LIST_ATOMIC_INNER.get(); } - public static MappingAtomicType mappingAtomicInner() { + public static MappingAtomicType getMappingAtomicInner() { return MAPPING_ATOMIC_INNER.get(); } - public static BddNode listSubtypeThreeElement() { + public static BddNode getListSubtypeThreeElement() { return LIST_SUBTYPE_THREE_ELEMENT; } - public static BddNode listSubtypeThreeElementRO() { + public static BddNode getListSubtypeThreeElementRO() { return LIST_SUBTYPE_THREE_ELEMENT_RO; } - public static BddNode listSubtypeTwoElement() { + public static BddNode getListSubtypeTwoElement() { return LIST_SUBTYPE_TWO_ELEMENT; } - public static SemType simpleOrStringType() { + public static SemType getSimpleOrStringType() { return SIMPLE_OR_STRING; } - public static SemType tableType() { + public static SemType getTableType() { return from(BasicTypeCode.BT_TABLE); } 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 index 6234864e236e..c1018437bb53 100644 --- 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 @@ -50,14 +50,13 @@ 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.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.listType; import static io.ballerina.runtime.api.types.semtype.Builder.undef; +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; -import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; -import static io.ballerina.runtime.internal.types.semtype.BMappingProj.mappingMemberTypeInner; /** * Contain functions defined in `core.bal` file. @@ -76,7 +75,7 @@ public static SemType diff(SemType t1, SemType t2) { int some2 = t2.some(); if (some1 == 0) { if (some2 == 0) { - return Builder.basicTypeUnion(all1 & ~all2); + return Builder.getBasicTypeUnion(all1 & ~all2); } else { if (all1 == 0) { return t1; @@ -85,7 +84,7 @@ public static SemType diff(SemType t1, SemType t2) { } else { if (some2 == 0) { if (all2 == VT_MASK) { - return Builder.basicTypeUnion(0); + return Builder.getBasicTypeUnion(0); } } } @@ -142,13 +141,14 @@ public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { // 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() & listType().all()) != 0 ? Builder.valType() : Builder.neverType(); + return (t.all() & listType().all()) != 0 ? Builder.getValType() : Builder.neverType(); } else { SubTypeData keyData = intSubtype(k); if (isNothingSubtype(keyData)) { return Builder.neverType(); } - return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData(t, BT_LIST), keyData, Builder.valType()); + return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData(t, BT_LIST), keyData, + Builder.getValType()); } } @@ -160,14 +160,14 @@ public static SemType union(SemType t1, SemType t2) { int some2 = t2.some(); if (some1 == 0) { if (some2 == 0) { - return Builder.basicTypeUnion(all1 | all2); + return Builder.getBasicTypeUnion(all1 | all2); } } int all = all1 | all2; int some = (some1 | some2) & ~all; if (some == 0) { - return Builder.basicTypeUnion(all); + return Builder.getBasicTypeUnion(all); } SubType[] subtypes = Builder.initializeSubtypeArray(some); int i = 0; @@ -287,7 +287,7 @@ public static boolean isEmpty(Context cx, SemType t) { } public static SemType complement(SemType t) { - return diff(Builder.valType(), t); + return diff(Builder.getValType(), t); } public static boolean isNever(SemType t) { @@ -347,20 +347,21 @@ private static int cardinality(int bitset) { return Integer.bitCount(bitset); } - public static SemType cellContainingInnerVal(Env env, SemType t) { + public static SemType getCellContainingInnerVal(Env env, SemType t) { CellAtomicType cat = cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); - return cellContaining(env, diff(cat.ty(), undef()), cat.mut()); + return Builder.getCellContaining(env, diff(cat.ty(), undef()), cat.mut()); } - public static SemType intersectMemberSemTypes(Env env, SemType t1, SemType t2) { + 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 cellContaining(env, atomicType.ty(), undef().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + return Builder.getCellContaining(env, atomicType.ty(), + undef().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); } public static Optional cellAtomicType(SemType t) { @@ -416,7 +417,7 @@ public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k } public static Optional listAtomicType(Context cx, SemType t) { - ListAtomicType listAtomicInner = Builder.listAtomicInner(); + ListAtomicType listAtomicInner = Builder.getListAtomicInner(); if (t.some() == 0) { return Core.isSubtypeSimple(t, Builder.listType()) ? Optional.ofNullable(listAtomicInner) : Optional.empty(); @@ -433,7 +434,7 @@ public static SemType floatToInt(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_FLOAT, Builder.intType(), - (floatValue) -> ((Double) floatValue).longValue(), Builder::intConst); + (floatValue) -> ((Double) floatValue).longValue(), Builder::getIntConst); } public static SemType floatToDecimal(SemType t) { @@ -441,7 +442,7 @@ public static SemType floatToDecimal(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_FLOAT, Builder.decimalType(), - (floatValue) -> BigDecimal.valueOf((Double) floatValue), Builder::decimalConst); + (floatValue) -> BigDecimal.valueOf((Double) floatValue), Builder::getDecimalConst); } public static SemType decimalToInt(SemType t) { @@ -449,7 +450,7 @@ public static SemType decimalToInt(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_DECIMAL, Builder.intType(), - (decimalVal) -> ((BigDecimal) decimalVal).longValue(), Builder::intConst); + (decimalVal) -> ((BigDecimal) decimalVal).longValue(), Builder::getIntConst); } public static SemType decimalToFloat(SemType t) { @@ -457,7 +458,7 @@ public static SemType decimalToFloat(SemType t) { return Builder.neverType(); } return convertEnumerableNumericType(t, BT_DECIMAL, Builder.floatType(), - (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), Builder::floatConst); + (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), Builder::getFloatConst); } public static SemType intToFloat(SemType t) { @@ -472,7 +473,7 @@ public static SemType intToFloat(SemType t) { return Builder.floatType(); } BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; - return intSubTypeData.values().stream().map(Builder::floatConst).reduce(Builder.neverType(), Core::union); + return intSubTypeData.values().stream().map(Builder::getFloatConst).reduce(Builder.neverType(), Core::union); } public static SemType intToDecimal(SemType t) { @@ -487,7 +488,7 @@ public static SemType intToDecimal(SemType t) { return Builder.decimalType(); } BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; - return intSubTypeData.values().stream().map(BigDecimal::new).map(Builder::decimalConst) + return intSubTypeData.values().stream().map(BigDecimal::new).map(Builder::getDecimalConst) .reduce(Builder.neverType(), Core::union); } 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 index 7661052c4d6e..b787619f823d 100644 --- 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 @@ -34,6 +34,7 @@ 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 @@ -104,18 +105,14 @@ private TypeAtom typeAtom(AtomicType atomicType) { } } - Optional getCachedCellType(SemType ty, CellAtomicType.CellMutability mut) { + // 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 Optional.empty(); - } - return Optional.ofNullable(this.cellTypeCache.get(new CellSemTypeCacheKey(ty, mut))); - } - - void cacheCellType(SemType ty, CellAtomicType.CellMutability mut, SemType semType) { - if (ty.some() != 0) { - return; + return semTypeCreator.get(); } - this.cellTypeCache.put(new CellSemTypeCacheKey(ty, mut), semType); + return this.cellTypeCache.computeIfAbsent(new CellSemTypeCacheKey(ty, mut), k -> semTypeCreator.get()); } public RecAtom recListAtom() { 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 index 646310006938..732e1f7f174f 100644 --- 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 @@ -20,8 +20,4 @@ public record FieldPair(String name, SemType type1, SemType type2, Integer index1, Integer index2) { - public static FieldPair create(String name, SemType type1, SemType type2, Integer index1, - Integer index2) { - return new FieldPair(name, type1, type2, index1, 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 index 36c37ce6ce14..78bc55284694 100644 --- 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 @@ -114,22 +114,22 @@ private FieldPair internalNext() { if (this.i2 >= this.len2) { return null; } - p = FieldPair.create(curName2(), this.rest1, curType2(), null, this.i2); + p = new FieldPair(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); + 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 = FieldPair.create(name1, curType1(), this.rest2, this.i1, null); + p = new FieldPair(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); + p = new FieldPair(name2, this.rest1, curType2(), null, this.i2); this.i2 += 1; } else { - p = FieldPair.create(name1, curType1(), curType2(), this.i1, this.i2); + p = new FieldPair(name1, curType1(), curType2(), this.i1, this.i2); this.i1 += 1; this.i2 += 1; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java index 13746dfd901e..10e75f2c1ad8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java @@ -21,7 +21,7 @@ import io.ballerina.runtime.internal.types.semtype.BListProj; /** - * Wrapper utility class for list type projection. + * Utility class for list type projection. * * @since 2201.10.0 */ diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java index 7ed7e47bed96..cd67382714c7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -21,6 +21,11 @@ import io.ballerina.runtime.internal.types.semtype.BMappingProj; +/** + * Utility class for mapping type projection. + * + * @since 2201.10.0 + */ public final class MappingProj { private MappingProj() { 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 index a139a2a70469..e93e668d7e84 100644 --- 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 @@ -18,6 +18,15 @@ 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.10.0 + */ public record Pair(E1 first, E2 second) { public static Pair from(E1 first, E2 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 index 5a8ac3ad047a..947a474d19a6 100644 --- 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 @@ -46,9 +46,9 @@ 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.basicTypeUnion; import static io.ballerina.runtime.api.types.semtype.Builder.from; -import static io.ballerina.runtime.api.types.semtype.Builder.stringConst; +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; @@ -69,7 +69,7 @@ final class PredefinedTypeEnv { // 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(basicTypeUnion(VT_MASK), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + () -> CellAtomicType.from(getBasicTypeUnion(VT_MASK), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); private final Supplier atomCellVal = @@ -97,7 +97,7 @@ final class PredefinedTypeEnv { // 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.mappingType(), Builder.undef()), + () -> CellAtomicType.from(union(Builder.getMappingType(), Builder.undef()), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); @@ -219,7 +219,7 @@ final class PredefinedTypeEnv { private final Supplier cellAtomicObjectMemberKind = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( - union(stringConst("field"), stringConst("method")), + union(getStringConst("field"), getStringConst("method")), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); private final Supplier atomCellObjectMemberKind = @@ -229,7 +229,7 @@ final class PredefinedTypeEnv { private final Supplier cellAtomicObjectMemberVisibility = new ConcurrentLazySupplierWithCallback<>( () -> CellAtomicType.from( - union(stringConst("public"), stringConst("private")), + union(getStringConst("public"), getStringConst("private")), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom); private final Supplier atomCellObjectMemberVisibility = 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 index be8e418a4282..a7f26fd81c02 100644 --- 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 @@ -24,17 +24,17 @@ public static Optional shapeOf(Context cx, Object object) { if (object == null) { return Optional.of(Builder.nilType()); } else if (object instanceof DecimalValue decimalValue) { - return Optional.of(Builder.decimalConst(decimalValue.value())); + return Optional.of(Builder.getDecimalConst(decimalValue.value())); } else if (object instanceof Double doubleValue) { - return Optional.of(Builder.floatConst(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.intConst(value)); + return Optional.of(Builder.getIntConst(value)); } else if (object instanceof Boolean booleanValue) { - return Optional.of(Builder.booleanConst(booleanValue)); + return Optional.of(Builder.getBooleanConst(booleanValue)); } else if (object instanceof BString stringValue) { - return Optional.of(Builder.stringConst(stringValue.getValue())); + return Optional.of(Builder.getStringConst(stringValue.getValue())); } else if (object instanceof BValue bValue) { Type type = bValue.getType(); if (type instanceof TypeWithShape typeWithShape) { @@ -48,18 +48,18 @@ public static Optional shapeOf(Context cx, Object object) { public static Optional inherentTypeOf(Context cx, Object object) { if (object instanceof BValue bValue) { - return bValue.shapeOf(cx); + return bValue.inherentTypeOf(cx); } if (object == null) { return Optional.of(Builder.nilType()); } else if (object instanceof Double doubleValue) { - return Optional.of(Builder.floatConst(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.intConst(value)); + return Optional.of(Builder.getIntConst(value)); } else if (object instanceof Boolean booleanValue) { - return Optional.of(Builder.booleanConst(booleanValue)); + return Optional.of(Builder.getBooleanConst(booleanValue)); } return Optional.empty(); } 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 e32190547840..8477a5c48dba 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 @@ -95,7 +95,7 @@ public TypeWithShape getTypeWithShape() { } @Override - public Optional shapeOf(Context cx) { + public Optional inherentTypeOf(Context cx) { TypeWithShape type = getTypeWithShape(); return type.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } 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 b707293ee83b..2e0f3aac5db5 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 @@ -62,11 +62,16 @@ default String informalStringValue(BLink parent) { Type getType(); - default SemType widenedType(Context cx) { + /** + * Basic type of the value. + * + * @return {@code SemType} representing the value's basic type + */ + default SemType widenedType() { return SemType.tryInto(getType()); } - default Optional shapeOf(Context cx) { + 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 d7aba47e97ff..de8c143e7859 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 @@ -441,16 +441,16 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { if (basicTypePredicate.test(Builder.stringType())) { return isEqual(lhsValue, rhsValue); } - if (basicTypePredicate.test(Builder.xmlType())) { + if (basicTypePredicate.test(Builder.getXmlType())) { return isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); } - if (basicTypePredicate.test(Builder.handleType())) { + if (basicTypePredicate.test(Builder.getHandleType())) { return isHandleValueRefEqual(lhsValue, rhsValue); } - if (basicTypePredicate.test(Builder.functionType())) { + if (basicTypePredicate.test(Builder.getFunctionType())) { return isFunctionPointerEqual(getImpliedType(getType(lhsValue)), getImpliedType(getType(rhsValue))); } - if (basicTypePredicate.test(Builder.regexType())) { + if (basicTypePredicate.test(Builder.getRegexType())) { RegExpValue lhsReg = (RegExpValue) lhsValue; RegExpValue rhsReg = (RegExpValue) rhsValue; return lhsReg.equals(rhsReg, new HashSet<>()); @@ -628,7 +628,7 @@ private static boolean isSubType(Type source, Type target) { private static SemType widenedType(Context cx, Object value) { if (value instanceof BValue bValue) { - return bValue.widenedType(cx); + return bValue.widenedType(); } if (value == null) { return Builder.nilType(); @@ -645,9 +645,10 @@ private static SemType widenedType(Context cx, Object value) { } private static SemType createInherentlyImmutableType() { - return Stream.of(createSimpleBasicType(), Builder.stringType(), Builder.errorType(), Builder.functionType(), - Builder.typeDescType(), Builder.handleType(), Builder.xmlTextType(), Builder.xmlNeverType(), - Builder.regexType()) + return Stream.of(createSimpleBasicType(), Builder.stringType(), Builder.getErrorType(), + Builder.getFunctionType(), + Builder.getTypeDescType(), Builder.getHandleType(), Builder.getXmlTextType(), Builder.getXmlNeverType(), + Builder.getRegexType()) .reduce(Builder.neverType(), Core::union); } @@ -817,8 +818,8 @@ public static boolean isEqual(Object lhsValue, Object rhsValue, Set c } private static SemType createRefValueMask() { - return Stream.of(Builder.xmlType(), Builder.mappingType(), Builder.listType(), Builder.errorType(), - Builder.tableType(), Builder.regexType()) + return Stream.of(Builder.getXmlType(), Builder.getMappingType(), Builder.listType(), Builder.getErrorType(), + Builder.getTableType(), Builder.getRegexType()) .reduce(Builder.neverType(), Core::union); } @@ -997,7 +998,7 @@ private enum FillerValueResult { private static SemType createTopTypesWithFillerValues() { return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), - Builder.booleanType(), Builder.nilType(), Builder.tableType(), Builder.mappingType(), + Builder.booleanType(), Builder.nilType(), Builder.getTableType(), Builder.getMappingType(), Builder.listType()).reduce(Builder.neverType(), Core::union); } @@ -1279,7 +1280,7 @@ static boolean isSimpleBasicSemType(SemType semType) { static boolean belongToSingleBasicTypeOrString(Type type) { Context cx = context(); SemType semType = SemType.tryInto(type); - return isSingleBasicType(semType) && Core.isSubType(cx, semType, Builder.simpleOrStringType()) && + return isSingleBasicType(semType) && Core.isSubType(cx, semType, Builder.getSimpleOrStringType()) && !Core.isSubType(cx, semType, Builder.nilType()); } 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 a907e33df01e..2a1b83644531 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 @@ -134,7 +134,7 @@ public void setIntersectionType(IntersectionType intersectionType) { } private static SemType pickSemType(boolean readonly) { - SemType semType = Builder.anyType(); + SemType semType = Builder.getAnyType(); if (readonly) { semType = Core.intersect(semType, Builder.readonlyType()); } 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 b0c21310d379..1672681216d2 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 @@ -92,7 +92,7 @@ public String toString() { // TODO: this type don't have mutable parts so this should be a immutable semtype @Override public SemType createSemType() { - SemType semType = Builder.anyDataType(); + SemType semType = Builder.getAnyDataType(); if (isReadOnly()) { semType = Core.intersect(semType, Builder.readonlyType()); } 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 888343f142a7..964f3f2f35a8 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 @@ -37,10 +37,10 @@ public final class BBooleanType extends BSemTypeWrapper new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), - TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.booleanConst(true)); + 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.booleanConst(false)); + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.getBooleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. 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 59f51aa10b39..90c92349106d 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 @@ -46,7 +46,8 @@ public final class BByteType extends BSemTypeWrapper im * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { - this(() -> new BByteTypeImpl(typeName, pkg), typeName, EMPTY_MODULE, Builder.intRange(0, UNSIGNED8_MAX_VALUE)); + this(() -> new BByteTypeImpl(typeName, pkg), typeName, EMPTY_MODULE, + Builder.createIntRange(0, UNSIGNED8_MAX_VALUE)); } private BByteType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { @@ -55,7 +56,7 @@ private BByteType(Supplier bTypeSupplier, String typeName, Module public static BByteType singletonType(long value) { return new BByteType(() -> (BByteTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.BYTE_TNAME, EMPTY_MODULE, - Builder.intConst(value)); + Builder.getIntConst(value)); } protected static final class BByteTypeImpl extends BType implements ByteType, Cloneable { 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 9341bbe63c39..1e9ef44a4cef 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 @@ -54,7 +54,7 @@ public BDecimalType(String typeName, Module pkg) { public static BDecimalType singletonType(BigDecimal value) { return new BDecimalType(() -> (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.DECIMAL_TNAME, - EMPTY_MODULE, Builder.decimalConst(value)); + EMPTY_MODULE, Builder.getDecimalConst(value)); } private BDecimalType(Supplier bType, String typeName, Module pkg, SemType semType) { 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 d773328a7879..6c1b9dcf4b84 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 @@ -128,7 +128,7 @@ public void setIntersectionType(IntersectionType intersectionType) { public synchronized SemType createSemType() { SemType err; if (detailType == null || isTopType()) { - err = Builder.errorType(); + err = Builder.getErrorType(); } else { err = ErrorUtils.errorDetail(tryInto(getDetailType())); } 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 8ab193e8d89a..73a56b33aba4 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 @@ -53,7 +53,7 @@ private BFloatType(Supplier bType, String typeName, Module pkg, public static BFloatType singletonType(Double value) { return new BFloatType(() -> new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), - TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.floatConst(value)); + TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.getFloatConst(value)); } protected static final class BFloatTypeImpl extends BType implements FloatType { 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 675cd813233d..ae7ba8f26702 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 @@ -218,7 +218,7 @@ public long getFlags() { private static SemType createIsolatedTop(Env env) { FunctionDefinition fd = new FunctionDefinition(); - SemType ret = Builder.valType(); + SemType ret = Builder.getValType(); return fd.define(env, Builder.neverType(), ret, FunctionQualifiers.create(true, false)); } @@ -260,7 +260,7 @@ private SemType getTopType() { if (SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED)) { return ISOLATED_TOP; } - return Builder.functionType(); + return Builder.getFunctionType(); } private record SemTypeResult(boolean hasBTypePart, SemType pureSemTypePart) { 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 5966b122096d..370c2c119160 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 @@ -101,7 +101,7 @@ private String getConstraintString() { @Override public SemType createSemType() { if (constraint == null) { - return Builder.futureType(); + return Builder.getFutureType(); } return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); } 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 8ed70dbed88d..9d4ad5afdab9 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 @@ -42,7 +42,7 @@ public final class BHandleType extends BSemTypeWrapper (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, pkg, TypeTags.HANDLE_TAG, - Builder.handleType()); + Builder.getHandleType()); } protected static final class BHandleTypeImpl extends BType implements HandleType { 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 d7ae1f17c97b..c98909ad3fd4 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 @@ -71,12 +71,12 @@ private BIntegerType(Supplier bIntegerTypeSupplier, String typ private static SemType pickSemType(int tag) { return switch (tag) { case TypeTags.INT_TAG -> Builder.intType(); - case TypeTags.SIGNED8_INT_TAG -> Builder.intRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); - case TypeTags.SIGNED16_INT_TAG -> Builder.intRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); - case TypeTags.SIGNED32_INT_TAG -> Builder.intRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); - case TypeTags.UNSIGNED8_INT_TAG, TypeTags.BYTE_TAG -> Builder.intRange(0, UNSIGNED8_MAX_VALUE); - case TypeTags.UNSIGNED16_INT_TAG -> Builder.intRange(0, UNSIGNED16_MAX_VALUE); - case TypeTags.UNSIGNED32_INT_TAG -> Builder.intRange(0, UNSIGNED32_MAX_VALUE); + 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"); }; } @@ -90,7 +90,7 @@ public static BIntegerType singletonType(long value) { private static BIntegerType createSingletonType(long value) { return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, EMPTY_MODULE, - TypeTags.INT_TAG, Builder.intConst(value)); + TypeTags.INT_TAG, Builder.getIntConst(value)); } protected static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { 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 cf130fff5b6c..d38af85b1753 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 @@ -238,12 +238,12 @@ private SemType createSemTypeInner(Function semTypeFunction) { } SemType result = constituentTypes.stream().map(semTypeFunction).reduce(Core::intersect).orElseThrow(); // TODO:refactor this - if (Core.isSubtypeSimple(result, Builder.errorType())) { + if (Core.isSubtypeSimple(result, Builder.getErrorType())) { BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, effectiveErrorType.getTypeIdSet()); result = distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(result, Core::intersect); - } else if (Core.isSubtypeSimple(result, Builder.objectType())) { + } else if (Core.isSubtypeSimple(result, Builder.getObjectType())) { BObjectType effectiveObjectType = (BObjectType) getImpliedType(effectiveType); DistinctIdSupplier distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, effectiveObjectType.getTypeIdSet()); 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 622dbc5a0244..913a56d4fd0d 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 @@ -487,7 +487,7 @@ static MethodData fromResourceMethod(BResourceMethodType method) { List paramTypes = new ArrayList<>(); for (Type part : pathSegmentTypes) { if (part == null) { - paramTypes.add(Builder.anyType()); + paramTypes.add(Builder.getAnyType()); } else { paramTypes.add(tryInto(part)); } 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 index 5c66134d0098..9fd0ecb96ba8 100644 --- 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 @@ -135,13 +135,13 @@ public boolean isNative() { @Override public boolean isAnydata() { Context cx = TypeChecker.context(); - return Core.isSubType(cx, this, Builder.anyDataType()); + return Core.isSubType(cx, this, Builder.getAnyDataType()); } @Override public boolean isPureType() { Context cx = TypeChecker.context(); - return Core.isSubType(cx, this, Builder.errorType()) || isAnydata(); + return Core.isSubType(cx, this, Builder.getErrorType()) || isAnydata(); } @Override 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 f2761e7ae50a..4ca45afc78f3 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 @@ -145,7 +145,7 @@ public boolean equals(Object obj) { @Override public synchronized SemType createSemType() { if (constraint == null) { - return Builder.streamType(); + return Builder.getStreamType(); } Env env = TypeChecker.context().env; if (definition != null) { 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 dd29f9c16341..a8cd996457e1 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 @@ -63,7 +63,7 @@ private BStringType(Supplier bTypeSupplier, String typeName, Mo public static BStringType singletonType(String value) { return new BStringType(() -> (BStringTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.STRING_TNAME, - DEFAULT_MODULE, TypeTags.STRING_TAG, Builder.stringConst(value)); + DEFAULT_MODULE, TypeTags.STRING_TAG, Builder.getStringConst(value)); } private static SemType pickSemtype(int tag) { 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 2f8d6374b68d..b3753b8de96c 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 @@ -95,7 +95,7 @@ public String toString() { @Override public SemType createSemType() { if (constraint == null) { - return Builder.typeDescType(); + return Builder.getTypeDescType(); } SemType constraint = tryInto(getConstraint()); Context cx = TypeChecker.context(); 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 7445fd376fd6..c43aadcd5ff3 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 @@ -174,11 +174,11 @@ public SemType createSemType() { private SemType pickTopType() { return switch (tag) { - case TypeTags.XML_TAG -> Builder.xmlType(); - case TypeTags.XML_ELEMENT_TAG -> Builder.xmlElementType(); - case TypeTags.XML_COMMENT_TAG -> Builder.xmlCommentType(); - case TypeTags.XML_PI_TAG -> Builder.xmlPIType(); - case TypeTags.XML_TEXT_TAG -> Builder.xmlTextType(); + 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); }; } @@ -230,13 +230,13 @@ private Optional readonlyShapeOf(Object object) { .map(XmlUtils::xmlSequence); } else if (object instanceof XmlText) { // Text is inherently readonly - return Optional.of(Builder.xmlTextType()); + return Optional.of(Builder.getXmlTextType()); } else if (object instanceof XmlItem xml) { - return getSemType(xml, Builder.xmlElementType()); + return getSemType(xml, Builder.getXmlElementType()); } else if (object instanceof XmlComment xml) { - return getSemType(xml, Builder.xmlCommentType()); + return getSemType(xml, Builder.getXmlCommentType()); } else if (object instanceof XmlPi xml) { - return getSemType(xml, Builder.xmlPIType()); + return getSemType(xml, Builder.getXmlPIType()); } throw new IllegalArgumentException("Unexpected xml value: " + object); } 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 index 4de1709ceb08..ac9850ea3da8 100644 --- 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 @@ -91,7 +91,7 @@ public SubTypeData data() { private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { CellAtomicType combined; if (posList == null) { - combined = CellAtomicType.from(Builder.valType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + combined = CellAtomicType.from(Builder.getValType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); } else { combined = CellAtomicType.cellAtomType(posList.atom()); Conjunction p = posList.next(); @@ -119,7 +119,7 @@ private static boolean cellMutUnlimitedInhabited(Context cx, SemType pos, Conjun Conjunction neg = negList; while (neg != null) { if (CellAtomicType.cellAtomType(neg.atom()).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && - Core.isSameType(cx, Builder.valType(), CellAtomicType.cellAtomType(neg.atom()).ty())) { + Core.isSameType(cx, Builder.getValType(), CellAtomicType.cellAtomType(neg.atom()).ty())) { return false; } neg = neg.next(); 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 index 4b2ec6bf2ed2..79beaba3d356 100644 --- 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 @@ -61,7 +61,7 @@ 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.valType(), Core::intersect); + 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) { 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 index 4cf4342f9542..a8689a0753b4 100644 --- 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 @@ -66,7 +66,7 @@ public SubType intersect(SubType other) { @Override public SubType complement() { - return createDelegate(Builder.bddSubtypeRo().diff(inner)); + return createDelegate(Builder.getBddSubtypeRo().diff(inner)); } @Override @@ -75,7 +75,7 @@ public boolean isEmpty(Context cx) { // 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.bddSubtypeRo()) : b; + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getBddSubtypeRo()) : b; return cx.memoSubtypeIsEmpty(cx.mappingMemo, BErrorSubType::errorBddIsEmpty, b); } 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 index 94eb87eb4101..eea854064707 100644 --- 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 @@ -119,7 +119,7 @@ && functionPhiInner(cx, t0, Core.intersect(t1, s1), pos.next()) private static SemType functionIntersectRet(Context cx, Conjunction pos) { if (pos == null) { - return Builder.valType(); + return Builder.getValType(); } return Core.intersect(cx.functionAtomicType(pos.atom()).retType(), functionIntersectRet(cx, pos.next())); } 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 index c5a6bf910c12..aa0cbda129dc 100644 --- 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 @@ -36,8 +36,7 @@ import java.util.List; import java.util.Objects; -import static io.ballerina.runtime.api.types.semtype.Builder.cellContaining; -import static io.ballerina.runtime.api.types.semtype.Builder.roCellContaining; +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; @@ -65,7 +64,7 @@ private BListProj() { public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { if (t.some() == 0) { - return t == Builder.listType() ? Builder.valType() : Builder.neverType(); + return t == Builder.listType() ? Builder.getValType() : Builder.neverType(); } else { SubTypeData keyData = Core.intSubtype(k); if (isNothingSubtype(keyData)) { @@ -92,7 +91,7 @@ private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjuncti SemType rest; if (pos == null) { members = FixedLengthArray.empty(); - rest = cellContaining(cx.env, union(Builder.valType(), Builder.undef())); + rest = Builder.getRwCellContaining(cx.env, union(Builder.getValType(), Builder.undef())); } else { // combine all the positive tuples using intersection ListAtomicType lt = cx.listAtomType(pos.atom()); @@ -125,7 +124,7 @@ private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjuncti } // Ensure that we can use isNever on rest in listInhabited if (!isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { - rest = roCellContaining(cx.env, Builder.neverType()); + rest = getRoCellContaining(cx.env, Builder.neverType()); } } Integer[] indices = listSamples(cx, members, rest, neg); @@ -172,7 +171,7 @@ private static SemType listProjExcludeInnerVal(Context cx, Integer[] indices, In diff(cellInnerVal(memberTypes[i]), listMemberAtInnerVal(nt.members(), nt.rest(), indices[i])); if (!Core.isEmpty(cx, d)) { SemType[] t = memberTypes.clone(); - t[i] = cellContaining(cx.env, d); + 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())); 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 index c2757f1545d8..0bbda67e9141 100644 --- 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 @@ -38,10 +38,10 @@ import java.util.Objects; import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; -import static io.ballerina.runtime.api.types.semtype.Core.cellContainingInnerVal; 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.intersectMemberSemTypes; +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; // TODO: this has lot of common code with cell (and future mapping), consider refactoring (problem is createDelegate) @@ -104,7 +104,7 @@ static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) FixedLengthArray members; SemType rest; if (pos == null) { - ListAtomicType atom = Builder.listAtomicInner(); + ListAtomicType atom = Builder.getListAtomicInner(); members = atom.members(); rest = atom.rest(); } else { @@ -239,7 +239,7 @@ public static Pair listSampleTypes(Context cx, FixedLengthAr int nRequired = 0; for (int i = 0; i < indices.length; i++) { int index = indices[i]; - SemType t = cellContainingInnerVal(cx.env, listMemberAt(members, rest, index)); + SemType t = getCellContainingInnerVal(cx.env, listMemberAt(members, rest, index)); if (Core.isEmpty(cx, t)) { break; } @@ -343,11 +343,12 @@ public static Pair listIntersectWith(Env env, FixedLe SemType[] initial = new SemType[max]; for (int i = 0; i < max; i++) { initial[i] = - intersectMemberSemTypes(env, listMemberAt(members1, rest1, i), listMemberAt(members2, rest2, i)); + intersectCellMemberSemTypes(env, listMemberAt(members1, rest1, i), + listMemberAt(members2, rest2, i)); } return Pair.from(FixedLengthArray.from(initial, Integer.max(members1.fixedLength(), members2.fixedLength())), - intersectMemberSemTypes(env, rest1, rest2)); + intersectCellMemberSemTypes(env, rest1, rest2)); } private static boolean listLengthsDisjoint(FixedLengthArray members1, SemType rest1, 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 index 436db85fd007..2d97f3856831 100644 --- 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 @@ -50,7 +50,7 @@ public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k // This is what Castagna calls projection. public static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { if (t.some() == 0) { - return (t.all() & Builder.mappingType().all()) != 0 ? Builder.valType() : Builder.undef(); + return (t.all() & Builder.getMappingType().all()) != 0 ? Builder.getValType() : Builder.undef(); } else { SubTypeData keyData = stringSubtype(k); if (isNothingSubtype(keyData)) { 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 index 5749f83f1e06..b4be2442d044 100644 --- 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 @@ -89,7 +89,7 @@ public boolean isEmpty(Context cx) { static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { MappingAtomicType combined; if (posList == null) { - combined = Builder.mappingAtomicInner(); + combined = Builder.getMappingAtomicInner(); } else { // combine all the positive atoms using intersection combined = cx.mappingAtomType(posList.atom()); 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 index bc2ff8f6c018..54094b99c6ea 100644 --- 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 @@ -64,7 +64,7 @@ public SubType intersect(SubType other) { @Override public SubType complement() { - return createDelegate(Builder.listSubtypeTwoElement().diff(inner)); + return createDelegate(Builder.getListSubtypeTwoElement().diff(inner)); } @Override @@ -73,7 +73,7 @@ public boolean isEmpty(Context cx) { // 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.listSubtypeTwoElement()) : b; + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getListSubtypeTwoElement()) : b; return cx.memoSubtypeIsEmpty(cx.listMemo, (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); } 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 index 3f3a3fd3ee26..4a482f969734 100644 --- 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 @@ -66,7 +66,7 @@ public SubType intersect(SubType other) { @Override public SubType complement() { - return createDelegate(Builder.listSubtypeThreeElement().diff(inner)); + return createDelegate(Builder.getListSubtypeThreeElement().diff(inner)); } @Override @@ -75,7 +75,7 @@ public boolean isEmpty(Context cx) { // 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.listSubtypeThreeElement()) : b; + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getListSubtypeThreeElement()) : b; return cx.memoSubtypeIsEmpty(cx.listMemo, (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); } 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 index 66162fc911f9..714f18d3b29d 100644 --- 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 @@ -66,7 +66,7 @@ public SubType intersect(SubType other) { @Override public SubType complement() { - return createDelegate(Builder.bddSubtypeRo().diff(inner)); + return createDelegate(Builder.getBddSubtypeRo().diff(inner)); } @Override @@ -75,7 +75,7 @@ public boolean isEmpty(Context cx) { // 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.bddSubtypeRo()) : b; + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getBddSubtypeRo()) : b; return cx.memoSubtypeIsEmpty(cx.mappingMemo, BTypedescSubType::typedescBddIsEmpty, b); } 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 index f54380d95459..1fdc561ff569 100644 --- 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 @@ -39,15 +39,15 @@ private ErrorUtils() { public static SemType errorDetail(SemType detail) { SubTypeData data = Core.subTypeData(detail, BT_MAPPING); if (data == AllOrNothing.ALL) { - return Builder.errorType(); + return Builder.getErrorType(); } else if (data == AllOrNothing.NOTHING) { return Builder.neverType(); } assert data instanceof Bdd; - SubType sd = ((Bdd) data).intersect(Builder.bddSubtypeRo()); - if (sd.equals(Builder.bddSubtypeRo())) { - return Builder.errorType(); + SubType sd = ((Bdd) data).intersect(Builder.getBddSubtypeRo()); + if (sd.equals(Builder.getBddSubtypeRo())) { + return Builder.getErrorType(); } return basicSubType(BT_ERROR, BErrorSubType.createDelegate(sd)); } 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 index 539575549ddd..33e613bfa7d7 100644 --- 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 @@ -45,8 +45,8 @@ synchronized SemType toSemType(Env env) { if (semType == null) { ListDefinition ld = new ListDefinition(); SemType[] members = { - isolated ? Builder.booleanConst(true) : Builder.booleanType(), - transactional ? Builder.booleanType() : Builder.booleanConst(false) + isolated ? Builder.getBooleanConst(true) : Builder.booleanType(), + transactional ? Builder.booleanType() : Builder.getBooleanConst(false) }; semType = ld.defineListTypeWrapped(env, members, 2, Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); 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 index 2b026265d5df..14c57d8e4bec 100644 --- 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 @@ -36,8 +36,8 @@ private FutureUtils() { } public static SemType futureContaining(Env env, SemType constraint) { - if (constraint == Builder.valType()) { - return Builder.futureType(); + if (constraint == Builder.getValType()) { + return Builder.getFutureType(); } MappingDefinition md = new MappingDefinition(); SemType mappingType = md.defineMappingTypeWrapped(env, EMPTY_FIELDS, constraint, 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 index 852c31cc3605..d02ee2ac2b6d 100644 --- 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 @@ -21,6 +21,7 @@ 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; @@ -28,11 +29,10 @@ 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.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.undef; -import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; 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; public class ListDefinition implements Definition { @@ -54,9 +54,9 @@ public SemType defineListTypeWrapped(Env env, SemType[] initial, int fixedLength CellAtomicType.CellMutability mut) { SemType[] initialCells = new SemType[initial.length]; for (int i = 0; i < initial.length; i++) { - initialCells[i] = cellContaining(env, initial[i], mut); + initialCells[i] = Builder.getCellContaining(env, initial[i], mut); } - SemType restCell = cellContaining(env, union(rest, undef()), isNever(rest) ? CELL_MUT_NONE : mut); + SemType restCell = Builder.getCellContaining(env, union(rest, undef()), isNever(rest) ? CELL_MUT_NONE : mut); return define(env, initialCells, fixedLength, restCell); } 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 index d700df9be6e3..d7e6032a2528 100644 --- 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 @@ -29,7 +29,7 @@ import java.util.Collection; import static io.ballerina.runtime.api.types.semtype.Core.cellInner; -import static io.ballerina.runtime.api.types.semtype.Core.intersectMemberSemTypes; +import static io.ballerina.runtime.api.types.semtype.Core.intersectCellMemberSemTypes; import static io.ballerina.runtime.api.types.semtype.Core.isNever; public record MappingAtomicType(String[] names, SemType[] types, SemType rest) implements AtomicType { @@ -40,14 +40,14 @@ public MappingAtomicType intersectMapping(Env env, MappingAtomicType other) { Collection types = new ArrayList<>(expectedSize); for (FieldPair fieldPair : new FieldPairs(this, other)) { names.add(fieldPair.name()); - SemType t = intersectMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + SemType t = intersectCellMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); if (isNever(cellInner(fieldPair.type1()))) { return null; } types.add(t); } - SemType rest = intersectMemberSemTypes(env, this.rest(), other.rest()); + 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 index 3bcf2c3976d3..45d9562b36d2 100644 --- 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 @@ -22,6 +22,7 @@ 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; @@ -32,7 +33,6 @@ 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.cellContaining; import static io.ballerina.runtime.api.types.semtype.Builder.undef; import static io.ballerina.runtime.api.types.semtype.Core.isNever; import static io.ballerina.runtime.api.types.semtype.Core.union; @@ -68,7 +68,7 @@ public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, C BCellField cellField = BCellField.from(env, field, mut); cellFields[i] = cellField; } - SemType restCell = cellContaining(env, union(rest, undef()), + SemType restCell = Builder.getCellContaining(env, union(rest, undef()), isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); return define(env, cellFields, restCell); } @@ -106,7 +106,7 @@ record BCellField(String name, SemType type) { static BCellField from(Env env, Field field, CellAtomicType.CellMutability mut) { SemType type = field.ty; - SemType cellType = cellContaining(env, field.optional ? union(type, undef()) : type, + SemType cellType = Builder.getCellContaining(env, field.optional ? union(type, undef()) : 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 index 8d96cfcc543c..5b10766a20ea 100644 --- 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 @@ -21,7 +21,7 @@ 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.stringConst; +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; public record Member(String name, SemType valueTy, Kind kind, Visibility visibility, boolean immutable) { @@ -30,9 +30,9 @@ public enum Kind { Method; private static final MappingDefinition.Field FIELD = - new MappingDefinition.Field("kind", stringConst("field"), true, false); + new MappingDefinition.Field("kind", getStringConst("field"), true, false); private static final MappingDefinition.Field METHOD = - new MappingDefinition.Field("kind", stringConst("method"), true, false); + new MappingDefinition.Field("kind", getStringConst("method"), true, false); public MappingDefinition.Field field() { return switch (this) { @@ -46,10 +46,10 @@ public enum Visibility { Public, Private; - private static final SemType PUBLIC_TAG = stringConst("public"); + 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 = stringConst("private"); + 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 = 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 index d3494eccca04..9591f9323ecf 100644 --- 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 @@ -32,7 +32,6 @@ 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.Builder.cellContaining; 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; @@ -46,16 +45,14 @@ 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())); + 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 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())); return objectContaining(mappingType); } @@ -66,40 +63,26 @@ private SemType objectContaining(SemType mappingType) { 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.readonlyType() : Builder.valType(), - readonly, false), - Member.Kind.Field.field(), - Member.Visibility.ALL - }, - Builder.neverType(), + SemType fieldMemberType = fieldDefn.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", readonly ? Builder.readonlyType() : Builder.getValType(), + readonly, + false), + Member.Kind.Field.field(), Member.Visibility.ALL}, Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); MappingDefinition methodDefn = new MappingDefinition(); - SemType methodMemberType = methodDefn.defineMappingTypeWrapped( - env, - new MappingDefinition.Field[]{ - new MappingDefinition.Field("value", Builder.functionType(), true, false), - Member.Kind.Method.field(), - Member.Visibility.ALL - }, - Builder.neverType(), + SemType methodMemberType = methodDefn.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", Builder.getFunctionType(), true, false), + Member.Kind.Method.field(), Member.Visibility.ALL}, Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); - return cellContaining(env, union(fieldMemberType, methodMemberType), mut); + 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[]{ + SemType semtype = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ new MappingDefinition.Field("value", member.valueTy(), member.immutable(), false), - member.kind().field(), - member.visibility().field() - }, - Builder.neverType(), + member.kind().field(), member.visibility().field()}, Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); return new MappingDefinition.Field(member.name(), semtype, immutableObject | member.immutable(), false); } 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 index de05fcc5a1f7..31e8e1d3fe80 100644 --- 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 @@ -22,8 +22,8 @@ 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.booleanConst; -import static io.ballerina.runtime.api.types.semtype.Builder.stringConst; +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; public record ObjectQualifiers(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { @@ -31,7 +31,7 @@ public record ObjectQualifiers(boolean isolated, boolean readonly, NetworkQualif public MappingDefinition.Field field(Env env) { MappingDefinition md = new MappingDefinition(); MappingDefinition.Field isolatedField = - new MappingDefinition.Field("isolated", isolated ? booleanConst(true) : Builder.booleanType(), + new MappingDefinition.Field("isolated", isolated ? getBooleanConst(true) : Builder.booleanType(), true, false); MappingDefinition.Field networkField = networkQualifier.field(); SemType ty = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{isolatedField, networkField}, @@ -44,11 +44,11 @@ public enum NetworkQualifier { Service, None; - private static final SemType CLIENT_TAG = stringConst("client"); + 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 = stringConst("service"); + private static final SemType SERVICE_TAG = getStringConst("service"); private static final MappingDefinition.Field SERVICE = new MappingDefinition.Field("network", SERVICE_TAG, true, false); 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 index 9728e8429d81..3809940eb105 100644 --- 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 @@ -29,7 +29,7 @@ private RegexUtils() { } public static SemType regexShape(String value) { - SemType stringSubtype = Builder.stringConst(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/StreamDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java index ca8501f44b0c..80928835f5cf 100644 --- 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 @@ -39,8 +39,8 @@ public SemType getSemType(Env env) { } public SemType define(Env env, SemType valueType, SemType completionType) { - if (Builder.valType() == completionType && Builder.valType() == valueType) { - return Builder.streamType(); + if (Builder.getValType() == completionType && Builder.getValType() == valueType) { + return Builder.getStreamType(); } SemType tuple = listDefinition.defineListTypeWrapped(env, new SemType[]{valueType, completionType}, 2, Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); 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 index 93f3a8d699ef..bc265037c571 100644 --- 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 @@ -43,7 +43,7 @@ public static SemType tableContainingKeySpecifier(Context cx, SemType tableConst SemType[] fieldNameSingletons = new SemType[fieldNames.length]; SemType[] fieldTypes = new SemType[fieldNames.length]; for (int i = 0; i < fieldNames.length; i++) { - SemType key = Builder.stringConst(fieldNames[i]); + SemType key = Builder.getStringConst(fieldNames[i]); fieldNameSingletons[i] = key; fieldTypes[i] = Core.mappingMemberTypeInnerVal(cx, tableConstraint, key); } @@ -63,12 +63,12 @@ public static SemType tableContainingKeyConstraint(Context cx, SemType tableCons SemType normalizedKc = lat.map(atom -> { FixedLengthArray member = atom.members(); return switch (member.fixedLength()) { - case 0 -> Builder.valType(); + case 0 -> Builder.getValType(); case 1 -> Core.cellAtomicType(member.initial()[0]).orElseThrow().ty(); default -> keyConstraint; }; }).orElse(keyConstraint); - return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.valType(), CELL_MUT_LIMITED); + return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.getValType(), CELL_MUT_LIMITED); } public static SemType tableContaining(Env env, SemType tableConstraint) { @@ -76,12 +76,12 @@ public static SemType tableContaining(Env env, SemType tableConstraint) { } private static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { - return tableContaining(env, tableConstraint, Builder.valType(), Builder.valType(), 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.mappingType()); + tableConstraint = Core.intersect(tableConstraint, Builder.getMappingType()); ListDefinition typeParamArrDef = new ListDefinition(); SemType typeParamArray = typeParamArrDef.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, tableConstraint, mut); 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 index 66f7d5c7a7a9..83725da486d2 100644 --- 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 @@ -37,8 +37,8 @@ private TypedescUtils() { } public static SemType typedescContaining(Env env, SemType constraint) { - if (constraint == Builder.valType()) { - return Builder.typeDescType(); + if (constraint == Builder.getValType()) { + return Builder.getTypeDescType(); } MappingDefinition md = new MappingDefinition(); SemType mappingType = md.defineMappingTypeWrapped(env, EMPTY_FIELDS, constraint, 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 index 4b25b22e2d0e..f1cbf83790c2 100644 --- 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 @@ -73,7 +73,7 @@ private static SemType createXmlSingleton(int primitive) { private static SemType createXmlSemtype(SubTypeData xmlSubtype) { if (xmlSubtype instanceof AllOrNothing) { - return xmlSubtype == AllOrNothing.ALL ? Builder.xmlType() : Builder.neverType(); + return xmlSubtype == AllOrNothing.ALL ? Builder.getXmlType() : Builder.neverType(); } assert xmlSubtype instanceof BXmlSubType : "subtype must be wrapped by delegate by now"; return Builder.basicSubType(BasicTypeCode.BT_XML, (SubType) xmlSubtype); @@ -101,12 +101,12 @@ public static SubTypeData from(int primitives, Bdd sequence) { } public static SemType xmlSequence(SemType constituentType) { - assert Core.isSubtypeSimple(constituentType, Builder.xmlType()) : + 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.xmlType(), constituentType)); + assert Core.isNever(Core.diff(Builder.getXmlType(), constituentType)); return constituentType; } else { SubType xmlSubType = 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 4c6c10cd496f..ef38b445832c 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 @@ -329,7 +329,7 @@ public synchronized void resetReadonlyShapeDefinition() { } @Override - public Optional shapeOf(Context cx) { + 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 ffacb4890550..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 @@ -251,7 +251,7 @@ public final void cacheShape(SemType semType) { } @Override - public Optional shapeOf(Context cx) { + 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/DecimalValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java index a6dbe32a0e38..c3a7bf979cdd 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 @@ -490,12 +490,12 @@ public static DecimalValue valueOfJ(BigDecimal value) { } @Override - public Optional shapeOf(Context cx) { - return Optional.of(Builder.decimalConst(value)); + public Optional inherentTypeOf(Context cx) { + return Optional.of(Builder.getDecimalConst(value)); } @Override - public SemType widenedType(Context cx) { + public SemType widenedType() { return Builder.decimalType(); } } 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 fb3e117ddd15..a7d0b0c06428 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 @@ -134,12 +134,12 @@ public String toString() { } @Override - public SemType widenedType(Context cx) { - return Builder.functionType(); + public SemType widenedType() { + return Builder.getFunctionType(); } @Override - public Optional shapeOf(Context cx) { + 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 84c3f7405987..8663f7245e65 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 @@ -757,7 +757,7 @@ public SemType shapeOf() { } @Override - public Optional shapeOf(Context cx) { + 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/RegExpValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java index b8df500a3b8b..cedc9f59d589 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 @@ -127,8 +127,8 @@ public boolean equals(Object o, Set visitedValues) { } @Override - public SemType widenedType(Context cx) { - return Builder.regexType(); + public SemType widenedType() { + return Builder.getRegexType(); } @Override @@ -137,7 +137,7 @@ public Optional shapeOf() { } @Override - public Optional shapeOf(Context cx) { + 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 e8e508d8f659..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 @@ -44,7 +44,7 @@ protected StringValue(String value, boolean isNonBmp) { this.value = value; this.isNonBmp = isNonBmp; this.type = BStringType.singletonType(value); - this.shape = Builder.stringConst(value); + this.shape = Builder.getStringConst(value); } @Override @@ -109,7 +109,7 @@ public boolean equals(Object str) { } @Override - public Optional shapeOf(Context cx) { + 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 fc643242cd9b..e8c5aa55a874 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 @@ -913,7 +913,7 @@ public BArray getArrayValue(BString key) { } @Override - public Optional shapeOf(Context cx) { + 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/XmlValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java index 5ab0511a018c..55dd98168c33 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 @@ -279,7 +279,7 @@ public Type getIteratorNextReturnType() { } @Override - public Optional shapeOf(Context cx) { + public Optional inherentTypeOf(Context cx) { TypeWithShape typeWithShape = (TypeWithShape) type; return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); } 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 index 687aa77e3da5..a5cff74df24f 100644 --- 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 @@ -35,9 +35,9 @@ public void testCellTypes() { Env env = Env.getInstance(); Context cx = Context.from(env); SemType intTy = Builder.intType(); - SemType readonlyInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + SemType readonlyInt = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); assert Core.isSubType(cx, readonlyInt, readonlyInt); - SemType mutableInt = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + 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); @@ -47,8 +47,8 @@ public void testCellTypes() { public void testCellTypeCaching() { Env env = Env.getInstance(); SemType intTy = Builder.intType(); - SemType readonlyInt1 = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); - SemType readonlyInt2 = Builder.cellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + 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; } 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 index f0929b83f4ee..ae0357b51381 100644 --- 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 @@ -161,7 +161,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, Map cx, Map mod, BLangTypeDefinition defn, int depth, BLangStreamType td) { if (td.constraint == null) { - return Builder.streamType(); + return Builder.getStreamType(); } Env env = (Env) cx.getInnerEnv(); Definition attachedDefinition = attachedDefinitions.get(td); @@ -212,7 +212,7 @@ private static SemType getDistinctErrorType(Env env, SemType innerType) { private SemType createErrorType(TypeTestContext cx, Map mod, BLangTypeDefinition defn, int depth, BLangErrorType td) { if (td.detailType == null) { - return Builder.errorType(); + return Builder.getErrorType(); } else { SemType detailType = resolveTypeDesc(cx, mod, defn, depth + 1, td.detailType); return ErrorUtils.errorDetail(detailType); @@ -303,14 +303,14 @@ private SemType[] getParameters(TypeTestContext cx, Map params = new ArrayList<>(); if (functionType instanceof BLangResourceFunction resourceFunctionType) { - params.add(Builder.stringConst(resourceFunctionType.methodName.value)); + 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.typeDescType())) { + if (Core.isSubtypeSimple(semType, Builder.getTypeDescType())) { paramScope.put(paramVar.name.value, paramVar); } params.add(semType); @@ -328,12 +328,12 @@ private SemType resolveFunctionTypeDesc(TypeTestContext cx, Map cx, Map cx, } ListDefinition ld = new ListDefinition(); return ld.defineListTypeWrapped((Env) cx.getInnerEnv(), - new SemType[]{!isDependentlyType ? Builder.booleanType() : Builder.booleanConst(true), + new SemType[]{!isDependentlyType ? Builder.booleanType() : Builder.getBooleanConst(true), innerType}, 2, Builder.neverType(), CELL_MUT_LIMITED); } @@ -433,7 +433,7 @@ private SemType resolveRecordTypeDesc(TypeTestContext cx, Map resolveSingletonType(Object value, TypeKind targetTypeKind) { return switch (targetTypeKind) { case NIL -> Optional.of(Builder.nilType()); - case BOOLEAN -> Optional.of(Builder.booleanConst((Boolean) value)); + case BOOLEAN -> Optional.of(Builder.getBooleanConst((Boolean) value)); case INT, BYTE -> { assert !(value instanceof Byte); - yield Optional.of(Builder.intConst(((Number) value).longValue())); + yield Optional.of(Builder.getIntConst(((Number) value).longValue())); } case FLOAT -> { double doubleVal; @@ -576,17 +576,17 @@ private Optional resolveSingletonType(Object value, TypeKind targetType yield Optional.empty(); } } - yield Optional.of(Builder.floatConst(doubleVal)); + 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.decimalConst(new BigDecimal(repr))); + yield Optional.of(Builder.getDecimalConst(new BigDecimal(repr))); } - case STRING -> Optional.of(Builder.stringConst((String) value)); - case HANDLE -> Optional.of(Builder.handleType()); + case STRING -> Optional.of(Builder.getStringConst((String) value)); + case HANDLE -> Optional.of(Builder.getHandleType()); default -> Optional.empty(); }; } @@ -613,7 +613,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp } else if (td.pkgAlias.value.equals("xml")) { return resolveXmlSubType(name); } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { - return Builder.regexType(); + return Builder.getRegexType(); } BLangNode moduleLevelDef = mod.get(name); @@ -645,19 +645,19 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp private SemType resolveXmlSubType(String name) { return switch (name) { - case "Element" -> Builder.xmlElementType(); - case "Comment" -> Builder.xmlCommentType(); - case "Text" -> Builder.xmlTextType(); - case "ProcessingInstruction" -> Builder.xmlPIType(); + 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.objectType())) { + if (Core.isSubtypeSimple(innerType, Builder.getObjectType())) { return getDistinctObjectType(env, innerType); - } else if (Core.isSubtypeSimple(innerType, Builder.errorType())) { + } else if (Core.isSubtypeSimple(innerType, Builder.getErrorType())) { return getDistinctErrorType(env, innerType); } throw new IllegalArgumentException("Distinct type not supported for: " + innerType); @@ -666,12 +666,12 @@ private SemType getDistinctSemType(TypeTestContext cx, SemType innerTyp private SemType resolveIntSubtype(String name) { // TODO: support MAX_VALUE return switch (name) { - case "Signed8" -> Builder.intRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); - case "Signed16" -> Builder.intRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); - case "Signed32" -> Builder.intRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); - case "Unsigned8" -> Builder.intRange(0, UNSIGNED8_MAX_VALUE); - case "Unsigned16" -> Builder.intRange(0, UNSIGNED16_MAX_VALUE); - case "Unsigned32" -> Builder.intRange(0, UNSIGNED32_MAX_VALUE); + 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); }; } @@ -689,8 +689,8 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTy private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { return switch (td.typeKind) { case NEVER -> Builder.neverType(); - case XML -> Builder.xmlType(); - case FUTURE -> Builder.futureType(); + case XML -> Builder.getXmlType(); + case FUTURE -> Builder.getFutureType(); // TODO: implement json type default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); @@ -701,28 +701,28 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) return switch (td.typeKind) { case NIL -> Builder.nilType(); case BOOLEAN -> Builder.booleanType(); - case BYTE -> Builder.intRange(0, UNSIGNED8_MAX_VALUE); + case BYTE -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); case INT -> Builder.intType(); case FLOAT -> Builder.floatType(); case DECIMAL -> Builder.decimalType(); case STRING -> Builder.stringType(); case READONLY -> Builder.readonlyType(); - case ANY -> Builder.anyType(); - case ANYDATA -> Builder.anyDataType(); - case ERROR -> Builder.errorType(); - case XML -> Builder.xmlType(); - case HANDLE -> Builder.handleType(); - case TYPEDESC -> Builder.typeDescType(); + 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.intConst((long) constant.symbol.value.value); - case BOOLEAN -> Builder.booleanConst((boolean) constant.symbol.value.value); - case STRING -> Builder.stringConst((String) constant.symbol.value.value); - case FLOAT -> Builder.floatConst((double) constant.symbol.value.value); + 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"); }; } 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 index cc11cc8e0161..1b35b955e4de 100644 --- 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 @@ -57,12 +57,12 @@ public boolean isListType(SemType t) { @Override public boolean isMapType(SemType t) { - return Core.isSubtypeSimple(t, Builder.mappingType()); + return Core.isSubtypeSimple(t, Builder.getMappingType()); } @Override public SemType intConst(long l) { - return Builder.intConst(l); + return Builder.getIntConst(l); } @Override diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test-tv.bal deleted file mode 100644 index e69de29bb2d1..000000000000 From 816e9b46c0723ebb19a9336afaee192f510d8558 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 29 Sep 2024 17:45:23 +0530 Subject: [PATCH 169/178] Use holder classes to avoid initialization costs in TypeChecker --- .../runtime/api/types/semtype/Context.java | 4 +- .../runtime/internal/TypeChecker.java | 133 +++++++++++------- 2 files changed, 83 insertions(+), 54 deletions(-) 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 index 5fd6015eae75..fd6fee7e3882 100644 --- 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 @@ -30,8 +30,8 @@ import java.util.WeakHashMap; /** - * Context in which type checking operations are performed. Note context is not thread safe, requiring external - * synchronization if shared between threads. Multiple contexts may share same environment without issue. + * 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.10.0 */ 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 de8c143e7859..22c765549c92 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 @@ -112,13 +112,7 @@ public final class TypeChecker { private static final String REG_EXP_TYPENAME = "RegExp"; private static final ThreadLocal threadContext = ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); - private static final SemType SIMPLE_BASIC_TYPE = createSimpleBasicType(); - private static final SemType NUMERIC_TYPE = createNumericType(); - private static final SemType INHERENTLY_IMMUTABLE_TYPE = createInherentlyImmutableType(); - private static final SemType REF_TYPE_MASK = createRefValueMask(); - private static final SemType CONVERTIBLE_CAST_MASK = createConvertibleCastMask(); private static final byte MAX_TYPECAST_ERROR_COUNT = 20; - private static final SemType TOP_TYPES_WITH_ALWAYS_FILLING = createTopTypesWithFillerValues(); public static Object checkCast(Object sourceVal, Type targetType) { @@ -128,8 +122,8 @@ public static Object checkCast(Object sourceVal, Type targetType) { return sourceVal; } Type sourceType = getType(sourceVal); - if (Core.containsBasicType(SemType.tryInto(sourceType), CONVERTIBLE_CAST_MASK) && - Core.containsBasicType(SemType.tryInto(targetType), CONVERTIBLE_CAST_MASK)) { + 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()) { @@ -147,8 +141,7 @@ public static Object checkCast(Object sourceVal, Type targetType) { } public static Context context() { - // We are pinning each context to thread. This depends on the assumption physical thread is not going to - // get switched while type checking. Also for the same reason we don't need to synchronize this method. + // We are pinning each context to thread. We can't use the same context with multiple type checks concurrently return threadContext.get(); } @@ -318,7 +311,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole assert readonlyShape.isPresent(); SemType shape = readonlyShape.get(); SemType targetSemType = ShapeAnalyzer.acceptedTypeOf(cx, targetType).orElseThrow(); - if (Core.isSubType(cx, shape, NUMERIC_TYPE) && allowNumericConversion) { + if (Core.isSubType(cx, shape, NumericTypeHolder.NUMERIC_TYPE) && allowNumericConversion) { targetSemType = appendNumericConversionTypes(targetSemType); } return Core.isSubType(cx, shape, targetSemType); @@ -592,13 +585,8 @@ public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsV lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; } - private static SemType createNumericType() { - return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType()) - .reduce(Builder.neverType(), Core::union); - } - public static boolean isNumericType(Type type) { - return Core.isSubType(context(), SemType.tryInto(type), NUMERIC_TYPE); + return Core.isSubType(context(), SemType.tryInto(type), NumericTypeHolder.NUMERIC_TYPE); } static boolean isByteLiteral(long longValue) { @@ -644,18 +632,11 @@ private static SemType widenedType(Context cx, Object value) { throw new IllegalArgumentException("Unexpected object type"); } - private static SemType createInherentlyImmutableType() { - return Stream.of(createSimpleBasicType(), Builder.stringType(), Builder.getErrorType(), - Builder.getFunctionType(), - Builder.getTypeDescType(), Builder.getHandleType(), Builder.getXmlTextType(), Builder.getXmlNeverType(), - Builder.getRegexType()) - .reduce(Builder.neverType(), Core::union); - } - public static boolean isInherentlyImmutableType(Type sourceType) { // readonly part is there to match to old API return - Core.isSubType(context(), SemType.tryInto(sourceType), INHERENTLY_IMMUTABLE_TYPE) || + Core.isSubType(context(), SemType.tryInto(sourceType), + InherentlyImmutableTypeHolder.INHERENTLY_IMMUTABLE_TYPE) || sourceType instanceof ReadonlyType; } @@ -817,18 +798,6 @@ public static boolean isEqual(Object lhsValue, Object rhsValue, Set c return checkValueEqual(lhsValue, rhsValue, new HashSet<>(checkedValues)); } - private static SemType createRefValueMask() { - return Stream.of(Builder.getXmlType(), Builder.getMappingType(), Builder.listType(), Builder.getErrorType(), - Builder.getTableType(), Builder.getRegexType()) - .reduce(Builder.neverType(), Core::union); - } - - private static SemType createConvertibleCastMask() { - return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), - Builder.booleanType()) - .reduce(Builder.neverType(), Core::union); - } - private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { Context cx = context(); SemType lhsShape = ShapeAnalyzer.inherentTypeOf(cx, lhsValue).orElseThrow(); @@ -851,7 +820,7 @@ private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set Date: Tue, 1 Oct 2024 08:31:46 +0530 Subject: [PATCH 170/178] Fix invalid accepted type for union and tables --- .../api/types/semtype/ShapeAnalyzer.java | 5 ++-- .../runtime/internal/TypeChecker.java | 5 +--- .../runtime/internal/types/BTableType.java | 14 ++++++++-- .../runtime/internal/types/BUnionType.java | 10 ++++++- .../internal/types/TypeWithAcceptedType.java | 15 ++++++++++ .../runtime/internal/types/TypeWithShape.java | 4 +-- .../internal/types/semtype/TableUtils.java | 28 +++++++++++++++++-- 7 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java 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 index a7f26fd81c02..1eb31722231b 100644 --- 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 @@ -3,6 +3,7 @@ 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; @@ -14,8 +15,8 @@ private ShapeAnalyzer() { } public static Optional acceptedTypeOf(Context cx, Type typeDesc) { - if (typeDesc instanceof TypeWithShape typeWithShape) { - return typeWithShape.acceptedTypeOf(cx); + if (typeDesc instanceof TypeWithAcceptedType typeWithAcceptedType) { + return typeWithAcceptedType.acceptedTypeOf(cx); } return Optional.of(SemType.tryInto(typeDesc)); } 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 22c765549c92..132d6e5fec8c 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 @@ -68,7 +68,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; @@ -307,9 +306,7 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType) { */ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boolean allowNumericConversion) { Context cx = context(); - Optional readonlyShape = ShapeAnalyzer.shapeOf(cx, sourceValue); - assert readonlyShape.isPresent(); - SemType shape = readonlyShape.get(); + 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); 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 45226ee36d66..02948494ff14 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 @@ -26,6 +26,7 @@ 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; @@ -218,8 +219,17 @@ public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje @Override public Optional acceptedTypeOf(Context cx) { - // TODO: this is not correct but can we actually match tables? - return Optional.of(getSemType()); + 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) { 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 e47094cbb4c3..27ba26ae860a 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 @@ -27,8 +27,10 @@ import io.ballerina.runtime.api.types.Type; 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; @@ -48,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 = "|"; @@ -554,4 +556,10 @@ public void setIntersectionType(IntersectionType intersectionType) { public SemType createSemType() { return memberTypes.stream().map(SemType::tryInto).reduce(Builder.neverType(), Core::union); } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(memberTypes.stream().map(each -> ShapeAnalyzer.acceptedTypeOf(cx, each).orElseThrow()) + .reduce(Builder.neverType(), Core::union)); + } } 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..32e8d4243751 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java @@ -0,0 +1,15 @@ +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. + */ +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 index 650609221196..15b3623176b4 100644 --- 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 @@ -30,13 +30,11 @@ * 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}. */ -public interface TypeWithShape { +public interface TypeWithShape extends TypeWithAcceptedType { Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); - Optional acceptedTypeOf(Context cx); - boolean couldInherentTypeBeDifferent(); } 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 index bc265037c571..9495616f9ec2 100644 --- 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 @@ -31,6 +31,7 @@ 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; public final class TableUtils { @@ -39,7 +40,16 @@ public final class TableUtils { 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++) { @@ -55,10 +65,20 @@ public static SemType tableContainingKeySpecifier(Context cx, SemType tableConst SemType normalizedKc = fieldNames.length > 1 ? new ListDefinition().defineListTypeWrapped(cx.env, fieldTypes, fieldTypes.length, Builder.neverType(), CELL_MUT_NONE) : fieldTypes[0]; - return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs, CELL_MUT_LIMITED); + 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(); @@ -68,13 +88,17 @@ public static SemType tableContainingKeyConstraint(Context cx, SemType tableCons default -> keyConstraint; }; }).orElse(keyConstraint); - return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.getValType(), CELL_MUT_LIMITED); + 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); } From 8f70ddfad63be05790e82d56bcae7e0f679539b2 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Thu, 3 Oct 2024 07:52:14 +0530 Subject: [PATCH 171/178] Refactor type check caching We move the type check cache out of SemType to avoid unnecessary type resolutions --- .../runtime/api/types/semtype/Core.java | 8 +---- .../runtime/api/types/semtype/SemType.java | 31 ----------------- .../runtime/internal/TypeChecker.java | 27 +++++++++++++-- .../runtime/internal/types/BArrayType.java | 2 +- .../runtime/internal/types/BType.java | 33 ++++++++++++++++++- .../types/CacheableTypeDescriptor.java | 19 +++++++++++ 6 files changed, 77 insertions(+), 43 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java 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 index c1018437bb53..7788bb5dae87 100644 --- 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 @@ -295,13 +295,7 @@ public static boolean isNever(SemType t) { } public static boolean isSubType(Context cx, SemType t1, SemType t2) { - SemType.CachedResult cached = t1.cachedSubTypeRelation(t2); - if (cached != SemType.CachedResult.NOT_FOUND) { - return cached == SemType.CachedResult.TRUE; - } - boolean result = isEmpty(cx, diff(t1, t2)); - t1.cacheSubTypeRelation(t2, result); - return result; + return isEmpty(cx, diff(t1, t2)); } public static boolean isSubtypeSimple(SemType t1, SemType t2) { 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 index 8606126d1d2e..8eea044f57be 100644 --- 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 @@ -5,15 +5,11 @@ import io.ballerina.runtime.internal.types.semtype.MutableSemType; import io.ballerina.runtime.internal.types.semtype.SemTypeHelper; -import java.util.Map; -import java.util.WeakHashMap; - public sealed class SemType extends BasicTypeBitSet permits io.ballerina.runtime.internal.types.BType, ImmutableSemType { private int some; private SubType[] subTypeData; - private volatile Map cachedResults; protected SemType(int all, int some, SubType[] subTypeData) { super(all); @@ -42,33 +38,6 @@ public final SubType[] subTypeData() { return subTypeData; } - public final CachedResult cachedSubTypeRelation(SemType other) { - if (skipCache()) { - return CachedResult.NOT_FOUND; - } - if (cachedResults == null) { - synchronized (this) { - if (cachedResults == null) { - cachedResults = new WeakHashMap<>(); - } - } - return CachedResult.NOT_FOUND; - } - return cachedResults.getOrDefault(other, CachedResult.NOT_FOUND); - } - - private boolean skipCache() { - return this.some() == 0; - } - - public final void cacheSubTypeRelation(SemType other, boolean result) { - if (skipCache() || other.skipCache()) { - return; - } - // we always check of the result before caching so there will always be a map - cachedResults.put(other, result ? CachedResult.TRUE : CachedResult.FALSE); - } - public final SubType subTypeByCode(int code) { if ((some() & (1 << code)) == 0) { return null; 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 132d6e5fec8c..fcb302493058 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 @@ -53,6 +53,7 @@ import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.BTypeReferenceType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.CacheableTypeDescriptor; import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.HandleValue; @@ -68,6 +69,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; @@ -600,17 +602,36 @@ private static boolean isSubTypeWithInherentType(Context cx, Object sourceValue, } private static boolean isSubType(Type source, Type target) { + if (source instanceof CacheableTypeDescriptor sourceCacheableType && + target instanceof CacheableTypeDescriptor targetCacheableType) { + return isSubTypeWithCache(sourceCacheableType, targetCacheableType); + } // 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. - if (source.equals(target)) { - return true; - } + return isSubTypeInner(source, target); + } + + private static boolean isSubTypeInner(Type source, Type target) { Context cx = context(); SemType sourceSemType = SemType.tryInto(source); SemType targetSemType = SemType.tryInto(target); return Core.isSubType(cx, sourceSemType, targetSemType); } + private static boolean isSubTypeWithCache(CacheableTypeDescriptor source, CacheableTypeDescriptor target) { + if (!source.shouldCache() || !target.shouldCache()) { + return isSubTypeInner(source, target); + } + Optional cachedResult = source.cachedTypeCheckResult(target); + if (cachedResult.isPresent()) { + assert cachedResult.get() == isSubTypeInner(source, target); + return cachedResult.get(); + } + boolean result = isSubTypeInner(source, target); + source.cacheTypeCheckResult(target, result); + return result; + } + private static SemType widenedType(Context cx, Object value) { if (value instanceof BValue bValue) { return bValue.widenedType(); 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 1cf6b859d5fb..8e43ae9fcb14 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 @@ -148,7 +148,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; 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 01ac5e98905b..d5a6a08789ea 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 @@ -28,7 +28,10 @@ import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.MutableSemType; +import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.WeakHashMap; /** * {@code BType} represents a type in Ballerina. @@ -40,7 +43,8 @@ * * @since 0.995.0 */ -public abstract non-sealed class BType extends SemType implements Type, MutableSemType, Cloneable { +public abstract non-sealed class BType extends SemType + implements Type, MutableSemType, Cloneable, CacheableTypeDescriptor { protected String typeName; protected Module pkg; @@ -50,6 +54,7 @@ public abstract non-sealed class BType extends SemType implements Type, MutableS private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; private TypeCreator.TypeMemoKey lookupKey = null; + private volatile Map cachedResults; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -285,4 +290,30 @@ public BType clone() { public void setLookupKey(TypeCreator.TypeMemoKey lookupKey) { this.lookupKey = lookupKey; } + + @Override + public boolean shouldCache() { + return true; + } + + @Override + public final Optional cachedTypeCheckResult(CacheableTypeDescriptor other) { + if (other.equals(this)) { + return Optional.of(true); + } + if (cachedResults == null) { + synchronized (this) { + if (cachedResults == null) { + cachedResults = new WeakHashMap<>(); + } + } + return Optional.empty(); + } + return Optional.ofNullable(cachedResults.get(other)); + } + + @Override + public final void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result) { + cachedResults.put(other, result); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java new file mode 100644 index 000000000000..ef3bb346fde7 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java @@ -0,0 +1,19 @@ +package io.ballerina.runtime.internal.types; + +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 { + + boolean shouldCache(); + + Optional cachedTypeCheckResult(CacheableTypeDescriptor other); + + void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result); +} From dca84d02102869cb8553102821f7150303870aef Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 4 Oct 2024 09:09:30 +0530 Subject: [PATCH 172/178] Make cache sharable between type descriptors --- .../semtype}/CacheableTypeDescriptor.java | 4 +- .../runtime/api/types/semtype/Context.java | 18 ++++++ .../api/types/semtype/TypeCheckCache.java | 28 +++++++++ .../runtime/internal/TypeChecker.java | 16 ++--- .../internal/scheduling/ItemGroup.java | 4 +- .../runtime/internal/types/BTableType.java | 6 ++ .../runtime/internal/types/BType.java | 25 ++++---- .../typecast/TypeCastExprTest.java | 8 +-- .../expressions/typecast/type-casting.bal | 58 +++++++++---------- 9 files changed, 109 insertions(+), 58 deletions(-) rename bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/{internal/types => api/types/semtype}/CacheableTypeDescriptor.java (71%) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java similarity index 71% rename from bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java index ef3bb346fde7..fbcb7ca9709b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/CacheableTypeDescriptor.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java @@ -1,4 +1,4 @@ -package io.ballerina.runtime.internal.types; +package io.ballerina.runtime.api.types.semtype; import io.ballerina.runtime.api.types.Type; @@ -13,7 +13,7 @@ public interface CacheableTypeDescriptor extends Type { boolean shouldCache(); - Optional cachedTypeCheckResult(CacheableTypeDescriptor other); + Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other); void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result); } 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 index fd6fee7e3882..c9d688d97adf 100644 --- 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 @@ -24,6 +24,7 @@ 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; @@ -43,9 +44,22 @@ public final class Context { 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() { + 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) { @@ -128,4 +142,8 @@ public FunctionAtomicType functionAtomicType(Atom atom) { 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/TypeCheckCache.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java new file mode 100644 index 000000000000..05d54da822f6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java @@ -0,0 +1,28 @@ +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; + +public class TypeCheckCache { + + 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/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index fcb302493058..6b6cb438f7e8 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 @@ -29,6 +29,7 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; 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; @@ -53,7 +54,6 @@ import io.ballerina.runtime.internal.types.BType; import io.ballerina.runtime.internal.types.BTypeReferenceType; import io.ballerina.runtime.internal.types.BUnionType; -import io.ballerina.runtime.internal.types.CacheableTypeDescriptor; import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.HandleValue; @@ -608,26 +608,26 @@ private static boolean isSubType(Type source, Type target) { } // 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(source, target); + return isSubTypeInner(context(), source, target); } - private static boolean isSubTypeInner(Type source, Type target) { - Context cx = context(); + 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 boolean isSubTypeWithCache(CacheableTypeDescriptor source, CacheableTypeDescriptor target) { + Context cx = context(); if (!source.shouldCache() || !target.shouldCache()) { - return isSubTypeInner(source, target); + return isSubTypeInner(cx, source, target); } - Optional cachedResult = source.cachedTypeCheckResult(target); + Optional cachedResult = source.cachedTypeCheckResult(cx, target); if (cachedResult.isPresent()) { - assert cachedResult.get() == isSubTypeInner(source, target); + assert cachedResult.get() == isSubTypeInner(cx, source, target); return cachedResult.get(); } - boolean result = isSubTypeInner(source, target); + boolean result = isSubTypeInner(cx, source, target); source.cacheTypeCheckResult(target, result); return result; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/ItemGroup.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/ItemGroup.java index 4e67edd384fb..45f2517fe1d7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/ItemGroup.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/ItemGroup.java @@ -18,8 +18,8 @@ package io.ballerina.runtime.internal.scheduling; -import java.util.ArrayDeque; import java.util.Deque; +import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; @@ -39,7 +39,7 @@ public class ItemGroup { * Keep the list of items that should run on same thread. * Using a stack to get advantage of the locality. */ - Deque items = new ArrayDeque<>(); + Deque items = new ConcurrentLinkedDeque<>(); /** * Indicates this item is already in runnable list/executing or not. 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 02948494ff14..a2f36fd52b75 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 @@ -240,4 +240,10 @@ private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable } return createSemTypeWithConstraint(constraintType); } + + @Override + public boolean shouldCache() { + // TODO: remove this once we have fixed equals + return false; + } } 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 d5a6a08789ea..55d9b81b820e 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 @@ -23,15 +23,16 @@ import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; +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.Map; import java.util.Objects; import java.util.Optional; -import java.util.WeakHashMap; /** * {@code BType} represents a type in Ballerina. @@ -54,7 +55,7 @@ public abstract non-sealed class BType extends SemType private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; private TypeCreator.TypeMemoKey lookupKey = null; - private volatile Map cachedResults; + private volatile TypeCheckCache typeCheckCache; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -297,23 +298,21 @@ public boolean shouldCache() { } @Override - public final Optional cachedTypeCheckResult(CacheableTypeDescriptor other) { - if (other.equals(this)) { - return Optional.of(true); - } - if (cachedResults == null) { + public final Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other) { + if (typeCheckCache == null) { synchronized (this) { - if (cachedResults == null) { - cachedResults = new WeakHashMap<>(); + if (typeCheckCache == null) { + typeCheckCache = cx.getTypeCheckCache(this); } } - return Optional.empty(); } - return Optional.ofNullable(cachedResults.get(other)); + return typeCheckCache.cachedTypeCheckResult(other); } @Override public final void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result) { - cachedResults.put(other, result); + // This happening after checking the cache so it must be initialized by now + typeCheckCache.cacheTypeCheckResult(other, result); } + } 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/resources/test-src/expressions/typecast/type-casting.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal index 6d28ca85a3e5..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,31 +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'"; + string errMsg = "incompatible types: '(FooTC & readonly)' cannot be cast to 'BarTC'"; assertEquality("{ballerina}TypeCastError", err.message()); assertEquality(errMsg, checkpanic err.detail()["message"]); } From 019797bdc61306670fc09755726171c15e311036 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 4 Oct 2024 09:34:29 +0530 Subject: [PATCH 173/178] Avoid necessary caching in TypeCreator --- .../runtime/api/creators/TypeCreator.java | 98 +++++-------------- .../runtime/internal/types/BRecordType.java | 9 -- .../runtime/internal/types/BType.java | 6 -- 3 files changed, 23 insertions(+), 90 deletions(-) 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 33da18d6857b..7a7c51dc4ab8 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 @@ -43,18 +43,13 @@ 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.BUnionType; import io.ballerina.runtime.internal.types.BXmlType; import java.util.Arrays; -import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.WeakHashMap; -import java.util.function.Function; -import java.util.function.Supplier; /** * Class @{@link TypeCreator} provides APIs to create ballerina type instances. @@ -63,16 +58,6 @@ */ public final class TypeCreator { - private static final Map tupleTypeMemo = new WeakHashMap<>(100); - private static final Map mapTypeMemo = new WeakHashMap<>(100); - private static final Map recordTypeMemo = new WeakHashMap<>(100); - private static final Map objectTypeMemo = new WeakHashMap<>(100); - private static final Map streamTypeMemo = new WeakHashMap<>(100); - private static final Map unionTypeMemo = new WeakHashMap<>(100); - private static final Map errorTypeMemo = new WeakHashMap<>(100); - private static final Map xmlTypeMemo = new WeakHashMap<>(100); - private static final Map jsonTypeMemo = new WeakHashMap<>(100); - private static final Map mapTypeCache = new IdentityHashMap<>(); /** * Creates a new array type with given element type. * @@ -162,7 +147,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); } @@ -177,20 +162,18 @@ 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) { - TypeMemoKey key = new TypeMemoKey(name, pkg); - return tupleTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BTupleType(name, pkg, typeFlags, isCyclic, 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 mapTypeCache.computeIfAbsent(constraint, (ignore) -> new BMapType(constraint)); + return new BMapType(constraint); } /** @@ -213,9 +196,7 @@ public static MapType createMapType(Type constraint, boolean readonly) { * @return the new map type */ public static MapType createMapType(String typeName, Type constraint, Module module) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return mapTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BMapType(typeName, constraint, module))); + return new BMapType(typeName, constraint, module); } /** @@ -228,9 +209,7 @@ public static MapType createMapType(String typeName, Type constraint, Module mod * @return the new map type */ public static MapType createMapType(String typeName, Type constraint, Module module, boolean readonly) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return mapTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BMapType(typeName, constraint, module, readonly))); + return new BMapType(typeName, constraint, module, readonly); } /** @@ -245,9 +224,7 @@ public static MapType createMapType(String typeName, Type constraint, Module mod */ public static RecordType createRecordType(String typeName, Module module, long flags, boolean sealed, int typeFlags) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return recordTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BRecordType(typeName, typeName, module, flags, sealed, typeFlags))); + return new BRecordType(typeName, typeName, module, flags, sealed, typeFlags); } /** @@ -263,7 +240,8 @@ 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) { return new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags); } @@ -276,8 +254,7 @@ public static RecordType createRecordType(String typeName, Module module, long f * @return the new object type */ public static ObjectType createObjectType(String typeName, Module module, long flags) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return objectTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BObjectType(typeName, module, flags))); + return new BObjectType(typeName, module, flags); } /** @@ -302,9 +279,7 @@ public static StreamType createStreamType(Type constraint, Type completionType) */ public static StreamType createStreamType(String typeName, Type constraint, Type completionType, Module modulePath) { - TypeMemoKey key = new TypeMemoKey(typeName, modulePath); - return streamTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BStreamType(typeName, constraint, completionType, modulePath))); + return new BStreamType(typeName, constraint, completionType, modulePath); } /** @@ -330,9 +305,7 @@ public static StreamType createStreamType(Type constraint) { */ @Deprecated public static StreamType createStreamType(String typeName, Type completionType, Module modulePath) { - TypeMemoKey key = new TypeMemoKey(typeName, modulePath); - return streamTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BStreamType(typeName, completionType, modulePath))); + return new BStreamType(typeName, completionType, modulePath); } /** @@ -402,9 +375,7 @@ public static UnionType createUnionType(List memberTypes, int typeFlags, b */ public static UnionType createUnionType(List memberTypes, String name, Module pkg, int typeFlags, boolean isCyclic, long flags) { - TypeMemoKey key = new TypeMemoKey(name, pkg); - return unionTypeMemo.computeIfAbsent(key, - createMappingFn(() -> new BUnionType(memberTypes, name, pkg, typeFlags, isCyclic, flags))); + return new BUnionType(memberTypes, name, pkg, typeFlags, isCyclic, flags); } /** @@ -415,8 +386,7 @@ public static UnionType createUnionType(List memberTypes, String name, Mod * @return the new error type */ public static ErrorType createErrorType(String typeName, Module module) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return errorTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BErrorType(typeName, module))); + return new BErrorType(typeName, module); } /** @@ -428,8 +398,7 @@ public static ErrorType createErrorType(String typeName, Module module) { * @return the new error type */ public static ErrorType createErrorType(String typeName, Module module, Type detailType) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return errorTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BErrorType(typeName, module, detailType))); + return new BErrorType(typeName, module, detailType); } /** @@ -488,8 +457,7 @@ public static TableType createTableType(Type constraint, boolean readonly) { * @return new xml type */ public static XmlType createXMLType(String typeName, Type constraint, Module module) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return xmlTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BXmlType(typeName, constraint, module))); + return new BXmlType(typeName, constraint, module); } /** @@ -502,8 +470,7 @@ public static XmlType createXMLType(String typeName, Type constraint, Module mod * @return new xml type */ public static XmlType createXMLType(String typeName, Module module, int tag, boolean readonly) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return xmlTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BXmlType(typeName, module, tag, readonly))); + return new BXmlType(typeName, module, tag, readonly); } /** @@ -526,8 +493,7 @@ public static XmlType createXMLType(Type constraint, boolean readonly) { * @return new xml type */ public static JsonType createJSONType(String typeName, Module module, boolean readonly) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - return jsonTypeMemo.computeIfAbsent(key, createMappingFn(() -> new BJsonType(typeName, module, readonly))); + return new BJsonType(typeName, module, readonly); } /** @@ -554,22 +520,4 @@ public static FiniteType createFiniteType(String typeName, Set values, i private TypeCreator() { } - - public record TypeMemoKey(String typeName, Module module) { - - } - - public static void registerRecordType(String typeName, Module module, BRecordType recordType) { - TypeMemoKey key = new TypeMemoKey(typeName, module); - recordTypeMemo.put(key, recordType); - recordType.setLookupKey(key); - } - - private static Function createMappingFn(Supplier innerSuppler) { - return (key) -> { - E bType = innerSuppler.get(); - bType.setLookupKey(key); - return bType; - }; - } } 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 2f6d3a6af672..23ba31d58d14 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 @@ -21,7 +21,6 @@ import io.ballerina.identifier.Utils; import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; -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; @@ -95,7 +94,6 @@ public BRecordType(String typeName, String internalName, Module pkg, long flags, this.sealed = sealed; this.typeFlags = typeFlags; this.readonly = SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY); - registerWithTypeCreator(); } /** @@ -125,13 +123,6 @@ public BRecordType(String typeName, Module pkg, long flags, Map f this.fields = fields; } this.internalName = typeName; - registerWithTypeCreator(); - } - - private void registerWithTypeCreator() { - if (this.typeName != null && this.pkg != null) { - TypeCreator.registerRecordType(this.typeName, this.pkg, this); - } } private Map getReadOnlyFields(Map fields) { 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 55d9b81b820e..07b17986f8c7 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 @@ -20,7 +20,6 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; -import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.CacheableTypeDescriptor; @@ -54,7 +53,6 @@ public abstract non-sealed class BType extends SemType private Type cachedReferredType = null; private Type cachedImpliedType = null; private volatile SemType cachedSemType = null; - private TypeCreator.TypeMemoKey lookupKey = null; private volatile TypeCheckCache typeCheckCache; protected BType(String typeName, Module pkg, Class valueClass) { @@ -288,10 +286,6 @@ public BType clone() { } } - public void setLookupKey(TypeCreator.TypeMemoKey lookupKey) { - this.lookupKey = lookupKey; - } - @Override public boolean shouldCache() { return true; From 297e74eb707bdcc56468da2a88f5f490ca12f22f Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 6 Oct 2024 11:12:29 +0530 Subject: [PATCH 174/178] Avoid duplicate record type creation --- .../runtime/api/creators/TypeCreator.java | 39 ++++++++++++++++++- .../runtime/internal/types/BRecordType.java | 3 ++ .../runtime/internal/types/BType.java | 2 +- .../negative/TomlProviderNegativeTest.java | 24 ++++++------ 4 files changed, 52 insertions(+), 16 deletions(-) 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 7a7c51dc4ab8..365f252b2463 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,8 @@ */ public final class TypeCreator { + private static final Map registeredRecordTypes = new ConcurrentHashMap<>(); + /** * Creates a new array type with given element type. * @@ -224,6 +227,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 +247,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 +530,29 @@ 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; + } + TypeIdentifier typeIdentifier = new TypeIdentifier(name, pkg); + registeredRecordTypes.put(typeIdentifier, recordType); + } + + 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/internal/types/BRecordType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java index 23ba31d58d14..2640dc96dd1e 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 @@ -21,6 +21,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.TypeTags; +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; @@ -94,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); } /** @@ -123,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) { 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 07b17986f8c7..71c69888ca00 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 @@ -288,7 +288,7 @@ public BType clone() { @Override public boolean shouldCache() { - return true; + return this.pkg != null && this.typeName != null; } @Override 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 253b87ce7efb..d1113c78e6d7 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 " + From 42d63203d8feb5cc59a6af087e6d7f8ee83df885 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Tue, 8 Oct 2024 10:11:41 +0530 Subject: [PATCH 175/178] Fix narrow type getting cached invalidly --- .../runtime/api/creators/TypeCreator.java | 16 ++++++++++++++-- .../langlib/internal/SetNarrowType.java | 9 ++++++++- .../test/getresult/QueryExpressionsTests.java | 3 ++- 3 files changed, 24 insertions(+), 4 deletions(-) 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 365f252b2463..11c3134d0446 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 @@ -59,8 +59,7 @@ */ public final class TypeCreator { - private static final Map registeredRecordTypes = new ConcurrentHashMap<>(); - + private static final RecordTypeCache registeredRecordTypes = new RecordTypeCache(); /** * Creates a new array type with given element type. * @@ -548,6 +547,19 @@ public static void registerRecordType(BRecordType recordType) { 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 { 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/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"); } From 7fbf7f49207611d2ba4328e1b0d40e7bc9d0a550 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Wed, 9 Oct 2024 14:24:33 +0530 Subject: [PATCH 176/178] Fix tests with duplicate type definitions --- .../resources/testcases/evaluator/operations.clone.json | 8 ++++---- .../testcases/evaluator/operations.immutable.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) 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.", From c6cfef7b26b57b9488ff9a7de6947e6a7028d449 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Fri, 11 Oct 2024 14:10:03 +0530 Subject: [PATCH 177/178] Fix comments and naming --- .../runtime/api/types/FunctionType.java | 1 - .../runtime/api/types/semtype/Atom.java | 8 +- .../runtime/api/types/semtype/AtomicType.java | 2 +- .../api/types/semtype/BasicTypeCode.java | 2 +- .../runtime/api/types/semtype/Bdd.java | 8 +- .../api/types/semtype/BddAllOrNothing.java | 2 +- .../types/semtype/BddIsEmptyPredicate.java | 12 + .../runtime/api/types/semtype/BddNode.java | 5 + .../api/types/semtype/BddNodeImpl.java | 4 +- .../api/types/semtype/BddNodeSimple.java | 6 + .../api/types/semtype/BddPredicate.java | 2 +- .../runtime/api/types/semtype/Builder.java | 50 +- .../semtype/CacheableTypeDescriptor.java | 22 + .../types/semtype/ConcurrentLazySupplier.java | 6 + .../ConcurrentLazySupplierWithCallback.java | 6 + .../api/types/semtype/Conjunction.java | 5 +- .../runtime/api/types/semtype/Context.java | 22 +- .../runtime/api/types/semtype/Core.java | 74 +- .../runtime/api/types/semtype/Definition.java | 11 +- .../runtime/api/types/semtype/Env.java | 12 +- .../runtime/api/types/semtype/FieldPair.java | 11 + .../runtime/api/types/semtype/FieldPairs.java | 5 + .../runtime/api/types/semtype/ListProj.java | 2 +- .../api/types/semtype/MappingProj.java | 2 +- .../runtime/api/types/semtype/Pair.java | 2 +- .../api/types/semtype/PredefinedTypeEnv.java | 6 +- .../runtime/api/types/semtype/RecAtom.java | 2 +- .../runtime/api/types/semtype/SemType.java | 11 +- .../api/types/semtype/ShapeAnalyzer.java | 9 +- .../runtime/api/types/semtype/SubType.java | 2 +- .../runtime/api/types/semtype/TypeAtom.java | 9 + .../api/types/semtype/TypeCheckCache.java | 13 + .../ballerina/runtime/api/values/BArray.java | 8 +- .../runtime/api/values/BArray.java.rej | 10 - .../ballerina/runtime/api/values/BError.java | 9 +- .../io/ballerina/runtime/api/values/BMap.java | 8 +- .../ballerina/runtime/api/values/BValue.java | 3 + .../api/values/PatternMatchableValue.java | 28 - .../runtime/internal/FallbackTypeChecker.java | 2019 ----------------- .../ballerina/runtime/internal/MapUtils.java | 2 +- .../runtime/internal/TypeChecker.java | 73 +- .../runtime/internal/TypeConverter.java | 12 +- .../runtime/internal/ValueConverter.java | 2 +- .../runtime/internal/types/BAnyType.java | 2 +- .../runtime/internal/types/BAnydataType.java | 7 +- .../runtime/internal/types/BArrayType.java | 6 +- .../runtime/internal/types/BBooleanType.java | 2 +- .../runtime/internal/types/BDecimalType.java | 2 +- .../runtime/internal/types/BFiniteType.java | 30 +- .../runtime/internal/types/BFloatType.java | 2 +- .../runtime/internal/types/BFunctionType.java | 12 +- .../runtime/internal/types/BFutureType.java | 6 + .../runtime/internal/types/BIntegerType.java | 4 +- .../internal/types/BIntersectionType.java | 17 +- .../runtime/internal/types/BMapType.java | 2 +- .../internal/types/BNetworkObjectType.java | 4 +- .../runtime/internal/types/BNeverType.java | 2 +- .../runtime/internal/types/BNullType.java | 2 +- .../runtime/internal/types/BObjectType.java | 5 +- .../internal/types/BParameterizedType.java | 6 +- .../runtime/internal/types/BReadonlyType.java | 2 +- .../runtime/internal/types/BRecordType.java | 81 +- .../internal/types/BSemTypeWrapper.java | 13 +- .../runtime/internal/types/BStringType.java | 38 +- .../runtime/internal/types/BTableType.java | 4 +- .../runtime/internal/types/BTupleType.java | 20 +- .../internal/types/BTypeReferenceType.java | 2 +- .../runtime/internal/types/BUnionType.java | 4 +- .../runtime/internal/types/BXmlType.java | 6 +- .../internal/types/DistinctIdSupplier.java | 5 + .../runtime/internal/types/ShapeSupplier.java | 5 + .../internal/types/TypeWithAcceptedType.java | 2 + .../runtime/internal/types/TypeWithShape.java | 2 + .../internal/types/semtype/AllOrNothing.java | 7 +- .../types/semtype/BBooleanSubType.java | 4 +- .../internal/types/semtype/BCellSubType.java | 7 +- .../types/semtype/BCellSubTypeImpl.java | 8 +- .../types/semtype/BCellSubTypeSimple.java | 7 +- .../types/semtype/BDecimalSubType.java | 2 +- .../internal/types/semtype/BErrorSubType.java | 5 + .../internal/types/semtype/BFloatSubType.java | 2 +- .../types/semtype/BFunctionSubType.java | 9 +- .../types/semtype/BFutureSubType.java | 5 + .../internal/types/semtype/BIntSubType.java | 4 +- .../internal/types/semtype/BListProj.java | 18 +- .../internal/types/semtype/BListSubType.java | 12 +- .../internal/types/semtype/BMappingProj.java | 17 +- .../types/semtype/BMappingSubType.java | 5 + .../types/semtype/BObjectSubType.java | 5 + .../types/semtype/BStreamSubType.java | 5 + .../types/semtype/BStringSubType.java | 4 +- .../internal/types/semtype/BTableSubType.java | 5 + .../types/semtype/BTypedescSubType.java | 5 + .../internal/types/semtype/BXmlSubType.java | 5 + .../internal/types/semtype/BddMemo.java | 12 + .../types/semtype/CellAtomicType.java | 2 +- .../internal/types/semtype/Common.java | 5 + .../types/semtype/DelegatedSubType.java | 5 + .../types/semtype/EnumerableSubtypeData.java | 7 +- .../internal/types/semtype/ErrorUtils.java | 7 +- .../types/semtype/FixedLengthArray.java | 7 + .../types/semtype/FunctionAtomicType.java | 8 + .../types/semtype/FunctionDefinition.java | 5 + .../types/semtype/FunctionQualifiers.java | 11 +- .../internal/types/semtype/FutureUtils.java | 6 +- .../types/semtype/ImmutableSemType.java | 15 +- .../types/semtype/ListAtomicType.java | 7 + .../types/semtype/ListDefinition.java | 10 +- .../types/semtype/MappingAtomicType.java | 12 + .../types/semtype/MappingDefinition.java | 11 +- .../internal/types/semtype/Member.java | 11 + .../types/semtype/MutableSemType.java | 6 + .../types/semtype/ObjectDefinition.java | 20 +- .../types/semtype/ObjectQualifiers.java | 13 +- .../internal/types/semtype/PureSemType.java | 37 - .../internal/types/semtype/RegexUtils.java | 5 + .../internal/types/semtype/SemTypeHelper.java | 2 +- .../types/semtype/StreamDefinition.java | 8 +- .../internal/types/semtype/SubTypeData.java | 3 +- .../internal/types/semtype/SubtypePair.java | 10 + .../types/semtype/SubtypePairIterator.java | 2 +- .../internal/types/semtype/SubtypePairs.java | 2 +- .../internal/types/semtype/TableUtils.java | 11 +- .../internal/types/semtype/TypedescUtils.java | 5 + .../internal/types/semtype/XmlUtils.java | 8 +- .../runtime/internal/values/DecimalValue.java | 5 - .../runtime/internal/values/FPValue.java | 6 - .../internal/values/RecursiveValue.java | 2 + .../runtime/test/semtype/CoreTests.java | 9 +- .../src/test/resources/testng.xml | 2 +- .../function_invocation/Dependencies.toml | 18 - .../port/test/RuntimeSemTypeResolver.java | 42 +- .../semtype/port/test/RuntimeTypeTestAPI.java | 2 +- .../NativeConversionNegativeTest.java | 12 +- .../test/query/XMLQueryExpressionTest.java | 6 - .../list_constructor_infer_type.bal | 12 - .../dependently_typed_functions_test.bal | 7 +- 137 files changed, 769 insertions(+), 2571 deletions(-) delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java delete mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java delete mode 100644 tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/Dependencies.toml diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java index dcd676f3675e..94410bea3242 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/FunctionType.java @@ -40,5 +40,4 @@ public interface FunctionType extends AnnotatableType { Type getRestType(); Parameter[] getParameters(); - } 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 index 4afe068ae55b..0ddea623de6c 100644 --- 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 @@ -21,9 +21,15 @@ /** * Represent the BDD atom. * - * @since 2201.10.0 + * @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 index 7407bc97f39a..7e5163fa8a78 100644 --- 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 @@ -26,7 +26,7 @@ /** * Marker type representing AtomicType. * - * @since 2201.10.0 + * @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/BasicTypeCode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java index ba01d29eb71c..c9158bce4bf5 100644 --- 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 @@ -21,7 +21,7 @@ /** * Represent bit field that indicate which basic type a semType belongs to. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BasicTypeCode { 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 index 7d9ef4060457..2573230f05a8 100644 --- 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 @@ -23,10 +23,12 @@ 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. + * 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.10.0 + * @since 2201.11.0 */ public abstract sealed class Bdd extends SubType implements SubTypeData permits BddAllOrNothing, BddNode { 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 index f12f3c12f48b..e5b6c67ebad5 100644 --- 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 @@ -21,7 +21,7 @@ /** * Represent the leaf node of a Bdd. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BddAllOrNothing extends Bdd { 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 index 4b1f7bb6afa9..d08297a81b06 100644 --- 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 @@ -18,8 +18,20 @@ 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 index 949d36ed5b2e..001f154701ee 100644 --- 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 @@ -1,5 +1,10 @@ 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; 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 index 1f6239bbef60..71b1a9528220 100644 --- 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 @@ -19,9 +19,9 @@ package io.ballerina.runtime.api.types.semtype; /** - * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + * Generalized implementation of {@code BddNode}. * - * @since 2201.10.0 + * @since 2201.11.0 */ final class BddNodeImpl extends BddNode { 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 index 10b31a5d754b..bd64cdcf9af5 100644 --- 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 @@ -1,5 +1,11 @@ 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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java index ec2cbea729f0..4a6e47f3c870 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java @@ -22,7 +22,7 @@ /** * Represents a predicate that can be applied to a BDD conjunction. * - * @since 2201.10.0 + * @since 2201.11.0 */ @FunctionalInterface public interface BddPredicate { 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 index 23d09ee3527e..4c4f1149387b 100644 --- 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 @@ -38,6 +38,7 @@ 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; @@ -60,7 +61,7 @@ /** * Utility class for creating semtypes. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class Builder { @@ -90,16 +91,16 @@ public final class Builder { ListDefinition listDef = new ListDefinition(); MappingDefinition mapDef = new MappingDefinition(); SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); - SemType accum = - unionOf(getSimpleOrStringType(), getXmlType(), listDef.getSemType(env), mapDef.getSemType(env), - tableTy); + 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(readonlyType(), inner())); + new ConcurrentLazySupplier<>(() -> union(getReadonlyType(), getInnerType())); private static final ConcurrentLazySupplier LIST_ATOMIC_INNER = new ConcurrentLazySupplier<>(() -> new ListAtomicType( @@ -133,61 +134,61 @@ public static SemType from(BasicTypeCode typeCode) { return SemType.from(1 << typeCode.code()); } - public static SemType neverType() { + public static SemType getNeverType() { return SemType.from(0); } - public static SemType nilType() { + public static SemType getNilType() { return from(BasicTypeCode.BT_NIL); } - public static SemType undef() { + public static SemType getUndefType() { return from(BasicTypeCode.BT_UNDEF); } - public static SemType cell() { + public static SemType getCellType() { return from(BT_CELL); } - public static SemType inner() { + public static SemType getInnerType() { return INNER; } - public static SemType intType() { + public static SemType getIntType() { return from(BasicTypeCode.BT_INT); } - public static SemType decimalType() { + public static SemType getDecimalType() { return from(BasicTypeCode.BT_DECIMAL); } - public static SemType floatType() { + public static SemType getFloatType() { return from(BasicTypeCode.BT_FLOAT); } - public static SemType booleanType() { + public static SemType getBooleanType() { return from(BasicTypeCode.BT_BOOLEAN); } - public static SemType stringType() { + public static SemType getStringType() { return from(BasicTypeCode.BT_STRING); } - public static SemType charType() { + public static SemType getCharType() { return StringTypeCache.charType; } - public static SemType listType() { + public static SemType getListType() { return from(BT_LIST); } - public static SemType readonlyType() { + public static SemType getReadonlyType() { return PREDEFINED_TYPE_ENV.readonlyType(); } static SemType getBasicTypeUnion(int bitset) { return switch (bitset) { - case 0 -> neverType(); + case 0 -> getNeverType(); case VT_MASK -> VAL; default -> { if (Integer.bitCount(bitset) == 1) { @@ -204,7 +205,7 @@ static SemType getBasicTypeUnion(int 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); + assert checkDelegate(basicTypeCode, subType) : "BDd is wrapped in wrong delegate"; int some = 1 << basicTypeCode.code(); SubType[] subTypes = initializeSubtypeArray(some); subTypes[0] = subType; @@ -248,6 +249,7 @@ public static SemType getFloatConst(double 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}; @@ -351,14 +353,6 @@ public static SemType getAnyDataType() { return ANYDATA.get(); } - 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; - } - public static SemType getObjectType() { return OBJECT; } 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 index fbcb7ca9709b..bad069886c65 100644 --- 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 @@ -11,9 +11,31 @@ */ 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 index eaf7036cee5d..2ea49c4906f1 100644 --- 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 @@ -2,6 +2,12 @@ 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; 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 index 798717b164a3..94646141bb5b 100644 --- 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 @@ -21,6 +21,12 @@ 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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java index 34fbf71ded29..2faf2c559857 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java @@ -23,8 +23,9 @@ * Represents the Conjunction in the BDD. * * @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.10.0 + * @param next Next node in the conjunction, will be {@code null} if this is the + * last node + * @since 2201.11.0 */ public record Conjunction(Atom atom, Conjunction 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 index c9d688d97adf..4d6634f89f76 100644 --- 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 @@ -31,10 +31,12 @@ 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. + * 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.10.0 + * @since 2201.11.0 */ public final class Context { @@ -53,6 +55,8 @@ private Context(Env env) { } 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( @@ -87,11 +91,14 @@ public boolean memoSubtypeIsEmpty(Map memoTable, BddIsEmptyPredica } 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); } @@ -103,16 +110,19 @@ private void resetMemoizedValues(int initStackDepth, boolean isEmpty, boolean is 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(); } - // 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) { + // 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; 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 index 7788bb5dae87..3eefea1b58e4 100644 --- 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 @@ -50,8 +50,8 @@ 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.listType; -import static io.ballerina.runtime.api.types.semtype.Builder.undef; +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; @@ -61,7 +61,7 @@ /** * Contain functions defined in `core.bal` file. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class Core { @@ -141,11 +141,11 @@ public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { // 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() & listType().all()) != 0 ? Builder.getValType() : Builder.neverType(); + return (t.all() & getListType().all()) != 0 ? Builder.getValType() : Builder.getNeverType(); } else { SubTypeData keyData = intSubtype(k); if (isNothingSubtype(keyData)) { - return Builder.neverType(); + return Builder.getNeverType(); } return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData(t, BT_LIST), keyData, Builder.getValType()); @@ -344,7 +344,7 @@ private static int cardinality(int 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(), undef()), cat.mut()); + return Builder.getCellContaining(env, diff(cat.ty(), getUndefType()), cat.mut()); } public static SemType intersectCellMemberSemTypes(Env env, SemType t1, SemType t2) { @@ -355,11 +355,11 @@ public static SemType intersectCellMemberSemTypes(Env env, SemType t1, SemType t CellAtomicType atomicType = intersectCellAtomicType(c1, c2); return Builder.getCellContaining(env, atomicType.ty(), - undef().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + getUndefType().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); } public static Optional cellAtomicType(SemType t) { - SemType cell = Builder.cell(); + SemType cell = Builder.getCellType(); if (t.some() == 0) { return cell.equals(t) ? Optional.of(Builder.cellAtomicVal()) : Optional.empty(); } else { @@ -382,7 +382,7 @@ private static Optional bddCellAtomicType(Bdd bdd, CellAtomicTyp } public static SemType cellInnerVal(SemType t) { - return diff(cellInner(t), undef()); + return diff(cellInner(t), getUndefType()); } public static SemType cellInner(SemType t) { @@ -393,7 +393,7 @@ public static SemType cellInner(SemType t) { public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { if (bdd instanceof BddAllOrNothing) { - return bdd.isAll() ? Builder.from(typeCode) : Builder.neverType(); + return bdd.isAll() ? Builder.from(typeCode) : Builder.getNeverType(); } SubType subType = switch (typeCode.code()) { case CODE_OBJECT -> BObjectSubType.createDelegate(bdd); @@ -407,83 +407,83 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { } public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { - return diff(mappingMemberTypeInner(cx, t, k), Builder.undef()); + 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.listType()) ? Optional.ofNullable(listAtomicInner) : + return Core.isSubtypeSimple(t, Builder.getListType()) ? Optional.ofNullable(listAtomicInner) : Optional.empty(); } Env env = cx.env; - if (!isSubtypeSimple(t, Builder.listType())) { + 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.floatType())) { - return Builder.neverType(); + if (!containsBasicType(t, Builder.getFloatType())) { + return Builder.getNeverType(); } - return convertEnumerableNumericType(t, BT_FLOAT, Builder.intType(), + return convertEnumerableNumericType(t, BT_FLOAT, Builder.getIntType(), (floatValue) -> ((Double) floatValue).longValue(), Builder::getIntConst); } public static SemType floatToDecimal(SemType t) { - if (!containsBasicType(t, Builder.floatType())) { - return Builder.neverType(); + if (!containsBasicType(t, Builder.getFloatType())) { + return Builder.getNeverType(); } - return convertEnumerableNumericType(t, BT_FLOAT, Builder.decimalType(), + return convertEnumerableNumericType(t, BT_FLOAT, Builder.getDecimalType(), (floatValue) -> BigDecimal.valueOf((Double) floatValue), Builder::getDecimalConst); } public static SemType decimalToInt(SemType t) { - if (!containsBasicType(t, Builder.decimalType())) { - return Builder.neverType(); + if (!containsBasicType(t, Builder.getDecimalType())) { + return Builder.getNeverType(); } - return convertEnumerableNumericType(t, BT_DECIMAL, Builder.intType(), + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.getIntType(), (decimalVal) -> ((BigDecimal) decimalVal).longValue(), Builder::getIntConst); } public static SemType decimalToFloat(SemType t) { - if (!containsBasicType(t, Builder.decimalType())) { - return Builder.neverType(); + if (!containsBasicType(t, Builder.getDecimalType())) { + return Builder.getNeverType(); } - return convertEnumerableNumericType(t, BT_DECIMAL, Builder.floatType(), + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.getFloatType(), (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), Builder::getFloatConst); } public static SemType intToFloat(SemType t) { - if (!containsBasicType(t, Builder.intType())) { - return Builder.neverType(); + if (!containsBasicType(t, Builder.getIntType())) { + return Builder.getNeverType(); } SubTypeData subTypeData = subTypeData(t, BT_INT); if (subTypeData == AllOrNothing.NOTHING) { - return Builder.neverType(); + return Builder.getNeverType(); } if (subTypeData == AllOrNothing.ALL) { - return Builder.floatType(); + return Builder.getFloatType(); } BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; - return intSubTypeData.values().stream().map(Builder::getFloatConst).reduce(Builder.neverType(), Core::union); + return intSubTypeData.values().stream().map(Builder::getFloatConst).reduce(Builder.getNeverType(), Core::union); } public static SemType intToDecimal(SemType t) { - if (!containsBasicType(t, Builder.intType())) { - return Builder.neverType(); + if (!containsBasicType(t, Builder.getIntType())) { + return Builder.getNeverType(); } SubTypeData subTypeData = subTypeData(t, BT_INT); if (subTypeData == AllOrNothing.NOTHING) { - return Builder.neverType(); + return Builder.getNeverType(); } if (subTypeData == AllOrNothing.ALL) { - return Builder.decimalType(); + return Builder.getDecimalType(); } BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; return intSubTypeData.values().stream().map(BigDecimal::new).map(Builder::getDecimalConst) - .reduce(Builder.neverType(), Core::union); + .reduce(Builder.getNeverType(), Core::union); } private static , T extends Comparable> SemType convertEnumerableNumericType( @@ -491,7 +491,7 @@ private static , T extends Comparable> SemType conver Function semTypeCreator) { SubTypeData subTypeData = subTypeData(source, targetTypeCode); if (subTypeData == AllOrNothing.NOTHING) { - return Builder.neverType(); + return Builder.getNeverType(); } if (subTypeData == AllOrNothing.ALL) { return topType; @@ -500,7 +500,7 @@ private static , T extends Comparable> SemType conver EnumerableSubtypeData enumerableSubtypeData = (EnumerableSubtypeData) subTypeData; SemType posType = Arrays.stream(enumerableSubtypeData.values()).map(valueConverter).distinct().map(semTypeCreator) - .reduce(Builder.neverType(), Core::union); + .reduce(Builder.getNeverType(), Core::union); if (enumerableSubtypeData.allowed()) { return posType; } 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 index cd47a8302b3c..d759a4cdf87d 100644 --- 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 @@ -18,8 +18,17 @@ package io.ballerina.runtime.api.types.semtype; -// NOTE: definitions are not thread safe +/** + * Represent a type definition which will act as a layer of indirection between {@code Env} and the type descriptor. + * + * @since 2201.11.0 + */ public interface Definition { + /** + * Get the {@code SemType} of this definition in the given environment. + * + * @param env type environment + */ SemType getSemType(Env env); } 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 index b787619f823d..10b00639a035 100644 --- 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 @@ -37,9 +37,12 @@ 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.10.0 + * 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 @@ -48,7 +51,8 @@ public final class 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. + // 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; 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 index 732e1f7f174f..29ce90a72be5 100644 --- 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 @@ -18,6 +18,17 @@ 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 index 78bc55284694..82618c4c93f6 100644 --- 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 @@ -26,6 +26,11 @@ 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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java index 10e75f2c1ad8..e1d579e9b266 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java @@ -23,7 +23,7 @@ /** * Utility class for list type projection. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class ListProj { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java index cd67382714c7..cd3c113054d3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -24,7 +24,7 @@ /** * Utility class for mapping type projection. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class MappingProj { 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 index e93e668d7e84..f8d4922c7f56 100644 --- 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 @@ -25,7 +25,7 @@ * @param type of second value * @param first first values * @param second second value - * @since 2201.10.0 + * @since 2201.11.0 */ public record Pair(E1 first, E2 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 index 947a474d19a6..1545a36e572e 100644 --- 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 @@ -97,7 +97,7 @@ final class PredefinedTypeEnv { // 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.undef()), + () -> CellAtomicType.from(union(Builder.getMappingType(), Builder.getUndefType()), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); @@ -114,7 +114,7 @@ final class PredefinedTypeEnv { // 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.undef()), + () -> CellAtomicType.from(union(Builder.mappingRO(), Builder.getUndefType()), CellAtomicType.CellMutability.CELL_MUT_LIMITED), this::addInitializedCellAtom ); @@ -153,7 +153,7 @@ final class PredefinedTypeEnv { createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); private final Supplier cellAtomicUndef = new ConcurrentLazySupplierWithCallback<>( - () -> CellAtomicType.from(Builder.undef(), CellAtomicType.CellMutability.CELL_MUT_NONE), + () -> CellAtomicType.from(Builder.getUndefType(), CellAtomicType.CellMutability.CELL_MUT_NONE), this::addInitializedCellAtom ); private final Supplier atomCellUndef = 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 index 5e224575818f..f302a7dc3c45 100644 --- 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 @@ -22,7 +22,7 @@ /** * Represent a recursive type atom. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class RecAtom implements Atom { 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 index 8eea044f57be..d185c96b6c7f 100644 --- 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 @@ -5,6 +5,11 @@ 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 { @@ -59,12 +64,6 @@ public static SemType tryInto(Type type) { return (SemType) type; } - public enum CachedResult { - TRUE, - FALSE, - NOT_FOUND - } - @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 index 1eb31722231b..99bc4c9a51f5 100644 --- 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 @@ -9,6 +9,11 @@ import java.util.Optional; +/** + * Utility class for performing shape related operations. + * + * @since 2201.11.0 + */ public class ShapeAnalyzer { private ShapeAnalyzer() { @@ -23,7 +28,7 @@ public static Optional acceptedTypeOf(Context cx, Type typeDesc) { public static Optional shapeOf(Context cx, Object object) { if (object == null) { - return Optional.of(Builder.nilType()); + return Optional.of(Builder.getNilType()); } else if (object instanceof DecimalValue decimalValue) { return Optional.of(Builder.getDecimalConst(decimalValue.value())); } else if (object instanceof Double doubleValue) { @@ -52,7 +57,7 @@ public static Optional inherentTypeOf(Context cx, Object object) { return bValue.inherentTypeOf(cx); } if (object == null) { - return Optional.of(Builder.nilType()); + return Optional.of(Builder.getNilType()); } else if (object instanceof Double doubleValue) { return Optional.of(Builder.getFloatConst(doubleValue)); } else if (object instanceof Number intValue) { 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 index 2850e95e5898..85fabfa2303b 100644 --- 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 @@ -25,7 +25,7 @@ /** * Describe set of operation supported by each basic Type. * - * @since 2201.10.0 + * @since 2201.11.0 */ public abstract class SubType { 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 index cbc915ccf3f0..02080171937e 100644 --- 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 @@ -18,6 +18,15 @@ 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 { 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 index 05d54da822f6..cf9bc820da2a 100644 --- 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 @@ -6,8 +6,21 @@ 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; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java index 4d92f07dd6dd..efc1e70cb995 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java @@ -18,7 +18,6 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.internal.types.TypeWithShape; /** *

@@ -27,7 +26,7 @@ * * @since 1.1.0 */ -public interface BArray extends BRefValue, BCollection, PatternMatchableValue { +public interface BArray extends BRefValue, BCollection { /** * Get value in the given array index. @@ -237,9 +236,4 @@ public interface BArray extends BRefValue, BCollection, PatternMatchableValue { void setLength(long i); long getLength(); - - @Override - default TypeWithShape getTypeWithShape() { - return (TypeWithShape) getType(); - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej deleted file mode 100644 index 0ed99d03ea7b..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java.rej +++ /dev/null @@ -1,10 +0,0 @@ -diff a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BArray.java (rejected hunks) -@@ -30,7 +30,7 @@ import java.util.Optional; - * - * @since 1.1.0 - */ --public interface BArray extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { -+public interface BArray extends BRefValue, BCollection, PatternMatchableValue, RecursiveValue { - - /** - * Get value in the given array index. 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 8477a5c48dba..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 @@ -33,7 +33,7 @@ * * @since 1.1.0 */ -public abstract class BError extends RuntimeException implements BValue, PatternMatchableValue { +public abstract class BError extends RuntimeException implements BValue { public static final String ERROR_PRINT_PREFIX = "error: "; @@ -89,14 +89,9 @@ public void printStackTrace(PrintWriter printWriter) { */ public abstract List getCallStack(); - @Override - public TypeWithShape getTypeWithShape() { - return (TypeWithShape) getType(); - } - @Override public Optional inherentTypeOf(Context cx) { - TypeWithShape type = getTypeWithShape(); + 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/BMap.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java index 36dc6a246110..1eb65cc3fab8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BMap.java @@ -18,7 +18,6 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.internal.types.TypeWithShape; import java.util.Collection; import java.util.Map; @@ -34,7 +33,7 @@ * * @since 1.1.0 */ -public interface BMap extends BRefValue, BCollection, PatternMatchableValue { +public interface BMap extends BRefValue, BCollection { /** * Returns the value to which the specified key is mapped, or {@code null} if this map contains no @@ -194,9 +193,4 @@ public interface BMap extends BRefValue, BCollection, PatternMatchableValu Object merge(BMap v2, boolean checkMergeability); void populateInitialValue(K key, V value); - - @Override - default TypeWithShape getTypeWithShape() { - return (TypeWithShape) 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 2e0f3aac5db5..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 @@ -68,6 +68,9 @@ default String informalStringValue(BLink parent) { * @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()); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java deleted file mode 100644 index caf569e749ec..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/PatternMatchableValue.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.values; - -import io.ballerina.runtime.internal.types.TypeWithShape; - -// Marker interface for value that can be pattern matched in a match statement -public interface PatternMatchableValue { - - TypeWithShape getTypeWithShape(); -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java deleted file mode 100644 index bdf9b514a2b7..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/FallbackTypeChecker.java +++ /dev/null @@ -1,2019 +0,0 @@ -/* - * 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; - -import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.api.PredefinedTypes; -import io.ballerina.runtime.api.TypeTags; -import io.ballerina.runtime.api.flags.SymbolFlags; -import io.ballerina.runtime.api.types.ArrayType; -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.ParameterizedType; -import io.ballerina.runtime.api.types.ReferenceType; -import io.ballerina.runtime.api.types.Type; -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.values.BObject; -import io.ballerina.runtime.api.values.BXml; -import io.ballerina.runtime.internal.commons.TypeValuePair; -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.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.values.ArrayValue; -import io.ballerina.runtime.internal.values.DecimalValue; -import io.ballerina.runtime.internal.values.ErrorValue; -import io.ballerina.runtime.internal.values.MapValueImpl; -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.XmlSequence; -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.stream.Collectors; - -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANY; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_ANYDATA; -import static io.ballerina.runtime.api.PredefinedTypes.TYPE_JSON; -import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; -import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; -import static io.ballerina.runtime.internal.TypeChecker.isEqual; -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; - -// Contains all the existing non semtype type check logic, SEMTYPE-TODO: remove this once semtype implementation is -// complete -final class FallbackTypeChecker { - - static final byte MAX_TYPECAST_ERROR_COUNT = 20; - - private FallbackTypeChecker() { - } - - @Deprecated - static boolean checkIsType(BType sourceType, BType 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 TypeChecker.checkIsType(((IntersectionType) sourceType).getEffectiveType(), - targetTypeTag != TypeTags.INTERSECTION_TAG ? targetType : - ((IntersectionType) targetType).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return TypeChecker.checkIsType(((ReferenceType) sourceType).getReferredType(), - targetTypeTag != TypeTags.TYPE_REFERENCED_TYPE_TAG ? targetType : - ((ReferenceType) targetType).getReferredType(), unresolvedTypes); - case TypeTags.PARAMETERIZED_TYPE_TAG: - if (targetTypeTag != TypeTags.PARAMETERIZED_TYPE_TAG) { - return TypeChecker.checkIsType(((ParameterizedType) sourceType).getParamValueType(), targetType, - unresolvedTypes); - } - return TypeChecker.checkIsType(((ParameterizedType) sourceType).getParamValueType(), - ((ParameterizedType) targetType).getParamValueType(), unresolvedTypes); - case TypeTags.READONLY_TAG: - return TypeChecker.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 -> - TypeChecker.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 -> - TypeChecker.checkIsType(sourceType, ((IntersectionType) targetType).getEffectiveType(), - unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> - TypeChecker.checkIsType(sourceType, ((ReferenceType) targetType).getReferredType(), - unresolvedTypes); - default -> checkIsRecursiveType(sourceType, targetType, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - }; - } - - static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { - for (Object bValue : sourceType.valueSpace) { - if (!TypeChecker.checkIsType(bValue, targetType)) { - return false; - } - } - return true; - } - - static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, - List unresolvedTypes) { - for (Type type : sourceType.getMemberTypes()) { - if (!TypeChecker.checkIsType(type, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - static boolean hasIncompatibleReadOnlyFlags(Field targetField, Field sourceField) { - return SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.READONLY) && !SymbolFlags - .isFlagOn(sourceField.getFlags(), - SymbolFlags.READONLY); - } - - static boolean checkIsAnyType(Type sourceType) { - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { - case TypeTags.ERROR_TAG: - case TypeTags.READONLY_TAG: - return false; - case TypeTags.UNION_TAG: - case TypeTags.ANYDATA_TAG: - case TypeTags.JSON_TAG: - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsAnyType(memberType)) { - return false; - } - } - return true; - default: - return true; - } - } - - static boolean checkObjectEquivalency(Type sourceType, BObjectType targetType, - List unresolvedTypes) { - return checkObjectEquivalency(null, sourceType, targetType, unresolvedTypes); - } - - 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. - TypeChecker.TypePair pair = new TypeChecker.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) || - !TypeChecker.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 = TypeChecker.getType(fieldValue); - - if (fieldValueType.isReadOnly()) { - if (!TypeChecker.checkIsLikeType(fieldValue, lhsField.getFieldType())) { - return false; - } - continue; - } - - if (!TypeChecker.checkIsType(fieldValueType, lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } else if (!TypeChecker.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 (rhsFunc == null || - !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) || (matchingFuncIsResource && !lhsFuncIsResource)) { - 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 (!TypeChecker.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 (!TypeChecker.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 TypeChecker.checkIsType(source.getReturnType(), target.getReturnType(), unresolvedTypes); - } - - static boolean hasIncompatibleIsolatedFlags(FunctionType target, FunctionType source) { - return SymbolFlags.isFlagOn(target.getFlags(), SymbolFlags.ISOLATED) && !SymbolFlags - .isFlagOn(source.getFlags(), SymbolFlags.ISOLATED); - } - - 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) { - var flags = ((BObjectType) sourceType).flags; - return (flags & SymbolFlags.SERVICE) == SymbolFlags.SERVICE; - } - - return false; - } - - 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() != ArrayType.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. - */ - static boolean checkIsLikeType(List errors, Object sourceValue, Type targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName) { - Type sourceType = TypeChecker.getType(sourceValue); - if (TypeChecker.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. - */ - 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; - } - - switch (targetTypeTag) { - case TypeTags.READONLY_TAG: - return true; - case TypeTags.BYTE_TAG: - if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { - return TypeChecker.isByteLiteral(((Number) sourceValue).longValue()); - } - return allowNumericConversion && TypeConverter.isConvertibleToByte(sourceValue); - case TypeTags.INT_TAG: - return allowNumericConversion && TypeConverter.isConvertibleToInt(sourceValue); - 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: - if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { - return TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); - } - return allowNumericConversion && TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); - case TypeTags.FLOAT_TAG: - case TypeTags.DECIMAL_TAG: - return allowNumericConversion && TypeConverter.isConvertibleToFloatingPointTypes(sourceValue); - case TypeTags.CHAR_STRING_TAG: - return TypeConverter.isConvertibleToChar(sourceValue); - case TypeTags.RECORD_TYPE_TAG: - return checkIsLikeRecordType(sourceValue, (BRecordType) targetType, unresolvedValues, - allowNumericConversion, varName, errors); - case TypeTags.TABLE_TAG: - return checkIsLikeTableType(sourceValue, (BTableType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.JSON_TAG: - return checkIsLikeJSONType(sourceValue, sourceType, (BJsonType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.MAP_TAG: - return checkIsLikeMapType(sourceValue, (BMapType) targetType, unresolvedValues, allowNumericConversion); - case TypeTags.STREAM_TAG: - return checkIsLikeStreamType(sourceValue, (BStreamType) targetType); - case TypeTags.ARRAY_TAG: - return checkIsLikeArrayType(sourceValue, (BArrayType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.TUPLE_TAG: - return checkIsLikeTupleType(sourceValue, (BTupleType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.ERROR_TAG: - return checkIsLikeErrorType(sourceValue, (BErrorType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.ANYDATA_TAG: - return checkIsLikeAnydataType(sourceValue, sourceType, unresolvedValues, allowNumericConversion); - case TypeTags.FINITE_TYPE_TAG: - return checkFiniteTypeAssignable(sourceValue, sourceType, (BFiniteType) targetType, - unresolvedValues, allowNumericConversion); - case TypeTags.XML_ELEMENT_TAG: - case TypeTags.XML_COMMENT_TAG: - case TypeTags.XML_PI_TAG: - case TypeTags.XML_TEXT_TAG: - if (TypeTags.isXMLTypeTag(sourceTypeTag)) { - return checkIsLikeXmlValueSingleton((XmlValue) sourceValue, targetType); - } - return false; - case TypeTags.XML_TAG: - if (TypeTags.isXMLTypeTag(sourceTypeTag)) { - return checkIsLikeXMLSequenceType((XmlValue) sourceValue, targetType); - } - return false; - case TypeTags.UNION_TAG: - return checkIsLikeUnionType(errors, sourceValue, (BUnionType) targetType, unresolvedValues, - allowNumericConversion, varName); - case TypeTags.INTERSECTION_TAG: - return checkIsLikeOnValue(errors, sourceValue, sourceType, - ((BIntersectionType) targetType).getEffectiveType(), unresolvedValues, allowNumericConversion, - varName); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkIsLikeOnValue(errors, sourceValue, sourceType, - ((BTypeReferenceType) targetType).getReferredType(), unresolvedValues, allowNumericConversion, - varName); - default: - return 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) { - switch (getImpliedType(type).getTag()) { - case TypeTags.XML_ELEMENT_TAG: - return XmlNodeType.ELEMENT; - case TypeTags.XML_COMMENT_TAG: - return XmlNodeType.COMMENT; - case TypeTags.XML_PI_TAG: - return XmlNodeType.PI; - default: - return 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; - } - - private static boolean checkIsLikeAnydataType(Object sourceValue, Type sourceType, - List unresolvedValues, - boolean allowNumericConversion) { - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { - 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()); - switch (getImpliedType(arrayType.getElementType()).getTag()) { - case TypeTags.INT_TAG: - case TypeTags.FLOAT_TAG: - case TypeTags.DECIMAL_TAG: - case TypeTags.STRING_TAG: - case TypeTags.BOOLEAN_TAG: - case TypeTags.BYTE_TAG: - return true; - default: - return 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; - } - - private static boolean checkIsLikeArrayType(Object sourceValue, BArrayType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof ArrayValue)) { - return false; - } - - ArrayValue source = (ArrayValue) sourceValue; - Type targetTypeElementType = targetType.getElementType(); - if (source.getType().getTag() == TypeTags.ARRAY_TAG) { - Type sourceElementType = ((BArrayType) source.getType()).getElementType(); - if (isValueType(sourceElementType)) { - - if (TypeChecker.checkIsType(sourceElementType, targetTypeElementType, new ArrayList<>())) { - return true; - } - - if (allowNumericConversion && TypeChecker.isNumericType(sourceElementType)) { - if (TypeChecker.isNumericType(targetTypeElementType)) { - return true; - } - - if (targetTypeElementType.getTag() != TypeTags.UNION_TAG) { - return false; - } - - List targetNumericTypes = new ArrayList<>(); - for (Type memType : ((BUnionType) targetTypeElementType).getMemberTypes()) { - if (TypeChecker.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; - } - } - } - - int sourceSize = source.size(); - if ((targetType.getState() != ArrayType.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)) { - return false; - } - - for (Object mapEntry : ((MapValueImpl) sourceValue).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)) { - return false; - } - - BStreamType streamType = (BStreamType) ((StreamValue) sourceValue).getType(); - - return streamType.getConstrainedType() == targetType.getConstrainedType(); - } - - 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 (TypeChecker.checkIsType(elementType, targetType, new ArrayList<>())) { - return true; - } - - 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)) { - 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; - } - unresolvedValues.add(typeValuePair); - return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, - allowNumericConversion); - 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; - } - - private static boolean checkIsLikeRecordType(Object sourceValue, BRecordType targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName, List errors) { - if (!(sourceValue instanceof MapValueImpl)) { - return false; - } - - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; - } - unresolvedValues.add(typeValuePair); - - Map targetFieldTypes = new HashMap<>(); - Type restFieldType = targetType.restFieldType; - boolean returnVal = true; - - for (Field field : targetType.getFields().values()) { - targetFieldTypes.put(field.getFieldName(), field.getFieldType()); - } - - for (Map.Entry targetTypeEntry : targetFieldTypes.entrySet()) { - String fieldName = targetTypeEntry.getKey().toString(); - String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); - Field targetField = targetType.getFields().get(fieldName); - - if (!(((MapValueImpl) sourceValue).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; - } - } - - for (Object object : ((MapValueImpl) sourceValue).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(); - - 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; - } - - 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); - } - } - - private static boolean checkIsLikeTableType(Object sourceValue, BTableType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof TableValueImpl)) { - return false; - } - TableValueImpl tableValue = (TableValueImpl) sourceValue; - BTableType sourceType = (BTableType) getImpliedType(tableValue.getType()); - if (targetType.getKeyType() != null && sourceType.getFieldNames().length == 0) { - return false; - } - - if (sourceType.getKeyType() != null && - !TypeChecker.checkIsType(tableValue.getKeyType(), targetType.getKeyType())) { - return false; - } - - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; - } - - Object[] objects = tableValue.values().toArray(); - for (Object object : objects) { - if (!TypeChecker.checkIsLikeType(object, targetType.getConstrainedType(), allowNumericConversion)) { - return false; - } - } - return true; - } - - private static boolean checkFiniteTypeAssignable(Object sourceValue, Type sourceType, BFiniteType targetType, - List unresolvedValues, - boolean allowNumericConversion) { - if (targetType.valueSpace.size() == 1) { - Type valueType = getImpliedType(TypeChecker.getType(targetType.valueSpace.iterator().next())); - if (!TypeChecker.belongToSingleBasicTypeOrString(valueType) && - valueType.getTag() != TypeTags.NULL_TAG) { - return checkIsLikeOnValue(null, sourceValue, sourceType, valueType, unresolvedValues, - allowNumericConversion, null); - } - } - - for (Object valueSpaceItem : targetType.valueSpace) { - // TODO: 8/13/19 Maryam fix for conversion - if (isFiniteTypeValue(sourceValue, sourceType, valueSpaceItem, allowNumericConversion)) { - return true; - } - } - return false; - } - - protected static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object valueSpaceItem, - boolean allowNumericConversion) { - Type valueSpaceItemType = TypeChecker.getType(valueSpaceItem); - int sourceTypeTag = getImpliedType(sourceType).getTag(); - int valueSpaceItemTypeTag = getImpliedType(valueSpaceItemType).getTag(); - if (valueSpaceItemTypeTag > TypeTags.DECIMAL_TAG) { - return valueSpaceItemTypeTag == sourceTypeTag && - (valueSpaceItem == sourceValue || valueSpaceItem.equals(sourceValue)); - } - - switch (sourceTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - switch (valueSpaceItemTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue(); - case TypeTags.FLOAT_TAG: - return ((Number) sourceValue).longValue() == ((Number) valueSpaceItem).longValue() && - allowNumericConversion; - case TypeTags.DECIMAL_TAG: - return ((Number) sourceValue).longValue() == ((DecimalValue) valueSpaceItem).intValue() && - allowNumericConversion; - } - case TypeTags.FLOAT_TAG: - switch (valueSpaceItemTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - return ((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() - && allowNumericConversion; - case TypeTags.FLOAT_TAG: - return (((Number) sourceValue).doubleValue() == ((Number) valueSpaceItem).doubleValue() || - (Double.isNaN((Double) sourceValue) && Double.isNaN((Double) valueSpaceItem))); - case TypeTags.DECIMAL_TAG: - return ((Number) sourceValue).doubleValue() == ((DecimalValue) valueSpaceItem).floatValue() - && allowNumericConversion; - } - case TypeTags.DECIMAL_TAG: - switch (valueSpaceItemTypeTag) { - case TypeTags.BYTE_TAG: - case TypeTags.INT_TAG: - return TypeChecker.checkDecimalEqual((DecimalValue) sourceValue, - DecimalValue.valueOf(((Number) valueSpaceItem).longValue())) && allowNumericConversion; - case TypeTags.FLOAT_TAG: - return TypeChecker.checkDecimalEqual((DecimalValue) sourceValue, - DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && - allowNumericConversion; - case TypeTags.DECIMAL_TAG: - return TypeChecker.checkDecimalEqual((DecimalValue) sourceValue, (DecimalValue) valueSpaceItem); - } - default: - if (sourceTypeTag != valueSpaceItemTypeTag) { - return false; - } - return valueSpaceItem.equals(sourceValue); - } - } - - private static boolean checkIsLikeErrorType(Object sourceValue, BErrorType targetType, - List unresolvedValues, boolean allowNumericConversion) { - Type sourceTypeReferredType = getImpliedType(TypeChecker.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; - } - - static boolean checkTypeDescType(Type sourceType, BTypedescType targetType, - List unresolvedTypes) { - if (sourceType.getTag() != TypeTags.TYPEDESC_TAG) { - return false; - } - - BTypedescType sourceTypedesc = (BTypedescType) sourceType; - return TypeChecker.checkIsType(sourceTypedesc.getConstraint(), targetType.getConstraint(), unresolvedTypes); - } - - static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { - boolean isLhsXmlSequence = lhsValue.getNodeType() == XmlNodeType.SEQUENCE; - boolean isRhsXmlSequence = rhsValue.getNodeType() == XmlNodeType.SEQUENCE; - - if (isLhsXmlSequence && isRhsXmlSequence) { - return isXMLSequenceRefEqual((XmlSequence) lhsValue, (XmlSequence) rhsValue); - } - if (isLhsXmlSequence && lhsValue.isSingleton()) { - return ((XmlSequence) lhsValue).getChildrenList().get(0) == rhsValue; - } - if (isRhsXmlSequence && rhsValue.isSingleton()) { - return ((XmlSequence) rhsValue).getChildrenList().get(0) == lhsValue; - } - if (lhsValue.getNodeType() != rhsValue.getNodeType()) { - return false; - } - if (lhsValue.getNodeType() == XmlNodeType.TEXT && rhsValue.getNodeType() == XmlNodeType.TEXT) { - return isEqual(lhsValue, rhsValue); - } - return false; - } - - private static boolean isXMLSequenceRefEqual(XmlSequence lhsValue, XmlSequence rhsValue) { - Iterator lhsIter = lhsValue.getChildrenList().iterator(); - Iterator rhsIter = rhsValue.getChildrenList().iterator(); - while (lhsIter.hasNext() && rhsIter.hasNext()) { - BXml l = lhsIter.next(); - BXml r = rhsIter.next(); - if (!(l == r || isXMLValueRefEqual((XmlValue) l, (XmlValue) r))) { - return false; - } - } - // lhs hasNext = false & rhs hasNext = false -> empty sequences, hence ref equal - // lhs hasNext = true & rhs hasNext = true would never reach here - // only one hasNext method returns true means sequences are of different sizes, hence not ref equal - return lhsIter.hasNext() == rhsIter.hasNext(); - } - - static boolean checkIsRecursiveType(Type sourceType, Type targetType, List unresolvedTypes) { - switch (targetType.getTag()) { - case TypeTags.MAP_TAG: - return checkIsMapType(sourceType, (BMapType) targetType, unresolvedTypes); - case TypeTags.STREAM_TAG: - return checkIsStreamType(sourceType, (BStreamType) targetType, unresolvedTypes); - case TypeTags.TABLE_TAG: - return checkIsTableType(sourceType, (BTableType) targetType, unresolvedTypes); - case TypeTags.JSON_TAG: - return checkIsJSONType(sourceType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - return checkIsRecordType(sourceType, (BRecordType) targetType, unresolvedTypes); - case TypeTags.FUNCTION_POINTER_TAG: - return checkIsFunctionType(sourceType, (BFunctionType) targetType); - case TypeTags.ARRAY_TAG: - return checkIsArrayType(sourceType, (BArrayType) targetType, unresolvedTypes); - case TypeTags.TUPLE_TAG: - return checkIsTupleType(sourceType, (BTupleType) targetType, unresolvedTypes); - case TypeTags.UNION_TAG: - return checkIsUnionType(sourceType, (BUnionType) targetType, unresolvedTypes); - case TypeTags.OBJECT_TYPE_TAG: - return checkObjectEquivalency(sourceType, (BObjectType) targetType, - unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - return checkIsFiniteType(sourceType, (BFiniteType) targetType); - case TypeTags.FUTURE_TAG: - return checkIsFutureType(sourceType, (BFutureType) targetType, unresolvedTypes); - case TypeTags.ERROR_TAG: - return checkIsErrorType(sourceType, (BErrorType) targetType, unresolvedTypes); - case TypeTags.TYPEDESC_TAG: - return checkTypeDescType(sourceType, (BTypedescType) targetType, unresolvedTypes); - case TypeTags.XML_TAG: - return checkIsXMLType(sourceType, targetType, unresolvedTypes); - default: - // other non-recursive types shouldn't reach here - return false; - } - } - - 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); - TypeChecker.TypePair pair = new TypeChecker.TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - switch (sourceType.getTag()) { - case TypeTags.UNION_TAG: - case TypeTags.JSON_TAG: - case TypeTags.ANYDATA_TAG: - return isUnionTypeMatch((BUnionType) sourceType, targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - return isFiniteTypeMatch((BFiniteType) sourceType, targetType); - default: - for (Type type : targetType.getMemberTypes()) { - if (TypeChecker.checkIsType(sourceType, type, unresolvedTypes)) { - return true; - } - } - return false; - - } - } - - 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; - } - } - - 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 TypeChecker.checkIsType(source.constraint, targetConstraint, unresolvedTypes); - } - if (TypeTags.isXMLTypeTag(sourceTag)) { - return TypeChecker.checkIsType(sourceType, target.constraint, unresolvedTypes); - } - return false; - } - - 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 List getWideTypeComponents(BRecordType recType) { - List types = new ArrayList<>(); - for (Field f : recType.getFields().values()) { - types.add(f.getFieldType()); - } - 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; - } - return checkConstraints(((BStreamType) sourceType).getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes) - && checkConstraints(((BStreamType) sourceType).getCompletionType(), targetType.getCompletionType(), - unresolvedTypes); - } - - 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; - } - - 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); - } - - return Arrays.equals(srcTableType.getFieldNames(), targetType.getFieldNames()); - } - - 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).collect(Collectors.toList()); - - if (fields.size() != memTypes.size()) { - return null; - } - - if (fields.stream().allMatch( - field -> TypeChecker.isSameType(field.getFieldType(), fields.get(0).getFieldType()))) { - return fields.get(0); - } - return null; - default: - return null; - } - } - - 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. - TypeChecker.TypePair pair = new TypeChecker.TypePair(sourceType, jsonType); - if (unresolvedTypes.contains(pair)) { - 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: - case TypeTags.JSON_TAG: - return true; - case TypeTags.ARRAY_TAG: - // Element type of the array should be 'is type' JSON - return TypeChecker.checkIsType(((BArrayType) sourceType).getElementType(), jsonType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - return isFiniteTypeMatch((BFiniteType) sourceType, jsonType); - case TypeTags.MAP_TAG: - return TypeChecker.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)) { - return false; - } - } - - if (!recordType.sealed) { - return checkIsJSONType(recordType.restFieldType, unresolvedTypes); - } - 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); - switch (sourceType.getTag()) { - case TypeTags.RECORD_TYPE_TAG: - return checkIsRecordType((BRecordType) sourceType, targetType, unresolvedTypes); - case TypeTags.MAP_TAG: - return checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); - default: - return 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. - TypeChecker.TypePair pair = new TypeChecker.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 && - !TypeChecker.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 && - !TypeChecker.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 (!TypeChecker.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 (!TypeChecker.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. - TypeChecker.TypePair pair = new TypeChecker.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()) { - var flags = field.getFlags(); - if (!SymbolFlags.isFlagOn(flags, SymbolFlags.OPTIONAL)) { - return false; - } - - if (SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY) && !sourceType.isReadOnly()) { - return false; - } - - if (!TypeChecker.checkIsType(constraintType, field.getFieldType(), unresolvedTypes)) { - return false; - } - } - - return TypeChecker.checkIsType(constraintType, targetType.restFieldType, unresolvedTypes); - } - - private static boolean checkIsArrayType(BArrayType sourceType, BArrayType targetType, - List unresolvedTypes) { - switch (sourceType.getState()) { - case OPEN: - if (targetType.getState() != ArrayType.ArrayState.OPEN) { - return false; - } - break; - case CLOSED: - if (targetType.getState() == ArrayType.ArrayState.CLOSED && - sourceType.getSize() != targetType.getSize()) { - return false; - } - break; - default: - break; - } - return TypeChecker.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() == ArrayType.ArrayState.OPEN) { - for (Type sourceElementType : tupleTypes) { - if (!TypeChecker.checkIsType(sourceElementType, targetElementType, unresolvedTypes)) { - return false; - } - } - if (sourceRestType != null) { - return TypeChecker.checkIsType(sourceRestType, targetElementType, unresolvedTypes); - } - return true; - } - if (sourceRestType != null) { - return false; - } - if (tupleTypes.size() != targetType.getSize()) { - return false; - } - for (Type sourceElementType : tupleTypes) { - if (!TypeChecker.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 TypeChecker.checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return false; - case CLOSED: - if (sourceType.getSize() < targetTypes.size()) { - return false; - } - if (targetTypes.isEmpty()) { - if (targetRestType != null) { - return TypeChecker.checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return sourceType.getSize() == 0; - } - - for (Type targetElementType : targetTypes) { - if (!(TypeChecker.checkIsType(sourceElementType, targetElementType, unresolvedTypes))) { - return false; - } - } - if (sourceType.getSize() == targetTypes.size()) { - return true; - } - if (targetRestType != null) { - return TypeChecker.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 (!TypeChecker.checkIsType(sourceTypes.get(i), targetTypes.get(i), unresolvedTypes)) { - return false; - } - } - if (sourceTypeSize == targetTypeSize) { - if (sourceRestType != null) { - return TypeChecker.checkIsType(sourceRestType, targetRestType, unresolvedTypes); - } - return true; - } - - for (int i = targetTypeSize; i < sourceTypeSize; i++) { - if (!TypeChecker.checkIsType(sourceTypes.get(i), targetRestType, unresolvedTypes)) { - return false; - } - } - if (sourceRestType != null) { - return TypeChecker.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 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 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 (!TypeChecker.checkIsType(targetType.parameters[i].type, source.parameters[i].type, new ArrayList<>())) { - return false; - } - } - - return TypeChecker.checkIsType(source.retType, targetType.retType, new ArrayList<>()); - } - - private static boolean hasIncompatibleTransactionalFlags(FunctionType target, FunctionType source) { - return SymbolFlags.isFlagOn(source.getFlags(), SymbolFlags.TRANSACTIONAL) && !SymbolFlags - .isFlagOn(target.getFlags(), SymbolFlags.TRANSACTIONAL); - } - - private static boolean checkConstraints(Type sourceConstraint, Type targetConstraint, - List unresolvedTypes) { - if (sourceConstraint == null) { - sourceConstraint = TYPE_ANY; - } - - if (targetConstraint == null) { - targetConstraint = TYPE_ANY; - } - - return TypeChecker.checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); - } - - private static boolean checkIsErrorType(Type sourceType, BErrorType targetType, - List unresolvedTypes) { - if (sourceType.getTag() != TypeTags.ERROR_TAG) { - return false; - } - // Handle recursive error types. - TypeChecker.TypePair pair = new TypeChecker.TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - BErrorType bErrorType = (BErrorType) sourceType; - - if (!TypeChecker.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); - } -} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java index 0415d8373f79..a7c20509bed8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/MapUtils.java @@ -117,7 +117,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { - return Core.containsBasicType(SemType.tryInto(type), Builder.nilType()); + return Core.containsBasicType(SemType.tryInto(type), Builder.getNilType()); } public static BError createOpNotSupportedError(Type type, String op) { 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 6b6cb438f7e8..f9ea9fcf82d7 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 @@ -110,15 +110,14 @@ @SuppressWarnings({"rawtypes"}) 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())); - private static final byte MAX_TYPECAST_ERROR_COUNT = 20; public static Object checkCast(Object sourceVal, Type targetType) { List errors = new ArrayList<>(); - // TODO: we don't need to do this like this see checkIsType(Object, Type) if (checkIsType(sourceVal, targetType)) { return sourceVal; } @@ -262,8 +261,8 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { return true; } SemType sourceSemType = SemType.tryInto(sourceType); - SemType semTargetType = SemType.tryInto(targetType); - return couldInherentTypeBeDifferent(sourceSemType) && isSubTypeWithInherentType(cx, sourceVal, semTargetType); + return couldInherentTypeBeDifferent(sourceSemType) && + isSubTypeWithInherentType(cx, sourceVal, SemType.tryInto(targetType)); } /** @@ -320,8 +319,8 @@ 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.intType())) { - result = Core.union(Core.union(Builder.decimalType(), Builder.floatType()), result); + 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)); @@ -427,10 +426,9 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { if (isSimpleBasicSemType(lhsType)) { return isSimpleBasicValuesEqual(lhsValue, rhsValue); } - // Use belong to basic type Predicate basicTypePredicate = (basicType) -> Core.isSubType(cx, lhsType, basicType) && Core.isSubType(cx, rhsType, basicType); - if (basicTypePredicate.test(Builder.stringType())) { + if (basicTypePredicate.test(Builder.getStringType())) { return isEqual(lhsValue, rhsValue); } if (basicTypePredicate.test(Builder.getXmlType())) { @@ -510,10 +508,10 @@ private static boolean isSimpleBasicValuesEqual(Object v1, Object v2) { return false; } - if (Core.isSubType(cx, v1Ty, Builder.decimalType())) { + if (Core.isSubType(cx, v1Ty, Builder.getDecimalType())) { return checkDecimalExactEqual((DecimalValue) v1, (DecimalValue) v2); } - if (Core.isSubType(cx, v1Ty, Builder.intType())) { + if (Core.isSubType(cx, v1Ty, Builder.getIntType())) { Number n1 = (Number) v1; Number n2 = (Number) v2; return n1.longValue() == n2.longValue(); @@ -637,15 +635,15 @@ private static SemType widenedType(Context cx, Object value) { return bValue.widenedType(); } if (value == null) { - return Builder.nilType(); + return Builder.getNilType(); } else if (value instanceof Double) { - return Builder.floatType(); + return Builder.getFloatType(); } else if (value instanceof Number) { - return Builder.intType(); + return Builder.getIntType(); } else if (value instanceof BString) { - return Builder.stringType(); + return Builder.getStringType(); } else if (value instanceof Boolean) { - return Builder.booleanType(); + return Builder.getBooleanType(); } throw new IllegalArgumentException("Unexpected object type"); } @@ -822,20 +820,21 @@ private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set belongToSameBasicType = (basicType) -> Core.containsBasicType(lhsShape, basicType) && Core.containsBasicType(rhsShape, basicType); - if (belongToSameBasicType.test(Builder.stringType()) || belongToSameBasicType.test(Builder.booleanType())) { + if (belongToSameBasicType.test(Builder.getStringType()) || + belongToSameBasicType.test(Builder.getBooleanType())) { return lhsValue.equals(rhsValue); } - if (belongToSameBasicType.test(Builder.intType())) { + 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.floatType())) { + 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.decimalType())) { + if (belongToSameBasicType.test(Builder.getDecimalType())) { return checkDecimalEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); } if (belongToSameBasicType.test(RefValueTypeMaskHolder.REF_TYPE_MASK)) { @@ -984,7 +983,7 @@ private enum FillerValueResult { } private static FillerValueResult hasFillerValueSemType(Context cx, SemType type) { - if (Core.containsBasicType(type, Builder.nilType())) { + if (Core.containsBasicType(type, Builder.getNilType())) { return FillerValueResult.TRUE; } if (Integer.bitCount(type.all() | type.some()) > 1) { @@ -1258,7 +1257,7 @@ static boolean belongToSingleBasicTypeOrString(Type type) { Context cx = context(); SemType semType = SemType.tryInto(type); return isSingleBasicType(semType) && Core.isSubType(cx, semType, Builder.getSimpleOrStringType()) && - !Core.isSubType(cx, semType, Builder.nilType()); + !Core.isSubType(cx, semType, Builder.getNilType()); } private static boolean isSingleBasicType(SemType semType) { @@ -1273,8 +1272,9 @@ private static final class SimpleBasicTypeHolder { static final SemType SIMPLE_BASIC_TYPE = createSimpleBasicType(); private static SemType createSimpleBasicType() { - return Stream.of(Builder.nilType(), Builder.booleanType(), Builder.intType(), Builder.floatType(), - Builder.decimalType()).reduce(Builder.neverType(), Core::union); + return Stream.of(Builder.getNilType(), Builder.getBooleanType(), Builder.getIntType(), + Builder.getFloatType(), + Builder.getDecimalType()).reduce(Builder.getNeverType(), Core::union); } } @@ -1283,8 +1283,8 @@ private static final class NumericTypeHolder { static final SemType NUMERIC_TYPE = createNumericType(); private static SemType createNumericType() { - return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType()) - .reduce(Builder.neverType(), Core::union); + return Stream.of(Builder.getIntType(), Builder.getFloatType(), Builder.getDecimalType()) + .reduce(Builder.getNeverType(), Core::union); } } @@ -1294,12 +1294,12 @@ private static final class InherentlyImmutableTypeHolder { static final SemType INHERENTLY_IMMUTABLE_TYPE = createInherentlyImmutableType(); private static SemType createInherentlyImmutableType() { - return Stream.of(SimpleBasicTypeHolder.SIMPLE_BASIC_TYPE, Builder.stringType(), Builder.getErrorType(), + return Stream.of(SimpleBasicTypeHolder.SIMPLE_BASIC_TYPE, Builder.getStringType(), Builder.getErrorType(), Builder.getFunctionType(), Builder.getTypeDescType(), Builder.getHandleType(), Builder.getXmlTextType(), Builder.getXmlNeverType(), Builder.getRegexType()) - .reduce(Builder.neverType(), Core::union); + .reduce(Builder.getNeverType(), Core::union); } } @@ -1308,9 +1308,10 @@ private static final class RefValueTypeMaskHolder { static final SemType REF_TYPE_MASK = createRefValueMask(); private static SemType createRefValueMask() { - return Stream.of(Builder.getXmlType(), Builder.getMappingType(), Builder.listType(), Builder.getErrorType(), + return Stream.of(Builder.getXmlType(), Builder.getMappingType(), Builder.getListType(), + Builder.getErrorType(), Builder.getTableType(), Builder.getRegexType()) - .reduce(Builder.neverType(), Core::union); + .reduce(Builder.getNeverType(), Core::union); } } @@ -1319,9 +1320,10 @@ private static final class ConvertibleCastMaskHolder { private static final SemType CONVERTIBLE_CAST_MASK = createConvertibleCastMask(); private static SemType createConvertibleCastMask() { - return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), - Builder.booleanType()) - .reduce(Builder.neverType(), Core::union); + return Stream.of(Builder.getIntType(), Builder.getFloatType(), Builder.getDecimalType(), + Builder.getStringType(), + Builder.getBooleanType()) + .reduce(Builder.getNeverType(), Core::union); } } @@ -1331,9 +1333,10 @@ private static final class TopTypesWithFillValueMaskHolder { static final SemType TOP_TYPES_WITH_ALWAYS_FILLING = createTopTypesWithFillerValues(); private static SemType createTopTypesWithFillerValues() { - return Stream.of(Builder.intType(), Builder.floatType(), Builder.decimalType(), Builder.stringType(), - Builder.booleanType(), Builder.nilType(), Builder.getTableType(), Builder.getMappingType(), - Builder.listType()).reduce(Builder.neverType(), Core::union); + return Stream.of(Builder.getIntType(), Builder.getFloatType(), Builder.getDecimalType(), + Builder.getStringType(), + Builder.getBooleanType(), Builder.getNilType(), Builder.getTableType(), Builder.getMappingType(), + Builder.getListType()).reduce(Builder.getNeverType(), Core::union); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java index 7109627c2f50..5b7ee13ad482 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeConverter.java @@ -141,7 +141,7 @@ public static Object convertValues(Type targetType, Object inputValue) { private static Object castValueToInt(SemType targetType, Object inputValue) { Context cx = TypeChecker.context(); - assert Core.isSubType(cx, targetType, Builder.intType()); + assert Core.isSubType(cx, targetType, Builder.getIntType()); if (targetType instanceof BByteType) { return anyToByteCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); @@ -176,22 +176,22 @@ public static Object castValues(Type targetType, Object inputValue) { static Object castValuesInner(SemType targetType, Object inputValue, Supplier errorSupplier) { Context cx = TypeChecker.context(); - if (Core.isSubType(cx, targetType, Builder.intType())) { + if (Core.isSubType(cx, targetType, Builder.getIntType())) { return castValueToInt(targetType, inputValue); } - if (Core.isSubType(cx, targetType, Builder.decimalType())) { + if (Core.isSubType(cx, targetType, Builder.getDecimalType())) { return anyToDecimalCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_DECIMAL)); } - if (Core.isSubType(cx, targetType, Builder.floatType())) { + if (Core.isSubType(cx, targetType, Builder.getFloatType())) { return anyToFloatCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_FLOAT)); } - if (Core.isSubType(cx, targetType, Builder.stringType())) { + if (Core.isSubType(cx, targetType, Builder.getStringType())) { return anyToStringCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_STRING)); } - if (Core.isSubType(cx, targetType, Builder.booleanType())) { + if (Core.isSubType(cx, targetType, Builder.getBooleanType())) { return anyToBooleanCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BOOLEAN)); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java index 248c851a1f4a..9956db4a6320 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ValueConverter.java @@ -186,7 +186,7 @@ private static Object xmlSequenceHack(Object value, Type targetType) { for (BXml child : xmlSequence.getChildrenList()) { SemType childType = SemType.tryInto(child.getType()); boolean isReadonly = - Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.readonlyType()); + Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.getReadonlyType()); if (isReadonly) { list.add((BXml) CloneUtils.cloneReadOnly(child)); } else { 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 2a1b83644531..7d9bd11eaf5f 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 @@ -136,7 +136,7 @@ public void setIntersectionType(IntersectionType intersectionType) { private static SemType pickSemType(boolean readonly) { SemType semType = Builder.getAnyType(); if (readonly) { - semType = Core.intersect(semType, Builder.readonlyType()); + 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 1672681216d2..3abe79d5ca88 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 @@ -35,6 +35,7 @@ * @since 0.995.0 */ public class BAnydataType extends BUnionType implements AnydataType { + /** * Create a {@code BAnydataType} which represents the anydata type. * @@ -89,12 +90,14 @@ public String toString() { return super.toString(); } - // TODO: this type don't have mutable parts so this should be a immutable semtype + // 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.readonlyType()); + 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 8e43ae9fcb14..9e6f40e1b88a 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 @@ -36,7 +36,7 @@ import java.util.Optional; -import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +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; @@ -243,7 +243,7 @@ private SemType getSemTypePart(Env env, ListDefinition defn, int size, SemType e return defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, elementType, mut); } else { SemType[] initial = {elementType}; - return defn.defineListTypeWrapped(env, initial, size, neverType(), mut); + return defn.defineListTypeWrapped(env, initial, size, getNeverType(), mut); } } @@ -305,7 +305,7 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractAr memberTypes[i] = memberType.get(); } CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CELL_MUT_LIMITED; - SemType semType = ld.defineListTypeWrapped(cx.env, memberTypes, memberTypes.length, neverType(), mut); + 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 964f3f2f35a8..6abf3ae1a2e8 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 @@ -48,7 +48,7 @@ public final class BBooleanType extends BSemTypeWrapper new BBooleanTypeImpl(typeName, pkg), typeName, pkg, Builder.booleanType()); + this(() -> new BBooleanTypeImpl(typeName, pkg), typeName, pkg, Builder.getBooleanType()); } public static BBooleanType singletonType(boolean value) { 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 1e9ef44a4cef..7e0d0470a082 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 @@ -49,7 +49,7 @@ public final class BDecimalType extends BSemTypeWrapper new BDecimalTypeImpl(typeName, pkg), typeName, pkg, Builder.decimalType()); + this(() -> new BDecimalTypeImpl(typeName, pkg), typeName, pkg, Builder.getDecimalType()); } public static BDecimalType singletonType(BigDecimal value) { 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 4ced616ef111..ae1676fd99a7 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 @@ -22,13 +22,13 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; 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.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Optional; @@ -63,18 +63,6 @@ public BFiniteType(String typeName, String originalName, Set values, int this.originalName = originalName; } - BFiniteType cloneWithValueSpace(Set valueSpace) { - BFiniteType newFiniteType = new BFiniteType(typeName, originalName, valueSpace, typeFlags); - newFiniteType.valueSpace = valueSpace; - newFiniteType.typeFlags = typeFlags; - newFiniteType.originalName = originalName; - - newFiniteType.typeName = typeName; - newFiniteType.pkg = pkg; - - return newFiniteType; - } - @Override public V getZeroValue() { if (valueSpace.stream().anyMatch(val -> val == null || TypeChecker.getType(val).isNilable())) { @@ -223,17 +211,9 @@ public boolean equals(Object o) { @Override public SemType createSemType() { - Set bTypeValueSpace = new HashSet<>(); - SemType result = Builder.neverType(); - for (Object each : this.valueSpace) { - Optional semType = ShapeAnalyzer.inherentTypeOf(TypeChecker.context(), each); - if (semType.isPresent()) { - result = Core.union(result, semType.get()); - } else { - bTypeValueSpace.add(each); - } - } - assert bTypeValueSpace.isEmpty() : "All values must be semtypes"; - return result; + 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 73a56b33aba4..c4f2117032bd 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 @@ -44,7 +44,7 @@ public final class BFloatType extends BSemTypeWrapper * @param typeName string name of the type */ public BFloatType(String typeName, Module pkg) { - this(() -> new BFloatTypeImpl(typeName, pkg), typeName, pkg, Builder.floatType()); + this(() -> new BFloatTypeImpl(typeName, pkg), typeName, pkg, Builder.getFloatType()); } private BFloatType(Supplier bType, String typeName, Module pkg, SemType semType) { 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 ae7ba8f26702..eaffbaca69f6 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 @@ -219,7 +219,7 @@ public long getFlags() { private static SemType createIsolatedTop(Env env) { FunctionDefinition fd = new FunctionDefinition(); SemType ret = Builder.getValType(); - return fd.define(env, Builder.neverType(), ret, FunctionQualifiers.create(true, false)); + return fd.define(env, Builder.getNeverType(), ret, FunctionQualifiers.create(true, false)); } @Override @@ -241,14 +241,14 @@ public synchronized SemType createSemType() { if (restType instanceof BArrayType arrayType) { rest = getSemType(arrayType.getElementType()); } else { - rest = Builder.neverType(); + rest = Builder.getNeverType(); } SemType returnType; if (retType != null) { returnType = getSemType(retType); } else { - returnType = Builder.nilType(); + returnType = Builder.getNilType(); } ListDefinition paramListDefinition = new ListDefinition(); SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, @@ -263,11 +263,7 @@ private SemType getTopType() { return Builder.getFunctionType(); } - private record SemTypeResult(boolean hasBTypePart, SemType pureSemTypePart) { - - } - - public FunctionQualifiers getQualifiers() { + FunctionQualifiers getQualifiers() { return FunctionQualifiers.create(SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED), SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); } 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 370c2c119160..684fe9591bf4 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 @@ -105,4 +105,10 @@ public SemType createSemType() { } return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); } + + @Override + public boolean shouldCache() { + // {@code equals} depends on the type checker this is to avoid a possible infinite recursion + return false; + } } 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 c98909ad3fd4..ab811484b38c 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 @@ -56,7 +56,7 @@ public final class BIntegerType extends BSemTypeWrapper new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, pkg, TypeTags.INT_TAG, - Builder.intType()); + Builder.getIntType()); } public BIntegerType(String typeName, Module pkg, int tag) { @@ -70,7 +70,7 @@ private BIntegerType(Supplier bIntegerTypeSupplier, String typ private static SemType pickSemType(int tag) { return switch (tag) { - case TypeTags.INT_TAG -> Builder.intType(); + 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); 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 d38af85b1753..c94a70c44b23 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 @@ -234,22 +234,29 @@ public SemType createSemType() { private SemType createSemTypeInner(Function semTypeFunction) { if (constituentTypes.isEmpty()) { - return Builder.neverType(); + return Builder.getNeverType(); } SemType result = constituentTypes.stream().map(semTypeFunction).reduce(Core::intersect).orElseThrow(); - // TODO:refactor this + 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()); - result = distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(result, Core::intersect); + 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()); - result = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(result, Core::intersect); + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(Core::intersect); } - return result; + return Optional.empty(); } @Override 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 2b61df8abc5e..d998f600bfc1 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 @@ -255,7 +255,7 @@ static Optional shapeOfInner(Context cx, ShapeSupplier shapeSupplier, M } CellAtomicType.CellMutability mut = value.getType().isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; - SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.neverType(), mut); + SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.getNeverType(), mut); value.cacheShape(semType); value.resetReadonlyShapeDefinition(); return Optional.of(semType); 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 c14c38cb686c..9c6b9b8108d3 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 @@ -93,10 +93,10 @@ protected Collection allMethods() { Stream remoteMethodStream = Arrays.stream(getRemoteMethods()) .map(MethodData::fromRemoteMethod); - Stream resoucrMethodStream = + Stream resourceMethodStream = Arrays.stream(getResourceMethods()) .map(method -> MethodData.fromResourceMethod( (BResourceMethodType) method)); - return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resoucrMethodStream)).toList(); + 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 ec5357559229..70ce665c63ee 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 @@ -35,7 +35,7 @@ public final class BNeverType extends BNullType implements NeverType { * @param pkg package path */ public BNeverType(Module pkg) { - super(TypeConstants.NEVER_TNAME, pkg, Builder.neverType(), TypeTags.NEVER_TAG); + super(TypeConstants.NEVER_TNAME, pkg, Builder.getNeverType(), TypeTags.NEVER_TAG); } @Override 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 9bb1cc11affc..cfc851c9dceb 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 @@ -40,7 +40,7 @@ public sealed class BNullType extends BSemTypeWrapper i * @param pkg package path */ public BNullType(String typeName, Module pkg) { - this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, TypeTags.NULL_TAG, Builder.nilType()); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, TypeTags.NULL_TAG, Builder.getNilType()); } protected BNullType(String typeName, Module pkg, SemType semType, int tag) { 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 913a56d4fd0d..037e8fe1c006 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 @@ -242,6 +242,7 @@ public void setIntersectionType(IntersectionType intersectionType) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; this.distinctIdSupplier = null; + resetSemType(); } public BObjectType duplicate() { @@ -500,14 +501,14 @@ static MethodData fromResourceMethod(BResourceMethodType method) { if (restType instanceof BArrayType arrayType) { rest = tryInto(arrayType.getElementType()); } else { - rest = Builder.neverType(); + rest = Builder.getNeverType(); } SemType returnType; if (innerFn.getReturnType() != null) { returnType = tryInto(innerFn.getReturnType()); } else { - returnType = Builder.nilType(); + returnType = Builder.getNilType(); } ListDefinition paramListDefinition = new ListDefinition(); Env env = TypeChecker.context().env; 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 a030272e44f2..30b3b35d312b 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 @@ -84,10 +84,6 @@ public int getParamIndex() { @Override public SemType createSemType() { - Type paramValueType = this.paramValueType; - if (paramValueType instanceof BType bType) { - return bType.createSemType(); - } - return SemType.tryInto(paramValueType); + return SemType.tryInto(this.paramValueType); } } 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 710b72c25b8a..511318981172 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 @@ -33,7 +33,7 @@ public final class BReadonlyType extends BSemTypeWrapper(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, pkg, - TypeTags.READONLY_TAG, Builder.readonlyType()); + TypeTags.READONLY_TAG, Builder.getReadonlyType()); } protected static final class BReadonlyTypeImpl extends BType implements ReadonlyType { 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 2640dc96dd1e..2ac419183a97 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 @@ -57,7 +57,7 @@ import java.util.Set; import java.util.function.Function; -import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +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; @@ -267,17 +267,15 @@ private SemType createSemTypeInner(MappingDefinition md, Env env, CellMutability boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); SemType fieldType = semTypeFunction.apply(field.getFieldType()); if (!isOptional && Core.isNever(fieldType)) { - return neverType(); - } - boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - if (Core.isNever(fieldType)) { - isReadonly = true; + 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) : neverType(); + rest = restFieldType != null ? semTypeFunction.apply(restFieldType) : getNeverType(); return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); } @@ -306,7 +304,6 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm boolean takeFieldShape) { Env env = cx.env; int nFields = value.size(); - List fields = new ArrayList<>(nFields); Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); Set handledFields = new HashSet<>(nFields); MappingDefinition md; @@ -321,51 +318,41 @@ private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueIm } 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); - boolean readonlyField = fieldIsReadonly(fieldName); - boolean optionalField = fieldIsOptional(fieldName); - Optional fieldType; - if (takeFieldShape || readonlyField) { - optionalField = false; - fieldType = shapeSupplier.get(cx, fieldValue); - } else { - fieldType = Optional.of(SemType.tryInto(fieldType(fieldName))); - } - assert fieldType.isPresent(); - fields.add(new MappingDefinition.Field(fieldName, fieldType.get(), readonlyField, - optionalField)); + fields.add(fieldShape(cx, shapeSupplier, fieldName, fieldValue, takeFieldShape)); } if (!takeFieldShape) { - for (var field : getFields().values()) { - String name = field.getFieldName(); - if (handledFields.contains(name)) { - continue; - } - boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); - boolean isReadonly = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); - // TODO: refactor this - SemType fieldType = SemType.tryInto(field.getFieldType()); - if (isReadonly && isOptional && value.get(StringUtils.fromString(name)) == null) { - fieldType = Builder.undef(); - } - fields.add(new MappingDefinition.Field(field.getFieldName(), fieldType, - isReadonly, isOptional)); - } + getFields().values().stream() + .filter(field -> !handledFields.contains(field.getFieldName())) + .map(field -> fieldShapeWithoutValue(field, field.getFieldName())) + .forEach(fields::add); } - SemType semTypePart; MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); SemType rest; if (takeFieldShape) { - rest = Builder.neverType(); + rest = Builder.getNeverType(); } else { - rest = restFieldType != null ? SemType.tryInto(restFieldType) : neverType(); + rest = restFieldType != null ? SemType.tryInto(restFieldType) : getNeverType(); } - semTypePart = md.defineMappingTypeWrapped(env, fieldsArray, rest, mut()); + SemType shape = md.defineMappingTypeWrapped(env, fieldsArray, rest, mut()); value.resetReadonlyShapeDefinition(); - return semTypePart; + 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 @@ -416,4 +403,18 @@ 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 index 9fd0ecb96ba8..da72ce5704d8 100644 --- 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 @@ -31,14 +31,13 @@ import java.util.Objects; import java.util.function.Supplier; -// TODO: make this a sealed class with clearly defined extensions - /** - * 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. + * 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.10.0 + * @since 2201.11.0 */ public sealed class BSemTypeWrapper extends ImmutableSemType implements Type permits BAnyType, BBooleanType, BByteType, BDecimalType, BFloatType, BHandleType, BIntegerType, BNullType, @@ -94,7 +93,7 @@ public boolean equals(Object obj) { @Override public final boolean isNilable() { - return Core.containsBasicType(this, Builder.nilType()); + return Core.containsBasicType(this, Builder.getNilType()); } @Override @@ -147,7 +146,7 @@ public boolean isPureType() { @Override public boolean isReadOnly() { Context cx = TypeChecker.context(); - return Core.isSubType(cx, this, Builder.readonlyType()); + return Core.isSubType(cx, this, Builder.getReadonlyType()); } @Override 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 a8cd996457e1..781aed0b86d1 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 @@ -1,20 +1,20 @@ /* - * 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; @@ -49,7 +49,7 @@ public final class BStringType extends BSemTypeWrapper new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, pkg, TypeTags.STRING_TAG, - Builder.stringType()); + Builder.getStringType()); } public BStringType(String typeName, Module pkg, int tag) { @@ -68,8 +68,8 @@ public static BStringType singletonType(String value) { private static SemType pickSemtype(int tag) { return switch (tag) { - case TypeTags.STRING_TAG -> Builder.stringType(); - case TypeTags.CHAR_STRING_TAG -> Builder.charType(); + case TypeTags.STRING_TAG -> Builder.getStringType(); + case TypeTags.CHAR_STRING_TAG -> Builder.getCharType(); default -> throw new IllegalStateException("Unexpected string type tag: " + tag); }; } 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 a2f36fd52b75..dda2a3a49d8e 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 @@ -188,7 +188,7 @@ private SemType createSemTypeWithConstraint(SemType constraintType) { } if (isReadOnly()) { - semType = Core.intersect(semType, Builder.readonlyType()); + semType = Core.intersect(semType, Builder.getReadonlyType()); } return semType; } @@ -233,7 +233,7 @@ public Optional acceptedTypeOf(Context cx) { } private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { - SemType constraintType = Builder.neverType(); + SemType constraintType = Builder.getNeverType(); for (var value : table.values()) { SemType valueShape = shapeSupplier.get(cx, value).orElse(SemType.tryInto(constraint)); constraintType = Core.union(constraintType, valueShape); 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 7f743e220d83..f1c212ac8a43 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 @@ -42,7 +42,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -import static io.ballerina.runtime.api.types.semtype.Builder.neverType; +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; @@ -57,7 +57,9 @@ public class BTupleType extends BAnnotatableType implements TupleType, TypeWithS private Type restType; private int typeFlags; private final boolean readonly; - private boolean flagsPoisoned = false; + // 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; @@ -283,8 +285,12 @@ public boolean isPureType() { @Override public int getTypeFlags() { if (flagsPoisoned) { - checkAllMembers(); - flagsPoisoned = false; + synchronized (this) { + if (flagsPoisoned) { + checkAllMembers(); + flagsPoisoned = false; + } + } } return this.typeFlags; } @@ -343,11 +349,11 @@ private SemType createSemTypeInner(Env env, ListDefinition ld, Function shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Obje if (referredType instanceof TypeWithShape typeWithShape) { return typeWithShape.shapeOf(cx, shapeSupplierFn, object); } - return Optional.empty(); + return ShapeAnalyzer.shapeOf(cx, referredType); } @Override 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 27ba26ae860a..1bd0131b11f5 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 @@ -554,12 +554,12 @@ public void setIntersectionType(IntersectionType intersectionType) { @Override public SemType createSemType() { - return memberTypes.stream().map(SemType::tryInto).reduce(Builder.neverType(), Core::union); + return memberTypes.stream().map(SemType::tryInto).reduce(Builder.getNeverType(), Core::union); } @Override public Optional acceptedTypeOf(Context cx) { return Optional.of(memberTypes.stream().map(each -> ShapeAnalyzer.acceptedTypeOf(cx, each).orElseThrow()) - .reduce(Builder.neverType(), Core::union)); + .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 c43aadcd5ff3..351c194041b3 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 @@ -169,7 +169,7 @@ public SemType createSemType() { } semType = XmlUtils.xmlSequence(contraintSemtype); } - return isReadOnly() ? Core.intersect(Builder.readonlyType(), semType) : semType; + return isReadOnly() ? Core.intersect(Builder.getReadonlyType(), semType) : semType; } private SemType pickTopType() { @@ -204,7 +204,7 @@ public boolean couldInherentTypeBeDifferent() { @Override public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { - return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.readonlyType())); + return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.getReadonlyType())); } @Override @@ -243,7 +243,7 @@ private Optional readonlyShapeOf(Object object) { private static Optional getSemType(XmlValue xml, SemType baseType) { if (isReadOnly(xml)) { - return Optional.of(Core.intersect(baseType, Builder.readonlyType())); + return Optional.of(Core.intersect(baseType, Builder.getReadonlyType())); } return Optional.of(baseType); } 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 index 84d5f1151e84..771ea177cfa5 100644 --- 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 @@ -30,6 +30,11 @@ 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; 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 index d64f98145190..999986483930 100644 --- 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 @@ -24,6 +24,11 @@ import java.util.Optional; +/** + * Function that can be used to get the shape of a value. + * + * @since 2201.11.0 + */ @FunctionalInterface public interface ShapeSupplier { 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 index 32e8d4243751..3be380e83747 100644 --- 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 @@ -8,6 +8,8 @@ /** * 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 { 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 index 15b3623176b4..259062fc6896 100644 --- 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 @@ -29,6 +29,8 @@ * 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 { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java index d7e95aa12494..104ed50d84ba 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java @@ -20,11 +20,12 @@ package io.ballerina.runtime.internal.types.semtype; /** - * 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 + * 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 2201.10.0 + * @since 2201.11.0 */ public enum AllOrNothing implements SubTypeData { ALL, 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 index 6cba94c8d23b..a93adceb286b 100644 --- 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 @@ -22,9 +22,9 @@ import io.ballerina.runtime.api.types.semtype.SubType; /** - * Runtime representation of BooleanSubType. + * Runtime representation of Boolean Sub Type. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BBooleanSubType extends SubType { 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 index b053edf15c2f..2192eec3e530 100644 --- 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 @@ -9,6 +9,11 @@ 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 { @@ -38,7 +43,7 @@ public static BCellSubType createDelegate(SubType inner) { 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.undef()) || ty.some() != 0 || + if (Core.containsBasicType(ty, Builder.getUndefType()) || ty.some() != 0 || atomicType.mut() != CellAtomicType.CellMutability.CELL_MUT_LIMITED) { return new BCellSubTypeImpl(bdd); } 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 index ac9850ea3da8..0335d6513051 100644 --- 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 @@ -30,14 +30,10 @@ import java.util.Objects; import java.util.function.Predicate; -// TODO: would making this a child class of say BddNode be faster than making this a delegate -// -- Problem with this is modeling type operations (union, intersect, complement) since parent must return a Cell -// as well - /** * Runtime representation of CellSubType. * - * @since 2201.10.0 + * @since 2201.11.0 */ final class BCellSubTypeImpl extends BCellSubType implements DelegatedSubType { @@ -156,7 +152,7 @@ private static SemType cellListUnion(Conjunction negList) { } private static SemType filteredCellListUnion(Conjunction negList, Predicate predicate) { - SemType negUnion = Builder.neverType(); + SemType negUnion = Builder.getNeverType(); Conjunction neg = negList; while (neg != null) { if (predicate.test(neg)) { 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 index 79beaba3d356..8166bf862624 100644 --- 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 @@ -19,6 +19,11 @@ 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; @@ -80,7 +85,7 @@ public boolean isEmpty(Context cx) { if (pos.isEmpty()) { return true; } - SemType posUnion = pos.stream().reduce(Builder.neverType(), Core::union); + SemType posUnion = pos.stream().reduce(Builder.getNeverType(), Core::union); if (neg.isEmpty()) { return Core.isEmpty(cx, posUnion); } 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 index a25f5639478f..03d2902e17db 100644 --- 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 @@ -30,7 +30,7 @@ /** * Runtime representation of DecimalSubType. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BDecimalSubType extends SubType { 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 index a8689a0753b4..5045122f9a2a 100644 --- 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 @@ -28,6 +28,11 @@ 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; 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 index 3bc9e533abe3..333e986ae9be 100644 --- 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 @@ -29,7 +29,7 @@ /** * Runtime representation of FloatSubType. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BFloatSubType extends SubType { 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 index eea854064707..cad6312b7744 100644 --- 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 @@ -30,6 +30,11 @@ 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; @@ -126,14 +131,14 @@ private static SemType functionIntersectRet(Context cx, Conjunction pos) { private static SemType functionUnionParams(Context cx, Conjunction pos) { if (pos == null) { - return Builder.neverType(); + 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.neverType(); + return Builder.getNeverType(); } return Core.union(cx.functionAtomicType(pos.atom()).qualifiers(), functionUnionQualifiers(cx, pos.next())); } 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 index 06c6efa4403e..c9389cff9671 100644 --- 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 @@ -26,6 +26,11 @@ 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; 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 index 423c7bef51d7..30e123d338fe 100644 --- 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 @@ -29,9 +29,9 @@ import static io.ballerina.runtime.api.constants.RuntimeConstants.INT_MIN_VALUE; /** - * Runtime representation of IntSubType. + * Runtime representation of a int subtype. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BIntSubType extends SubType { 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 index aa0cbda129dc..adcd59d67c89 100644 --- 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 @@ -55,7 +55,7 @@ /** * utility class for list type projection. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BListProj { @@ -64,11 +64,11 @@ private BListProj() { public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { if (t.some() == 0) { - return t == Builder.listType() ? Builder.getValType() : Builder.neverType(); + return t == Builder.getListType() ? Builder.getValType() : Builder.getNeverType(); } else { SubTypeData keyData = Core.intSubtype(k); if (isNothingSubtype(keyData)) { - return Builder.neverType(); + return Builder.getNeverType(); } return listProjBddInnerVal(cx, keyData, (Bdd) getComplexSubtypeData(t, BasicTypeCode.BT_LIST), null, null); @@ -77,7 +77,7 @@ public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { 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.neverType(); + 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), @@ -91,7 +91,7 @@ private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjuncti SemType rest; if (pos == null) { members = FixedLengthArray.empty(); - rest = Builder.getRwCellContaining(cx.env, union(Builder.getValType(), Builder.undef())); + rest = Builder.getRwCellContaining(cx.env, union(Builder.getValType(), Builder.getUndefType())); } else { // combine all the positive tuples using intersection ListAtomicType lt = cx.listAtomType(pos.atom()); @@ -113,18 +113,18 @@ private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjuncti Pair intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); if (intersected == null) { - return Builder.neverType(); + return Builder.getNeverType(); } members = intersected.first(); rest = intersected.second(); } } if (fixedArrayAnyEmpty(cx, members)) { - return Builder.neverType(); + 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.neverType()); + rest = getRoCellContaining(cx.env, Builder.getNeverType()); } } Integer[] indices = listSamples(cx, members, rest, neg); @@ -139,7 +139,7 @@ private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjuncti private static SemType listProjExcludeInnerVal(Context cx, Integer[] indices, Integer[] keyIndices, SemType[] memberTypes, int nRequired, Conjunction neg) { - SemType p = Builder.neverType(); + SemType p = Builder.getNeverType(); if (neg == null) { int len = memberTypes.length; for (int k : keyIndices) { 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 index 0bbda67e9141..bb3bbec3769a 100644 --- 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 @@ -44,7 +44,11 @@ import static io.ballerina.runtime.api.types.semtype.Core.intersectCellMemberSemTypes; import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; -// TODO: this has lot of common code with cell (and future mapping), consider refactoring (problem is createDelegate) +/** + * Runtime representation of a subtype of list type. + * + * @since 2201.11.0 + */ public class BListSubType extends SubType implements DelegatedSubType { public final Bdd inner; @@ -383,7 +387,7 @@ public static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, SemType public static SemType bddListMemberTypeInnerVal(Context cx, Bdd b, SubTypeData key, SemType accum) { if (b instanceof BddAllOrNothing allOrNothing) { - return allOrNothing.isAll() ? accum : Builder.neverType(); + return allOrNothing.isAll() ? accum : Builder.getNeverType(); } else { BddNode bddNode = (BddNode) b; return Core.union(bddListMemberTypeInnerVal(cx, bddNode.left(), key, @@ -394,7 +398,7 @@ public static SemType bddListMemberTypeInnerVal(Context cx, Bdd b, SubTypeData k } private static SemType listAtomicMemberTypeInnerVal(ListAtomicType atomic, SubTypeData key) { - return Core.diff(listAtomicMemberTypeInner(atomic, key), Builder.undef()); + return Core.diff(listAtomicMemberTypeInner(atomic, key), Builder.getUndefType()); } private static SemType listAtomicMemberTypeInner(ListAtomicType atomic, SubTypeData key) { @@ -403,7 +407,7 @@ private static SemType listAtomicMemberTypeInner(ListAtomicType atomic, SubTypeD static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, SemType rest, SubTypeData key) { if (key instanceof BIntSubType.IntSubTypeData intSubtype) { - SemType m = Builder.neverType(); + SemType m = Builder.getNeverType(); int initLen = fixedArray.initial().length; int fixedLen = fixedArray.fixedLength(); if (fixedLen != 0) { 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 index 2d97f3856831..94f344aa1e85 100644 --- 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 @@ -36,13 +36,18 @@ 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.undef()); + return diff(mappingMemberTypeInner(cx, t, k), Builder.getUndefType()); } // This computes the spec operation called "member type of K in T", @@ -50,20 +55,20 @@ public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k // 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.undef(); + return (t.all() & Builder.getMappingType().all()) != 0 ? Builder.getValType() : Builder.getUndefType(); } else { SubTypeData keyData = stringSubtype(k); if (isNothingSubtype(keyData)) { - return Builder.undef(); + return Builder.getUndefType(); } return bddMappingMemberTypeInner(cx, (Bdd) getComplexSubtypeData(t, BT_MAPPING), keyData, - Builder.inner()); + Builder.getInnerType()); } } static SemType bddMappingMemberTypeInner(Context cx, Bdd b, SubTypeData key, SemType accum) { if (b instanceof BddAllOrNothing allOrNothing) { - return allOrNothing.isAll() ? accum : Builder.neverType(); + return allOrNothing.isAll() ? accum : Builder.getNeverType(); } else { BddNode bdd = (BddNode) b; return Core.union( @@ -84,7 +89,7 @@ static SemType mappingAtomicMemberTypeInner(MappingAtomicType atomic, SubTypeDat memberType = Core.union(memberType, ty); } } - return memberType == null ? Builder.undef() : memberType; + return memberType == null ? Builder.getUndefType() : memberType; } static List mappingAtomicApplicableMemberTypesInner(MappingAtomicType atomic, SubTypeData key) { 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 index b4be2442d044..bc56809a3c66 100644 --- 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 @@ -34,6 +34,11 @@ 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; 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 index 49db4dd05f07..de6516a4086a 100644 --- 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 @@ -26,6 +26,11 @@ 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; 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 index 54094b99c6ea..c666b54ce417 100644 --- 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 @@ -26,6 +26,11 @@ 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; 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 index 6a2af0654a89..0527874f61f1 100644 --- 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 @@ -27,9 +27,9 @@ import java.util.Objects; /** - * Runtime representation of StringSubType. + * Runtime representation of subtype of string type. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class BStringSubType extends SubType { 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 index 4a482f969734..91243d618228 100644 --- 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 @@ -28,6 +28,11 @@ 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; 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 index 714f18d3b29d..b8b0e91212a3 100644 --- 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 @@ -28,6 +28,11 @@ 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; 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 index f6137905b67f..35479680eb62 100644 --- 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 @@ -26,6 +26,11 @@ 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; 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 index faee572d96c1..739117d179f5 100644 --- 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 @@ -21,6 +21,11 @@ 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; @@ -30,11 +35,15 @@ public BddMemo() { } 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 } @@ -56,9 +65,12 @@ public int hashCode() { 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); } 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 index b83a655ea081..8e75f75af7b6 100644 --- 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 @@ -32,7 +32,7 @@ * * @param ty Type "wrapped" by this cell * @param mut Mutability of the cell - * @since 2201.10.0 + * @since 2201.11.0 */ public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { 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 index d4dda91cf99d..8d057aa5e4ef 100644 --- 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 @@ -19,6 +19,11 @@ 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() { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java index 8f0de442efce..6f798ca4d07c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java @@ -20,6 +20,11 @@ import io.ballerina.runtime.api.types.semtype.SubType; +/** + * Represents the subtype implemented by BDDs. + * + * @since 2201.11.0 + */ 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 index a7831a541c12..4f8429d51f1f 100644 --- 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 @@ -25,11 +25,12 @@ 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. + * 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.10.0 + * @since 2201.11.0 */ public abstract class EnumerableSubtypeData> { 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 index 1fdc561ff569..870fae6f4e7a 100644 --- 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 @@ -31,6 +31,11 @@ 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() { @@ -41,7 +46,7 @@ public static SemType errorDetail(SemType detail) { if (data == AllOrNothing.ALL) { return Builder.getErrorType(); } else if (data == AllOrNothing.NOTHING) { - return Builder.neverType(); + return Builder.getNeverType(); } assert data instanceof 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 index 6ab864273f18..8284a390a38b 100644 --- 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 @@ -23,8 +23,15 @@ 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; 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 index 1a84adc9fa4b..349ca151de59 100644 --- 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 @@ -21,6 +21,14 @@ 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 index 200787c2c09b..bc9f8a25ca0e 100644 --- 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 @@ -27,6 +27,11 @@ 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 implements Definition { private RecAtom rec; 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 index 33e613bfa7d7..0b90e9bad41d 100644 --- 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 @@ -22,6 +22,11 @@ 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); @@ -45,10 +50,10 @@ synchronized SemType toSemType(Env env) { if (semType == null) { ListDefinition ld = new ListDefinition(); SemType[] members = { - isolated ? Builder.getBooleanConst(true) : Builder.booleanType(), - transactional ? Builder.booleanType() : Builder.getBooleanConst(false) + isolated ? Builder.getBooleanConst(true) : Builder.getBooleanType(), + transactional ? Builder.getBooleanType() : Builder.getBooleanConst(false) }; - semType = ld.defineListTypeWrapped(env, members, 2, Builder.neverType(), + semType = ld.defineListTypeWrapped(env, members, 2, Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); } return semType; 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 index 14c57d8e4bec..8c927ac0b442 100644 --- 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 @@ -27,7 +27,11 @@ import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; -// TODO: this should be part of the public API +/** + * 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]; 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 index e74d079f5c70..d63513b1b31f 100644 --- 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 @@ -26,11 +26,11 @@ import java.util.Objects; /** - * Runtime representation of SemType. + * Runtime representation of an immutable semtype. * - * @since 2201.10.0 + * @since 2201.11.0 */ -public abstract sealed class ImmutableSemType extends SemType permits BSemTypeWrapper, PureSemType { +public abstract sealed class ImmutableSemType extends SemType permits BSemTypeWrapper { private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; @@ -78,4 +78,13 @@ 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 index 22d605b2844f..5631bbc55036 100644 --- 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 @@ -21,6 +21,13 @@ 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 index d02ee2ac2b6d..bd2b310c9e1b 100644 --- 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 @@ -29,11 +29,16 @@ 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.undef; +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 implements Definition { private RecAtom rec = null; @@ -56,7 +61,8 @@ public SemType defineListTypeWrapped(Env env, SemType[] initial, int fixedLength for (int i = 0; i < initial.length; i++) { initialCells[i] = Builder.getCellContaining(env, initial[i], mut); } - SemType restCell = Builder.getCellContaining(env, union(rest, undef()), isNever(rest) ? CELL_MUT_NONE : mut); + SemType restCell = + Builder.getCellContaining(env, union(rest, getUndefType()), isNever(rest) ? CELL_MUT_NONE : mut); return define(env, initialCells, fixedLength, restCell); } 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 index d7e6032a2528..9ad1916a6637 100644 --- 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 @@ -32,8 +32,20 @@ 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); 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 index 45d9562b36d2..182c9b9f9bd3 100644 --- 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 @@ -33,10 +33,15 @@ 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.undef; +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 implements Definition { private RecAtom rec = null; @@ -68,7 +73,7 @@ public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, C BCellField cellField = BCellField.from(env, field, mut); cellFields[i] = cellField; } - SemType restCell = Builder.getCellContaining(env, union(rest, undef()), + SemType restCell = Builder.getCellContaining(env, union(rest, getUndefType()), isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); return define(env, cellFields, restCell); } @@ -106,7 +111,7 @@ 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, undef()) : type, + 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 index 5b10766a20ea..19a6938b9d68 100644 --- 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 @@ -23,6 +23,17 @@ 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 { 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 index 01b223b66460..2059cb237766 100644 --- 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 @@ -21,6 +21,12 @@ 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(); 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 index 9591f9323ecf..2d2ee5f92623 100644 --- 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 @@ -36,6 +36,16 @@ 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 implements Definition { private final MappingDefinition mappingDefinition = new MappingDefinition(); @@ -64,16 +74,18 @@ private SemType objectContaining(SemType mappingType) { 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.readonlyType() : Builder.getValType(), + new MappingDefinition.Field( + "value", + readonly ? Builder.getReadonlyType() : Builder.getValType(), readonly, false), - Member.Kind.Field.field(), Member.Visibility.ALL}, Builder.neverType(), + 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.neverType(), + Member.Kind.Method.field(), Member.Visibility.ALL}, Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); return Builder.getCellContaining(env, union(fieldMemberType, methodMemberType), mut); } @@ -82,7 +94,7 @@ private static MappingDefinition.Field memberField(Env env, Member member, boole 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.neverType(), + member.kind().field(), member.visibility().field()}, Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); return new MappingDefinition.Field(member.name(), semtype, immutableObject | member.immutable(), false); } 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 index 31e8e1d3fe80..33d52a1816a9 100644 --- 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 @@ -26,16 +26,25 @@ 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.booleanType(), + 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.neverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); + Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); return new MappingDefinition.Field("$qualifiers", ty, true, false); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java deleted file mode 100644 index 92b794b0c19e..000000000000 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/PureSemType.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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; - -/** - * Represent types that conform only to {@code SemType} APIs. - * - * @since 2201.10.0 - */ -public final class PureSemType extends ImmutableSemType { - - public PureSemType(int all, int some, SubType[] subTypeData) { - super(all, some, subTypeData); - } - - public PureSemType(int all) { - super(all); - } -} 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 index 3809940eb105..39c4264ba8cc 100644 --- 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 @@ -22,6 +22,11 @@ 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() { 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 index 86bdcc2fe854..7bad30e0127b 100644 --- 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 @@ -44,7 +44,7 @@ /** * Collection of utility function on {@code SemType}. * - * @since 2201.10.0 + * @since 2201.11.0 */ public final class SemTypeHelper { 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 index 80928835f5cf..3d879c011238 100644 --- 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 @@ -29,6 +29,12 @@ 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 implements Definition { private final ListDefinition listDefinition = new ListDefinition(); @@ -43,7 +49,7 @@ public SemType define(Env env, SemType valueType, SemType completionType) { return Builder.getStreamType(); } SemType tuple = listDefinition.defineListTypeWrapped(env, new SemType[]{valueType, completionType}, 2, - Builder.neverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); return streamContaining(tuple); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java index d55a20832818..a418583d10af 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java @@ -21,9 +21,8 @@ /** * Marker interface for SubTypeData. * - * @since 2201.10.0 + * @since 2201.11.0 */ -// TODO: move this to api 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 index 71ac6c9b31c4..d56e4bd33899 100644 --- 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 @@ -20,6 +20,16 @@ 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 { 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 index a79e346495b3..f061b012896a 100644 --- 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 @@ -27,7 +27,7 @@ /** * Iteration implementation of `SubtypePairIterator`. * - * @since 2201.10.0 + * @since 2201.11.0 */ final class SubtypePairIterator implements Iterator { 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 index 07100a7b5ba6..95844611c535 100644 --- 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 @@ -25,7 +25,7 @@ /** * Implements the iterable for `SubtypePairIteratorImpl`. * - * @since 2201.10.0 + * @since 2201.11.0 */ public class SubtypePairs implements Iterable { 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 index 9495616f9ec2..158259252366 100644 --- 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 @@ -33,6 +33,11 @@ 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]; @@ -60,10 +65,10 @@ private static SemType tableContainingKeySpecifierInner(String[] fieldNames, Con SemType normalizedKs = new ListDefinition().defineListTypeWrapped(cx.env, fieldNameSingletons, fieldNameSingletons.length, - Builder.neverType(), CELL_MUT_NONE); + Builder.getNeverType(), CELL_MUT_NONE); SemType normalizedKc = fieldNames.length > 1 ? new ListDefinition().defineListTypeWrapped(cx.env, fieldTypes, - fieldTypes.length, Builder.neverType(), CELL_MUT_NONE) : fieldTypes[0]; + fieldTypes.length, Builder.getNeverType(), CELL_MUT_NONE) : fieldTypes[0]; return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs, cellMutLimited); } @@ -112,7 +117,7 @@ private static SemType tableContaining(Env env, SemType tableConstraint, SemType ListDefinition listDef = new ListDefinition(); SemType tupleType = listDef.defineListTypeWrapped(env, new SemType[]{typeParamArray, normalizedKc, normalizedKs}, 3, - Builder.neverType(), + 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 index 83725da486d2..4bf7b6a78406 100644 --- 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 @@ -28,6 +28,11 @@ 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]; 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 index f1cbf83790c2..f05813ead001 100644 --- 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 @@ -32,7 +32,11 @@ import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; -// TODO: this should be a part of the public API +/** + * Utility class for creating semtypes of XML type. + * + * @since 2201.11.0 + */ public final class XmlUtils { public static final int XML_PRIMITIVE_NEVER = 1; @@ -73,7 +77,7 @@ private static SemType createXmlSingleton(int primitive) { private static SemType createXmlSemtype(SubTypeData xmlSubtype) { if (xmlSubtype instanceof AllOrNothing) { - return xmlSubtype == AllOrNothing.ALL ? Builder.getXmlType() : Builder.neverType(); + 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); 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 c3a7bf979cdd..6de01ea4ee31 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 @@ -493,9 +493,4 @@ public static DecimalValue valueOfJ(BigDecimal value) { public Optional inherentTypeOf(Context cx) { return Optional.of(Builder.getDecimalConst(value)); } - - @Override - public SemType widenedType() { - return Builder.decimalType(); - } } 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 a7d0b0c06428..6b5a72d3f13d 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,7 +20,6 @@ import io.ballerina.runtime.api.async.StrandMetadata; import io.ballerina.runtime.api.constants.RuntimeConstants; 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.BFunctionPointer; @@ -133,11 +132,6 @@ public String toString() { return RuntimeConstants.EMPTY; } - @Override - public SemType widenedType() { - return Builder.getFunctionType(); - } - @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/RecursiveValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java index d8f478a8f0ae..ac6270fe252c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RecursiveValue.java @@ -6,6 +6,8 @@ * Every value that can contain a recursive reference should implement this interface. * * @param Type of the definition + * + * @since 2201.11.0 */ interface RecursiveValue { 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 index a5cff74df24f..fba49fa5c9e2 100644 --- 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 @@ -27,14 +27,13 @@ import io.ballerina.runtime.internal.types.semtype.ListDefinition; import org.testng.annotations.Test; -// These are temporary sanity checks until we have actual types using cell types are implemented public class CoreTests { @Test public void testCellTypes() { Env env = Env.getInstance(); Context cx = Context.from(env); - SemType intTy = Builder.intType(); + 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); @@ -46,7 +45,7 @@ public void testCellTypes() { @Test public void testCellTypeCaching() { Env env = Env.getInstance(); - SemType intTy = Builder.intType(); + 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; @@ -55,7 +54,7 @@ public void testCellTypeCaching() { @Test public void testSimpleList() { Env env = Env.getInstance(); - SemType intTy = Builder.intType(); + SemType intTy = Builder.getIntType(); // int[] ListDefinition ld = new ListDefinition(); SemType intListTy = @@ -66,7 +65,7 @@ public void testSimpleList() { ListDefinition ld1 = new ListDefinition(); SemType[] members = {intTy}; SemType intListTy1 = - ld1.defineListTypeWrapped(env, members, 1, Builder.neverType(), + ld1.defineListTypeWrapped(env, members, 1, Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); Context cx = Context.from(env); diff --git a/bvm/ballerina-runtime/src/test/resources/testng.xml b/bvm/ballerina-runtime/src/test/resources/testng.xml index ea13455011ff..605dbab4dee3 100644 --- a/bvm/ballerina-runtime/src/test/resources/testng.xml +++ b/bvm/ballerina-runtime/src/test/resources/testng.xml @@ -20,7 +20,7 @@ - + diff --git a/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/Dependencies.toml b/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/Dependencies.toml deleted file mode 100644 index f196dcee1657..000000000000 --- a/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/Dependencies.toml +++ /dev/null @@ -1,18 +0,0 @@ -# AUTO-GENERATED FILE. DO NOT MODIFY. - -# This file is auto-generated by Ballerina for managing dependency versions. -# It should not be modified by hand. - -[ballerina] -dependencies-toml-version = "2" -distribution-version = "2201.10.0-SNAPSHOT" - -[[package]] -org = "testorg" -name = "function_invocation" -version = "1.0.0" -modules = [ - {org = "testorg", packageName = "function_invocation", moduleName = "function_invocation"}, - {org = "testorg", packageName = "function_invocation", moduleName = "function_invocation.moduleA"} -] - 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 index ae0357b51381..eefbe6cfcebd 100644 --- 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 @@ -172,7 +172,7 @@ private SemType resolveStreamTypeDesc(TypeTestContext cx, Map cx, Map cx, Map cx, Map cx, BLangTypeDefinition defn, int depth, BLangType returnTypeNode) { if (returnTypeNode == null) { - return Builder.nilType(); + return Builder.getNilType(); } SemType innerType; // Dependently typed function are quite rare so doing it via exception handling should be faster than actually @@ -389,8 +389,8 @@ private SemType resolveReturnType(TypeTestContext cx, } ListDefinition ld = new ListDefinition(); return ld.defineListTypeWrapped((Env) cx.getInnerEnv(), - new SemType[]{!isDependentlyType ? Builder.booleanType() : Builder.getBooleanConst(true), - innerType}, 2, Builder.neverType(), + new SemType[]{!isDependentlyType ? Builder.getBooleanType() : Builder.getBooleanConst(true), + innerType}, 2, Builder.getNeverType(), CELL_MUT_LIMITED); } @@ -438,7 +438,7 @@ private SemType resolveRecordTypeDesc(TypeTestContext cx, Map cx, Map cx, ListDefinit Env env = (Env) cx.getInnerEnv(); if (size != -1) { SemType[] members = {eType}; - return ld.defineListTypeWrapped(env, members, Math.abs(size), Builder.neverType(), CELL_MUT_LIMITED); + 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); } @@ -550,12 +550,12 @@ private static SemType resolveListInner(TypeTestContext cx, ListDefinit private SemType resolveSingletonType(BLangFiniteTypeNode td) { return td.valueSpace.stream().map(each -> (BLangLiteral) each) .map(literal -> resolveSingletonType(literal.value, literal.getDeterminedType().getKind()).get()) - .reduce(Builder.neverType(), Core::union); + .reduce(Builder.getNeverType(), Core::union); } private Optional resolveSingletonType(Object value, TypeKind targetTypeKind) { return switch (targetTypeKind) { - case NIL -> Optional.of(Builder.nilType()); + case NIL -> Optional.of(Builder.getNilType()); case BOOLEAN -> Optional.of(Builder.getBooleanConst((Boolean) value)); case INT, BYTE -> { assert !(value instanceof Byte); @@ -609,7 +609,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedTyp if (td.pkgAlias.value.equals("int")) { return resolveIntSubtype(name); } else if (td.pkgAlias.value.equals("string") && name.equals("Char")) { - return Builder.charType(); + return Builder.getCharType(); } else if (td.pkgAlias.value.equals("xml")) { return resolveXmlSubType(name); } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { @@ -688,7 +688,7 @@ private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTy private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { return switch (td.typeKind) { - case NEVER -> Builder.neverType(); + case NEVER -> Builder.getNeverType(); case XML -> Builder.getXmlType(); case FUTURE -> Builder.getFutureType(); // TODO: implement json type @@ -699,14 +699,14 @@ private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { return switch (td.typeKind) { - case NIL -> Builder.nilType(); - case BOOLEAN -> Builder.booleanType(); + case NIL -> Builder.getNilType(); + case BOOLEAN -> Builder.getBooleanType(); case BYTE -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); - case INT -> Builder.intType(); - case FLOAT -> Builder.floatType(); - case DECIMAL -> Builder.decimalType(); - case STRING -> Builder.stringType(); - case READONLY -> Builder.readonlyType(); + 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(); 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 index 1b35b955e4de..cd2d6e0671ae 100644 --- 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 @@ -52,7 +52,7 @@ public boolean isSubtypeSimple(SemType t1, SemType t2) { @Override public boolean isListType(SemType t) { - return Core.isSubtypeSimple(t, Builder.listType()); + return Core.isSubtypeSimple(t, Builder.getListType()); } @Override 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 4462eb0cc2fa..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 @@ -137,26 +137,18 @@ public void testConvertRecordToRecordWithCyclicValueReferences() { "'Manager' value has cyclic reference"); } - @Test(description = "Test converting record to map having cyclic reference.", enabled = false) + @Test(description = "Test converting record to map having cyclic reference.") 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.", enabled = false) + @Test(description = "Test converting record to json having cyclic reference.") 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/query/XMLQueryExpressionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java index 52b93920e74a..6fe2729ab2d9 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/XMLQueryExpressionTest.java @@ -112,12 +112,6 @@ public void testSimpleQueryExprForXML4() { BRunUtil.invoke(result, "testSimpleQueryExprForXML4"); } - @Test - public void test() { - Object restult = BRunUtil.invoke(result, "simpleQueryExprForXML5"); - assert restult == null; - } - @Test(description = "Test simple query expression with limit clause for XMLs") public void testQueryExprWithLimitForXML() { Object returnValues = BRunUtil.invoke(result, "testQueryExprWithLimitForXML"); 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 06625a4a9df2..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 @@ -122,18 +122,6 @@ function testInferringForReadOnly() { assertEquality("modification not allowed on readonly value", err.detail()["message"]); } -function test() { - FooListInfer & readonly foo = { - s: "May", - i: 20 - }; - boolean b = true; - readonly rd2 = [1, [b, false], foo, foo]; - - assertEquality(true, rd2 is [int, [boolean, boolean], FooListInfer, FooListInfer & readonly] & readonly); - assertEquality(false, rd2 is [int, [boolean, boolean], object {} & readonly, FooListInfer & readonly] & readonly); -} - function testInferringForReadOnlyInUnion() { FooListInfer & readonly foo = { s: "May", 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 37e457ff06e6..9d7f63f1add7 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 @@ -554,11 +554,6 @@ public function testSubtypingWithDependentlyTypedMethods() { assert(false, new Garply() is Grault); } -function test() { - Baz baz = new; - assert(false, baz is Bar); -} - function getWithDefaultableParams(int|string x, int|string y = 1, typedesc z = int) returns z = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", @@ -823,7 +818,7 @@ function testDependentlyTypedFunctionWithIncludedRecordParam() { int p6 = cl->post(); assert(0, p6); - + string p7 = cl.calculate(0, mediaType = "application/json", header = "active", targetType = string); assert("application/json active0", p7); From 5dfc0a43733e255fd32c92b31bc37d35a5dc3ff4 Mon Sep 17 00:00:00 2001 From: Heshan Padmasiri Date: Sun, 13 Oct 2024 12:28:14 +0530 Subject: [PATCH 178/178] Fix Dependently typed functions --- .../runtime/internal/types/BArrayType.java | 6 +++ .../runtime/internal/types/BErrorType.java | 7 ++++ .../runtime/internal/types/BFunctionType.java | 38 +++++++++++++++---- .../runtime/internal/types/BFutureType.java | 7 ++++ .../internal/types/BIntersectionType.java | 7 ++++ .../runtime/internal/types/BMapType.java | 6 +++ .../runtime/internal/types/BObjectType.java | 6 +++ .../internal/types/BParameterizedType.java | 7 ++++ .../runtime/internal/types/BRecordType.java | 6 +++ .../internal/types/BSemTypeWrapper.java | 13 ++++++- .../runtime/internal/types/BStreamType.java | 9 +++++ .../runtime/internal/types/BTableType.java | 6 +++ .../runtime/internal/types/BTupleType.java | 7 ++++ .../runtime/internal/types/BType.java | 20 +++++++++- .../internal/types/BTypeReferenceType.java | 6 +++ .../runtime/internal/types/BTypedescType.java | 8 ++++ .../runtime/internal/types/BUnionType.java | 7 ++++ .../internal/types/MayBeDependentType.java | 34 +++++++++++++++++ .../internal/types/TypeWithAcceptedType.java | 19 ++++++++++ .../DependentlyTypedFunctionsTest.java | 2 +- .../dependently_typed_functions_test.bal | 10 ++--- 21 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java 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 9e6f40e1b88a..0b431e1a63ee 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 @@ -35,6 +35,7 @@ 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; @@ -253,6 +254,11 @@ public void resetSemType() { 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()) { 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 6c1b9dcf4b84..3f1ca277a18e 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 @@ -35,6 +35,7 @@ import io.ballerina.runtime.internal.values.MapValueImpl; import java.util.Optional; +import java.util.Set; /** * {@code BErrorType} represents error type in Ballerina. @@ -139,6 +140,12 @@ public synchronized SemType createSemType() { return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); } + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return detailType instanceof MayBeDependentType mayBeDependentType && + mayBeDependentType.isDependentlyTyped(visited); + } + private boolean isTopType() { return detailType == PredefinedTypes.TYPE_DETAIL; } 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 eaffbaca69f6..e29d1ecf8bb2 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 @@ -35,6 +35,7 @@ import java.util.Arrays; import java.util.Objects; +import java.util.Set; /** * {@code {@link BFunctionType }} represents a function type in ballerina. @@ -233,7 +234,6 @@ public synchronized SemType createSemType() { FunctionDefinition fd = new FunctionDefinition(); this.defn = fd; SemType[] params = new SemType[parameters.length]; - boolean hasBType = false; for (int i = 0; i < parameters.length; i++) { params[i] = getSemType(parameters[i].type); } @@ -244,12 +244,7 @@ public synchronized SemType createSemType() { rest = Builder.getNeverType(); } - SemType returnType; - if (retType != null) { - returnType = getSemType(retType); - } else { - returnType = Builder.getNilType(); - } + SemType returnType = resolveReturnType(); ListDefinition paramListDefinition = new ListDefinition(); SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, CellAtomicType.CellMutability.CELL_MUT_NONE); @@ -281,4 +276,33 @@ public synchronized void resetSemType() { defn = null; 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 684fe9591bf4..12482f45dd7f 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 @@ -27,6 +27,8 @@ import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.types.semtype.FutureUtils; +import java.util.Set; + /** * {@code BFutureType} represents a future value in Ballerina. * @@ -111,4 +113,9 @@ public boolean shouldCache() { // {@code equals} depends on the type checker this is to avoid a possible infinite recursion 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/BIntersectionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java index c94a70c44b23..15a73dded5ab 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 @@ -38,6 +38,7 @@ 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; @@ -232,6 +233,12 @@ 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(); 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 d998f600bfc1..c4129f9de423 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 @@ -37,6 +37,7 @@ 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; @@ -272,4 +273,9 @@ public BMapType clone() { clone.defn = null; 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/BObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java index 037e8fe1c006..aedd3e9fec67 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 @@ -519,4 +519,10 @@ static MethodData fromResourceMethod(BResourceMethodType method) { 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 30b3b35d312b..29ae8b859533 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 @@ -23,6 +23,8 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.semtype.SemType; +import java.util.Set; + /** * {@code ParameterizedType} represents the parameterized type in dependently-typed functions. * @@ -86,4 +88,9 @@ public int getParamIndex() { 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/BRecordType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java index 2ac419183a97..8fe3cd10b147 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 @@ -285,6 +285,12 @@ public void resetSemType() { 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()) { 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 index da72ce5704d8..4c1f5ed85391 100644 --- 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 @@ -29,6 +29,7 @@ import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; import java.util.Objects; +import java.util.Set; import java.util.function.Supplier; /** @@ -39,7 +40,7 @@ * @param The type of the {@code BType} that is being wrapped. * @since 2201.11.0 */ -public sealed class BSemTypeWrapper extends ImmutableSemType implements Type +public sealed class BSemTypeWrapper extends ImmutableSemType implements Type, MayBeDependentType permits BAnyType, BBooleanType, BByteType, BDecimalType, BFloatType, BHandleType, BIntegerType, BNullType, BReadonlyType, BStringType { @@ -192,4 +193,14 @@ public Type getCachedImpliedType() { 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 4ca45afc78f3..73cb1fcad178 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 @@ -32,6 +32,7 @@ import io.ballerina.runtime.internal.values.StreamValue; import java.util.Objects; +import java.util.Set; /** * {@link BStreamType} represents streaming data in Ballerina. @@ -155,4 +156,12 @@ public synchronized SemType createSemType() { definition = sd; 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/BTableType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java index dda2a3a49d8e..bee1dd8d836e 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 @@ -35,6 +35,7 @@ import io.ballerina.runtime.internal.values.TableValueImpl; import java.util.Optional; +import java.util.Set; /** * {@code BTableType} represents tabular data in Ballerina. @@ -246,4 +247,9 @@ 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 f1c212ac8a43..dc4a0e38778c 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 @@ -39,6 +39,7 @@ 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; @@ -363,6 +364,12 @@ public void resetSemType() { 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()) { 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 71c69888ca00..87644c1e1873 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 @@ -30,8 +30,10 @@ 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; /** * {@code BType} represents a type in Ballerina. @@ -44,7 +46,7 @@ * @since 0.995.0 */ public abstract non-sealed class BType extends SemType - implements Type, MutableSemType, Cloneable, CacheableTypeDescriptor { + implements Type, MutableSemType, Cloneable, CacheableTypeDescriptor, MayBeDependentType { protected String typeName; protected Module pkg; @@ -309,4 +311,20 @@ public final void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean re 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 cd93f45c7dc4..c5aa770009e3 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 @@ -31,6 +31,7 @@ import java.util.Objects; import java.util.Optional; +import java.util.Set; /** * {@code TypeReferencedType} represents a type description which refers to another type. @@ -136,6 +137,11 @@ public SemType createSemType() { 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()) { 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 b3753b8de96c..e9b9c4151cca 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 @@ -32,6 +32,8 @@ 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. * @@ -101,4 +103,10 @@ public SemType createSemType() { 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 1bd0131b11f5..ca0cc3e1388f 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 @@ -557,6 +557,13 @@ 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()) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java new file mode 100644 index 000000000000..2a68601ce4bc --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java @@ -0,0 +1,34 @@ +/* + * 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 java.util.Set; + +/** + * Represents a type that may be dependently typed. + * + * @since 2201.11.0 + */ +public interface MayBeDependentType { + + boolean isDependentlyTyped(); + + boolean isDependentlyTyped(Set visited); +} 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 index 3be380e83747..6dda26b4a8d6 100644 --- 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 @@ -1,3 +1,22 @@ +/* + * 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; 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 c2ed0f76013d..37019530a19a 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 @@ -231,7 +231,7 @@ public Object[][] getFuncNames() { {"testComplexTypes"}, {"testObjectExternFunctions"}, {"testDependentlyTypedMethodsWithObjectTypeInclusion"}, - // {"testSubtypingWithDependentlyTypedMethods"}, + {"testSubtypingWithDependentlyTypedMethods"}, {"testDependentlyTypedFunctionWithDefaultableParams"}, {"testStartActionWithDependentlyTypedFunctions"}, {"testArgsForDependentlyTypedFunctionViaTupleRestArg"}, 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 9d7f63f1add7..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 @@ -534,7 +534,7 @@ public function testSubtypingWithDependentlyTypedMethods() { assert(true, bar is Baz); assert(true, qux is Bar); assert(true, bar is Qux); - assert(true, baz is Bar); + assert(false, baz is Bar); assert(false, new Quux() is Qux); assert(false, qux is Quux); @@ -548,10 +548,10 @@ public function testSubtypingWithDependentlyTypedMethods() { 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 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 =