From: Gunnar Beutner Date: Tue, 7 Aug 2018 11:55:41 +0000 (+0200) Subject: Implement support for the namespace and using keywords X-Git-Tag: v2.10.0~56^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8fda8d72acb5c96565665903a0b6116dff5680ba;p=icinga2 Implement support for the namespace and using keywords --- diff --git a/lib/base/configwriter.cpp b/lib/base/configwriter.cpp index 58851c2cf..a085be915 100644 --- a/lib/base/configwriter.cpp +++ b/lib/base/configwriter.cpp @@ -239,7 +239,8 @@ const std::vector& ConfigWriter::GetKeywords() keywords.emplace_back("globals"); keywords.emplace_back("locals"); keywords.emplace_back("use"); - keywords.emplace_back("__using"); + keywords.emplace_back("using"); + keywords.emplace_back("namespace"); keywords.emplace_back("default"); keywords.emplace_back("ignore_on_error"); keywords.emplace_back("current_filename"); diff --git a/lib/base/scriptframe.cpp b/lib/base/scriptframe.cpp index 65597a783..01f871ec4 100644 --- a/lib/base/scriptframe.cpp +++ b/lib/base/scriptframe.cpp @@ -19,27 +19,41 @@ #include "base/scriptframe.hpp" #include "base/scriptglobal.hpp" +#include "base/namespace.hpp" #include "base/exception.hpp" using namespace icinga; boost::thread_specific_ptr > ScriptFrame::m_ScriptFrames; -Array::Ptr ScriptFrame::m_Imports; + +static auto l_InternalNSBehavior = new ConstNamespaceBehavior(); INITIALIZE_ONCE_WITH_PRIORITY([]() { - Dictionary::Ptr systemNS = new Dictionary(); - ScriptGlobal::Set("System", systemNS); - ScriptFrame::AddImport(systemNS); + Namespace::Ptr globalNS = ScriptGlobal::GetGlobals(); + + auto systemNSBehavior = new ConstNamespaceBehavior(); + systemNSBehavior->Freeze(); + Namespace::Ptr systemNS = new Namespace(systemNSBehavior); + globalNS->SetAttribute("System", std::make_shared(systemNS)); - Dictionary::Ptr typesNS = new Dictionary(); - ScriptGlobal::Set("Types", typesNS); - ScriptFrame::AddImport(typesNS); + auto typesNSBehavior = new ConstNamespaceBehavior(); + typesNSBehavior->Freeze(); + Namespace::Ptr typesNS = new Namespace(typesNSBehavior); + globalNS->SetAttribute("Types", std::make_shared(typesNS)); - Dictionary::Ptr deprecatedNS = new Dictionary(); - ScriptGlobal::Set("Deprecated", deprecatedNS); - ScriptFrame::AddImport(deprecatedNS); + auto statsNSBehavior = new ConstNamespaceBehavior(); + statsNSBehavior->Freeze(); + Namespace::Ptr statsNS = new Namespace(statsNSBehavior); + globalNS->SetAttribute("StatsFunctions", std::make_shared(statsNS)); + + Namespace::Ptr internalNS = new Namespace(l_InternalNSBehavior); + globalNS->SetAttribute("Internal", std::make_shared(internalNS)); }, 50); +INITIALIZE_ONCE_WITH_PRIORITY([]() { + l_InternalNSBehavior->Freeze(); +}, 0); + ScriptFrame::ScriptFrame(bool allocLocals) : Locals(allocLocals ? new Dictionary() : nullptr), Self(ScriptGlobal::GetGlobals()), Sandboxed(false), Depth(0) { @@ -120,23 +134,3 @@ void ScriptFrame::PushFrame(ScriptFrame *frame) frames->push(frame); } - -Array::Ptr ScriptFrame::GetImports() -{ - return m_Imports; -} - -void ScriptFrame::AddImport(const Object::Ptr& import) -{ - Array::Ptr imports; - - if (!m_Imports) - imports = new Array(); - else - imports = m_Imports->ShallowClone(); - - imports->Add(import); - - m_Imports = imports; -} - diff --git a/lib/base/scriptframe.hpp b/lib/base/scriptframe.hpp index 16a6592f5..56ff4f922 100644 --- a/lib/base/scriptframe.hpp +++ b/lib/base/scriptframe.hpp @@ -45,12 +45,8 @@ struct ScriptFrame static ScriptFrame *GetCurrentFrame(); - static Array::Ptr GetImports(); - static void AddImport(const Object::Ptr& import); - private: static boost::thread_specific_ptr > m_ScriptFrames; - static Array::Ptr m_Imports; static void PushFrame(ScriptFrame *frame); static ScriptFrame *PopFrame(); diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index 6a2d84027..899280ff7 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -180,7 +180,7 @@ this return T_THIS; globals return T_GLOBALS; locals return T_LOCALS; use return T_USE; -__using return T_USING; +using return T_USING; apply return T_APPLY; default return T_DEFAULT; to return T_TO; @@ -203,6 +203,7 @@ ignore_on_error return T_IGNORE_ON_ERROR; current_filename return T_CURRENT_FILENAME; current_line return T_CURRENT_LINE; debugger return T_DEBUGGER; +namespace return T_NAMESPACE; =\> return T_FOLLOWS; \<\< return T_SHIFT_LEFT; \>\> return T_SHIFT_RIGHT; diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 889d144d3..5db8f5598 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -149,8 +149,9 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig %token T_CURRENT_FILENAME "current_filename (T_CURRENT_FILENAME)" %token T_CURRENT_LINE "current_line (T_CURRENT_LINE)" %token T_DEBUGGER "debugger (T_DEBUGGER)" +%token T_NAMESPACE "namespace (T_NAMESPACE)" %token T_USE "use (T_USE)" -%token T_USING "__using (T_USING)" +%token T_USING "using (T_USING)" %token T_OBJECT "object (T_OBJECT)" %token T_TEMPLATE "template (T_TEMPLATE)" %token T_INCLUDE "include (T_INCLUDE)" @@ -602,9 +603,23 @@ lterm: T_LIBRARY rterm { $$ = new BreakpointExpression(@$); } + | T_NAMESPACE rterm + { + BeginFlowControlBlock(context, FlowControlReturn, false); + } + rterm_scope_require_side_effect + { + EndFlowControlBlock(context); + + std::unique_ptr expr{$2}; + BindToScope(expr, ScopeGlobal); + $$ = new SetExpression(std::move(expr), OpSetLiteral, std::unique_ptr(new NamespaceExpression(std::unique_ptr($4), @$)), @$); + } | T_USING rterm { - $$ = new UsingExpression(std::unique_ptr($2), @$); + std::shared_ptr expr{$2}; + context->AddImport(expr); + $$ = MakeLiteralRaw(); } | apply | object @@ -879,7 +894,7 @@ rterm_no_side_effect_no_dict: T_STRING } | T_IDENTIFIER { - $$ = new VariableExpression(*$1, @1); + $$ = new VariableExpression(*$1, context->GetImports(), @1); delete $1; } | T_MULTIPLY rterm %prec DEREF_OP @@ -1105,7 +1120,7 @@ use_specifier_items: use_specifier_item use_specifier_item: identifier { - $$ = new std::pair >(*$1, std::unique_ptr(new VariableExpression(*$1, @1))); + $$ = new std::pair >(*$1, std::unique_ptr(new VariableExpression(*$1, context->GetImports(), @1))); delete $1; } | identifier T_SET rterm diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index f8b754bb9..05db34e02 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -361,3 +361,12 @@ bool ConfigCompiler::IsAbsolutePath(const String& path) #endif /* _WIN32 */ } +void ConfigCompiler::AddImport(const std::shared_ptr& import) +{ + m_Imports.push_back(import); +} + +std::vector > ConfigCompiler::GetImports() const +{ + return m_Imports; +} diff --git a/lib/config/configcompiler.hpp b/lib/config/configcompiler.hpp index d4a03681e..4bbc5c083 100644 --- a/lib/config/configcompiler.hpp +++ b/lib/config/configcompiler.hpp @@ -109,6 +109,9 @@ public: void SetPackage(const String& package); String GetPackage() const; + void AddImport(const std::shared_ptr& import); + std::vector > GetImports() const; + static void CollectIncludes(std::vector >& expressions, const String& file, const String& zone, const String& package); @@ -134,6 +137,7 @@ private: std::istream *m_Input; String m_Zone; String m_Package; + std::vector > m_Imports; void *m_Scanner; diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 9cfc44a22..7600b130e 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -117,6 +117,14 @@ const DebugInfo& DebuggableExpression::GetDebugInfo() const return m_DebugInfo; } +VariableExpression::VariableExpression(String variable, std::vector > imports, const DebugInfo& debugInfo) + : DebuggableExpression(debugInfo), m_Variable(std::move(variable)), m_Imports(std::move(imports)) +{ + m_Imports.push_back(MakeIndexer(ScopeGlobal, "System")); + m_Imports.push_back(MakeIndexer(ScopeGlobal, "Types")); + m_Imports.push_back(MakeIndexer(ScopeGlobal, "Icinga")); +} + ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { Value value; @@ -125,7 +133,7 @@ ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d return value; else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get() && frame.Self.Get()->GetOwnField(m_Variable, &value)) return value; - else if (VMOps::FindVarImport(frame, m_Variable, &value, m_DebugInfo)) + else if (VMOps::FindVarImport(frame, m_Imports, m_Variable, &value, m_DebugInfo)) return value; else return ScriptGlobal::Get(m_Variable); @@ -145,7 +153,7 @@ bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value if (dhint && *dhint) *dhint = new DebugHint((*dhint)->GetChild(m_Variable)); - } else if (VMOps::FindVarImportRef(frame, m_Variable, parent, m_DebugInfo)) { + } else if (VMOps::FindVarImportRef(frame, m_Imports, m_Variable, parent, m_DebugInfo)) { return true; } else if (ScriptGlobal::Exists(m_Variable)) { *parent = ScriptGlobal::GetGlobals(); @@ -883,6 +891,17 @@ ExpressionResult ApplyExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhin m_Package, m_FKVar, m_FVVar, m_FTerm, m_ClosedVars, m_IgnoreOnError, m_Expression, m_DebugInfo); } +ExpressionResult NamespaceExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const +{ + Namespace::Ptr ns = new Namespace(new ConstNamespaceBehavior()); + + ScriptFrame innerFrame(true, ns); + ExpressionResult result = m_Expression->Evaluate(innerFrame); + CHECK_RESULT(result); + + return ns; +} + ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { if (frame.Sandboxed) @@ -1006,23 +1025,6 @@ ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint return Empty; } -ExpressionResult UsingExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const -{ - if (frame.Sandboxed) - BOOST_THROW_EXCEPTION(ScriptError("Using directives are not allowed in sandbox mode.", m_DebugInfo)); - - ExpressionResult importres = m_Name->Evaluate(frame); - CHECK_RESULT(importres); - Value import = importres.GetValue(); - - if (!import.IsObjectType()) - BOOST_THROW_EXCEPTION(ScriptError("The parameter must resolve to an object of type 'Dictionary'", m_DebugInfo)); - - ScriptFrame::AddImport(import); - - return Empty; -} - ExpressionResult TryExceptExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const { try { diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 865ded4a7..c0fab4096 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -305,9 +305,7 @@ protected: class VariableExpression final : public DebuggableExpression { public: - VariableExpression(String variable, const DebugInfo& debugInfo = DebugInfo()) - : DebuggableExpression(debugInfo), m_Variable(std::move(variable)) - { } + VariableExpression(String variable, std::vector > imports, const DebugInfo& debugInfo = DebugInfo()); String GetVariable() const { @@ -320,6 +318,7 @@ protected: private: String m_Variable; + std::vector > m_Imports; friend void BindToScope(std::unique_ptr& expr, ScopeSpecifier scopeSpec); }; @@ -856,6 +855,20 @@ private: std::shared_ptr m_Expression; }; +class NamespaceExpression final : public DebuggableExpression +{ +public: + NamespaceExpression(std::unique_ptr expression, const DebugInfo& debugInfo = DebugInfo()) + : DebuggableExpression(debugInfo), m_Expression(std::move(expression)) + { } + +protected: + ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; + +private: + std::shared_ptr m_Expression; +}; + class ObjectExpression final : public DebuggableExpression { public: @@ -952,20 +965,6 @@ protected: ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; }; -class UsingExpression final : public DebuggableExpression -{ -public: - UsingExpression(std::unique_ptr name, const DebugInfo& debugInfo = DebugInfo()) - : DebuggableExpression(debugInfo), m_Name(std::move(name)) - { } - -protected: - ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; - -private: - std::unique_ptr m_Name; -}; - class TryExceptExpression final : public DebuggableExpression { public: diff --git a/lib/config/vmops.hpp b/lib/config/vmops.hpp index 7bfa5628d..38f8507a7 100644 --- a/lib/config/vmops.hpp +++ b/lib/config/vmops.hpp @@ -43,15 +43,13 @@ namespace icinga class VMOps { public: - static inline bool FindVarImportRef(ScriptFrame& frame, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) + static inline bool FindVarImportRef(ScriptFrame& frame, const std::vector >& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) { - Array::Ptr imports = ScriptFrame::GetImports(); - - ObjectLock olock(imports); - for (const Value& import : imports) { - Object::Ptr obj = import; + for (const auto& import : imports) { + ExpressionResult res = import->Evaluate(frame); + Object::Ptr obj = res.GetValue(); if (obj->HasOwnField(name)) { - *result = import; + *result = obj; return true; } } @@ -59,11 +57,11 @@ public: return false; } - static inline bool FindVarImport(ScriptFrame& frame, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) + static inline bool FindVarImport(ScriptFrame& frame, const std::vector >& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo()) { Value parent; - if (FindVarImportRef(frame, name, &parent, debugInfo)) { + if (FindVarImportRef(frame, imports, name, &parent, debugInfo)) { *result = GetField(parent, name, frame.Sandboxed, debugInfo); return true; } diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index 045cb59d5..aff47a9a9 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -290,13 +290,9 @@ std::vector ConsoleHandler::GetAutocompletionSuggestions(const String& w } } - { - Array::Ptr imports = ScriptFrame::GetImports(); - ObjectLock olock(imports); - for (const Value& import : imports) { - AddSuggestions(matches, word, "", false, import); - } - } + AddSuggestions(matches, word, "", false, ScriptGlobal::Get("System")); + AddSuggestions(matches, word, "", false, ScriptGlobal::Get("Types")); + AddSuggestions(matches, word, "", false, ScriptGlobal::Get("Icinga")); String::SizeType cperiod = word.RFind(".");