Add PCH read/write support for Objective-C Selectors.

Note: This support is non-lazy. Once we get "Cocoa.h" humming, we can optimize this.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69884 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 851eebc..0e2d06e 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -1063,7 +1063,7 @@
 
 unsigned PCHStmtReader::VisitObjCSelectorExpr(ObjCSelectorExpr *E) {
   VisitExpr(E);
-  // FIXME: Selectors.
+  E->setSelector(Reader.GetSelector(Record, Idx));
   E->setAtLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
   E->setRParenLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
   return 0;
@@ -1586,8 +1586,69 @@
   }
 }
 
+bool PCHReader::ReadSelectorBlock() {
+  if (Stream.EnterSubBlock(pch::SELECTOR_BLOCK_ID))
+    return Error("Malformed selector block record");
+  
+  RecordData Record;
+  while (true) {
+    unsigned Code = Stream.ReadCode();
+    switch (Code) {
+    case llvm::bitc::END_BLOCK:
+      if (Stream.ReadBlockEnd())
+        return Error("Error at end of preprocessor block");
+      return false;
+    
+    case llvm::bitc::ENTER_SUBBLOCK:
+      // No known subblocks, always skip them.
+      Stream.ReadSubBlockID();
+      if (Stream.SkipBlock())
+        return Error("Malformed block record");
+      continue;
+    
+    case llvm::bitc::DEFINE_ABBREV:
+      Stream.ReadAbbrevRecord();
+      continue;
+    default: break;
+    }
+    
+    // Read a record.
+    Record.clear();
+    pch::PCHRecordTypes RecType =
+      (pch::PCHRecordTypes)Stream.ReadRecord(Code, Record);
+    switch (RecType) {
+    default:  // Default behavior: ignore unknown records.
+      break;
+    case pch::SELECTOR_TABLE:
+      unsigned Idx = 1; // Record[0] == pch::SELECTOR_TABLE.
+      unsigned NumSels = Record[Idx++];
+      
+      llvm::SmallVector<IdentifierInfo *, 8> KeyIdents;
+      for (unsigned SelIdx = 0; SelIdx < NumSels; SelIdx++) {
+        unsigned NumArgs = Record[Idx++];
+        KeyIdents.clear();
+        if (NumArgs <= 1) {
+          IdentifierInfo *II = DecodeIdentifierInfo(Record[Idx++]);
+          assert(II && "DecodeIdentifierInfo returned 0");
+          KeyIdents.push_back(II);
+        } else {
+          for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) {
+            IdentifierInfo *II = DecodeIdentifierInfo(Record[Idx++]);
+            assert(II && "DecodeIdentifierInfo returned 0");
+            KeyIdents.push_back(II);
+          }
+        }
+        Selector Sel = PP.getSelectorTable().getSelector(NumArgs,&KeyIdents[0]);
+        SelectorData.push_back(Sel);
+      }
+    }
+  }
+  return false;
+}
+
 PCHReader::PCHReadResult 
-PCHReader::ReadPCHBlock(uint64_t &PreprocessorBlockOffset) {
+PCHReader::ReadPCHBlock(uint64_t &PreprocessorBlockOffset,
+                        uint64_t &SelectorBlockOffset) {
   if (Stream.EnterSubBlock(pch::PCH_BLOCK_ID)) {
     Error("Malformed block record");
     return Failure;
@@ -1630,6 +1691,20 @@
           return Failure;
         }
         break;
+
+      case pch::SELECTOR_BLOCK_ID:
+        // Skip the selector block for now, but remember where it is.  We
+        // want to read it in after the identifier table.
+        if (SelectorBlockOffset) {
+          Error("Multiple selector blocks found.");
+          return Failure;
+        }
+        SelectorBlockOffset = Stream.GetCurrentBitNo();
+        if (Stream.SkipBlock()) {
+          Error("Malformed block record");
+          return Failure;
+        }
+        break;
           
       case pch::SOURCE_MANAGER_BLOCK_ID:
         switch (ReadSourceManagerBlock()) {
@@ -1740,7 +1815,6 @@
       TotalLexicalDeclContexts = Record[2];
       TotalVisibleDeclContexts = Record[3];
       break;
-
     case pch::TENTATIVE_DEFINITIONS:
       if (!TentativeDefinitions.empty()) {
         Error("Duplicate TENTATIVE_DEFINITIONS record in PCH file");
@@ -1758,7 +1832,6 @@
       break;
     }
   }
-
   Error("Premature end of bitstream");
   return Failure;
 }
@@ -1791,6 +1864,8 @@
   // We expect a number of well-defined blocks, though we don't necessarily
   // need to understand them all.
   uint64_t PreprocessorBlockOffset = 0;
+  uint64_t SelectorBlockOffset = 0;
+  
   while (!Stream.AtEndOfStream()) {
     unsigned Code = Stream.ReadCode();
     
@@ -1810,7 +1885,7 @@
       }
       break;
     case pch::PCH_BLOCK_ID:
-      switch (ReadPCHBlock(PreprocessorBlockOffset)) {
+      switch (ReadPCHBlock(PreprocessorBlockOffset, SelectorBlockOffset)) {
       case Success:
         break;
 
@@ -1882,6 +1957,14 @@
       return Failure;
     }
   }
+  if (SelectorBlockOffset) {
+    SavedStreamPosition SavedPos(Stream);
+    Stream.JumpToBit(SelectorBlockOffset);
+    if (ReadSelectorBlock()) {
+      Error("Malformed preprocessor block");
+      return Failure;
+    }
+  }
 
   return Success;
 }
@@ -2633,6 +2716,22 @@
   return reinterpret_cast<IdentifierInfo *>(IdentifierData[ID - 1]);
 }
 
+Selector PCHReader::DecodeSelector(unsigned ID) {
+  if (ID == 0)
+    return Selector();
+  
+  if (SelectorData.empty()) {
+    Error("No selector table in PCH file");
+    return Selector();
+  }
+  
+  if (ID > SelectorData.size()) {
+    Error("Selector ID out of range");
+    return Selector();
+  }
+  return SelectorData[ID-1];
+}
+
 DeclarationName 
 PCHReader::ReadDeclarationName(const RecordData &Record, unsigned &Idx) {
   DeclarationName::NameKind Kind = (DeclarationName::NameKind)Record[Idx++];
diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp
index dd8d95a..62e1299 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -1182,9 +1182,7 @@
 
 void PCHStmtWriter::VisitObjCSelectorExpr(ObjCSelectorExpr *E) {
   VisitExpr(E);
-  assert(0 && "Can't write a selector yet!");
-  // FIXME!  Write selectors.
-  //Writer.WriteSubStmt(E->getSelector());
+  Writer.AddSelectorRef(E->getSelector(), Record);
   Writer.AddSourceLocation(E->getAtLoc(), Record);
   Writer.AddSourceLocation(E->getRParenLoc(), Record);
   Code = pch::EXPR_OBJC_SELECTOR_EXPR;
@@ -1888,6 +1886,26 @@
   Stream.EmitRecord(pch::IDENTIFIER_OFFSET, IdentifierOffsets);
 }
 
+void PCHWriter::WriteSelectorTable() {
+  Stream.EnterSubblock(pch::SELECTOR_BLOCK_ID, 3);
+  RecordData Record;
+  Record.push_back(pch::SELECTOR_TABLE);
+  Record.push_back(SelectorIDs.size());
+  
+  // Create the on-disk representation.
+  for (unsigned selIdx = 0; selIdx < SelVector.size(); selIdx++) {
+    assert(SelVector[selIdx].getAsOpaquePtr() && "NULL Selector found");
+    Record.push_back(SelVector[selIdx].getNumArgs());
+    if (SelVector[selIdx].getNumArgs())
+      for (unsigned i = 0; i < SelVector[selIdx].getNumArgs(); i++)
+        AddIdentifierRef(SelVector[selIdx].getIdentifierInfoForSlot(i), Record);
+    else
+      AddIdentifierRef(SelVector[selIdx].getIdentifierInfoForSlot(0), Record);
+  }
+  Stream.EmitRecord(pch::SELECTOR_TABLE, Record);
+  Stream.ExitBlock();
+}
+
 /// \brief Write a record containing the given attributes.
 void PCHWriter::WriteAttributeRecord(const Attr *Attr) {
   RecordData Record;
@@ -2078,6 +2096,7 @@
   WritePreprocessor(PP);
   WriteTypesBlock(Context);
   WriteDeclsBlock(Context);
+  WriteSelectorTable();
   WriteIdentifierTable(PP);
   Stream.EmitRecord(pch::TYPE_OFFSET, TypeOffsets);
   Stream.EmitRecord(pch::DECL_OFFSET, DeclOffsets);
@@ -2145,6 +2164,20 @@
   return ID;
 }
 
+void PCHWriter::AddSelectorRef(const Selector SelRef, RecordData &Record) {
+  if (SelRef.getAsOpaquePtr() == 0) {
+    Record.push_back(0);
+    return;
+  }
+
+  pch::SelectorID &SID = SelectorIDs[SelRef];
+  if (SID == 0) {
+    SID = SelectorIDs.size();
+    SelVector.push_back(SelRef);
+  }
+  Record.push_back(SID);
+}
+
 void PCHWriter::AddTypeRef(QualType T, RecordData &Record) {
   if (T.isNull()) {
     Record.push_back(pch::PREDEF_TYPE_NULL_ID);
@@ -2223,7 +2256,7 @@
   case DeclarationName::ObjCZeroArgSelector:
   case DeclarationName::ObjCOneArgSelector:
   case DeclarationName::ObjCMultiArgSelector:
-    assert(false && "Serialization of Objective-C selectors unavailable");
+    AddSelectorRef(Name.getObjCSelector(), Record);
     break;
 
   case DeclarationName::CXXConstructorName: