Handle UTF-16 and UTF-32 constant CFStrings
authorSean Callanan <scallanan@apple.com>
Thu, 1 Dec 2016 17:46:51 +0000 (17:46 +0000)
committerSean Callanan <scallanan@apple.com>
Thu, 1 Dec 2016 17:46:51 +0000 (17:46 +0000)
We have a longstanding issue where the expression parser does not handle wide CFStrings (e.g., @"凸凹") correctly, producing the useless error message

Internal error [IRForTarget]: An Objective-C constant string's string initializer is not an array
error: warning: expression result unused
error: The expression could not be prepared to run in the target

This is just a side effect of the fact that we don't handle wide string constants when converting these to CFStringCreateWithBytes. That function takes the string's encoding as an argument, so I made it work and added a testcase.

https://reviews.llvm.org/D27291
<rdar://problem/13190557>

llvm-svn: 288386

lldb/packages/Python/lldbsuite/test/lang/objc/unicode-string/TestUnicodeString.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/objc/unicode-string/main.m [new file with mode: 0644]
lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp

diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/unicode-string/TestUnicodeString.py b/lldb/packages/Python/lldbsuite/test/lang/objc/unicode-string/TestUnicodeString.py
new file mode 100644 (file)
index 0000000..c9986aa
--- /dev/null
@@ -0,0 +1,6 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(
+    __file__, globals(), [
+        decorators.skipUnlessDarwin])
diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/unicode-string/main.m b/lldb/packages/Python/lldbsuite/test/lang/objc/unicode-string/main.m
new file mode 100644 (file)
index 0000000..e55eb1e
--- /dev/null
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+
+int main() {
+  NSLog(@"凸"); //% self.expect("po @\"凹\"", DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ["凹"])
+}
index ac6e93c..53c57e9 100644 (file)
@@ -498,42 +498,60 @@ bool IRForTarget::RewriteObjCConstString(llvm::GlobalVariable *ns_str,
   Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty)
                              : Constant::getNullValue(i8_ptr_ty);
   Constant *numBytes_arg = ConstantInt::get(
-      m_intptr_ty, cstr ? string_array->getNumElements() - 1 : 0, false);
-  Constant *encoding_arg = ConstantInt::get(
-      i32_ty, 0x0600, false); /* 0x0600 is kCFStringEncodingASCII */
-  Constant *isExternal_arg =
-      ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */
-
-  Value *argument_array[5];
-
-  argument_array[0] = alloc_arg;
-  argument_array[1] = bytes_arg;
-  argument_array[2] = numBytes_arg;
-  argument_array[3] = encoding_arg;
-  argument_array[4] = isExternal_arg;
-
-  ArrayRef<Value *> CFSCWB_arguments(argument_array, 5);
-
-  FunctionValueCache CFSCWB_Caller(
-      [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * {
-        return CallInst::Create(
-            m_CFStringCreateWithBytes, CFSCWB_arguments,
-            "CFStringCreateWithBytes",
-            llvm::cast<Instruction>(
-                m_entry_instruction_finder.GetValue(function)));
-      });
-
-  if (!UnfoldConstant(ns_str, nullptr, CFSCWB_Caller,
-                      m_entry_instruction_finder, m_error_stream)) {
-    if (log)
-      log->PutCString(
-          "Couldn't replace the NSString with the result of the call");
-
-    m_error_stream.Printf("error [IRForTarget internal]: Couldn't replace an "
-                          "Objective-C constant string with a dynamic "
-                          "string\n");
-
-    return false;
+      m_intptr_ty, cstr ? (string_array->getNumElements() - 1) * string_array->getElementByteSize() : 0, false);
+ int encoding_flags = 0;
+ switch (string_array->getElementByteSize()) {
+ case 1:
+   encoding_flags = 0x08000100; /* 0x08000100 is kCFStringEncodingUTF8 */
+   break;
+ case 2:
+   encoding_flags = 0x0100; /* 0x0100 is kCFStringEncodingUTF16 */
+   break;
+ case 4:
+   encoding_flags = 0x0c000100; /* 0x0c000100 is kCFStringEncodingUTF32 */
+   break;
+ default:
+   encoding_flags = 0x0600; /* fall back to 0x0600, kCFStringEncodingASCII */
+   if (log) {
+     log->Printf("Encountered an Objective-C constant string with unusual "
+                 "element size %llu",
+                 string_array->getElementByteSize());
+   }
+ }
+ Constant *encoding_arg = ConstantInt::get(i32_ty, encoding_flags, false);
+ Constant *isExternal_arg =
+     ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */
+
+ Value *argument_array[5];
+
+ argument_array[0] = alloc_arg;
+ argument_array[1] = bytes_arg;
+ argument_array[2] = numBytes_arg;
+ argument_array[3] = encoding_arg;
+ argument_array[4] = isExternal_arg;
+
+ ArrayRef<Value *> CFSCWB_arguments(argument_array, 5);
+
+ FunctionValueCache CFSCWB_Caller(
+     [this, &CFSCWB_arguments](llvm::Function *function) -> llvm::Value * {
+       return CallInst::Create(
+           m_CFStringCreateWithBytes, CFSCWB_arguments,
+           "CFStringCreateWithBytes",
+           llvm::cast<Instruction>(
+               m_entry_instruction_finder.GetValue(function)));
+     });
+
+ if (!UnfoldConstant(ns_str, nullptr, CFSCWB_Caller, m_entry_instruction_finder,
+                     m_error_stream)) {
+   if (log)
+     log->PutCString(
+         "Couldn't replace the NSString with the result of the call");
+
+   m_error_stream.Printf("error [IRForTarget internal]: Couldn't replace an "
+                         "Objective-C constant string with a dynamic "
+                         "string\n");
+
+   return false;
   }
 
   ns_str->eraseFromParent();
@@ -642,31 +660,23 @@ bool IRForTarget::RewriteObjCConstStrings() {
         return false;
       }
 
-      if (nsstring_expr->getOpcode() != Instruction::GetElementPtr) {
-        if (log)
-          log->Printf("NSString initializer's str element is not a "
-                      "GetElementPtr expression, it's a %s",
-                      nsstring_expr->getOpcodeName());
-
-        m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
-                              "constant string's string initializer is not an "
-                              "array\n");
+      GlobalVariable *cstr_global = nullptr;
 
-        return false;
+      if (nsstring_expr->getOpcode() == Instruction::GetElementPtr) {
+        Constant *nsstring_cstr = nsstring_expr->getOperand(0);
+        cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
+      } else if (nsstring_expr->getOpcode() == Instruction::BitCast) {
+        Constant *nsstring_cstr = nsstring_expr->getOperand(0);
+        cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
       }
 
-      Constant *nsstring_cstr = nsstring_expr->getOperand(0);
-
-      GlobalVariable *cstr_global = dyn_cast<GlobalVariable>(nsstring_cstr);
-
       if (!cstr_global) {
         if (log)
           log->PutCString(
               "NSString initializer's str element is not a GlobalVariable");
 
-        m_error_stream.Printf("Internal error [IRForTarget]: An Objective-C "
-                              "constant string's string initializer doesn't "
-                              "point to a global\n");
+        m_error_stream.Printf("Internal error [IRForTarget]: Unhandled"
+                              "constant string initializer\n");
 
         return false;
       }