Skip to content

Commit

Permalink
Merge pull request #42898 from ravinperera00/issue_42835
Browse files Browse the repository at this point in the history
Add checks and logic to support `unshift()` operation for tuples at runtime
  • Loading branch information
warunalakshitha authored Jul 11, 2024
2 parents 6c996f9 + 5ec8be9 commit f3e6bec
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ protected void checkFixedLength(long length) {
@Override
protected void unshift(long index, Object[] vals) {
handleImmutableArrayValue();
validateInherentTypeOfExistingMembers((int) index, vals.length);
unshiftArray(index, vals.length, getCurrentArrayLength());
addToRefArray(vals, (int) index);
}
Expand Down Expand Up @@ -804,15 +805,16 @@ private void addToRefArray(Object[] vals, int startIndex) {
}

private void unshiftArray(long index, int unshiftByN, int arrLength) {
int lastIndex = size() + unshiftByN - 1;
int currSize = size();
int lastIndex = currSize + unshiftByN - 1;
prepareForConsecutiveMultiAdd(lastIndex, arrLength);
if (index > lastIndex) {
throw ErrorHelper.getRuntimeException(getModulePrefixedReason(ARRAY_LANG_LIB,
INDEX_OUT_OF_RANGE_ERROR_IDENTIFIER), ErrorCodes.INDEX_NUMBER_TOO_LARGE, index);
}

int i = (int) index;
System.arraycopy(this.refValues, i, this.refValues, i + unshiftByN, this.size - i);
System.arraycopy(this.refValues, i, this.refValues, i + unshiftByN, currSize - i);
}

private int getCurrentArrayLength() {
Expand Down Expand Up @@ -842,4 +844,17 @@ private void validateTupleSizeAndInherentType() {
}
}
}

private void validateInherentTypeOfExistingMembers(int index, int offset) {
Type targetType;
for (int i = index; i < this.size; i++) {
targetType = (i + offset >= this.tupleType.getTupleTypes().size()) ?
this.tupleType.getRestType() : this.tupleType.getTupleTypes().get(i + offset);
if (!TypeChecker.checkIsType(this.getRefValue(i), targetType)) {
throw ErrorHelper.getRuntimeException(getModulePrefixedReason(ARRAY_LANG_LIB,
INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER),
ErrorCodes.INCOMPATIBLE_TYPE, TypeChecker.getType(this.getRefValue(i)), targetType);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@

package org.ballerinalang.langlib.array;

import io.ballerina.runtime.api.utils.TypeUtils;
import io.ballerina.runtime.api.values.BArray;

import static org.ballerinalang.langlib.array.utils.ArrayUtils.checkIsArrayOnlyOperation;

/**
* Native implementation of lang.array:unshift((any|error)[], (any|error)...).
*
Expand All @@ -31,7 +28,6 @@
public class Unshift {

public static void unshift(BArray arr, Object... vals) {
checkIsArrayOnlyOperation(TypeUtils.getImpliedType(arr.getType()), "unshift()");
arr.unshift(vals);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ public Object[] testFunctions() {
"testLastIndexOf",
"testPush",
"testShiftOperation",
"testUnshiftOperation",
"testSort1",
"testSort2",
"testSort4",
Expand Down
83 changes: 83 additions & 0 deletions langlib/langlib-test/src/test/resources/test-src/arraylib_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,89 @@ function testShiftOnTupleWithProperConditions() {
assertValueEquality([person2], properTuple5);
}

type UnshiftType1 record {string val1; int val2;};
type UnshiftType2 record {string val1;};
function testUnshiftOperation() {
testUnshiftTuplesPositives();
testUnshiftTuplesNegatives();
}

function testUnshiftTuplesPositives() {
[int, int...] tuple1 = [];
[int, int, int...] tuple3 = [1, 3, 4, 5, 7];
[int, int...] list = [];
[UnshiftType1, UnshiftType1...] tuple4 = [{val1:"Hello", val2:67}];

tuple1.unshift(5, 6, 7, 8);
tuple3.unshift(12, 67);
foreach int i in 0 ..< 400 {
list.unshift(i);
}
tuple4.unshift({val1:"world", val2:82});

assertValueEquality([5, 6, 7, 8, 0], tuple1);
assertValueEquality([12, 67, 1, 3, 4, 5, 7], tuple3);
assertValueEquality(401, list.length());
assertValueEquality(399, list[0]);
assertValueEquality(199, list[200]);
assertValueEquality(0, list[399]);
assertValueEquality(0, list[400]);
assertValueEquality([{val1:"world", val2:82}, {val1:"Hello", val2:67}], tuple4);
}

function testUnshiftTuplesNegatives() {
[boolean, string...] tuple1 = [];
[int, boolean, string...] tuple2 = [8, false, "hello"];
[int, boolean, string...] tuple3 = [];
[UnshiftType1, UnshiftType2...] tuple4 = [{val1:"Hello", val2:67}];

var fn1 = function() {
tuple1.unshift("hello");
};

var fn2 = function() {
tuple2.unshift(false, 78, "world");
};

var fn3 = function() {
tuple3.unshift(10, false, "Hello", "World");
};

var fn4 = function() {
tuple4.unshift({val1:"world"});
};

error? res1 = trap fn1();
error? res2 = trap fn2();
error? res3 = trap fn3();
error? res4 = trap fn4();
assertTrue(res1 is error);
assertTrue(res2 is error);
assertTrue(res3 is error);
assertTrue(res4 is error);

error err1 = <error>res1;
error err2 = <error>res2;
error err3 = <error>res3;
error err4 = <error>res4;
var message1 = err1.detail()["message"];
var message2 = err2.detail()["message"];
var message3 = err3.detail()["message"];
var message4 = err4.detail()["message"];
string detailMessage1 = message1 is error ? message1.toString() : message1.toString();
string detailMessage2 = message2 is error ? message2.toString() : message2.toString();
string detailMessage3 = message3 is error ? message3.toString() : message3.toString();
string detailMessage4 = message4 is error ? message4.toString() : message4.toString();
assertValueEquality("{ballerina/lang.array}InherentTypeViolation", err1.message());
assertValueEquality("incompatible types: expected 'boolean', found 'string'", detailMessage1);
assertValueEquality("{ballerina/lang.array}InherentTypeViolation", err2.message());
assertValueEquality("incompatible types: expected 'int', found 'string'", detailMessage2);
assertValueEquality("{ballerina/lang.array}InherentTypeViolation", err3.message());
assertValueEquality("incompatible types: expected 'int', found 'string'", detailMessage3);
assertValueEquality("{ballerina/lang.array}InherentTypeViolation", err4.message());
assertValueEquality("incompatible types: expected 'UnshiftType1', found 'UnshiftType2'", detailMessage4);
}

type Student record {|
int id;
string? fname;
Expand Down

0 comments on commit f3e6bec

Please sign in to comment.