From de1960af2ef75da69c6ce956928efcbd8e5465f7 Mon Sep 17 00:00:00 2001 From: Heshan Padamsiri Date: Wed, 26 Feb 2025 21:30:52 +0530 Subject: [PATCH] Optimize inherent type check for objects --- .../api/types/semtype/ShapeAnalyzer.java | 60 ++++++++++++++++++- .../runtime/internal/TypeChecker.java | 12 +--- .../runtime/internal/types/TypeWithShape.java | 2 +- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java index 3076356ec5a1..f7c8a155f136 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java @@ -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. @@ -104,4 +111,55 @@ public static Optional 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 sourceFieldNames = sourceType.getFields().keySet(); + for (String each : targetType.getFields().keySet()) { + if (!sourceFieldNames.contains(each)) { + return true; + } + } + Set 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 inherentShapeTestCache = CacheFactory.createCache(); +} \ No newline at end of file diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 66e5d58a0f79..1f0d041f3d20 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java @@ -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; @@ -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)); } @@ -321,14 +321,6 @@ public static boolean checkIsType(List 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. * diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java index a1802d15aa70..96d2736da98c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java @@ -38,4 +38,4 @@ public interface TypeWithShape extends TypeWithAcceptedType { Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); boolean couldInherentTypeBeDifferent(); -} +} \ No newline at end of file