Skip to content

Commit

Permalink
Migrate record value put to use BString keys
Browse files Browse the repository at this point in the history
  • Loading branch information
manuranga committed Jan 29, 2020
1 parent 1d41b9b commit 9e7606e
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.ballerinalang.jvm.util.exceptions.RuntimeErrors;
import org.ballerinalang.jvm.values.ErrorValue;
import org.ballerinalang.jvm.values.MapValue;
import org.ballerinalang.jvm.values.StringValue;

import static java.lang.String.format;
import static org.ballerinalang.jvm.util.BLangConstants.MAP_LANG_LIB;
Expand All @@ -41,6 +42,56 @@
*/
public class MapUtils {

public static void handleMapStore(MapValue<StringValue, Object> mapValue, StringValue fieldName, Object value) {
BType mapType = mapValue.getType();
switch (mapType.getTag()) {
case TypeTags.MAP_TAG:
if (!TypeChecker.checkIsType(value, ((BMapType) mapType).getConstrainedType())) {
BType expType = ((BMapType) mapType).getConstrainedType();
BType valuesType = TypeChecker.getType(value);
throw BallerinaErrors.createError(getModulePrefixedReason(MAP_LANG_LIB,
INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER),
BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_MAP_INSERTION, expType,
valuesType));
}
mapValue.put(fieldName, value);
break;
case TypeTags.RECORD_TYPE_TAG:
BRecordType recType = (BRecordType) mapType;
//TODO: bstring - remove getValue
BField recField = recType.getFields().get(fieldName.getValue());
BType recFieldType;

if (recField != null) {
// If there is a corresponding field in the record, use it
recFieldType = recField.type;
} else if (recType.restFieldType != null) {
// If there isn't a corresponding field, but there is a rest field, use it
recFieldType = recType.restFieldType;
} else {
// If both of the above conditions fail, the implication is that this is an attempt to insert a
// value to a non-existent field in a closed record.
throw BallerinaErrors.createError(MAP_KEY_NOT_FOUND_ERROR,
//TODO: bstring - remove getValue after migrating error value
BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_RECORD_FIELD_ACCESS,
fieldName.getValue(), recType));
}

if (!TypeChecker.checkIsType(value, recFieldType)) {
BType valuesType = TypeChecker.getType(value);
throw BallerinaErrors.createError(getModulePrefixedReason(MAP_LANG_LIB,
INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER),
//TODO: bstring - remove getValue after migrating error value
BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_RECORD_FIELD_ADDITION,
fieldName.getValue(), recFieldType, valuesType));
}

mapValue.put(fieldName, value);
break;
}
}

@Deprecated
public static void handleMapStore(MapValue<String, Object> mapValue, String fieldName, Object value) {
BType mapType = mapValue.getType();
switch (mapType.getTag()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,9 @@ public StringValue concat(StringValue str) {
public String stringValue() {
return value;
}

@Override
public int hashCode() {
return value.hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,9 @@ public StringValue concat(StringValue str) {
public String stringValue() {
return value;
}

@Override
public int hashCode() {
return value.hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2560,6 +2560,13 @@ function nameOfBStringFunc(string nonBStringFuncName) returns string {
return nonBStringFuncName + "$bstring";
}

function conditionalBStringName(string nonBStringName, boolean useBString) returns string {
if(useBString) {
return nameOfBStringFunc(nonBStringName);
}
return nonBStringName;
}

function nameOfNonBStringFunc(string funcName) returns string {
if(isBStringFunc(funcName)) {
return funcName.substring(0, funcName.length() - 8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,12 @@ public type ObjectGenerator object {

self.createObjectInit(cw, fields, className);
self.createCallMethod(cw, attachedFuncs, className, objectType.name.value, isService);
self.createObjectGetMethod(cw, fields, className);
self.createObjectSetMethod(cw, fields, className, false);
if (IS_BSTRING) {
self.createObjectGetMethod(cw, fields, className, true);
self.createObjectSetMethod(cw, fields, className, true);
}
self.createObjectGetMethod(cw, fields, className, false);
self.createObjectSetMethod(cw, fields, className, false);
self.createLambdas(cw);

cw.visitEnd();
Expand Down Expand Up @@ -227,12 +228,20 @@ public type ObjectGenerator object {
mv.visitEnd();
}

private function createObjectGetMethod(jvm:ClassWriter cw, bir:BObjectField?[] fields, string className) {
private function createObjectGetMethod(jvm:ClassWriter cw, bir:BObjectField?[] fields, string className,
boolean useBString) {
jvm:MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get",
io:sprintf("(L%s;)L%s;", STRING_VALUE, OBJECT), (), ());
io:sprintf("(L%s;)L%s;", useBString ? I_STRING_VALUE : STRING_VALUE, OBJECT), (), ());
mv.visitCode();

int fieldNameRegIndex = 1;
if(useBString) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEINTERFACE, I_STRING_VALUE, "getValue", io:sprintf("()L%s;", STRING_VALUE) , true);
fieldNameRegIndex = 2;
mv.visitVarInsn(ASTORE, fieldNameRegIndex);
}

jvm:Label defaultCaseLabel = new jvm:Label();

// sort the fields before generating switch case
Expand All @@ -250,7 +259,8 @@ public type ObjectGenerator object {
jvm:Label targetLabel = targetLabels[i];
mv.visitLabel(targetLabel);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, field.name.value, getTypeDesc(field.typeValue));
mv.visitFieldInsn(GETFIELD, className, conditionalBStringName(field.name.value, useBString),
getTypeDesc(field.typeValue, useBString));
addBoxInsn(mv, field.typeValue);
mv.visitInsn(ARETURN);
i += 1;
Expand Down Expand Up @@ -434,7 +444,12 @@ public type ObjectGenerator object {
private function createRecordFields(jvm:ClassWriter cw, bir:BRecordField?[] fields) {
foreach var field in fields {
if (field is bir:BRecordField) {
jvm:FieldVisitor fv = cw.visitField(0, field.name.value, getTypeDesc(field.typeValue));
jvm:FieldVisitor fv;
if(IS_BSTRING) {
fv = cw.visitField(0, nameOfBStringFunc(field.name.value), getTypeDesc(field.typeValue, true));
fv.visitEnd();
}
fv = cw.visitField(0, field.name.value, getTypeDesc(field.typeValue));
fv.visitEnd();

if (self.isOptionalRecordField(field)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -701,8 +701,8 @@ type InstructionGenerator object {
io:sprintf("(L%s;L%s;L%s;)V", OBJECT, STRING_VALUE, OBJECT), false);
} else {
self.mv.visitMethodInsn(INVOKESTATIC, MAP_UTILS, "handleMapStore",
io:sprintf("(L%s;L%s;L%s;)V", MAP_VALUE, STRING_VALUE, OBJECT),
false);
io:sprintf("(L%s;L%s;L%s;)V",
MAP_VALUE, IS_BSTRING ? I_STRING_VALUE : STRING_VALUE, OBJECT), false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2560,6 +2560,14 @@ function nameOfBStringFunc(string nonBStringFuncName) returns string {
return nonBStringFuncName + "$bstring";
}

function conditionalBStringName(string nonBStringName, boolean useBString) returns string {
if(useBString) {
return nameOfBStringFunc(nonBStringName);
}
return nonBStringName;
}


function nameOfNonBStringFunc(string funcName) returns string {
if(isBStringFunc(funcName)) {
return funcName.substring(0, funcName.length() - 8);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,12 @@ public type ObjectGenerator object {

self.createObjectInit(cw, fields, className);
self.createCallMethod(cw, attachedFuncs, className, objectType.name.value, isService);
self.createObjectGetMethod(cw, fields, className);
if (IS_BSTRING) {
self.createObjectGetMethod(cw, fields, className, true);
self.createObjectSetMethod(cw, fields, className, true);
} else {
self.createObjectSetMethod(cw, fields, className, false);
self.createObjectGetMethod(cw, fields, className, false);
self.createObjectSetMethod(cw, fields, className, false);
}
self.createLambdas(cw);

Expand Down Expand Up @@ -229,12 +230,20 @@ public type ObjectGenerator object {
mv.visitEnd();
}

private function createObjectGetMethod(jvm:ClassWriter cw, bir:BObjectField?[] fields, string className) {
private function createObjectGetMethod(jvm:ClassWriter cw, bir:BObjectField?[] fields, string className,
boolean useBString) {
jvm:MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get",
io:sprintf("(L%s;)L%s;", STRING_VALUE, OBJECT), (), ());
io:sprintf("(L%s;)L%s;", useBString ? I_STRING_VALUE : STRING_VALUE, OBJECT), (), ());
mv.visitCode();

int fieldNameRegIndex = 1;
if(useBString) {
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEINTERFACE, I_STRING_VALUE, "getValue", io:sprintf("()L%s;", STRING_VALUE) , true);
fieldNameRegIndex = 2;
mv.visitVarInsn(ASTORE, fieldNameRegIndex);
}

jvm:Label defaultCaseLabel = new jvm:Label();

// sort the fields before generating switch case
Expand All @@ -252,7 +261,8 @@ public type ObjectGenerator object {
jvm:Label targetLabel = targetLabels[i];
mv.visitLabel(targetLabel);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, field.name.value, getTypeDesc(field.typeValue));
mv.visitFieldInsn(GETFIELD, className, conditionalBStringName(field.name.value, useBString),
getTypeDesc(field.typeValue, useBString));
addBoxInsn(mv, field.typeValue);
mv.visitInsn(ARETURN);
i += 1;
Expand Down Expand Up @@ -436,7 +446,12 @@ public type ObjectGenerator object {
private function createRecordFields(jvm:ClassWriter cw, bir:BRecordField?[] fields) {
foreach var field in fields {
if (field is bir:BRecordField) {
jvm:FieldVisitor fv = cw.visitField(0, field.name.value, getTypeDesc(field.typeValue));
jvm:FieldVisitor fv;
if(IS_BSTRING) {
fv = cw.visitField(0, nameOfBStringFunc(field.name.value), getTypeDesc(field.typeValue, true));
} else {
fv = cw.visitField(0, field.name.value, getTypeDesc(field.typeValue));
}
fv.visitEnd();

if (self.isOptionalRecordField(field)) {
Expand Down Expand Up @@ -523,7 +538,10 @@ public type ObjectGenerator object {

// cast key to java.lang.String
mv.visitVarInsn(ALOAD, fieldNameRegIndex);
mv.visitTypeInsn(CHECKCAST, STRING_VALUE);
mv.visitTypeInsn(CHECKCAST, IS_BSTRING ? I_STRING_VALUE : STRING_VALUE);
if (IS_BSTRING) {
mv.visitMethodInsn(INVOKEINTERFACE, I_STRING_VALUE, "getValue", io:sprintf("()L%s;", STRING_VALUE) , true);
}
mv.visitVarInsn(ASTORE, strKeyVarIndex);

// sort the fields before generating switch case
Expand All @@ -545,14 +563,13 @@ public type ObjectGenerator object {
// load the existing value to return
string fieldName = field.name.value;
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, fieldName, getTypeDesc(field.typeValue));
mv.visitFieldInsn(GETFIELD, className, conditionalBStringName(fieldName, IS_BSTRING), getTypeDesc(field.typeValue, IS_BSTRING));
addBoxInsn(mv, field.typeValue);

mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, valueRegIndex);
addUnboxInsn(mv, field.typeValue);
mv.visitFieldInsn(PUTFIELD, className, fieldName, getTypeDesc(field.typeValue));

addUnboxInsn(mv, field.typeValue, IS_BSTRING);
mv.visitFieldInsn(PUTFIELD, className, conditionalBStringName(fieldName, IS_BSTRING), getTypeDesc(field.typeValue, IS_BSTRING));
// if the field is an optional-field, then also set the isPresent flag of that field to true.
if (self.isOptionalRecordField(field)) {
mv.visitVarInsn(ALOAD, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
package org.ballerinalang.langlib.string;

import org.ballerinalang.jvm.BallerinaValues;
import org.ballerinalang.jvm.StringUtils;
import org.ballerinalang.jvm.scheduling.Strand;
import org.ballerinalang.jvm.types.BFunctionType;
import org.ballerinalang.jvm.types.BRecordType;
import org.ballerinalang.jvm.types.BUnionType;
import org.ballerinalang.jvm.values.MapValueImpl;
import org.ballerinalang.jvm.values.ObjectValue;
import org.ballerinalang.jvm.values.api.BString;
import org.ballerinalang.model.types.TypeKind;
import org.ballerinalang.natives.annotations.BallerinaFunction;
import org.ballerinalang.natives.annotations.Receiver;
Expand All @@ -47,11 +49,15 @@
isPublic = true
)
public class Next {
public static final String IS_STRING_VALUE_PROP = "ballerina.bstring";
public static final boolean USE_BSTRING = System.getProperty(IS_STRING_VALUE_PROP) != null;

//TODO: refactor hard coded values
public static Object next(Strand strand, ObjectValue m) {
StringCharacterIterator stringCharacterIterator = (StringCharacterIterator) m.getNativeData("&iterator&");
if (stringCharacterIterator == null) {
stringCharacterIterator = new StringCharacterIterator((String) m.get("m"));
String s = USE_BSTRING ? ((BString) m.get(StringUtils.fromString("m"))).getValue() : (String) m.get("m");
stringCharacterIterator = new StringCharacterIterator(s);
m.addNativeData("&iterator&", stringCharacterIterator);
}

Expand All @@ -60,7 +66,9 @@ public static Object next(Strand strand, ObjectValue m) {
stringCharacterIterator.next();
BFunctionType nextFuncType = m.getType().getAttachedFunctions()[0].type;
BRecordType recordType = (BRecordType) ((BUnionType) nextFuncType.retType).getMemberTypes().get(0);
return BallerinaValues.createRecord(new MapValueImpl<>(recordType), String.valueOf(character));
Object charAsStr = USE_BSTRING ? StringUtils.fromString(String.valueOf(character)) :
String.valueOf(character);
return BallerinaValues.createRecord(new MapValueImpl<>(recordType), charAsStr);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ public void testNonBMPStringLength() {
Assert.assertEquals(((BInteger) returns[0]).intValue(), 5);
}

@Test
public void testRecordStringValuePut() {
BValue[] returns = BRunUtil.invoke(result, "recordStringValuePut");
//TODO assert return value has BString
}

@AfterClass
public void down() {
System.clearProperty(IS_STRING_VALUE_PROP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ function nonBMPLength() returns (int) {
return smiley.length();
}

function recordStringValuePut() returns () {
string smiley = "h😀llo";
record {| string myField; |} r = {myField: smiley};
//TODO: return r
}

0 comments on commit 9e7606e

Please sign in to comment.