diff --git a/policy/bro.init b/policy/bro.init index 7fbbe216bb..93902140fc 100644 --- a/policy/bro.init +++ b/policy/bro.init @@ -274,6 +274,10 @@ type entropy_test_result: record { serial_correlation: double; }; +type test_object: record { + opaque: any; +}; + # Prototypes of Bro built-in functions. @load strings.bif.bro @load bro.bif.bro diff --git a/src/Val.h b/src/Val.h index 76edea5429..d7e128717a 100644 --- a/src/Val.h +++ b/src/Val.h @@ -1030,6 +1030,47 @@ protected: VectorType* vector_type; }; +// An opaque value of script type "any" that can only be manipulated via +// bifs. The value is a smart pointer that wraps a C pointer to an object of +// type T, which will be deleted when the value is no longer referenced. At +// the script level, the value is handled in a type-safe manner by embedding +// it into a record value. +template +class OpaqueVal : public Val { +public: + // Takes ownership of the object. + OpaqueVal(RecordType* arg_rt, T* arg_obj) : Val(TYPE_ANY) + { + obj = arg_obj; + rt = arg_rt; + rt->Ref(); + + assert(rt->NumFields() == 1); + } + + ~OpaqueVal() + { + delete obj; + Unref(rt); + } + + T* AsObject() { return obj; } + const T* AsObject() const { return obj; } + + // This returns a ref'ed RecordVal. + RecordVal* MakeRecordVal() + { + RecordVal* rv = new RecordVal(rt); + rv->Assign(0, this); + Ref(); + return rv; + } + +private: + T* obj; + RecordType* rt; + bool refed; +}; // Checks the given value for consistency with the given type. If an // exact match, returns it. If promotable, returns the promoted version, diff --git a/src/bro.bif b/src/bro.bif index 1d2c159f65..71a278b42a 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -3327,3 +3327,33 @@ function entropy_test_finish%(index: any%): entropy_test_result delete s; return ent_result; %} + +%%{ +class TestObject +{ +public: + TestObject() { fprintf(stderr, "ctor %p\n", this); } + ~TestObject() { fprintf(stderr, "dtor %p\n", this); } +}; +%%} + +function create_test_object%(%) : test_object + %{ + TestObject* obj = new TestObject; + + OpaqueVal* v = new OpaqueVal(BifType::Record::test_object, obj); + RecordVal* rv = v->MakeRecordVal(); + + Unref(v); + return rv; + %} + +function do_something_with_test_object%(rv: test_object%) : any + %{ + OpaqueVal* v = (OpaqueVal*) rv->AsRecordVal()->Lookup(0); + TestObject* obj = v->AsObject(); + + fprintf(stderr, "doing something with %p\n", obj); + + return 0; + %} diff --git a/src/types.bif b/src/types.bif index 4496e444a1..c0bfe913a9 100644 --- a/src/types.bif +++ b/src/types.bif @@ -51,6 +51,8 @@ enum rpc_status %{ RPC_UNKNOWN_ERROR, %} +type test_object: record; + module Log; enum Writer %{ diff --git a/testing/btest/Baseline/language.opaque/output b/testing/btest/Baseline/language.opaque/output new file mode 100644 index 0000000000..f711273a43 --- /dev/null +++ b/testing/btest/Baseline/language.opaque/output @@ -0,0 +1,9 @@ +ctor 0x673ed40 +doing something with 0x673ed40 +ctor 0x673b290 +dtor 0x673ed40 +ctor 0x6785890 +dtor 0x673b290 +{ +[1] = [opaque=] +} diff --git a/testing/btest/language/opaque.bro b/testing/btest/language/opaque.bro new file mode 100644 index 0000000000..caf957e6e0 --- /dev/null +++ b/testing/btest/language/opaque.bro @@ -0,0 +1,18 @@ + +# @TEST-EXEC: bro %INPUT >output 2>&1 +# @TEST-EXEC: btest-diff output + +global obj: test_object; +global t: table[count] of test_object; + +obj = create_test_object(); + +do_something_with_test_object(obj); + +obj = create_test_object(); + +t[1] = obj; +print t; +delete t[1]; + +obj = create_test_object();