From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id EJwwFNiY71/aTQAAWB0awg (envelope-from ) for ; Fri, 01 Jan 2021 16:49:12 -0500 Received: by simark.ca (Postfix, from userid 112) id 98AE91F0C5; Fri, 1 Jan 2021 16:49:11 -0500 (EST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=0.4 required=5.0 tests=DKIM_SIGNED,MAILING_LIST_MULTI, RCVD_IN_BL_SPAMCOP_NET,T_DKIM_INVALID,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 271DE1F0B7 for ; Fri, 1 Jan 2021 16:49:08 -0500 (EST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E1F093896C30; Fri, 1 Jan 2021 21:48:41 +0000 (GMT) Received: from gateway33.websitewelcome.com (gateway33.websitewelcome.com [192.185.146.130]) by sourceware.org (Postfix) with ESMTPS id E63443896C16 for ; Fri, 1 Jan 2021 21:48:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org E63443896C16 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=tromey.com Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=tom@tromey.com Received: from cm13.websitewelcome.com (cm13.websitewelcome.com [100.42.49.6]) by gateway33.websitewelcome.com (Postfix) with ESMTP id 8611710BE638 for ; Fri, 1 Jan 2021 15:48:34 -0600 (CST) Received: from box5379.bluehost.com ([162.241.216.53]) by cmsmtp with SMTP id vSHikZEyuoE4DvSHikuHNw; Fri, 01 Jan 2021 15:48:34 -0600 X-Authority-Reason: nr=8 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tromey.com; s=default; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=LKKC/Iq0ACe7uY+VS1Q6syqQCJ5qtrjm9o/8Xlzotjg=; b=lm+JAjaaafvqtv5UOjzofuTSEB uRXnF8kr+n9gqF35BL/FCxFE4xYpCio2nO2tqgkUJyyO26IEqiOiXs7kPzpRG/zY6/zdAodHZ4tg9 bf4zKcHHdObeMrLiUKrjIillQ; Received: from 97-122-81-39.hlrn.qwest.net ([97.122.81.39]:60414 helo=localhost.localdomain) by box5379.bluehost.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.93) (envelope-from ) id 1kvSHi-0029lU-BW for gdb-patches@sourceware.org; Fri, 01 Jan 2021 14:48:34 -0700 From: Tom Tromey To: gdb-patches@sourceware.org Subject: [PATCH 142/203] Implement function call operations Date: Fri, 1 Jan 2021 14:46:22 -0700 Message-Id: <20210101214723.1784144-143-tom@tromey.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20210101214723.1784144-1-tom@tromey.com> References: <20210101214723.1784144-1-tom@tromey.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - box5379.bluehost.com X-AntiAbuse: Original Domain - sourceware.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - tromey.com X-BWhitelist: no X-Source-IP: 97.122.81.39 X-Source-L: No X-Exim-ID: 1kvSHi-0029lU-BW X-Source: X-Source-Args: X-Source-Dir: X-Source-Sender: 97-122-81-39.hlrn.qwest.net (localhost.localdomain) [97.122.81.39]:60414 X-Source-Auth: tom+tromey.com X-Email-Count: 143 X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTM3OS5ibHVlaG9zdC5jb20= X-Local-Domain: yes X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces@sourceware.org Sender: "Gdb-patches" This implement function call operations. The current function call code relies on some very lengthy code (evaluate_funcall is 398 lines...) to distinguish between the different opcodes that might appear in the callee position. Rather than try to replicate this, and have a function that tried to dissect many different kinds of operation subclass, this patch instead puts the work into the callee. A new operation::evaluate_funcall method is added, and then this is overridden in the classes that require special treatment. gdb/ChangeLog 2021-01-01 Tom Tromey * expression.h (class operation) : New method. * expop.h (class scope_operation) : New method. (class var_value_operation) : New method. (class structop_base_operation) : New method. (class structop_member_base): New class. (class structop_member_operation): Derive from structop_member_base. (class structop_mptr_operation): Derive from structop_member_base. (class funcall_operation): New class. * eval.c (operation::evaluate_funcall) (var_value_operation::evaluate_funcall) (scope_operation::evaluate_funcall) (structop_member_base::evaluate_funcall) (structop_base_operation::evaluate_funcall): New methods. --- gdb/ChangeLog | 18 +++ gdb/eval.c | 279 +++++++++++++++++++++++++++++++++++++++++++++++ gdb/expop.h | 55 +++++++++- gdb/expression.h | 9 ++ 4 files changed, 358 insertions(+), 3 deletions(-) diff --git a/gdb/eval.c b/gdb/eval.c index 2589e35ae30..ff3c58cd4a8 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -1202,6 +1202,285 @@ evaluate_funcall (type *expect_type, expression *exp, int *pos, var_func_name, expect_type); } +namespace expr +{ + +value * +operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + std::vector vals (args.size ()); + + value *callee = evaluate_with_coercion (exp, noside); + for (int i = 0; i < args.size (); ++i) + vals[i] = args[i]->evaluate_with_coercion (exp, noside); + + return evaluate_subexp_do_call (exp, noside, callee, vals, + nullptr, expect_type); +} + +value * +var_value_operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + if (!overload_resolution + || exp->language_defn->la_language != language_cplus) + return operation::evaluate_funcall (expect_type, exp, noside, args); + + std::vector argvec (args.size ()); + for (int i = 0; i < args.size (); ++i) + argvec[i] = args[i]->evaluate_with_coercion (exp, noside); + + struct symbol *symp; + find_overload_match (argvec, NULL, NON_METHOD, + NULL, std::get<0> (m_storage), + NULL, &symp, NULL, 0, noside); + + if (SYMBOL_TYPE (symp)->code () == TYPE_CODE_ERROR) + error_unknown_type (symp->print_name ()); + value *callee = evaluate_var_value (noside, std::get<1> (m_storage), symp); + + return evaluate_subexp_do_call (exp, noside, callee, argvec, + nullptr, expect_type); +} + +value * +scope_operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + if (!overload_resolution + || exp->language_defn->la_language != language_cplus) + return operation::evaluate_funcall (expect_type, exp, noside, args); + + /* Unpack it locally so we can properly handle overload + resolution. */ + const std::string &name = std::get<1> (m_storage); + struct type *type = std::get<0> (m_storage); + + symbol *function = NULL; + const char *function_name = NULL; + std::vector argvec (1 + args.size ()); + if (type->code () == TYPE_CODE_NAMESPACE) + { + function = cp_lookup_symbol_namespace (type->name (), + name.c_str (), + get_selected_block (0), + VAR_DOMAIN).symbol; + if (function == NULL) + error (_("No symbol \"%s\" in namespace \"%s\"."), + name.c_str (), type->name ()); + } + else + { + gdb_assert (type->code () == TYPE_CODE_STRUCT + || type->code () == TYPE_CODE_UNION); + function_name = name.c_str (); + + /* We need a properly typed value for method lookup. */ + argvec[0] = value_zero (type, lval_memory); + } + + for (int i = 0; i < args.size (); ++i) + argvec[i + 1] = args[i]->evaluate_with_coercion (exp, noside); + gdb::array_view arg_view = argvec; + + value *callee = nullptr; + if (function_name != nullptr) + { + int static_memfuncp; + + find_overload_match (arg_view, function_name, METHOD, + &argvec[0], nullptr, &callee, nullptr, + &static_memfuncp, 0, noside); + if (!static_memfuncp) + { + /* For the time being, we don't handle this. */ + error (_("Call to overloaded function %s requires " + "`this' pointer"), + function_name); + } + + arg_view = arg_view.slice (1); + } + else + { + symbol *symp; + arg_view = arg_view.slice (1); + find_overload_match (arg_view, nullptr, + NON_METHOD, nullptr, function, + nullptr, &symp, nullptr, 1, noside); + callee = value_of_variable (symp, get_selected_block (0)); + } + + return evaluate_subexp_do_call (exp, noside, callee, arg_view, + nullptr, expect_type); +} + +value * +structop_member_base::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + /* First, evaluate the structure into lhs. */ + value *lhs; + if (opcode () == STRUCTOP_MEMBER) + lhs = std::get<0> (m_storage)->evaluate_for_address (exp, noside); + else + lhs = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); + + std::vector vals (args.size () + 1); + gdb::array_view val_view = vals; + /* If the function is a virtual function, then the aggregate + value (providing the structure) plays its part by providing + the vtable. Otherwise, it is just along for the ride: call + the function directly. */ + value *rhs = std::get<1> (m_storage)->evaluate (nullptr, exp, noside); + value *callee; + + type *a1_type = check_typedef (value_type (rhs)); + if (a1_type->code () == TYPE_CODE_METHODPTR) + { + if (noside == EVAL_AVOID_SIDE_EFFECTS) + callee = value_zero (TYPE_TARGET_TYPE (a1_type), not_lval); + else + callee = cplus_method_ptr_to_value (&lhs, rhs); + + vals[0] = lhs; + } + else if (a1_type->code () == TYPE_CODE_MEMBERPTR) + { + struct type *type_ptr + = lookup_pointer_type (TYPE_SELF_TYPE (a1_type)); + struct type *target_type_ptr + = lookup_pointer_type (TYPE_TARGET_TYPE (a1_type)); + + /* Now, convert this value to an address. */ + lhs = value_cast (type_ptr, lhs); + + long mem_offset = value_as_long (rhs); + + callee = value_from_pointer (target_type_ptr, + value_as_long (lhs) + mem_offset); + callee = value_ind (callee); + + val_view = val_view.slice (1); + } + else + error (_("Non-pointer-to-member value used in pointer-to-member " + "construct")); + + for (int i = 0; i < args.size (); ++i) + vals[i + 1] = args[i]->evaluate_with_coercion (exp, noside); + + return evaluate_subexp_do_call (exp, noside, callee, val_view, + nullptr, expect_type); + +} + +value * +structop_base_operation::evaluate_funcall + (struct type *expect_type, struct expression *exp, enum noside noside, + const std::vector &args) +{ + std::vector vals (args.size () + 1); + /* First, evaluate the structure into vals[0]. */ + enum exp_opcode op = opcode (); + if (op == STRUCTOP_STRUCT) + { + /* If v is a variable in a register, and the user types + v.method (), this will produce an error, because v has no + address. + + A possible way around this would be to allocate a copy of + the variable on the stack, copy in the contents, call the + function, and copy out the contents. I.e. convert this + from call by reference to call by copy-return (or + whatever it's called). However, this does not work + because it is not the same: the method being called could + stash a copy of the address, and then future uses through + that address (after the method returns) would be expected + to use the variable itself, not some copy of it. */ + vals[0] = std::get<0> (m_storage)->evaluate_for_address (exp, noside); + } + else + { + vals[0] = std::get<0> (m_storage)->evaluate (nullptr, exp, noside); + /* Check to see if the operator '->' has been overloaded. + If the operator has been overloaded replace vals[0] with the + value returned by the custom operator and continue + evaluation. */ + while (unop_user_defined_p (op, vals[0])) + { + struct value *value = nullptr; + try + { + value = value_x_unop (vals[0], op, noside); + } + catch (const gdb_exception_error &except) + { + if (except.error == NOT_FOUND_ERROR) + break; + else + throw; + } + + vals[0] = value; + } + } + + for (int i = 0; i < args.size (); ++i) + vals[i + 1] = args[i]->evaluate_with_coercion (exp, noside); + gdb::array_view arg_view = vals; + + int static_memfuncp; + value *callee; + const char *tstr = std::get<1> (m_storage).c_str (); + if (overload_resolution + && exp->language_defn->la_language == language_cplus) + { + /* Language is C++, do some overload resolution before + evaluation. */ + value *val0 = vals[0]; + find_overload_match (arg_view, tstr, METHOD, + &val0, nullptr, &callee, nullptr, + &static_memfuncp, 0, noside); + vals[0] = val0; + } + else + /* Non-C++ case -- or no overload resolution. */ + { + struct value *temp = vals[0]; + + callee = value_struct_elt (&temp, &vals[1], tstr, + &static_memfuncp, + op == STRUCTOP_STRUCT + ? "structure" : "structure pointer"); + /* value_struct_elt updates temp with the correct value of the + ``this'' pointer if necessary, so modify it to reflect any + ``this'' changes. */ + vals[0] = value_from_longest (lookup_pointer_type (value_type (temp)), + value_address (temp) + + value_embedded_offset (temp)); + } + + /* Take out `this' if needed. */ + if (static_memfuncp) + arg_view = arg_view.slice (1); + + return evaluate_subexp_do_call (exp, noside, callee, arg_view, + nullptr, expect_type); +} + + +} /* namespace expr */ + /* Return true if type is integral or reference to integral */ static bool diff --git a/gdb/expop.h b/gdb/expop.h index 08e40345e76..de4b876de39 100644 --- a/gdb/expop.h +++ b/gdb/expop.h @@ -594,6 +594,11 @@ class scope_operation value *evaluate_for_address (struct expression *exp, enum noside noside) override; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; + enum exp_opcode opcode () const override { return OP_SCOPE; } @@ -631,6 +636,11 @@ class var_value_operation value *evaluate_for_address (struct expression *exp, enum noside noside) override; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; + enum exp_opcode opcode () const override { return OP_VAR_VALUE; } @@ -971,6 +981,11 @@ class structop_base_operation EVAL_AVOID_SIDE_EFFECTS); } + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; + protected: using tuple_holding_operation::tuple_holding_operation; @@ -1044,13 +1059,26 @@ class structop_ptr_operation } }; -class structop_member_operation +class structop_member_base : public tuple_holding_operation { public: using tuple_holding_operation::tuple_holding_operation; + value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) override; +}; + +class structop_member_operation + : public structop_member_base +{ +public: + + using structop_member_base::structop_member_base; + value *evaluate (struct type *expect_type, struct expression *exp, enum noside noside) override @@ -1067,11 +1095,11 @@ class structop_member_operation }; class structop_mptr_operation - : public tuple_holding_operation + : public structop_member_base { public: - using tuple_holding_operation::tuple_holding_operation; + using structop_member_base::structop_member_base; value *evaluate (struct type *expect_type, struct expression *exp, @@ -2068,6 +2096,27 @@ class array_operation enum noside noside, int nargs); }; +/* A function call. This holds the callee operation and the + arguments. */ +class funcall_operation + : public tuple_holding_operation> +{ +public: + + using tuple_holding_operation::tuple_holding_operation; + + value *evaluate (struct type *expect_type, + struct expression *exp, + enum noside noside) override + { + return std::get<0> (m_storage)->evaluate_funcall (expect_type, exp, noside, + std::get<1> (m_storage)); + } + + enum exp_opcode opcode () const override + { return OP_FUNCALL; } +}; + } /* namespace expr */ #endif /* EXPOP_H */ diff --git a/gdb/expression.h b/gdb/expression.h index 08a6424fdd2..a850f64d160 100644 --- a/gdb/expression.h +++ b/gdb/expression.h @@ -144,6 +144,15 @@ class operation virtual value *evaluate_for_address (struct expression *exp, enum noside noside); + /* Evaluate a function call, with this object as the callee. + EXPECT_TYPE, EXP, and NOSIDE have the same meaning as in + 'evaluate'. ARGS holds the operations that should be evaluated + to get the arguments to the call. */ + virtual value *evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args); + /* True if this is a constant expression. */ virtual bool constant_p () const { return false; } -- 2.26.2