diff --git a/testing/btest/Baseline/language.closure-binding-errors/.stderr b/testing/btest/Baseline/language.closure-binding-errors/.stderr new file mode 100644 index 0000000000..1b82c68894 --- /dev/null +++ b/testing/btest/Baseline/language.closure-binding-errors/.stderr @@ -0,0 +1,10 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/closure-binding-errors.zeek, line 12: a is captured but not used inside lambda (function(){ print no a!}) +error in <...>/closure-binding-errors.zeek, line 13: no such local identifier: a2 +error in <...>/closure-binding-errors.zeek, line 14: b is used inside lambda but not captured (function(){ print b}) +error in <...>/closure-binding-errors.zeek, line 14: a is captured but not used inside lambda (function(){ print b}) +error in <...>/closure-binding-errors.zeek, line 15: a is captured but not used inside lambda (function(){ print b}) +error in <...>/closure-binding-errors.zeek, line 16: b listed multiple times in capture (function(){ print b}) +error in <...>/closure-binding-errors.zeek, line 18: cannot specify global in capture: c +error in <...>/closure-binding-errors.zeek, line 19: cannot specify type in capture: t +error in <...>/closure-binding-errors.zeek, line 9 and <...>/closure-binding-errors.zeek, line 20: already defined (a) diff --git a/testing/btest/Baseline/language.closure-binding-errors/out b/testing/btest/Baseline/language.closure-binding-errors/out new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/language.closure-binding-errors/out @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/language.closure-binding/out b/testing/btest/Baseline/language.closure-binding/out new file mode 100644 index 0000000000..891492d324 --- /dev/null +++ b/testing/btest/Baseline/language.closure-binding/out @@ -0,0 +1,46 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +4, 10 +6, 8 +7, 7 +4, 10 +5, 8 +6, 7 +4, 10 +5, 9 +6, 8 +4, 10 +5, 8 +6, 7 +4, 10 +5, 9 +6, 8 +4 +2, 10, 47 +4 +2, 8, 47 +3 +1, 7, 47 +4 +2, 10, 47 +5 +3, 8, 47 +6 +4, 7, 47 +4 +2, 10, 47 +5 +3, 9, 47 +6 +4, 8, 47 +4 +2, 10, 91 +5 +3, 9, 91 +6 +4, 9, 91 +4 +2, 10, 91 +5 +3, 10, 91 +6 +4, 10, 91 diff --git a/testing/btest/Baseline/language.closure-sending2/recv.recv.out b/testing/btest/Baseline/language.closure-sending2/recv.recv.out new file mode 100644 index 0000000000..4760591c3c --- /dev/null +++ b/testing/btest/Baseline/language.closure-sending2/recv.recv.out @@ -0,0 +1,27 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +hello :-) +peer added +receiver got ping: function 2 +inside: 1 | outside: 11 | global: 100 +77 +receiver got ping: function 1 +begin: 100 | base_step: 2 +begin: 100 | base_step: 2 | step: 76 +178 +receiver got ping: function 2 +inside: 3 | outside: 11 | global: 100 +79 +receiver got ping: function 1 +begin: 100 | base_step: 4 +begin: 100 | base_step: 4 | step: 76 +180 +receiver got ping: function 2 +inside: 5 | outside: 11 | global: 100 +81 +receiver got ping: function 1 +begin: 100 | base_step: 6 +begin: 100 | base_step: 6 | step: 76 +182 +receiver got ping: function 2 +inside: 7 | outside: 11 | global: 100 +83 diff --git a/testing/btest/Baseline/language.closure-sending2/send.send.out b/testing/btest/Baseline/language.closure-sending2/send.send.out new file mode 100644 index 0000000000..1d644231bc --- /dev/null +++ b/testing/btest/Baseline/language.closure-sending2/send.send.out @@ -0,0 +1,32 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +hello :) +peer added +begin: 100 | base_step: 50 +sender got pong: function 2 +inside: 1 | outside: 11 | global: 10 +77 +begin: 100 | base_step: 50 +sender got pong: function 1 +begin: 100 | base_step: 2 +begin: 100 | base_step: 2 | step: 76 +178 +begin: 100 | base_step: 50 +sender got pong: function 2 +inside: 3 | outside: 11 | global: 10 +79 +begin: 100 | base_step: 50 +sender got pong: function 1 +begin: 100 | base_step: 4 +begin: 100 | base_step: 4 | step: 76 +180 +begin: 100 | base_step: 50 +sender got pong: function 2 +inside: 5 | outside: 11 | global: 10 +81 +begin: 100 | base_step: 50 +sender got pong: function 1 +begin: 100 | base_step: 6 +begin: 100 | base_step: 6 | step: 76 +182 +begin: 100 | base_step: 50 +peer lost diff --git a/testing/btest/language/closure-binding-errors.zeek b/testing/btest/language/closure-binding-errors.zeek new file mode 100644 index 0000000000..694918dc7d --- /dev/null +++ b/testing/btest/language/closure-binding-errors.zeek @@ -0,0 +1,21 @@ +# @TEST-EXEC-FAIL: zeek -b %INPUT >out +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff .stderr + +global c: string; +type t: addr; + +event zeek_init() + { + local a = 3; + local b = "hi there"; + + local f1 = function[a]() { print "no a!"; }; + local f2 = function[a2](a2: addr) { print a2; }; + local f3 = function[a]() { print b; }; + local f4 = function[a, b]() { print b; }; + local f5 = function[b, b]() { print b; }; + local f6 = function() { print c; }; # should be okay + local f7 = function[c]() { print c; }; + local f8 = function[t]() { local t2: t; }; + local f9 = function[a]() { local a = 4; }; # error due to shadowing + } diff --git a/testing/btest/language/closure-binding.zeek b/testing/btest/language/closure-binding.zeek new file mode 100644 index 0000000000..6e1596b6f9 --- /dev/null +++ b/testing/btest/language/closure-binding.zeek @@ -0,0 +1,199 @@ +# @TEST-EXEC: zeek -b %INPUT >out +# @TEST-EXEC: btest-diff out + +type mutable_aggregate: record { x: count; }; + +function reference_capture() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function() { print ++a, --b$x; }; + f(); + ++a; + --b$x; + f(); + + return f; + } + +function shallow_copy_capture() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[a, b]() { print ++a, --b$x; }; + f(); + ++a; + --b$x; + f(); + + return f; + } + +function deep_copy_capture() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[copy a, copy b]() { print ++a, --b$x; }; + f(); + ++a; + --b$x; + f(); + + return f; + } + +function mixed_copy_capture_a() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[copy a, b]() { print ++a, --b$x; }; + f(); + ++a; + --b$x; + f(); + + return f; + } + +function mixed_copy_capture_b() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[a, copy b]() { print ++a, --b$x; }; + f(); + ++a; + --b$x; + f(); + + return f; + } + +function reference_capture_double() : function() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function() : function() { + local c = mutable_aggregate($x=88); + print ++a; + local f2 = function() { print a -= 2, --b$x, c$x += 3; }; + c$x = c$x / 2; + return f2; + }; + f()(); + ++a; + --b$x; + f()(); + + return f; + } + +function shallow_copy_capture_double() : function() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[a,b]() : function() { + local c = mutable_aggregate($x=88); + print ++a; + local f2 = function[a, b, c]() { print a -= 2, --b$x, c$x += 3; }; + c$x = c$x / 2; + return f2; + }; + f()(); + ++a; + --b$x; + f()(); + + return f; + } + +function deep_copy1_capture_double() : function() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[copy a, copy b]() : function() { + local c = mutable_aggregate($x=88); + print ++a; + local f2 = function[a, b, c]() { print a -= 2, --b$x, c$x += 3; }; + c$x = c$x / 2; + return f2; + }; + f()(); + ++a; + --b$x; + f()(); + + return f; + } + +function deep_copy2_capture_double() : function() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[a, b]() : function() { + local c = mutable_aggregate($x=88); + print ++a; + local f2 = function[copy a, copy b, copy c]() + { print a -= 2, --b$x, c$x += 3; }; + c$x = c$x / 2; + return f2; + }; + f()(); + ++a; + --b$x; + f()(); + + return f; + } + +function deep_copy3_capture_double() : function() : function() + { + local a = 3; + local b = mutable_aggregate($x=11); + local f = function[copy a, copy b]() : function() { + local c = mutable_aggregate($x=88); + print ++a; + local f2 = function[copy a, copy b, copy c]() + { print a -= 2, --b$x, c$x += 3; }; + c$x = c$x / 2; + return f2; + }; + f()(); + ++a; + --b$x; + f()(); + + return f; + } + +event zeek_init() + { + local rc = reference_capture(); + rc(); + + local scc = shallow_copy_capture(); + scc(); + + local dcc = deep_copy_capture(); + dcc(); + + local mcca = mixed_copy_capture_a(); + mcca(); + + local mccb = mixed_copy_capture_b(); + mccb(); + + local rc2 = reference_capture_double(); + rc2()(); + + local scc2 = shallow_copy_capture_double(); + scc2()(); + + local dcc2_1 = deep_copy1_capture_double(); + dcc2_1()(); + + local dcc2_2 = deep_copy2_capture_double(); + dcc2_2()(); + + local dcc2_3 = deep_copy3_capture_double(); + dcc2_3()(); + } diff --git a/testing/btest/language/closure-sending2.zeek b/testing/btest/language/closure-sending2.zeek new file mode 100644 index 0000000000..c6a421a708 --- /dev/null +++ b/testing/btest/language/closure-sending2.zeek @@ -0,0 +1,167 @@ +# @TEST-PORT: BROKER_PORT +# +# @TEST-EXEC: btest-bg-run recv "zeek -B broker -b ../recv.zeek >recv.out" +# @TEST-EXEC: btest-bg-run send "zeek -B broker -b ../send.zeek >send.out" +# +# @TEST-EXEC: btest-bg-wait 45 +# @TEST-EXEC: btest-diff recv/recv.out +# @TEST-EXEC: btest-diff send/send.out + +@TEST-START-FILE send.zeek + +redef exit_only_after_terminate = T; +type myfunctype: function(c: count) : function(d: count) : count; + +global global_with_same_name = 10; + +global ping: event(msg: string, f: myfunctype); + +event zeek_init() + { + print "hello :)"; + Broker::subscribe("zeek/event/my_topic"); + Broker::peer("127.0.0.1", to_port(getenv("BROKER_PORT"))); + } + +global n = 0; + +function send_event() + { + # in this frame event_count has an offset of three. + # in the receiving frame it has an offset of one. + # this tests to ensure that id lookups are being routed properly. + local dog = 0; + local not_dog = 1; + local event_count = 11; + + local log : myfunctype = function[event_count](c: count) : function(d: count) : count + { + print fmt("inside: %s | outside: %s | global: %s", c, event_count, global_with_same_name); + return function[c](d: count) : count { return d + c; }; + }; + + local two_part_adder_maker = function (begin : count) : function (base_step : count) : function ( step : count) : count + { + return function [begin](base_step : count) : function (step : count) : count + { + print fmt("begin: %s | base_step: %s", begin, base_step); + return function[begin, base_step] (step : count) : count + { + print fmt("begin: %s | base_step: %s | step: %s", begin, base_step, step); + return (begin += base_step + step); }; }; }; + + local l = two_part_adder_maker(100); + local stepper = l(50); + + ++n; + ++event_count; + if ( n % 2 == 0) + { + local e2 = Broker::make_event(ping, "function 1", l); + Broker::publish("zeek/event/my_topic", e2); + } + else + { + local e = Broker::make_event(ping, "function 2", log); + Broker::publish("zeek/event/my_topic", e); + } + } + +event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string) + { + print "peer added"; + send_event(); + } + +event Broker::peer_lost(endpoint: Broker::EndpointInfo, msg: string) + { + print "peer lost"; + terminate(); + } + +event pong(msg: string, f: myfunctype) + { + print fmt("sender got pong: %s", msg); + local adder = f(n); + print adder(76); + send_event(); + } + +@TEST-END-FILE + +@TEST-START-FILE recv.zeek + +redef exit_only_after_terminate = T; +const events_to_recv = 7; +type myfunctype: function(c: count) : function(d: count) : count; +# type myfunctype: function(c: count); + +global global_with_same_name = 100; + +global pong: event(msg: string, f: myfunctype); + +# This is one, of many, ways to declare your functions that you plan to receive. +# All you are doing is giving the parser a version of their body, so they can be +# anywhere. This seems to work quite nicely because it keeps them scoped and stops +# them from ever being evaluated. +function my_funcs() + { + return; + + local begin = 100; + local event_count = begin; + + local l : myfunctype = function[event_count](c: count) : function(d: count) : count + { + print fmt("inside: %s | outside: %s | global: %s", c, event_count, global_with_same_name); + return function[c](d: count) : count { return d + c; }; + }; + + local dog_fish = function [begin](base_step : count) : function (step : count) : count + { +# actual formatting doesn't matter for name resolution. +print fmt("begin: %s | base_step: %s", begin, base_step); + return function [begin, base_step](step : count) : count + { + print fmt("begin: %s | base_step: %s | step: %s", begin, base_step, step); + return (begin += base_step + step); }; }; + } + +event zeek_init() + { + print "hello :-)"; + Broker::subscribe("zeek/event/my_topic"); + Broker::listen("127.0.0.1", to_port(getenv("BROKER_PORT"))); + } + +event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string) + { + print "peer added"; + } + +event Broker::peer_lost(endpoint: Broker::EndpointInfo, msg: string) + { + print "peer lost"; + } + +global n = 0; + +event ping(msg: string, f: myfunctype) + { + print fmt("receiver got ping: %s", msg); + ++n; + local adder = f(n); + print adder(76); + + if ( n == events_to_recv ) + { + terminate(); + } + else + { + local e = Broker::make_event(pong, msg, f); + Broker::publish("zeek/event/my_topic", e); + } + } + +@TEST-END-FILE