@@ -82,6 +82,74 @@ func (unrelatedNamedContractCapability) CapabilityContracts() map[string]Capabil
8282 }
8383}
8484
85+ type instanceIvarContractCapability struct {
86+ invokeCount * int
87+ }
88+
89+ func (c instanceIvarContractCapability ) Bind (binding CapabilityBinding ) (map [string ]Value , error ) {
90+ classDef := & ClassDef {
91+ Name : "CapabilityBox" ,
92+ Methods : map [string ]* ScriptFunction {},
93+ ClassMethods : map [string ]* ScriptFunction {},
94+ ClassVars : map [string ]Value {},
95+ }
96+ instance := & Instance {
97+ Class : classDef ,
98+ Ivars : map [string ]Value {
99+ "call" : NewBuiltin ("probe.call" , func (exec * Execution , receiver Value , args []Value , kwargs map [string ]Value , block Value ) (Value , error ) {
100+ * c .invokeCount = * c .invokeCount + 1
101+ return NewString ("ok" ), nil
102+ }),
103+ },
104+ }
105+ return map [string ]Value {"box" : NewInstance (instance )}, nil
106+ }
107+
108+ func (c instanceIvarContractCapability ) CapabilityContracts () map [string ]CapabilityMethodContract {
109+ return map [string ]CapabilityMethodContract {
110+ "probe.call" : {
111+ ValidateArgs : func (args []Value , kwargs map [string ]Value , block Value ) error {
112+ if len (args ) != 1 || args [0 ].Kind () != KindInt {
113+ return fmt .Errorf ("probe.call expects int" )
114+ }
115+ return nil
116+ },
117+ },
118+ }
119+ }
120+
121+ type classVarContractCapability struct {
122+ invokeCount * int
123+ }
124+
125+ func (c classVarContractCapability ) Bind (binding CapabilityBinding ) (map [string ]Value , error ) {
126+ classDef := & ClassDef {
127+ Name : "CapabilityHolder" ,
128+ Methods : map [string ]* ScriptFunction {},
129+ ClassMethods : map [string ]* ScriptFunction {},
130+ ClassVars : map [string ]Value {
131+ "call" : NewBuiltin ("probe.class_call" , func (exec * Execution , receiver Value , args []Value , kwargs map [string ]Value , block Value ) (Value , error ) {
132+ * c .invokeCount = * c .invokeCount + 1
133+ return NewString ("ok" ), nil
134+ }),
135+ },
136+ }
137+ return map [string ]Value {"holder" : NewClass (classDef )}, nil
138+ }
139+
140+ func (c classVarContractCapability ) CapabilityContracts () map [string ]CapabilityMethodContract {
141+ return map [string ]CapabilityMethodContract {
142+ "probe.class_call" : {
143+ ValidateArgs : func (args []Value , kwargs map [string ]Value , block Value ) error {
144+ if len (args ) != 1 || args [0 ].Kind () != KindInt {
145+ return fmt .Errorf ("probe.class_call expects int" )
146+ }
147+ return nil
148+ },
149+ },
150+ }
151+ }
152+
85153func TestCapabilityContractRejectsInvalidArguments (t * testing.T ) {
86154 engine := MustNewEngine (Config {})
87155 script , err := engine .Compile (`def run()
@@ -184,3 +252,55 @@ end`)
184252 t .Fatalf ("unexpected merge result: %#v" , result .Hash ())
185253 }
186254}
255+
256+ func TestCapabilityContractsTraverseInstanceValues (t * testing.T ) {
257+ engine := MustNewEngine (Config {})
258+ script , err := engine .Compile (`def run()
259+ box.call("bad")
260+ end` )
261+ if err != nil {
262+ t .Fatalf ("compile failed: %v" , err )
263+ }
264+
265+ invocations := 0
266+ _ , err = script .Call (context .Background (), "run" , nil , CallOptions {
267+ Capabilities : []CapabilityAdapter {
268+ instanceIvarContractCapability {invokeCount : & invocations },
269+ },
270+ })
271+ if err == nil {
272+ t .Fatalf ("expected instance contract validation error" )
273+ }
274+ if got := err .Error (); ! strings .Contains (got , "probe.call expects int" ) {
275+ t .Fatalf ("unexpected error: %s" , got )
276+ }
277+ if invocations != 0 {
278+ t .Fatalf ("instance capability should not execute when contract fails" )
279+ }
280+ }
281+
282+ func TestCapabilityContractsTraverseClassValues (t * testing.T ) {
283+ engine := MustNewEngine (Config {})
284+ script , err := engine .Compile (`def run()
285+ holder.call("bad")
286+ end` )
287+ if err != nil {
288+ t .Fatalf ("compile failed: %v" , err )
289+ }
290+
291+ invocations := 0
292+ _ , err = script .Call (context .Background (), "run" , nil , CallOptions {
293+ Capabilities : []CapabilityAdapter {
294+ classVarContractCapability {invokeCount : & invocations },
295+ },
296+ })
297+ if err == nil {
298+ t .Fatalf ("expected class contract validation error" )
299+ }
300+ if got := err .Error (); ! strings .Contains (got , "probe.class_call expects int" ) {
301+ t .Fatalf ("unexpected error: %s" , got )
302+ }
303+ if invocations != 0 {
304+ t .Fatalf ("class capability should not execute when contract fails" )
305+ }
306+ }
0 commit comments