Add "-o" option to "expression" which prints the object description if available.

git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@115115 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp
index 1e2d023..0060a71 100644
--- a/source/Commands/CommandObjectExpression.cpp
+++ b/source/Commands/CommandObjectExpression.cpp
@@ -16,6 +16,7 @@
 #include "lldb/Interpreter/Args.h"
 #include "lldb/Core/Value.h"
 #include "lldb/Core/InputReader.h"
+#include "lldb/Core/ValueObjectVariable.h"
 #include "lldb/Expression/ClangExpressionVariable.h"
 #include "lldb/Expression/ClangUserExpression.h"
 #include "lldb/Expression/ClangFunction.h"
@@ -24,6 +25,7 @@
 #include "lldb/Core/Debugger.h"
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Symbol/Variable.h"
 #include "lldb/Target/Process.h"
@@ -69,6 +71,10 @@
     case 'f':
         error = Args::StringToFormat(option_arg, format);
         break;
+        
+    case 'o':
+        print_object = true;
+        break;
 
     default:
         error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option);
@@ -85,6 +91,7 @@
     //language.Clear();
     debug = false;
     format = eFormatDefault;
+    print_object = false;
     show_types = true;
     show_summary = true;
 }
@@ -230,16 +237,36 @@
     {
         StreamString ss;
         
-        Error rc = expr_result->Print (ss, 
-                                       m_exe_ctx, 
-                                       m_options.format,
-                                       m_options.show_types,
-                                       m_options.show_summary,
-                                       m_options.debug);
-        
-        if (rc.Fail()) {
-            error_stream.Printf ("Couldn't print result : %s\n", rc.AsCString());
-            return false;
+        if (m_options.print_object)
+        {
+            Value result_value;
+            if (expr_result->PointValueAtData(result_value, &m_exe_ctx))
+            {                
+                bool obj_result;
+                ObjCLanguageRuntime *runtime = m_exe_ctx.process->GetObjCLanguageRuntime();
+                obj_result = runtime->GetObjectDescription (ss, result_value, m_exe_ctx.GetBestExecutionContextScope());
+                if (!obj_result)
+                {
+                    error_stream.Printf ("Could not get object description: %s.\n", ss.GetData());
+                    return false;
+                }
+                // Sometimes the description doesn't have a newline on the end.  For now, I'll just add one here, if
+                ss.Printf("\n");
+            }
+        }
+        else
+        {
+            Error rc = expr_result->Print (ss, 
+                                           m_exe_ctx, 
+                                           m_options.format,
+                                           m_options.show_types,
+                                           m_options.show_summary,
+                                           m_options.debug);
+            
+            if (rc.Fail()) {
+                error_stream.Printf ("Couldn't print result : %s\n", rc.AsCString());
+                return false;
+            }
         }
 
         output_stream.PutCString(ss.GetString().c_str());
@@ -345,7 +372,8 @@
 CommandObjectExpression::CommandOptions::g_option_table[] =
 {
   //{ LLDB_OPT_SET_ALL, false, "language",   'l', required_argument, NULL, 0, "[c|c++|objc|objc++]",          "Sets the language to use when parsing the expression."},
-{ LLDB_OPT_SET_ALL, false, "format",     'f', required_argument, NULL, 0, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]",  "Specify the format that the expression output should use."},
+{ LLDB_OPT_SET_1, false, "format",     'f', required_argument, NULL, 0, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]",  "Specify the format that the expression output should use."},
+{ LLDB_OPT_SET_2, false, "object-description",     'o', no_argument,       NULL, 0, NULL,                           "Print the object description of the value resulting from the expression"},
 { LLDB_OPT_SET_ALL, false, "debug",      'g', no_argument,       NULL, 0, NULL,                           "Enable verbose debug logging of the expression parsing and evaluation."},
 { LLDB_OPT_SET_ALL, false, "use-ir",     'i', no_argument,       NULL, 0, NULL,                           "[Temporary] Instructs the expression evaluator to use IR instead of ASTs."},
 { 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL }
diff --git a/source/Commands/CommandObjectExpression.h b/source/Commands/CommandObjectExpression.h
index 4aed304..3229a42 100644
--- a/source/Commands/CommandObjectExpression.h
+++ b/source/Commands/CommandObjectExpression.h
@@ -50,6 +50,7 @@
         lldb::Encoding  encoding;
         lldb::Format    format;
         bool        debug;
+        bool        print_object;
         bool        show_types;
         bool        show_summary;
     };
diff --git a/source/Expression/ClangExpressionVariable.cpp b/source/Expression/ClangExpressionVariable.cpp
index 37fc6c2..f406583 100644
--- a/source/Expression/ClangExpressionVariable.cpp
+++ b/source/Expression/ClangExpressionVariable.cpp
@@ -50,23 +50,14 @@
 {
     Error err;
     
-    if (!m_data_vars.get() || !m_data_vars->m_data)
+    Value val;
+    if (!PointValueAtData (val, &exe_ctx))
     {
         err.SetErrorToGenericError();
         err.SetErrorStringWithFormat("Variable doesn't contain a value");
         return err;
     }
     
-    Value val;
-    
-    clang::ASTContext *ast_context = m_user_type.GetASTContext();
-    
-    val.SetContext (Value::eContextTypeOpaqueClangQualType, m_user_type.GetOpaqueQualType ());
-    val.SetValueType (Value::eValueTypeHostAddress);
-    val.GetScalar() = (uint64_t)m_data_vars->m_data->GetBytes ();
-    
-    val.ResolveValue (&exe_ctx, ast_context);
-    
     if (val.GetContextType () == Value::eContextTypeInvalid &&
         val.GetValueType () == Value::eValueTypeScalar &&
         format == lldb::eFormatDefault)
@@ -77,6 +68,8 @@
         return err;
     }
     
+    clang::ASTContext *ast_context = m_user_type.GetASTContext();
+
     // The expression result is more complex and requires special handling
     DataExtractor data;
     Error expr_error = val.GetValueAsData (&exe_ctx, ast_context, data, 0);
@@ -165,14 +158,18 @@
 }
 
 bool
-ClangExpressionVariable::PointValueAtData(Value &value)
+ClangExpressionVariable::PointValueAtData(Value &value, ExecutionContext *exe_ctx)
 {
-    if (!m_data_vars.get())
+    if (!m_data_vars.get() || !m_data_vars->m_data)
         return false;
     
     value.SetContext(Value::eContextTypeOpaqueClangQualType, m_user_type.GetOpaqueQualType());
     value.SetValueType(Value::eValueTypeHostAddress);
     value.GetScalar() = (uint64_t)m_data_vars->m_data->GetBytes();
+    clang::ASTContext *ast_context = m_user_type.GetASTContext();
+
+    if (exe_ctx)
+        value.ResolveValue (exe_ctx, ast_context);
     
     return true;
 }
diff --git a/source/Expression/ClangFunction.cpp b/source/Expression/ClangFunction.cpp
index aaff16d..7f8d3ae 100644
--- a/source/Expression/ClangFunction.cpp
+++ b/source/Expression/ClangFunction.cpp
@@ -667,7 +667,8 @@
     // Thread we ran the function in may have gone away because we ran the target
     // Check that it's still there.
     exe_ctx.thread = exe_ctx.process->GetThreadList().FindThreadByIndexID(tid, true).get();
-    exe_ctx.frame = exe_ctx.thread->GetStackFrameAtIndex(0).get();
+    if (exe_ctx.thread)
+        exe_ctx.frame = exe_ctx.thread->GetStackFrameAtIndex(0).get();
     
     // Also restore the current process'es selected frame & thread, since this function calling may
     // be done behind the user's back.
diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.cpp b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.cpp
index 346dc54..e4acd96 100644
--- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.cpp
+++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.cpp
@@ -10,6 +10,8 @@
 #include "AppleObjCRuntimeV2.h"
 #include "AppleObjCTrampolineHandler.h"
 
+#include "clang/AST/Type.h"
+
 #include "lldb/Core/ConstString.h"
 #include "lldb/Core/Error.h"
 #include "lldb/Core/Log.h"
@@ -38,27 +40,10 @@
 AppleObjCRuntimeV2::GetObjectDescription (Stream &str, ValueObject &object, ExecutionContextScope *exe_scope)
 {
 
-    if (!m_read_objc_library)
-        return false;
-        
-    ExecutionContext exe_ctx;
-    exe_scope->Calculate(exe_ctx);
-    
-    if (!exe_ctx.process)
-        return false;
-    
-    // We need other parts of the exe_ctx, but the processes have to match.
-    assert (m_process == exe_ctx.process);
-    
     // ObjC objects can only be pointers:
     if (!ClangASTContext::IsPointerType (object.GetClangType()))
         return NULL;
     
-    // Get the function address for the print function.
-    const Address *function_address = GetPrintForDebuggerAddr();
-    if (!function_address)
-        return false;
-    
     // Make the argument list: we pass one arg, the address of our pointer, to the print function.
     Scalar scalar;
     
@@ -70,12 +55,60 @@
                                         scalar))
         return NULL;
                         
-    Value val(scalar);
-    val.SetContext(Value::eContextTypeOpaqueClangQualType, 
-                   ClangASTContext::GetVoidPtrType(object.GetClangAST(), false));
+    Value val(scalar);                   
+    return GetObjectDescription(str, val, exe_scope);
                    
+}
+bool
+AppleObjCRuntimeV2::GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope)
+{
+    if (!m_read_objc_library)
+        return false;
+        
+    ExecutionContext exe_ctx;
+    exe_scope->Calculate(exe_ctx);
+    
+    if (!exe_ctx.process)
+        return false;
+    
+    // We need other parts of the exe_ctx, but the processes have to match.
+    assert (m_process == exe_ctx.process);
+    
+    // Get the function address for the print function.
+    const Address *function_address = GetPrintForDebuggerAddr();
+    if (!function_address)
+        return false;
+    
+    if (value.GetClangType())
+    {
+        clang::QualType value_type = clang::QualType::getFromOpaquePtr (value.GetClangType());
+        if (!value_type->isObjCObjectPointerType())
+        {
+            str.Printf ("Value doesn't point to an ObjC object.\n");
+            return false;
+        }
+        // FIXME: If we use the real types here then we end up crashing in the expression parser.
+        // For now, forcing this to be a generic pointer makes it work...
+#if 1
+        ClangASTContext *ast_context = exe_ctx.target->GetScratchClangASTContext();
+        if (value.GetContextType() == Value::eContextTypeOpaqueClangQualType)
+        {
+            value.SetContext(Value::eContextTypeOpaqueClangQualType, ast_context->GetVoidPtrType(false));
+        }
+#endif
+    }
+    else 
+    {
+        // If it is not a pointer, see if we can make it into a pointer.
+        ClangASTContext *ast_context = exe_ctx.target->GetScratchClangASTContext();
+        void *opaque_type_ptr = ast_context->GetBuiltInType_objc_id();
+        if (opaque_type_ptr == NULL)
+            opaque_type_ptr = ast_context->GetVoidPtrType(false);
+        value.SetContext(Value::eContextTypeOpaqueClangQualType, opaque_type_ptr);    
+    }
+
     ValueList arg_value_list;
-    arg_value_list.PushValue(val);
+    arg_value_list.PushValue(value);
     
     // This is the return value:
     const char *target_triple = exe_ctx.process->GetTargetTriple().GetCString();
diff --git a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.h b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.h
index 17c94bc..fcd1ec2 100644
--- a/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.h
+++ b/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntimeV2/AppleObjCRuntimeV2.h
@@ -31,6 +31,9 @@
     
     // These are generic runtime functions:
     virtual bool
+    GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope);
+    
+    virtual bool
     GetObjectDescription (Stream &str, ValueObject &object, ExecutionContextScope *exe_scope);
     
     virtual lldb::ValueObjectSP
diff --git a/source/Target/CPPLanguageRuntime.cpp b/source/Target/CPPLanguageRuntime.cpp
index d694b77..d0fc7af 100644
--- a/source/Target/CPPLanguageRuntime.cpp
+++ b/source/Target/CPPLanguageRuntime.cpp
@@ -33,3 +33,10 @@
     // C++ has no generic way to do this.
     return false;
 }
+
+bool
+CPPLanguageRuntime::GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope)
+{
+    // C++ has no generic way to do this.
+    return false;
+}
diff --git a/source/Target/ObjCLanguageRuntime.cpp b/source/Target/ObjCLanguageRuntime.cpp
index 0a13633..7f4de24 100644
--- a/source/Target/ObjCLanguageRuntime.cpp
+++ b/source/Target/ObjCLanguageRuntime.cpp
@@ -6,9 +6,12 @@
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
+#include "clang/AST/Type.h"
 
 #include "lldb/Core/Log.h"
 #include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Target/ObjCLanguageRuntime.h"
 
 using namespace lldb;