Skip to content

Commit

Permalink
Optimize inherent type check for objects
Browse files Browse the repository at this point in the history
  • Loading branch information
heshanpadmasiri committed Feb 26, 2025
1 parent 7a15970 commit de1960a
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@

package io.ballerina.runtime.api.types.semtype;

import com.github.benmanes.caffeine.cache.Cache;
import io.ballerina.runtime.api.types.Type;
import io.ballerina.runtime.api.utils.TypeUtils;
import io.ballerina.runtime.api.values.BString;
import io.ballerina.runtime.api.values.BValue;
import io.ballerina.runtime.internal.types.BObjectType;
import io.ballerina.runtime.internal.types.TypeWithAcceptedType;
import io.ballerina.runtime.internal.types.TypeWithShape;
import io.ballerina.runtime.internal.types.semtype.CacheFactory;
import io.ballerina.runtime.internal.values.DecimalValue;

import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Utility class for performing shape related operations.
Expand Down Expand Up @@ -104,4 +111,55 @@ public static Optional<SemType> inherentTypeOf(Context cx, Object object) {
}
}
}
}

public static boolean canOptimizeInherentTypeCheck(Context cx, Type sourceType, Type targetType) {
if (sourceType instanceof CacheableTypeDescriptor cacheableSource &&
targetType instanceof CacheableTypeDescriptor cacheableTarget) {
InherentShapeTestKeys key = new InherentShapeTestKeys(cacheableSource.typeId(), cacheableTarget.typeId());
Boolean result = inherentShapeTestCache.getIfPresent(key);
if (result == null) {
result = canOptimizeInherentTypeCheckInner(cx, cacheableSource, cacheableTarget);
inherentShapeTestCache.put(key, result);
}
return result;
}
return canOptimizeInherentTypeCheckInner(cx, sourceType, targetType);
}

private static boolean canOptimizeInherentTypeCheckInner(Context cx, Type sourceType, Type targetType) {
sourceType = TypeUtils.getReferredType(sourceType);
targetType = TypeUtils.getReferredType(targetType);
if (sourceType instanceof TypeWithShape typeWithShape && !typeWithShape.couldInherentTypeBeDifferent()) {
return true;
}
if (sourceType instanceof BObjectType sourceObjectType && targetType instanceof BObjectType targetObjectType) {
return canOptimizeInherentTypeCheckForObjectTypes(sourceObjectType, targetObjectType);
}
return Core.isEmpty(cx, Core.intersect(SemType.tryInto(cx, sourceType), SemType.tryInto(cx, targetType)));
}

// Here we are using the fact that source type defines the upper bound of method and field names, even if not
// the types (whereas the actual type defines the lower bound).
private static boolean canOptimizeInherentTypeCheckForObjectTypes(BObjectType sourceType, BObjectType targetType) {
Set<String> sourceFieldNames = sourceType.getFields().keySet();
for (String each : targetType.getFields().keySet()) {
if (!sourceFieldNames.contains(each)) {
return true;
}
}
Set<String> sourceMethodNames = Arrays.stream(sourceType.getMethods()).map(Type::getName).collect(
Collectors.toSet());
for (Type each : targetType.getMethods()) {
if (!sourceMethodNames.contains(each.getName())) {
return true;
}
}
return false;
}

record InherentShapeTestKeys(int sourceTypeId, int targetTypeId) {

}

private static final Cache<InherentShapeTestKeys, Boolean> inherentShapeTestCache = CacheFactory.createCache();
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_32;
import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_8;
import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_NULL;
import static io.ballerina.runtime.api.types.semtype.ShapeAnalyzer.canOptimizeInherentTypeCheck;
import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType;
import static io.ballerina.runtime.internal.utils.CloneUtils.getErrorMessage;

Expand Down Expand Up @@ -303,8 +304,7 @@ private static boolean checkIsTypeInner(Object sourceVal, Type targetType, Conte
if (isSubType(cx, sourceType, targetType)) {
return true;
}
SemType sourceSemType = SemType.tryInto(cx, sourceType);
return couldInherentTypeBeDifferent(sourceSemType) &&
return !canOptimizeInherentTypeCheck(cx, sourceType, targetType) &&
isSubTypeWithInherentType(cx, sourceVal, SemType.tryInto(cx, targetType));
}

Expand All @@ -321,14 +321,6 @@ public static boolean checkIsType(List<String> errors, Object sourceVal, Type so
return checkIsType(sourceVal, targetType);
}

// This is just an optimization since shapes are not cached, when in doubt return false
private static boolean couldInherentTypeBeDifferent(SemType type) {
if (type instanceof TypeWithShape typeWithShape) {
return typeWithShape.couldInherentTypeBeDifferent();
}
return true;
}

/**
* Check whether a given value has the same shape as the given type.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ public interface TypeWithShape extends TypeWithAcceptedType {
Optional<SemType> shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object);

boolean couldInherentTypeBeDifferent();
}
}

0 comments on commit de1960a

Please sign in to comment.