Implement support for namespaces

fixes #12408
This commit is contained in:
Gunnar Beutner 2016-08-12 11:25:36 +02:00
parent 76ed38fbfe
commit c5a170a972
15 changed files with 204 additions and 100 deletions

View File

@ -244,6 +244,7 @@ const std::vector<String>& ConfigWriter::GetKeywords(void)
keywords.push_back("globals");
keywords.push_back("locals");
keywords.push_back("use");
keywords.push_back("__using");
keywords.push_back("ignore_on_error");
keywords.push_back("current_filename");
keywords.push_back("current_line");

View File

@ -66,22 +66,22 @@ private:
Callback m_Callback;
};
#define REGISTER_SCRIPTFUNCTION(name, callback) \
namespace { namespace UNIQUE_NAME(sf) { namespace sf ## name { \
void RegisterFunction(void) { \
Function::Ptr sf = new icinga::Function(#name, WrapFunction(callback)); \
ScriptGlobal::Set(#name, sf); \
} \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }
#define REGISTER_SCRIPTFUNCTION_NS(ns, name, callback) \
namespace { namespace UNIQUE_NAME(sf) { namespace sf ## ns ## name { \
void RegisterFunction(void) { \
Function::Ptr sf = new icinga::Function(#ns "#" #name, WrapFunction(callback), false); \
ScriptGlobal::Set(#ns "." #name, sf); \
} \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }
#define REGISTER_SCRIPTFUNCTION_NS_PREFIX(ns, name, callback) \
namespace { namespace UNIQUE_NAME(sf) { namespace sf ## ns ## name { \
void RegisterFunction(void) { \
Function::Ptr sf = new icinga::Function(#ns "#" #name, WrapFunction(callback), false); \
ScriptGlobal::Set(#ns "." #name, sf); \
Function::Ptr dsf = new icinga::Function("__" #name " (deprecated)", WrapFunction(callback), false, true); \
ScriptGlobal::Set("__" #name, dsf); \
ScriptGlobal::Set("System.__" #name, dsf); \
} \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }
@ -97,22 +97,22 @@ private:
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }
#define REGISTER_SAFE_SCRIPTFUNCTION(name, callback) \
namespace { namespace UNIQUE_NAME(sf) { namespace sf ## name { \
void RegisterFunction(void) { \
Function::Ptr sf = new icinga::Function(#name, WrapFunction(callback), true); \
ScriptGlobal::Set(#name, sf); \
} \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }
#define REGISTER_SAFE_SCRIPTFUNCTION_NS(ns, name, callback) \
namespace { namespace UNIQUE_NAME(sf) { namespace sf ## ns ## name { \
void RegisterFunction(void) { \
Function::Ptr sf = new icinga::Function(#ns "#" #name, WrapFunction(callback), true); \
ScriptGlobal::Set(#ns "." #name, sf); \
} \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }
#define REGISTER_SAFE_SCRIPTFUNCTION_NS_PREFIX(ns, name, callback) \
namespace { namespace UNIQUE_NAME(sf) { namespace sf ## ns ## name { \
void RegisterFunction(void) { \
Function::Ptr sf = new icinga::Function(#ns "#" #name, WrapFunction(callback), true); \
ScriptGlobal::Set(#ns "." #name, sf); \
Function::Ptr dsf = new icinga::Function("__" #name " (deprecated)", WrapFunction(callback), true, true); \
ScriptGlobal::Set("__" #name, dsf); \
ScriptGlobal::Set("System.__" #name, dsf); \
} \
INITIALIZE_ONCE_WITH_PRIORITY(RegisterFunction, 10); \
} } }

View File

@ -30,8 +30,15 @@ ScriptFrame::ScriptFrame(void)
{
std::stack<ScriptFrame *> *frames = m_ScriptFrames.get();
if (frames && !frames->empty())
Sandboxed = frames->top()->Sandboxed;
if (frames && !frames->empty()) {
ScriptFrame *frame = frames->top();
Sandboxed = frame->Sandboxed;
Imports = frame->Imports;
} else {
Imports = new Array();
Imports->Add(ScriptGlobal::Get("System"));
}
PushFrame(this);
}

View File

@ -22,6 +22,7 @@
#include "config/i2-config.hpp"
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include <boost/thread/tss.hpp>
#include <stack>
@ -31,6 +32,7 @@ namespace icinga
struct I2_BASE_API ScriptFrame
{
Dictionary::Ptr Locals;
Array::Ptr Imports;
Value Self;
bool Sandboxed;
int Depth;

View File

@ -37,35 +37,35 @@
using namespace icinga;
REGISTER_SAFE_SCRIPTFUNCTION(regex, &ScriptUtils::Regex);
REGISTER_SAFE_SCRIPTFUNCTION(match, &Utility::Match);
REGISTER_SAFE_SCRIPTFUNCTION(cidr_match, &Utility::CidrMatch);
REGISTER_SAFE_SCRIPTFUNCTION(len, &ScriptUtils::Len);
REGISTER_SAFE_SCRIPTFUNCTION(union, &ScriptUtils::Union);
REGISTER_SAFE_SCRIPTFUNCTION(intersection, &ScriptUtils::Intersection);
REGISTER_SCRIPTFUNCTION(log, &ScriptUtils::Log);
REGISTER_SCRIPTFUNCTION(range, &ScriptUtils::Range);
REGISTER_SCRIPTFUNCTION(exit, &Application::Exit);
REGISTER_SAFE_SCRIPTFUNCTION(typeof, &ScriptUtils::TypeOf);
REGISTER_SAFE_SCRIPTFUNCTION(keys, &ScriptUtils::Keys);
REGISTER_SAFE_SCRIPTFUNCTION(random, &Utility::Random);
REGISTER_SAFE_SCRIPTFUNCTION(get_object, &ScriptUtils::GetObject);
REGISTER_SAFE_SCRIPTFUNCTION(get_objects, &ScriptUtils::GetObjects);
REGISTER_SCRIPTFUNCTION(assert, &ScriptUtils::Assert);
REGISTER_SAFE_SCRIPTFUNCTION(string, &ScriptUtils::CastString);
REGISTER_SAFE_SCRIPTFUNCTION(number, &ScriptUtils::CastNumber);
REGISTER_SAFE_SCRIPTFUNCTION(bool, &ScriptUtils::CastBool);
REGISTER_SAFE_SCRIPTFUNCTION(get_time, &Utility::GetTime);
REGISTER_SAFE_SCRIPTFUNCTION(basename, &Utility::BaseName);
REGISTER_SAFE_SCRIPTFUNCTION(dirname, &Utility::DirName);
REGISTER_SAFE_SCRIPTFUNCTION(msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim);
REGISTER_SAFE_SCRIPTFUNCTION(track_parents, &ScriptUtils::TrackParents);
REGISTER_SAFE_SCRIPTFUNCTION(escape_shell_cmd, &Utility::EscapeShellCmd);
REGISTER_SAFE_SCRIPTFUNCTION(escape_shell_arg, &Utility::EscapeShellArg);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &Utility::Match);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &Utility::CidrMatch);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, len, &ScriptUtils::Len);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, union, &ScriptUtils::Union);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, intersection, &ScriptUtils::Intersection);
REGISTER_SCRIPTFUNCTION_NS(System, log, &ScriptUtils::Log);
REGISTER_SCRIPTFUNCTION_NS(System, range, &ScriptUtils::Range);
REGISTER_SCRIPTFUNCTION_NS(System, exit, &Application::Exit);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, typeof, &ScriptUtils::TypeOf);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, keys, &ScriptUtils::Keys);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, random, &Utility::Random);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, get_object, &ScriptUtils::GetObject);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, get_objects, &ScriptUtils::GetObjects);
REGISTER_SCRIPTFUNCTION_NS(System, assert, &ScriptUtils::Assert);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, string, &ScriptUtils::CastString);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, number, &ScriptUtils::CastNumber);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, bool, &ScriptUtils::CastBool);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, get_time, &Utility::GetTime);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, basename, &Utility::BaseName);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, dirname, &Utility::DirName);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, msi_get_component_path, &ScriptUtils::MsiGetComponentPathShim);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, track_parents, &ScriptUtils::TrackParents);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_shell_cmd, &Utility::EscapeShellCmd);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_shell_arg, &Utility::EscapeShellArg);
#ifdef _WIN32
REGISTER_SAFE_SCRIPTFUNCTION(escape_create_process_arg, &Utility::EscapeCreateProcessArg);
REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_create_process_arg, &Utility::EscapeCreateProcessArg);
#endif /* _WIN32 */
REGISTER_SCRIPTFUNCTION(ptr, &ScriptUtils::Ptr);
REGISTER_SCRIPTFUNCTION_NS(System, ptr, &ScriptUtils::Ptr);
String ScriptUtils::CastString(const Value& value)
{

View File

@ -180,6 +180,7 @@ this return T_THIS;
globals return T_GLOBALS;
locals return T_LOCALS;
use return T_USE;
__using return T_USING;
apply return T_APPLY;
to return T_TO;
where return T_WHERE;

View File

@ -150,6 +150,7 @@ static void MakeRBinaryOp(Expression** result, Expression *left, Expression *rig
%token T_CURRENT_LINE "current_line (T_CURRENT_LINE)"
%token T_DEBUGGER "debugger (T_DEBUGGER)"
%token T_USE "use (T_USE)"
%token T_USING "__using (T_USING)"
%token T_OBJECT "object (T_OBJECT)"
%token T_TEMPLATE "template (T_TEMPLATE)"
%token T_INCLUDE "include (T_INCLUDE)"
@ -596,6 +597,10 @@ lterm: T_LIBRARY rterm
{
$$ = new BreakpointExpression(@$);
}
| T_USING rterm
{
$$ = new UsingExpression($2, @$);
}
| apply
| object
| T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')'

View File

@ -23,6 +23,8 @@
#include "config/configcompiler.hpp"
#include "base/initialize.hpp"
#include "base/debug.hpp"
#include "base/exception.hpp"
#include "base/application.hpp"
#define REGISTER_CONFIG_FRAGMENT(id, name, fragment) \
namespace { \
@ -30,8 +32,13 @@
{ \
icinga::Expression *expression = icinga::ConfigCompiler::CompileText(name, fragment); \
VERIFY(expression); \
icinga::ScriptFrame frame; \
expression->Evaluate(frame); \
try { \
icinga::ScriptFrame frame; \
expression->Evaluate(frame); \
} catch (const std::exception& ex) { \
std::cerr << icinga::DiagnosticInformation(ex) << std::endl; \
icinga::Application::Exit(1); \
} \
delete expression; \
} \
\

View File

@ -46,7 +46,7 @@ ConfigItem::TypeMap ConfigItem::m_Items;
ConfigItem::ItemList ConfigItem::m_UnnamedItems;
ConfigItem::IgnoredItemList ConfigItem::m_IgnoredItems;
REGISTER_SCRIPTFUNCTION_NS(Internal, run_with_activation_context, &ConfigItem::RunWithActivationContext);
REGISTER_SCRIPTFUNCTION_NS_PREFIX(Internal, run_with_activation_context, &ConfigItem::RunWithActivationContext);
/**
* Constructor for the ConfigItem class.

View File

@ -124,6 +124,8 @@ ExpressionResult VariableExpression::DoEvaluate(ScriptFrame& frame, DebugHint *d
return value;
else if (frame.Self.IsObject() && frame.Locals != static_cast<Object::Ptr>(frame.Self) && static_cast<Object::Ptr>(frame.Self)->HasOwnField(m_Variable))
return VMOps::GetField(frame.Self, m_Variable, frame.Sandboxed, m_DebugInfo);
else if (VMOps::FindVarImport(frame, m_Variable, &value, m_DebugInfo))
return value;
else
return ScriptGlobal::Get(m_Variable);
}
@ -142,6 +144,8 @@ 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)) {
return true;
} else if (ScriptGlobal::Exists(m_Variable)) {
*parent = ScriptGlobal::GetGlobals();
@ -891,3 +895,25 @@ 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.IsObject())
BOOST_THROW_EXCEPTION(ScriptError("The parameter does not resolve to an object", m_DebugInfo));
if (!frame.Imports)
frame.Imports = new Array();
else
frame.Imports = static_pointer_cast<Array>(frame.Imports->Clone());
frame.Imports->Add(import);
return Empty;
}

View File

@ -953,6 +953,25 @@ protected:
virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
};
class I2_CONFIG_API UsingExpression : public DebuggableExpression
{
public:
UsingExpression(Expression *name, const DebugInfo& debugInfo = DebugInfo())
: DebuggableExpression(debugInfo), m_Name(name)
{ }
~UsingExpression(void)
{
delete m_Name;
}
protected:
virtual ExpressionResult DoEvaluate(ScriptFrame& frame, DebugHint *dhint) const override;
private:
Expression *m_Name;
};
}
#endif /* EXPRESSION_H */

View File

@ -44,15 +44,33 @@ namespace icinga
class VMOps
{
public:
static inline Value Variable(ScriptFrame& frame, const String& name, const DebugInfo& debugInfo = DebugInfo())
static inline bool FindVarImportRef(ScriptFrame& frame, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
{
Value value;
if (frame.Locals && frame.Locals->Get(name, &value))
return value;
else if (frame.Self.IsObject() && frame.Locals != static_cast<Object::Ptr>(frame.Self) && static_cast<Object::Ptr>(frame.Self)->HasOwnField(name))
return GetField(frame.Self, name, frame.Sandboxed, debugInfo);
else
return ScriptGlobal::Get(name);
if (!frame.Imports)
return false;
ObjectLock olock(frame.Imports);
BOOST_FOREACH(const Value& import, frame.Imports) {
Object::Ptr obj = import;
if (obj->HasOwnField(name)) {
*result = import;
return true;
}
}
return false;
}
static inline bool FindVarImport(ScriptFrame& frame, const String& name, Value *result, const DebugInfo& debugInfo = DebugInfo())
{
Value parent;
if (FindVarImportRef(frame, name, &parent, debugInfo)) {
*result = GetField(parent, name, frame.Sandboxed, debugInfo);
return true;
}
return false;
}
static inline Value ConstructorCall(const Type::Ptr& type, const std::vector<Value>& args, const DebugInfo& debugInfo = DebugInfo())

View File

@ -29,16 +29,16 @@
using namespace icinga;
REGISTER_SCRIPTFUNCTION(get_host, &Host::GetByName);
REGISTER_SCRIPTFUNCTION(get_service, &ObjectUtils::GetService);
REGISTER_SCRIPTFUNCTION(get_user, &User::GetByName);
REGISTER_SCRIPTFUNCTION(get_check_command, &CheckCommand::GetByName);
REGISTER_SCRIPTFUNCTION(get_event_command, &EventCommand::GetByName);
REGISTER_SCRIPTFUNCTION(get_notification_command, &NotificationCommand::GetByName);
REGISTER_SCRIPTFUNCTION(get_host_group, &HostGroup::GetByName);
REGISTER_SCRIPTFUNCTION(get_service_group, &ServiceGroup::GetByName);
REGISTER_SCRIPTFUNCTION(get_user_group, &UserGroup::GetByName);
REGISTER_SCRIPTFUNCTION(get_time_period, &TimePeriod::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_host, &Host::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_service, &ObjectUtils::GetService);
REGISTER_SCRIPTFUNCTION_NS(System, get_user, &User::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_check_command, &CheckCommand::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_event_command, &EventCommand::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_notification_command, &NotificationCommand::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_host_group, &HostGroup::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_service_group, &ServiceGroup::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_user_group, &UserGroup::GetByName);
REGISTER_SCRIPTFUNCTION_NS(System, get_time_period, &TimePeriod::GetByName);
Service::Ptr ObjectUtils::GetService(const String& host, const String& name)
{

View File

@ -30,7 +30,7 @@
using namespace icinga;
REGISTER_TYPE(PerfdataValue);
REGISTER_SCRIPTFUNCTION(parse_performance_data, PerfdataValue::Parse);
REGISTER_SCRIPTFUNCTION_NS(System, parse_performance_data, PerfdataValue::Parse);
PerfdataValue::PerfdataValue(void)
{ }

View File

@ -215,6 +215,45 @@ static void AddSuggestion(std::vector<String>& matches, const String& word, cons
matches.push_back(suggestion);
}
static void AddSuggestions(std::vector<String>& matches, const String& word, const String& pword, const Value& value)
{
String prefix;
if (!pword.IsEmpty())
prefix = pword + ".";
if (value.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = value;
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
AddSuggestion(matches, word, prefix + kv.first);
}
}
Type::Ptr type = value.GetReflectionType();
for (int i = 0; i < type->GetFieldCount(); i++) {
Field field = type->GetFieldInfo(i);
AddSuggestion(matches, word, prefix + field.Name);
}
while (type) {
Object::Ptr prototype = type->GetPrototype();
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(prototype);
if (dict) {
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
AddSuggestion(matches, word, prefix + kv.first);
}
}
type = type->GetBaseType();
}
}
std::vector<String> ConsoleHandler::GetAutocompletionSuggestions(const String& word, ScriptFrame& frame)
{
std::vector<String> matches;
@ -237,6 +276,13 @@ std::vector<String> ConsoleHandler::GetAutocompletionSuggestions(const String& w
}
}
if (frame.Imports) {
ObjectLock olock(frame.Imports);
BOOST_FOREACH(const Value& import, frame.Imports) {
AddSuggestions(matches, word, "", import);
}
}
String::SizeType cperiod = word.RFind(".");
if (cperiod != String::NPos) {
@ -250,36 +296,8 @@ std::vector<String> ConsoleHandler::GetAutocompletionSuggestions(const String& w
if (expr)
value = expr->Evaluate(frame);
if (value.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = value;
AddSuggestions(matches, word, pword, value);
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
AddSuggestion(matches, word, pword + "." + kv.first);
}
}
Type::Ptr type = value.GetReflectionType();
for (int i = 0; i < type->GetFieldCount(); i++) {
Field field = type->GetFieldInfo(i);
AddSuggestion(matches, word, pword + "." + field.Name);
}
while (type) {
Object::Ptr prototype = type->GetPrototype();
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(prototype);
if (dict) {
ObjectLock olock(dict);
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
AddSuggestion(matches, word, pword + "." + kv.first);
}
}
type = type->GetBaseType();
}
} catch (...) { /* Ignore the exception */ }
}