]> granicus.if.org Git - icinga2/commitdiff
Implement support for namespaces
authorGunnar Beutner <gunnar.beutner@netways.de>
Fri, 12 Aug 2016 09:25:36 +0000 (11:25 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Fri, 12 Aug 2016 09:32:16 +0000 (11:32 +0200)
fixes #12408

15 files changed:
lib/base/configwriter.cpp
lib/base/function.hpp
lib/base/scriptframe.cpp
lib/base/scriptframe.hpp
lib/base/scriptutils.cpp
lib/config/config_lexer.ll
lib/config/config_parser.yy
lib/config/configfragment.hpp
lib/config/configitem.cpp
lib/config/expression.cpp
lib/config/expression.hpp
lib/config/vmops.hpp
lib/icinga/objectutils.cpp
lib/icinga/perfdatavalue.cpp
lib/remote/consolehandler.cpp

index 33be46e278f7a3a0a0e3ee88c182384f4737f56a..4a7f5d6e4feba56e6f40d8eb71e85b63f7228d72 100644 (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");
index 3d76f173ec26cdadb5d53df179c682b37a856ad0..82d8b152a0efd059f3207fe333f9efaed3475025 100644 (file)
@@ -66,22 +66,22 @@ private:
        Callback m_Callback;
 };
 
-#define REGISTER_SCRIPTFUNCTION(name, callback) \
-       namespace { namespace UNIQUE_NAME(sf) { namespace sf ## name { \
+#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(#name, WrapFunction(callback)); \
-                       ScriptGlobal::Set(#name, sf); \
+                       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(ns, name, callback) \
+#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 { \
+#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(#name, WrapFunction(callback), true); \
-                       ScriptGlobal::Set(#name, sf); \
+                       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(ns, name, callback) \
+#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); \
        } } }
index 3526c08d465e0541a3c9e4d0d32c53a145aba0ba..8c5c7b14e1ce0d860a19abbc88dc8185fa8092ad 100644 (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);
 }
index edb4413a69a2cc986a249d78fe9077d059776083..c0ceb3829a429f06abb23bf35d0f2ea2ea8dca0b 100644 (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;
index 36f73d2700e1de4899c708b384626cb88fa7a8f8..d27f6e59af89d5c1aa31b4ad502c7a1980f63e5a 100644 (file)
 
 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)
 {
index b52d8ebf505d572c572570625ea75cfc80eb7571..8cac9f15772db2ba48886d4200343867b033c722 100644 (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;
index baf9b241fa84959208dfbe559f3dd38654172ad9..e778bf1b21034b6eabe03f5cb95aa98ea3b922cc 100644 (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 ')'
index a528bd40480082854a4d9627dfdcab83221c8a20..ad3efba22826cd9089c1b22d94bbece21c833433 100644 (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 { \
                { \
                        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; \
                } \
                \
index fa1b9714ddb8900b767fd0d8cbe95c31b634d4ad..5266464cc0853e0883289101dc1e375ac0c5dffd 100644 (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.
index 2e52425fd3e6efa84eb78c1459f1d9ac0a60e610..11ffc9c089c597416923ccc46f76b05a637d57dd 100644 (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;
+}
+
index 226b1b90a712f184bad4f24d029536262312f5f6..7eb5986213ffba866fe7bd38862bb26b832cf1d8 100644 (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 */
index a23687217e67c29c135ad41b4140e296df5de17b..40d56170db95f4a5c0a1544ee335b7a77085ad89 100644 (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())
index aafe72f3113ff6804f69dc56ee371f4efd647061..7d88e6eeaee44878e36078da5c2da19a68aefde1 100644 (file)
 
 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)
 {
index 3983e804641960d835970c0bbfc70c041562aa26..5c361415fc56257c208e7391224b2c82a54124e2 100644 (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)
 { }
index 7e8c656773793bc3a74b100044c05cedc5ccabd6..3fb41edfc34f3979fd260eeccd2aaa813d4ce6e5 100644 (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;
-
-                               ObjectLock olock(dict);
-                               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
-                                       AddSuggestion(matches, word, pword + "." + kv.first);
-                               }
-                       }
+                       AddSuggestions(matches, word, pword, value);
 
-                       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 */ }
        }