@@ -615,14 +615,20 @@ func (exec *Execution) invokeCallable(callee Value, receiver Value, args []Value
615615 case KindBuiltin :
616616 builtin := callee .Builtin ()
617617 scope := exec .capabilityContractScopes [builtin ]
618- var preCallArgBuiltins map [* Builtin ]struct {}
618+ var preCallKnownBuiltins map [* Builtin ]struct {}
619619 if scope != nil && len (scope .contracts ) > 0 {
620- preCallArgBuiltins = make (map [* Builtin ]struct {})
620+ preCallKnownBuiltins = make (map [* Builtin ]struct {})
621+ if receiver .Kind () != KindNil {
622+ collectCapabilityBuiltins (receiver , preCallKnownBuiltins )
623+ }
624+ for _ , root := range scope .roots {
625+ collectCapabilityBuiltins (root , preCallKnownBuiltins )
626+ }
621627 for _ , arg := range args {
622- collectCapabilityBuiltins (arg , preCallArgBuiltins )
628+ collectCapabilityBuiltins (arg , preCallKnownBuiltins )
623629 }
624630 for _ , kwarg := range kwargs {
625- collectCapabilityBuiltins (kwarg , preCallArgBuiltins )
631+ collectCapabilityBuiltins (kwarg , preCallKnownBuiltins )
626632 }
627633 }
628634 contract , hasContract := exec .capabilityContracts [builtin ]
@@ -645,22 +651,22 @@ func (exec *Execution) invokeCallable(callee Value, receiver Value, args []Value
645651 // Capability methods can lazily publish additional builtins at runtime
646652 // (e.g. through factory return values or receiver mutation). Re-scan
647653 // these values so future calls still enforce declared contracts.
648- bindCapabilityContracts (result , scope , exec .capabilityContracts , exec .capabilityContractScopes )
654+ bindCapabilityContractsExcluding (result , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallKnownBuiltins )
649655 if receiver .Kind () != KindNil {
650- bindCapabilityContracts (receiver , scope , exec .capabilityContracts , exec .capabilityContractScopes )
656+ bindCapabilityContractsExcluding (receiver , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallKnownBuiltins )
651657 }
652658 // Methods can mutate sibling scope roots via captured references; refresh
653659 // all adapter roots so newly exposed builtins also get bound.
654660 for _ , root := range scope .roots {
655- bindCapabilityContracts (root , scope , exec .capabilityContracts , exec .capabilityContractScopes )
661+ bindCapabilityContractsExcluding (root , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallKnownBuiltins )
656662 }
657663 // Methods can also publish builtins by mutating positional or keyword
658664 // argument objects supplied by script code.
659665 for _ , arg := range args {
660- bindCapabilityContractsExcluding (arg , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallArgBuiltins )
666+ bindCapabilityContractsExcluding (arg , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallKnownBuiltins )
661667 }
662668 for _ , kwarg := range kwargs {
663- bindCapabilityContractsExcluding (kwarg , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallArgBuiltins )
669+ bindCapabilityContractsExcluding (kwarg , scope , exec .capabilityContracts , exec .capabilityContractScopes , preCallKnownBuiltins )
664670 }
665671 }
666672 return result , nil
0 commit comments