diff options
Diffstat (limited to 'lib/config/expression.cpp')
-rw-r--r-- | lib/config/expression.cpp | 1078 |
1 files changed, 1078 insertions, 0 deletions
diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp new file mode 100644 index 0000000..6a709ac --- /dev/null +++ b/lib/config/expression.cpp @@ -0,0 +1,1078 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "config/expression.hpp" +#include "config/configitem.hpp" +#include "config/configcompiler.hpp" +#include "config/vmops.hpp" +#include "base/array.hpp" +#include "base/json.hpp" +#include "base/object.hpp" +#include "base/logger.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::ScriptBreakpoint(ScriptFrame& frame, ScriptError *ex, const DebugInfo& di) +{ + bool *inHandler = l_InBreakpointHandler.get(); + if (!inHandler || !*inHandler) { + inHandler = new bool(true); + l_InBreakpointHandler.reset(inHandler); + OnBreakpoint(frame, ex, di); + *inHandler = false; + } +} + +ExpressionResult Expression::Evaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + try { +#ifdef I2_DEBUG +/* std::ostringstream msgbuf; + ShowCodeLocation(msgbuf, GetDebugInfo(), false); + Log(LogDebug, "Expression") + << "Executing:\n" << msgbuf.str();*/ +#endif /* I2_DEBUG */ + + frame.IncreaseStackDepth(); + ExpressionResult result = DoEvaluate(frame, dhint); + frame.DecreaseStackDepth(); + return result; + } catch (ScriptError& ex) { + frame.DecreaseStackDepth(); + + ScriptBreakpoint(frame, &ex, GetDebugInfo()); + throw; + } catch (const std::exception& ex) { + frame.DecreaseStackDepth(); + + BOOST_THROW_EXCEPTION(ScriptError("Error while evaluating expression: " + String(ex.what()), GetDebugInfo()) + << boost::errinfo_nested_exception(boost::current_exception())); + } + + frame.DecreaseStackDepth(); +} + +bool Expression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const +{ + return false; +} + +const DebugInfo& Expression::GetDebugInfo() const +{ + static DebugInfo debugInfo; + return debugInfo; +} + +std::unique_ptr<Expression> icinga::MakeIndexer(ScopeSpecifier scopeSpec, const String& index) +{ + std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)}; + return std::unique_ptr<Expression>(new IndexerExpression(std::move(scope), MakeLiteral(index))); +} + +void DictExpression::MakeInline() +{ + m_Inline = true; +} + +LiteralExpression::LiteralExpression(Value value) + : m_Value(std::move(value)) +{ } + +ExpressionResult LiteralExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + return m_Value; +} + +const DebugInfo& DebuggableExpression::GetDebugInfo() const +{ + return m_DebugInfo; +} + +VariableExpression::VariableExpression(String variable, std::vector<Expression::Ptr> imports, const DebugInfo& debugInfo) + : DebuggableExpression(debugInfo), m_Variable(std::move(variable)), m_Imports(std::move(imports)) +{ + m_Imports.push_back(MakeIndexer(ScopeGlobal, "System").release()); + m_Imports.push_back(new IndexerExpression(MakeIndexer(ScopeGlobal, "System"), MakeLiteral("Configuration"))); + m_Imports.push_back(MakeIndexer(ScopeGlobal, "Types").release()); + m_Imports.push_back(MakeIndexer(ScopeGlobal, "Icinga").release()); +} + +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 != 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); +} + +bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const +{ + *index = m_Variable; + + if (frame.Locals && frame.Locals->Contains(m_Variable)) { + *parent = frame.Locals; + + if (dhint) + *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_Imports, m_Variable, parent, m_DebugInfo)) { + return true; + } else if (ScriptGlobal::Exists(m_Variable)) { + *parent = ScriptGlobal::GetGlobals(); + + if (dhint) + *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); + CHECK_RESULT(operand); + + return ~(long)operand.GetValue(); +} + +ExpressionResult LogicalNegateExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand = m_Operand->Evaluate(frame); + CHECK_RESULT(operand); + + return !operand.GetValue().ToBool(); +} + +ExpressionResult AddExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() + operand2.GetValue(); +} + +ExpressionResult SubtractExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() - operand2.GetValue(); +} + +ExpressionResult MultiplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() * operand2.GetValue(); +} + +ExpressionResult DivideExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() / operand2.GetValue(); +} + +ExpressionResult ModuloExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() % operand2.GetValue(); +} + +ExpressionResult XorExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() ^ operand2.GetValue(); +} + +ExpressionResult BinaryAndExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() & operand2.GetValue(); +} + +ExpressionResult BinaryOrExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() | operand2.GetValue(); +} + +ExpressionResult ShiftLeftExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() << operand2.GetValue(); +} + +ExpressionResult ShiftRightExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() >> operand2.GetValue(); +} + +ExpressionResult EqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() == operand2.GetValue(); +} + +ExpressionResult NotEqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() != operand2.GetValue(); +} + +ExpressionResult LessThanExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() < operand2.GetValue(); +} + +ExpressionResult GreaterThanExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() > operand2.GetValue(); +} + +ExpressionResult LessThanOrEqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() <= operand2.GetValue(); +} + +ExpressionResult GreaterThanOrEqualExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand1.GetValue() >= operand2.GetValue(); +} + +ExpressionResult InExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + if (operand2.GetValue().IsEmpty()) + return false; + else if (!operand2.GetValue().IsObjectType<Array>()) + BOOST_THROW_EXCEPTION(ScriptError("Invalid right side argument for 'in' operator: " + JsonEncode(operand2.GetValue()), m_DebugInfo)); + + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1) + + Array::Ptr arr = operand2.GetValue(); + return arr->Contains(operand1.GetValue()); +} + +ExpressionResult NotInExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + if (operand2.GetValue().IsEmpty()) + return true; + else if (!operand2.GetValue().IsObjectType<Array>()) + BOOST_THROW_EXCEPTION(ScriptError("Invalid right side argument for 'in' operator: " + JsonEncode(operand2.GetValue()), m_DebugInfo)); + + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + Array::Ptr arr = operand2.GetValue(); + return !arr->Contains(operand1.GetValue()); +} + +ExpressionResult LogicalAndExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + if (!operand1.GetValue().ToBool()) + return operand1; + else { + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand2.GetValue(); + } +} + +ExpressionResult LogicalOrExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + CHECK_RESULT(operand1); + + if (operand1.GetValue().ToBool()) + return operand1; + else { + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + CHECK_RESULT(operand2); + + return operand2.GetValue(); + } +} + +ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + Value self, vfunc; + String index; + + if (m_FName->GetReference(frame, false, &self, &index)) + vfunc = VMOps::GetField(self, index, frame.Sandboxed, m_DebugInfo); + else { + ExpressionResult vfuncres = m_FName->Evaluate(frame); + CHECK_RESULT(vfuncres); + + vfunc = vfuncres.GetValue(); + } + + if (vfunc.IsObjectType<Type>()) { + std::vector<Value> arguments; + arguments.reserve(m_Args.size()); + for (const auto& arg : m_Args) { + ExpressionResult argres = arg->Evaluate(frame); + CHECK_RESULT(argres); + + arguments.push_back(argres.GetValue()); + } + + return VMOps::ConstructorCall(vfunc, arguments, m_DebugInfo); + } + + if (!vfunc.IsObjectType<Function>()) + BOOST_THROW_EXCEPTION(ScriptError("Argument is not a callable object.", m_DebugInfo)); + + Function::Ptr func = vfunc; + + if (!func->IsSideEffectFree() && frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Function is not marked as safe for sandbox mode.", m_DebugInfo)); + + std::vector<Value> arguments; + arguments.reserve(m_Args.size()); + for (const auto& arg : m_Args) { + ExpressionResult argres = arg->Evaluate(frame); + CHECK_RESULT(argres); + + arguments.push_back(argres.GetValue()); + } + + return VMOps::FunctionCall(frame, self, func, arguments); +} + +ExpressionResult ArrayExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ArrayData result; + result.reserve(m_Expressions.size()); + + for (const auto& aexpr : m_Expressions) { + ExpressionResult element = aexpr->Evaluate(frame); + CHECK_RESULT(element); + + result.push_back(element.GetValue()); + } + + return new Array(std::move(result)); +} + +ExpressionResult DictExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + Value self; + + if (!m_Inline) { + self = frame.Self; + frame.Self = new Dictionary(); + } + + Value result; + + try { + for (const auto& aexpr : m_Expressions) { + ExpressionResult element = aexpr->Evaluate(frame, m_Inline ? dhint : nullptr); + CHECK_RESULT(element); + result = element.GetValue(); + } + } catch (...) { + if (!m_Inline) + std::swap(self, frame.Self); + throw; + } + + if (m_Inline) + return result; + else { + std::swap(self, frame.Self); + return self; + } +} + +ExpressionResult GetScopeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (m_ScopeSpec == ScopeLocal) + return frame.Locals; + else if (m_ScopeSpec == ScopeThis) + return frame.Self; + else if (m_ScopeSpec == ScopeGlobal) + return ScriptGlobal::GetGlobals(); + else + VERIFY(!"Invalid scope."); +} + +static inline +void WarnOnImplicitlySetGlobalVar(const std::unique_ptr<Expression>& setLhs, const Value& setLhsParent, CombinedSetOp setOp, const DebugInfo& debug) +{ + auto var (dynamic_cast<VariableExpression*>(setLhs.get())); + + if (var && setLhsParent.IsObject()) { + auto ns (dynamic_pointer_cast<Namespace>(setLhsParent.Get<Object::Ptr>())); + + if (ns && ns == ScriptGlobal::GetGlobals() && debug.Path.GetLength()) { + const char *opStr = nullptr; + + switch (setOp) { + case OpSetLiteral: + opStr = "="; + break; + case OpSetAdd: + opStr = "+="; + break; + case OpSetSubtract: + opStr = "-="; + break; + case OpSetMultiply: + opStr = "*="; + break; + case OpSetDivide: + opStr = "/="; + break; + case OpSetModulo: + opStr = "%="; + break; + case OpSetXor: + opStr = "^="; + break; + case OpSetBinaryAnd: + opStr = "&="; + break; + case OpSetBinaryOr: + opStr = "|="; + break; + default: + VERIFY(!"Invalid opcode."); + } + + auto varName (var->GetVariable()); + + Log(LogWarning, "config") + << "Global variable '" << varName << "' has been set implicitly via '" << varName << ' ' << opStr << " ...' " << debug << "." + " Please set it explicitly via 'globals." << varName << ' ' << opStr << " ...' instead."; + } + } +} + +ExpressionResult SetExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Assignments are not allowed in sandbox mode.", m_DebugInfo)); + + DebugHint *psdhint = dhint; + + Value parent; + String index; + + if (!m_Operand1->GetReference(frame, true, &parent, &index, &psdhint)) + BOOST_THROW_EXCEPTION(ScriptError("Expression cannot be assigned to.", m_DebugInfo)); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame, dhint); + CHECK_RESULT(operand2); + + if (m_Op != OpSetLiteral) { + Value object = VMOps::GetField(parent, index, frame.Sandboxed, m_DebugInfo); + + switch (m_Op) { + case OpSetAdd: + operand2 = object + operand2; + break; + case OpSetSubtract: + operand2 = object - operand2; + break; + case OpSetMultiply: + operand2 = object * operand2; + break; + case OpSetDivide: + operand2 = object / operand2; + break; + case OpSetModulo: + operand2 = object % operand2; + break; + case OpSetXor: + operand2 = object ^ operand2; + break; + case OpSetBinaryAnd: + operand2 = object & operand2; + break; + case OpSetBinaryOr: + operand2 = object | operand2; + break; + default: + VERIFY(!"Invalid opcode."); + } + } + + VMOps::SetField(parent, index, operand2.GetValue(), m_OverrideFrozen, m_DebugInfo); + + if (psdhint) { + psdhint->AddMessage("=", m_DebugInfo); + + if (psdhint != dhint) + delete psdhint; + } + + WarnOnImplicitlySetGlobalVar(m_Operand1, parent, m_Op, m_DebugInfo); + + 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, new ConstEmbeddedNamespaceValue(operand)); + + return Empty; +} + +ExpressionResult ConditionalExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult condition = m_Condition->Evaluate(frame, dhint); + CHECK_RESULT(condition); + + if (condition.GetValue().ToBool()) + return m_TrueBranch->Evaluate(frame, dhint); + else if (m_FalseBranch) + return m_FalseBranch->Evaluate(frame, dhint); + + return Empty; +} + +ExpressionResult WhileExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("While loops are not allowed in sandbox mode.", m_DebugInfo)); + + for (;;) { + ExpressionResult condition = m_Condition->Evaluate(frame, dhint); + CHECK_RESULT(condition); + + if (!condition.GetValue().ToBool()) + break; + + ExpressionResult loop_body = m_LoopBody->Evaluate(frame, dhint); + CHECK_RESULT_LOOP(loop_body); + } + + return Empty; +} + +ExpressionResult ReturnExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand = m_Operand->Evaluate(frame); + CHECK_RESULT(operand); + + return ExpressionResult(operand.GetValue(), ResultReturn); +} + +ExpressionResult BreakExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + return ExpressionResult(Empty, ResultBreak); +} + +ExpressionResult ContinueExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + return ExpressionResult(Empty, ResultContinue); +} + +ExpressionResult IndexerExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult operand1 = m_Operand1->Evaluate(frame, dhint); + CHECK_RESULT(operand1); + + ExpressionResult operand2 = m_Operand2->Evaluate(frame, dhint); + CHECK_RESULT(operand2); + + return VMOps::GetField(operand1.GetValue(), operand2.GetValue(), frame.Sandboxed, m_DebugInfo); +} + +bool IndexerExpression::GetReference(ScriptFrame& frame, bool init_dict, Value *parent, String *index, DebugHint **dhint) const +{ + Value vparent; + String vindex; + DebugHint *psdhint = nullptr; + bool free_psd = false; + + if (dhint) + psdhint = *dhint; + + if (frame.Sandboxed) + init_dict = false; + + if (m_Operand1->GetReference(frame, init_dict, &vparent, &vindex, &psdhint)) { + if (init_dict) { + 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_OverrideFrozen, m_Operand1->GetDebugInfo()); + } + + *parent = VMOps::GetField(vparent, vindex, frame.Sandboxed, m_DebugInfo); + free_psd = true; + } else { + ExpressionResult operand1 = m_Operand1->Evaluate(frame); + *parent = operand1.GetValue(); + } + + ExpressionResult operand2 = m_Operand2->Evaluate(frame); + *index = operand2.GetValue(); + + if (dhint) { + if (psdhint) + *dhint = new DebugHint(psdhint->GetChild(*index)); + else + *dhint = nullptr; + } + + if (free_psd) + delete psdhint; + + return true; +} + +void IndexerExpression::SetOverrideFrozen() +{ + m_OverrideFrozen = true; +} + +void icinga::BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec) +{ + auto *dexpr = dynamic_cast<DictExpression *>(expr.get()); + + if (dexpr) { + for (auto& expr : dexpr->m_Expressions) + BindToScope(expr, scopeSpec); + + return; + } + + auto *aexpr = dynamic_cast<SetExpression *>(expr.get()); + + if (aexpr) { + BindToScope(aexpr->m_Operand1, scopeSpec); + + return; + } + + auto *iexpr = dynamic_cast<IndexerExpression *>(expr.get()); + + if (iexpr) { + BindToScope(iexpr->m_Operand1, scopeSpec); + return; + } + + auto *lexpr = dynamic_cast<LiteralExpression *>(expr.get()); + + if (lexpr && lexpr->GetValue().IsString()) { + std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)}; + expr.reset(new IndexerExpression(std::move(scope), std::move(expr), lexpr->GetDebugInfo())); + } + + auto *vexpr = dynamic_cast<VariableExpression *>(expr.get()); + + if (vexpr) { + std::unique_ptr<Expression> scope{new GetScopeExpression(scopeSpec)}; + expr.reset(new IndexerExpression(std::move(scope), MakeLiteral(vexpr->GetVariable()), vexpr->GetDebugInfo())); + } +} + +ExpressionResult ThrowExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ExpressionResult messageres = m_Message->Evaluate(frame); + CHECK_RESULT(messageres); + Value message = messageres.GetValue(); + BOOST_THROW_EXCEPTION(ScriptError(message, m_DebugInfo, m_IncompleteExpr)); +} + +ExpressionResult ImportExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Imports are not allowed in sandbox mode.", m_DebugInfo)); + + String type = VMOps::GetField(frame.Self, "type", frame.Sandboxed, m_DebugInfo); + ExpressionResult nameres = m_Name->Evaluate(frame); + CHECK_RESULT(nameres); + Value name = nameres.GetValue(); + + if (!name.IsString()) + BOOST_THROW_EXCEPTION(ScriptError("Template/object name must be a string", m_DebugInfo)); + + ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(Type::GetByName(type), name); + + if (!item) + BOOST_THROW_EXCEPTION(ScriptError("Import references unknown template: '" + name + "'", m_DebugInfo)); + + Dictionary::Ptr scope = item->GetScope(); + + if (scope) + scope->CopyTo(frame.Locals); + + ExpressionResult result = item->GetExpression()->Evaluate(frame, dhint); + CHECK_RESULT(result); + + return Empty; +} + +ExpressionResult ImportDefaultTemplatesExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + 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(ptype)) { + Dictionary::Ptr scope = item->GetScope(); + + if (scope) + scope->CopyTo(frame.Locals); + + ExpressionResult result = item->GetExpression()->Evaluate(frame, dhint); + CHECK_RESULT(result); + } + + return Empty; +} + +ExpressionResult FunctionExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + return VMOps::NewFunction(frame, m_Name, m_Args, m_ClosedVars, m_Expression); +} + +ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Apply rules are not allowed in sandbox mode.", m_DebugInfo)); + + ExpressionResult nameres = m_Name->Evaluate(frame); + 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); +} + +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 +{ + 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) { + ExpressionResult nameres = m_Name->Evaluate(frame, dhint); + CHECK_RESULT(nameres); + + name = nameres.GetValue(); + } + + 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 +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("For loops are not allowed in sandbox mode.", m_DebugInfo)); + + ExpressionResult valueres = m_Value->Evaluate(frame, dhint); + CHECK_RESULT(valueres); + + return VMOps::For(frame, m_FKVar, m_FVVar, valueres.GetValue(), m_Expression, m_DebugInfo); +} + +ExpressionResult LibraryExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Loading libraries is not allowed in sandbox mode.", m_DebugInfo)); + + ExpressionResult libres = m_Operand->Evaluate(frame, dhint); + CHECK_RESULT(libres); + + Log(LogNotice, "config") + << "Ignoring explicit load request for library \"" << libres << "\"."; + + return Empty; +} + +ExpressionResult IncludeExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + if (frame.Sandboxed) + BOOST_THROW_EXCEPTION(ScriptError("Includes are not allowed in sandbox mode.", m_DebugInfo)); + + std::unique_ptr<Expression> expr; + String name, path, pattern; + + switch (m_Type) { + case IncludeRegular: + { + ExpressionResult pathres = m_Path->Evaluate(frame, dhint); + CHECK_RESULT(pathres); + path = pathres.GetValue(); + } + + expr = ConfigCompiler::HandleInclude(m_RelativeBase, path, m_SearchIncludes, m_Zone, m_Package, m_DebugInfo); + break; + + case IncludeRecursive: + { + ExpressionResult pathres = m_Path->Evaluate(frame, dhint); + CHECK_RESULT(pathres); + path = pathres.GetValue(); + } + + { + ExpressionResult patternres = m_Pattern->Evaluate(frame, dhint); + CHECK_RESULT(patternres); + pattern = patternres.GetValue(); + } + + expr = ConfigCompiler::HandleIncludeRecursive(m_RelativeBase, path, pattern, m_Zone, m_Package, m_DebugInfo); + break; + + case IncludeZones: + { + ExpressionResult nameres = m_Name->Evaluate(frame, dhint); + CHECK_RESULT(nameres); + name = nameres.GetValue(); + } + + { + ExpressionResult pathres = m_Path->Evaluate(frame, dhint); + CHECK_RESULT(pathres); + path = pathres.GetValue(); + } + + { + ExpressionResult patternres = m_Pattern->Evaluate(frame, dhint); + CHECK_RESULT(patternres); + pattern = patternres.GetValue(); + } + + expr = ConfigCompiler::HandleIncludeZones(m_RelativeBase, name, path, pattern, m_Package, m_DebugInfo); + break; + } + + ExpressionResult res(Empty); + + try { + res = expr->Evaluate(frame, dhint); + } catch (const std::exception&) { + throw; + } + + return res; +} + +ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + ScriptBreakpoint(frame, nullptr, GetDebugInfo()); + + return Empty; +} + +ExpressionResult TryExceptExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + 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; +} + |