Implement support for the namespace and using keywords

This commit is contained in:
Gunnar Beutner 2018-08-07 13:55:41 +02:00
parent 10d6f70a85
commit 8fda8d72ac
11 changed files with 107 additions and 92 deletions

View File

@ -239,7 +239,8 @@ const std::vector<String>& ConfigWriter::GetKeywords()
keywords.emplace_back("globals"); keywords.emplace_back("globals");
keywords.emplace_back("locals"); keywords.emplace_back("locals");
keywords.emplace_back("use"); keywords.emplace_back("use");
keywords.emplace_back("__using"); keywords.emplace_back("using");
keywords.emplace_back("namespace");
keywords.emplace_back("default"); keywords.emplace_back("default");
keywords.emplace_back("ignore_on_error"); keywords.emplace_back("ignore_on_error");
keywords.emplace_back("current_filename"); keywords.emplace_back("current_filename");

View File

@ -19,27 +19,41 @@
#include "base/scriptframe.hpp" #include "base/scriptframe.hpp"
#include "base/scriptglobal.hpp" #include "base/scriptglobal.hpp"
#include "base/namespace.hpp"
#include "base/exception.hpp" #include "base/exception.hpp"
using namespace icinga; using namespace icinga;
boost::thread_specific_ptr<std::stack<ScriptFrame *> > ScriptFrame::m_ScriptFrames; boost::thread_specific_ptr<std::stack<ScriptFrame *> > ScriptFrame::m_ScriptFrames;
Array::Ptr ScriptFrame::m_Imports;
static auto l_InternalNSBehavior = new ConstNamespaceBehavior();
INITIALIZE_ONCE_WITH_PRIORITY([]() { INITIALIZE_ONCE_WITH_PRIORITY([]() {
Dictionary::Ptr systemNS = new Dictionary(); Namespace::Ptr globalNS = ScriptGlobal::GetGlobals();
ScriptGlobal::Set("System", systemNS);
ScriptFrame::AddImport(systemNS);
Dictionary::Ptr typesNS = new Dictionary(); auto systemNSBehavior = new ConstNamespaceBehavior();
ScriptGlobal::Set("Types", typesNS); systemNSBehavior->Freeze();
ScriptFrame::AddImport(typesNS); Namespace::Ptr systemNS = new Namespace(systemNSBehavior);
globalNS->SetAttribute("System", std::make_shared<ConstEmbeddedNamespaceValue>(systemNS));
Dictionary::Ptr deprecatedNS = new Dictionary(); auto typesNSBehavior = new ConstNamespaceBehavior();
ScriptGlobal::Set("Deprecated", deprecatedNS); typesNSBehavior->Freeze();
ScriptFrame::AddImport(deprecatedNS); Namespace::Ptr typesNS = new Namespace(typesNSBehavior);
globalNS->SetAttribute("Types", std::make_shared<ConstEmbeddedNamespaceValue>(typesNS));
auto statsNSBehavior = new ConstNamespaceBehavior();
statsNSBehavior->Freeze();
Namespace::Ptr statsNS = new Namespace(statsNSBehavior);
globalNS->SetAttribute("StatsFunctions", std::make_shared<ConstEmbeddedNamespaceValue>(statsNS));
Namespace::Ptr internalNS = new Namespace(l_InternalNSBehavior);
globalNS->SetAttribute("Internal", std::make_shared<ConstEmbeddedNamespaceValue>(internalNS));
}, 50); }, 50);
INITIALIZE_ONCE_WITH_PRIORITY([]() {
l_InternalNSBehavior->Freeze();
}, 0);
ScriptFrame::ScriptFrame(bool allocLocals) ScriptFrame::ScriptFrame(bool allocLocals)
: Locals(allocLocals ? new Dictionary() : nullptr), Self(ScriptGlobal::GetGlobals()), Sandboxed(false), Depth(0) : Locals(allocLocals ? new Dictionary() : nullptr), Self(ScriptGlobal::GetGlobals()), Sandboxed(false), Depth(0)
{ {
@ -120,23 +134,3 @@ void ScriptFrame::PushFrame(ScriptFrame *frame)
frames->push(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;
}

View File

@ -45,12 +45,8 @@ struct ScriptFrame
static ScriptFrame *GetCurrentFrame(); static ScriptFrame *GetCurrentFrame();
static Array::Ptr GetImports();
static void AddImport(const Object::Ptr& import);
private: private:
static boost::thread_specific_ptr<std::stack<ScriptFrame *> > m_ScriptFrames; static boost::thread_specific_ptr<std::stack<ScriptFrame *> > m_ScriptFrames;
static Array::Ptr m_Imports;
static void PushFrame(ScriptFrame *frame); static void PushFrame(ScriptFrame *frame);
static ScriptFrame *PopFrame(); static ScriptFrame *PopFrame();

View File

@ -180,7 +180,7 @@ this return T_THIS;
globals return T_GLOBALS; globals return T_GLOBALS;
locals return T_LOCALS; locals return T_LOCALS;
use return T_USE; use return T_USE;
__using return T_USING; using return T_USING;
apply return T_APPLY; apply return T_APPLY;
default return T_DEFAULT; default return T_DEFAULT;
to return T_TO; to return T_TO;
@ -203,6 +203,7 @@ ignore_on_error return T_IGNORE_ON_ERROR;
current_filename return T_CURRENT_FILENAME; current_filename return T_CURRENT_FILENAME;
current_line return T_CURRENT_LINE; current_line return T_CURRENT_LINE;
debugger return T_DEBUGGER; debugger return T_DEBUGGER;
namespace return T_NAMESPACE;
=\> return T_FOLLOWS; =\> return T_FOLLOWS;
\<\< return T_SHIFT_LEFT; \<\< return T_SHIFT_LEFT;
\>\> return T_SHIFT_RIGHT; \>\> return T_SHIFT_RIGHT;

View File

@ -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_FILENAME "current_filename (T_CURRENT_FILENAME)"
%token T_CURRENT_LINE "current_line (T_CURRENT_LINE)" %token T_CURRENT_LINE "current_line (T_CURRENT_LINE)"
%token T_DEBUGGER "debugger (T_DEBUGGER)" %token T_DEBUGGER "debugger (T_DEBUGGER)"
%token T_NAMESPACE "namespace (T_NAMESPACE)"
%token T_USE "use (T_USE)" %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_OBJECT "object (T_OBJECT)"
%token T_TEMPLATE "template (T_TEMPLATE)" %token T_TEMPLATE "template (T_TEMPLATE)"
%token T_INCLUDE "include (T_INCLUDE)" %token T_INCLUDE "include (T_INCLUDE)"
@ -602,9 +603,23 @@ lterm: T_LIBRARY rterm
{ {
$$ = new BreakpointExpression(@$); $$ = new BreakpointExpression(@$);
} }
| T_NAMESPACE rterm
{
BeginFlowControlBlock(context, FlowControlReturn, false);
}
rterm_scope_require_side_effect
{
EndFlowControlBlock(context);
std::unique_ptr<Expression> expr{$2};
BindToScope(expr, ScopeGlobal);
$$ = new SetExpression(std::move(expr), OpSetLiteral, std::unique_ptr<Expression>(new NamespaceExpression(std::unique_ptr<Expression>($4), @$)), @$);
}
| T_USING rterm | T_USING rterm
{ {
$$ = new UsingExpression(std::unique_ptr<Expression>($2), @$); std::shared_ptr<Expression> expr{$2};
context->AddImport(expr);
$$ = MakeLiteralRaw();
} }
| apply | apply
| object | object
@ -879,7 +894,7 @@ rterm_no_side_effect_no_dict: T_STRING
} }
| T_IDENTIFIER | T_IDENTIFIER
{ {
$$ = new VariableExpression(*$1, @1); $$ = new VariableExpression(*$1, context->GetImports(), @1);
delete $1; delete $1;
} }
| T_MULTIPLY rterm %prec DEREF_OP | T_MULTIPLY rterm %prec DEREF_OP
@ -1105,7 +1120,7 @@ use_specifier_items: use_specifier_item
use_specifier_item: identifier use_specifier_item: identifier
{ {
$$ = new std::pair<String, std::unique_ptr<Expression> >(*$1, std::unique_ptr<Expression>(new VariableExpression(*$1, @1))); $$ = new std::pair<String, std::unique_ptr<Expression> >(*$1, std::unique_ptr<Expression>(new VariableExpression(*$1, context->GetImports(), @1)));
delete $1; delete $1;
} }
| identifier T_SET rterm | identifier T_SET rterm

View File

@ -361,3 +361,12 @@ bool ConfigCompiler::IsAbsolutePath(const String& path)
#endif /* _WIN32 */ #endif /* _WIN32 */
} }
void ConfigCompiler::AddImport(const std::shared_ptr<Expression>& import)
{
m_Imports.push_back(import);
}
std::vector<std::shared_ptr<Expression> > ConfigCompiler::GetImports() const
{
return m_Imports;
}

View File

@ -109,6 +109,9 @@ public:
void SetPackage(const String& package); void SetPackage(const String& package);
String GetPackage() const; String GetPackage() const;
void AddImport(const std::shared_ptr<Expression>& import);
std::vector<std::shared_ptr<Expression> > GetImports() const;
static void CollectIncludes(std::vector<std::unique_ptr<Expression> >& expressions, static void CollectIncludes(std::vector<std::unique_ptr<Expression> >& expressions,
const String& file, const String& zone, const String& package); const String& file, const String& zone, const String& package);
@ -134,6 +137,7 @@ private:
std::istream *m_Input; std::istream *m_Input;
String m_Zone; String m_Zone;
String m_Package; String m_Package;
std::vector<std::shared_ptr<Expression> > m_Imports;
void *m_Scanner; void *m_Scanner;

View File

@ -117,6 +117,14 @@ const DebugInfo& DebuggableExpression::GetDebugInfo() const
return m_DebugInfo; return m_DebugInfo;
} }
VariableExpression::VariableExpression(String variable, std::vector<std::shared_ptr<Expression> > 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 ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
{ {
Value value; Value value;
@ -125,7 +133,7 @@ ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d
return value; return value;
else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get<Object::Ptr>() && frame.Self.Get<Object::Ptr>()->GetOwnField(m_Variable, &value)) else if (frame.Self.IsObject() && frame.Locals != frame.Self.Get<Object::Ptr>() && frame.Self.Get<Object::Ptr>()->GetOwnField(m_Variable, &value))
return 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; return value;
else else
return ScriptGlobal::Get(m_Variable); return ScriptGlobal::Get(m_Variable);
@ -145,7 +153,7 @@ bool VariableExpression::GetReference(ScriptFrame& frame, bool init_dict, Value
if (dhint && *dhint) if (dhint && *dhint)
*dhint = new DebugHint((*dhint)->GetChild(m_Variable)); *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; return true;
} else if (ScriptGlobal::Exists(m_Variable)) { } else if (ScriptGlobal::Exists(m_Variable)) {
*parent = ScriptGlobal::GetGlobals(); *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); 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 ExpressionResult ObjectExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
{ {
if (frame.Sandboxed) if (frame.Sandboxed)
@ -1006,23 +1025,6 @@ ExpressionResult BreakpointExpression::DoEvaluate(ScriptFrame& frame, DebugHint
return Empty; 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<Dictionary>())
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 ExpressionResult TryExceptExpression::DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const
{ {
try { try {

View File

@ -305,9 +305,7 @@ protected:
class VariableExpression final : public DebuggableExpression class VariableExpression final : public DebuggableExpression
{ {
public: public:
VariableExpression(String variable, const DebugInfo& debugInfo = DebugInfo()) VariableExpression(String variable, std::vector<std::shared_ptr<Expression> > imports, const DebugInfo& debugInfo = DebugInfo());
: DebuggableExpression(debugInfo), m_Variable(std::move(variable))
{ }
String GetVariable() const String GetVariable() const
{ {
@ -320,6 +318,7 @@ protected:
private: private:
String m_Variable; String m_Variable;
std::vector<std::shared_ptr<Expression> > m_Imports;
friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec); friend void BindToScope(std::unique_ptr<Expression>& expr, ScopeSpecifier scopeSpec);
}; };
@ -856,6 +855,20 @@ private:
std::shared_ptr<Expression> m_Expression; std::shared_ptr<Expression> m_Expression;
}; };
class NamespaceExpression final : public DebuggableExpression
{
public:
NamespaceExpression(std::unique_ptr<Expression> 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<Expression> m_Expression;
};
class ObjectExpression final : public DebuggableExpression class ObjectExpression final : public DebuggableExpression
{ {
public: public:
@ -952,20 +965,6 @@ protected:
ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override; ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
}; };
class UsingExpression final : public DebuggableExpression
{
public:
UsingExpression(std::unique_ptr<Expression> 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<Expression> m_Name;
};
class TryExceptExpression final : public DebuggableExpression class TryExceptExpression final : public DebuggableExpression
{ {
public: public:

View File

@ -43,15 +43,13 @@ namespace icinga
class VMOps class VMOps
{ {
public: 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<std::shared_ptr<Expression> >& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
{ {
Array::Ptr imports = ScriptFrame::GetImports(); for (const auto& import : imports) {
ExpressionResult res = import->Evaluate(frame);
ObjectLock olock(imports); Object::Ptr obj = res.GetValue();
for (const Value& import : imports) {
Object::Ptr obj = import;
if (obj->HasOwnField(name)) { if (obj->HasOwnField(name)) {
*result = import; *result = obj;
return true; return true;
} }
} }
@ -59,11 +57,11 @@ public:
return false; 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<std::shared_ptr<Expression> >& imports, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
{ {
Value parent; Value parent;
if (FindVarImportRef(frame, name, &parent, debugInfo)) { if (FindVarImportRef(frame, imports, name, &parent, debugInfo)) {
*result = GetField(parent, name, frame.Sandboxed, debugInfo); *result = GetField(parent, name, frame.Sandboxed, debugInfo);
return true; return true;
} }

View File

@ -290,13 +290,9 @@ std::vector<String> ConsoleHandler::GetAutocompletionSuggestions(const String& w
} }
} }
{ AddSuggestions(matches, word, "", false, ScriptGlobal::Get("System"));
Array::Ptr imports = ScriptFrame::GetImports(); AddSuggestions(matches, word, "", false, ScriptGlobal::Get("Types"));
ObjectLock olock(imports); AddSuggestions(matches, word, "", false, ScriptGlobal::Get("Icinga"));
for (const Value& import : imports) {
AddSuggestions(matches, word, "", false, import);
}
}
String::SizeType cperiod = word.RFind("."); String::SizeType cperiod = word.RFind(".");