summaryrefslogtreecommitdiffstats
path: root/lib/config/vmops.hpp
blob: ea30983593366d2a17a8fabcd04242b4f650d893 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */

#ifndef VMOPS_H
#define VMOPS_H

#include "config/i2-config.hpp"
#include "config/expression.hpp"
#include "config/configitembuilder.hpp"
#include "config/applyrule.hpp"
#include "config/objectrule.hpp"
#include "base/debuginfo.hpp"
#include "base/array.hpp"
#include "base/dictionary.hpp"
#include "base/namespace.hpp"
#include "base/function.hpp"
#include "base/scriptglobal.hpp"
#include "base/exception.hpp"
#include "base/convert.hpp"
#include "base/objectlock.hpp"
#include <map>
#include <vector>

namespace icinga
{

class VMOps
{
public:
	static inline bool FindVarImportRef(ScriptFrame& frame, const std::vector<Expression::Ptr>& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
	{
		for (const auto& import : imports) {
			ExpressionResult res = import->Evaluate(frame);
			Object::Ptr obj = res.GetValue();
			if (obj->HasOwnField(name)) {
				*result = obj;
				return true;
			}
		}

		return false;
	}

	static inline bool FindVarImport(ScriptFrame& frame, const std::vector<Expression::Ptr>& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
	{
		Value parent;

		if (FindVarImportRef(frame, imports, name, &parent, debugInfo)) {
			*result = GetField(parent, name, frame.Sandboxed, debugInfo);
			return true;
		}

		return false;
	}

	static inline Value ConstructorCall(const Type::Ptr& type, const std::vector<Value>& args, const DebugInfo& debugInfo = DebugInfo())
	{
		if (type->GetName() == "String") {
			if (args.empty())
				return "";
			else if (args.size() == 1)
				return Convert::ToString(args[0]);
			else
				BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
		} else if (type->GetName() == "Number") {
			if (args.empty())
				return 0;
			else if (args.size() == 1)
				return Convert::ToDouble(args[0]);
			else
				BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
		} else if (type->GetName() == "Boolean") {
			if (args.empty())
				return 0;
			else if (args.size() == 1)
				return Convert::ToBool(args[0]);
			else
				BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
		} else if (args.size() == 1 && type->IsAssignableFrom(args[0].GetReflectionType()))
			return args[0];
		else
			return type->Instantiate(args);
	}

	static inline Value FunctionCall(ScriptFrame& frame, const Value& self, const Function::Ptr& func, const std::vector<Value>& arguments)
	{
		if (!self.IsEmpty() || self.IsString())
			return func->InvokeThis(self, arguments);
		else
			return func->Invoke(arguments);

	}

	static inline Value NewFunction(ScriptFrame& frame, const String& name, const std::vector<String>& argNames,
		const std::map<String, std::unique_ptr<Expression> >& closedVars, const Expression::Ptr& expression)
	{
		auto evaluatedClosedVars = EvaluateClosedVars(frame, closedVars);

		auto wrapper = [argNames, evaluatedClosedVars, expression](const std::vector<Value>& arguments) -> Value {
			if (arguments.size() < argNames.size())
				BOOST_THROW_EXCEPTION(std::invalid_argument("Too few arguments for function"));

			ScriptFrame *frame = ScriptFrame::GetCurrentFrame();

			frame->Locals = new Dictionary();

			if (evaluatedClosedVars)
				evaluatedClosedVars->CopyTo(frame->Locals);

			for (std::vector<Value>::size_type i = 0; i < std::min(arguments.size(), argNames.size()); i++)
				frame->Locals->Set(argNames[i], arguments[i]);

			return expression->Evaluate(*frame);
		};

		return new Function(name, wrapper, argNames);
	}

	static inline Value NewApply(ScriptFrame& frame, const String& type, const String& target, const String& name, const Expression::Ptr& filter,
		const String& package, const String& fkvar, const String& fvvar, const Expression::Ptr& fterm, const std::map<String, std::unique_ptr<Expression> >& closedVars,
		bool ignoreOnError, const Expression::Ptr& expression, const DebugInfo& debugInfo = DebugInfo())
	{
		ApplyRule::AddRule(type, target, name, expression, filter, package, fkvar,
			fvvar, fterm, ignoreOnError, debugInfo, EvaluateClosedVars(frame, closedVars));

		return Empty;
	}

	static inline Value NewObject(ScriptFrame& frame, bool abstract, const Type::Ptr& type, const String& name, const Expression::Ptr& filter,
		const String& zone, const String& package, bool defaultTmpl, bool ignoreOnError, const std::map<String, std::unique_ptr<Expression> >& closedVars, const Expression::Ptr& expression, const DebugInfo& debugInfo = DebugInfo())
	{
		ConfigItemBuilder item{debugInfo};

		String checkName = name;

		if (!abstract) {
			auto *nc = dynamic_cast<NameComposer *>(type.get());

			if (nc)
				checkName = nc->MakeName(name, nullptr);
		}

		if (!checkName.IsEmpty()) {
			ConfigItem::Ptr oldItem = ConfigItem::GetByTypeAndName(type, checkName);

			if (oldItem) {
				std::ostringstream msgbuf;
				msgbuf << "Object '" << name << "' of type '" << type->GetName() << "' re-defined: " << debugInfo << "; previous definition: " << oldItem->GetDebugInfo();
				BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debugInfo));
			}
		}

		if (filter && !ObjectRule::IsValidSourceType(type->GetName())) {
			std::ostringstream msgbuf;
			msgbuf << "Object '" << name << "' of type '" << type->GetName() << "' must not have 'assign where' and 'ignore where' rules: " << debugInfo;
			BOOST_THROW_EXCEPTION(ScriptError(msgbuf.str(), debugInfo));
		}

		item.SetType(type);
		item.SetName(name);

		if (!abstract)
			item.AddExpression(new ImportDefaultTemplatesExpression());

		item.AddExpression(new OwnedExpression(expression));
		item.SetAbstract(abstract);
		item.SetScope(EvaluateClosedVars(frame, closedVars));
		item.SetZone(zone);
		item.SetPackage(package);
		item.SetFilter(filter);
		item.SetDefaultTemplate(defaultTmpl);
		item.SetIgnoreOnError(ignoreOnError);
		item.Compile()->Register();

		return Empty;
	}

	static inline ExpressionResult For(ScriptFrame& frame, const String& fkvar, const String& fvvar, const Value& value, const std::unique_ptr<Expression>& expression, const DebugInfo& debugInfo = DebugInfo())
	{
		if (value.IsObjectType<Array>()) {
			if (!fvvar.IsEmpty())
				BOOST_THROW_EXCEPTION(ScriptError("Cannot use dictionary iterator for array.", debugInfo));

			Array::Ptr arr = value;

			for (Array::SizeType i = 0; i < arr->GetLength(); i++) {
				frame.Locals->Set(fkvar, arr->Get(i));
				ExpressionResult res = expression->Evaluate(frame);
				CHECK_RESULT_LOOP(res);
			}
		} else if (value.IsObjectType<Dictionary>()) {
			if (fvvar.IsEmpty())
				BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for dictionary.", debugInfo));

			Dictionary::Ptr dict = value;
			std::vector<String> keys;

			{
				ObjectLock olock(dict);
				for (const Dictionary::Pair& kv : dict) {
					keys.push_back(kv.first);
				}
			}

			for (const String& key : keys) {
				frame.Locals->Set(fkvar, key);
				frame.Locals->Set(fvvar, dict->Get(key));
				ExpressionResult res = expression->Evaluate(frame);
				CHECK_RESULT_LOOP(res);
			}
		} else if (value.IsObjectType<Namespace>()) {
			if (fvvar.IsEmpty())
				BOOST_THROW_EXCEPTION(ScriptError("Cannot use array iterator for namespace.", debugInfo));

			Namespace::Ptr ns = value;
			std::vector<String> keys;

			{
				ObjectLock olock(ns);
				for (const Namespace::Pair& kv : ns) {
					keys.push_back(kv.first);
				}
			}

			for (const String& key : keys) {
				frame.Locals->Set(fkvar, key);
				frame.Locals->Set(fvvar, ns->Get(key));
				ExpressionResult res = expression->Evaluate(frame);
				CHECK_RESULT_LOOP(res);
			}
		} else
			BOOST_THROW_EXCEPTION(ScriptError("Invalid type in for expression: " + value.GetTypeName(), debugInfo));

		return Empty;
	}

	static inline Value GetField(const Value& context, const String& field, bool sandboxed = false, const DebugInfo& debugInfo = DebugInfo())
	{
		if (BOOST_UNLIKELY(context.IsEmpty() && !context.IsString()))
			return Empty;

		if (BOOST_UNLIKELY(!context.IsObject()))
			return GetPrototypeField(context, field, true, debugInfo);

		Object::Ptr object = context;

		return object->GetFieldByName(field, sandboxed, debugInfo);
	}

	static inline void SetField(const Object::Ptr& context, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo = DebugInfo())
	{
		if (!context)
			BOOST_THROW_EXCEPTION(ScriptError("Cannot set field '" + field + "' on a value that is not an object.", debugInfo));

		return context->SetFieldByName(field, value, overrideFrozen, debugInfo);
	}

private:
	static inline Dictionary::Ptr EvaluateClosedVars(ScriptFrame& frame, const std::map<String, std::unique_ptr<Expression> >& closedVars)
	{
		if (closedVars.empty())
			return nullptr;

		DictionaryData locals;

		for (const auto& cvar : closedVars)
			locals.emplace_back(cvar.first, cvar.second->Evaluate(frame));

		return new Dictionary(std::move(locals));
	}
};

}

#endif /* VMOPS_H */