From a9e2bc4b4038cd79ca84a004bd560b0d1384e8f9 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 11 Dec 2014 21:12:34 +0100 Subject: [PATCH] Implement a way to call methods on objects fixes #8071 --- lib/base/utility.cpp | 4 --- lib/config/CMakeLists.txt | 1 + lib/config/config_parser.yy | 8 ++++- lib/config/expression.cpp | 36 +++++++++++++++++-- lib/config/expression.hpp | 8 +++-- lib/config/vmframe.cpp | 24 +++++++++++++ lib/config/vmframe.hpp | 70 +++++++++++++++++++++++++++---------- lib/config/vmops.hpp | 13 +++++-- 8 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 lib/config/vmframe.cpp diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index 31c766b92..80e861f48 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -26,7 +26,6 @@ #include "base/utility.hpp" #include "base/json.hpp" #include "base/objectlock.hpp" -#include "base/scriptfunction.hpp" #include #include #include @@ -58,9 +57,6 @@ using namespace icinga; boost::thread_specific_ptr Utility::m_ThreadName; boost::thread_specific_ptr Utility::m_RandSeed; -REGISTER_SCRIPTFUNCTION(escape, &Utility::EscapeString); -REGISTER_SCRIPTFUNCTION(unescape, &Utility::UnescapeString); - /** * Demangles a symbol name. * diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 596c70ccc..f6dffe6ad 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -35,6 +35,7 @@ set(config_SOURCES configcompilercontext.cpp configcompiler.cpp configitembuilder.cpp configitem.cpp ${FLEX_config_lexer_OUTPUTS} ${BISON_config_parser_OUTPUTS} configtype.cpp expression.cpp objectrule.cpp typerule.cpp typerulelist.cpp + vmframe.cpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index bef34dd59..f45ad237a 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -787,9 +787,15 @@ rterm_without_indexer: T_STRING { $$ = MakeLiteral(); } + | indexer '(' rterm_items ')' + { + $$ = new FunctionCallExpression(*$1, NULL, *$3, DebugInfoRange(@1, @4)); + delete $1; + delete $3; + } | rterm '(' rterm_items ')' { - $$ = new FunctionCallExpression($1, *$3, DebugInfoRange(@1, @4)); + $$ = new FunctionCallExpression(MakeIndexer("this"), $1, *$3, DebugInfoRange(@1, @4)); delete $3; } | T_IDENTIFIER diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 27ced4aa2..12fe5a531 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -224,14 +224,46 @@ Value LogicalOrExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const Value FunctionCallExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const { - Value funcName = m_FName->Evaluate(frame); + Object::Ptr self; + Value funcName; + + if (!m_IName.empty()) { + Value result = m_IName[0]->Evaluate(frame); + + if (m_IName.size() == 2) { + if (!result.IsObject()) + BOOST_THROW_EXCEPTION(ScriptError("Tried to invoke method on something that is not an Object.", GetDebugInfo())); + + self = result; + } + + for (int i = 1; i < m_IName.size(); i++) { + if (result.IsEmpty()) + return Empty; + + Value index = m_IName[i]->Evaluate(frame); + result = VMOps::GetField(result, index); + + if (i == m_IName.size() - 2) { + if (!result.IsObject()) + BOOST_THROW_EXCEPTION(ScriptError("Tried to invoke method on something that is not an Object.", GetDebugInfo())); + + self = result; + } + } + + funcName = result; + } + + if (m_FName) + funcName = m_FName->Evaluate(frame); std::vector arguments; BOOST_FOREACH(Expression *arg, m_Args) { arguments.push_back(arg->Evaluate(frame)); } - return VMOps::FunctionCall(frame, funcName, arguments); + return VMOps::FunctionCall(frame, self, funcName, arguments); } Value ArrayExpression::DoEvaluate(VMFrame& frame, DebugHint *dhint) const diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 924c44783..830321507 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -516,12 +516,15 @@ protected: class I2_CONFIG_API FunctionCallExpression : public DebuggableExpression { public: - FunctionCallExpression(Expression *fname, const std::vector& args, const DebugInfo& debugInfo = DebugInfo()) - : DebuggableExpression(debugInfo), m_FName(fname), m_Args(args) + FunctionCallExpression(const std::vector iname, Expression *fname, const std::vector& args, const DebugInfo& debugInfo = DebugInfo()) + : DebuggableExpression(debugInfo), m_IName(iname), m_FName(fname), m_Args(args) { } ~FunctionCallExpression(void) { + BOOST_FOREACH(Expression *expr, m_IName) + delete expr; + delete m_FName; BOOST_FOREACH(Expression *expr, m_Args) @@ -532,6 +535,7 @@ protected: virtual Value DoEvaluate(VMFrame& frame, DebugHint *dhint) const; public: + std::vector m_IName; Expression *m_FName; std::vector m_Args; }; diff --git a/lib/config/vmframe.cpp b/lib/config/vmframe.cpp new file mode 100644 index 000000000..42b7fa120 --- /dev/null +++ b/lib/config/vmframe.cpp @@ -0,0 +1,24 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "config/vmframe.hpp" + +using namespace icinga; + +boost::thread_specific_ptr VMFrame::m_CurrentFrame; diff --git a/lib/config/vmframe.hpp b/lib/config/vmframe.hpp index 68ca2b76d..1c8e9fd3d 100644 --- a/lib/config/vmframe.hpp +++ b/lib/config/vmframe.hpp @@ -1,27 +1,28 @@ /****************************************************************************** -* Icinga 2 * -* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * -* * -* This program is free software; you can redistribute it and/or * -* modify it under the terms of the GNU General Public License * -* as published by the Free Software Foundation; either version 2 * -* of the License, or (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program; if not, write to the Free Software Foundation * -* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * -******************************************************************************/ + * Icinga 2 * + * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ #ifndef VMFRAME_H #define VMFRAME_H #include "config/i2-config.hpp" #include "base/dictionary.hpp" +#include namespace icinga { @@ -30,14 +31,45 @@ struct VMFrame { Dictionary::Ptr Locals; Object::Ptr Self; + VMFrame *NextFrame; VMFrame(void) : Locals(new Dictionary()), Self(Locals) - { } + { + NextFrame = GetCurrentFrame(); + SetCurrentFrame(this); + } VMFrame(const Object::Ptr& self) : Locals(new Dictionary()), Self(self) - { } + { + NextFrame = GetCurrentFrame(); + SetCurrentFrame(this); + } + + ~VMFrame(void) + { + ASSERT(GetCurrentFrame() == this); + SetCurrentFrame(NextFrame); + } + + static inline VMFrame *GetCurrentFrame(void) + { + VMFrame **pframe = m_CurrentFrame.get(); + + if (pframe) + return *pframe; + else + return NULL; + } + +private: + static boost::thread_specific_ptr m_CurrentFrame; + + static inline void SetCurrentFrame(VMFrame *frame) + { + m_CurrentFrame.reset(new VMFrame *(frame)); + } }; } diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index b7861e7a2..0ebd20f05 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -35,6 +35,7 @@ #include "base/convert.hpp" #include "base/objectlock.hpp" #include +#include #include #include @@ -57,7 +58,7 @@ public: return ScriptVariable::Get(name); } - static inline Value FunctionCall(VMFrame& frame, const Value& funcName, const std::vector& arguments) + static inline Value FunctionCall(VMFrame& frame, const Object::Ptr& self, const Value& funcName, const std::vector& arguments) { ScriptFunction::Ptr func; @@ -69,6 +70,13 @@ public: if (!func) BOOST_THROW_EXCEPTION(ScriptError("Function '" + funcName + "' does not exist.")); + boost::shared_ptr vframe; + + if (self) + vframe = boost::make_shared(self); /* passes self to the callee using a TLS variable */ + else + vframe = boost::make_shared(); + return func->Invoke(arguments); } @@ -287,7 +295,8 @@ private: if (arguments.size() < funcargs.size()) BOOST_THROW_EXCEPTION(std::invalid_argument("Too few arguments for function")); - VMFrame frame; + VMFrame *vframe = VMFrame::GetCurrentFrame(); + VMFrame frame(vframe->Self); if (closedVars) closedVars->CopyTo(frame.Locals);