diff --git a/src/gsi/gsi/gsiExpression.cc b/src/gsi/gsi/gsiExpression.cc index 9a979e9e7..48f7fb39b 100644 --- a/src/gsi/gsi/gsiExpression.cc +++ b/src/gsi/gsi/gsiExpression.cc @@ -286,6 +286,25 @@ struct test_arg_func return; } + if (arg.is_list ()) { + + // we may implicitly convert an array into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the array given. + + int n = arg.size (); + + *ret = false; + for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + *ret = true; + break; + } + } + + return; + + } + if (! arg.is_user ()) { *ret = false; return; @@ -628,29 +647,77 @@ struct writer { void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap) { - if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) { + if (arg->is_nil ()) { - if (arg->is_nil ()) { + if (atype.is_ref () || atype.is_cref ()) { + throw tl::Exception (tl::to_string (tr ("Cannot pass nil to reference parameters"))); + } else if (! atype.is_cptr () && ! atype.is_ptr ()) { + throw tl::Exception (tl::to_string (tr ("Cannot pass nil to direct parameters"))); + } - if (atype.is_ref () || atype.is_cref ()) { - throw tl::Exception (tl::to_string (tr ("Cannot pass nil to reference parameters"))); + aa->write ((void *) 0); + + } else if (arg->is_list ()) { + + // we may implicitly convert an array into a constructor call of a target object - + // for now we only check whether the number of arguments is compatible with the array given. + + int n = arg->size (); + const gsi::MethodBase *meth = 0; + for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) { + if ((*c)->compatible_with_num_args (n)) { + meth = *c; + break; } + } - aa->write ((void *) 0); + if (!meth) { + throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), atype.cls ()->name (), n); + } - } else { + // implicit call of constructor + gsi::SerialArgs retlist (meth->retsize ()); + gsi::SerialArgs arglist (meth->argsize ()); - if (! arg->is_user ()) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); + int narg = 0; + for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < n; ++a, ++narg) { + try { + // Note: this const_cast is ugly, but it will basically enable "out" parameters + // TODO: clean this up. + gsi::do_on_type () (a->type (), &arglist, (arg->get_list ().begin () + narg).operator-> (), *a, heap); + } catch (tl::Exception &ex) { + std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ()); + throw tl::Exception (msg); } + } - const tl::VariantUserClassBase *cls = arg->user_cls (); - if (!cls) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); - } - if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ())); - } + meth->call (0, arglist, retlist); + + void *new_obj = retlist.read (*heap); + if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) { + // For pointers or refs, ownership over these objects is not transferred. + // Hence we have to keep them on the heap. + // TODO: what if the called method takes ownership using keep()? + heap->push (new gsi::ObjectHolder (atype.cls (), new_obj)); + } + + aa->write (new_obj); + + } else { + + if (! arg->is_user ()) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); + } + + const tl::VariantUserClassBase *cls = arg->user_cls (); + if (!cls) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); + } + if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) { + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ())); + } + + if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) { if (cls->gsi_cls ()->is_derived_from (atype.cls ())) { @@ -673,36 +740,24 @@ struct writer throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); } - } + } else { - } else { + if (cls->gsi_cls ()->is_derived_from (atype.cls ())) { - if (! arg->is_user ()) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); - } + if (cls->gsi_cls ()->adapted_type_info ()) { + aa->write (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg))); + } else { + aa->write ((void *) cls->gsi_cls ()->clone (get_object (*arg))); + } - const tl::VariantUserClassBase *cls = arg->user_cls (); - if (!cls) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); - } - if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ())); - } + } else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) { - if (cls->gsi_cls ()->is_derived_from (atype.cls ())) { + aa->write (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg))); - if (cls->gsi_cls ()->adapted_type_info ()) { - aa->write (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg))); } else { - aa->write ((void *) cls->gsi_cls ()->clone (get_object (*arg))); + throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); } - } else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) { - - aa->write (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg))); - - } else { - throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ())); } } diff --git a/src/gsi/unit_tests/gsiExpressionTests.cc b/src/gsi/unit_tests/gsiExpressionTests.cc index a31717fcf..1bfdf1013 100644 --- a/src/gsi/unit_tests/gsiExpressionTests.cc +++ b/src/gsi/unit_tests/gsiExpressionTests.cc @@ -57,14 +57,14 @@ TEST(1) v = e.parse ("A.aa").execute (); EXPECT_EQ (v.to_string (), std::string ("static_a")); - v = e.parse ("A.new.a1").execute (); + v = e.parse ("A.new.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("17")); v = e.parse ("var a=A.new").execute (); - v = e.parse ("a.a5(-5); a.a1").execute (); + v = e.parse ("a.a5(-5); a.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("-5")); // mapping of property assignment to method - v = e.parse ("a.n = -177; a.a1").execute (); + v = e.parse ("a.n = -177; a.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("-177")); bool error = false; try { @@ -104,9 +104,9 @@ TEST(1) v = e.parse ("A.instance_count").execute (); EXPECT_EQ (v.to_int (), base_insts); // remaining instances - v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a1.a1").execute (); + v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a1.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("-15")); - v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a2.a1").execute (); + v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a2.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("107")); v = e.parse ("var a=A.new; a.get_e.to_s").execute (); @@ -218,12 +218,22 @@ TEST(2) EXPECT_EQ (v.to_string (), std::string ("5")); v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = [ a1, a2 ]; to_s(b.av)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); + v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a(a1); b.push_a(a2); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); + v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a_cref(a1); b.push_a_cptr(a2); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); + v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a_ref(a1); b.push_a_ptr(a2); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42")); v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(1); b.av_cref = [ a1, a2 ]; to_s(b.av_cref)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 1")); - v = e.parse ("var b=B.new; b.av = [ A.new(-13) ]; b.av_cptr = nil; to_s(b.av)").execute (); + v = e.parse ("var b=B.new; b.av_cptr = [ A.new(-13) ]; to_s(b.av)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: -13")); - v = e.parse ("var b=B.new; b.av = [ A.new(13) ]; b.av_ptr = nil; to_s(b.av)").execute (); + v = e.parse ("var b=B.new; b.av_ptr = [ A.new(13) ]; to_s(b.av)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: 13")); + v = e.parse ("var b=B.new; b.av = [ A.new(-13) ]; b.av_cptr = nil; to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("")); + v = e.parse ("var b=B.new; b.av = [ A.new(13) ]; b.av_ptr = nil; to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("")); v = e.parse ("var b=B.new; var a1=A.new(17); b.av_ref = [ a1 ]; to_s(b.av_ref)").execute (); EXPECT_EQ (v.to_string (), std::string ("A: 17")); v = e.parse ("var b=B.new; b.arg_is_not_nil(nil)").execute (); @@ -234,6 +244,21 @@ TEST(2) EXPECT_EQ (v.to_string (), std::string ("17")); v = e.parse ("var b=B.new; b.bx(-1)").execute (); EXPECT_EQ (v.to_string (), std::string ("xz")); + + // List to constructor call + v = e.parse ("var b=B.new; b.av = [ [5, 6], [4, 6, 0.5], [42] ]; to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 11,A: 5,A: 42")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_cref([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_cptr([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_ref([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_ptr([ 17 ]); to_s(b.av)").execute (); + EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17")); + /* TODO: No detailed type analysis for ambiguity resolution so far: v = e.parse ("var b=B.new; b.bx('hello', 1)").execute (); @@ -316,15 +341,15 @@ TEST(2) EXPECT_EQ (v.to_string (), std::string ("A: 177")); v = e.parse ("b.amember_or_nil(false)").execute (); EXPECT_EQ (v.to_string (), std::string ("nil")); - v = e.parse ("b.amember_ptr.a5(177); b.amember_ref.a1").execute (); + v = e.parse ("b.amember_ptr.a5(177); b.amember_ref.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("177")); - v = e.parse ("b.amember_ref.a1c").execute (); + v = e.parse ("b.amember_ref.get_n_const").execute (); EXPECT_EQ (v.to_string (), std::string ("177")); - v = e.parse ("b.amember_cref.a1c").execute (); + v = e.parse ("b.amember_cref.get_n_const").execute (); EXPECT_EQ (v.to_string (), std::string ("177")); error = false; try { - v = e.parse ("b.amember_cref.a1").execute (); + v = e.parse ("b.amember_cref.get_n").execute (); } catch (...) { // can't call non-const method on const ref error = true; @@ -334,16 +359,16 @@ TEST(2) // references: storage in variables v = e.parse ("var aref = b.amember_ptr").execute (); v = e.parse ("aref.n = 178").execute (); - v = e.parse ("aref.a1").execute (); + v = e.parse ("aref.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("178")); - v = e.parse ("aref.a1 == 178").execute (); + v = e.parse ("aref.get_n == 178").execute (); EXPECT_EQ (v.to_string (), std::string ("true")); - v = e.parse ("b.amember_ref.a1").execute (); + v = e.parse ("b.amember_ref.get_n").execute (); EXPECT_EQ (v.to_string (), std::string ("178")); // references: storage in variables v = e.parse ("var aref = b.amember_cptr").execute (); - v = e.parse ("aref.a1c").execute (); + v = e.parse ("aref.get_n_const").execute (); EXPECT_EQ (v.to_string (), std::string ("178")); error = false; try { diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index 473bff366..77c1e746a 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -265,7 +265,15 @@ bool TestBase::do_test (bool editable, bool slow) reset_checkpoint (); - execute (this); + try { + execute (this); + } catch (tl::Exception &ex) { + raise (std::string ("Exception caught: ") + ex.msg ()); + } catch (std::runtime_error &ex) { + raise (std::string ("std::runtime_error caught: ") + ex.what ()); + } catch (...) { + raise (std::string ("unspecific exception caught: ")); + } m_testtmp.clear ();