summaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
authorStefan Gränitz <stefan.graenitz@gmail.com>2023-05-17 10:40:40 +0200
committerStefan Gränitz <stefan.graenitz@gmail.com>2023-05-17 13:56:01 +0200
commit0b6264738f3d3688719b23c5d272725d3d9bf4e0 (patch)
tree5a12af1dfb30f8d2a9e9e45e15b64519a05b7ed1 /lldb
parent77589e945f0d167bd46ed3218b81c16af1e917ae (diff)
downloadllvm-0b6264738f3d3688719b23c5d272725d3d9bf4e0.tar.gz
[lldb][gnustep] Add minimal GNUstepObjCRuntime plugin for LanguageTypeObjC on non-Apple platforms
This is the next patch after D146058. We can now parse expressions to print instance variables from ObjC classes. Until now the expression parser would bail out with an error like this: ``` error: expression failed to parse: error: Error [IRForTarget]: Couldn't find Objective-C indirect ivar symbol OBJC_IVAR_$_TestObj._int ``` Reviewed By: aprantl Differential Revision: https://reviews.llvm.org/D146154
Diffstat (limited to 'lldb')
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp13
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt2
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt19
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp204
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h111
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp16
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h3
-rw-r--r--lldb/test/Shell/Expr/objc-gnustep-print.m61
8 files changed, 417 insertions, 12 deletions
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 79666ffadb08..8d8af21e1994 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -587,12 +587,19 @@ ClangExpressionParser::ClangExpressionParser(
if (process_sp && lang_opts.ObjC) {
if (auto *runtime = ObjCLanguageRuntime::Get(*process_sp)) {
- if (runtime->GetRuntimeVersion() ==
- ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2)
+ switch (runtime->GetRuntimeVersion()) {
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V2:
lang_opts.ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7));
- else
+ break;
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eObjC_VersionUnknown:
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eAppleObjC_V1:
lang_opts.ObjCRuntime.set(ObjCRuntime::FragileMacOSX,
VersionTuple(10, 7));
+ break;
+ case ObjCLanguageRuntime::ObjCRuntimeVersions::eGNUstep_libobjc2:
+ lang_opts.ObjCRuntime.set(ObjCRuntime::GNUstep, VersionTuple(2, 0));
+ break;
+ }
if (runtime->HasNewLiteralsAndIndexing())
lang_opts.DebuggerObjCLiteral = true;
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
index d6de9dcc31bc..e8b9415241c8 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/CMakeLists.txt
@@ -7,4 +7,6 @@ add_lldb_library(lldbPluginObjCRuntime
lldbTarget
lldbUtility
)
+
add_subdirectory(AppleObjCRuntime)
+add_subdirectory(GNUstepObjCRuntime)
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt
new file mode 100644
index 000000000000..1da3ac58ec9f
--- /dev/null
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_lldb_library(lldbPluginGNUstepObjCRuntime PLUGIN
+ GNUstepObjCRuntime.cpp
+
+ LINK_LIBS
+ lldbBreakpoint
+ lldbCore
+ lldbExpression
+ lldbHost
+ lldbInterpreter
+ lldbSymbol
+ lldbTarget
+ lldbUtility
+ lldbPluginExpressionParserClang
+ lldbPluginTypeSystemClang
+ CLANG_LIBS
+ clangAST
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
new file mode 100644
index 000000000000..fb2656ef1385
--- /dev/null
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
@@ -0,0 +1,204 @@
+//===-- GNUstepObjCRuntime.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "GNUstepObjCRuntime.h"
+
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/UtilityFunction.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/ConstString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(GNUstepObjCRuntime)
+
+char GNUstepObjCRuntime::ID = 0;
+
+void GNUstepObjCRuntime::Initialize() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), "GNUstep Objective-C Language Runtime - libobjc2",
+ CreateInstance);
+}
+
+void GNUstepObjCRuntime::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+LanguageRuntime *GNUstepObjCRuntime::CreateInstance(Process *process,
+ LanguageType language) {
+ if (language != eLanguageTypeObjC)
+ return nullptr;
+ if (!process)
+ return nullptr;
+
+ Target &target = process->GetTarget();
+ const llvm::Triple &TT = target.GetArchitecture().GetTriple();
+ if (TT.getVendor() == llvm::Triple::VendorType::Apple)
+ return nullptr;
+
+ const ModuleList &images = target.GetImages();
+ if (TT.isOSBinFormatELF()) {
+ SymbolContextList eh_pers;
+ RegularExpression regex("__gnustep_objc[x]*_personality_v[0-9]+");
+ images.FindSymbolsMatchingRegExAndType(regex, eSymbolTypeCode, eh_pers);
+ if (eh_pers.GetSize() == 0)
+ return nullptr;
+ } else if (TT.isOSWindows()) {
+ SymbolContextList objc_mandatory;
+ images.FindSymbolsWithNameAndType(ConstString("__objc_load"),
+ eSymbolTypeCode, objc_mandatory);
+ if (objc_mandatory.GetSize() == 0)
+ return nullptr;
+ }
+
+ return new GNUstepObjCRuntime(process);
+}
+
+GNUstepObjCRuntime::~GNUstepObjCRuntime() = default;
+
+GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process)
+ : ObjCLanguageRuntime(process), m_objc_module_sp(nullptr) {
+ ReadObjCLibraryIfNeeded(process->GetTarget().GetImages());
+}
+
+bool GNUstepObjCRuntime::GetObjectDescription(Stream &str,
+ ValueObject &valobj) {
+ // TODO: ObjC has a generic way to do this
+ return false;
+}
+bool GNUstepObjCRuntime::GetObjectDescription(
+ Stream &strm, Value &value, ExecutionContextScope *exe_scope) {
+ // TODO: ObjC has a generic way to do this
+ return false;
+}
+
+bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
+ static constexpr bool check_cxx = false;
+ static constexpr bool check_objc = true;
+ return in_value.GetCompilerType().IsPossibleDynamicType(nullptr, check_cxx,
+ check_objc);
+}
+
+bool GNUstepObjCRuntime::GetDynamicTypeAndAddress(
+ ValueObject &in_value, DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name, Address &address,
+ Value::ValueType &value_type) {
+ return false;
+}
+
+TypeAndOrName
+GNUstepObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) {
+ CompilerType static_type(static_value.GetCompilerType());
+ Flags static_type_flags(static_type.GetTypeInfo());
+
+ TypeAndOrName ret(type_and_or_name);
+ if (type_and_or_name.HasType()) {
+ // The type will always be the type of the dynamic object. If our parent's
+ // type was a pointer, then our type should be a pointer to the type of the
+ // dynamic object. If a reference, then the original type should be
+ // okay...
+ CompilerType orig_type = type_and_or_name.GetCompilerType();
+ CompilerType corrected_type = orig_type;
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_type = orig_type.GetPointerType();
+ ret.SetCompilerType(corrected_type);
+ } else {
+ // If we are here we need to adjust our dynamic type name to include the
+ // correct & or * symbol
+ std::string corrected_name(type_and_or_name.GetName().GetCString());
+ if (static_type_flags.AllSet(eTypeIsPointer))
+ corrected_name.append(" *");
+ // the parent type should be a correctly pointer'ed or referenc'ed type
+ ret.SetCompilerType(static_type);
+ ret.SetName(corrected_name.c_str());
+ }
+ return ret;
+}
+
+BreakpointResolverSP
+GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt,
+ bool catch_bp, bool throw_bp) {
+ BreakpointResolverSP resolver_sp;
+
+ if (throw_bp)
+ resolver_sp = std::make_shared<BreakpointResolverName>(
+ bkpt, "objc_exception_throw", eFunctionNameTypeBase,
+ eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo);
+
+ return resolver_sp;
+}
+
+llvm::Expected<std::unique_ptr<UtilityFunction>>
+GNUstepObjCRuntime::CreateObjectChecker(std::string name,
+ ExecutionContext &exe_ctx) {
+ // TODO: This function is supposed to check whether an ObjC selector is
+ // present for an object. Might be implemented similar as in the Apple V2
+ // runtime.
+ const char *function_template = R"(
+ extern "C" void
+ %s(void *$__lldb_arg_obj, void *$__lldb_arg_selector) {}
+ )";
+
+ char empty_function_code[2048];
+ int len = ::snprintf(empty_function_code, sizeof(empty_function_code),
+ function_template, name.c_str());
+
+ assert(len < (int)sizeof(empty_function_code));
+ UNUSED_IF_ASSERT_DISABLED(len);
+
+ return GetTargetRef().CreateUtilityFunction(empty_function_code, name,
+ eLanguageTypeC, exe_ctx);
+}
+
+ThreadPlanSP
+GNUstepObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) {
+ // TODO: Implement this properly to avoid stepping into things like PLT stubs
+ return nullptr;
+}
+
+void GNUstepObjCRuntime::UpdateISAToDescriptorMapIfNeeded() {
+ // TODO: Support lazily named and dynamically loaded Objective-C classes
+}
+
+bool GNUstepObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) {
+ if (!module_sp)
+ return false;
+ const FileSpec &module_file_spec = module_sp->GetFileSpec();
+ if (!module_file_spec)
+ return false;
+ llvm::StringRef filename = module_file_spec.GetFilename().GetStringRef();
+ const llvm::Triple &TT = GetTargetRef().GetArchitecture().GetTriple();
+ if (TT.isOSBinFormatELF())
+ return filename.starts_with("libobjc.so");
+ if (TT.isOSWindows())
+ return filename == "objc.dll";
+ return false;
+}
+
+bool GNUstepObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) {
+ assert(m_objc_module_sp == nullptr && "Check HasReadObjCLibrary() first");
+ m_objc_module_sp = module_sp;
+
+ // Right now we don't use this, but we might want to check for debugger
+ // runtime support symbols like 'gdb_object_getClass' in the future.
+ return true;
+}
+
+void GNUstepObjCRuntime::ModulesDidLoad(const ModuleList &module_list) {
+ ReadObjCLibraryIfNeeded(module_list);
+}
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h
new file mode 100644
index 000000000000..b22088807f97
--- /dev/null
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h
@@ -0,0 +1,111 @@
+//===-- GNUstepObjCRuntime.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_GNUSTEPOBJCRUNTIME_GNUSTEPOBJCRUNTIME_H
+#define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_GNUSTEPOBJCRUNTIME_GNUSTEPOBJCRUNTIME_H
+
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/lldb-private.h"
+
+#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+#include <optional>
+
+namespace lldb_private {
+
+class GNUstepObjCRuntime : public lldb_private::ObjCLanguageRuntime {
+public:
+ ~GNUstepObjCRuntime() override;
+
+ //
+ // PluginManager, PluginInterface and LLVM RTTI implementation
+ //
+
+ static char ID;
+
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb_private::LanguageRuntime *
+ CreateInstance(Process *process, lldb::LanguageType language);
+
+ static llvm::StringRef GetPluginNameStatic() {
+ return "gnustep-objc-libobjc2";
+ }
+
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ void ModulesDidLoad(const ModuleList &module_list) override;
+
+ bool isA(const void *ClassID) const override {
+ return ClassID == &ID || ObjCLanguageRuntime::isA(ClassID);
+ }
+
+ static bool classof(const LanguageRuntime *runtime) {
+ return runtime->isA(&ID);
+ }
+
+ //
+ // LanguageRuntime implementation
+ //
+ bool GetObjectDescription(Stream &str, Value &value,
+ ExecutionContextScope *exe_scope) override;
+
+ bool GetObjectDescription(Stream &str, ValueObject &object) override;
+
+ bool CouldHaveDynamicValue(ValueObject &in_value) override;
+
+ bool GetDynamicTypeAndAddress(ValueObject &in_value,
+ lldb::DynamicValueType use_dynamic,
+ TypeAndOrName &class_type_or_name,
+ Address &address,
+ Value::ValueType &value_type) override;
+
+ TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
+ ValueObject &static_value) override;
+
+ lldb::BreakpointResolverSP
+ CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
+ bool throw_bp) override;
+
+ lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
+ bool stop_others) override;
+
+ //
+ // ObjCLanguageRuntime implementation
+ //
+
+ bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) override;
+
+ bool HasReadObjCLibrary() override { return m_objc_module_sp != nullptr; }
+
+ llvm::Expected<std::unique_ptr<UtilityFunction>>
+ CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override;
+
+ ObjCRuntimeVersions GetRuntimeVersion() const override {
+ return ObjCRuntimeVersions::eGNUstep_libobjc2;
+ }
+
+ void UpdateISAToDescriptorMapIfNeeded() override;
+
+protected:
+ // Call CreateInstance instead.
+ GNUstepObjCRuntime(Process *process);
+
+ lldb::ModuleSP m_objc_module_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_OBJC_GNUSTEPOBJCRUNTIME_GNUSTEPOBJCRUNTIME_H
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
index e28f224101bb..214642f654e2 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
@@ -235,6 +235,22 @@ ObjCLanguageRuntime::GetDescriptorIteratorPair(bool update_if_needed) {
m_isa_to_descriptor.begin(), m_isa_to_descriptor.end());
}
+void ObjCLanguageRuntime::ReadObjCLibraryIfNeeded(
+ const ModuleList &module_list) {
+ if (!HasReadObjCLibrary()) {
+ std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex());
+
+ size_t num_modules = module_list.GetSize();
+ for (size_t i = 0; i < num_modules; i++) {
+ auto mod = module_list.GetModuleAtIndex(i);
+ if (IsModuleObjCLibrary(mod)) {
+ ReadObjCLibrary(mod);
+ break;
+ }
+ }
+ }
+}
+
ObjCLanguageRuntime::ObjCISA
ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) {
ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa));
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
index f968a090c0f7..06eec22054b4 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
@@ -38,7 +38,8 @@ public:
enum class ObjCRuntimeVersions {
eObjC_VersionUnknown = 0,
eAppleObjC_V1 = 1,
- eAppleObjC_V2 = 2
+ eAppleObjC_V2 = 2,
+ eGNUstep_libobjc2 = 3,
};
typedef lldb::addr_t ObjCISA;
diff --git a/lldb/test/Shell/Expr/objc-gnustep-print.m b/lldb/test/Shell/Expr/objc-gnustep-print.m
index 565a96f15a3f..3f13bf1234cb 100644
--- a/lldb/test/Shell/Expr/objc-gnustep-print.m
+++ b/lldb/test/Shell/Expr/objc-gnustep-print.m
@@ -27,23 +27,39 @@ __attribute__((objc_root_class))
}
@end
-@interface TestObj : NSObject {}
-- (int)ok;
+@interface TestObj : NSObject {
+ int _int;
+ float _float;
+ char _char;
+ void *_ptr_void;
+ NSObject *_ptr_nsobject;
+ id _id_objc;
+}
+- (void)check_ivars_zeroed;
+- (void)set_ivars;
@end
@implementation TestObj
-- (int)ok {
- return self ? 0 : 1;
+- (void)check_ivars_zeroed {
+ ;
+}
+- (void)set_ivars {
+ _int = 1;
+ _float = 2.0f;
+ _char = '\3';
+ _ptr_void = (void*)4;
+ _ptr_nsobject = (NSObject*)5;
+ _id_objc = (id)6;
}
@end
-// RUN: %lldb -b -o "b objc-gnustep-print.m:35" -o "run" -o "p self" -o "p *self" -- %t | FileCheck %s --check-prefix=SELF
+// RUN: %lldb -b -o "b objc-gnustep-print.m:43" -o "run" -o "p self" -o "p *self" -- %t | FileCheck %s --check-prefix=SELF
//
-// SELF: (lldb) b objc-gnustep-print.m:35
+// SELF: (lldb) b objc-gnustep-print.m:43
// SELF: Breakpoint {{.*}} at objc-gnustep-print.m
//
// SELF: (lldb) run
// SELF: Process {{[0-9]+}} stopped
-// SELF: -[TestObj ok](self=[[SELF_PTR:0x[0-9a-f]+]]{{.*}}) at objc-gnustep-print.m:35
+// SELF: -[TestObj check_ivars_zeroed](self=[[SELF_PTR:0x[0-9a-f]+]]{{.*}}) at objc-gnustep-print.m
//
// SELF: (lldb) p self
// SELF: (TestObj *) [[SELF_PTR]]
@@ -54,9 +70,38 @@ __attribute__((objc_root_class))
// SELF: isa
// SELF: refcount
// SELF: }
+// SELF: _int = 0
+// SELF: _float = 0
+// SELF: _char = '\0'
+// SELF: _ptr_void = 0x{{0*}}
+// SELF: _ptr_nsobject = nil
+// SELF: _id_objc = nil
// SELF: }
+// RUN: %lldb -b -o "b objc-gnustep-print.m:106" -o "run" -o "p t->_int" -o "p t->_float" -o "p t->_char" \
+// RUN: -o "p t->_ptr_void" -o "p t->_ptr_nsobject" -o "p t->_id_objc" -- %t | FileCheck %s --check-prefix=IVARS_SET
+//
+// IVARS_SET: (lldb) p t->_int
+// IVARS_SET: (int) 1
+//
+// IVARS_SET: (lldb) p t->_float
+// IVARS_SET: (float) 2
+//
+// IVARS_SET: (lldb) p t->_char
+// IVARS_SET: (char) '\x03'
+//
+// IVARS_SET: (lldb) p t->_ptr_void
+// IVARS_SET: (void *) 0x{{0*}}4
+//
+// IVARS_SET: (lldb) p t->_ptr_nsobject
+// IVARS_SET: (NSObject *) 0x{{0*}}5
+//
+// IVARS_SET: (lldb) p t->_id_objc
+// IVARS_SET: (id) 0x{{0*}}6
+
int main() {
TestObj *t = [TestObj new];
- return [t ok];
+ [t check_ivars_zeroed];
+ [t set_ivars];
+ return 0;
}