1 /******************************************************************************
3 * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "config/expression.hpp"
21 #include "config/configitem.hpp"
22 #include "config/configitembuilder.hpp"
23 #include "config/applyrule.hpp"
24 #include "config/objectrule.hpp"
25 #include "base/array.hpp"
26 #include "base/json.hpp"
27 #include "base/scriptfunction.hpp"
28 #include "base/scriptvariable.hpp"
29 #include "base/utility.hpp"
30 #include "base/objectlock.hpp"
31 #include "base/object.hpp"
32 #include "base/logger.hpp"
33 #include "base/configerror.hpp"
34 #include <boost/foreach.hpp>
35 #include <boost/exception_ptr.hpp>
36 #include <boost/exception/errinfo_nested_exception.hpp>
38 using namespace icinga;
40 Expression::Expression(OpCallback op, const Value& operand1, const DebugInfo& di)
41 : m_Operator(op), m_Operand1(operand1), m_Operand2(), m_DebugInfo(di)
44 Expression::Expression(OpCallback op, const Value& operand1, const Value& operand2, const DebugInfo& di)
45 : m_Operator(op), m_Operand1(operand1), m_Operand2(operand2), m_DebugInfo(di)
48 Value Expression::Evaluate(const Dictionary::Ptr& locals, DebugHint *dhint) const
52 if (m_Operator != &Expression::OpLiteral) {
53 std::ostringstream msgbuf;
54 ShowCodeFragment(msgbuf, m_DebugInfo, false);
55 Log(LogDebug, "Expression")
56 << "Executing:\n" << msgbuf.str();
60 return m_Operator(this, locals, dhint);
61 } catch (const std::exception& ex) {
62 if (boost::get_error_info<boost::errinfo_nested_exception>(ex))
65 BOOST_THROW_EXCEPTION(ConfigError("Error while evaluating expression: " + String(ex.what())) << boost::errinfo_nested_exception(boost::current_exception()) << errinfo_debuginfo(m_DebugInfo));
69 void Expression::MakeInline(void)
71 if (m_Operator == &Expression::OpDict)
75 void Expression::DumpOperand(std::ostream& stream, const Value& operand, int indent) {
76 if (operand.IsObjectType<Array>()) {
77 Array::Ptr arr = operand;
78 stream << String(indent, ' ') << "Array:\n";
79 ObjectLock olock(arr);
80 BOOST_FOREACH(const Value& elem, arr) {
81 DumpOperand(stream, elem, indent + 1);
83 } else if (operand.IsObjectType<Expression>()) {
84 Expression::Ptr left = operand;
85 left->Dump(stream, indent);
87 stream << String(indent, ' ') << JsonEncode(operand) << "\n";
91 void Expression::Dump(std::ostream& stream, int indent) const
93 String sym = Utility::GetSymbolName(reinterpret_cast<const void *>(m_Operator));
94 stream << String(indent, ' ') << "op: " << Utility::DemangleSymbolName(sym) << "\n";
95 stream << String(indent, ' ') << "left:\n";
96 DumpOperand(stream, m_Operand1, indent + 1);
98 stream << String(indent, ' ') << "right:\n";
99 DumpOperand(stream, m_Operand2, indent + 1);
102 Value Expression::EvaluateOperand1(const Dictionary::Ptr& locals, DebugHint *dhint) const
104 return static_cast<Expression::Ptr>(m_Operand1)->Evaluate(locals, dhint);
107 Value Expression::EvaluateOperand2(const Dictionary::Ptr& locals, DebugHint *dhint) const
109 return static_cast<Expression::Ptr>(m_Operand2)->Evaluate(locals, dhint);
112 Value Expression::OpLiteral(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
114 return expr->m_Operand1;
117 Value Expression::OpVariable(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
119 Dictionary::Ptr scope = locals;
122 if (scope->Contains(expr->m_Operand1))
123 return scope->Get(expr->m_Operand1);
125 scope = scope->Get("__parent");
128 return ScriptVariable::Get(expr->m_Operand1);
131 Value Expression::OpNegate(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
133 return ~(long)expr->EvaluateOperand1(locals);
136 Value Expression::OpLogicalNegate(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
138 return !expr->EvaluateOperand1(locals).ToBool();
141 Value Expression::OpAdd(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
143 return expr->EvaluateOperand1(locals) + expr->EvaluateOperand2(locals);
146 Value Expression::OpSubtract(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
148 return expr->EvaluateOperand1(locals) - expr->EvaluateOperand2(locals);
151 Value Expression::OpMultiply(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
153 return expr->EvaluateOperand1(locals) * expr->EvaluateOperand2(locals);
156 Value Expression::OpDivide(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
158 return expr->EvaluateOperand1(locals) / expr->EvaluateOperand2(locals);
161 Value Expression::OpBinaryAnd(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
163 return expr->EvaluateOperand1(locals) & expr->EvaluateOperand2(locals);
166 Value Expression::OpBinaryOr(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
168 return expr->EvaluateOperand1(locals) | expr->EvaluateOperand2(locals);
171 Value Expression::OpShiftLeft(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
173 return expr->EvaluateOperand1(locals) << expr->EvaluateOperand2(locals);
176 Value Expression::OpShiftRight(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
178 return expr->EvaluateOperand1(locals) >> expr->EvaluateOperand2(locals);
181 Value Expression::OpEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
183 return expr->EvaluateOperand1(locals) == expr->EvaluateOperand2(locals);
186 Value Expression::OpNotEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
188 return expr->EvaluateOperand1(locals) != expr->EvaluateOperand2(locals);
191 Value Expression::OpLessThan(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
193 return expr->EvaluateOperand1(locals) < expr->EvaluateOperand2(locals);
196 Value Expression::OpGreaterThan(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
198 return expr->EvaluateOperand1(locals) > expr->EvaluateOperand2(locals);
201 Value Expression::OpLessThanOrEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
203 return expr->EvaluateOperand1(locals) <= expr->EvaluateOperand2(locals);
206 Value Expression::OpGreaterThanOrEqual(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
208 return expr->EvaluateOperand1(locals) >= expr->EvaluateOperand2(locals);
211 Value Expression::OpIn(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
213 Value right = expr->EvaluateOperand2(locals);
217 else if (!right.IsObjectType<Array>())
218 BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonEncode(right)));
220 Value left = expr->EvaluateOperand1(locals);
222 Array::Ptr arr = right;
224 ObjectLock olock(arr);
225 BOOST_FOREACH(const Value& value, arr) {
235 Value Expression::OpNotIn(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
237 return !OpIn(expr, locals, dhint);
240 Value Expression::OpLogicalAnd(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
242 return expr->EvaluateOperand1(locals).ToBool() && expr->EvaluateOperand2(locals).ToBool();
245 Value Expression::OpLogicalOr(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
247 return expr->EvaluateOperand1(locals).ToBool() || expr->EvaluateOperand2(locals).ToBool();
250 Value Expression::OpFunctionCall(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
252 Value funcName = expr->EvaluateOperand1(locals);
254 ScriptFunction::Ptr func;
256 if (funcName.IsObjectType<ScriptFunction>())
259 func = ScriptFunction::GetByName(funcName);
262 BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
264 Array::Ptr arr = expr->EvaluateOperand2(locals);
265 std::vector<Value> arguments;
266 for (Array::SizeType index = 0; index < arr->GetLength(); index++) {
267 const Expression::Ptr& aexpr = arr->Get(index);
268 arguments.push_back(aexpr->Evaluate(locals));
271 return func->Invoke(arguments);
274 Value Expression::OpArray(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
276 Array::Ptr arr = expr->m_Operand1;
277 Array::Ptr result = make_shared<Array>();
280 for (Array::SizeType index = 0; index < arr->GetLength(); index++) {
281 const Expression::Ptr& aexpr = arr->Get(index);
282 result->Add(aexpr->Evaluate(locals));
289 Value Expression::OpDict(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
291 Array::Ptr arr = expr->m_Operand1;
292 bool in_place = expr->m_Operand2;
293 Dictionary::Ptr result = make_shared<Dictionary>();
295 result->Set("__parent", locals);
298 for (Array::SizeType index = 0; index < arr->GetLength(); index++) {
299 const Expression::Ptr& aexpr = arr->Get(index);
300 Dictionary::Ptr alocals = in_place ? locals : result;
301 aexpr->Evaluate(alocals, dhint);
303 if (alocals->Contains("__result"))
308 Dictionary::Ptr xresult = result->ShallowClone();
309 xresult->Remove("__parent");
313 Value Expression::OpSet(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
315 Array::Ptr left = expr->m_Operand1;
316 Array::Ptr indexer = left->Get(0);
317 int csop = left->Get(1);
319 DebugHint *sdhint = dhint;
321 Value parent, object;
324 for (Array::SizeType i = 0; i < indexer->GetLength(); i++) {
325 Expression::Ptr indexExpr = indexer->Get(i);
326 String tempindex = indexExpr->Evaluate(locals, dhint);
328 if (i == indexer->GetLength() - 1)
333 object = locals->Get(tempindex);
337 Expression::Ptr eparent = make_shared<Expression>(&Expression::OpLiteral, parent, expr->m_DebugInfo);
338 Expression::Ptr eindex = make_shared<Expression>(&Expression::OpLiteral, tempindex, expr->m_DebugInfo);
340 Expression::Ptr eip = make_shared<Expression>(&Expression::OpIndexer, eparent, eindex, expr->m_DebugInfo);
341 object = eip->Evaluate(locals, dhint);
345 sdhint = sdhint->GetChild(index);
347 if (i != indexer->GetLength() - 1 && object.IsEmpty()) {
348 object = make_shared<Dictionary>();
350 Dictionary::Ptr pdict = parent;
351 pdict->Set(tempindex, object);
355 Value right = expr->EvaluateOperand2(locals, dhint);
357 if (csop != OpSetLiteral) {
358 Expression::OpCallback op;
362 op = &Expression::OpAdd;
365 op = &Expression::OpSubtract;
368 op = &Expression::OpMultiply;
371 op = &Expression::OpDivide;
374 VERIFY(!"Invalid opcode.");
377 Expression::Ptr ecp = make_shared<Expression>(op,
378 make_shared<Expression>(&Expression::OpLiteral, object, expr->m_DebugInfo),
379 make_shared<Expression>(&Expression::OpLiteral, right, expr->m_DebugInfo),
382 right = ecp->Evaluate(locals, dhint);
385 Dictionary::Ptr pdict = parent;
386 pdict->Set(index, right);
389 sdhint->AddMessage("=", expr->m_DebugInfo);
394 Value Expression::OpIndexer(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
396 Value value = expr->EvaluateOperand1(locals);
397 Value index = expr->EvaluateOperand2(locals);
399 if (value.IsObjectType<Dictionary>()) {
400 Dictionary::Ptr dict = value;
401 return dict->Get(index);
402 } else if (value.IsObjectType<Array>()) {
403 Array::Ptr arr = value;
404 return arr->Get(index);
405 } else if (value.IsObjectType<Object>()) {
406 Object::Ptr object = value;
407 Type::Ptr type = object->GetReflectionType();
410 BOOST_THROW_EXCEPTION(ConfigError("Dot operator applied to object which does not support reflection"));
412 int field = type->GetFieldId(index);
415 BOOST_THROW_EXCEPTION(ConfigError("Tried to access invalid property '" + index + "'"));
417 return object->GetField(field);
418 } else if (value.IsEmpty()) {
421 BOOST_THROW_EXCEPTION(ConfigError("Dot operator cannot be applied to type '" + value.GetTypeName() + "'"));
425 Value Expression::OpImport(const Expression *expr, const Dictionary::Ptr& locals, DebugHint *dhint)
427 Value type = expr->EvaluateOperand1(locals);
428 Value name = expr->EvaluateOperand2(locals);
430 ConfigItem::Ptr item = ConfigItem::GetObject(type, name);
433 BOOST_THROW_EXCEPTION(ConfigError("Import references unknown template: '" + name + "'"));
435 item->GetExpressionList()->Evaluate(locals, dhint);
440 Value Expression::FunctionWrapper(const std::vector<Value>& arguments, const Array::Ptr& funcargs, const Expression::Ptr& expr, const Dictionary::Ptr& scope)
442 if (arguments.size() < funcargs->GetLength())
443 BOOST_THROW_EXCEPTION(ConfigError("Too few arguments for function"));
445 Dictionary::Ptr locals = make_shared<Dictionary>();
446 locals->Set("__parent", scope);
448 for (std::vector<Value>::size_type i = 0; i < std::min(arguments.size(), funcargs->GetLength()); i++)
449 locals->Set(funcargs->Get(i), arguments[i]);
451 expr->Evaluate(locals);
452 return locals->Get("__result");
455 Value Expression::OpFunction(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint)
457 Array::Ptr left = expr->m_Operand1;
458 Expression::Ptr aexpr = left->Get(1);
459 String name = left->Get(0);
461 Array::Ptr funcargs = expr->m_Operand2;
462 ScriptFunction::Ptr func = make_shared<ScriptFunction>(boost::bind(&Expression::FunctionWrapper, _1, funcargs, aexpr, locals));
465 ScriptFunction::Register(name, func);
470 Value Expression::OpApply(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint)
472 Array::Ptr left = expr->m_Operand1;
473 Expression::Ptr exprl = expr->m_Operand2;
474 String type = left->Get(0);
475 String target = left->Get(1);
476 Expression::Ptr aname = left->Get(2);
477 Expression::Ptr filter = left->Get(3);
478 String fkvar = left->Get(4);
479 String fvvar = left->Get(5);
480 Expression::Ptr fterm = left->Get(6);
482 String name = aname->Evaluate(locals, dhint);
484 ApplyRule::AddRule(type, target, name, exprl, filter, fkvar, fvvar, fterm, expr->m_DebugInfo, locals);
489 Value Expression::OpObject(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint)
491 Array::Ptr left = expr->m_Operand1;
492 Expression::Ptr exprl = expr->m_Operand2;
493 bool abstract = left->Get(0);
494 String type = left->Get(1);
495 Expression::Ptr aname = left->Get(2);
496 Expression::Ptr filter = left->Get(3);
497 String zone = left->Get(4);
499 String name = aname->Evaluate(locals, dhint);
501 ConfigItemBuilder::Ptr item = make_shared<ConfigItemBuilder>(expr->m_DebugInfo);
503 String checkName = name;
506 shared_ptr<NameComposer> nc = dynamic_pointer_cast<NameComposer>(Type::GetByName(type));
509 checkName = nc->MakeName(name, Dictionary::Ptr());
512 if (!checkName.IsEmpty()) {
513 ConfigItem::Ptr oldItem = ConfigItem::GetObject(type, checkName);
516 std::ostringstream msgbuf;
517 msgbuf << "Object '" << name << "' of type '" << type << "' re-defined: " << expr->m_DebugInfo << "; previous definition: " << oldItem->GetDebugInfo();
518 BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(expr->m_DebugInfo));
524 if (name.FindFirstOf("!") != String::NPos) {
525 std::ostringstream msgbuf;
526 msgbuf << "Name for object '" << name << "' of type '" << type << "' is invalid: Object names may not contain '!'";
527 BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(expr->m_DebugInfo));
532 item->AddExpression(exprl);
533 item->SetAbstract(abstract);
534 item->SetScope(locals);
536 item->Compile()->Register();
538 ObjectRule::AddRule(type, name, exprl, filter, expr->m_DebugInfo, locals);
543 Value Expression::OpFor(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint)
545 Array::Ptr left = expr->m_Operand1;
546 String kvar = left->Get(0);
547 String vvar = left->Get(1);
548 Expression::Ptr aexpr = left->Get(2);
549 Expression::Ptr ascope = expr->m_Operand2;
551 Value value = aexpr->Evaluate(locals, dhint);
553 if (value.IsObjectType<Array>()) {
555 BOOST_THROW_EXCEPTION(ConfigError("Cannot use dictionary iterator for array.") << errinfo_debuginfo(expr->m_DebugInfo));
557 Array::Ptr arr = value;
559 ObjectLock olock(arr);
560 BOOST_FOREACH(const Value& value, arr) {
561 Dictionary::Ptr xlocals = make_shared<Dictionary>();
562 xlocals->Set("__parent", locals);
563 xlocals->Set(kvar, value);
565 ascope->Evaluate(xlocals, dhint);
567 } else if (value.IsObjectType<Dictionary>()) {
569 BOOST_THROW_EXCEPTION(ConfigError("Cannot use array iterator for dictionary.") << errinfo_debuginfo(expr->m_DebugInfo));
571 Dictionary::Ptr dict = value;
573 ObjectLock olock(dict);
574 BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
575 Dictionary::Ptr xlocals = make_shared<Dictionary>();
576 xlocals->Set("__parent", locals);
577 xlocals->Set(kvar, kv.first);
578 xlocals->Set(vvar, kv.second);
580 ascope->Evaluate(xlocals, dhint);
583 BOOST_THROW_EXCEPTION(ConfigError("Invalid type in __for expression: " + value.GetTypeName()) << errinfo_debuginfo(expr->m_DebugInfo));
588 Dictionary::Ptr DebugHint::ToDictionary(void) const
590 Dictionary::Ptr result = make_shared<Dictionary>();
592 Array::Ptr messages = make_shared<Array>();
593 typedef std::pair<String, DebugInfo> MessageType;
594 BOOST_FOREACH(const MessageType& message, Messages) {
595 Array::Ptr amsg = make_shared<Array>();
596 amsg->Add(message.first);
597 amsg->Add(message.second.Path);
598 amsg->Add(message.second.FirstLine);
599 amsg->Add(message.second.FirstColumn);
600 amsg->Add(message.second.LastLine);
601 amsg->Add(message.second.LastColumn);
605 result->Set("messages", messages);
607 Dictionary::Ptr properties = make_shared<Dictionary>();
609 typedef std::map<String, DebugHint>::value_type ChildType;
610 BOOST_FOREACH(const ChildType& kv, Children) {
611 properties->Set(kv.first, kv.second.ToDictionary());
614 result->Set("properties", properties);
619 Expression::Ptr icinga::MakeLiteral(const Value& lit)
621 return make_shared<Expression>(&Expression::OpLiteral, lit, DebugInfo());