Skip to content

Commit

Permalink
Merge pull request #29792 from grainier/fix-28262-v2
Browse files Browse the repository at this point in the history
Improve runtime anonymous type resolution
  • Loading branch information
hasithaa authored Apr 16, 2021
2 parents fd33951 + fa7bb1c commit 2756823
Show file tree
Hide file tree
Showing 11 changed files with 749 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package io.ballerina.runtime.internal.values;

import io.ballerina.runtime.api.types.Type;
import io.ballerina.runtime.api.values.BError;
import io.ballerina.runtime.api.values.BObject;
import io.ballerina.runtime.api.values.BString;
Expand Down Expand Up @@ -86,4 +87,7 @@ public abstract BObject createObjectValue(String objectTypeName, Scheduler sched

public abstract BError createErrorValue(String errorTypeName, BString message, BError cause, Object details)
throws BError;

public abstract Type getAnonType(int typeHash, String typeShape) throws BError;

}
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ public class JvmConstants {
public static final String CREATE_RECORD_VALUE = "createRecordValue";
public static final String CREATE_OBJECT_VALUE = "createObjectValue";
public static final String CREATE_ERROR_VALUE = "createErrorValue";
public static final String GET_ANON_TYPE = "getAnonType";

// strand data related constants
public static final String STRAND = "strand";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,13 +455,14 @@ private void generateModuleClasses(BIRPackage module, Map<String, byte[]> jarEnt
ClassWriter cw = new BallerinaClassWriter(COMPUTE_FRAMES);
AsyncDataCollector asyncDataCollector = new AsyncDataCollector(moduleClass);
boolean isInitClass = Objects.equals(moduleClass, moduleInitClass);
JvmTypeGen jvmTypeGen = new JvmTypeGen(stringConstantsGen);
JvmTypeGen jvmTypeGen = new JvmTypeGen(stringConstantsGen, module.packageID);
JvmCastGen jvmCastGen = new JvmCastGen(symbolTable, jvmTypeGen);
LambdaGen lambdaGen = new LambdaGen(this, jvmCastGen);
if (isInitClass) {
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, moduleClass, null, VALUE_CREATOR, null);
JvmCodeGenUtil.generateDefaultConstructor(cw, VALUE_CREATOR);
jvmTypeGen.generateUserDefinedTypeFields(cw, module.typeDefs);
jvmTypeGen.generateGetAnonTypeMethod(cw, module.typeDefs, moduleInitClass);
jvmTypeGen.generateValueCreatorMethods(cw, module.typeDefs, module.packageID, moduleInitClass,
symbolTable, asyncDataCollector);
// populate global variable to class name mapping and generate them
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
import org.wso2.ballerinalang.compiler.bir.codegen.internal.BIRVarToJVMIndexMap;
import org.wso2.ballerinalang.compiler.bir.codegen.internal.ScheduleFunctionInfo;
import org.wso2.ballerinalang.compiler.bir.model.BIRNode.BIRTypeDefinition;
import org.wso2.ballerinalang.compiler.parser.BLangAnonymousModelHelper;
import org.wso2.ballerinalang.compiler.semantics.analyzer.IsAnydataUniqueVisitor;
import org.wso2.ballerinalang.compiler.semantics.analyzer.IsPureTypeUniqueVisitor;
import org.wso2.ballerinalang.compiler.semantics.analyzer.TypeHashVisitor;
import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAttachedFunction;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BObjectTypeSymbol;
Expand Down Expand Up @@ -66,6 +68,7 @@
import org.wso2.ballerinalang.util.Flags;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -91,6 +94,7 @@
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.IFNONNULL;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INSTANCEOF;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
Expand Down Expand Up @@ -133,6 +137,7 @@
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUNCTION_TYPE_IMPL;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUTURE_TYPE_IMPL;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUTURE_VALUE;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.GET_ANON_TYPE;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.HANDLE_TYPE;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.HANDLE_VALUE;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.INTEGER_TYPE;
Expand Down Expand Up @@ -195,6 +200,7 @@
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.XML_TYPE_IMPL;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.XML_VALUE;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmValueGen.NAME_HASH_COMPARATOR;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmValueGen.TYPE_HASH_COMPARATOR;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmValueGen.createDefaultCase;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmValueGen.getTypeDescClassName;
import static org.wso2.ballerinalang.compiler.bir.codegen.JvmValueGen.getTypeValueClassName;
Expand All @@ -209,11 +215,15 @@ public class JvmTypeGen {
private final IsPureTypeUniqueVisitor isPureTypeUniqueVisitor;
private final IsAnydataUniqueVisitor isAnydataUniqueVisitor;
private final JvmBStringConstantsGen stringConstantsGen;
private final TypeHashVisitor typeHashVisitor;
private final PackageID packageID;

public JvmTypeGen(JvmBStringConstantsGen stringConstantsGen) {
public JvmTypeGen(JvmBStringConstantsGen stringConstantsGen, PackageID packageID) {
this.stringConstantsGen = stringConstantsGen;
this.packageID = packageID;
isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor();
isAnydataUniqueVisitor = new IsAnydataUniqueVisitor();
typeHashVisitor = new TypeHashVisitor();
}

/**
Expand Down Expand Up @@ -501,6 +511,75 @@ static List<Label> createLabelsForEqualCheck(MethodVisitor mv, int nameRegIndex,
return targetLabels;
}

// -------------------------------------------------------
// getAnonType() generation methods
// -------------------------------------------------------

void generateGetAnonTypeMethod(ClassWriter cw, List<BIRTypeDefinition> typeDefinitions, String typeOwnerClass) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, GET_ANON_TYPE,
String.format("(IL%s;)L%s;", STRING_VALUE, TYPE), null, null);
mv.visitCode();

int hashParamRegIndex = 1;
int shapeParamRegIndex = 2;
Label defaultCaseLabel = new Label();

// filter anon types and sorts them before generating switch case.
Set<BIRTypeDefinition> typeDefSet = new TreeSet<>(TYPE_HASH_COMPARATOR);
for (BIRTypeDefinition t : typeDefinitions) {
if (t.internalName.value.contains(BLangAnonymousModelHelper.ANON_PREFIX)
|| Symbols.isFlagOn(t.type.flags, Flags.ANONYMOUS)) {
typeDefSet.add(t);
}
}

Map<String, Label> labels = createLabelsForAnonTypeHashSwitch(mv, hashParamRegIndex,
typeDefSet, defaultCaseLabel);

for (Map.Entry<String, Label> labelEntry : labels.entrySet()) {
String fieldName = labelEntry.getKey();
Label targetLabel = labelEntry.getValue();
mv.visitLabel(targetLabel);
mv.visitFieldInsn(GETSTATIC, typeOwnerClass, fieldName, String.format("L%s;", TYPE));
mv.visitInsn(ARETURN);
}

createDefaultCase(mv, defaultCaseLabel, shapeParamRegIndex, "No such type: ");
mv.visitMaxs(typeDefSet.size() + 10, typeDefSet.size() + 10);
mv.visitEnd();
}

private Map<String, Label> createLabelsForAnonTypeHashSwitch(MethodVisitor mv,
int nameRegIndex,
Set<BIRTypeDefinition> nodes,
Label defaultCaseLabel) {
mv.visitVarInsn(ILOAD, nameRegIndex);
// Create labels for the cases
Map<String, Label> labelFieldMapping = new LinkedHashMap<>();
Map<Integer, Label> labelHashMapping = new LinkedHashMap<>();
for (BIRTypeDefinition node : nodes) {
if (node != null) {
BType type = node.type;
String fieldName = getTypeFieldName(node.internalName.value);
Integer typeHash = typeHashVisitor.visit(type);
boolean fieldExists = labelFieldMapping.containsKey(fieldName);
boolean hashExists = labelHashMapping.containsKey(typeHash);
if (!fieldExists && !hashExists) {
Label label = new Label();
labelFieldMapping.put(fieldName, label);
labelHashMapping.put(typeHash, label);
} else {
assert fieldExists && hashExists; // hashing issues.
}
typeHashVisitor.reset();
}
}
int[] hashes = labelHashMapping.keySet().stream().mapToInt(Integer::intValue).toArray();
Label[] labels = labelHashMapping.values().toArray(new Label[0]);
mv.visitLookupSwitchInsn(defaultCaseLabel, hashes, labels);
return labelFieldMapping;
}

// -------------------------------------------------------
// Runtime value creation methods
// -------------------------------------------------------
Expand Down Expand Up @@ -1856,13 +1935,31 @@ private void loadTupleType(MethodVisitor mv, BTupleType bType) {
* @param bType user defined type
*/
private void loadUserDefinedType(MethodVisitor mv, BType bType) {
BTypeSymbol typeSymbol = bType.tsymbol.isTypeParamResolved ? bType.tsymbol.typeParamTSymbol : bType.tsymbol;
BType typeToLoad = bType.tsymbol.isTypeParamResolved ? typeSymbol.type : bType;
PackageID packageID = typeSymbol.pkgID;
String typeOwner = JvmCodeGenUtil.getPackageName(packageID) + MODULE_INIT_CLASS_NAME;
String fieldName = getTypeFieldName(toNameString(typeToLoad));

PackageID packageID = bType.tsymbol.pkgID;
// if name contains $anon and doesn't belong to the same package, load type using getAnonType() method.
if (!this.packageID.equals(packageID) &&
(fieldName.contains(BLangAnonymousModelHelper.ANON_PREFIX)
|| Symbols.isFlagOn(typeToLoad.flags, Flags.ANONYMOUS))) {
Integer hash = typeHashVisitor.visit(typeToLoad);
String shape = typeToLoad.toString();
typeHashVisitor.reset();

String typeOwner = JvmCodeGenUtil.getPackageName(packageID) + MODULE_INIT_CLASS_NAME;
String fieldName = getTypeFieldName(toNameString(bType));
mv.visitTypeInsn(NEW, typeOwner);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, typeOwner, JVM_INIT_METHOD, "()V", false);

mv.visitFieldInsn(GETSTATIC, typeOwner, fieldName, String.format("L%s;", TYPE));
mv.visitLdcInsn(hash);
mv.visitLdcInsn(String.format("Package: %s, TypeName: %s, Shape: %s", typeOwner, fieldName, shape));
mv.visitMethodInsn(INVOKEVIRTUAL, typeOwner, GET_ANON_TYPE,
String.format("(IL%s;)L%s;", STRING_VALUE, TYPE), false);
} else {
mv.visitFieldInsn(GETSTATIC, typeOwner, fieldName, String.format("L%s;", TYPE));
}
}

/**
Expand All @@ -1871,7 +1968,7 @@ private void loadUserDefinedType(MethodVisitor mv, BType bType) {
* @param typeName type name
* @return name of the field that holds the type instance
*/
private String getTypeFieldName(String typeName) {
private static String getTypeFieldName(String typeName) {

return String.format("$type$%s", typeName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector;
import org.wso2.ballerinalang.compiler.bir.codegen.internal.FieldNameHashComparator;
import org.wso2.ballerinalang.compiler.bir.codegen.internal.NameHashComparator;
import org.wso2.ballerinalang.compiler.bir.codegen.internal.TypeHashComparator;
import org.wso2.ballerinalang.compiler.bir.codegen.interop.BIRFunctionWrapper;
import org.wso2.ballerinalang.compiler.bir.codegen.interop.ExternalMethodGen;
import org.wso2.ballerinalang.compiler.bir.codegen.interop.JFieldBIRFunction;
Expand Down Expand Up @@ -144,6 +145,7 @@ class JvmValueGen {

static final FieldNameHashComparator FIELD_NAME_HASH_COMPARATOR = new FieldNameHashComparator();
static final NameHashComparator NAME_HASH_COMPARATOR = new NameHashComparator();
static final TypeHashComparator TYPE_HASH_COMPARATOR = new TypeHashComparator();
static final String ENCODED_RECORD_INIT =
IdentifierUtils.encodeFunctionIdentifier(Names.INIT_FUNCTION_SUFFIX.value);
private final BIRNode.BIRPackage module;
Expand Down Expand Up @@ -668,7 +670,7 @@ private byte[] createRecordValueClass(BRecordType recordType, String className,
} else {
cw.visitSource(className, null);
}
JvmTypeGen jvmTypeGen = new JvmTypeGen(stringConstantsGen);
JvmTypeGen jvmTypeGen = new JvmTypeGen(stringConstantsGen, module.packageID);
JvmCastGen jvmCastGen = new JvmCastGen(jvmPackageGen.symbolTable, jvmTypeGen);
LambdaGen lambdaGen = new LambdaGen(jvmPackageGen, jvmCastGen);
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, className,
Expand Down Expand Up @@ -1383,7 +1385,7 @@ private byte[] createObjectValueClass(BObjectType objectType, String className,
ClassWriter cw = new BallerinaClassWriter(COMPUTE_FRAMES);
cw.visitSource(typeDef.pos.lineRange().filePath(), null);

JvmTypeGen jvmTypeGen = new JvmTypeGen(stringConstantsGen);
JvmTypeGen jvmTypeGen = new JvmTypeGen(stringConstantsGen, module.packageID);
JvmCastGen jvmCastGen = new JvmCastGen(jvmPackageGen.symbolTable, jvmTypeGen);
LambdaGen lambdaGen = new LambdaGen(jvmPackageGen, jvmCastGen);
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, className, null, ABSTRACT_OBJECT_VALUE, new String[]{B_OBJECT});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2021, 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.wso2.ballerinalang.compiler.bir.codegen.internal;

import org.wso2.ballerinalang.compiler.bir.model.BIRNode.BIRTypeDefinition;
import org.wso2.ballerinalang.compiler.semantics.analyzer.TypeHashVisitor;

import java.util.Comparator;

/**
* BType hash comparator to sort BIRTypeDefinitions according its type hash code.
*
* @since 2.0.0
*/
public class TypeHashComparator implements Comparator<BIRTypeDefinition> {

private final TypeHashVisitor typeHashVisitor = new TypeHashVisitor();

@Override
public int compare(BIRTypeDefinition o1, BIRTypeDefinition o2) {
Integer o1TypeHash = typeHashVisitor.visit(o1.type);
typeHashVisitor.reset();
Integer o2TypeHash = typeHashVisitor.visit(o2.type);
typeHashVisitor.reset();
return Integer.compare(o1TypeHash, o2TypeHash);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void generateConfigMapper(List<PackageID> imprtMods, BIRNode.BIRPackage p
mv.visitEnd();

generateConfigInit(cw, moduleInitClass, imprtMods, pkg.packageID);
populateConfigDataMethod(cw, moduleInitClass, pkg, new JvmTypeGen(stringConstantsGen));
populateConfigDataMethod(cw, moduleInitClass, pkg, new JvmTypeGen(stringConstantsGen, pkg.packageID));
cw.visitEnd();
jarEntries.put(innerClassName + ".class", cw.toByteArray());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,18 @@ public class BLangAnonymousModelHelper {
private Map<PackageID, Integer> intersectionRecordCount;
private Map<PackageID, Integer> intersectionErrorCount;

private static final String ANON_TYPE = "$anonType$";
public static final String ANON_PREFIX = "$anon";
private static final String ANON_TYPE = ANON_PREFIX + "Type$";
public static final String LAMBDA = "$lambda$";
private static final String SERVICE = "$$service$";
private static final String ANON_SERVICE = "$anonService$";
private static final String BUILTIN_ANON_TYPE = "$anonType$builtin$";
private static final String ANON_SERVICE = ANON_PREFIX + "Service$";
private static final String BUILTIN_ANON_TYPE = ANON_PREFIX + "Type$builtin$";
private static final String BUILTIN_LAMBDA = "$lambda$builtin$";
private static final String FORK = "$fork$";
private static final String ANON_TYPE_ID = "$anonTypeid$";
private static final String ANON_TYPE_ID = ANON_PREFIX + "Typeid$";
private static final String RAW_TEMPLATE_TYPE = "$rawTemplate$";
private static final String ANON_INTERSECTION_RECORD = "$anonIntersectionRecordType$";
private static final String ANON_INTERSECTION_ERROR_TYPE = "$anonIntersectionErrorType$";
private static final String ANON_INTERSECTION_RECORD = ANON_PREFIX + "IntersectionRecordType$";
private static final String ANON_INTERSECTION_ERROR_TYPE = ANON_PREFIX + "IntersectionErrorType$";
private static final String TUPLE_VAR = "$tupleVar$";
private static final String RECORD_VAR = "$recordVar$";
private static final String ERROR_VAR = "$errorVar$";
Expand Down
Loading

0 comments on commit 2756823

Please sign in to comment.