Snapshot idea/138.1696 from git://git.jetbrains.org/idea/community.git

Change-Id: I50c97b83a815ce635e49a38380ba5b8765e4b16a
diff --git a/python/helpers/pydev/pydevd_vars.py b/python/helpers/pydev/pydevd_vars.py
index de8c241..0cc45f7 100644
--- a/python/helpers/pydev/pydevd_vars.py
+++ b/python/helpers/pydev/pydevd_vars.py
@@ -3,7 +3,6 @@
 """
 import pickle
 from django_frame import DjangoTemplateFrame
-from pydevd_constants import * #@UnusedWildImport
 from types import * #@UnusedWildImport
 
 from pydevd_custom_frames import getCustomFrame
@@ -19,16 +18,15 @@
     import _pydev_threading as threading
 else:
     import threading
-import pydevd_resolver
 import traceback
 import pydevd_save_locals
-from pydev_imports import Exec, quote, execfile
+from pydev_imports import Exec, execfile
 
 try:
     import types
     frame_type = types.FrameType
 except:
-    frame_type = None
+    frame_type = type(sys._getframe())
 
 
 #-------------------------------------------------------------------------- defining true and false for earlier versions
@@ -37,25 +35,14 @@
     __setFalse = False
 except:
     import __builtin__
-
     setattr(__builtin__, 'True', 1)
     setattr(__builtin__, 'False', 0)
 
 #------------------------------------------------------------------------------------------------------ class for errors
 
-class VariableError(RuntimeError): pass
+class VariableError(RuntimeError):pass
 
-class FrameNotFoundError(RuntimeError): pass
-
-
-if USE_PSYCO_OPTIMIZATION:
-    try:
-        import psyco
-
-        varToXML = psyco.proxy(varToXML)
-    except ImportError:
-        if hasattr(sys, 'exc_clear'): #jython does not have it
-            sys.exc_clear() #don't keep the traceback -- clients don't want to see it
+class FrameNotFoundError(RuntimeError):pass
 
 def iterFrames(initialFrame):
     '''NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)'''
@@ -166,50 +153,127 @@
         traceback.print_exc()
         return None
 
-def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
-    """ returns the value of the compound variable as a dictionary"""
+def getVariable(thread_id, frame_id, scope, attrs):
+    """
+    returns the value of a variable
+
+    :scope: can be BY_ID, EXPRESSION, GLOBAL, LOCAL, FRAME
+
+    BY_ID means we'll traverse the list of all objects alive to get the object.
+
+    :attrs: after reaching the proper scope, we have to get the attributes until we find
+            the proper location (i.e.: obj\tattr1\tattr2)
+
+    :note: when BY_ID is used, the frame_id is considered the id of the object to find and
+           not the frame (as we don't care about the frame in this case).
+    """
+    if scope == 'BY_ID':
+        if thread_id != GetThreadId(threading.currentThread()) :
+            raise VariableError("getVariable: must execute on same thread")
+
+        try:
+            import gc
+            objects = gc.get_objects()
+        except:
+            pass  #Not all python variants have it.
+        else:
+            frame_id = int(frame_id)
+            for var in objects:
+                if id(var) == frame_id:
+                    if attrs is not None:
+                        attrList = attrs.split('\t')
+                        for k in attrList:
+                            _type, _typeName, resolver = getType(var)
+                            var = resolver.resolve(var, k)
+
+                    return var
+
+        #If it didn't return previously, we coudn't find it by id (i.e.: alrceady garbage collected).
+        sys.stderr.write('Unable to find object with id: %s\n' % (frame_id,))
+        return None
+
     frame = findFrame(thread_id, frame_id)
     if frame is None:
         return {}
 
-    attrList = attrs.split('\t')
-    
-    if scope == "GLOBAL":
-        var = frame.f_globals
-        del attrList[0] # globals are special, and they get a single dummy unused attribute
+    if attrs is not None:
+        attrList = attrs.split('\t')
     else:
-        var = frame.f_locals
-        type, _typeName, resolver = getType(var)
-        try:
-            resolver.resolve(var, attrList[0])
-        except:
-            var = frame.f_globals
+        attrList = []
 
-    for k in attrList:
-        type, _typeName, resolver = getType(var)
-        var = resolver.resolve(var, k)
+    if scope == 'EXPRESSION':
+        for count in xrange(len(attrList)):
+            if count == 0:
+                # An Expression can be in any scope (globals/locals), therefore it needs to evaluated as an expression
+                var = evaluateExpression(thread_id, frame_id, attrList[count], False)
+            else:
+                _type, _typeName, resolver = getType(var)
+                var = resolver.resolve(var, attrList[count])
+    else:
+        if scope == "GLOBAL":
+            var = frame.f_globals
+            del attrList[0]  # globals are special, and they get a single dummy unused attribute
+        else:
+            var = frame.f_locals
+
+        for k in attrList:
+            _type, _typeName, resolver = getType(var)
+            var = resolver.resolve(var, k)
+
+    return var
+
+
+def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
+    """ returns the value of the compound variable as a dictionary"""
+
+    var = getVariable(thread_id, frame_id, scope, attrs)
 
     try:
-        type, _typeName, resolver = getType(var)
+        _type, _typeName, resolver = getType(var)
         return resolver.getDictionary(var)
     except:
+        sys.stderr.write('Error evaluating: thread_id: %s\nframe_id: %s\nscope: %s\nattrs: %s\n' % (
+            thread_id, frame_id, scope, attrs,))
         traceback.print_exc()
-        
-        
+
+
 def resolveVar(var, attrs):
     attrList = attrs.split('\t')
-    
+
     for k in attrList:
         type, _typeName, resolver = getType(var)
-        
+
         var = resolver.resolve(var, k)
-    
+
     try:
         type, _typeName, resolver = getType(var)
         return resolver.getDictionary(var)
     except:
         traceback.print_exc()
-    
+
+
+def customOperation(thread_id, frame_id, scope, attrs, style, code_or_file, operation_fn_name):
+    """
+    We'll execute the code_or_file and then search in the namespace the operation_fn_name to execute with the given var.
+
+    code_or_file: either some code (i.e.: from pprint import pprint) or a file to be executed.
+    operation_fn_name: the name of the operation to execute after the exec (i.e.: pprint)
+    """
+    expressionValue = getVariable(thread_id, frame_id, scope, attrs)
+
+    try:
+        namespace = {'__name__': '<customOperation>'}
+        if style == "EXECFILE":
+            namespace['__file__'] = code_or_file
+            execfile(code_or_file, namespace, namespace)
+        else:  # style == EXEC
+            namespace['__file__'] = '<customOperationCode>'
+            Exec(code_or_file, namespace, namespace)
+
+        return str(namespace[operation_fn_name](expressionValue))
+    except:
+        traceback.print_exc()
+
 
 def evaluateExpression(thread_id, frame_id, expression, doExec):
     '''returns the result of the evaluated expression
@@ -230,6 +294,7 @@
     updated_globals.update(frame.f_locals)  #locals later because it has precedence over the actual globals
 
     try:
+
         if doExec:
             try:
                 #try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
@@ -240,7 +305,7 @@
                 pydevd_save_locals.save_locals(frame)
             else:
                 result = eval(compiled, updated_globals, frame.f_locals)
-                if result is not None: #Only print if it's not None (as python does)
+                if result is not None:  #Only print if it's not None (as python does)
                     sys.stdout.write('%s\n' % (result,))
             return
 
@@ -251,7 +316,6 @@
             except Exception:
                 s = StringIO()
                 traceback.print_exc(file=s)
-
                 result = s.getvalue()
 
                 try:
@@ -265,6 +329,22 @@
 
                 result = ExceptionOnEvaluate(result)
 
+                # Ok, we have the initial error message, but let's see if we're dealing with a name mangling error...
+                try:
+                    if '__' in expression:
+                        # Try to handle '__' name mangling...
+                        split = expression.split('.')
+                        curr = frame.f_locals.get(split[0])
+                        for entry in split[1:]:
+                            if entry.startswith('__') and not hasattr(curr, entry):
+                                entry = '_%s%s' % (curr.__class__.__name__, entry)
+                            curr = getattr(curr, entry)
+
+                        result = curr
+                except:
+                    pass
+
+
             return result
     finally:
         #Should not be kept alive if an exception happens and this frame is kept in the stack.
@@ -273,22 +353,18 @@
 
 def changeAttrExpression(thread_id, frame_id, attr, expression):
     '''Changes some attribute in a given frame.
-    @note: it will not (currently) work if we're not in the topmost frame (that's a python
-    deficiency -- and it appears that there is no way of making it currently work --
-    will probably need some change to the python internals)
     '''
     frame = findFrame(thread_id, frame_id)
     if frame is None:
         return
 
-    if isinstance(frame, DjangoTemplateFrame):
-        result = eval(expression, frame.f_globals, frame.f_locals)
-        frame.changeVariable(attr, result)
-
     try:
         expression = expression.replace('@LINE@', '\n')
 
-
+        if isinstance(frame, DjangoTemplateFrame):
+            result = eval(expression, frame.f_globals, frame.f_locals)
+            frame.changeVariable(attr, result)
+            return
 
         if attr[:7] == "Globals":
             attr = attr[8:]