]> granicus.if.org Git - icinga2/blobdiff - lib/config/expression.cpp
Merge pull request #6999 from Icinga/bugfix/compiler-warnings
[icinga2] / lib / config / expression.cpp
index 1f6ed83483d2f81778d50cc9fcfcb98afaa5b24d..814a153047881c606298f160df2456fa1a89c3bc 100644 (file)
@@ -1,21 +1,4 @@
-/******************************************************************************
- * Icinga 2                                                                   *
- * Copyright (C) 2012-2016 Icinga Development Team (https://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 | (c) 2012 Icinga GmbH | GPLv2+ */
 
 #include "config/expression.hpp"
 #include "config/configitem.hpp"
 #include "base/exception.hpp"
 #include "base/scriptglobal.hpp"
 #include "base/loader.hpp"
+#include "base/reference.hpp"
+#include "base/namespace.hpp"
+#include <boost/exception_ptr.hpp>
+#include <boost/exception/errinfo_nested_exception.hpp>
 
 using namespace icinga;
 
 boost::signals2::signal<void (ScriptFrame&, ScriptError *ex, const DebugInfo&)> Expression::OnBreakpoint;
 boost::thread_specific_ptr<bool> l_InBreakpointHandler;
 
-Expression::~Expression(void)
+Expression::~Expression()
 { }
 
 void Expression::ScriptBreakpoint(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di)
@@ -70,7 +57,8 @@ ExpressionResult Expression::Evaluate(ScriptFrame& frame, DebugHint *dhint) cons
        } catch (const std::exception& ex) {
                frame.DecreaseStackDepth();
 
-               BOOST_THROW_EXCEPTION(ScriptError("Error while evaluating expression: " + String(ex.what()), GetDebugInfo()));
+               BOOST_THROW_EXCEPTION(ScriptError("Error while evaluating expression: " + String(ex.what()), GetDebugInfo())
+                       << boost::errinfo_nested_exception(boost::current_exception()));
        }
 
        frame.DecreaseStackDepth();
@@ -81,25 +69,25 @@ bool Expression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent,
        return false;
 }
 
-const DebugInfo& Expression::GetDebugInfo(void) const
+const DebugInfo& Expression::GetDebugInfo() const
 {
        static DebugInfo debugInfo;
        return debugInfo;
 }
 
-Expression *icinga::MakeIndexer(ScopeSpecifier scopeSpec, const String& index)
+std::unique_ptr<Expression> icinga::MakeIndexer(ScopeSpecifier scopeSpec, const String& index)
 {
-       Expression *scope = new GetScopeExpression(scopeSpec);
-       return new IndexerExpression(scope, MakeLiteral(index));
+       std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)};
+       return std::unique_ptr<Expression>(new IndexerExpression(std::move(scope), MakeLiteral(index)));
 }
 
-void DictExpression::MakeInline(void)
+void DictExpression::MakeInline()
 {
        m_Inline = true;
 }
 
-LiteralExpression::LiteralExpression(const Value& value)
-       : m_Value(value)
+LiteralExpression::LiteralExpression(Value value)
+       : m_Value(std::move(value))
 { }
 
 ExpressionResult LiteralExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
@@ -107,20 +95,29 @@ ExpressionResult LiteralExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh
        return m_Value;
 }
 
-const DebugInfo& DebuggableExpression::GetDebugInfo(void) const
+const DebugInfo& DebuggableExpression::GetDebugInfo() const
 {
        return m_DebugInfo;
 }
 
+VariableExpression::VariableExpression(String variable, std::vector<std::shared_ptr<Expression> > imports, const DebugInfo& debugInfo)
+       : DebuggableExpression(debugInfo), m_Variable(std::move(variable)), m_Imports(std::move(imports))
+{
+       m_Imports.push_back(MakeIndexer(ScopeGlobal, "System"));
+       m_Imports.push_back(std::unique_ptr<Expression>(new IndexerExpression(MakeIndexer(ScopeGlobal, "System"), MakeLiteral("Configuration"))));
+       m_Imports.push_back(MakeIndexer(ScopeGlobal, "Types"));
+       m_Imports.push_back(MakeIndexer(ScopeGlobal, "Icinga"));
+}
+
 ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
 {
        Value value;
 
        if (frame.Locals && frame.Locals->Get(m_Variable, &value))
                return value;
-       else if (frame.Self.IsObject() && frame.Locals != static_cast<Object::Ptr>(frame.Self) && static_cast<Object::Ptr>(frame.Self)->HasOwnField(m_Variable))
-               return VMOps::GetField(frame.Self, m_Variable, frame.Sandboxed, m_DebugInfo);
-       else if (VMOps::FindVarImport(frame, m_Variable, &value, m_DebugInfo))
+       else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get<Object::Ptr>() && frame.Self.Get<Object::Ptr>()->GetOwnField(m_Variable, &value))
+               return value;
+       else if (VMOps::FindVarImport(frame, m_Imports, m_Variable, &value, m_DebugInfo))
                return value;
        else
                return ScriptGlobal::Get(m_Variable);
@@ -134,25 +131,66 @@ bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value
                *parent = frame.Locals;
 
                if (dhint)
-                       *dhint = NULL;
-       } else if (frame.Self.IsObject() && frame.Locals != static_cast<Object::Ptr>(frame.Self) && static_cast<Object::Ptr>(frame.Self)->HasOwnField(m_Variable)) {
+                       *dhint = nullptr;
+       } else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get<Object::Ptr>() && frame.Self.Get<Object::Ptr>()->HasOwnField(m_Variable)) {
                *parent = frame.Self;
 
                if (dhint && *dhint)
                        *dhint = new DebugHint((*dhint)->GetChild(m_Variable));
-       } else if (VMOps::FindVarImportRef(frame, m_Variable, parent, m_DebugInfo)) {
+       } else if (VMOps::FindVarImportRef(frame, m_Imports, m_Variable, parent, m_DebugInfo)) {
                return true;
        } else if (ScriptGlobal::Exists(m_Variable)) {
                *parent = ScriptGlobal::GetGlobals();
 
                if (dhint)
-                       *dhint = NULL;
+                       *dhint = nullptr;
        } else
                *parent = frame.Self;
 
        return true;
 }
 
+ExpressionResult RefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+       Value parent;
+       String index;
+
+       if (!m_Operand->GetReference(frame, false, &parent, &index, &dhint))
+               BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression.", m_DebugInfo));
+
+       if (!parent.IsObject())
+               BOOST_THROW_EXCEPTION(ScriptError("Cannot obtain reference for expression because parent is not an object.", m_DebugInfo));
+
+       return new Reference(parent, index);
+}
+
+ExpressionResult DerefExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+       ExpressionResult operand = m_Operand->Evaluate(frame);
+       CHECK_RESULT(operand);
+
+       Object::Ptr obj = operand.GetValue();
+       Reference::Ptr ref = dynamic_pointer_cast<Reference>(obj);
+
+       if (!ref)
+               BOOST_THROW_EXCEPTION(ScriptError("Invalid reference specified.", GetDebugInfo()));
+
+       return ref->Get();
+}
+
+bool DerefExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const
+{
+       ExpressionResult operand = m_Operand->Evaluate(frame);
+       if (operand.GetCode() != ResultOK)
+               return false;
+
+       Reference::Ptr ref = operand.GetValue();
+
+       *parent = ref->GetParent();
+       *index = ref->GetIndex();
+       return true;
+}
+
 ExpressionResult NegateExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
 {
        ExpressionResult operand = m_Operand->Evaluate(frame);
@@ -425,7 +463,8 @@ ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHin
 
        if (vfunc.IsObjectType<Type>()) {
                std::vector<Value> arguments;
-               for (Expression *arg : m_Args) {
+               arguments.reserve(m_Args.size());
+               for (const auto& arg : m_Args) {
                        ExpressionResult argres = arg->Evaluate(frame);
                        CHECK_RESULT(argres);
 
@@ -444,7 +483,8 @@ ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHin
                BOOST_THROW_EXCEPTION(ScriptError("Function is not marked as safe for sandbox mode.", m_DebugInfo));
 
        std::vector<Value> arguments;
-       for (Expression *arg : m_Args) {
+       arguments.reserve(m_Args.size());
+       for (const auto& arg : m_Args) {
                ExpressionResult argres = arg->Evaluate(frame);
                CHECK_RESULT(argres);
 
@@ -456,17 +496,17 @@ ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHin
 
 ExpressionResult ArrayExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
 {
-       Array::Ptr result = new Array();
-       result->Reserve(m_Expressions.size());
+       ArrayData result;
+       result.reserve(m_Expressions.size());
 
-       for (Expression *aexpr : m_Expressions) {
+       for (const auto& aexpr : m_Expressions) {
                ExpressionResult element = aexpr->Evaluate(frame);
                CHECK_RESULT(element);
 
-               result->Add(element.GetValue());
+               result.push_back(element.GetValue());
        }
 
-       return result;
+       return new Array(std::move(result));
 }
 
 ExpressionResult DictExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
@@ -481,8 +521,8 @@ ExpressionResult DictExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint
        Value result;
 
        try {
-               for (Expression *aexpr : m_Expressions) {
-                       ExpressionResult element = aexpr->Evaluate(frame, m_Inline ? dhint : NULL);
+               for (const auto& aexpr : m_Expressions) {
+                       ExpressionResult element = aexpr->Evaluate(frame, m_Inline ? dhint : nullptr);
                        CHECK_RESULT(element);
                        result = element.GetValue();
                }
@@ -561,7 +601,7 @@ ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint)
                }
        }
 
-       VMOps::SetField(parent, index, operand2.GetValue(), m_DebugInfo);
+       VMOps::SetField(parent, index, operand2.GetValue(), m_OverrideFrozen, m_DebugInfo);
 
        if (psdhint) {
                psdhint->AddMessage("=", m_DebugInfo);
@@ -573,6 +613,33 @@ ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint)
        return Empty;
 }
 
+void SetExpression::SetOverrideFrozen()
+{
+       m_OverrideFrozen = true;
+}
+
+ExpressionResult SetConstExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+       auto globals = ScriptGlobal::GetGlobals();
+
+       auto attr = globals->GetAttribute(m_Name);
+
+       if (dynamic_pointer_cast<ConstEmbeddedNamespaceValue>(attr)) {
+               std::ostringstream msgbuf;
+               msgbuf << "Value for constant '" << m_Name << "' was modified. This behaviour is deprecated.\n";
+               ShowCodeLocation(msgbuf, GetDebugInfo(), false);
+               Log(LogWarning, msgbuf.str());
+       }
+
+       ExpressionResult operandres = m_Operand->Evaluate(frame);
+       CHECK_RESULT(operandres);
+       Value operand = operandres.GetValue();
+
+       globals->SetAttribute(m_Name, std::make_shared<ConstEmbeddedNamespaceValue>(operand));
+
+       return Empty;
+}
+
 ExpressionResult ConditionalExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
 {
        ExpressionResult condition = m_Condition->Evaluate(frame, dhint);
@@ -638,7 +705,7 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *
 {
        Value vparent;
        String vindex;
-       DebugHint *psdhint = NULL;
+       DebugHint *psdhint = nullptr;
        bool free_psd = false;
 
        if (dhint)
@@ -649,10 +716,19 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *
 
        if (m_Operand1->GetReference(frame, init_dict, &vparent, &vindex, &psdhint)) {
                if (init_dict) {
-                       Value old_value =  VMOps::GetField(vparent, vindex, frame.Sandboxed, m_Operand1->GetDebugInfo());
+                       Value old_value;
+                       bool has_field = true;
+
+                       if (vparent.IsObject()) {
+                               Object::Ptr oparent = vparent;
+                               has_field = oparent->HasOwnField(vindex);
+                       }
+
+                       if (has_field)
+                               old_value = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_Operand1->GetDebugInfo());
 
                        if (old_value.IsEmpty() && !old_value.IsString())
-                               VMOps::SetField(vparent, vindex, new Dictionary(), m_Operand1->GetDebugInfo());
+                               VMOps::SetField(vparent, vindex, new Dictionary(), m_OverrideFrozen, m_Operand1->GetDebugInfo());
                }
 
                *parent = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_DebugInfo);
@@ -669,7 +745,7 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *
                if (psdhint)
                        *dhint = new DebugHint(psdhint->GetChild(*index));
                else
-                       *dhint = NULL;
+                       *dhint = nullptr;
        }
 
        if (free_psd)
@@ -678,18 +754,23 @@ bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *
        return true;
 }
 
-void icinga::BindToScope(Expression *& expr, ScopeSpecifier scopeSpec)
+void IndexerExpression::SetOverrideFrozen()
+{
+       m_OverrideFrozen = true;
+}
+
+void icinga::BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec)
 {
-       DictExpression *dexpr = dynamic_cast<DictExpression *>(expr);
+       auto *dexpr = dynamic_cast<DictExpression *>(expr.get());
 
        if (dexpr) {
-               for (Expression *& expr : dexpr->m_Expressions)
+               for (auto& expr : dexpr->m_Expressions)
                        BindToScope(expr, scopeSpec);
 
                return;
        }
 
-       SetExpression *aexpr = dynamic_cast<SetExpression *>(expr);
+       auto *aexpr = dynamic_cast<SetExpression *>(expr.get());
 
        if (aexpr) {
                BindToScope(aexpr->m_Operand1, scopeSpec);
@@ -697,27 +778,25 @@ void icinga::BindToScope(Expression *& expr, ScopeSpecifier scopeSpec)
                return;
        }
 
-       IndexerExpression *iexpr = dynamic_cast<IndexerExpression *>(expr);
+       auto *iexpr = dynamic_cast<IndexerExpression *>(expr.get());
 
        if (iexpr) {
                BindToScope(iexpr->m_Operand1, scopeSpec);
                return;
        }
 
-       LiteralExpression *lexpr = dynamic_cast<LiteralExpression *>(expr);
+       auto *lexpr = dynamic_cast<LiteralExpression *>(expr.get());
 
        if (lexpr && lexpr->GetValue().IsString()) {
-               Expression *scope = new GetScopeExpression(scopeSpec);
-               expr = new IndexerExpression(scope, lexpr, lexpr->GetDebugInfo());
+               std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)};
+               expr.reset(new IndexerExpression(std::move(scope), std::move(expr), lexpr->GetDebugInfo()));
        }
 
-       VariableExpression *vexpr = dynamic_cast<VariableExpression *>(expr);
+       auto *vexpr = dynamic_cast<VariableExpression *>(expr.get());
 
        if (vexpr) {
-               Expression *scope = new GetScopeExpression(scopeSpec);
-               Expression *new_expr = new IndexerExpression(scope, MakeLiteral(vexpr->GetVariable()), vexpr->GetDebugInfo());
-               delete expr;
-               expr = new_expr;
+               std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)};
+               expr.reset(new IndexerExpression(std::move(scope), MakeLiteral(vexpr->GetVariable()), vexpr->GetDebugInfo()));
        }
 }
 
@@ -742,7 +821,7 @@ ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
        if (!name.IsString())
                BOOST_THROW_EXCEPTION(ScriptError("Template/object name must be a string", m_DebugInfo));
 
-       ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name);
+       ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name);
 
        if (!item)
                BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo));
@@ -764,8 +843,9 @@ ExpressionResult ImportDefaultTemplatesExpression::DoEvaluate(ScriptFrame& frame
                BOOST_THROW_EXCEPTION(ScriptError("Imports are not allowed in sandbox mode.", m_DebugInfo));
 
        String type = VMOps::GetField(frame.Self, "type", frame.Sandboxed, m_DebugInfo);
+       Type::Ptr ptype = Type::GetByName(type);
 
-       for (const ConfigItem::Ptr& item : ConfigItem::GetDefaultTemplates(type)) {
+       for (const ConfigItem::Ptr& item : ConfigItem::GetDefaultTemplates(ptype)) {
                Dictionary::Ptr scope = item->GetScope();
 
                if (scope)
@@ -792,7 +872,18 @@ ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhin
        CHECK_RESULT(nameres);
 
        return VMOps::NewApply(frame, m_Type, m_Target, nameres.GetValue(), m_Filter,
-           m_Package, m_FKVar, m_FVVar, m_FTerm, m_ClosedVars, m_IgnoreOnError, m_Expression, m_DebugInfo);
+               m_Package, m_FKVar, m_FVVar, m_FTerm, m_ClosedVars, m_IgnoreOnError, m_Expression, m_DebugInfo);
+}
+
+ExpressionResult NamespaceExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+{
+       Namespace::Ptr ns = new Namespace(new ConstNamespaceBehavior());
+
+       ScriptFrame innerFrame(true, ns);
+       ExpressionResult result = m_Expression->Evaluate(innerFrame);
+       CHECK_RESULT(result);
+
+       return ns;
 }
 
 ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
@@ -800,6 +891,10 @@ ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
        if (frame.Sandboxed)
                BOOST_THROW_EXCEPTION(ScriptError("Object definitions are not allowed in sandbox mode.", m_DebugInfo));
 
+       ExpressionResult typeres = m_Type->Evaluate(frame, dhint);
+       CHECK_RESULT(typeres);
+       Type::Ptr type = typeres.GetValue();
+
        String name;
 
        if (m_Name) {
@@ -809,8 +904,8 @@ ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhi
                name = nameres.GetValue();
        }
 
-       return VMOps::NewObject(frame, m_Abstract, m_Type, name, m_Filter, m_Zone,
-           m_Package, m_DefaultTmpl, m_IgnoreOnError, m_ClosedVars, m_Expression, m_DebugInfo);
+       return VMOps::NewObject(frame, m_Abstract, type, name, m_Filter, m_Zone,
+               m_Package, m_DefaultTmpl, m_IgnoreOnError, m_ClosedVars, m_Expression, m_DebugInfo);
 }
 
 ExpressionResult ForExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
@@ -832,7 +927,8 @@ ExpressionResult LibraryExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh
        ExpressionResult libres = m_Operand->Evaluate(frame, dhint);
        CHECK_RESULT(libres);
 
-       Loader::LoadExtensionLibrary(libres.GetValue());
+       Log(LogNotice, "config")
+               << "Ignoring explicit load request for library \"" << libres << "\".";
 
        return Empty;
 }
@@ -842,7 +938,7 @@ ExpressionResult IncludeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh
        if (frame.Sandboxed)
                BOOST_THROW_EXCEPTION(ScriptError("Includes are not allowed in sandbox mode.", m_DebugInfo));
 
-       Expression *expr;
+       std::unique_ptr<Expression> expr;
        String name, path, pattern;
 
        switch (m_Type) {
@@ -900,35 +996,28 @@ ExpressionResult IncludeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dh
        try {
                res = expr->Evaluate(frame, dhint);
        } catch (const std::exception&) {
-               delete expr;
                throw;
        }
 
-       delete expr;
-
        return res;
 }
 
 ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
 {
-       ScriptBreakpoint(frame, NULL, GetDebugInfo());
+       ScriptBreakpoint(frame, nullptr, GetDebugInfo());
 
        return Empty;
 }
 
-ExpressionResult UsingExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
+ExpressionResult TryExceptExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
 {
-       if (frame.Sandboxed)
-               BOOST_THROW_EXCEPTION(ScriptError("Using directives are not allowed in sandbox mode.", m_DebugInfo));
-
-       ExpressionResult importres = m_Name->Evaluate(frame);
-       CHECK_RESULT(importres);
-       Value import = importres.GetValue();
-
-       if (!import.IsObjectType<Dictionary>())
-               BOOST_THROW_EXCEPTION(ScriptError("The parameter must resolve to an object of type 'Dictionary'", m_DebugInfo));
-
-       ScriptFrame::AddImport(import);
+       try {
+               ExpressionResult tryResult = m_TryBody->Evaluate(frame, dhint);
+               CHECK_RESULT(tryResult);
+       } catch (const std::exception&) {
+               ExpressionResult exceptResult = m_ExceptBody->Evaluate(frame, dhint);
+               CHECK_RESULT(exceptResult);
+       }
 
        return Empty;
 }