Skip to content

Commit

Permalink
Avoid duplicate record type creation
Browse files Browse the repository at this point in the history
  • Loading branch information
heshanpadmasiri committed Oct 6, 2024
1 parent 019797b commit ddbafa2
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -58,6 +59,8 @@
*/
public final class TypeCreator {

private static final Map<TypeIdentifier, BRecordType> registeredRecordTypes = new ConcurrentHashMap<>();

/**
* Creates a new array type with given element type.
*
Expand Down Expand Up @@ -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);
}

Expand All @@ -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<String, Field> 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);
}

Expand Down Expand Up @@ -520,4 +530,29 @@ public static FiniteType createFiniteType(String typeName, Set<Object> 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -123,6 +125,7 @@ public BRecordType(String typeName, Module pkg, long flags, Map<String, Field> f
this.fields = fields;
}
this.internalName = typeName;
TypeCreator.registerRecordType(this);
}

private Map<String, Field> getReadOnlyFields(Map<String, Field> fields) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public void testTableNegativeConfig(String tomlFileName, String[] errorMessages)
Field age = TypeCreator.createField(PredefinedTypes.TYPE_INT, "age", SymbolFlags.OPTIONAL);
Map<String, Field> 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);
Expand All @@ -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<test_module:Person> 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<test_module:PersonTPNT> 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<test_module:Person> key(name) & readonly', but found 'record'",
" 'table<test_module:PersonTPNT> 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'"
}},
Expand All @@ -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 " +
Expand All @@ -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<test_module:Person> key(name) & readonly', but found 'array'"
"of type 'table<test_module:PersonTPNT> key(name) & readonly', but found 'array'"
}},
};
}
Expand Down Expand Up @@ -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 " +
Expand Down

0 comments on commit ddbafa2

Please sign in to comment.