diff --git a/lib/cli/CMakeLists.txt b/lib/cli/CMakeLists.txt index 121effb76..8590d3607 100644 --- a/lib/cli/CMakeLists.txt +++ b/lib/cli/CMakeLists.txt @@ -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 diff --git a/lib/cli/codegencommand.cpp b/lib/cli/codegencommand.cpp new file mode 100644 index 000000000..57c358748 --- /dev/null +++ b/lib/cli/codegencommand.cpp @@ -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& ap) const +{ + Logger::SetConsoleLogSeverity(LogWarning); + + Expression *expr = ConfigCompiler::CompileStream("", &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 " << "\n" + << "\n" + << "using namespace icinga;" << "\n" + << "\n"; + + std::map 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; +} diff --git a/lib/cli/codegencommand.hpp b/lib/cli/codegencommand.hpp new file mode 100644 index 000000000..813c39350 --- /dev/null +++ b/lib/cli/codegencommand.hpp @@ -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 + +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& ap) const; +}; + +} + +#endif /* CODEGENCOMMAND_H */ diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index a62e1fb86..966af8568 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -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 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 >()) { - ConfigCompiler::CompileFile(configPath); + Expression *expression = ConfigCompiler::CompileFile(configPath); + Dictionary::Ptr context = new Dictionary(); + expression->Evaluate(context); } - } else if (!vm.count("no-config")) - ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf"); + } else if (!vm.count("no-config")) { + Expression *expression = ConfigCompiler::CompileFile(Application::GetSysconfDir() + "/icinga2/icinga2.conf"); + Dictionary::Ptr context = new Dictionary(); + expression->Evaluate(context); + } /* 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(); diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 596c70ccc..b09aaf848 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -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) diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 54744819e..79b0face3 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -227,8 +227,9 @@ static std::stack m_Ignore; static std::stack m_FKVar; static std::stack m_FVVar; static std::stack m_FTerm; +static std::stack > 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(); m_FVVar = std::stack(); m_FTerm = std::stack(); + m_Expressions.push(std::vector()); 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(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)); diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index 19c78f466..cd39617a0 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -97,6 +97,11 @@ String ConfigCompiler::GetZone(void) const return m_Zone; } +void ConfigCompiler::CollectIncludes(std::vector& 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 items; + std::vector 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 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); diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index 3c3dd0dd1..ee63c8061 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -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& 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); diff --git a/lib/config/configtype.cpp b/lib/config/configtype.cpp index 9a5873339..423ad7a1f 100644 --- a/lib/config/configtype.cpp +++ b/lib/config/configtype.cpp @@ -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(value).IsEmpty())) { ConfigCompilerContext::GetInstance()->AddMessage(true, diff --git a/lib/config/expression-codegen.cpp b/lib/config/expression-codegen.cpp new file mode 100644 index 000000000..c0e44c9ea --- /dev/null +++ b/lib/config/expression-codegen.cpp @@ -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 + +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("; + 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("; + 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(this); + + df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n" + << "{" << "\n" + << " Value funcName = ("; + m_FName->GenerateCode(definitions, df); + df << ");" << "\n" + << " std::vector 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(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(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(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(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(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(this); + + df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n" + << "{" << "\n" + << " std::vector 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(" + << 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(this); + + df << "static Value " << namebuf.str() << "(const Object::Ptr& context)" << "\n" + << "{" << "\n" + << " boost::shared_ptr fterm;" << "\n"; + + if (m_FTerm) + df << " fterm = boost::make_shared(" << CodeGenExpression(definitions, m_FTerm.get()) << ");" << "\n"; + + df << " boost::shared_ptr filter = boost::make_shared(" << CodeGenExpression(definitions, m_Filter.get()) << ");" << "\n" + << " boost::shared_ptr expression = boost::make_shared(" << 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(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 filter;" << "\n"; + + if (m_Filter) + df << " filter = boost::make_shared(" + << CodeGenExpression(definitions, m_Filter.get()) << ");" << "\n"; + + df << " boost::shared_ptr expression = boost::make_shared(" + << 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(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(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(); +} diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 5bc4ecc39..e3cbc3db3 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -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 #include -#include #include 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 @@ -199,7 +182,7 @@ Value InExpression::DoEvaluate(const Object::Ptr& context, DebugHint *dhint) con BOOST_THROW_EXCEPTION(ConfigError("Invalid right side argument for 'in' operator: " + JsonEncode(right))); Value left = m_Operand1->Evaluate(context); - + Array::Ptr arr = right; return arr->Contains(left); } @@ -233,22 +216,12 @@ Value FunctionCallExpression::DoEvaluate(const Object::Ptr& context, DebugHint * { Value funcName = m_FName->Evaluate(context); - ScriptFunction::Ptr func; - - if (funcName.IsObjectType()) - func = funcName; - else - func = ScriptFunction::GetByName(funcName); - - if (!func) - BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist.")); - std::vector 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::Ptr dict = value; - return dict->Get(index); - } else if (value.IsObjectType()) { - 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& arguments, - const std::vector& funcargs, const boost::shared_ptr& 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::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& arguments) -{ - ScriptFunction::Ptr func; - - if (funcName.IsObjectType()) - 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(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()) { - 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()) { - 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(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(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(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); } diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 1411b070d..709acc9c8 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -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 +#include namespace icinga { @@ -96,6 +100,8 @@ enum CombinedSetOp OpSetDivide }; +typedef std::map 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& arguments, - const std::vector& funcargs, - const boost::shared_ptr& 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 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 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 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 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; @@ -532,7 +585,7 @@ private: Expression *m_Operand2; }; - + class I2_CONFIG_API IndexerExpression : public BinaryExpression { public: @@ -542,8 +595,9 @@ 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 { public: @@ -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; diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp new file mode 100644 index 000000000..3db6b6532 --- /dev/null +++ b/lib/config/vmops.hpp @@ -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 +#include +#include + +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& arguments) + { + ScriptFunction::Ptr func; + + if (funcName.IsObjectType()) + 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::Ptr dict = value; + return dict->Get(index); + } else if (value.IsObjectType()) { + 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& args, const boost::shared_ptr& 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& filter, + const String& fkvar, const String& fvvar, const boost::shared_ptr& fterm, + const boost::shared_ptr& 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& filter, + const String& zone, const boost::shared_ptr& 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(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()) { + 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()) { + 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(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(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(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& arguments, + const std::vector& funcargs, const boost::shared_ptr& 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::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& arguments) + { + ScriptFunction::Ptr func; + + if (funcName.IsObjectType()) + func = funcName; + else + func = ScriptFunction::GetByName(funcName); + + if (!func) + BOOST_THROW_EXCEPTION(ConfigError("Function '" + funcName + "' does not exist.")); + + func->Invoke(arguments); + } +}; + +} + +#endif /* VMOPS_H */