Implement a C++ code generator for libconfig

fixes #7699
fixes #7704
fixes #7706
This commit is contained in:
Gunnar Beutner 2014-11-15 08:20:22 +01:00
parent 0078e00c13
commit 8e265b7b7f
13 changed files with 1232 additions and 323 deletions

View File

@ -16,6 +16,7 @@
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
set(cli_SOURCES
codegencommand.cpp
nodeaddcommand.cpp nodeblackandwhitelistcommand.cpp nodelistcommand.cpp noderemovecommand.cpp
nodesetcommand.cpp nodesetupcommand.cpp nodeupdateconfigcommand.cpp nodewizardcommand.cpp nodeutility.cpp
clicommand.cpp

121
lib/cli/codegencommand.cpp Normal file
View File

@ -0,0 +1,121 @@
/******************************************************************************
* 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 "cli/codegencommand.hpp"
#include "config/expression.hpp"
#include "config/configcompiler.hpp"
#include "config/configcompilercontext.hpp"
#include "base/logger.hpp"
using namespace icinga;
REGISTER_CLICOMMAND("codegen", CodeGenCommand);
String CodeGenCommand::GetDescription(void) const
{
return "Generates native code for an Icinga 2 config file.";
}
String CodeGenCommand::GetShortDescription(void) const
{
return "compiles an Icinga 2 config file";
}
void CodeGenCommand::InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const
{
}
bool CodeGenCommand::IsHidden(void) const
{
return true;
}
/**
* The entry point for the "codegen" CLI command.
*
* @returns An exit status.
*/
int CodeGenCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
{
Logger::SetConsoleLogSeverity(LogWarning);
Expression *expr = ConfigCompiler::CompileStream("<stdin>", &std::cin);
int errors = 0;
BOOST_FOREACH(const ConfigCompilerMessage& message, ConfigCompilerContext::GetInstance()->GetMessages()) {
String logmsg = String("Config ") + (message.Error ? "error" : "warning") + ": " + message.Text;
if (message.Error) {
Log(LogCritical, "config", logmsg);
errors++;
} else {
Log(LogWarning, "config", logmsg);
}
}
if (errors > 0)
return 1;
std::cout << "#include \"config/expression.hpp\"" << "\n"
<< "#include \"config/vmops.hpp\"" << "\n"
<< "#include \"base/json.hpp\"" << "\n"
<< "#include \"base/initialize.hpp\"" << "\n"
<< "#include <boost/smart_ptr/make_shared.hpp>" << "\n"
<< "\n"
<< "using namespace icinga;" << "\n"
<< "\n";
std::map<String, String> definitions;
String name = CodeGenExpression(definitions, expr);
BOOST_FOREACH(const DefinitionMap::value_type& kv, definitions) {
std::cout << "static Value " << kv.first << "(const Object::Ptr& context);" << "\n";
}
std::cout << "\n"
<< "static Dictionary::Ptr l_ModuleScope = new Dictionary();" << "\n"
<< "\n"
<< "static void RunCode(void)" << "\n"
<< "{" << "\n"
<< " " << name << "(l_ModuleScope);" << "\n"
<< "}" << "\n"
<< "\n"
<< "INITIALIZE_ONCE(RunCode);" << "\n"
<< "\n"
<< "int main(int argc, char **argv)" << "\n"
<< "{" << "\n"
<< " RunCode();" << "\n"
<< " if (l_ModuleScope->Contains(\"__result\"))" << "\n"
<< " std::cout << \"Result: \" << JsonEncode(l_ModuleScope->Get(\"__result\")) << \"\\n\";" << "\n"
<< " else" << "\n"
<< " std::cout << \"No result.\" << \"\\n\";" << "\n"
<< "}" << "\n"
<< "\n";
BOOST_FOREACH(const DefinitionMap::value_type& kv, definitions) {
std::cout << kv.second << "\n";
}
delete expr;
return 0;
}

View File

@ -0,0 +1,51 @@
/******************************************************************************
* 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 CODEGENCOMMAND_H
#define CODEGENCOMMAND_H
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "cli/clicommand.hpp"
#include <ostream>
namespace icinga
{
/**
* The "variable get" command.
*
* @ingroup cli
*/
class CodeGenCommand : public CLICommand
{
public:
DECLARE_PTR_TYPEDEFS(CodeGenCommand);
virtual String GetDescription(void) const;
virtual String GetShortDescription(void) const;
virtual bool IsHidden(void) const;
void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const;
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const;
};
}
#endif /* CODEGENCOMMAND_H */

View File

@ -65,7 +65,11 @@ static String LoadAppType(const String& typeSpec)
static void IncludeZoneDirRecursive(const String& path)
{
String zoneName = Utility::BaseName(path);
Utility::GlobRecursive(path, "*.conf", boost::bind(&ConfigCompiler::CompileFile, _1, zoneName), GlobFile);
std::vector<Expression *> expressions;
Utility::GlobRecursive(path, "*.conf", boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, zoneName), GlobFile);
Dictionary::Ptr context = new Dictionary();
DictExpression(expressions).Evaluate(context);
}
static void IncludeNonLocalZone(const String& zonePath)
@ -88,10 +92,15 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con
if (vm.count("config") > 0) {
BOOST_FOREACH(const String& configPath, vm["config"].as<std::vector<std::string> >()) {
ConfigCompiler::CompileFile(configPath);
Expression *expression = ConfigCompiler::CompileFile(configPath);
Dictionary::Ptr context = new Dictionary();
expression->Evaluate(context);
}
} else if (!vm.count("no-config")) {
Expression *expression = ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
Dictionary::Ptr context = new Dictionary();
expression->Evaluate(context);
}
} else if (!vm.count("no-config"))
ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf");
/* Load cluster config files - this should probably be in libremote but
* unfortunately moving it there is somewhat non-trivial. */
@ -105,7 +114,9 @@ static bool LoadConfigFiles(const boost::program_options::variables_map& vm, con
String name, fragment;
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
ConfigCompiler::CompileText(name, fragment);
Expression *expression = ConfigCompiler::CompileText(name, fragment);
Dictionary::Ptr context = new Dictionary();
expression->Evaluate(context);
}
ConfigItemBuilder::Ptr builder = new ConfigItemBuilder();

View File

@ -34,7 +34,7 @@ set(config_SOURCES
applyrule.cpp base-type.conf base-type.cpp
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
configtype.cpp expression.cpp expression-codegen.cpp objectrule.cpp typerule.cpp typerulelist.cpp
)
if(ICINGA2_UNITY_BUILD)

View File

@ -227,8 +227,9 @@ static std::stack<Expression *> m_Ignore;
static std::stack<String> m_FKVar;
static std::stack<String> m_FVVar;
static std::stack<Expression *> m_FTerm;
static std::stack<std::vector<Expression *> > m_Expressions;
void ConfigCompiler::Compile(void)
Expression *ConfigCompiler::Compile(void)
{
m_ModuleScope = new Dictionary();
@ -243,15 +244,25 @@ void ConfigCompiler::Compile(void)
m_FKVar = std::stack<String>();
m_FVVar = std::stack<String>();
m_FTerm = std::stack<Expression *>();
m_Expressions.push(std::vector<Expression *>());
try {
yyparse(this);
DictExpression *expr = new DictExpression(m_Expressions.top());
m_Expressions.pop();
expr->MakeInline();
return expr;
} catch (const ConfigError& ex) {
const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
} catch (const std::exception& ex) {
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
}
m_Expressions.pop();
return NULL;
}
#define scanner (context->GetScanner())
@ -263,39 +274,13 @@ statements: /* empty */
| statements statement
;
statement: type | include | include_recursive | library | constant
statement: type | library | constant
{ }
| newlines
{ }
| lterm
{
$1->Evaluate(m_ModuleScope);
delete $1;
}
;
include: T_INCLUDE rterm sep
{
context->HandleInclude($2->Evaluate(m_ModuleScope), false, DebugInfoRange(@1, @2));
delete $2;
}
| T_INCLUDE T_STRING_ANGLE
{
context->HandleInclude($2, true, DebugInfoRange(@1, @2));
free($2);
}
;
include_recursive: T_INCLUDE_RECURSIVE rterm
{
context->HandleIncludeRecursive($2->Evaluate(m_ModuleScope), "*.conf", DebugInfoRange(@1, @2));
delete $2;
}
| T_INCLUDE_RECURSIVE rterm ',' rterm
{
context->HandleIncludeRecursive($2->Evaluate(m_ModuleScope), $4->Evaluate(m_ModuleScope), DebugInfoRange(@1, @4));
delete $2;
delete $4;
m_Expressions.top().push_back($1);
}
;
@ -590,6 +575,27 @@ lterm: indexer combined_set_op rterm
$$ = new SetExpression(*$1, $2, $3, DebugInfoRange(@1, @3));
delete $1;
}
| T_INCLUDE rterm sep
{
$$ = context->HandleInclude($2->Evaluate(m_ModuleScope), false, DebugInfoRange(@1, @2));
delete $2;
}
| T_INCLUDE T_STRING_ANGLE
{
$$ = context->HandleInclude($2, true, DebugInfoRange(@1, @2));
free($2);
}
| T_INCLUDE_RECURSIVE rterm
{
$$ = context->HandleIncludeRecursive($2->Evaluate(m_ModuleScope), "*.conf", DebugInfoRange(@1, @2));
delete $2;
}
| T_INCLUDE_RECURSIVE rterm ',' rterm
{
$$ = context->HandleIncludeRecursive($2->Evaluate(m_ModuleScope), $4->Evaluate(m_ModuleScope), DebugInfoRange(@1, @4));
delete $2;
delete $4;
}
| T_IMPORT rterm
{
Expression *avar = new VariableExpression("type", DebugInfoRange(@1, @2));

View File

@ -97,6 +97,11 @@ String ConfigCompiler::GetZone(void) const
return m_Zone;
}
void ConfigCompiler::CollectIncludes(std::vector<Expression *>& expressions, const String& file, const String& zone)
{
expressions.push_back(CompileFile(file, zone));
}
/**
* Handles an include directive.
*
@ -104,7 +109,7 @@ String ConfigCompiler::GetZone(void) const
* @param search Whether to search global include dirs.
* @param debuginfo Debug information.
*/
void ConfigCompiler::HandleInclude(const String& include, bool search, const DebugInfo& debuginfo)
Expression *ConfigCompiler::HandleInclude(const String& include, bool search, const DebugInfo& debuginfo)
{
String path;
@ -126,32 +131,36 @@ void ConfigCompiler::HandleInclude(const String& include, bool search, const Deb
}
}
std::vector<ConfigItem::Ptr> items;
std::vector<Expression *> expressions;
if (!Utility::Glob(includePath, boost::bind(&ConfigCompiler::CompileFile, _1, m_Zone), GlobFile) && includePath.FindFirstOf("*?") == String::NPos) {
if (!Utility::Glob(includePath, boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, m_Zone), GlobFile) && includePath.FindFirstOf("*?") == String::NPos) {
std::ostringstream msgbuf;
msgbuf << "Include file '" + include + "' does not exist: " << debuginfo;
BOOST_THROW_EXCEPTION(std::invalid_argument(msgbuf.str()));
}
return new DictExpression(expressions);
}
/**
* Handles recursive includes.
*
* @param include The directory path.
* @param path The directory path.
* @param pattern The file pattern.
* @param debuginfo Debug information.
*/
void ConfigCompiler::HandleIncludeRecursive(const String& include, const String& pattern, const DebugInfo&)
Expression *ConfigCompiler::HandleIncludeRecursive(const String& path, const String& pattern, const DebugInfo&)
{
String path;
String ppath;
if (include.GetLength() > 0 && include[0] == '/')
path = include;
if (path.GetLength() > 0 && path[0] == '/')
ppath = path;
else
path = Utility::DirName(GetPath()) + "/" + include;
ppath = Utility::DirName(GetPath()) + "/" + path;
Utility::GlobRecursive(path, pattern, boost::bind(&ConfigCompiler::CompileFile, _1, m_Zone), GlobFile);
std::vector<Expression *> expressions;
Utility::GlobRecursive(ppath, pattern, boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, m_Zone), GlobFile);
return new DictExpression(expressions);
}
/**
@ -171,14 +180,14 @@ void ConfigCompiler::HandleLibrary(const String& library)
* @param stream The input stream.
* @returns Configuration items.
*/
void ConfigCompiler::CompileStream(const String& path, std::istream *stream, const String& zone)
Expression *ConfigCompiler::CompileStream(const String& path, std::istream *stream, const String& zone)
{
CONTEXT("Compiling configuration stream with name '" + path + "'");
stream->exceptions(std::istream::badbit);
ConfigCompiler ctx(path, stream, zone);
ctx.Compile();
return ctx.Compile();
}
/**
@ -187,7 +196,7 @@ void ConfigCompiler::CompileStream(const String& path, std::istream *stream, con
* @param path The path.
* @returns Configuration items.
*/
void ConfigCompiler::CompileFile(const String& path, const String& zone)
Expression *ConfigCompiler::CompileFile(const String& path, const String& zone)
{
CONTEXT("Compiling configuration file '" + path + "'");
@ -213,7 +222,7 @@ void ConfigCompiler::CompileFile(const String& path, const String& zone)
* @param text The text.
* @returns Configuration items.
*/
void ConfigCompiler::CompileText(const String& path, const String& text, const String& zone)
Expression *ConfigCompiler::CompileText(const String& path, const String& text, const String& zone)
{
std::stringstream stream(text);
return CompileStream(path, &stream, zone);

View File

@ -21,6 +21,7 @@
#define CONFIGCOMPILER_H
#include "config/i2-config.hpp"
#include "config/expression.hpp"
#include "base/debuginfo.hpp"
#include "base/registry.hpp"
#include "base/initialize.hpp"
@ -43,11 +44,11 @@ public:
explicit ConfigCompiler(const String& path, std::istream *input, const String& zone = String());
virtual ~ConfigCompiler(void);
void Compile(void);
Expression *Compile(void);
static void CompileStream(const String& path, std::istream *stream, const String& zone = String());
static void CompileFile(const String& path, const String& zone = String());
static void CompileText(const String& path, const String& text, const String& zone = String());
static Expression *CompileStream(const String& path, std::istream *stream, const String& zone = String());
static Expression *CompileFile(const String& path, const String& zone = String());
static Expression *CompileText(const String& path, const String& text, const String& zone = String());
static void AddIncludeSearchDir(const String& dir);
@ -56,9 +57,11 @@ public:
void SetZone(const String& zone);
String GetZone(void) const;
static void CollectIncludes(std::vector<Expression *>& expressions, const String& file, const String& zone);
/* internally used methods */
void HandleInclude(const String& include, bool search, const DebugInfo& debuginfo);
void HandleIncludeRecursive(const String& include, const String& pattern, const DebugInfo& debuginfo);
Expression *HandleInclude(const String& include, bool search, const DebugInfo& debuginfo = DebugInfo());
Expression *HandleIncludeRecursive(const String& path, const String& pattern, const DebugInfo& debuginfo = DebugInfo());
void HandleLibrary(const String& library);
size_t ReadInput(char *buffer, size_t max_bytes);

View File

@ -19,7 +19,7 @@
#include "config/configtype.hpp"
#include "config/configcompilercontext.hpp"
#include "config/expression.hpp"
#include "config/vmops.hpp"
#include "base/objectlock.hpp"
#include "base/convert.hpp"
#include "base/singleton.hpp"
@ -162,7 +162,7 @@ void ConfigType::ValidateObject(const Object::Ptr& object,
BOOST_FOREACH(const String& require, ruleList->GetRequires()) {
locations.push_back("Attribute '" + require + "'");
Value value = Expression::GetField(object, require);
Value value = VMOps::GetField(object, require);
if (value.IsEmpty() || (value.IsString() && static_cast<String>(value).IsEmpty())) {
ConfigCompilerContext::GetInstance()->AddMessage(true,

View File

@ -0,0 +1,565 @@
/******************************************************************************
* 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/expression.hpp"
#include "config/vmops.hpp"
#include "base/object.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
void LiteralExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
if (m_Value.IsString())
fp << "String(\"" << m_Value << "\")";
else if (m_Value.IsNumber())
fp << m_Value;
else if (m_Value.IsEmpty())
fp << "Value()";
else
throw std::invalid_argument("Literal expression has invalid type: " + m_Value.GetTypeName());
}
void VariableExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "VMOps::Variable(context, \"" << m_Variable << "\")";
}
void NegateExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "~(long)(";
m_Operand->GenerateCode(definitions, fp);
fp << ")";
}
void LogicalNegateExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "!(";
m_Operand->GenerateCode(definitions, fp);
fp << ")";
}
void AddExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") + (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void SubtractExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") - (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void MultiplyExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") * (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void DivideExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") / (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void BinaryAndExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") & (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void BinaryOrExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") | (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void ShiftLeftExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") << (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void ShiftRightExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") >> (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void EqualExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") == (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void NotEqualExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") != (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void LessThanExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") < (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void GreaterThanExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") > (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void LessThanOrEqualExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") <= (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void GreaterThanOrEqualExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ") >= (";
m_Operand2->GenerateCode(definitions, fp);
fp << ")";
}
void InExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "!static_cast<Array::Ptr>(";
m_Operand2->GenerateCode(definitions, fp);
fp << ")->Contains(";
m_Operand1->GenerateCode(definitions, fp);
fp << ")";
}
void NotInExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "!static_cast<Array::Ptr>(";
m_Operand2->GenerateCode(definitions, fp);
fp << ")->Contains(";
m_Operand1->GenerateCode(definitions, fp);
fp << ")";
}
void LogicalAndExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ").ToBool() && (";
m_Operand2->GenerateCode(definitions, fp);
fp << ").ToBool()";
}
void LogicalOrExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "(";
m_Operand1->GenerateCode(definitions, fp);
fp << ").ToBool() || (";
m_Operand2->GenerateCode(definitions, fp);
fp << ").ToBool()";
}
void FunctionCallExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_fcall_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " Value funcName = (";
m_FName->GenerateCode(definitions, df);
df << ");" << "\n"
<< " std::vector<Value> args;" << "\n";
BOOST_FOREACH(Expression *expr, m_Args) {
df << " args.push_back(";
expr->GenerateCode(definitions, df);
df << ");" << "\n";
}
df << " return VMOps::FunctionCall(context, funcName, args);" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void ArrayExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_array_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " Array::Ptr result = new Array();" << "\n";
BOOST_FOREACH(Expression *aexpr, m_Expressions) {
df << " result->Add(";
aexpr->GenerateCode(definitions, df);
df << ");" << "\n";
}
df << " return result;" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void DictExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_dict_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& ucontext)" << "\n"
<< "{" << "\n";
if (!m_Inline) {
df << " Dictionary::Ptr result = new Dictionary();" << "\n"
<< " result->Set(\"__parent\", ucontext);" << "\n"
<< " Object::Ptr context = result;" << "\n";
} else
df << "Object::Ptr context = ucontext;" << "\n";
df << " do {" << "\n";
BOOST_FOREACH(Expression *expression, m_Expressions) {
df << " ";
expression->GenerateCode(definitions, df);
df << ";" << "\n"
<< " if (Expression::HasField(context, \"__result\"))" << "\n"
<< " break;" << "\n";
}
df << " } while (0);" << "\n"
<< "\n";
if (!m_Inline) {
df << " Dictionary::Ptr xresult = result->ShallowClone();" << "\n"
<< " xresult->Remove(\"__parent\");" << "\n"
<< " return xresult;" << "\n";
} else
df << " return Empty;" << "\n";
df << "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void SetExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_set_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " Value parent, object;" << "\n"
<< " String tempindex, index;" << "\n";
for (Array::SizeType i = 0; i < m_Indexer.size(); i++) {
Expression *indexExpr = m_Indexer[i];
df << " tempindex = (";
indexExpr->GenerateCode(definitions, df);
df << ");" << "\n";
if (i == 0)
df << " parent = context;" << "\n";
else
df << " parent = object;" << "\n";
if (i == m_Indexer.size() - 1) {
df << " index = tempindex" << ";" << "\n";
/* No need to look up the last indexer's value if this is a direct set */
if (m_Op == OpSetLiteral)
break;
}
df << " object = VMOps::Indexer(context, parent, tempindex);" << "\n";
if (i != m_Indexer.size() - 1) {
df << " if (object.IsEmpty()) {" << "\n"
<< " object = new Dictionary();" << "\n"
<< " Expression::SetField(parent, tempindex, object);" << "\n"
<< " }" << "\n";
}
}
df << " Value right = (";
m_Operand2->GenerateCode(definitions, df);
df << ");" << "\n";
if (m_Op != OpSetLiteral) {
String opcode;
switch (m_Op) {
case OpSetAdd:
opcode = "+";
break;
case OpSetSubtract:
opcode = "-";
break;
case OpSetMultiply:
opcode = "*";
break;
case OpSetDivide:
opcode = "/";
break;
default:
VERIFY(!"Invalid opcode.");
}
df << " right = object " << opcode << " right;" << "\n";
}
df << " Expression::SetField(parent, index, right);" << "\n"
<< " return right;" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void IndexerExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_indexer_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " return VMOps::Indexer(context, (";
m_Operand1->GenerateCode(definitions, df);
df << "), (";
m_Operand2->GenerateCode(definitions, df);
df << "));" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void ImportExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_import_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " String name = (";
m_Name->GenerateCode(definitions, df);
df << ");" << "\n"
<< " String type = (";
m_Type->GenerateCode(definitions, df);
df << ");" << "\n"
<< "\n"
<< " ConfigItem::Ptr item = ConfigItem::GetObject(type, name);" << "\n"
<< "\n"
<< " if (!item)" << "\n"
<< " BOOST_THROW_EXCEPTION(ConfigError(\"Import references unknown template: '\" + name + \"' of type '\" + type + \"'\"));" << "\n"
<< "\n"
<< " item->GetExpression()->Evaluate(context);" << "\n"
<< "\n"
<< " return Empty;" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void FunctionExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_function_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " std::vector<String> args;" << "\n";
BOOST_FOREACH(const String& arg, m_Args)
df << " args.push_back(\"" << arg << "\");" << "\n";
df << " return VMOps::NewFunction(context, \"" << m_Name << "\", args, boost::make_shared<NativeExpression>("
<< CodeGenExpression(definitions, m_Expression.get()) << "));" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void SlotExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
fp << "VMOps::NewSlot(context, \"" << m_Signal << "\", ";
m_Slot->GenerateCode(definitions, fp);
fp << ")";
}
void ApplyExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_apply_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " boost::shared_ptr<Expression> fterm;" << "\n";
if (m_FTerm)
df << " fterm = boost::make_shared<NativeExpression>(" << CodeGenExpression(definitions, m_FTerm.get()) << ");" << "\n";
df << " boost::shared_ptr<Expression> filter = boost::make_shared<NativeExpression>(" << CodeGenExpression(definitions, m_Filter.get()) << ");" << "\n"
<< " boost::shared_ptr<Expression> expression = boost::make_shared<NativeExpression>(" << CodeGenExpression(definitions, m_Expression.get()) << ");" << "\n"
<< " return VMOps::NewApply(context, \"" << m_Type << "\", \"" << m_Target << "\", (";
m_Name->GenerateCode(definitions, df);
df << "), filter, "
<< "\"" << m_FKVar << "\", \"" << m_FVVar << "\", fterm, expression);" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void ObjectExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_object_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " String name;" << "\n";
if (m_Name) {
df << " name = (";
m_Name->GenerateCode(definitions, df);
df << ");" << "\n";
}
df << " boost::shared_ptr<Expression> filter;" << "\n";
if (m_Filter)
df << " filter = boost::make_shared<NativeExpression>("
<< CodeGenExpression(definitions, m_Filter.get()) << ");" << "\n";
df << " boost::shared_ptr<Expression> expression = boost::make_shared<NativeExpression>("
<< CodeGenExpression(definitions, m_Expression.get()) << ");" << "\n"
<< " return VMOps::NewObject(context, " << m_Abstract << ", \"" << m_Type << "\", name, filter, \"" << m_Zone << "\", expression);" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
void ForExpression::GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
std::ostringstream namebuf, df;
namebuf << "native_for_" << reinterpret_cast<uintptr_t>(this);
df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " static NativeExpression expression("
<< CodeGenExpression(definitions, m_Expression) << ");" << "\n"
<< " return VMOps::For(context, \"" << m_FKVar << "\", \"" << m_FVVar << "\", (";
m_Value->GenerateCode(definitions, df);
df << "), &expression);" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = df.str();
fp << namebuf.str() << "(context)";
}
String icinga::CodeGenExpression(DefinitionMap& definitions, Expression *expression)
{
std::ostringstream namebuf, definitionbuf;
namebuf << "native_expression_" << reinterpret_cast<uintptr_t>(expression);
definitionbuf << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n"
<< "{" << "\n"
<< " return (";
expression->GenerateCode(definitions, definitionbuf);
definitionbuf << ");" << "\n"
<< "}" << "\n";
definitions[namebuf.str()] = definitionbuf.str();
return namebuf.str();
}

View File

@ -19,22 +19,14 @@
#include "config/expression.hpp"
#include "config/configitem.hpp"
#include "config/configitembuilder.hpp"
#include "config/applyrule.hpp"
#include "config/objectrule.hpp"
#include "config/vmops.hpp"
#include "base/array.hpp"
#include "base/json.hpp"
#include "base/scriptfunction.hpp"
#include "base/scriptvariable.hpp"
#include "base/scriptsignal.hpp"
#include "base/utility.hpp"
#include "base/objectlock.hpp"
#include "base/object.hpp"
#include "base/logger.hpp"
#include "base/configerror.hpp"
#include <boost/foreach.hpp>
#include <boost/exception_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/exception/errinfo_nested_exception.hpp>
using namespace icinga;
@ -97,16 +89,7 @@ const DebugInfo& DebuggableExpression::GetDebugInfo(void) const
Value VariableExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
Object::Ptr scope = context;
while (scope) {
if (HasField(scope, m_Variable))
return GetField(scope, m_Variable);
scope = GetField(scope, "__parent");
}
return ScriptVariable::Get(m_Variable);
return VMOps::Variable(context, m_Variable);
}
Value NegateExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
@ -233,22 +216,12 @@ Value FunctionCallExpression::DoEvaluate(const Object::Ptr& context, DebugHint *
{
Value funcName = m_FName->Evaluate(context);
ScriptFunction::Ptr func;
if (funcName.IsObjectType<ScriptFunction>())
func = funcName;
else
func = ScriptFunction::GetByName(funcName);
if (!func)
BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
std::vector<Value> arguments;
BOOST_FOREACH(Expression *arg, m_Args) {
arguments.push_back(arg->Evaluate(context));
}
return func->Invoke(arguments);
return VMOps::FunctionCall(context, funcName, arguments);
}
Value ArrayExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
@ -272,7 +245,7 @@ Value DictExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) c
Object::Ptr acontext = m_Inline ? context : result;
aexpr->Evaluate(acontext, dhint);
if (HasField(acontext, "__result"))
if (VMOps::HasField(acontext, "__result"))
break;
}
@ -311,16 +284,12 @@ Value SetExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) co
break;
}
LiteralExpression *eparent = MakeLiteral(parent);
LiteralExpression *eindex = MakeLiteral(tempindex);
IndexerExpression eip(eparent, eindex, m_DebugInfo);
object = eip.Evaluate(context, psdhint);
object = VMOps::Indexer(context, parent, tempindex);
if (i != m_Indexer.size() - 1 && object.IsEmpty()) {
object = new Dictionary();
SetField(parent, tempindex, object);
VMOps::SetField(parent, tempindex, object);
}
}
@ -348,7 +317,7 @@ Value SetExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) co
}
}
SetField(parent, index, right);
VMOps::SetField(parent, index, right);
if (psdhint)
psdhint->AddMessage("=", m_DebugInfo);
@ -358,33 +327,7 @@ Value SetExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) co
Value IndexerExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
Value value = m_Operand1->Evaluate(context);
Value index = m_Operand2->Evaluate(context);
if (value.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = value;
return dict->Get(index);
} else if (value.IsObjectType<Array>()) {
Array::Ptr arr = value;
return arr->Get(index);
} else if (value.IsObject()) {
Object::Ptr object = value;
Type::Ptr type = object->GetReflectionType();
if (!type)
BOOST_THROW_EXCEPTION(ConfigError("Dot operator applied to object which does not support reflection"));
int field = type->GetFieldId(index);
if (field == -1)
BOOST_THROW_EXCEPTION(ConfigError("Tried to access invalid property '" + index + "'"));
return object->GetField(field);
} else if (value.IsEmpty()) {
return Empty;
} else {
BOOST_THROW_EXCEPTION(ConfigError("Dot operator cannot be applied to type '" + value.GetTypeName() + "'"));
}
return VMOps::Indexer(context, m_Operand1->Evaluate(context), m_Operand2->Evaluate(context));
}
Value ImportExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
@ -402,66 +345,19 @@ Value ImportExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint)
return Empty;
}
Value Expression::FunctionWrapper(const std::vector<Value>& arguments,
const std::vector<String>& funcargs, const boost::shared_ptr<Expression>& expr, const Object::Ptr& scope)
{
if (arguments.size() < funcargs.size())
BOOST_THROW_EXCEPTION(ConfigError("Too few arguments for function"));
Dictionary::Ptr context = new Dictionary();
context->Set("__parent", scope);
for (std::vector<Value>::size_type i = 0; i < std::min(arguments.size(), funcargs.size()); i++)
context->Set(funcargs[i], arguments[i]);
expr->Evaluate(context);
return context->Get("__result");
}
Value FunctionExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
ScriptFunction::Ptr func = new ScriptFunction(boost::bind(&Expression::FunctionWrapper, _1, m_Args, m_Expression, context));
if (!m_Name.IsEmpty())
ScriptFunction::Register(m_Name, func);
return func;
}
static void InvokeSlot(const Value& funcName, const std::vector<Value>& arguments)
{
ScriptFunction::Ptr func;
if (funcName.IsObjectType<ScriptFunction>())
func = funcName;
else
func = ScriptFunction::GetByName(funcName);
if (!func)
BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
func->Invoke(arguments);
return VMOps::NewFunction(context, m_Name, m_Args, m_Expression);
}
Value SlotExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
ScriptSignal::Ptr sig = ScriptSignal::GetByName(m_Signal);
if (!sig)
BOOST_THROW_EXCEPTION(ConfigError("Signal '" + m_Signal + "' does not exist."));
sig->AddSlot(boost::bind(InvokeSlot, m_Slot->Evaluate(context), _1));
return Empty;
return VMOps::NewSlot(context, m_Signal, m_Slot->Evaluate(context));
}
Value ApplyExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
String name = m_Name->Evaluate(context, dhint);
ApplyRule::AddRule(m_Type, m_Target, name, m_Expression, m_Filter, m_FKVar, m_FVVar, m_FTerm, m_DebugInfo, context);
return Empty;
return VMOps::NewApply(context, m_Type, m_Target, m_Name->Evaluate(context), m_Filter, m_FKVar, m_FVVar, m_FTerm, m_Expression, m_DebugInfo);
}
Value ObjectExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
@ -471,150 +367,14 @@ Value ObjectExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint)
if (m_Name)
name = m_Name->Evaluate(context, dhint);
ConfigItemBuilder::Ptr item = new ConfigItemBuilder(m_DebugInfo);
String checkName = name;
if (!m_Abstract) {
Type::Ptr ptype = Type::GetByName(m_Type);
NameComposer *nc = dynamic_cast<NameComposer *>(ptype.get());
if (nc)
checkName = nc->MakeName(name, Dictionary::Ptr());
}
if (!checkName.IsEmpty()) {
ConfigItem::Ptr oldItem = ConfigItem::GetObject(m_Type, checkName);
if (oldItem) {
std::ostringstream msgbuf;
msgbuf << "Object '" << name << "' of type '" << m_Type << "' re-defined: " << m_DebugInfo << "; previous definition: " << oldItem->GetDebugInfo();
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(m_DebugInfo));
}
}
item->SetType(m_Type);
if (name.FindFirstOf("!") != String::NPos) {
std::ostringstream msgbuf;
msgbuf << "Name for object '" << name << "' of type '" << m_Type << "' is invalid: Object names may not contain '!'";
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(m_DebugInfo));
}
item->SetName(name);
item->AddExpression(new OwnedExpression(m_Expression));
item->SetAbstract(m_Abstract);
item->SetScope(context);
item->SetZone(m_Zone);
item->SetFilter(m_Filter);
item->Compile()->Register();
return Empty;
return VMOps::NewObject(context, m_Abstract, m_Type, name, m_Filter, m_Zone,
m_Expression, m_DebugInfo);
}
Value ForExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
Value value = m_Value->Evaluate(context, dhint);
if (value.IsObjectType<Array>()) {
if (!m_FVVar.IsEmpty())
BOOST_THROW_EXCEPTION(ConfigError("Cannot use dictionary iterator for array.") << errinfo_debuginfo(m_DebugInfo));
Array::Ptr arr = value;
ObjectLock olock(arr);
BOOST_FOREACH(const Value& value, arr) {
Dictionary::Ptr xcontext = new Dictionary();
xcontext->Set("__parent", context);
xcontext->Set(m_FKVar, value);
m_Expression->Evaluate(xcontext, dhint);
}
} else if (value.IsObjectType<Dictionary>()) {
if (m_FVVar.IsEmpty())
BOOST_THROW_EXCEPTION(ConfigError("Cannot use array iterator for dictionary.") << errinfo_debuginfo(m_DebugInfo));
Dictionary::Ptr dict = value;
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
Dictionary::Ptr xcontext = new Dictionary();
xcontext->Set("__parent", context);
xcontext->Set(m_FKVar, kv.first);
xcontext->Set(m_FVVar, kv.second);
m_Expression->Evaluate(xcontext, dhint);
}
} else
BOOST_THROW_EXCEPTION(ConfigError("Invalid type in __for expression: " + value.GetTypeName()) << errinfo_debuginfo(m_DebugInfo));
return Empty;
}
bool Expression::HasField(const Object::Ptr& context, const String& field)
{
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(context);
if (dict)
return dict->Contains(field);
else {
Type::Ptr type = context->GetReflectionType();
if (!type)
return false;
return type->GetFieldId(field) != -1;
}
}
Value Expression::GetField(const Object::Ptr& context, const String& field)
{
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(context);
if (dict)
return dict->Get(field);
else {
Type::Ptr type = context->GetReflectionType();
if (!type)
return Empty;
int fid = type->GetFieldId(field);
if (fid == -1)
return Empty;
return context->GetField(fid);
}
}
void Expression::SetField(const Object::Ptr& context, const String& field, const Value& value)
{
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(context);
if (dict)
dict->Set(field, value);
else {
Type::Ptr type = context->GetReflectionType();
if (!type)
BOOST_THROW_EXCEPTION(ConfigError("Cannot set field on object."));
int fid = type->GetFieldId(field);
if (fid == -1)
BOOST_THROW_EXCEPTION(ConfigError("Attribute '" + field + "' does not exist."));
try {
context->SetField(fid, value);
} catch (const boost::bad_lexical_cast&) {
BOOST_THROW_EXCEPTION(ConfigError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "'"));
} catch (const std::bad_cast&) {
BOOST_THROW_EXCEPTION(ConfigError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "'"));
}
}
return VMOps::For(context, m_FKVar, m_FVVar, m_Value->Evaluate(context), m_Expression, m_DebugInfo);
}

View File

@ -24,7 +24,11 @@
#include "base/debuginfo.hpp"
#include "base/array.hpp"
#include "base/dictionary.hpp"
#include "base/scriptfunction.hpp"
#include "base/configerror.hpp"
#include "base/convert.hpp"
#include <boost/foreach.hpp>
#include <map>
namespace icinga
{
@ -96,6 +100,8 @@ enum CombinedSetOp
OpSetDivide
};
typedef std::map<String, String> DefinitionMap;
/**
* @ingroup config
*/
@ -107,16 +113,8 @@ public:
Value Evaluate(const Object::Ptr& context, DebugHint *dhint = NULL) const;
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const = 0;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fp) const = 0;
virtual const DebugInfo& GetDebugInfo(void) const;
public:
static Value FunctionWrapper(const std::vector<Value>& arguments,
const std::vector<String>& funcargs,
const boost::shared_ptr<Expression>& expr, const Object::Ptr& scope);
static bool HasField(const Object::Ptr& context, const String& field);
static Value GetField(const Object::Ptr& context, const String& field);
static void SetField(const Object::Ptr& context, const String& field, const Value& value);
};
I2_CONFIG_API std::vector<Expression *> MakeIndexer(const String& index1);
@ -134,6 +132,11 @@ protected:
return m_Expression->DoEvaluate(context, dhint);
}
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
return m_Expression->GenerateCode(definitions, fp);
}
virtual const DebugInfo& GetDebugInfo(void) const
{
return m_Expression->GetDebugInfo();
@ -143,6 +146,30 @@ private:
boost::shared_ptr<Expression> m_Expression;
};
class I2_CONFIG_API NativeExpression : public Expression
{
public:
typedef Value (*Callback)(const Object::Ptr& context);
NativeExpression(Callback callback)
: m_Callback(callback)
{ }
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const
{
return m_Callback(context);
}
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fp) const
{
throw std::runtime_error("Native expression does not support codegen.");
}
private:
Callback m_Callback;
};
class I2_CONFIG_API LiteralExpression : public Expression
{
public:
@ -150,6 +177,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fp) const;
private:
Value m_Value;
@ -217,6 +245,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
String m_Variable;
@ -231,6 +260,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API LogicalNegateExpression : public UnaryExpression
@ -242,6 +272,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API AddExpression : public BinaryExpression
@ -253,6 +284,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API SubtractExpression : public BinaryExpression
@ -264,6 +296,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API MultiplyExpression : public BinaryExpression
@ -275,6 +308,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API DivideExpression : public BinaryExpression
@ -286,6 +320,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API BinaryAndExpression : public BinaryExpression
@ -297,6 +332,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API BinaryOrExpression : public BinaryExpression
@ -308,6 +344,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API ShiftLeftExpression : public BinaryExpression
@ -319,6 +356,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API ShiftRightExpression : public BinaryExpression
@ -330,6 +368,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API EqualExpression : public BinaryExpression
@ -341,6 +380,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API NotEqualExpression : public BinaryExpression
@ -352,6 +392,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API LessThanExpression : public BinaryExpression
@ -363,6 +404,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API GreaterThanExpression : public BinaryExpression
@ -374,6 +416,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API LessThanOrEqualExpression : public BinaryExpression
@ -385,6 +428,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API GreaterThanOrEqualExpression : public BinaryExpression
@ -396,6 +440,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API InExpression : public BinaryExpression
@ -407,6 +452,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API NotInExpression : public BinaryExpression
@ -418,6 +464,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API LogicalAndExpression : public BinaryExpression
@ -429,6 +476,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API LogicalOrExpression : public BinaryExpression
@ -440,6 +488,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API FunctionCallExpression : public DebuggableExpression
@ -459,6 +508,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
public:
Expression *m_FName;
@ -480,6 +530,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
std::vector<Expression *> m_Expressions;
@ -502,6 +553,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
std::vector<Expression *> m_Expressions;
@ -525,6 +577,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
CombinedSetOp m_Op;
@ -542,6 +595,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
};
class I2_CONFIG_API ImportExpression : public DebuggableExpression
@ -559,12 +613,15 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
Expression *m_Type;
Expression *m_Name;
};
I2_CONFIG_API String CodeGenExpression(DefinitionMap& definitions, Expression *expression);
class I2_CONFIG_API FunctionExpression : public DebuggableExpression
{
public:
@ -574,6 +631,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
String m_Name;
@ -590,6 +648,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
String m_Signal;
@ -614,6 +673,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
String m_Type;
@ -640,6 +700,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
bool m_Abstract;
@ -665,6 +726,7 @@ public:
protected:
virtual Value DoEvaluate(const Object::Ptr& context, DebugHint *dhint) const;
virtual void GenerateCode(DefinitionMap& definitions, std::ostream& fpg) const;
private:
String m_FKVar;

320
lib/config/vmops.hpp Normal file
View File

@ -0,0 +1,320 @@
/******************************************************************************
* 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 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/scriptfunction.hpp"
#include "base/scriptsignal.hpp"
#include "base/scriptvariable.hpp"
#include "base/configerror.hpp"
#include "base/convert.hpp"
#include <boost/foreach.hpp>
#include <map>
#include <vector>
namespace icinga
{
class VMOps
{
public:
static inline Value Variable(const Object::Ptr& context, const String& name)
{
Object::Ptr scope = context;
while (scope) {
if (HasField(scope, name))
return GetField(scope, name);
scope = GetField(scope, "__parent");
}
return ScriptVariable::Get(name);
}
static inline Value FunctionCall(const Object::Ptr& context, const Value& funcName, const std::vector<Value>& arguments)
{
ScriptFunction::Ptr func;
if (funcName.IsObjectType<ScriptFunction>())
func = funcName;
else
func = ScriptFunction::GetByName(funcName);
if (!func)
BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
return func->Invoke(arguments);
}
static inline Value Indexer(const Object::Ptr& context, const Value& value, const String& index)
{
if (value.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = value;
return dict->Get(index);
} else if (value.IsObjectType<Array>()) {
Array::Ptr arr = value;
return arr->Get(Convert::ToLong(index));
} else if (value.IsObject()) {
Object::Ptr object = value;
Type::Ptr type = object->GetReflectionType();
if (!type)
BOOST_THROW_EXCEPTION(ConfigError("Dot operator applied to object which does not support reflection"));
int field = type->GetFieldId(index);
if (field == -1)
BOOST_THROW_EXCEPTION(ConfigError("Tried to access invalid property '" + index + "'"));
return object->GetField(field);
} else if (value.IsEmpty()) {
return Empty;
} else {
BOOST_THROW_EXCEPTION(ConfigError("Dot operator cannot be applied to type '" + value.GetTypeName() + "'"));
}
}
static inline Value NewFunction(const Object::Ptr& context, const String& name, const std::vector<String>& args, const boost::shared_ptr<Expression>& expression)
{
ScriptFunction::Ptr func = new ScriptFunction(boost::bind(&FunctionWrapper, _1, args, expression, context));
if (!name.IsEmpty())
ScriptFunction::Register(name, func);
return func;
}
static inline Value NewSlot(const Object::Ptr& context, const String& signal, const Value& slot)
{
ScriptSignal::Ptr sig = ScriptSignal::GetByName(signal);
if (!sig)
BOOST_THROW_EXCEPTION(ConfigError("Signal '" + signal + "' does not exist."));
sig->AddSlot(boost::bind(SlotWrapper, slot, _1));
return Empty;
}
static inline Value NewApply(const Object::Ptr& context, const String& type, const String& target, const String& name, const boost::shared_ptr<Expression>& filter,
const String& fkvar, const String& fvvar, const boost::shared_ptr<Expression>& fterm,
const boost::shared_ptr<Expression>& expression, const DebugInfo& debugInfo = DebugInfo())
{
ApplyRule::AddRule(type, target, name, expression, filter, fkvar, fvvar, fterm, debugInfo, context);
return Empty;
}
static inline Value NewObject(const Object::Ptr& context, bool abstract, const String& type, const String& name, const boost::shared_ptr<Expression>& filter,
const String& zone, const boost::shared_ptr<Expression>& expression, const DebugInfo& debugInfo = DebugInfo())
{
ConfigItemBuilder::Ptr item = new ConfigItemBuilder(debugInfo);
String checkName = name;
if (!abstract) {
Type::Ptr ptype = Type::GetByName(type);
NameComposer *nc = dynamic_cast<NameComposer *>(ptype.get());
if (nc)
checkName = nc->MakeName(name, Dictionary::Ptr());
}
if (!checkName.IsEmpty()) {
ConfigItem::Ptr oldItem = ConfigItem::GetObject(type, checkName);
if (oldItem) {
std::ostringstream msgbuf;
msgbuf << "Object '" << name << "' of type '" << type << "' re-defined: " << debugInfo << "; previous definition: " << oldItem->GetDebugInfo();
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(debugInfo));
}
}
item->SetType(type);
if (name.FindFirstOf("!") != String::NPos) {
std::ostringstream msgbuf;
msgbuf << "Name for object '" << name << "' of type '" << type << "' is invalid: Object names may not contain '!'";
BOOST_THROW_EXCEPTION(ConfigError(msgbuf.str()) << errinfo_debuginfo(debugInfo));
}
item->SetName(name);
item->AddExpression(new OwnedExpression(expression));
item->SetAbstract(abstract);
item->SetScope(context);
item->SetZone(zone);
item->Compile()->Register();
if (filter)
ObjectRule::AddRule(type, name, filter, debugInfo, context);
return Empty;
}
static inline Value For(const Object::Ptr& context, const String& fkvar, const String& fvvar, const Value& value, Expression *expression, const DebugInfo& debugInfo = DebugInfo())
{
if (value.IsObjectType<Array>()) {
if (!fvvar.IsEmpty())
BOOST_THROW_EXCEPTION(ConfigError("Cannot use dictionary iterator for array.") << errinfo_debuginfo(debugInfo));
Array::Ptr arr = value;
ObjectLock olock(arr);
BOOST_FOREACH(const Value& value, arr) {
Dictionary::Ptr xcontext = new Dictionary();
xcontext->Set("__parent", context);
xcontext->Set(fkvar, value);
expression->Evaluate(xcontext);
}
}
else if (value.IsObjectType<Dictionary>()) {
if (fvvar.IsEmpty())
BOOST_THROW_EXCEPTION(ConfigError("Cannot use array iterator for dictionary.") << errinfo_debuginfo(debugInfo));
Dictionary::Ptr dict = value;
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
Dictionary::Ptr xcontext = new Dictionary();
xcontext->Set("__parent", context);
xcontext->Set(fkvar, kv.first);
xcontext->Set(fvvar, kv.second);
expression->Evaluate(xcontext);
}
}
else
BOOST_THROW_EXCEPTION(ConfigError("Invalid type in __for expression: " + value.GetTypeName()) << errinfo_debuginfo(debugInfo));
return Empty;
}
static inline bool HasField(const Object::Ptr& context, const String& field)
{
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(context);
if (dict)
return dict->Contains(field);
else {
Type::Ptr type = context->GetReflectionType();
if (!type)
return false;
return type->GetFieldId(field) != -1;
}
}
static inline Value GetField(const Object::Ptr& context, const String& field)
{
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(context);
if (dict)
return dict->Get(field);
else {
Type::Ptr type = context->GetReflectionType();
if (!type)
return Empty;
int fid = type->GetFieldId(field);
if (fid == -1)
return Empty;
return context->GetField(fid);
}
}
static inline void SetField(const Object::Ptr& context, const String& field, const Value& value)
{
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(context);
if (dict)
dict->Set(field, value);
else {
Type::Ptr type = context->GetReflectionType();
if (!type)
BOOST_THROW_EXCEPTION(ConfigError("Cannot set field on object."));
int fid = type->GetFieldId(field);
if (fid == -1)
BOOST_THROW_EXCEPTION(ConfigError("Attribute '" + field + "' does not exist."));
try {
context->SetField(fid, value);
} catch (const boost::bad_lexical_cast&) {
BOOST_THROW_EXCEPTION(ConfigError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "'"));
} catch (const std::bad_cast&) {
BOOST_THROW_EXCEPTION(ConfigError("Attribute '" + field + "' cannot be set to value of type '" + value.GetTypeName() + "'"));
}
}
}
private:
static inline Value FunctionWrapper(const std::vector<Value>& arguments,
const std::vector<String>& funcargs, const boost::shared_ptr<Expression>& expr, const Object::Ptr& scope)
{
if (arguments.size() < funcargs.size())
BOOST_THROW_EXCEPTION(ConfigError("Too few arguments for function"));
Dictionary::Ptr context = new Dictionary();
context->Set("__parent", scope);
for (std::vector<Value>::size_type i = 0; i < std::min(arguments.size(), funcargs.size()); i++)
context->Set(funcargs[i], arguments[i]);
expr->Evaluate(context);
return context->Get("__result");
}
static void SlotWrapper(const Value& funcName, const std::vector<Value>& arguments)
{
ScriptFunction::Ptr func;
if (funcName.IsObjectType<ScriptFunction>())
func = funcName;
else
func = ScriptFunction::GetByName(funcName);
if (!func)
BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist."));
func->Invoke(arguments);
}
};
}
#endif /* VMOPS_H */