Skip to content

Commit

Permalink
Merge pull request #43290 from ravinperera00/issue_43029
Browse files Browse the repository at this point in the history
Add checks and logic to support `sort` operation for tuples at runtime
  • Loading branch information
gimantha authored Oct 4, 2024
2 parents afc5db7 + 20e8d79 commit ddd9696
Show file tree
Hide file tree
Showing 6 changed files with 650 additions and 104 deletions.
2 changes: 1 addition & 1 deletion bvm/ballerina-runtime/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
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;
org.ballerinalang.debugadapter.runtime, io.ballerina.lang.function, io.ballerina.lang.regexp, io.ballerina.testerina.core, io.ballerina.lang.array;
exports io.ballerina.runtime.observability.metrics.noop;
exports io.ballerina.runtime.observability.tracer.noop;
exports io.ballerina.runtime.internal.regexp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,28 @@

package org.ballerinalang.langlib.array;

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.creators.ValueCreator;
import io.ballerina.runtime.api.utils.TypeUtils;
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.BError;
import io.ballerina.runtime.api.values.BFunctionPointer;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BString;
import io.ballerina.runtime.internal.ValueComparisonUtils;
import io.ballerina.runtime.internal.scheduling.Scheduler;
import io.ballerina.runtime.internal.types.BTupleType;

import java.util.HashSet;
import java.util.Set;

import static io.ballerina.runtime.api.constants.RuntimeConstants.ARRAY_LANG_LIB;
import static io.ballerina.runtime.internal.errors.ErrorReasons.INVALID_TYPE_TO_SORT;
import static io.ballerina.runtime.internal.errors.ErrorReasons.getModulePrefixedReason;
import static org.ballerinalang.langlib.array.utils.ArrayUtils.checkIsArrayOnlyOperation;
import static org.ballerinalang.langlib.array.utils.SortUtils.isOrderedType;

/**
* Native implementation of lang.array:sort((any|error)[], direction, function).
Expand All @@ -46,12 +52,16 @@ private Sort() {
}

public static BArray sort(BArray arr, Object direction, Object func) {
checkIsArrayOnlyOperation(TypeUtils.getImpliedType(arr.getType()), "sort()");
BArray sortedArray;
Type arrType = arr.getType();
BFunctionPointer<Object, Object> function = (BFunctionPointer<Object, Object>) func;

// Check if the array type is an Ordered type, otherwise a key function is mandatory
if (!isOrderedType(arrType) && function == null) {
throw ErrorCreator.createError(getModulePrefixedReason(ARRAY_LANG_LIB, INVALID_TYPE_TO_SORT),
StringUtils.fromString("valid key function required"));
}
Object[][] sortArr = new Object[arr.size()][2];
Object[][] sortArrClone = new Object[arr.size()][2];

if (function != null) {
for (int i = 0; i < arr.size(); i++) {
sortArr[i][0] = function.call(new Object[]{Scheduler.getStrand(), arr.get(i), true});
Expand All @@ -62,15 +72,21 @@ public static BArray sort(BArray arr, Object direction, Object func) {
sortArr[i][0] = sortArr[i][1] = arr.get(i);
}
}

mergesort(sortArr, sortArrClone, 0, sortArr.length - 1, direction.toString());

BArray sortedArray = ValueCreator.createArrayValue(TypeCreator.createArrayType(arr.getElementType()));

if (arrType.getTag() == TypeTags.TUPLE_TAG) {
BTupleType tupleType = (BTupleType) arrType;
Set<Type> typeList = new HashSet<>(tupleType.getTupleTypes());
if (tupleType.getRestType() != null) {
typeList.add(tupleType.getRestType());
}
sortedArray = ValueCreator.createArrayValue(TypeCreator.createArrayType(
TypeCreator.createUnionType(typeList.stream().toList())));
} else {
sortedArray = ValueCreator.createArrayValue(TypeCreator.createArrayType(arr.getElementType()));
}
for (int k = 0; k < sortArr.length; k++) {
sortedArray.add(k, sortArr[k][1]);
}

return sortedArray;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.ballerinalang.langlib.array.utils;

import io.ballerina.runtime.api.TypeTags;
import io.ballerina.runtime.api.types.Type;
import io.ballerina.runtime.api.types.UnionType;
import io.ballerina.runtime.api.utils.TypeUtils;
import io.ballerina.runtime.internal.TypeChecker;
import io.ballerina.runtime.internal.types.BArrayType;
import io.ballerina.runtime.internal.types.BFiniteType;
import io.ballerina.runtime.internal.types.BTupleType;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* A utility class containing methods needed for the sort operation on tuples and arrays.
*
* @since 2201.11.0
*/
public class SortUtils {

/**
* A private constructor to avoid code coverage warnings.
*/
private SortUtils() {};

/**
* Check if the provided type is an Ordered type.
* @param type type to be checked.
* @return true if type is Ordered, false otherwise.
*/
public static boolean isOrderedType(Type type) {
type = TypeUtils.getImpliedType(type);
switch (type.getTag()) {
case TypeTags.UNION_TAG:
UnionType unionType = (UnionType) type;
if (unionType.isCyclic()) {
return true;
}
Set<Type> memberTypes = new HashSet<>(unionType.getMemberTypes());
Type firstTypeInUnion = TypeUtils.getImpliedType(memberTypes.stream().findFirst().
orElse(memberTypes.iterator().next()));
if (firstTypeInUnion.getTag() == TypeTags.NULL_TAG) {
// Union contains only the nil type.
return true;
}
if (!isOrderedType(firstTypeInUnion)) {
return false;
}
for (Type memType : memberTypes) {
memType = TypeUtils.getImpliedType(memType);
if (!isOrderedType(memType) || isDifferentOrderedType(memType, firstTypeInUnion)) {
return false;
}
}
return true;
case TypeTags.ARRAY_TAG:
return isOrderedType(((BArrayType) type).getElementType());
case TypeTags.TUPLE_TAG:
BTupleType tupleType = (BTupleType) type;
List<Type> tupleTypes = tupleType.getTupleTypes();
if (tupleType.getRestType() != null) {
tupleTypes.add(tupleType.getRestType());
}
if (!isOrderedType(tupleTypes.get(0))) {
return false;
}
for (Type memType : tupleTypes) {
if (!isOrderedType(memType) || isDifferentOrderedType(memType, tupleTypes.get(0))) {
return false;
}
}
return true;
case TypeTags.FINITE_TYPE_TAG:
BFiniteType finiteType = (BFiniteType) type;
Set<Object> valSpace = finiteType.getValueSpace();
Type baseExprType = TypeUtils.getType(valSpace.iterator().next());
if (!checkValueSpaceHasSameType(finiteType, baseExprType)) {
return false;
}
return isOrderedType(baseExprType);
default:
return isSimpleBasicType(type.getTag());
}
}

/**
* Check if the value space of the provided finite type belongs to the value space of the given type.
* @param finiteType finite type to be checked.
* @param type type to be checked against.
* @return true if the finite type belongs to the same value space, false otherwise.
*/
public static boolean checkValueSpaceHasSameType(BFiniteType finiteType, Type type) {
Type baseType = TypeUtils.getImpliedType(type);
if (baseType.getTag() == TypeTags.FINITE_TYPE_TAG) {
return checkValueSpaceHasSameType((BFiniteType) baseType,
TypeUtils.getType(finiteType.getValueSpace().iterator().next()));
}
for (Object expr : finiteType.getValueSpace()) {
if (isDifferentOrderedType(TypeUtils.getType(expr), baseType)) {
return false;
}
}
return true;
}

/**
* Check whether a given type is different to a target type.
* @param source type to check.
* @param target type to compare with.
* @return true if the source type does not belong to the target type, false otherwise.
*/
public static boolean isDifferentOrderedType(Type source, Type target) {
source = TypeUtils.getImpliedType(source);
target = TypeUtils.getImpliedType(target);
if (source.getTag() == TypeTags.NULL_TAG || target.getTag() == TypeTags.NULL_TAG) {
return false;
}
return !TypeChecker.checkIsType(source, target);
}

/**
* Check whether the given type tag belongs to a simple basic type.
* @param tag type tag to check.
* @return true if the tag belongs to a simple basic type, false otherwise.
*/
public static boolean isSimpleBasicType(int tag) {
return switch (tag) {
case TypeTags.BYTE_TAG, TypeTags.FLOAT_TAG, TypeTags.DECIMAL_TAG, TypeTags.BOOLEAN_TAG, TypeTags.NULL_TAG ->
true;
default -> tag >= TypeTags.INT_TAG && tag <= TypeTags.CHAR_STRING_TAG;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,8 @@ public Object[] testFunctions() {
"testSort8",
"testSort9",
"testSort10",
"testSort11",
"testSortNegative",
"testReadOnlyArrayFilter",
"testTupleFilter",
"testTupleReverse",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ public void testRemove() {
BRunUtil.invoke(compileResult, "testRemove");
}

@Test(expectedExceptions = RuntimeException.class,
expectedExceptionsMessageRegExp = ".*error: \\{ballerina/lang.array\\}OperationNotSupported.*")
@Test
public void testSort() {
BRunUtil.invoke(compileResult, "testSort");
}
Expand Down
Loading

0 comments on commit ddd9696

Please sign in to comment.