Skip to content

Commit

Permalink
Merge pull request #40979 from ushirask/byte-array-literal
Browse files Browse the repository at this point in the history
Support constant byte array literal
  • Loading branch information
hasithaa authored Aug 11, 2023
2 parents 5536d2d + a6c2bfb commit 1c72d71
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -781,8 +781,9 @@ public void visit(BLangPackage pkgNode) {
annotationDesugar.initializeAnnotationMap(pkgNode);

pkgNode.constants.stream()
.filter(constant -> constant.expr.getKind() == NodeKind.LITERAL ||
.filter(constant -> (constant.expr.getKind() == NodeKind.LITERAL ||
constant.expr.getKind() == NodeKind.NUMERIC_LITERAL)
&& constant.expr.getBType().tag != TypeTags.TUPLE)
.forEach(constant -> pkgNode.typeDefinitions.add(constant.associatedTypeDefinition));

BLangBlockStmt serviceAttachments = serviceDesugar.rewriteServiceVariables(pkgNode.services, env);
Expand Down Expand Up @@ -5794,7 +5795,8 @@ public void visit(BLangForkJoin forkJoin) {

@Override
public void visit(BLangLiteral literalExpr) {
if (Types.getReferredType(literalExpr.getBType()).tag == TypeTags.ARRAY) {
int tag = Types.getReferredType(literalExpr.getBType()).tag;
if (tag == TypeTags.ARRAY || tag == TypeTags.TUPLE) {
// this is blob literal as byte array
result = rewriteBlobLiteral(literalExpr);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -944,8 +944,8 @@ public BLangNode transform(ConstantDeclarationNode constantDeclarationNode) {

// Check whether the value is a literal or a unary expression and if it is not any one of the before mentioned
// kinds it is an invalid case, so we don't need to consider it.
if (nodeKind == NodeKind.LITERAL || nodeKind == NodeKind.NUMERIC_LITERAL ||
nodeKind == NodeKind.UNARY_EXPR) {
if ((nodeKind == NodeKind.LITERAL || nodeKind == NodeKind.NUMERIC_LITERAL || nodeKind == NodeKind.UNARY_EXPR)
&& (constantNode.typeNode == null || constantNode.typeNode.getKind() != NodeKind.ARRAY_TYPE)) {
// Note - If the RHS is a literal, we need to create an anonymous type definition which can later be used
// in type definitions.h
createAnonymousTypeDefForConstantDeclaration(constantNode, pos, identifierPos);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,7 @@ private boolean isValidContextForInferredArray(BLangNode node) {
private boolean isValidVariableForInferredArray(BLangNode node) {
switch (node.getKind()) {
case LITERAL:
if (node.getBType().tag == TypeTags.ARRAY) {
if (node.getBType().tag == TypeTags.ARRAY || node.getBType().tag == TypeTags.TUPLE) {
return true;
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ public void visit(BLangLiteral literalExpr, AnalyzerData data) {
return;
}

if (literalType.tag == TypeTags.BYTE_ARRAY) {
literalType = rewriteByteArrayLiteral(literalExpr, data);
}

if (literalExpr.isFiniteContext) {
return;
}
Expand All @@ -269,6 +273,24 @@ public void visit(BLangLiteral literalExpr, AnalyzerData data) {
data.resultType = finiteType;
}

private BType rewriteByteArrayLiteral(BLangLiteral literalExpr, AnalyzerData data) {
byte[] values = types.convertToByteArray((String) literalExpr.value);

List<BType> memberTypes = new ArrayList<>();
for (byte b : values) {
memberTypes.add(getFiniteType(Byte.toUnsignedLong(b), data.constantSymbol, literalExpr.pos,
symTable.intType));
}

BType expType = Types.getReferredType(data.expType);
if (expType.tag == TypeTags.ARRAY && ((BArrayType) expType).state == BArrayState.INFERRED) {
((BArrayType) expType).size = memberTypes.size();
((BArrayType) expType).state = BArrayState.CLOSED;
}

return createNewTupleType(literalExpr.pos, memberTypes, data);
}

@Override
public void visit(BLangSimpleVarRef varRefExpr, AnalyzerData data) {
// Set error type as the actual type.
Expand Down Expand Up @@ -1092,6 +1114,7 @@ private BType checkListConstructorCompatibility(BType expType, BLangListConstruc

switch (possibleType.tag) {
case TypeTags.ARRAY:
case TypeTags.BYTE_ARRAY:
return checkArrayType((BArrayType) possibleType, listConstructor, data);
case TypeTags.TUPLE:
return checkTupleType((BTupleType) possibleType, listConstructor, data);
Expand Down Expand Up @@ -1364,6 +1387,7 @@ private BType checkExprIncompatible(BType eType, BLangExpression expr, AnalyzerD
private BType getListConstructorCompatibleNonUnionType(BType type, AnalyzerData data) {
switch (type.tag) {
case TypeTags.ARRAY:
case TypeTags.BYTE_ARRAY:
case TypeTags.TUPLE:
case TypeTags.READONLY:
case TypeTags.TYPEDESC:
Expand Down Expand Up @@ -1717,12 +1741,6 @@ private BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType,
}
}

// Byte arrays are not yet supported in constants.
if (literalExpr.getBType().tag == TypeTags.BYTE_ARRAY) {
dlog.error(literalExpr.pos, DiagnosticErrorCode.EXPRESSION_IS_NOT_A_CONSTANT_EXPRESSION);
return symTable.semanticError;
}

return literalType;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1018,8 +1018,9 @@ private boolean isAssignable(BType source, BType target, Set<TypePair> unresolve
return isFunctionTypeAssignable((BInvokableType) source, (BInvokableType) target, new HashSet<>());
}

return sourceTag == TypeTags.ARRAY && targetTag == TypeTags.ARRAY &&
isArrayTypesAssignable((BArrayType) source, target, unresolvedTypes);
return (sourceTag == TypeTags.ARRAY || sourceTag == TypeTags.BYTE_ARRAY)
&& targetTag == TypeTags.ARRAY
&& isArrayTypesAssignable((BArrayType) source, target, unresolvedTypes);
}

private boolean isMutable(BType type) {
Expand Down Expand Up @@ -1542,6 +1543,7 @@ private boolean isSelectivelyImmutableType(BType input, boolean disallowReadOnly
case TypeTags.XML_COMMENT:
case TypeTags.XML_ELEMENT:
case TypeTags.XML_PI:
case TypeTags.BYTE_ARRAY:
return true;
case TypeTags.ARRAY:
BArrayType arrayType = (BArrayType) type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public class SymbolTable {

public final BFutureType futureType = new BFutureType(TypeTags.FUTURE, nilType, null);
public final BArrayType arrayType = new BArrayType(anyType);
public final BArrayType byteArrayType = new BArrayType(byteType);
public final BArrayType arrayStringType = new BArrayType(stringType);
BVarSymbol varSymbol = new BVarSymbol(0, null, null,
noType, null, null, SymbolOrigin.VIRTUAL);
Expand Down Expand Up @@ -385,6 +386,8 @@ public BType getTypeFromTag(int tag) {
return charStringType;
case TypeTags.REGEXP:
return regExpType;
case TypeTags.BYTE_ARRAY:
return byteArrayType;
default:
return semanticError;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ private static BIntersectionType setImmutableType(Location pos, Types types,
return defineImmutableXMLType(pos, types, env, pkgId, owner, symTable, anonymousModelHelper, names,
unresolvedTypes, (BXMLType) type);
case TypeTags.ARRAY:
case TypeTags.BYTE_ARRAY:
return defineImmutableArrayType(pos, types, env, pkgId, owner, symTable, anonymousModelHelper, names,
unresolvedTypes, (BArrayType) type);
case TypeTags.TUPLE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,44 +33,30 @@ public class BByteArrayValueNegativeTest {
public void testBlobValueNegative() {
CompileResult result = BCompileUtil.compile("test-src/types/byte/byte-array-value-negative.bal");
int index = 0;
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 2, 16);
BAssertUtil.validateError(result, index++, "undefined symbol 'base1'", 2, 16);
BAssertUtil.validateError(result, index++, "invalid expr in assignment lhs", 2, 22);
BAssertUtil.validateError(result, index++, "missing semicolon token", 2, 22);
BAssertUtil.validateError(result, index++, "invalid literal for type 'other': raw templates can only " +
"be assigned to abstract subtypes of 'ballerina/lang.object:0.0.0:RawTemplate'", 2, 24);
BAssertUtil.validateError(result, index++, "missing equal token", 2, 24);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 3, 16);
BAssertUtil.validateError(result, index++, "undefined symbol 'base'", 3, 16);
BAssertUtil.validateError(result, index++, "invalid expr in assignment lhs", 3, 21);
BAssertUtil.validateError(result, index++, "missing semicolon token", 3, 21);
BAssertUtil.validateError(result, index++, "invalid literal for type 'other': raw templates can only " +
"be assigned to abstract subtypes of 'ballerina/lang.object:0.0.0:RawTemplate'", 3, 24);
BAssertUtil.validateError(result, index++, "missing equal token", 3, 24);
BAssertUtil.validateError(result, index++, "invalid base16 content in byte array literal", 4, 24);
BAssertUtil.validateError(result, index++, "invalid base16 content in byte array literal", 5, 24);
BAssertUtil.validateError(result, index++, "invalid base16 content in byte array literal", 6, 24);
BAssertUtil.validateError(result, index++, "invalid base16 content in byte array literal", 7, 24);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 8, 16);
BAssertUtil.validateError(result, index++, "missing byte array content", 8, 16);
BAssertUtil.validateError(result, index++, "missing binary operator", 8, 23);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 12, 16);
BAssertUtil.validateError(result, index++, "undefined symbol 'base6'", 12, 16);
BAssertUtil.validateError(result, index++, "invalid expr in assignment lhs", 12, 22);
BAssertUtil.validateError(result, index++, "missing semicolon token", 12, 22);
BAssertUtil.validateError(result, index++, "invalid literal for type 'other': raw templates can only " +
"be assigned to abstract subtypes of 'ballerina/lang.object:0.0.0:RawTemplate'", 12, 24);
BAssertUtil.validateError(result, index++, "missing equal token", 12, 24);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 13, 16);
BAssertUtil.validateError(result, index++, "undefined symbol 'base'", 13, 16);
BAssertUtil.validateError(result, index++, "invalid expr in assignment lhs", 13, 21);
BAssertUtil.validateError(result, index++, "missing semicolon token", 13, 21);
BAssertUtil.validateError(result, index++, "invalid literal for type 'other': raw templates can only " +
"be assigned to abstract subtypes of 'ballerina/lang.object:0.0.0:RawTemplate'", 13, 24);
BAssertUtil.validateError(result, index++, "missing equal token", 13, 24);
BAssertUtil.validateError(result, index++, "invalid base64 content in byte array literal", 14, 24);
BAssertUtil.validateError(result, index++, "invalid base64 content in byte array literal", 15, 24);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 16, 16);
BAssertUtil.validateError(result, index++, "missing byte array content", 16, 16);
BAssertUtil.validateError(result, index++, "missing binary operator", 16, 23);
BAssertUtil.validateError(result, index++, "invalid base64 content in byte array literal", 17, 24);
Expand All @@ -81,13 +67,11 @@ public void testBlobValueNegative() {
BAssertUtil.validateError(result, index++, "incompatible types: expected 'int[2]', found 'byte[3]'", 30, 16);
BAssertUtil.validateError(result, index++, "incompatible types: 'byte[2]' cannot be cast to " +
"'(byte[3] & readonly)'", 33, 16);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[]', found 'other'", 33, 16);
BAssertUtil.validateError(result, index++, "incompatible types: 'byte[3]' cannot be cast to " +
"'(int[2] & readonly)'", 34, 15);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'int[]', found 'other'", 34, 15);

BAssertUtil.validateError(result, index++, "incompatible types: 'byte[3]' cannot be cast to " +
"'(string[] & readonly)'", 35, 18);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'string[]', found 'other'", 35, 18);
BAssertUtil.validateError(result, index++, "incompatible types: expected 'byte[3]', found 'byte[2]'", 39, 17);
Assert.assertEquals(result.getErrorCount(), index);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ public void testConstantAssignmentNegative() {
"'record {| (record {| \"a\" a; |} & readonly) x; int i; |}'", 24, 55);
BAssertUtil.validateError(negativeCompileResult, i++, "missing non-defaultable required record field 'a'",
26, 14);
BAssertUtil.validateError(negativeCompileResult, i++,
"incompatible types: expected 'string[]', found '[170,187]'", 28, 20);
BAssertUtil.validateError(negativeCompileResult, i++,
"incompatible types: expected '[string,int]', found '[170,187]'", 30, 26);
BAssertUtil.validateError(negativeCompileResult, i++,
"incompatible types: expected '[170]', found '[170,187]'", 32, 19);
Assert.assertEquals(negativeCompileResult.getErrorCount(), i);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public Object[] testFunctions() {
"testTypesOfConstantMaps",
"testConstTypesInline",
"testInvalidRuntimeUpdateOfConstMaps",
"testResolvingConstValForConstantsOfUserDefinedTypes"
"testResolvingConstValForConstantsOfUserDefinedTypes",
"testConstByteArrLiteral"
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ const Foo F1 = {x: {b : "a"}, i: 1, c: 2};
const record{|X x; int i;|} F2 = {x: {b : "a"}, i: 1, c: 2};

const X F3 = {b : "b"};

const string[] Y = base16 `aabb`;

const [string, int] Z = base16 `aabb`;

const [170] Z1 = base16 `aabb`;
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,58 @@ function testResolvingConstValForConstantsOfUserDefinedTypes() {
assertEqual(q, -60);
}

const byte[] data1 = base16 `55 EE 66`;
const byte[] data2 = base64 `ABCD pqrs`;

const byte[3] data3 = base16 `55 EE 66`;
const byte[6] data4 = base64 `ABCD pqrs`;

const data5 = base16 `55 EE 66`;
const data6 = base64 `ABCD pqrs`;

const byte[*] data7 = base16 `55 EE 66`;
const byte[*] data8 = base64 `ABCD pqrs`;

const [170] data9 = base16 `aa`;
const [170, 187] data10 = base16 `aabb`;

function testConstByteArrLiteral() {
assertEqual(data1.length(), 3);
assertEqual(data2.length(), 6);
assertEqual(data1[0], 0x55);
assertEqual(data1[1], 0xEE);
assertEqual(data1[2], 0x66);
assertEqual(data2[0], 0);
assertEqual(data2[1], 16);
assertEqual(data2[2], 131);
assertEqual(data2[3], 166);
assertEqual(data2[4], 170);
assertEqual(data2[5], 236);

assertEqual(data3.length(), 3);
assertEqual(data4.length(), 6);
assertEqual(data3[1], 0xEE);
assertEqual(data4[2], 131);

assertEqual(data5.length(), 3);
assertEqual(data6.length(), 6);
assertEqual(data5[1], 0xEE);
assertEqual(data6[2], 131);

assertEqual(data5.length(), 3);
assertEqual(data6.length(), 6);
assertEqual(data5[1], 0xEE);
assertEqual(data6[2], 131);

assertEqual(data7.length(), 3);
assertEqual(data8.length(), 6);
assertEqual(data7[1], 0xEE);
assertEqual(data8[2], 131);

assertEqual(data9[0], 170);
assertEqual(data10[1], 187);
}

function assertInvalidUpdateError(error? res, string expectedDetailMessage) {
assertTrue(res is error);
error err = <error> res;
Expand Down

0 comments on commit 1c72d71

Please sign in to comment.