diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java index 8af0eacb0..0393c4271 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java @@ -51,7 +51,7 @@ public abstract class ElementOrEntryNode extends RegularMemberNode { @Cached("create()") @Shared("callNode") IndirectCallNode callNode) { var result = bodyNode.executeGeneric(frame); return VmUtils.shouldRunTypeCheck(frame) - ? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null) + ? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null) : result; } @@ -62,7 +62,7 @@ public abstract class ElementOrEntryNode extends RegularMemberNode { @Cached("create()") @Shared("callNode") IndirectCallNode callNode) { var result = bodyNode.executeGeneric(frame); return VmUtils.shouldRunTypeCheck(frame) - ? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null) + ? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null) : result; } diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java index 8898a930a..8d448d360 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java @@ -55,37 +55,35 @@ public abstract class VmListingOrMapping extends VmObject { this.typeCheckOwner = typeCheckOwner; } - // Recursively executes type casts between `owner` and `this` and returns the resulting value. + // Executes type casts for every parent between `owner` and `this` and returns the resulting + // value. public final Object executeTypeCasts( Object value, VmObjectLike owner, IndirectCallNode callNode, // if non-null, a stack frame for this member is inserted if a type cast fails - @Nullable ObjectMember member, - // Next type cast to be performed by the caller. - // Avoids repeating the same type cast in some cases. - @Nullable ListingOrMappingTypeCastNode nextTypeCastNode) { - var newNextTypeCastNode = typeCastNode != null ? typeCastNode : nextTypeCastNode; - Object result; - if (this == owner) { - result = value; - } else { - assert parent != null; - result = - ((VmListingOrMapping) parent) - .executeTypeCasts(value, owner, callNode, member, newNextTypeCastNode); - } - if (typeCastNode == null || typeCastNode == nextTypeCastNode) return result; - var callTarget = typeCastNode.getCallTarget(); - try { - return callNode.call(callTarget, typeCheckReceiver, typeCheckOwner, result); - } catch (VmException e) { - CompilerDirectives.transferToInterpreter(); - if (member != null) { - VmUtils.insertStackFrame(member, callTarget, e); + @Nullable ObjectMember member) { + var result = value; + VmObject parent = this; + ListingOrMappingTypeCastNode prevTypeCastNode = null; + while (parent != null && parent != owner) { + var obj = (VmListingOrMapping) parent; + if (obj.typeCastNode != null && obj.typeCastNode != prevTypeCastNode) { + var callTarget = obj.typeCastNode.getCallTarget(); + try { + result = callNode.call(callTarget, obj.typeCheckReceiver, obj.typeCheckOwner, result); + prevTypeCastNode = obj.typeCastNode; + } catch (VmException e) { + CompilerDirectives.transferToInterpreter(); + if (member != null) { + VmUtils.insertStackFrame(member, callTarget, e); + } + throw e; + } } - throw e; + parent = parent.parent; } + return result; } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java index 471524360..3c722a6cf 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java @@ -396,7 +396,7 @@ public final class VmUtils { && owner instanceof VmListingOrMapping) { // `owner instanceof VmListingOrMapping` guards against // PropertiesRenderer amending VmDynamic with VmListing (hack?) - result = listingOrMapping.executeTypeCasts(constantValue, owner, callNode, member, null); + result = listingOrMapping.executeTypeCasts(constantValue, owner, callNode, member); } receiver.setCachedValue(memberKey, result);