From 13cc256e404620c1de0cbcc4e43ce1e2dbbc4898 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Sun, 2 Dec 2012 13:20:44 +0000 Subject: Vendor import of clang release_32 branch r168974 (effectively, 3.2 RC2): http://llvm.org/svn/llvm-project/cfe/branches/release_32@168974 --- lib/Rewrite/CMakeLists.txt | 35 +- lib/Rewrite/Core/CMakeLists.txt | 24 + lib/Rewrite/Core/DeltaTree.cpp | 464 ++ lib/Rewrite/Core/HTMLRewrite.cpp | 584 +++ lib/Rewrite/Core/Makefile | 18 + lib/Rewrite/Core/RewriteRope.cpp | 807 +++ lib/Rewrite/Core/Rewriter.cpp | 486 ++ lib/Rewrite/Core/TokenRewriter.cpp | 99 + lib/Rewrite/DeltaTree.cpp | 467 -- lib/Rewrite/FixItRewriter.cpp | 205 - lib/Rewrite/Frontend/CMakeLists.txt | 28 + lib/Rewrite/Frontend/FixItRewriter.cpp | 205 + lib/Rewrite/Frontend/FrontendActions.cpp | 192 + lib/Rewrite/Frontend/HTMLPrint.cpp | 94 + lib/Rewrite/Frontend/InclusionRewriter.cpp | 363 ++ lib/Rewrite/Frontend/Makefile | 18 + lib/Rewrite/Frontend/RewriteMacros.cpp | 217 + lib/Rewrite/Frontend/RewriteModernObjC.cpp | 7607 ++++++++++++++++++++++++++++ lib/Rewrite/Frontend/RewriteObjC.cpp | 6029 ++++++++++++++++++++++ lib/Rewrite/Frontend/RewriteTest.cpp | 39 + lib/Rewrite/FrontendActions.cpp | 192 - lib/Rewrite/HTMLPrint.cpp | 94 - lib/Rewrite/HTMLRewrite.cpp | 583 --- lib/Rewrite/InclusionRewriter.cpp | 361 -- lib/Rewrite/Makefile | 10 +- lib/Rewrite/RewriteMacros.cpp | 217 - lib/Rewrite/RewriteModernObjC.cpp | 7543 --------------------------- lib/Rewrite/RewriteObjC.cpp | 6035 ---------------------- lib/Rewrite/RewriteRope.cpp | 811 --- lib/Rewrite/RewriteTest.cpp | 39 - lib/Rewrite/Rewriter.cpp | 486 -- lib/Rewrite/TokenRewriter.cpp | 99 - 32 files changed, 17279 insertions(+), 17172 deletions(-) create mode 100644 lib/Rewrite/Core/CMakeLists.txt create mode 100644 lib/Rewrite/Core/DeltaTree.cpp create mode 100644 lib/Rewrite/Core/HTMLRewrite.cpp create mode 100644 lib/Rewrite/Core/Makefile create mode 100644 lib/Rewrite/Core/RewriteRope.cpp create mode 100644 lib/Rewrite/Core/Rewriter.cpp create mode 100644 lib/Rewrite/Core/TokenRewriter.cpp delete mode 100644 lib/Rewrite/DeltaTree.cpp delete mode 100644 lib/Rewrite/FixItRewriter.cpp create mode 100644 lib/Rewrite/Frontend/CMakeLists.txt create mode 100644 lib/Rewrite/Frontend/FixItRewriter.cpp create mode 100644 lib/Rewrite/Frontend/FrontendActions.cpp create mode 100644 lib/Rewrite/Frontend/HTMLPrint.cpp create mode 100644 lib/Rewrite/Frontend/InclusionRewriter.cpp create mode 100644 lib/Rewrite/Frontend/Makefile create mode 100644 lib/Rewrite/Frontend/RewriteMacros.cpp create mode 100644 lib/Rewrite/Frontend/RewriteModernObjC.cpp create mode 100644 lib/Rewrite/Frontend/RewriteObjC.cpp create mode 100644 lib/Rewrite/Frontend/RewriteTest.cpp delete mode 100644 lib/Rewrite/FrontendActions.cpp delete mode 100644 lib/Rewrite/HTMLPrint.cpp delete mode 100644 lib/Rewrite/HTMLRewrite.cpp delete mode 100644 lib/Rewrite/InclusionRewriter.cpp delete mode 100644 lib/Rewrite/RewriteMacros.cpp delete mode 100644 lib/Rewrite/RewriteModernObjC.cpp delete mode 100644 lib/Rewrite/RewriteObjC.cpp delete mode 100644 lib/Rewrite/RewriteRope.cpp delete mode 100644 lib/Rewrite/RewriteTest.cpp delete mode 100644 lib/Rewrite/Rewriter.cpp delete mode 100644 lib/Rewrite/TokenRewriter.cpp (limited to 'lib/Rewrite') diff --git a/lib/Rewrite/CMakeLists.txt b/lib/Rewrite/CMakeLists.txt index af8f6d4f90dd2..d3d75430233fe 100644 --- a/lib/Rewrite/CMakeLists.txt +++ b/lib/Rewrite/CMakeLists.txt @@ -1,33 +1,2 @@ -add_clang_library(clangRewrite - DeltaTree.cpp - FixItRewriter.cpp - FrontendActions.cpp - HTMLPrint.cpp - HTMLRewrite.cpp - InclusionRewriter.cpp - RewriteMacros.cpp - RewriteModernObjC.cpp - RewriteObjC.cpp - RewriteRope.cpp - RewriteTest.cpp - Rewriter.cpp - TokenRewriter.cpp - ) - -add_dependencies(clangRewrite - ClangAttrClasses - ClangAttrList - ClangAttrParsedAttrList - ClangCommentNodes - ClangDeclNodes - ClangDiagnosticCommon - ClangDiagnosticFrontend - ClangStmtNodes - ) - -target_link_libraries(clangRewrite - clangBasic - clangAST - clangParse - clangFrontend - ) +add_subdirectory(Core) +add_subdirectory(Frontend) diff --git a/lib/Rewrite/Core/CMakeLists.txt b/lib/Rewrite/Core/CMakeLists.txt new file mode 100644 index 0000000000000..07978187ff190 --- /dev/null +++ b/lib/Rewrite/Core/CMakeLists.txt @@ -0,0 +1,24 @@ +add_clang_library(clangRewriteCore + DeltaTree.cpp + HTMLRewrite.cpp + RewriteRope.cpp + Rewriter.cpp + TokenRewriter.cpp + ) + +add_dependencies(clangRewriteCore + ClangAttrClasses + ClangAttrList + ClangAttrParsedAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticFrontend + ClangStmtNodes + ) + +target_link_libraries(clangRewriteCore + clangBasic + clangAST + clangParse + ) diff --git a/lib/Rewrite/Core/DeltaTree.cpp b/lib/Rewrite/Core/DeltaTree.cpp new file mode 100644 index 0000000000000..46922772c5a2f --- /dev/null +++ b/lib/Rewrite/Core/DeltaTree.cpp @@ -0,0 +1,464 @@ +//===--- DeltaTree.cpp - B-Tree for Rewrite Delta tracking ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the DeltaTree and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Core/DeltaTree.h" +#include "clang/Basic/LLVM.h" +#include +#include +using namespace clang; + +/// The DeltaTree class is a multiway search tree (BTree) structure with some +/// fancy features. B-Trees are generally more memory and cache efficient +/// than binary trees, because they store multiple keys/values in each node. +/// +/// DeltaTree implements a key/value mapping from FileIndex to Delta, allowing +/// fast lookup by FileIndex. However, an added (important) bonus is that it +/// can also efficiently tell us the full accumulated delta for a specific +/// file offset as well, without traversing the whole tree. +/// +/// The nodes of the tree are made up of instances of two classes: +/// DeltaTreeNode and DeltaTreeInteriorNode. The later subclasses the +/// former and adds children pointers. Each node knows the full delta of all +/// entries (recursively) contained inside of it, which allows us to get the +/// full delta implied by a whole subtree in constant time. + +namespace { + /// SourceDelta - As code in the original input buffer is added and deleted, + /// SourceDelta records are used to keep track of how the input SourceLocation + /// object is mapped into the output buffer. + struct SourceDelta { + unsigned FileLoc; + int Delta; + + static SourceDelta get(unsigned Loc, int D) { + SourceDelta Delta; + Delta.FileLoc = Loc; + Delta.Delta = D; + return Delta; + } + }; + + /// DeltaTreeNode - The common part of all nodes. + /// + class DeltaTreeNode { + public: + struct InsertResult { + DeltaTreeNode *LHS, *RHS; + SourceDelta Split; + }; + + private: + friend class DeltaTreeInteriorNode; + + /// WidthFactor - This controls the number of K/V slots held in the BTree: + /// how wide it is. Each level of the BTree is guaranteed to have at least + /// WidthFactor-1 K/V pairs (except the root) and may have at most + /// 2*WidthFactor-1 K/V pairs. + enum { WidthFactor = 8 }; + + /// Values - This tracks the SourceDelta's currently in this node. + /// + SourceDelta Values[2*WidthFactor-1]; + + /// NumValuesUsed - This tracks the number of values this node currently + /// holds. + unsigned char NumValuesUsed; + + /// IsLeaf - This is true if this is a leaf of the btree. If false, this is + /// an interior node, and is actually an instance of DeltaTreeInteriorNode. + bool IsLeaf; + + /// FullDelta - This is the full delta of all the values in this node and + /// all children nodes. + int FullDelta; + public: + DeltaTreeNode(bool isLeaf = true) + : NumValuesUsed(0), IsLeaf(isLeaf), FullDelta(0) {} + + bool isLeaf() const { return IsLeaf; } + int getFullDelta() const { return FullDelta; } + bool isFull() const { return NumValuesUsed == 2*WidthFactor-1; } + + unsigned getNumValuesUsed() const { return NumValuesUsed; } + const SourceDelta &getValue(unsigned i) const { + assert(i < NumValuesUsed && "Invalid value #"); + return Values[i]; + } + SourceDelta &getValue(unsigned i) { + assert(i < NumValuesUsed && "Invalid value #"); + return Values[i]; + } + + /// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into + /// this node. If insertion is easy, do it and return false. Otherwise, + /// split the node, populate InsertRes with info about the split, and return + /// true. + bool DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes); + + void DoSplit(InsertResult &InsertRes); + + + /// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a + /// local walk over our contained deltas. + void RecomputeFullDeltaLocally(); + + void Destroy(); + }; +} // end anonymous namespace + +namespace { + /// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers. + /// This class tracks them. + class DeltaTreeInteriorNode : public DeltaTreeNode { + DeltaTreeNode *Children[2*WidthFactor]; + ~DeltaTreeInteriorNode() { + for (unsigned i = 0, e = NumValuesUsed+1; i != e; ++i) + Children[i]->Destroy(); + } + friend class DeltaTreeNode; + public: + DeltaTreeInteriorNode() : DeltaTreeNode(false /*nonleaf*/) {} + + DeltaTreeInteriorNode(const InsertResult &IR) + : DeltaTreeNode(false /*nonleaf*/) { + Children[0] = IR.LHS; + Children[1] = IR.RHS; + Values[0] = IR.Split; + FullDelta = IR.LHS->getFullDelta()+IR.RHS->getFullDelta()+IR.Split.Delta; + NumValuesUsed = 1; + } + + const DeltaTreeNode *getChild(unsigned i) const { + assert(i < getNumValuesUsed()+1 && "Invalid child"); + return Children[i]; + } + DeltaTreeNode *getChild(unsigned i) { + assert(i < getNumValuesUsed()+1 && "Invalid child"); + return Children[i]; + } + + static inline bool classof(const DeltaTreeNode *N) { return !N->isLeaf(); } + }; +} + + +/// Destroy - A 'virtual' destructor. +void DeltaTreeNode::Destroy() { + if (isLeaf()) + delete this; + else + delete cast(this); +} + +/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a +/// local walk over our contained deltas. +void DeltaTreeNode::RecomputeFullDeltaLocally() { + int NewFullDelta = 0; + for (unsigned i = 0, e = getNumValuesUsed(); i != e; ++i) + NewFullDelta += Values[i].Delta; + if (DeltaTreeInteriorNode *IN = dyn_cast(this)) + for (unsigned i = 0, e = getNumValuesUsed()+1; i != e; ++i) + NewFullDelta += IN->getChild(i)->getFullDelta(); + FullDelta = NewFullDelta; +} + +/// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into +/// this node. If insertion is easy, do it and return false. Otherwise, +/// split the node, populate InsertRes with info about the split, and return +/// true. +bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta, + InsertResult *InsertRes) { + // Maintain full delta for this node. + FullDelta += Delta; + + // Find the insertion point, the first delta whose index is >= FileIndex. + unsigned i = 0, e = getNumValuesUsed(); + while (i != e && FileIndex > getValue(i).FileLoc) + ++i; + + // If we found an a record for exactly this file index, just merge this + // value into the pre-existing record and finish early. + if (i != e && getValue(i).FileLoc == FileIndex) { + // NOTE: Delta could drop to zero here. This means that the delta entry is + // useless and could be removed. Supporting erases is more complex than + // leaving an entry with Delta=0, so we just leave an entry with Delta=0 in + // the tree. + Values[i].Delta += Delta; + return false; + } + + // Otherwise, we found an insertion point, and we know that the value at the + // specified index is > FileIndex. Handle the leaf case first. + if (isLeaf()) { + if (!isFull()) { + // For an insertion into a non-full leaf node, just insert the value in + // its sorted position. This requires moving later values over. + if (i != e) + memmove(&Values[i+1], &Values[i], sizeof(Values[0])*(e-i)); + Values[i] = SourceDelta::get(FileIndex, Delta); + ++NumValuesUsed; + return false; + } + + // Otherwise, if this is leaf is full, split the node at its median, insert + // the value into one of the children, and return the result. + assert(InsertRes && "No result location specified"); + DoSplit(*InsertRes); + + if (InsertRes->Split.FileLoc > FileIndex) + InsertRes->LHS->DoInsertion(FileIndex, Delta, 0 /*can't fail*/); + else + InsertRes->RHS->DoInsertion(FileIndex, Delta, 0 /*can't fail*/); + return true; + } + + // Otherwise, this is an interior node. Send the request down the tree. + DeltaTreeInteriorNode *IN = cast(this); + if (!IN->Children[i]->DoInsertion(FileIndex, Delta, InsertRes)) + return false; // If there was space in the child, just return. + + // Okay, this split the subtree, producing a new value and two children to + // insert here. If this node is non-full, we can just insert it directly. + if (!isFull()) { + // Now that we have two nodes and a new element, insert the perclated value + // into ourself by moving all the later values/children down, then inserting + // the new one. + if (i != e) + memmove(&IN->Children[i+2], &IN->Children[i+1], + (e-i)*sizeof(IN->Children[0])); + IN->Children[i] = InsertRes->LHS; + IN->Children[i+1] = InsertRes->RHS; + + if (e != i) + memmove(&Values[i+1], &Values[i], (e-i)*sizeof(Values[0])); + Values[i] = InsertRes->Split; + ++NumValuesUsed; + return false; + } + + // Finally, if this interior node was full and a node is percolated up, split + // ourself and return that up the chain. Start by saving all our info to + // avoid having the split clobber it. + IN->Children[i] = InsertRes->LHS; + DeltaTreeNode *SubRHS = InsertRes->RHS; + SourceDelta SubSplit = InsertRes->Split; + + // Do the split. + DoSplit(*InsertRes); + + // Figure out where to insert SubRHS/NewSplit. + DeltaTreeInteriorNode *InsertSide; + if (SubSplit.FileLoc < InsertRes->Split.FileLoc) + InsertSide = cast(InsertRes->LHS); + else + InsertSide = cast(InsertRes->RHS); + + // We now have a non-empty interior node 'InsertSide' to insert + // SubRHS/SubSplit into. Find out where to insert SubSplit. + + // Find the insertion point, the first delta whose index is >SubSplit.FileLoc. + i = 0; e = InsertSide->getNumValuesUsed(); + while (i != e && SubSplit.FileLoc > InsertSide->getValue(i).FileLoc) + ++i; + + // Now we know that i is the place to insert the split value into. Insert it + // and the child right after it. + if (i != e) + memmove(&InsertSide->Children[i+2], &InsertSide->Children[i+1], + (e-i)*sizeof(IN->Children[0])); + InsertSide->Children[i+1] = SubRHS; + + if (e != i) + memmove(&InsertSide->Values[i+1], &InsertSide->Values[i], + (e-i)*sizeof(Values[0])); + InsertSide->Values[i] = SubSplit; + ++InsertSide->NumValuesUsed; + InsertSide->FullDelta += SubSplit.Delta + SubRHS->getFullDelta(); + return true; +} + +/// DoSplit - Split the currently full node (which has 2*WidthFactor-1 values) +/// into two subtrees each with "WidthFactor-1" values and a pivot value. +/// Return the pieces in InsertRes. +void DeltaTreeNode::DoSplit(InsertResult &InsertRes) { + assert(isFull() && "Why split a non-full node?"); + + // Since this node is full, it contains 2*WidthFactor-1 values. We move + // the first 'WidthFactor-1' values to the LHS child (which we leave in this + // node), propagate one value up, and move the last 'WidthFactor-1' values + // into the RHS child. + + // Create the new child node. + DeltaTreeNode *NewNode; + if (DeltaTreeInteriorNode *IN = dyn_cast(this)) { + // If this is an interior node, also move over 'WidthFactor' children + // into the new node. + DeltaTreeInteriorNode *New = new DeltaTreeInteriorNode(); + memcpy(&New->Children[0], &IN->Children[WidthFactor], + WidthFactor*sizeof(IN->Children[0])); + NewNode = New; + } else { + // Just create the new leaf node. + NewNode = new DeltaTreeNode(); + } + + // Move over the last 'WidthFactor-1' values from here to NewNode. + memcpy(&NewNode->Values[0], &Values[WidthFactor], + (WidthFactor-1)*sizeof(Values[0])); + + // Decrease the number of values in the two nodes. + NewNode->NumValuesUsed = NumValuesUsed = WidthFactor-1; + + // Recompute the two nodes' full delta. + NewNode->RecomputeFullDeltaLocally(); + RecomputeFullDeltaLocally(); + + InsertRes.LHS = this; + InsertRes.RHS = NewNode; + InsertRes.Split = Values[WidthFactor-1]; +} + + + +//===----------------------------------------------------------------------===// +// DeltaTree Implementation +//===----------------------------------------------------------------------===// + +//#define VERIFY_TREE + +#ifdef VERIFY_TREE +/// VerifyTree - Walk the btree performing assertions on various properties to +/// verify consistency. This is useful for debugging new changes to the tree. +static void VerifyTree(const DeltaTreeNode *N) { + const DeltaTreeInteriorNode *IN = dyn_cast(N); + if (IN == 0) { + // Verify leaves, just ensure that FullDelta matches up and the elements + // are in proper order. + int FullDelta = 0; + for (unsigned i = 0, e = N->getNumValuesUsed(); i != e; ++i) { + if (i) + assert(N->getValue(i-1).FileLoc < N->getValue(i).FileLoc); + FullDelta += N->getValue(i).Delta; + } + assert(FullDelta == N->getFullDelta()); + return; + } + + // Verify interior nodes: Ensure that FullDelta matches up and the + // elements are in proper order and the children are in proper order. + int FullDelta = 0; + for (unsigned i = 0, e = IN->getNumValuesUsed(); i != e; ++i) { + const SourceDelta &IVal = N->getValue(i); + const DeltaTreeNode *IChild = IN->getChild(i); + if (i) + assert(IN->getValue(i-1).FileLoc < IVal.FileLoc); + FullDelta += IVal.Delta; + FullDelta += IChild->getFullDelta(); + + // The largest value in child #i should be smaller than FileLoc. + assert(IChild->getValue(IChild->getNumValuesUsed()-1).FileLoc < + IVal.FileLoc); + + // The smallest value in child #i+1 should be larger than FileLoc. + assert(IN->getChild(i+1)->getValue(0).FileLoc > IVal.FileLoc); + VerifyTree(IChild); + } + + FullDelta += IN->getChild(IN->getNumValuesUsed())->getFullDelta(); + + assert(FullDelta == N->getFullDelta()); +} +#endif // VERIFY_TREE + +static DeltaTreeNode *getRoot(void *Root) { + return (DeltaTreeNode*)Root; +} + +DeltaTree::DeltaTree() { + Root = new DeltaTreeNode(); +} +DeltaTree::DeltaTree(const DeltaTree &RHS) { + // Currently we only support copying when the RHS is empty. + assert(getRoot(RHS.Root)->getNumValuesUsed() == 0 && + "Can only copy empty tree"); + Root = new DeltaTreeNode(); +} + +DeltaTree::~DeltaTree() { + getRoot(Root)->Destroy(); +} + +/// getDeltaAt - Return the accumulated delta at the specified file offset. +/// This includes all insertions or delections that occurred *before* the +/// specified file index. +int DeltaTree::getDeltaAt(unsigned FileIndex) const { + const DeltaTreeNode *Node = getRoot(Root); + + int Result = 0; + + // Walk down the tree. + while (1) { + // For all nodes, include any local deltas before the specified file + // index by summing them up directly. Keep track of how many were + // included. + unsigned NumValsGreater = 0; + for (unsigned e = Node->getNumValuesUsed(); NumValsGreater != e; + ++NumValsGreater) { + const SourceDelta &Val = Node->getValue(NumValsGreater); + + if (Val.FileLoc >= FileIndex) + break; + Result += Val.Delta; + } + + // If we have an interior node, include information about children and + // recurse. Otherwise, if we have a leaf, we're done. + const DeltaTreeInteriorNode *IN = dyn_cast(Node); + if (!IN) return Result; + + // Include any children to the left of the values we skipped, all of + // their deltas should be included as well. + for (unsigned i = 0; i != NumValsGreater; ++i) + Result += IN->getChild(i)->getFullDelta(); + + // If we found exactly the value we were looking for, break off the + // search early. There is no need to search the RHS of the value for + // partial results. + if (NumValsGreater != Node->getNumValuesUsed() && + Node->getValue(NumValsGreater).FileLoc == FileIndex) + return Result+IN->getChild(NumValsGreater)->getFullDelta(); + + // Otherwise, traverse down the tree. The selected subtree may be + // partially included in the range. + Node = IN->getChild(NumValsGreater); + } + // NOT REACHED. +} + +/// AddDelta - When a change is made that shifts around the text buffer, +/// this method is used to record that info. It inserts a delta of 'Delta' +/// into the current DeltaTree at offset FileIndex. +void DeltaTree::AddDelta(unsigned FileIndex, int Delta) { + assert(Delta && "Adding a noop?"); + DeltaTreeNode *MyRoot = getRoot(Root); + + DeltaTreeNode::InsertResult InsertRes; + if (MyRoot->DoInsertion(FileIndex, Delta, &InsertRes)) { + Root = MyRoot = new DeltaTreeInteriorNode(InsertRes); + } + +#ifdef VERIFY_TREE + VerifyTree(MyRoot); +#endif +} + diff --git a/lib/Rewrite/Core/HTMLRewrite.cpp b/lib/Rewrite/Core/HTMLRewrite.cpp new file mode 100644 index 0000000000000..0e8e4fec9f4a5 --- /dev/null +++ b/lib/Rewrite/Core/HTMLRewrite.cpp @@ -0,0 +1,584 @@ +//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the HTMLRewriter clas, which is used to translate the +// text of a source file into prettified HTML. +// +//===----------------------------------------------------------------------===// + +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Lex/TokenConcatenation.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + + +/// HighlightRange - Highlight a range in the source code with the specified +/// start/end tags. B/E must be in the same file. This ensures that +/// start/end tags are placed at the start/end of each line if the range is +/// multiline. +void html::HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E, + const char *StartTag, const char *EndTag) { + SourceManager &SM = R.getSourceMgr(); + B = SM.getExpansionLoc(B); + E = SM.getExpansionLoc(E); + FileID FID = SM.getFileID(B); + assert(SM.getFileID(E) == FID && "B/E not in the same file!"); + + unsigned BOffset = SM.getFileOffset(B); + unsigned EOffset = SM.getFileOffset(E); + + // Include the whole end token in the range. + EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr(), R.getLangOpts()); + + bool Invalid = false; + const char *BufferStart = SM.getBufferData(FID, &Invalid).data(); + if (Invalid) + return; + + HighlightRange(R.getEditBuffer(FID), BOffset, EOffset, + BufferStart, StartTag, EndTag); +} + +/// HighlightRange - This is the same as the above method, but takes +/// decomposed file locations. +void html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E, + const char *BufferStart, + const char *StartTag, const char *EndTag) { + // Insert the tag at the absolute start/end of the range. + RB.InsertTextAfter(B, StartTag); + RB.InsertTextBefore(E, EndTag); + + // Scan the range to see if there is a \r or \n. If so, and if the line is + // not blank, insert tags on that line as well. + bool HadOpenTag = true; + + unsigned LastNonWhiteSpace = B; + for (unsigned i = B; i != E; ++i) { + switch (BufferStart[i]) { + case '\r': + case '\n': + // Okay, we found a newline in the range. If we have an open tag, we need + // to insert a close tag at the first non-whitespace before the newline. + if (HadOpenTag) + RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag); + + // Instead of inserting an open tag immediately after the newline, we + // wait until we see a non-whitespace character. This prevents us from + // inserting tags around blank lines, and also allows the open tag to + // be put *after* whitespace on a non-blank line. + HadOpenTag = false; + break; + case '\0': + case ' ': + case '\t': + case '\f': + case '\v': + // Ignore whitespace. + break; + + default: + // If there is no tag open, do it now. + if (!HadOpenTag) { + RB.InsertTextAfter(i, StartTag); + HadOpenTag = true; + } + + // Remember this character. + LastNonWhiteSpace = i; + break; + } + } +} + +void html::EscapeText(Rewriter &R, FileID FID, + bool EscapeSpaces, bool ReplaceTabs) { + + const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); + const char* C = Buf->getBufferStart(); + const char* FileEnd = Buf->getBufferEnd(); + + assert (C <= FileEnd); + + RewriteBuffer &RB = R.getEditBuffer(FID); + + unsigned ColNo = 0; + for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) { + switch (*C) { + default: ++ColNo; break; + case '\n': + case '\r': + ColNo = 0; + break; + + case ' ': + if (EscapeSpaces) + RB.ReplaceText(FilePos, 1, " "); + ++ColNo; + break; + case '\f': + RB.ReplaceText(FilePos, 1, "
"); + ColNo = 0; + break; + + case '\t': { + if (!ReplaceTabs) + break; + unsigned NumSpaces = 8-(ColNo&7); + if (EscapeSpaces) + RB.ReplaceText(FilePos, 1, + StringRef("     " + "   ", 6*NumSpaces)); + else + RB.ReplaceText(FilePos, 1, StringRef(" ", NumSpaces)); + ColNo += NumSpaces; + break; + } + case '<': + RB.ReplaceText(FilePos, 1, "<"); + ++ColNo; + break; + + case '>': + RB.ReplaceText(FilePos, 1, ">"); + ++ColNo; + break; + + case '&': + RB.ReplaceText(FilePos, 1, "&"); + ++ColNo; + break; + } + } +} + +std::string html::EscapeText(const std::string& s, bool EscapeSpaces, + bool ReplaceTabs) { + + unsigned len = s.size(); + std::string Str; + llvm::raw_string_ostream os(Str); + + for (unsigned i = 0 ; i < len; ++i) { + + char c = s[i]; + switch (c) { + default: + os << c; break; + + case ' ': + if (EscapeSpaces) os << " "; + else os << ' '; + break; + + case '\t': + if (ReplaceTabs) { + if (EscapeSpaces) + for (unsigned i = 0; i < 4; ++i) + os << " "; + else + for (unsigned i = 0; i < 4; ++i) + os << " "; + } + else + os << c; + + break; + + case '<': os << "<"; break; + case '>': os << ">"; break; + case '&': os << "&"; break; + } + } + + return os.str(); +} + +static void AddLineNumber(RewriteBuffer &RB, unsigned LineNo, + unsigned B, unsigned E) { + SmallString<256> Str; + llvm::raw_svector_ostream OS(Str); + + OS << "" + << LineNo << ""; + + if (B == E) { // Handle empty lines. + OS << " "; + RB.InsertTextBefore(B, OS.str()); + } else { + RB.InsertTextBefore(B, OS.str()); + RB.InsertTextBefore(E, ""); + } +} + +void html::AddLineNumbers(Rewriter& R, FileID FID) { + + const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); + const char* FileBeg = Buf->getBufferStart(); + const char* FileEnd = Buf->getBufferEnd(); + const char* C = FileBeg; + RewriteBuffer &RB = R.getEditBuffer(FID); + + assert (C <= FileEnd); + + unsigned LineNo = 0; + unsigned FilePos = 0; + + while (C != FileEnd) { + + ++LineNo; + unsigned LineStartPos = FilePos; + unsigned LineEndPos = FileEnd - FileBeg; + + assert (FilePos <= LineEndPos); + assert (C < FileEnd); + + // Scan until the newline (or end-of-file). + + while (C != FileEnd) { + char c = *C; + ++C; + + if (c == '\n') { + LineEndPos = FilePos++; + break; + } + + ++FilePos; + } + + AddLineNumber(RB, LineNo, LineStartPos, LineEndPos); + } + + // Add one big table tag that surrounds all of the code. + RB.InsertTextBefore(0, "\n"); + RB.InsertTextAfter(FileEnd - FileBeg, "
"); +} + +void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID, + const char *title) { + + const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); + const char* FileStart = Buf->getBufferStart(); + const char* FileEnd = Buf->getBufferEnd(); + + SourceLocation StartLoc = R.getSourceMgr().getLocForStartOfFile(FID); + SourceLocation EndLoc = StartLoc.getLocWithOffset(FileEnd-FileStart); + + std::string s; + llvm::raw_string_ostream os(s); + os << "\n" // Use HTML 5 doctype + "\n\n"; + + if (title) + os << "" << html::EscapeText(title) << "\n"; + + os << "\n\n"; + + // Generate header + R.InsertTextBefore(StartLoc, os.str()); + // Generate footer + + R.InsertTextAfter(EndLoc, "\n"); +} + +/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with +/// information about keywords, macro expansions etc. This uses the macro +/// table state from the end of the file, so it won't be perfectly perfect, +/// but it will be reasonably close. +void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) { + RewriteBuffer &RB = R.getEditBuffer(FID); + + const SourceManager &SM = PP.getSourceManager(); + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer L(FID, FromFile, SM, PP.getLangOpts()); + const char *BufferStart = L.getBufferStart(); + + // Inform the preprocessor that we want to retain comments as tokens, so we + // can highlight them. + L.SetCommentRetentionState(true); + + // Lex all the tokens in raw mode, to avoid entering #includes or expanding + // macros. + Token Tok; + L.LexFromRawLexer(Tok); + + while (Tok.isNot(tok::eof)) { + // Since we are lexing unexpanded tokens, all tokens are from the main + // FileID. + unsigned TokOffs = SM.getFileOffset(Tok.getLocation()); + unsigned TokLen = Tok.getLength(); + switch (Tok.getKind()) { + default: break; + case tok::identifier: + llvm_unreachable("tok::identifier in raw lexing mode!"); + case tok::raw_identifier: { + // Fill in Result.IdentifierInfo and update the token kind, + // looking up the identifier in the identifier table. + PP.LookUpIdentifierInfo(Tok); + + // If this is a pp-identifier, for a keyword, highlight it as such. + if (Tok.isNot(tok::identifier)) + HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart, + "", ""); + break; + } + case tok::comment: + HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart, + "", ""); + break; + case tok::utf8_string_literal: + // Chop off the u part of u8 prefix + ++TokOffs; + --TokLen; + // FALL THROUGH to chop the 8 + case tok::wide_string_literal: + case tok::utf16_string_literal: + case tok::utf32_string_literal: + // Chop off the L, u, U or 8 prefix + ++TokOffs; + --TokLen; + // FALL THROUGH. + case tok::string_literal: + // FIXME: Exclude the optional ud-suffix from the highlighted range. + HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart, + "", ""); + break; + case tok::hash: { + // If this is a preprocessor directive, all tokens to end of line are too. + if (!Tok.isAtStartOfLine()) + break; + + // Eat all of the tokens until we get to the next one at the start of + // line. + unsigned TokEnd = TokOffs+TokLen; + L.LexFromRawLexer(Tok); + while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) { + TokEnd = SM.getFileOffset(Tok.getLocation())+Tok.getLength(); + L.LexFromRawLexer(Tok); + } + + // Find end of line. This is a hack. + HighlightRange(RB, TokOffs, TokEnd, BufferStart, + "", ""); + + // Don't skip the next token. + continue; + } + } + + L.LexFromRawLexer(Tok); + } +} + +/// HighlightMacros - This uses the macro table state from the end of the +/// file, to re-expand macros and insert (into the HTML) information about the +/// macro expansions. This won't be perfectly perfect, but it will be +/// reasonably close. +void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) { + // Re-lex the raw token stream into a token buffer. + const SourceManager &SM = PP.getSourceManager(); + std::vector TokenStream; + + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer L(FID, FromFile, SM, PP.getLangOpts()); + + // Lex all the tokens in raw mode, to avoid entering #includes or expanding + // macros. + while (1) { + Token Tok; + L.LexFromRawLexer(Tok); + + // If this is a # at the start of a line, discard it from the token stream. + // We don't want the re-preprocess step to see #defines, #includes or other + // preprocessor directives. + if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) + continue; + + // If this is a ## token, change its kind to unknown so that repreprocessing + // it will not produce an error. + if (Tok.is(tok::hashhash)) + Tok.setKind(tok::unknown); + + // If this raw token is an identifier, the raw lexer won't have looked up + // the corresponding identifier info for it. Do this now so that it will be + // macro expanded when we re-preprocess it. + if (Tok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(Tok); + + TokenStream.push_back(Tok); + + if (Tok.is(tok::eof)) break; + } + + // Temporarily change the diagnostics object so that we ignore any generated + // diagnostics from this pass. + DiagnosticsEngine TmpDiags(PP.getDiagnostics().getDiagnosticIDs(), + &PP.getDiagnostics().getDiagnosticOptions(), + new IgnoringDiagConsumer); + + // FIXME: This is a huge hack; we reuse the input preprocessor because we want + // its state, but we aren't actually changing it (we hope). This should really + // construct a copy of the preprocessor. + Preprocessor &TmpPP = const_cast(PP); + DiagnosticsEngine *OldDiags = &TmpPP.getDiagnostics(); + TmpPP.setDiagnostics(TmpDiags); + + // Inform the preprocessor that we don't want comments. + TmpPP.SetCommentRetentionState(false, false); + + // We don't want pragmas either. Although we filtered out #pragma, removing + // _Pragma and __pragma is much harder. + bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled(); + TmpPP.setPragmasEnabled(false); + + // Enter the tokens we just lexed. This will cause them to be macro expanded + // but won't enter sub-files (because we removed #'s). + TmpPP.EnterTokenStream(&TokenStream[0], TokenStream.size(), false, false); + + TokenConcatenation ConcatInfo(TmpPP); + + // Lex all the tokens. + Token Tok; + TmpPP.Lex(Tok); + while (Tok.isNot(tok::eof)) { + // Ignore non-macro tokens. + if (!Tok.getLocation().isMacroID()) { + TmpPP.Lex(Tok); + continue; + } + + // Okay, we have the first token of a macro expansion: highlight the + // expansion by inserting a start tag before the macro expansion and + // end tag after it. + std::pair LLoc = + SM.getExpansionRange(Tok.getLocation()); + + // Ignore tokens whose instantiation location was not the main file. + if (SM.getFileID(LLoc.first) != FID) { + TmpPP.Lex(Tok); + continue; + } + + assert(SM.getFileID(LLoc.second) == FID && + "Start and end of expansion must be in the same ultimate file!"); + + std::string Expansion = EscapeText(TmpPP.getSpelling(Tok)); + unsigned LineLen = Expansion.size(); + + Token PrevPrevTok; + Token PrevTok = Tok; + // Okay, eat this token, getting the next one. + TmpPP.Lex(Tok); + + // Skip all the rest of the tokens that are part of this macro + // instantiation. It would be really nice to pop up a window with all the + // spelling of the tokens or something. + while (!Tok.is(tok::eof) && + SM.getExpansionLoc(Tok.getLocation()) == LLoc.first) { + // Insert a newline if the macro expansion is getting large. + if (LineLen > 60) { + Expansion += "
"; + LineLen = 0; + } + + LineLen -= Expansion.size(); + + // If the tokens were already space separated, or if they must be to avoid + // them being implicitly pasted, add a space between them. + if (Tok.hasLeadingSpace() || + ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok)) + Expansion += ' '; + + // Escape any special characters in the token text. + Expansion += EscapeText(TmpPP.getSpelling(Tok)); + LineLen += Expansion.size(); + + PrevPrevTok = PrevTok; + PrevTok = Tok; + TmpPP.Lex(Tok); + } + + + // Insert the expansion as the end tag, so that multi-line macros all get + // highlighted. + Expansion = "" + Expansion + ""; + + HighlightRange(R, LLoc.first, LLoc.second, + "", Expansion.c_str()); + } + + // Restore the preprocessor's old state. + TmpPP.setDiagnostics(*OldDiags); + TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled); +} diff --git a/lib/Rewrite/Core/Makefile b/lib/Rewrite/Core/Makefile new file mode 100644 index 0000000000000..8c8d2e4781354 --- /dev/null +++ b/lib/Rewrite/Core/Makefile @@ -0,0 +1,18 @@ +##===- clang/lib/Rewrite/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements code transformation / rewriting facilities. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangRewriteCore + +include $(CLANG_LEVEL)/Makefile + diff --git a/lib/Rewrite/Core/RewriteRope.cpp b/lib/Rewrite/Core/RewriteRope.cpp new file mode 100644 index 0000000000000..fe7aa2d6477db --- /dev/null +++ b/lib/Rewrite/Core/RewriteRope.cpp @@ -0,0 +1,807 @@ +//===--- RewriteRope.cpp - Rope specialized for rewriter --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the RewriteRope class, which is a powerful string. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Core/RewriteRope.h" +#include "clang/Basic/LLVM.h" +#include +using namespace clang; + +/// RewriteRope is a "strong" string class, designed to make insertions and +/// deletions in the middle of the string nearly constant time (really, they are +/// O(log N), but with a very low constant factor). +/// +/// The implementation of this datastructure is a conceptual linear sequence of +/// RopePiece elements. Each RopePiece represents a view on a separately +/// allocated and reference counted string. This means that splitting a very +/// long string can be done in constant time by splitting a RopePiece that +/// references the whole string into two rope pieces that reference each half. +/// Once split, another string can be inserted in between the two halves by +/// inserting a RopePiece in between the two others. All of this is very +/// inexpensive: it takes time proportional to the number of RopePieces, not the +/// length of the strings they represent. +/// +/// While a linear sequences of RopePieces is the conceptual model, the actual +/// implementation captures them in an adapted B+ Tree. Using a B+ tree (which +/// is a tree that keeps the values in the leaves and has where each node +/// contains a reasonable number of pointers to children/values) allows us to +/// maintain efficient operation when the RewriteRope contains a *huge* number +/// of RopePieces. The basic idea of the B+ Tree is that it allows us to find +/// the RopePiece corresponding to some offset very efficiently, and it +/// automatically balances itself on insertions of RopePieces (which can happen +/// for both insertions and erases of string ranges). +/// +/// The one wrinkle on the theory is that we don't attempt to keep the tree +/// properly balanced when erases happen. Erases of string data can both insert +/// new RopePieces (e.g. when the middle of some other rope piece is deleted, +/// which results in two rope pieces, which is just like an insert) or it can +/// reduce the number of RopePieces maintained by the B+Tree. In the case when +/// the number of RopePieces is reduced, we don't attempt to maintain the +/// standard 'invariant' that each node in the tree contains at least +/// 'WidthFactor' children/values. For our use cases, this doesn't seem to +/// matter. +/// +/// The implementation below is primarily implemented in terms of three classes: +/// RopePieceBTreeNode - Common base class for: +/// +/// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece +/// nodes. This directly represents a chunk of the string with those +/// RopePieces contatenated. +/// RopePieceBTreeInterior - An interior node in the B+ Tree, which manages +/// up to '2*WidthFactor' other nodes in the tree. + + +//===----------------------------------------------------------------------===// +// RopePieceBTreeNode Class +//===----------------------------------------------------------------------===// + +namespace { + /// RopePieceBTreeNode - Common base class of RopePieceBTreeLeaf and + /// RopePieceBTreeInterior. This provides some 'virtual' dispatching methods + /// and a flag that determines which subclass the instance is. Also + /// important, this node knows the full extend of the node, including any + /// children that it has. This allows efficient skipping over entire subtrees + /// when looking for an offset in the BTree. + class RopePieceBTreeNode { + protected: + /// WidthFactor - This controls the number of K/V slots held in the BTree: + /// how wide it is. Each level of the BTree is guaranteed to have at least + /// 'WidthFactor' elements in it (either ropepieces or children), (except + /// the root, which may have less) and may have at most 2*WidthFactor + /// elements. + enum { WidthFactor = 8 }; + + /// Size - This is the number of bytes of file this node (including any + /// potential children) covers. + unsigned Size; + + /// IsLeaf - True if this is an instance of RopePieceBTreeLeaf, false if it + /// is an instance of RopePieceBTreeInterior. + bool IsLeaf; + + RopePieceBTreeNode(bool isLeaf) : Size(0), IsLeaf(isLeaf) {} + ~RopePieceBTreeNode() {} + public: + + bool isLeaf() const { return IsLeaf; } + unsigned size() const { return Size; } + + void Destroy(); + + /// split - Split the range containing the specified offset so that we are + /// guaranteed that there is a place to do an insertion at the specified + /// offset. The offset is relative, so "0" is the start of the node. + /// + /// If there is no space in this subtree for the extra piece, the extra tree + /// node is returned and must be inserted into a parent. + RopePieceBTreeNode *split(unsigned Offset); + + /// insert - Insert the specified ropepiece into this tree node at the + /// specified offset. The offset is relative, so "0" is the start of the + /// node. + /// + /// If there is no space in this subtree for the extra piece, the extra tree + /// node is returned and must be inserted into a parent. + RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); + + /// erase - Remove NumBytes from this node at the specified offset. We are + /// guaranteed that there is a split at Offset. + void erase(unsigned Offset, unsigned NumBytes); + + }; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// RopePieceBTreeLeaf Class +//===----------------------------------------------------------------------===// + +namespace { + /// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece + /// nodes. This directly represents a chunk of the string with those + /// RopePieces contatenated. Since this is a B+Tree, all values (in this case + /// instances of RopePiece) are stored in leaves like this. To make iteration + /// over the leaves efficient, they maintain a singly linked list through the + /// NextLeaf field. This allows the B+Tree forward iterator to be constant + /// time for all increments. + class RopePieceBTreeLeaf : public RopePieceBTreeNode { + /// NumPieces - This holds the number of rope pieces currently active in the + /// Pieces array. + unsigned char NumPieces; + + /// Pieces - This tracks the file chunks currently in this leaf. + /// + RopePiece Pieces[2*WidthFactor]; + + /// NextLeaf - This is a pointer to the next leaf in the tree, allowing + /// efficient in-order forward iteration of the tree without traversal. + RopePieceBTreeLeaf **PrevLeaf, *NextLeaf; + public: + RopePieceBTreeLeaf() : RopePieceBTreeNode(true), NumPieces(0), + PrevLeaf(0), NextLeaf(0) {} + ~RopePieceBTreeLeaf() { + if (PrevLeaf || NextLeaf) + removeFromLeafInOrder(); + clear(); + } + + bool isFull() const { return NumPieces == 2*WidthFactor; } + + /// clear - Remove all rope pieces from this leaf. + void clear() { + while (NumPieces) + Pieces[--NumPieces] = RopePiece(); + Size = 0; + } + + unsigned getNumPieces() const { return NumPieces; } + + const RopePiece &getPiece(unsigned i) const { + assert(i < getNumPieces() && "Invalid piece ID"); + return Pieces[i]; + } + + const RopePieceBTreeLeaf *getNextLeafInOrder() const { return NextLeaf; } + void insertAfterLeafInOrder(RopePieceBTreeLeaf *Node) { + assert(PrevLeaf == 0 && NextLeaf == 0 && "Already in ordering"); + + NextLeaf = Node->NextLeaf; + if (NextLeaf) + NextLeaf->PrevLeaf = &NextLeaf; + PrevLeaf = &Node->NextLeaf; + Node->NextLeaf = this; + } + + void removeFromLeafInOrder() { + if (PrevLeaf) { + *PrevLeaf = NextLeaf; + if (NextLeaf) + NextLeaf->PrevLeaf = PrevLeaf; + } else if (NextLeaf) { + NextLeaf->PrevLeaf = 0; + } + } + + /// FullRecomputeSizeLocally - This method recomputes the 'Size' field by + /// summing the size of all RopePieces. + void FullRecomputeSizeLocally() { + Size = 0; + for (unsigned i = 0, e = getNumPieces(); i != e; ++i) + Size += getPiece(i).size(); + } + + /// split - Split the range containing the specified offset so that we are + /// guaranteed that there is a place to do an insertion at the specified + /// offset. The offset is relative, so "0" is the start of the node. + /// + /// If there is no space in this subtree for the extra piece, the extra tree + /// node is returned and must be inserted into a parent. + RopePieceBTreeNode *split(unsigned Offset); + + /// insert - Insert the specified ropepiece into this tree node at the + /// specified offset. The offset is relative, so "0" is the start of the + /// node. + /// + /// If there is no space in this subtree for the extra piece, the extra tree + /// node is returned and must be inserted into a parent. + RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); + + + /// erase - Remove NumBytes from this node at the specified offset. We are + /// guaranteed that there is a split at Offset. + void erase(unsigned Offset, unsigned NumBytes); + + static inline bool classof(const RopePieceBTreeNode *N) { + return N->isLeaf(); + } + }; +} // end anonymous namespace + +/// split - Split the range containing the specified offset so that we are +/// guaranteed that there is a place to do an insertion at the specified +/// offset. The offset is relative, so "0" is the start of the node. +/// +/// If there is no space in this subtree for the extra piece, the extra tree +/// node is returned and must be inserted into a parent. +RopePieceBTreeNode *RopePieceBTreeLeaf::split(unsigned Offset) { + // Find the insertion point. We are guaranteed that there is a split at the + // specified offset so find it. + if (Offset == 0 || Offset == size()) { + // Fastpath for a common case. There is already a splitpoint at the end. + return 0; + } + + // Find the piece that this offset lands in. + unsigned PieceOffs = 0; + unsigned i = 0; + while (Offset >= PieceOffs+Pieces[i].size()) { + PieceOffs += Pieces[i].size(); + ++i; + } + + // If there is already a split point at the specified offset, just return + // success. + if (PieceOffs == Offset) + return 0; + + // Otherwise, we need to split piece 'i' at Offset-PieceOffs. Convert Offset + // to being Piece relative. + unsigned IntraPieceOffset = Offset-PieceOffs; + + // We do this by shrinking the RopePiece and then doing an insert of the tail. + RopePiece Tail(Pieces[i].StrData, Pieces[i].StartOffs+IntraPieceOffset, + Pieces[i].EndOffs); + Size -= Pieces[i].size(); + Pieces[i].EndOffs = Pieces[i].StartOffs+IntraPieceOffset; + Size += Pieces[i].size(); + + return insert(Offset, Tail); +} + + +/// insert - Insert the specified RopePiece into this tree node at the +/// specified offset. The offset is relative, so "0" is the start of the node. +/// +/// If there is no space in this subtree for the extra piece, the extra tree +/// node is returned and must be inserted into a parent. +RopePieceBTreeNode *RopePieceBTreeLeaf::insert(unsigned Offset, + const RopePiece &R) { + // If this node is not full, insert the piece. + if (!isFull()) { + // Find the insertion point. We are guaranteed that there is a split at the + // specified offset so find it. + unsigned i = 0, e = getNumPieces(); + if (Offset == size()) { + // Fastpath for a common case. + i = e; + } else { + unsigned SlotOffs = 0; + for (; Offset > SlotOffs; ++i) + SlotOffs += getPiece(i).size(); + assert(SlotOffs == Offset && "Split didn't occur before insertion!"); + } + + // For an insertion into a non-full leaf node, just insert the value in + // its sorted position. This requires moving later values over. + for (; i != e; --e) + Pieces[e] = Pieces[e-1]; + Pieces[i] = R; + ++NumPieces; + Size += R.size(); + return 0; + } + + // Otherwise, if this is leaf is full, split it in two halves. Since this + // node is full, it contains 2*WidthFactor values. We move the first + // 'WidthFactor' values to the LHS child (which we leave in this node) and + // move the last 'WidthFactor' values into the RHS child. + + // Create the new node. + RopePieceBTreeLeaf *NewNode = new RopePieceBTreeLeaf(); + + // Move over the last 'WidthFactor' values from here to NewNode. + std::copy(&Pieces[WidthFactor], &Pieces[2*WidthFactor], + &NewNode->Pieces[0]); + // Replace old pieces with null RopePieces to drop refcounts. + std::fill(&Pieces[WidthFactor], &Pieces[2*WidthFactor], RopePiece()); + + // Decrease the number of values in the two nodes. + NewNode->NumPieces = NumPieces = WidthFactor; + + // Recompute the two nodes' size. + NewNode->FullRecomputeSizeLocally(); + FullRecomputeSizeLocally(); + + // Update the list of leaves. + NewNode->insertAfterLeafInOrder(this); + + // These insertions can't fail. + if (this->size() >= Offset) + this->insert(Offset, R); + else + NewNode->insert(Offset - this->size(), R); + return NewNode; +} + +/// erase - Remove NumBytes from this node at the specified offset. We are +/// guaranteed that there is a split at Offset. +void RopePieceBTreeLeaf::erase(unsigned Offset, unsigned NumBytes) { + // Since we are guaranteed that there is a split at Offset, we start by + // finding the Piece that starts there. + unsigned PieceOffs = 0; + unsigned i = 0; + for (; Offset > PieceOffs; ++i) + PieceOffs += getPiece(i).size(); + assert(PieceOffs == Offset && "Split didn't occur before erase!"); + + unsigned StartPiece = i; + + // Figure out how many pieces completely cover 'NumBytes'. We want to remove + // all of them. + for (; Offset+NumBytes > PieceOffs+getPiece(i).size(); ++i) + PieceOffs += getPiece(i).size(); + + // If we exactly include the last one, include it in the region to delete. + if (Offset+NumBytes == PieceOffs+getPiece(i).size()) + PieceOffs += getPiece(i).size(), ++i; + + // If we completely cover some RopePieces, erase them now. + if (i != StartPiece) { + unsigned NumDeleted = i-StartPiece; + for (; i != getNumPieces(); ++i) + Pieces[i-NumDeleted] = Pieces[i]; + + // Drop references to dead rope pieces. + std::fill(&Pieces[getNumPieces()-NumDeleted], &Pieces[getNumPieces()], + RopePiece()); + NumPieces -= NumDeleted; + + unsigned CoverBytes = PieceOffs-Offset; + NumBytes -= CoverBytes; + Size -= CoverBytes; + } + + // If we completely removed some stuff, we could be done. + if (NumBytes == 0) return; + + // Okay, now might be erasing part of some Piece. If this is the case, then + // move the start point of the piece. + assert(getPiece(StartPiece).size() > NumBytes); + Pieces[StartPiece].StartOffs += NumBytes; + + // The size of this node just shrunk by NumBytes. + Size -= NumBytes; +} + +//===----------------------------------------------------------------------===// +// RopePieceBTreeInterior Class +//===----------------------------------------------------------------------===// + +namespace { + /// RopePieceBTreeInterior - This represents an interior node in the B+Tree, + /// which holds up to 2*WidthFactor pointers to child nodes. + class RopePieceBTreeInterior : public RopePieceBTreeNode { + /// NumChildren - This holds the number of children currently active in the + /// Children array. + unsigned char NumChildren; + RopePieceBTreeNode *Children[2*WidthFactor]; + public: + RopePieceBTreeInterior() : RopePieceBTreeNode(false), NumChildren(0) {} + + RopePieceBTreeInterior(RopePieceBTreeNode *LHS, RopePieceBTreeNode *RHS) + : RopePieceBTreeNode(false) { + Children[0] = LHS; + Children[1] = RHS; + NumChildren = 2; + Size = LHS->size() + RHS->size(); + } + + ~RopePieceBTreeInterior() { + for (unsigned i = 0, e = getNumChildren(); i != e; ++i) + Children[i]->Destroy(); + } + + bool isFull() const { return NumChildren == 2*WidthFactor; } + + unsigned getNumChildren() const { return NumChildren; } + const RopePieceBTreeNode *getChild(unsigned i) const { + assert(i < NumChildren && "invalid child #"); + return Children[i]; + } + RopePieceBTreeNode *getChild(unsigned i) { + assert(i < NumChildren && "invalid child #"); + return Children[i]; + } + + /// FullRecomputeSizeLocally - Recompute the Size field of this node by + /// summing up the sizes of the child nodes. + void FullRecomputeSizeLocally() { + Size = 0; + for (unsigned i = 0, e = getNumChildren(); i != e; ++i) + Size += getChild(i)->size(); + } + + + /// split - Split the range containing the specified offset so that we are + /// guaranteed that there is a place to do an insertion at the specified + /// offset. The offset is relative, so "0" is the start of the node. + /// + /// If there is no space in this subtree for the extra piece, the extra tree + /// node is returned and must be inserted into a parent. + RopePieceBTreeNode *split(unsigned Offset); + + + /// insert - Insert the specified ropepiece into this tree node at the + /// specified offset. The offset is relative, so "0" is the start of the + /// node. + /// + /// If there is no space in this subtree for the extra piece, the extra tree + /// node is returned and must be inserted into a parent. + RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); + + /// HandleChildPiece - A child propagated an insertion result up to us. + /// Insert the new child, and/or propagate the result further up the tree. + RopePieceBTreeNode *HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS); + + /// erase - Remove NumBytes from this node at the specified offset. We are + /// guaranteed that there is a split at Offset. + void erase(unsigned Offset, unsigned NumBytes); + + static inline bool classof(const RopePieceBTreeNode *N) { + return !N->isLeaf(); + } + }; +} // end anonymous namespace + +/// split - Split the range containing the specified offset so that we are +/// guaranteed that there is a place to do an insertion at the specified +/// offset. The offset is relative, so "0" is the start of the node. +/// +/// If there is no space in this subtree for the extra piece, the extra tree +/// node is returned and must be inserted into a parent. +RopePieceBTreeNode *RopePieceBTreeInterior::split(unsigned Offset) { + // Figure out which child to split. + if (Offset == 0 || Offset == size()) + return 0; // If we have an exact offset, we're already split. + + unsigned ChildOffset = 0; + unsigned i = 0; + for (; Offset >= ChildOffset+getChild(i)->size(); ++i) + ChildOffset += getChild(i)->size(); + + // If already split there, we're done. + if (ChildOffset == Offset) + return 0; + + // Otherwise, recursively split the child. + if (RopePieceBTreeNode *RHS = getChild(i)->split(Offset-ChildOffset)) + return HandleChildPiece(i, RHS); + return 0; // Done! +} + +/// insert - Insert the specified ropepiece into this tree node at the +/// specified offset. The offset is relative, so "0" is the start of the +/// node. +/// +/// If there is no space in this subtree for the extra piece, the extra tree +/// node is returned and must be inserted into a parent. +RopePieceBTreeNode *RopePieceBTreeInterior::insert(unsigned Offset, + const RopePiece &R) { + // Find the insertion point. We are guaranteed that there is a split at the + // specified offset so find it. + unsigned i = 0, e = getNumChildren(); + + unsigned ChildOffs = 0; + if (Offset == size()) { + // Fastpath for a common case. Insert at end of last child. + i = e-1; + ChildOffs = size()-getChild(i)->size(); + } else { + for (; Offset > ChildOffs+getChild(i)->size(); ++i) + ChildOffs += getChild(i)->size(); + } + + Size += R.size(); + + // Insert at the end of this child. + if (RopePieceBTreeNode *RHS = getChild(i)->insert(Offset-ChildOffs, R)) + return HandleChildPiece(i, RHS); + + return 0; +} + +/// HandleChildPiece - A child propagated an insertion result up to us. +/// Insert the new child, and/or propagate the result further up the tree. +RopePieceBTreeNode * +RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) { + // Otherwise the child propagated a subtree up to us as a new child. See if + // we have space for it here. + if (!isFull()) { + // Insert RHS after child 'i'. + if (i + 1 != getNumChildren()) + memmove(&Children[i+2], &Children[i+1], + (getNumChildren()-i-1)*sizeof(Children[0])); + Children[i+1] = RHS; + ++NumChildren; + return 0; + } + + // Okay, this node is full. Split it in half, moving WidthFactor children to + // a newly allocated interior node. + + // Create the new node. + RopePieceBTreeInterior *NewNode = new RopePieceBTreeInterior(); + + // Move over the last 'WidthFactor' values from here to NewNode. + memcpy(&NewNode->Children[0], &Children[WidthFactor], + WidthFactor*sizeof(Children[0])); + + // Decrease the number of values in the two nodes. + NewNode->NumChildren = NumChildren = WidthFactor; + + // Finally, insert the two new children in the side the can (now) hold them. + // These insertions can't fail. + if (i < WidthFactor) + this->HandleChildPiece(i, RHS); + else + NewNode->HandleChildPiece(i-WidthFactor, RHS); + + // Recompute the two nodes' size. + NewNode->FullRecomputeSizeLocally(); + FullRecomputeSizeLocally(); + return NewNode; +} + +/// erase - Remove NumBytes from this node at the specified offset. We are +/// guaranteed that there is a split at Offset. +void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) { + // This will shrink this node by NumBytes. + Size -= NumBytes; + + // Find the first child that overlaps with Offset. + unsigned i = 0; + for (; Offset >= getChild(i)->size(); ++i) + Offset -= getChild(i)->size(); + + // Propagate the delete request into overlapping children, or completely + // delete the children as appropriate. + while (NumBytes) { + RopePieceBTreeNode *CurChild = getChild(i); + + // If we are deleting something contained entirely in the child, pass on the + // request. + if (Offset+NumBytes < CurChild->size()) { + CurChild->erase(Offset, NumBytes); + return; + } + + // If this deletion request starts somewhere in the middle of the child, it + // must be deleting to the end of the child. + if (Offset) { + unsigned BytesFromChild = CurChild->size()-Offset; + CurChild->erase(Offset, BytesFromChild); + NumBytes -= BytesFromChild; + // Start at the beginning of the next child. + Offset = 0; + ++i; + continue; + } + + // If the deletion request completely covers the child, delete it and move + // the rest down. + NumBytes -= CurChild->size(); + CurChild->Destroy(); + --NumChildren; + if (i != getNumChildren()) + memmove(&Children[i], &Children[i+1], + (getNumChildren()-i)*sizeof(Children[0])); + } +} + +//===----------------------------------------------------------------------===// +// RopePieceBTreeNode Implementation +//===----------------------------------------------------------------------===// + +void RopePieceBTreeNode::Destroy() { + if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) + delete Leaf; + else + delete cast(this); +} + +/// split - Split the range containing the specified offset so that we are +/// guaranteed that there is a place to do an insertion at the specified +/// offset. The offset is relative, so "0" is the start of the node. +/// +/// If there is no space in this subtree for the extra piece, the extra tree +/// node is returned and must be inserted into a parent. +RopePieceBTreeNode *RopePieceBTreeNode::split(unsigned Offset) { + assert(Offset <= size() && "Invalid offset to split!"); + if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) + return Leaf->split(Offset); + return cast(this)->split(Offset); +} + +/// insert - Insert the specified ropepiece into this tree node at the +/// specified offset. The offset is relative, so "0" is the start of the +/// node. +/// +/// If there is no space in this subtree for the extra piece, the extra tree +/// node is returned and must be inserted into a parent. +RopePieceBTreeNode *RopePieceBTreeNode::insert(unsigned Offset, + const RopePiece &R) { + assert(Offset <= size() && "Invalid offset to insert!"); + if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) + return Leaf->insert(Offset, R); + return cast(this)->insert(Offset, R); +} + +/// erase - Remove NumBytes from this node at the specified offset. We are +/// guaranteed that there is a split at Offset. +void RopePieceBTreeNode::erase(unsigned Offset, unsigned NumBytes) { + assert(Offset+NumBytes <= size() && "Invalid offset to erase!"); + if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) + return Leaf->erase(Offset, NumBytes); + return cast(this)->erase(Offset, NumBytes); +} + + +//===----------------------------------------------------------------------===// +// RopePieceBTreeIterator Implementation +//===----------------------------------------------------------------------===// + +static const RopePieceBTreeLeaf *getCN(const void *P) { + return static_cast(P); +} + +// begin iterator. +RopePieceBTreeIterator::RopePieceBTreeIterator(const void *n) { + const RopePieceBTreeNode *N = static_cast(n); + + // Walk down the left side of the tree until we get to a leaf. + while (const RopePieceBTreeInterior *IN = dyn_cast(N)) + N = IN->getChild(0); + + // We must have at least one leaf. + CurNode = cast(N); + + // If we found a leaf that happens to be empty, skip over it until we get + // to something full. + while (CurNode && getCN(CurNode)->getNumPieces() == 0) + CurNode = getCN(CurNode)->getNextLeafInOrder(); + + if (CurNode != 0) + CurPiece = &getCN(CurNode)->getPiece(0); + else // Empty tree, this is an end() iterator. + CurPiece = 0; + CurChar = 0; +} + +void RopePieceBTreeIterator::MoveToNextPiece() { + if (CurPiece != &getCN(CurNode)->getPiece(getCN(CurNode)->getNumPieces()-1)) { + CurChar = 0; + ++CurPiece; + return; + } + + // Find the next non-empty leaf node. + do + CurNode = getCN(CurNode)->getNextLeafInOrder(); + while (CurNode && getCN(CurNode)->getNumPieces() == 0); + + if (CurNode != 0) + CurPiece = &getCN(CurNode)->getPiece(0); + else // Hit end(). + CurPiece = 0; + CurChar = 0; +} + +//===----------------------------------------------------------------------===// +// RopePieceBTree Implementation +//===----------------------------------------------------------------------===// + +static RopePieceBTreeNode *getRoot(void *P) { + return static_cast(P); +} + +RopePieceBTree::RopePieceBTree() { + Root = new RopePieceBTreeLeaf(); +} +RopePieceBTree::RopePieceBTree(const RopePieceBTree &RHS) { + assert(RHS.empty() && "Can't copy non-empty tree yet"); + Root = new RopePieceBTreeLeaf(); +} +RopePieceBTree::~RopePieceBTree() { + getRoot(Root)->Destroy(); +} + +unsigned RopePieceBTree::size() const { + return getRoot(Root)->size(); +} + +void RopePieceBTree::clear() { + if (RopePieceBTreeLeaf *Leaf = dyn_cast(getRoot(Root))) + Leaf->clear(); + else { + getRoot(Root)->Destroy(); + Root = new RopePieceBTreeLeaf(); + } +} + +void RopePieceBTree::insert(unsigned Offset, const RopePiece &R) { + // #1. Split at Offset. + if (RopePieceBTreeNode *RHS = getRoot(Root)->split(Offset)) + Root = new RopePieceBTreeInterior(getRoot(Root), RHS); + + // #2. Do the insertion. + if (RopePieceBTreeNode *RHS = getRoot(Root)->insert(Offset, R)) + Root = new RopePieceBTreeInterior(getRoot(Root), RHS); +} + +void RopePieceBTree::erase(unsigned Offset, unsigned NumBytes) { + // #1. Split at Offset. + if (RopePieceBTreeNode *RHS = getRoot(Root)->split(Offset)) + Root = new RopePieceBTreeInterior(getRoot(Root), RHS); + + // #2. Do the erasing. + getRoot(Root)->erase(Offset, NumBytes); +} + +//===----------------------------------------------------------------------===// +// RewriteRope Implementation +//===----------------------------------------------------------------------===// + +/// MakeRopeString - This copies the specified byte range into some instance of +/// RopeRefCountString, and return a RopePiece that represents it. This uses +/// the AllocBuffer object to aggregate requests for small strings into one +/// allocation instead of doing tons of tiny allocations. +RopePiece RewriteRope::MakeRopeString(const char *Start, const char *End) { + unsigned Len = End-Start; + assert(Len && "Zero length RopePiece is invalid!"); + + // If we have space for this string in the current alloc buffer, use it. + if (AllocOffs+Len <= AllocChunkSize) { + memcpy(AllocBuffer->Data+AllocOffs, Start, Len); + AllocOffs += Len; + return RopePiece(AllocBuffer, AllocOffs-Len, AllocOffs); + } + + // If we don't have enough room because this specific allocation is huge, + // just allocate a new rope piece for it alone. + if (Len > AllocChunkSize) { + unsigned Size = End-Start+sizeof(RopeRefCountString)-1; + RopeRefCountString *Res = + reinterpret_cast(new char[Size]); + Res->RefCount = 0; + memcpy(Res->Data, Start, End-Start); + return RopePiece(Res, 0, End-Start); + } + + // Otherwise, this was a small request but we just don't have space for it + // Make a new chunk and share it with later allocations. + + // If we had an old allocation, drop our reference to it. + if (AllocBuffer && --AllocBuffer->RefCount == 0) + delete [] (char*)AllocBuffer; + + unsigned AllocSize = offsetof(RopeRefCountString, Data) + AllocChunkSize; + AllocBuffer = reinterpret_cast(new char[AllocSize]); + AllocBuffer->RefCount = 0; + memcpy(AllocBuffer->Data, Start, Len); + AllocOffs = Len; + + // Start out the new allocation with a refcount of 1, since we have an + // internal reference to it. + AllocBuffer->addRef(); + return RopePiece(AllocBuffer, 0, Len); +} + + diff --git a/lib/Rewrite/Core/Rewriter.cpp b/lib/Rewrite/Core/Rewriter.cpp new file mode 100644 index 0000000000000..4df967f39bc0e --- /dev/null +++ b/lib/Rewrite/Core/Rewriter.cpp @@ -0,0 +1,486 @@ +//===--- Rewriter.cpp - Code rewriting interface --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Rewriter class, which is used for code +// transformations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/FileSystem.h" +using namespace clang; + +raw_ostream &RewriteBuffer::write(raw_ostream &os) const { + // FIXME: eliminate the copy by writing out each chunk at a time + os << std::string(begin(), end()); + return os; +} + +/// \brief Return true if this character is non-new-line whitespace: +/// ' ', '\\t', '\\f', '\\v', '\\r'. +static inline bool isWhitespace(unsigned char c) { + switch (c) { + case ' ': + case '\t': + case '\f': + case '\v': + case '\r': + return true; + default: + return false; + } +} + +void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, + bool removeLineIfEmpty) { + // Nothing to remove, exit early. + if (Size == 0) return; + + unsigned RealOffset = getMappedOffset(OrigOffset, true); + assert(RealOffset+Size < Buffer.size() && "Invalid location"); + + // Remove the dead characters. + Buffer.erase(RealOffset, Size); + + // Add a delta so that future changes are offset correctly. + AddReplaceDelta(OrigOffset, -Size); + + if (removeLineIfEmpty) { + // Find the line that the remove occurred and if it is completely empty + // remove the line as well. + + iterator curLineStart = begin(); + unsigned curLineStartOffs = 0; + iterator posI = begin(); + for (unsigned i = 0; i != RealOffset; ++i) { + if (*posI == '\n') { + curLineStart = posI; + ++curLineStart; + curLineStartOffs = i + 1; + } + ++posI; + } + + unsigned lineSize = 0; + posI = curLineStart; + while (posI != end() && isWhitespace(*posI)) { + ++posI; + ++lineSize; + } + if (posI != end() && *posI == '\n') { + Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/); + AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/)); + } + } +} + +void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, + bool InsertAfter) { + + // Nothing to insert, exit early. + if (Str.empty()) return; + + unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); + Buffer.insert(RealOffset, Str.begin(), Str.end()); + + // Add a delta so that future changes are offset correctly. + AddInsertDelta(OrigOffset, Str.size()); +} + +/// ReplaceText - This method replaces a range of characters in the input +/// buffer with a new string. This is effectively a combined "remove+insert" +/// operation. +void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, + StringRef NewStr) { + unsigned RealOffset = getMappedOffset(OrigOffset, true); + Buffer.erase(RealOffset, OrigLength); + Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); + if (OrigLength != NewStr.size()) + AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); +} + + +//===----------------------------------------------------------------------===// +// Rewriter class +//===----------------------------------------------------------------------===// + +/// getRangeSize - Return the size in bytes of the specified range if they +/// are in the same file. If not, this returns -1. +int Rewriter::getRangeSize(const CharSourceRange &Range, + RewriteOptions opts) const { + if (!isRewritable(Range.getBegin()) || + !isRewritable(Range.getEnd())) return -1; + + FileID StartFileID, EndFileID; + unsigned StartOff, EndOff; + + StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); + EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); + + if (StartFileID != EndFileID) + return -1; + + // If edits have been made to this buffer, the delta between the range may + // have changed. + std::map::const_iterator I = + RewriteBuffers.find(StartFileID); + if (I != RewriteBuffers.end()) { + const RewriteBuffer &RB = I->second; + EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange); + StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange); + } + + + // Adjust the end offset to the end of the last token, instead of being the + // start of the last token if this is a token range. + if (Range.isTokenRange()) + EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); + + return EndOff-StartOff; +} + +int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const { + return getRangeSize(CharSourceRange::getTokenRange(Range), opts); +} + + +/// getRewrittenText - Return the rewritten form of the text in the specified +/// range. If the start or end of the range was unrewritable or if they are +/// in different buffers, this returns an empty string. +/// +/// Note that this method is not particularly efficient. +/// +std::string Rewriter::getRewrittenText(SourceRange Range) const { + if (!isRewritable(Range.getBegin()) || + !isRewritable(Range.getEnd())) + return ""; + + FileID StartFileID, EndFileID; + unsigned StartOff, EndOff; + StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); + EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); + + if (StartFileID != EndFileID) + return ""; // Start and end in different buffers. + + // If edits have been made to this buffer, the delta between the range may + // have changed. + std::map::const_iterator I = + RewriteBuffers.find(StartFileID); + if (I == RewriteBuffers.end()) { + // If the buffer hasn't been rewritten, just return the text from the input. + const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); + + // Adjust the end offset to the end of the last token, instead of being the + // start of the last token. + EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); + return std::string(Ptr, Ptr+EndOff-StartOff); + } + + const RewriteBuffer &RB = I->second; + EndOff = RB.getMappedOffset(EndOff, true); + StartOff = RB.getMappedOffset(StartOff); + + // Adjust the end offset to the end of the last token, instead of being the + // start of the last token. + EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); + + // Advance the iterators to the right spot, yay for linear time algorithms. + RewriteBuffer::iterator Start = RB.begin(); + std::advance(Start, StartOff); + RewriteBuffer::iterator End = Start; + std::advance(End, EndOff-StartOff); + + return std::string(Start, End); +} + +unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, + FileID &FID) const { + assert(Loc.isValid() && "Invalid location"); + std::pair V = SourceMgr->getDecomposedLoc(Loc); + FID = V.first; + return V.second; +} + + +/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. +/// +RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { + std::map::iterator I = + RewriteBuffers.lower_bound(FID); + if (I != RewriteBuffers.end() && I->first == FID) + return I->second; + I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); + + StringRef MB = SourceMgr->getBufferData(FID); + I->second.Initialize(MB.begin(), MB.end()); + + return I->second; +} + +/// InsertText - Insert the specified string at the specified location in the +/// original buffer. +bool Rewriter::InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter, bool indentNewLines) { + if (!isRewritable(Loc)) return true; + FileID FID; + unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); + + SmallString<128> indentedStr; + if (indentNewLines && Str.find('\n') != StringRef::npos) { + StringRef MB = SourceMgr->getBufferData(FID); + + unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; + const SrcMgr::ContentCache * + Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); + unsigned lineOffs = Content->SourceLineCache[lineNo]; + + // Find the whitespace at the start of the line. + StringRef indentSpace; + { + unsigned i = lineOffs; + while (isWhitespace(MB[i])) + ++i; + indentSpace = MB.substr(lineOffs, i-lineOffs); + } + + SmallVector lines; + Str.split(lines, "\n"); + + for (unsigned i = 0, e = lines.size(); i != e; ++i) { + indentedStr += lines[i]; + if (i < e-1) { + indentedStr += '\n'; + indentedStr += indentSpace; + } + } + Str = indentedStr.str(); + } + + getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); + return false; +} + +bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) { + if (!isRewritable(Loc)) return true; + FileID FID; + unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); + RewriteOptions rangeOpts; + rangeOpts.IncludeInsertsAtBeginOfRange = false; + StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts); + getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true); + return false; +} + +/// RemoveText - Remove the specified text region. +bool Rewriter::RemoveText(SourceLocation Start, unsigned Length, + RewriteOptions opts) { + if (!isRewritable(Start)) return true; + FileID FID; + unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); + getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty); + return false; +} + +/// ReplaceText - This method replaces a range of characters in the input +/// buffer with a new string. This is effectively a combined "remove/insert" +/// operation. +bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef NewStr) { + if (!isRewritable(Start)) return true; + FileID StartFileID; + unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); + + getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr); + return false; +} + +bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) { + if (!isRewritable(range.getBegin())) return true; + if (!isRewritable(range.getEnd())) return true; + if (replacementRange.isInvalid()) return true; + SourceLocation start = range.getBegin(); + unsigned origLength = getRangeSize(range); + unsigned newLength = getRangeSize(replacementRange); + FileID FID; + unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(), + FID); + StringRef MB = SourceMgr->getBufferData(FID); + return ReplaceText(start, origLength, MB.substr(newOffs, newLength)); +} + +/// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty +/// printer to generate the replacement code. This returns true if the input +/// could not be rewritten, or false if successful. +bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) { + // Measaure the old text. + int Size = getRangeSize(From->getSourceRange()); + if (Size == -1) + return true; + + // Get the new text. + std::string SStr; + llvm::raw_string_ostream S(SStr); + To->printPretty(S, 0, PrintingPolicy(*LangOpts)); + const std::string &Str = S.str(); + + ReplaceText(From->getLocStart(), Size, Str); + return false; +} + +std::string Rewriter::ConvertToString(Stmt *From) { + std::string SStr; + llvm::raw_string_ostream S(SStr); + From->printPretty(S, 0, PrintingPolicy(*LangOpts)); + return S.str(); +} + +bool Rewriter::IncreaseIndentation(CharSourceRange range, + SourceLocation parentIndent) { + if (range.isInvalid()) return true; + if (!isRewritable(range.getBegin())) return true; + if (!isRewritable(range.getEnd())) return true; + if (!isRewritable(parentIndent)) return true; + + FileID StartFileID, EndFileID, parentFileID; + unsigned StartOff, EndOff, parentOff; + + StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID); + EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID); + parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID); + + if (StartFileID != EndFileID || StartFileID != parentFileID) + return true; + if (StartOff > EndOff) + return true; + + FileID FID = StartFileID; + StringRef MB = SourceMgr->getBufferData(FID); + + unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1; + unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1; + unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1; + + const SrcMgr::ContentCache * + Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); + + // Find where the lines start. + unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; + unsigned startLineOffs = Content->SourceLineCache[startLineNo]; + + // Find the whitespace at the start of each line. + StringRef parentSpace, startSpace; + { + unsigned i = parentLineOffs; + while (isWhitespace(MB[i])) + ++i; + parentSpace = MB.substr(parentLineOffs, i-parentLineOffs); + + i = startLineOffs; + while (isWhitespace(MB[i])) + ++i; + startSpace = MB.substr(startLineOffs, i-startLineOffs); + } + if (parentSpace.size() >= startSpace.size()) + return true; + if (!startSpace.startswith(parentSpace)) + return true; + + StringRef indent = startSpace.substr(parentSpace.size()); + + // Indent the lines between start/end offsets. + RewriteBuffer &RB = getEditBuffer(FID); + for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { + unsigned offs = Content->SourceLineCache[lineNo]; + unsigned i = offs; + while (isWhitespace(MB[i])) + ++i; + StringRef origIndent = MB.substr(offs, i-offs); + if (origIndent.startswith(startSpace)) + RB.InsertText(offs, indent, /*InsertAfter=*/false); + } + + return false; +} + +// A wrapper for a file stream that atomically overwrites the target. +// +// Creates a file output stream for a temporary file in the constructor, +// which is later accessible via getStream() if ok() return true. +// Flushes the stream and moves the temporary file to the target location +// in the destructor. +class AtomicallyMovedFile { +public: + AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename, + bool &AllWritten) + : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) { + TempFilename = Filename; + TempFilename += "-%%%%%%%%"; + int FD; + if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename, + /*makeAbsolute=*/true, 0664)) { + AllWritten = false; + Diagnostics.Report(clang::diag::err_unable_to_make_temp) + << TempFilename; + } else { + FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true)); + } + } + + ~AtomicallyMovedFile() { + if (!ok()) return; + + FileStream->flush(); +#ifdef _WIN32 + // Win32 does not allow rename/removing opened files. + FileStream.reset(); +#endif + if (llvm::error_code ec = + llvm::sys::fs::rename(TempFilename.str(), Filename)) { + AllWritten = false; + Diagnostics.Report(clang::diag::err_unable_to_rename_temp) + << TempFilename << Filename << ec.message(); + bool existed; + // If the remove fails, there's not a lot we can do - this is already an + // error. + llvm::sys::fs::remove(TempFilename.str(), existed); + } + } + + bool ok() { return FileStream; } + llvm::raw_ostream &getStream() { return *FileStream; } + +private: + DiagnosticsEngine &Diagnostics; + StringRef Filename; + SmallString<128> TempFilename; + OwningPtr FileStream; + bool &AllWritten; +}; + +bool Rewriter::overwriteChangedFiles() { + bool AllWritten = true; + for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { + const FileEntry *Entry = + getSourceMgr().getFileEntryForID(I->first); + AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(), + AllWritten); + if (File.ok()) { + I->second.write(File.getStream()); + } + } + return !AllWritten; +} diff --git a/lib/Rewrite/Core/TokenRewriter.cpp b/lib/Rewrite/Core/TokenRewriter.cpp new file mode 100644 index 0000000000000..940ece2f9e039 --- /dev/null +++ b/lib/Rewrite/Core/TokenRewriter.cpp @@ -0,0 +1,99 @@ +//===--- TokenRewriter.cpp - Token-based code rewriting interface ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the TokenRewriter class, which is used for code +// transformations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Core/TokenRewriter.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/ScratchBuffer.h" +#include "clang/Basic/SourceManager.h" +using namespace clang; + +TokenRewriter::TokenRewriter(FileID FID, SourceManager &SM, + const LangOptions &LangOpts) { + ScratchBuf.reset(new ScratchBuffer(SM)); + + // Create a lexer to lex all the tokens of the main file in raw mode. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer RawLex(FID, FromFile, SM, LangOpts); + + // Return all comments and whitespace as tokens. + RawLex.SetKeepWhitespaceMode(true); + + // Lex the file, populating our datastructures. + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { +#if 0 + if (Tok.is(tok::raw_identifier)) { + // Look up the identifier info for the token. This should use + // IdentifierTable directly instead of PP. + PP.LookUpIdentifierInfo(Tok); + } +#endif + + AddToken(RawTok, TokenList.end()); + RawLex.LexFromRawLexer(RawTok); + } +} + +TokenRewriter::~TokenRewriter() { +} + + +/// RemapIterator - Convert from token_iterator (a const iterator) to +/// TokenRefTy (a non-const iterator). +TokenRewriter::TokenRefTy TokenRewriter::RemapIterator(token_iterator I) { + if (I == token_end()) return TokenList.end(); + + // FIXME: This is horrible, we should use our own list or something to avoid + // this. + std::map::iterator MapIt = + TokenAtLoc.find(I->getLocation()); + assert(MapIt != TokenAtLoc.end() && "iterator not in rewriter?"); + return MapIt->second; +} + + +/// AddToken - Add the specified token into the Rewriter before the other +/// position. +TokenRewriter::TokenRefTy +TokenRewriter::AddToken(const Token &T, TokenRefTy Where) { + Where = TokenList.insert(Where, T); + + bool InsertSuccess = TokenAtLoc.insert(std::make_pair(T.getLocation(), + Where)).second; + assert(InsertSuccess && "Token location already in rewriter!"); + (void)InsertSuccess; + return Where; +} + + +TokenRewriter::token_iterator +TokenRewriter::AddTokenBefore(token_iterator I, const char *Val) { + unsigned Len = strlen(Val); + + // Plop the string into the scratch buffer, then create a token for this + // string. + Token Tok; + Tok.startToken(); + const char *Spelling; + Tok.setLocation(ScratchBuf->getToken(Val, Len, Spelling)); + Tok.setLength(Len); + + // TODO: Form a whole lexer around this and relex the token! For now, just + // set kind to tok::unknown. + Tok.setKind(tok::unknown); + + return AddToken(Tok, RemapIterator(I)); +} + diff --git a/lib/Rewrite/DeltaTree.cpp b/lib/Rewrite/DeltaTree.cpp deleted file mode 100644 index 4297dc8de62fb..0000000000000 --- a/lib/Rewrite/DeltaTree.cpp +++ /dev/null @@ -1,467 +0,0 @@ -//===--- DeltaTree.cpp - B-Tree for Rewrite Delta tracking ----------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the DeltaTree and related classes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/DeltaTree.h" -#include "clang/Basic/LLVM.h" -#include -#include -using namespace clang; - -/// The DeltaTree class is a multiway search tree (BTree) structure with some -/// fancy features. B-Trees are generally more memory and cache efficient -/// than binary trees, because they store multiple keys/values in each node. -/// -/// DeltaTree implements a key/value mapping from FileIndex to Delta, allowing -/// fast lookup by FileIndex. However, an added (important) bonus is that it -/// can also efficiently tell us the full accumulated delta for a specific -/// file offset as well, without traversing the whole tree. -/// -/// The nodes of the tree are made up of instances of two classes: -/// DeltaTreeNode and DeltaTreeInteriorNode. The later subclasses the -/// former and adds children pointers. Each node knows the full delta of all -/// entries (recursively) contained inside of it, which allows us to get the -/// full delta implied by a whole subtree in constant time. - -namespace { - /// SourceDelta - As code in the original input buffer is added and deleted, - /// SourceDelta records are used to keep track of how the input SourceLocation - /// object is mapped into the output buffer. - struct SourceDelta { - unsigned FileLoc; - int Delta; - - static SourceDelta get(unsigned Loc, int D) { - SourceDelta Delta; - Delta.FileLoc = Loc; - Delta.Delta = D; - return Delta; - } - }; - - /// DeltaTreeNode - The common part of all nodes. - /// - class DeltaTreeNode { - public: - struct InsertResult { - DeltaTreeNode *LHS, *RHS; - SourceDelta Split; - }; - - private: - friend class DeltaTreeInteriorNode; - - /// WidthFactor - This controls the number of K/V slots held in the BTree: - /// how wide it is. Each level of the BTree is guaranteed to have at least - /// WidthFactor-1 K/V pairs (except the root) and may have at most - /// 2*WidthFactor-1 K/V pairs. - enum { WidthFactor = 8 }; - - /// Values - This tracks the SourceDelta's currently in this node. - /// - SourceDelta Values[2*WidthFactor-1]; - - /// NumValuesUsed - This tracks the number of values this node currently - /// holds. - unsigned char NumValuesUsed; - - /// IsLeaf - This is true if this is a leaf of the btree. If false, this is - /// an interior node, and is actually an instance of DeltaTreeInteriorNode. - bool IsLeaf; - - /// FullDelta - This is the full delta of all the values in this node and - /// all children nodes. - int FullDelta; - public: - DeltaTreeNode(bool isLeaf = true) - : NumValuesUsed(0), IsLeaf(isLeaf), FullDelta(0) {} - - bool isLeaf() const { return IsLeaf; } - int getFullDelta() const { return FullDelta; } - bool isFull() const { return NumValuesUsed == 2*WidthFactor-1; } - - unsigned getNumValuesUsed() const { return NumValuesUsed; } - const SourceDelta &getValue(unsigned i) const { - assert(i < NumValuesUsed && "Invalid value #"); - return Values[i]; - } - SourceDelta &getValue(unsigned i) { - assert(i < NumValuesUsed && "Invalid value #"); - return Values[i]; - } - - /// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into - /// this node. If insertion is easy, do it and return false. Otherwise, - /// split the node, populate InsertRes with info about the split, and return - /// true. - bool DoInsertion(unsigned FileIndex, int Delta, InsertResult *InsertRes); - - void DoSplit(InsertResult &InsertRes); - - - /// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a - /// local walk over our contained deltas. - void RecomputeFullDeltaLocally(); - - void Destroy(); - - //static inline bool classof(const DeltaTreeNode *) { return true; } - }; -} // end anonymous namespace - -namespace { - /// DeltaTreeInteriorNode - When isLeaf = false, a node has child pointers. - /// This class tracks them. - class DeltaTreeInteriorNode : public DeltaTreeNode { - DeltaTreeNode *Children[2*WidthFactor]; - ~DeltaTreeInteriorNode() { - for (unsigned i = 0, e = NumValuesUsed+1; i != e; ++i) - Children[i]->Destroy(); - } - friend class DeltaTreeNode; - public: - DeltaTreeInteriorNode() : DeltaTreeNode(false /*nonleaf*/) {} - - DeltaTreeInteriorNode(const InsertResult &IR) - : DeltaTreeNode(false /*nonleaf*/) { - Children[0] = IR.LHS; - Children[1] = IR.RHS; - Values[0] = IR.Split; - FullDelta = IR.LHS->getFullDelta()+IR.RHS->getFullDelta()+IR.Split.Delta; - NumValuesUsed = 1; - } - - const DeltaTreeNode *getChild(unsigned i) const { - assert(i < getNumValuesUsed()+1 && "Invalid child"); - return Children[i]; - } - DeltaTreeNode *getChild(unsigned i) { - assert(i < getNumValuesUsed()+1 && "Invalid child"); - return Children[i]; - } - - //static inline bool classof(const DeltaTreeInteriorNode *) { return true; } - static inline bool classof(const DeltaTreeNode *N) { return !N->isLeaf(); } - }; -} - - -/// Destroy - A 'virtual' destructor. -void DeltaTreeNode::Destroy() { - if (isLeaf()) - delete this; - else - delete cast(this); -} - -/// RecomputeFullDeltaLocally - Recompute the FullDelta field by doing a -/// local walk over our contained deltas. -void DeltaTreeNode::RecomputeFullDeltaLocally() { - int NewFullDelta = 0; - for (unsigned i = 0, e = getNumValuesUsed(); i != e; ++i) - NewFullDelta += Values[i].Delta; - if (DeltaTreeInteriorNode *IN = dyn_cast(this)) - for (unsigned i = 0, e = getNumValuesUsed()+1; i != e; ++i) - NewFullDelta += IN->getChild(i)->getFullDelta(); - FullDelta = NewFullDelta; -} - -/// DoInsertion - Do an insertion of the specified FileIndex/Delta pair into -/// this node. If insertion is easy, do it and return false. Otherwise, -/// split the node, populate InsertRes with info about the split, and return -/// true. -bool DeltaTreeNode::DoInsertion(unsigned FileIndex, int Delta, - InsertResult *InsertRes) { - // Maintain full delta for this node. - FullDelta += Delta; - - // Find the insertion point, the first delta whose index is >= FileIndex. - unsigned i = 0, e = getNumValuesUsed(); - while (i != e && FileIndex > getValue(i).FileLoc) - ++i; - - // If we found an a record for exactly this file index, just merge this - // value into the pre-existing record and finish early. - if (i != e && getValue(i).FileLoc == FileIndex) { - // NOTE: Delta could drop to zero here. This means that the delta entry is - // useless and could be removed. Supporting erases is more complex than - // leaving an entry with Delta=0, so we just leave an entry with Delta=0 in - // the tree. - Values[i].Delta += Delta; - return false; - } - - // Otherwise, we found an insertion point, and we know that the value at the - // specified index is > FileIndex. Handle the leaf case first. - if (isLeaf()) { - if (!isFull()) { - // For an insertion into a non-full leaf node, just insert the value in - // its sorted position. This requires moving later values over. - if (i != e) - memmove(&Values[i+1], &Values[i], sizeof(Values[0])*(e-i)); - Values[i] = SourceDelta::get(FileIndex, Delta); - ++NumValuesUsed; - return false; - } - - // Otherwise, if this is leaf is full, split the node at its median, insert - // the value into one of the children, and return the result. - assert(InsertRes && "No result location specified"); - DoSplit(*InsertRes); - - if (InsertRes->Split.FileLoc > FileIndex) - InsertRes->LHS->DoInsertion(FileIndex, Delta, 0 /*can't fail*/); - else - InsertRes->RHS->DoInsertion(FileIndex, Delta, 0 /*can't fail*/); - return true; - } - - // Otherwise, this is an interior node. Send the request down the tree. - DeltaTreeInteriorNode *IN = cast(this); - if (!IN->Children[i]->DoInsertion(FileIndex, Delta, InsertRes)) - return false; // If there was space in the child, just return. - - // Okay, this split the subtree, producing a new value and two children to - // insert here. If this node is non-full, we can just insert it directly. - if (!isFull()) { - // Now that we have two nodes and a new element, insert the perclated value - // into ourself by moving all the later values/children down, then inserting - // the new one. - if (i != e) - memmove(&IN->Children[i+2], &IN->Children[i+1], - (e-i)*sizeof(IN->Children[0])); - IN->Children[i] = InsertRes->LHS; - IN->Children[i+1] = InsertRes->RHS; - - if (e != i) - memmove(&Values[i+1], &Values[i], (e-i)*sizeof(Values[0])); - Values[i] = InsertRes->Split; - ++NumValuesUsed; - return false; - } - - // Finally, if this interior node was full and a node is percolated up, split - // ourself and return that up the chain. Start by saving all our info to - // avoid having the split clobber it. - IN->Children[i] = InsertRes->LHS; - DeltaTreeNode *SubRHS = InsertRes->RHS; - SourceDelta SubSplit = InsertRes->Split; - - // Do the split. - DoSplit(*InsertRes); - - // Figure out where to insert SubRHS/NewSplit. - DeltaTreeInteriorNode *InsertSide; - if (SubSplit.FileLoc < InsertRes->Split.FileLoc) - InsertSide = cast(InsertRes->LHS); - else - InsertSide = cast(InsertRes->RHS); - - // We now have a non-empty interior node 'InsertSide' to insert - // SubRHS/SubSplit into. Find out where to insert SubSplit. - - // Find the insertion point, the first delta whose index is >SubSplit.FileLoc. - i = 0; e = InsertSide->getNumValuesUsed(); - while (i != e && SubSplit.FileLoc > InsertSide->getValue(i).FileLoc) - ++i; - - // Now we know that i is the place to insert the split value into. Insert it - // and the child right after it. - if (i != e) - memmove(&InsertSide->Children[i+2], &InsertSide->Children[i+1], - (e-i)*sizeof(IN->Children[0])); - InsertSide->Children[i+1] = SubRHS; - - if (e != i) - memmove(&InsertSide->Values[i+1], &InsertSide->Values[i], - (e-i)*sizeof(Values[0])); - InsertSide->Values[i] = SubSplit; - ++InsertSide->NumValuesUsed; - InsertSide->FullDelta += SubSplit.Delta + SubRHS->getFullDelta(); - return true; -} - -/// DoSplit - Split the currently full node (which has 2*WidthFactor-1 values) -/// into two subtrees each with "WidthFactor-1" values and a pivot value. -/// Return the pieces in InsertRes. -void DeltaTreeNode::DoSplit(InsertResult &InsertRes) { - assert(isFull() && "Why split a non-full node?"); - - // Since this node is full, it contains 2*WidthFactor-1 values. We move - // the first 'WidthFactor-1' values to the LHS child (which we leave in this - // node), propagate one value up, and move the last 'WidthFactor-1' values - // into the RHS child. - - // Create the new child node. - DeltaTreeNode *NewNode; - if (DeltaTreeInteriorNode *IN = dyn_cast(this)) { - // If this is an interior node, also move over 'WidthFactor' children - // into the new node. - DeltaTreeInteriorNode *New = new DeltaTreeInteriorNode(); - memcpy(&New->Children[0], &IN->Children[WidthFactor], - WidthFactor*sizeof(IN->Children[0])); - NewNode = New; - } else { - // Just create the new leaf node. - NewNode = new DeltaTreeNode(); - } - - // Move over the last 'WidthFactor-1' values from here to NewNode. - memcpy(&NewNode->Values[0], &Values[WidthFactor], - (WidthFactor-1)*sizeof(Values[0])); - - // Decrease the number of values in the two nodes. - NewNode->NumValuesUsed = NumValuesUsed = WidthFactor-1; - - // Recompute the two nodes' full delta. - NewNode->RecomputeFullDeltaLocally(); - RecomputeFullDeltaLocally(); - - InsertRes.LHS = this; - InsertRes.RHS = NewNode; - InsertRes.Split = Values[WidthFactor-1]; -} - - - -//===----------------------------------------------------------------------===// -// DeltaTree Implementation -//===----------------------------------------------------------------------===// - -//#define VERIFY_TREE - -#ifdef VERIFY_TREE -/// VerifyTree - Walk the btree performing assertions on various properties to -/// verify consistency. This is useful for debugging new changes to the tree. -static void VerifyTree(const DeltaTreeNode *N) { - const DeltaTreeInteriorNode *IN = dyn_cast(N); - if (IN == 0) { - // Verify leaves, just ensure that FullDelta matches up and the elements - // are in proper order. - int FullDelta = 0; - for (unsigned i = 0, e = N->getNumValuesUsed(); i != e; ++i) { - if (i) - assert(N->getValue(i-1).FileLoc < N->getValue(i).FileLoc); - FullDelta += N->getValue(i).Delta; - } - assert(FullDelta == N->getFullDelta()); - return; - } - - // Verify interior nodes: Ensure that FullDelta matches up and the - // elements are in proper order and the children are in proper order. - int FullDelta = 0; - for (unsigned i = 0, e = IN->getNumValuesUsed(); i != e; ++i) { - const SourceDelta &IVal = N->getValue(i); - const DeltaTreeNode *IChild = IN->getChild(i); - if (i) - assert(IN->getValue(i-1).FileLoc < IVal.FileLoc); - FullDelta += IVal.Delta; - FullDelta += IChild->getFullDelta(); - - // The largest value in child #i should be smaller than FileLoc. - assert(IChild->getValue(IChild->getNumValuesUsed()-1).FileLoc < - IVal.FileLoc); - - // The smallest value in child #i+1 should be larger than FileLoc. - assert(IN->getChild(i+1)->getValue(0).FileLoc > IVal.FileLoc); - VerifyTree(IChild); - } - - FullDelta += IN->getChild(IN->getNumValuesUsed())->getFullDelta(); - - assert(FullDelta == N->getFullDelta()); -} -#endif // VERIFY_TREE - -static DeltaTreeNode *getRoot(void *Root) { - return (DeltaTreeNode*)Root; -} - -DeltaTree::DeltaTree() { - Root = new DeltaTreeNode(); -} -DeltaTree::DeltaTree(const DeltaTree &RHS) { - // Currently we only support copying when the RHS is empty. - assert(getRoot(RHS.Root)->getNumValuesUsed() == 0 && - "Can only copy empty tree"); - Root = new DeltaTreeNode(); -} - -DeltaTree::~DeltaTree() { - getRoot(Root)->Destroy(); -} - -/// getDeltaAt - Return the accumulated delta at the specified file offset. -/// This includes all insertions or delections that occurred *before* the -/// specified file index. -int DeltaTree::getDeltaAt(unsigned FileIndex) const { - const DeltaTreeNode *Node = getRoot(Root); - - int Result = 0; - - // Walk down the tree. - while (1) { - // For all nodes, include any local deltas before the specified file - // index by summing them up directly. Keep track of how many were - // included. - unsigned NumValsGreater = 0; - for (unsigned e = Node->getNumValuesUsed(); NumValsGreater != e; - ++NumValsGreater) { - const SourceDelta &Val = Node->getValue(NumValsGreater); - - if (Val.FileLoc >= FileIndex) - break; - Result += Val.Delta; - } - - // If we have an interior node, include information about children and - // recurse. Otherwise, if we have a leaf, we're done. - const DeltaTreeInteriorNode *IN = dyn_cast(Node); - if (!IN) return Result; - - // Include any children to the left of the values we skipped, all of - // their deltas should be included as well. - for (unsigned i = 0; i != NumValsGreater; ++i) - Result += IN->getChild(i)->getFullDelta(); - - // If we found exactly the value we were looking for, break off the - // search early. There is no need to search the RHS of the value for - // partial results. - if (NumValsGreater != Node->getNumValuesUsed() && - Node->getValue(NumValsGreater).FileLoc == FileIndex) - return Result+IN->getChild(NumValsGreater)->getFullDelta(); - - // Otherwise, traverse down the tree. The selected subtree may be - // partially included in the range. - Node = IN->getChild(NumValsGreater); - } - // NOT REACHED. -} - -/// AddDelta - When a change is made that shifts around the text buffer, -/// this method is used to record that info. It inserts a delta of 'Delta' -/// into the current DeltaTree at offset FileIndex. -void DeltaTree::AddDelta(unsigned FileIndex, int Delta) { - assert(Delta && "Adding a noop?"); - DeltaTreeNode *MyRoot = getRoot(Root); - - DeltaTreeNode::InsertResult InsertRes; - if (MyRoot->DoInsertion(FileIndex, Delta, &InsertRes)) { - Root = MyRoot = new DeltaTreeInteriorNode(InsertRes); - } - -#ifdef VERIFY_TREE - VerifyTree(MyRoot); -#endif -} - diff --git a/lib/Rewrite/FixItRewriter.cpp b/lib/Rewrite/FixItRewriter.cpp deleted file mode 100644 index 3863adb4f1625..0000000000000 --- a/lib/Rewrite/FixItRewriter.cpp +++ /dev/null @@ -1,205 +0,0 @@ -//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This is a diagnostic client adaptor that performs rewrites as -// suggested by code modification hints attached to diagnostics. It -// then forwards any diagnostics to the adapted diagnostic client. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/FixItRewriter.h" -#include "clang/Edit/Commit.h" -#include "clang/Edit/EditsReceiver.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Frontend/FrontendDiagnostic.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Path.h" -#include "llvm/ADT/OwningPtr.h" -#include - -using namespace clang; - -FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, - const LangOptions &LangOpts, - FixItOptions *FixItOpts) - : Diags(Diags), - Editor(SourceMgr, LangOpts), - Rewrite(SourceMgr, LangOpts), - FixItOpts(FixItOpts), - NumFailures(0), - PrevDiagSilenced(false) { - OwnsClient = Diags.ownsClient(); - Client = Diags.takeClient(); - Diags.setClient(this); -} - -FixItRewriter::~FixItRewriter() { - Diags.takeClient(); - Diags.setClient(Client, OwnsClient); -} - -bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { - const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); - if (!RewriteBuf) return true; - RewriteBuf->write(OS); - OS.flush(); - return false; -} - -namespace { - -class RewritesReceiver : public edit::EditsReceiver { - Rewriter &Rewrite; - -public: - RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } - - virtual void insert(SourceLocation loc, StringRef text) { - Rewrite.InsertText(loc, text); - } - virtual void replace(CharSourceRange range, StringRef text) { - Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); - } -}; - -} - -bool FixItRewriter::WriteFixedFiles( - std::vector > *RewrittenFiles) { - if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { - Diag(FullSourceLoc(), diag::warn_fixit_no_changes); - return true; - } - - RewritesReceiver Rec(Rewrite); - Editor.applyRewrites(Rec); - - for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { - const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); - int fd; - std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); - std::string Err; - OwningPtr OS; - if (fd != -1) { - OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); - } else { - OS.reset(new llvm::raw_fd_ostream(Filename.c_str(), Err, - llvm::raw_fd_ostream::F_Binary)); - } - if (!Err.empty()) { - Diags.Report(clang::diag::err_fe_unable_to_open_output) - << Filename << Err; - continue; - } - RewriteBuffer &RewriteBuf = I->second; - RewriteBuf.write(*OS); - OS->flush(); - - if (RewrittenFiles) - RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); - } - - return false; -} - -bool FixItRewriter::IncludeInDiagnosticCounts() const { - return Client ? Client->IncludeInDiagnosticCounts() : true; -} - -void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, - const Diagnostic &Info) { - // Default implementation (Warnings/errors count). - DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); - - if (!FixItOpts->Silent || - DiagLevel >= DiagnosticsEngine::Error || - (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || - (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { - Client->HandleDiagnostic(DiagLevel, Info); - PrevDiagSilenced = false; - } else { - PrevDiagSilenced = true; - } - - // Skip over any diagnostics that are ignored or notes. - if (DiagLevel <= DiagnosticsEngine::Note) - return; - // Skip over errors if we are only fixing warnings. - if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { - ++NumFailures; - return; - } - - // Make sure that we can perform all of the modifications we - // in this diagnostic. - edit::Commit commit(Editor); - for (unsigned Idx = 0, Last = Info.getNumFixItHints(); - Idx < Last; ++Idx) { - const FixItHint &Hint = Info.getFixItHint(Idx); - - if (Hint.CodeToInsert.empty()) { - if (Hint.InsertFromRange.isValid()) - commit.insertFromRange(Hint.RemoveRange.getBegin(), - Hint.InsertFromRange, /*afterToken=*/false, - Hint.BeforePreviousInsertions); - else - commit.remove(Hint.RemoveRange); - } else { - if (Hint.RemoveRange.isTokenRange() || - Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) - commit.replace(Hint.RemoveRange, Hint.CodeToInsert); - else - commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, - /*afterToken=*/false, Hint.BeforePreviousInsertions); - } - } - bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); - - if (!CanRewrite) { - if (Info.getNumFixItHints() > 0) - Diag(Info.getLocation(), diag::note_fixit_in_macro); - - // If this was an error, refuse to perform any rewriting. - if (DiagLevel >= DiagnosticsEngine::Error) { - if (++NumFailures == 1) - Diag(Info.getLocation(), diag::note_fixit_unfixed_error); - } - return; - } - - if (!Editor.commit(commit)) { - ++NumFailures; - Diag(Info.getLocation(), diag::note_fixit_failed); - return; - } - - Diag(Info.getLocation(), diag::note_fixit_applied); -} - -/// \brief Emit a diagnostic via the adapted diagnostic client. -void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { - // When producing this diagnostic, we temporarily bypass ourselves, - // clear out any current diagnostic, and let the downstream client - // format the diagnostic. - Diags.takeClient(); - Diags.setClient(Client); - Diags.Clear(); - Diags.Report(Loc, DiagID); - Diags.takeClient(); - Diags.setClient(this); -} - -DiagnosticConsumer *FixItRewriter::clone(DiagnosticsEngine &Diags) const { - return new FixItRewriter(Diags, Diags.getSourceManager(), - Rewrite.getLangOpts(), FixItOpts); -} - -FixItOptions::~FixItOptions() {} diff --git a/lib/Rewrite/Frontend/CMakeLists.txt b/lib/Rewrite/Frontend/CMakeLists.txt new file mode 100644 index 0000000000000..9017e479ab77a --- /dev/null +++ b/lib/Rewrite/Frontend/CMakeLists.txt @@ -0,0 +1,28 @@ +add_clang_library(clangRewriteFrontend + FixItRewriter.cpp + FrontendActions.cpp + HTMLPrint.cpp + InclusionRewriter.cpp + RewriteMacros.cpp + RewriteModernObjC.cpp + RewriteObjC.cpp + RewriteTest.cpp + ) + +add_dependencies(clangRewriteFrontend + ClangAttrClasses + ClangAttrList + ClangAttrParsedAttrList + ClangCommentNodes + ClangDeclNodes + ClangDiagnosticCommon + ClangDiagnosticFrontend + ClangStmtNodes + ) + +target_link_libraries(clangRewriteFrontend + clangBasic + clangAST + clangParse + clangFrontend + ) diff --git a/lib/Rewrite/Frontend/FixItRewriter.cpp b/lib/Rewrite/Frontend/FixItRewriter.cpp new file mode 100644 index 0000000000000..43a1ab1ac100b --- /dev/null +++ b/lib/Rewrite/Frontend/FixItRewriter.cpp @@ -0,0 +1,205 @@ +//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a diagnostic client adaptor that performs rewrites as +// suggested by code modification hints attached to diagnostics. It +// then forwards any diagnostics to the adapted diagnostic client. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/ADT/OwningPtr.h" +#include + +using namespace clang; + +FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + const LangOptions &LangOpts, + FixItOptions *FixItOpts) + : Diags(Diags), + Editor(SourceMgr, LangOpts), + Rewrite(SourceMgr, LangOpts), + FixItOpts(FixItOpts), + NumFailures(0), + PrevDiagSilenced(false) { + OwnsClient = Diags.ownsClient(); + Client = Diags.takeClient(); + Diags.setClient(this); +} + +FixItRewriter::~FixItRewriter() { + Diags.takeClient(); + Diags.setClient(Client, OwnsClient); +} + +bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { + const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); + if (!RewriteBuf) return true; + RewriteBuf->write(OS); + OS.flush(); + return false; +} + +namespace { + +class RewritesReceiver : public edit::EditsReceiver { + Rewriter &Rewrite; + +public: + RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } + + virtual void insert(SourceLocation loc, StringRef text) { + Rewrite.InsertText(loc, text); + } + virtual void replace(CharSourceRange range, StringRef text) { + Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); + } +}; + +} + +bool FixItRewriter::WriteFixedFiles( + std::vector > *RewrittenFiles) { + if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { + Diag(FullSourceLoc(), diag::warn_fixit_no_changes); + return true; + } + + RewritesReceiver Rec(Rewrite); + Editor.applyRewrites(Rec); + + for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { + const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); + int fd; + std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); + std::string Err; + OwningPtr OS; + if (fd != -1) { + OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); + } else { + OS.reset(new llvm::raw_fd_ostream(Filename.c_str(), Err, + llvm::raw_fd_ostream::F_Binary)); + } + if (!Err.empty()) { + Diags.Report(clang::diag::err_fe_unable_to_open_output) + << Filename << Err; + continue; + } + RewriteBuffer &RewriteBuf = I->second; + RewriteBuf.write(*OS); + OS->flush(); + + if (RewrittenFiles) + RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); + } + + return false; +} + +bool FixItRewriter::IncludeInDiagnosticCounts() const { + return Client ? Client->IncludeInDiagnosticCounts() : true; +} + +void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + + if (!FixItOpts->Silent || + DiagLevel >= DiagnosticsEngine::Error || + (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || + (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { + Client->HandleDiagnostic(DiagLevel, Info); + PrevDiagSilenced = false; + } else { + PrevDiagSilenced = true; + } + + // Skip over any diagnostics that are ignored or notes. + if (DiagLevel <= DiagnosticsEngine::Note) + return; + // Skip over errors if we are only fixing warnings. + if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { + ++NumFailures; + return; + } + + // Make sure that we can perform all of the modifications we + // in this diagnostic. + edit::Commit commit(Editor); + for (unsigned Idx = 0, Last = Info.getNumFixItHints(); + Idx < Last; ++Idx) { + const FixItHint &Hint = Info.getFixItHint(Idx); + + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); + } + } + bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); + + if (!CanRewrite) { + if (Info.getNumFixItHints() > 0) + Diag(Info.getLocation(), diag::note_fixit_in_macro); + + // If this was an error, refuse to perform any rewriting. + if (DiagLevel >= DiagnosticsEngine::Error) { + if (++NumFailures == 1) + Diag(Info.getLocation(), diag::note_fixit_unfixed_error); + } + return; + } + + if (!Editor.commit(commit)) { + ++NumFailures; + Diag(Info.getLocation(), diag::note_fixit_failed); + return; + } + + Diag(Info.getLocation(), diag::note_fixit_applied); +} + +/// \brief Emit a diagnostic via the adapted diagnostic client. +void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { + // When producing this diagnostic, we temporarily bypass ourselves, + // clear out any current diagnostic, and let the downstream client + // format the diagnostic. + Diags.takeClient(); + Diags.setClient(Client); + Diags.Clear(); + Diags.Report(Loc, DiagID); + Diags.takeClient(); + Diags.setClient(this); +} + +DiagnosticConsumer *FixItRewriter::clone(DiagnosticsEngine &Diags) const { + return new FixItRewriter(Diags, Diags.getSourceManager(), + Rewrite.getLangOpts(), FixItOpts); +} + +FixItOptions::~FixItOptions() {} diff --git a/lib/Rewrite/Frontend/FrontendActions.cpp b/lib/Rewrite/Frontend/FrontendActions.cpp new file mode 100644 index 0000000000000..7d29b6d4219d8 --- /dev/null +++ b/lib/Rewrite/Frontend/FrontendActions.cpp @@ -0,0 +1,192 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/Parser.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/FileSystem.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +ASTConsumer *HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) + return CreateHTMLPrinter(OS, CI.getPreprocessor()); + return 0; +} + +FixItAction::FixItAction() {} +FixItAction::~FixItAction() {} + +ASTConsumer *FixItAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ASTConsumer(); +} + +namespace { +class FixItRewriteInPlace : public FixItOptions { +public: + std::string RewriteFilename(const std::string &Filename, int &fd) { + fd = -1; + return Filename; + } +}; + +class FixItActionSuffixInserter : public FixItOptions { + std::string NewSuffix; + +public: + FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) + : NewSuffix(NewSuffix) { + this->FixWhatYouCan = FixWhatYouCan; + } + + std::string RewriteFilename(const std::string &Filename, int &fd) { + fd = -1; + SmallString<128> Path(Filename); + llvm::sys::path::replace_extension(Path, + NewSuffix + llvm::sys::path::extension(Path)); + return Path.str(); + } +}; + +class FixItRewriteToTemp : public FixItOptions { +public: + std::string RewriteFilename(const std::string &Filename, int &fd) { + SmallString<128> Path; + Path = llvm::sys::path::filename(Filename); + Path += "-%%%%%%%%"; + Path += llvm::sys::path::extension(Filename); + SmallString<128> NewPath; + llvm::sys::fs::unique_file(Path.str(), fd, NewPath); + return NewPath.str(); + } +}; +} // end anonymous namespace + +bool FixItAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts(); + if (!FEOpts.FixItSuffix.empty()) { + FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix, + FEOpts.FixWhatYouCan)); + } else { + FixItOpts.reset(new FixItRewriteInPlace); + FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; + } + Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get())); + return true; +} + +void FixItAction::EndSourceFileAction() { + // Otherwise rewrite all files. + Rewriter->WriteFixedFiles(); +} + +bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { + + std::vector > RewrittenFiles; + bool err = false; + { + const FrontendOptions &FEOpts = CI.getFrontendOpts(); + OwningPtr FixAction(new SyntaxOnlyAction()); + if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) { + OwningPtr FixItOpts; + if (FEOpts.FixToTemporaries) + FixItOpts.reset(new FixItRewriteToTemp()); + else + FixItOpts.reset(new FixItRewriteInPlace()); + FixItOpts->Silent = true; + FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; + FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings; + FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get()); + FixAction->Execute(); + + err = Rewriter.WriteFixedFiles(&RewrittenFiles); + + FixAction->EndSourceFile(); + CI.setSourceManager(0); + CI.setFileManager(0); + } else { + err = true; + } + } + if (err) + return false; + CI.getDiagnosticClient().clear(); + CI.getDiagnostics().Reset(); + + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(), + RewrittenFiles.begin(), RewrittenFiles.end()); + PPOpts.RemappedFilesKeepOriginalName = false; + + return true; +} + +//===----------------------------------------------------------------------===// +// Preprocessor Actions +//===----------------------------------------------------------------------===// + +ASTConsumer *RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "cpp")) { + if (CI.getLangOpts().ObjCRuntime.isNonFragile()) + return CreateModernObjCRewriter(InFile, OS, + CI.getDiagnostics(), CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros); + return CreateObjCRewriter(InFile, OS, + CI.getDiagnostics(), CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros); + } + return 0; +} + +void RewriteMacrosAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + RewriteMacrosInInput(CI.getPreprocessor(), OS); +} + +void RewriteTestAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(false, getCurrentFile()); + if (!OS) return; + + DoRewriteTest(CI.getPreprocessor(), OS); +} + +void RewriteIncludesAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + RewriteIncludesInInput(CI.getPreprocessor(), OS, + CI.getPreprocessorOutputOpts()); +} diff --git a/lib/Rewrite/Frontend/HTMLPrint.cpp b/lib/Rewrite/Frontend/HTMLPrint.cpp new file mode 100644 index 0000000000000..79e44470ada56 --- /dev/null +++ b/lib/Rewrite/Frontend/HTMLPrint.cpp @@ -0,0 +1,94 @@ +//===--- HTMLPrint.cpp - Source code -> HTML pretty-printing --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Pretty-printing of source code to HTML. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// Functional HTML pretty-printing. +//===----------------------------------------------------------------------===// + +namespace { + class HTMLPrinter : public ASTConsumer { + Rewriter R; + raw_ostream *Out; + Preprocessor &PP; + bool SyntaxHighlight, HighlightMacros; + + public: + HTMLPrinter(raw_ostream *OS, Preprocessor &pp, + bool _SyntaxHighlight, bool _HighlightMacros) + : Out(OS), PP(pp), SyntaxHighlight(_SyntaxHighlight), + HighlightMacros(_HighlightMacros) {} + + void Initialize(ASTContext &context); + void HandleTranslationUnit(ASTContext &Ctx); + }; +} + +ASTConsumer* clang::CreateHTMLPrinter(raw_ostream *OS, + Preprocessor &PP, + bool SyntaxHighlight, + bool HighlightMacros) { + return new HTMLPrinter(OS, PP, SyntaxHighlight, HighlightMacros); +} + +void HTMLPrinter::Initialize(ASTContext &context) { + R.setSourceMgr(context.getSourceManager(), context.getLangOpts()); +} + +void HTMLPrinter::HandleTranslationUnit(ASTContext &Ctx) { + if (PP.getDiagnostics().hasErrorOccurred()) + return; + + // Format the file. + FileID FID = R.getSourceMgr().getMainFileID(); + const FileEntry* Entry = R.getSourceMgr().getFileEntryForID(FID); + const char* Name; + // In some cases, in particular the case where the input is from stdin, + // there is no entry. Fall back to the memory buffer for a name in those + // cases. + if (Entry) + Name = Entry->getName(); + else + Name = R.getSourceMgr().getBuffer(FID)->getBufferIdentifier(); + + html::AddLineNumbers(R, FID); + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Name); + + // If we have a preprocessor, relex the file and syntax highlight. + // We might not have a preprocessor if we come from a deserialized AST file, + // for example. + + if (SyntaxHighlight) html::SyntaxHighlight(R, FID, PP); + if (HighlightMacros) html::HighlightMacros(R, FID, PP); + html::EscapeText(R, FID, false, true); + + // Emit the HTML. + const RewriteBuffer &RewriteBuf = R.getEditBuffer(FID); + char *Buffer = (char*)malloc(RewriteBuf.size()); + std::copy(RewriteBuf.begin(), RewriteBuf.end(), Buffer); + Out->write(Buffer, RewriteBuf.size()); + free(Buffer); +} diff --git a/lib/Rewrite/Frontend/InclusionRewriter.cpp b/lib/Rewrite/Frontend/InclusionRewriter.cpp new file mode 100644 index 0000000000000..9d1bec957d6d2 --- /dev/null +++ b/lib/Rewrite/Frontend/InclusionRewriter.cpp @@ -0,0 +1,363 @@ +//===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code rewrites include invocations into their expansions. This gives you +// a file with all included files merged into it. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace llvm; + +namespace { + +class InclusionRewriter : public PPCallbacks { + /// Information about which #includes were actually performed, + /// created by preprocessor callbacks. + struct FileChange { + SourceLocation From; + FileID Id; + SrcMgr::CharacteristicKind FileType; + FileChange(SourceLocation From) : From(From) { + } + }; + Preprocessor &PP; ///< Used to find inclusion directives. + SourceManager &SM; ///< Used to read and manage source files. + raw_ostream &OS; ///< The destination stream for rewritten contents. + bool ShowLineMarkers; ///< Show #line markers. + bool UseLineDirective; ///< Use of line directives or line markers. + typedef std::map FileChangeMap; + FileChangeMap FileChanges; /// Tracks which files were included where. + /// Used transitively for building up the FileChanges mapping over the + /// various \c PPCallbacks callbacks. + FileChangeMap::iterator LastInsertedFileChange; +public: + InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers); + bool Process(FileID FileId, SrcMgr::CharacteristicKind FileType); +private: + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID); + virtual void FileSkipped(const FileEntry &ParentFile, + const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType); + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported); + void WriteLineInfo(const char *Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef EOL, StringRef Extra = StringRef()); + void OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef EOL, int &lines, + bool EnsureNewline = false); + void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, + const MemoryBuffer &FromFile, StringRef EOL, + unsigned &NextToWrite, int &Lines); + const FileChange *FindFileChangeLocation(SourceLocation Loc) const; + StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); +}; + +} // end anonymous namespace + +/// Initializes an InclusionRewriter with a \p PP source and \p OS destination. +InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS, + bool ShowLineMarkers) + : PP(PP), SM(PP.getSourceManager()), OS(OS), + ShowLineMarkers(ShowLineMarkers), + LastInsertedFileChange(FileChanges.end()) { + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOpts().MicrosoftExt; +} + +/// Write appropriate line information as either #line directives or GNU line +/// markers depending on what mode we're in, including the \p Filename and +/// \p Line we are located at, using the specified \p EOL line separator, and +/// any \p Extra context specifiers in GNU line directives. +void InclusionRewriter::WriteLineInfo(const char *Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef EOL, StringRef Extra) { + if (!ShowLineMarkers) + return; + if (UseLineDirective) { + OS << "#line" << ' ' << Line << ' ' << '"' << Filename << '"'; + } else { + // Use GNU linemarkers as described here: + // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html + OS << '#' << ' ' << Line << ' ' << '"' << Filename << '"'; + if (!Extra.empty()) + OS << Extra; + if (FileType == SrcMgr::C_System) + // "`3' This indicates that the following text comes from a system header + // file, so certain warnings should be suppressed." + OS << " 3"; + else if (FileType == SrcMgr::C_ExternCSystem) + // as above for `3', plus "`4' This indicates that the following text + // should be treated as being wrapped in an implicit extern "C" block." + OS << " 3 4"; + } + OS << EOL; +} + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. +void InclusionRewriter::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, + FileID) { + if (Reason != EnterFile) + return; + if (LastInsertedFileChange == FileChanges.end()) + // we didn't reach this file (eg: the main file) via an inclusion directive + return; + LastInsertedFileChange->second.Id = FullSourceLoc(Loc, SM).getFileID(); + LastInsertedFileChange->second.FileType = NewFileType; + LastInsertedFileChange = FileChanges.end(); +} + +/// Called whenever an inclusion is skipped due to canonical header protection +/// macros. +void InclusionRewriter::FileSkipped(const FileEntry &/*ParentFile*/, + const Token &/*FilenameTok*/, + SrcMgr::CharacteristicKind /*FileType*/) { + assert(LastInsertedFileChange != FileChanges.end() && "A file, that wasn't " + "found via an inclusion directive, was skipped"); + FileChanges.erase(LastInsertedFileChange); + LastInsertedFileChange = FileChanges.end(); +} + +/// This should be called whenever the preprocessor encounters include +/// directives. It does not say whether the file has been included, but it +/// provides more information about the directive (hash location instead +/// of location inside the included file). It is assumed that the matching +/// FileChanged() or FileSkipped() is called after this. +void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, + const Token &/*IncludeTok*/, + StringRef /*FileName*/, + bool /*IsAngled*/, + CharSourceRange /*FilenameRange*/, + const FileEntry * /*File*/, + StringRef /*SearchPath*/, + StringRef /*RelativePath*/, + const Module * /*Imported*/) { + assert(LastInsertedFileChange == FileChanges.end() && "Another inclusion " + "directive was found before the previous one was processed"); + std::pair p = FileChanges.insert( + std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc))); + assert(p.second && "Unexpected revisitation of the same include directive"); + LastInsertedFileChange = p.first; +} + +/// Simple lookup for a SourceLocation (specifically one denoting the hash in +/// an inclusion directive) in the map of inclusion information, FileChanges. +const InclusionRewriter::FileChange * +InclusionRewriter::FindFileChangeLocation(SourceLocation Loc) const { + FileChangeMap::const_iterator I = FileChanges.find(Loc.getRawEncoding()); + if (I != FileChanges.end()) + return &I->second; + return NULL; +} + +/// Detect the likely line ending style of \p FromFile by examining the first +/// newline found within it. +static StringRef DetectEOL(const MemoryBuffer &FromFile) { + // detect what line endings the file uses, so that added content does not mix + // the style + const char *Pos = strchr(FromFile.getBufferStart(), '\n'); + if (Pos == NULL) + return "\n"; + if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r') + return "\n\r"; + if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r') + return "\r\n"; + return "\n"; +} + +/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at +/// \p WriteTo - 1. +void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef EOL, int &Line, + bool EnsureNewline) { + if (WriteTo <= WriteFrom) + return; + OS.write(FromFile.getBufferStart() + WriteFrom, WriteTo - WriteFrom); + // count lines manually, it's faster than getPresumedLoc() + Line += std::count(FromFile.getBufferStart() + WriteFrom, + FromFile.getBufferStart() + WriteTo, '\n'); + if (EnsureNewline) { + char LastChar = FromFile.getBufferStart()[WriteTo - 1]; + if (LastChar != '\n' && LastChar != '\r') + OS << EOL; + } + WriteFrom = WriteTo; +} + +/// Print characters from \p FromFile starting at \p NextToWrite up until the +/// inclusion directive at \p StartToken, then print out the inclusion +/// inclusion directive disabled by a #if directive, updating \p NextToWrite +/// and \p Line to track the number of source lines visited and the progress +/// through the \p FromFile buffer. +void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex, + const Token &StartToken, + const MemoryBuffer &FromFile, + StringRef EOL, + unsigned &NextToWrite, int &Line) { + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(StartToken.getLocation()), EOL, Line); + Token DirectiveToken; + do { + DirectiveLex.LexFromRawLexer(DirectiveToken); + } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof)); + OS << "#if 0 /* expanded by -frewrite-includes */" << EOL; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(DirectiveToken.getLocation()) + DirectiveToken.getLength(), + EOL, Line); + OS << "#endif /* expanded by -frewrite-includes */" << EOL; +} + +/// Find the next identifier in the pragma directive specified by \p RawToken. +StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, + Token &RawToken) { + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.is(tok::identifier)) + return RawToken.getIdentifierInfo()->getName(); + return StringRef(); +} + +/// Use a raw lexer to analyze \p FileId, inccrementally copying parts of it +/// and including content of included files recursively. +bool InclusionRewriter::Process(FileID FileId, + SrcMgr::CharacteristicKind FileType) +{ + bool Invalid; + const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid); + if (Invalid) // invalid inclusion + return true; + const char *FileName = FromFile.getBufferIdentifier(); + Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts()); + RawLex.SetCommentRetentionState(false); + + StringRef EOL = DetectEOL(FromFile); + + // Per the GNU docs: "1" indicates the start of a new file. + WriteLineInfo(FileName, 1, FileType, EOL, " 1"); + + if (SM.getFileIDSize(FileId) == 0) + return true; + + // The next byte to be copied from the source file + unsigned NextToWrite = 0; + int Line = 1; // The current input file line number. + + Token RawToken; + RawLex.LexFromRawLexer(RawToken); + + // TODO: Consider adding a switch that strips possibly unimportant content, + // such as comments, to reduce the size of repro files. + while (RawToken.isNot(tok::eof)) { + if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) { + RawLex.setParsingPreprocessorDirective(true); + Token HashToken = RawToken; + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.is(tok::identifier)) { + switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { + case tok::pp_include: + case tok::pp_include_next: + case tok::pp_import: { + CommentOutDirective(RawLex, HashToken, FromFile, EOL, NextToWrite, + Line); + if (const FileChange *Change = FindFileChangeLocation( + HashToken.getLocation())) { + // now include and recursively process the file + if (Process(Change->Id, Change->FileType)) + // and set lineinfo back to this file, if the nested one was + // actually included + // `2' indicates returning to a file (after having included + // another file. + WriteLineInfo(FileName, Line, FileType, EOL, " 2"); + } else + // fix up lineinfo (since commented out directive changed line + // numbers) for inclusions that were skipped due to header guards + WriteLineInfo(FileName, Line, FileType, EOL); + break; + } + case tok::pp_pragma: { + StringRef Identifier = NextIdentifierName(RawLex, RawToken); + if (Identifier == "clang" || Identifier == "GCC") { + if (NextIdentifierName(RawLex, RawToken) == "system_header") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, EOL, + NextToWrite, Line); + // update our own type + FileType = SM.getFileCharacteristic(RawToken.getLocation()); + WriteLineInfo(FileName, Line, FileType, EOL); + } + } else if (Identifier == "once") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, EOL, + NextToWrite, Line); + WriteLineInfo(FileName, Line, FileType, EOL); + } + break; + } + default: + break; + } + } + RawLex.setParsingPreprocessorDirective(false); + } + RawLex.LexFromRawLexer(RawToken); + } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(SM.getLocForEndOfFile(FileId)) + 1, EOL, Line, + /*EnsureNewline*/true); + return true; +} + +/// InclusionRewriterInInput - Implement -frewrite-includes mode. +void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + SourceManager &SM = PP.getSourceManager(); + InclusionRewriter *Rewrite = new InclusionRewriter(PP, *OS, + Opts.ShowLineMarkers); + PP.addPPCallbacks(Rewrite); + + // First let the preprocessor process the entire file and call callbacks. + // Callbacks will record which #include's were actually performed. + PP.EnterMainSourceFile(); + Token Tok; + // Only preprocessor directives matter here, so disable macro expansion + // everywhere else as an optimization. + // TODO: It would be even faster if the preprocessor could be switched + // to a mode where it would parse only preprocessor directives and comments, + // nothing else matters for parsing or processing. + PP.SetMacroExpansionOnlyInDirectives(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); + Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User); + OS->flush(); +} diff --git a/lib/Rewrite/Frontend/Makefile b/lib/Rewrite/Frontend/Makefile new file mode 100644 index 0000000000000..ac97d4074ecbc --- /dev/null +++ b/lib/Rewrite/Frontend/Makefile @@ -0,0 +1,18 @@ +##===- clang/lib/Rewrite/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements code transformation / rewriting facilities. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangRewriteFrontend + +include $(CLANG_LEVEL)/Makefile + diff --git a/lib/Rewrite/Frontend/RewriteMacros.cpp b/lib/Rewrite/Frontend/RewriteMacros.cpp new file mode 100644 index 0000000000000..f399dd5d7ce90 --- /dev/null +++ b/lib/Rewrite/Frontend/RewriteMacros.cpp @@ -0,0 +1,217 @@ +//===--- RewriteMacros.cpp - Rewrite macros into their expansions ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code rewrites macro invocations into their expansions. This gives you +// a macro expanded file that retains comments and #includes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/ADT/OwningPtr.h" +#include + +using namespace clang; + +/// isSameToken - Return true if the two specified tokens start have the same +/// content. +static bool isSameToken(Token &RawTok, Token &PPTok) { + // If two tokens have the same kind and the same identifier info, they are + // obviously the same. + if (PPTok.getKind() == RawTok.getKind() && + PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) + return true; + + // Otherwise, if they are different but have the same identifier info, they + // are also considered to be the same. This allows keywords and raw lexed + // identifiers with the same name to be treated the same. + if (PPTok.getIdentifierInfo() && + PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) + return true; + + return false; +} + + +/// GetNextRawTok - Return the next raw token in the stream, skipping over +/// comments if ReturnComment is false. +static const Token &GetNextRawTok(const std::vector &RawTokens, + unsigned &CurTok, bool ReturnComment) { + assert(CurTok < RawTokens.size() && "Overran eof!"); + + // If the client doesn't want comments and we have one, skip it. + if (!ReturnComment && RawTokens[CurTok].is(tok::comment)) + ++CurTok; + + return RawTokens[CurTok++]; +} + + +/// LexRawTokensFromMainFile - Lets all the raw tokens from the main file into +/// the specified vector. +static void LexRawTokensFromMainFile(Preprocessor &PP, + std::vector &RawTokens) { + SourceManager &SM = PP.getSourceManager(); + + // Create a lexer to lex all the tokens of the main file in raw mode. Even + // though it is in raw mode, it will not return comments. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts()); + + // Switch on comment lexing because we really do want them. + RawLex.SetCommentRetentionState(true); + + Token RawTok; + do { + RawLex.LexFromRawLexer(RawTok); + + // If we have an identifier with no identifier info for our raw token, look + // up the indentifier info. This is important for equality comparison of + // identifier tokens. + if (RawTok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawTok); + + RawTokens.push_back(RawTok); + } while (RawTok.isNot(tok::eof)); +} + + +/// RewriteMacrosInInput - Implement -rewrite-macros mode. +void clang::RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS) { + SourceManager &SM = PP.getSourceManager(); + + Rewriter Rewrite; + Rewrite.setSourceMgr(SM, PP.getLangOpts()); + RewriteBuffer &RB = Rewrite.getEditBuffer(SM.getMainFileID()); + + std::vector RawTokens; + LexRawTokensFromMainFile(PP, RawTokens); + unsigned CurRawTok = 0; + Token RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + + + // Get the first preprocessing token. + PP.EnterMainSourceFile(); + Token PPTok; + PP.Lex(PPTok); + + // Preprocess the input file in parallel with raw lexing the main file. Ignore + // all tokens that are preprocessed from a file other than the main file (e.g. + // a header). If we see tokens that are in the preprocessed file but not the + // lexed file, we have a macro expansion. If we see tokens in the lexed file + // that aren't in the preprocessed view, we have macros that expand to no + // tokens, or macro arguments etc. + while (RawTok.isNot(tok::eof) || PPTok.isNot(tok::eof)) { + SourceLocation PPLoc = SM.getExpansionLoc(PPTok.getLocation()); + + // If PPTok is from a different source file, ignore it. + if (!SM.isFromMainFile(PPLoc)) { + PP.Lex(PPTok); + continue; + } + + // If the raw file hits a preprocessor directive, they will be extra tokens + // in the raw file that don't exist in the preprocsesed file. However, we + // choose to preserve them in the output file and otherwise handle them + // specially. + if (RawTok.is(tok::hash) && RawTok.isAtStartOfLine()) { + // If this is a #warning directive or #pragma mark (GNU extensions), + // comment the line out. + if (RawTokens[CurRawTok].is(tok::identifier)) { + const IdentifierInfo *II = RawTokens[CurRawTok].getIdentifierInfo(); + if (II->getName() == "warning") { + // Comment out #warning. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); + } else if (II->getName() == "pragma" && + RawTokens[CurRawTok+1].is(tok::identifier) && + (RawTokens[CurRawTok+1].getIdentifierInfo()->getName() == + "mark")) { + // Comment out #pragma mark. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); + } + } + + // Otherwise, if this is a #include or some other directive, just leave it + // in the file by skipping over the line. + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + while (!RawTok.isAtStartOfLine() && RawTok.isNot(tok::eof)) + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + continue; + } + + // Okay, both tokens are from the same file. Get their offsets from the + // start of the file. + unsigned PPOffs = SM.getFileOffset(PPLoc); + unsigned RawOffs = SM.getFileOffset(RawTok.getLocation()); + + // If the offsets are the same and the token kind is the same, ignore them. + if (PPOffs == RawOffs && isSameToken(RawTok, PPTok)) { + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + PP.Lex(PPTok); + continue; + } + + // If the PP token is farther along than the raw token, something was + // deleted. Comment out the raw token. + if (RawOffs <= PPOffs) { + // Comment out a whole run of tokens instead of bracketing each one with + // comments. Add a leading space if RawTok didn't have one. + bool HasSpace = RawTok.hasLeadingSpace(); + RB.InsertTextAfter(RawOffs, &" /*"[HasSpace]); + unsigned EndPos; + + do { + EndPos = RawOffs+RawTok.getLength(); + + RawTok = GetNextRawTok(RawTokens, CurRawTok, true); + RawOffs = SM.getFileOffset(RawTok.getLocation()); + + if (RawTok.is(tok::comment)) { + // Skip past the comment. + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + break; + } + + } while (RawOffs <= PPOffs && !RawTok.isAtStartOfLine() && + (PPOffs != RawOffs || !isSameToken(RawTok, PPTok))); + + RB.InsertTextBefore(EndPos, "*/"); + continue; + } + + // Otherwise, there was a replacement an expansion. Insert the new token + // in the output buffer. Insert the whole run of new tokens at once to get + // them in the right order. + unsigned InsertPos = PPOffs; + std::string Expansion; + while (PPOffs < RawOffs) { + Expansion += ' ' + PP.getSpelling(PPTok); + PP.Lex(PPTok); + PPLoc = SM.getExpansionLoc(PPTok.getLocation()); + PPOffs = SM.getFileOffset(PPLoc); + } + Expansion += ' '; + RB.InsertTextBefore(InsertPos, Expansion); + } + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(SM.getMainFileID())) { + //printf("Changed:\n"); + *OS << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + fprintf(stderr, "No changes\n"); + } + OS->flush(); +} diff --git a/lib/Rewrite/Frontend/RewriteModernObjC.cpp b/lib/Rewrite/Frontend/RewriteModernObjC.cpp new file mode 100644 index 0000000000000..4b56b3720a3fc --- /dev/null +++ b/lib/Rewrite/Frontend/RewriteModernObjC.cpp @@ -0,0 +1,7607 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/DenseSet.h" + +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteModernObjC : public ASTConsumer { + protected: + + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the + __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose + support routines */ + BLOCK_BYREF_CURRENT_MAX = 256 + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + static const int OBJC_ABI_VERSION = 7; + + Rewriter Rewrite; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + std::string InFileName; + raw_ostream* OutFile; + std::string Preamble; + + TypeDecl *ProtocolTypeDecl; + VarDecl *GlobalVarDecl; + Expr *GlobalConstructionExp; + unsigned RewriteFailedDiag; + unsigned GlobalBlockRewriteFailedDiag; + // ObjC string constant support. + unsigned NumObjCStringLiterals; + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + unsigned TryFinallyContainsReturnDiag; + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *GetSuperClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperContructorFunctionDecl; + FunctionDecl *CurFunctionDef; + + /* Misc. containers needed for meta-data rewrite. */ + SmallVector ClassImplementation; + SmallVector CategoryImplementation; + llvm::SmallPtrSet ObjCSynthesizedStructs; + llvm::SmallPtrSet ObjCSynthesizedProtocols; + llvm::SmallPtrSet ObjCWrittenInterfaces; + llvm::SmallPtrSet GlobalDefinedTags; + SmallVector ObjCInterfacesSeen; + /// DefinedNonLazyClasses - List of defined "non-lazy" classes. + SmallVector DefinedNonLazyClasses; + + /// DefinedNonLazyCategories - List of defined "non-lazy" categories. + llvm::SmallVector DefinedNonLazyCategories; + + SmallVector Stmts; + SmallVector ObjCBcLabelNo; + // Remember all the @protocol() expressions. + llvm::SmallPtrSet ProtocolExprDecls; + + llvm::DenseSet CopyDestroyCache; + + // Block expressions. + SmallVector Blocks; + SmallVector InnerDeclRefsCount; + SmallVector InnerDeclRefs; + + SmallVector BlockDeclRefs; + + // Block related declarations. + SmallVector BlockByCopyDecls; + llvm::SmallPtrSet BlockByCopyDeclsPtrSet; + SmallVector BlockByRefDecls; + llvm::SmallPtrSet BlockByRefDeclsPtrSet; + llvm::DenseMap BlockByRefDeclNo; + llvm::SmallPtrSet ImportedBlockDecls; + llvm::SmallPtrSet ImportedLocalExternalDecls; + + llvm::DenseMap RewrittenBlockExprs; + llvm::DenseMap > ReferencedIvars; + + // This maps an original source AST to it's rewritten form. This allows + // us to avoid rewriting the same node twice (which is very uncommon). + // This is needed to support some of the exotic property rewriting. + llvm::DenseMap ReplacedNodes; + + // Needed for header files being rewritten + bool IsHeader; + bool SilenceRewriteMacroWarning; + bool objc_impl_method; + + bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteModernObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteModernObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; + void InitializeCommon(ASTContext &context); + + public: + llvm::DenseMap MethodInternalNames; + // Top Level Driver code. + virtual bool HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *Class = dyn_cast(*I)) { + if (!Class->isThisDeclarationADefinition()) { + RewriteForwardClassDecl(D); + break; + } else { + // Keep track of all interface declarations seen. + ObjCInterfacesSeen.push_back(Class); + break; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast(*I)) { + if (!Proto->isThisDeclarationADefinition()) { + RewriteForwardProtocolDecl(D); + break; + } + } + + HandleTopLevelSingleDecl(*I); + } + return true; + } + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteModernObjC(std::string inFile, raw_ostream *OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn); + + ~RewriteModernObjC() {} + + virtual void HandleTranslationUnit(ASTContext &C); + + void ReplaceStmt(Stmt *Old, Stmt *New) { + Stmt *ReplacingStmt = ReplacedNodes[Old]; + + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceStmt(Old, New)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + if (DisableReplaceStmt) + return; + + // Measure the old text. + int Size = Rewrite.getRangeSize(SrcRange); + if (Size == -1) { + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + return; + } + // Get the new text. + std::string SStr; + llvm::raw_string_ostream S(SStr); + New->printPretty(S, 0, PrintingPolicy(LangOpts)); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, Str, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef Str) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, Str) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewriteRecordBody(RecordDecl *RD); + void RewriteInclude(); + void RewriteLineDirective(const Decl *D); + void ConvertSourceLocationToLineDirective(SourceLocation Loc, + std::string &LineString); + void RewriteForwardClassDecl(DeclGroupRef D); + void RewriteForwardClassDecl(const llvm::SmallVector &DG); + void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString); + void RewriteImplementations(); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType); + void RewriteByRefString(std::string &ResultStr, const std::string &Name, + ValueDecl *VD, bool def=false); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(DeclGroupRef D); + void RewriteForwardProtocolDecl(const llvm::SmallVector &DG); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteBlockPointerType(std::string& Str, QualType Type); + void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteTypeOfDecl(VarDecl *VD); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + std::string getIvarAccessString(ObjCIvarDecl *D); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp); + Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp); + Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp); + Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void RewriteCastExpr(CStyleCastExpr *CE); + void RewriteImplicitCastObjCExpr(CastExpr *IE); + void RewriteLinkageSpec(LinkageSpecDecl *LSD); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + + // Block specific rewrite rules. + void RewriteBlockPointerDecl(NamedDecl *VD); + void RewriteByRefVar(VarDecl *VD, bool firstDecl, bool lastDecl); + Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); + Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result); + bool IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, TagDecl *Tag, + bool &IsNamedDefinition); + void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result); + + bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result); + + void RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, + std::string &Result); + + virtual void Initialize(ASTContext &context); + + // Misc. AST transformation routines. Sometimes they end up calling + // rewriting routines on the new ASTs. + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + Expr **args, unsigned nargs, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + Expr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl &ArgTypes, + SmallVectorImpl &MsgExprs, + ObjCMethodDecl *Method); + + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + void SynthCountByEnumWithState(std::string &buf); + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthGetSuperClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperContructorFunctionDecl(); + + // Rewriting metadata + template + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result); + void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + std::string &Result); + void RewriteObjCProtocolListMetaData( + const ObjCList &Prots, + StringRef prefix, StringRef ClassName, std::string &Result); + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result); + void RewriteClassSetupInitHook(std::string &Result); + + void RewriteMetaDataIntoBuffer(std::string &Result); + void WriteImageInfo(std::string &Result); + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result); + void RewriteCategorySetupInitHook(std::string &Result); + + // Rewriting ivar + void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result); + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); + + + std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, + std::string Tag, std::string Desc); + std::string SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, + int i, StringRef funcName, + unsigned hasCopy); + Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName); + FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp, + const SmallVector &InnerBlockDeclRefs); + + // Misc. helper routines. + QualType getProtocolType(); + void WarnAboutReturnGotoStmts(Stmt *S); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + bool IsDeclStmtInForeachHeader(DeclStmt *DS); + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockDeclRefExprs(Stmt *S); + void GetInnerBlockDeclRefExprs(Stmt *S, + SmallVector &InnerBlockDeclRefs, + llvm::SmallPtrSet &InnerContexts); + + // We avoid calling Type::isBlockPointerType(), since it operates on the + // canonical type. We only care if the top-level type is a closure pointer. + bool isTopLevelBlockPointerType(QualType T) { + return isa(T); + } + + /// convertBlockPointerToFunctionPointer - Converts a block-pointer type + /// to a function pointer type and upon success, returns true; false + /// otherwise. + bool convertBlockPointerToFunctionPointer(QualType &T) { + if (isTopLevelBlockPointerType(T)) { + const BlockPointerType *BPT = T->getAs(); + T = Context->getPointerType(BPT->getPointeeType()); + return true; + } + return false; + } + + bool convertObjCTypeToCStyleType(QualType &T); + + bool needToScanForQualifiers(QualType T); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + QualType convertFunctionTypeOfBlocks(const FunctionType *FT); + bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); + + void convertToUnqualifiedObjCType(QualType &T) { + if (T->isObjCQualifiedIdType()) { + bool isConst = T.isConstQualified(); + T = isConst ? Context->getObjCIdType().withConst() + : Context->getObjCIdType(); + } + else if (T->isObjCQualifiedClassType()) + T = Context->getObjCClassType(); + else if (T->isObjCObjectPointerType() && + T->getPointeeType()->isObjCQualifiedInterfaceType()) { + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); + T = QualType(IFaceT, 0); + T = Context->getPointerType(T); + } + } + } + + // FIXME: This predicate seems like it would be useful to add to ASTContext. + bool isObjCType(QualType T) { + if (!LangOpts.ObjC1 && !LangOpts.ObjC2) + return false; + + QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); + + if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || + OCT == Context->getCanonicalType(Context->getObjCClassType())) + return true; + + if (const PointerType *PT = OCT->getAs()) { + if (isa(PT->getPointeeType()) || + PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + return false; + } + bool PointerTypeTakesAnyBlockArguments(QualType QT); + bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for (unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + + QualType getSimpleFunctionType(QualType result, + const QualType *args, + unsigned numArgs, + bool variadic = false) { + if (result == Context->getObjCInstanceType()) + result = Context->getObjCIdType(); + FunctionProtoType::ExtProtoInfo fpi; + fpi.Variadic = variadic; + return Context->getFunctionType(result, args, numArgs, fpi); + } + + // Helper function: create a CStyleCastExpr with trivial type source info. + CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, + CastKind Kind, Expr *E) { + TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); + return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, 0, TInfo, + SourceLocation(), SourceLocation()); + } + + bool ImplementationIsNonLazy(const ObjCImplDecl *OD) const { + IdentifierInfo* II = &Context->Idents.get("load"); + Selector LoadSel = Context->Selectors.getSelector(0, &II); + return OD->getClassMethod(LoadSel) != 0; + } + }; + +} + +void RewriteModernObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (const FunctionProtoType *fproto + = dyn_cast(funcType.IgnoreParens())) { + for (FunctionProtoType::arg_type_iterator I = fproto->arg_type_begin(), + E = fproto->arg_type_end(); I && (I != E); ++I) + if (isTopLevelBlockPointerType(*I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteModernObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAs(); + if (PT && PointerTypeTakesAnyBlockArguments(funcType)) + RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); +} + +static bool IsHeaderFile(const std::string &Filename) { + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + // no file extension + return false; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + return Ext == "h" || Ext == "hh" || Ext == "H"; +} + +RewriteModernObjC::RewriteModernObjC(std::string inFile, raw_ostream* OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), + SilenceRewriteMacroWarning(silenceMacroWarn) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + // FIXME. This should be an error. But if block is not called, it is OK. And it + // may break including some headers. + GlobalBlockRewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting block literal declared in global scope is not implemented"); + + TryFinallyContainsReturnDiag = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +ASTConsumer *clang::CreateModernObjCRewriter(const std::string& InFile, + raw_ostream* OS, + DiagnosticsEngine &Diags, + const LangOptions &LOpts, + bool SilenceRewriteMacroWarning) { + return new RewriteModernObjC(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning); +} + +void RewriteModernObjC::InitializeCommon(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = 0; + MsgSendSuperFunctionDecl = 0; + MsgSendStretFunctionDecl = 0; + MsgSendSuperStretFunctionDecl = 0; + MsgSendFpretFunctionDecl = 0; + GetClassFunctionDecl = 0; + GetMetaClassFunctionDecl = 0; + GetSuperClassFunctionDecl = 0; + SelGetUidFunctionDecl = 0; + CFStringFunctionDecl = 0; + ConstantStringClassReference = 0; + NSStringRecord = 0; + CurMethodDef = 0; + CurFunctionDef = 0; + GlobalVarDecl = 0; + GlobalConstructionExp = 0; + SuperStructDecl = 0; + ProtocolTypeDecl = 0; + ConstantStringDecl = 0; + BcLabelCount = 0; + SuperContructorFunctionDecl = 0; + NumObjCStringLiterals = 0; + PropParentMap = 0; + CurrentBody = 0; + DisableReplaceStmt = false; + objc_impl_method = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteModernObjC::HandleTopLevelSingleDecl(Decl *D) { + if (Diags.hasErrorOccurred()) + return; + + // Two cases: either the decl could be in the main file, or it could be in a + // #included file. If the former, rewrite it now. If the later, check to see + // if we rewrote the #include/#import. + SourceLocation Loc = D->getLocation(); + Loc = SM->getExpansionLoc(Loc); + + // If this is for a builtin, ignore it. + if (Loc.isInvalid()) return; + + // Look for built-in declarations that we need to refer during the rewrite. + if (FunctionDecl *FD = dyn_cast(D)) { + RewriteFunctionDecl(FD); + } else if (VarDecl *FVD = dyn_cast(D)) { + // declared in + if (FVD->getName() == "_NSConstantStringClassReference") { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCCategoryDecl *CD = dyn_cast(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast(D)) { + if (PD->isThisDeclarationADefinition()) + RewriteProtocolDecl(PD); + } else if (LinkageSpecDecl *LSD = dyn_cast(D)) { + // FIXME. This will not work in all situations and leaving it out + // is harmless. + // RewriteLinkageSpec(LSD); + + // Recurse into linkage specifications + for (DeclContext::decl_iterator DI = LSD->decls_begin(), + DIEnd = LSD->decls_end(); + DI != DIEnd; ) { + if (ObjCInterfaceDecl *IFace = dyn_cast((*DI))) { + if (!IFace->isThisDeclarationADefinition()) { + SmallVector DG; + SourceLocation StartLoc = IFace->getLocStart(); + do { + if (isa(*DI) && + !cast(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardClassDecl(DG); + continue; + } + else { + // Keep track of all interface declarations seen. + ObjCInterfacesSeen.push_back(IFace); + ++DI; + continue; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast((*DI))) { + if (!Proto->isThisDeclarationADefinition()) { + SmallVector DG; + SourceLocation StartLoc = Proto->getLocStart(); + do { + if (isa(*DI) && + !cast(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardProtocolDecl(DG); + continue; + } + } + + HandleTopLevelSingleDecl(*DI); + ++DI; + } + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isFromMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteModernObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + StringRef MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.begin(); + const char *MainBufEnd = MainBuf.end(); + size_t ImportLen = strlen("import"); + + // Loop over the whole file, looking for includes. + for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { + if (*BufPtr == '#') { + if (++BufPtr == MainBufEnd) + return; + while (*BufPtr == ' ' || *BufPtr == '\t') + if (++BufPtr == MainBufEnd) + return; + if (!strncmp(BufPtr, "import", ImportLen)) { + // replace import with include + SourceLocation ImportLoc = + LocStart.getLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include"); + BufPtr += ImportLen; + } + } + } +} + +static void WriteInternalIvarName(const ObjCInterfaceDecl *IDecl, + ObjCIvarDecl *IvarDecl, std::string &Result) { + Result += "OBJC_IVAR_$_"; + Result += IDecl->getName(); + Result += "$"; + Result += IvarDecl->getName(); +} + +std::string +RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) { + const ObjCInterfaceDecl *ClassDecl = D->getContainingInterface(); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + WriteInternalIvarName(ClassDecl, D, IvarOffsetName); + + + std::string S = "(*("; + QualType IvarT = D->getType(); + + if (!isa(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName.c_str())); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + std::string TypeString(castT.getAsString(Context->getPrintingPolicy())); + S += TypeString; + S += ")"; + + // ((char *)self + IVAR_OFFSET_SYMBOL_NAME) + S += "((char *)self + "; + S += IvarOffsetName; + S += "))"; + ReferencedIvars[const_cast(ClassDecl)].insert(D); + return S; +} + +/// mustSynthesizeSetterGetterMethod - returns true if setter or getter has not +/// been found in the class implementation. In this case, it must be synthesized. +static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP, + ObjCPropertyDecl *PD, + bool getter) { + return getter ? !IMP->getInstanceMethod(PD->getGetterName()) + : !IMP->getInstanceMethod(PD->getSetterName()); + +} + +void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + static bool objcGetPropertyDefined = false; + static bool objcSetPropertyDefined = false; + SourceLocation startGetterSetterLoc; + + if (PID->getLocStart().isValid()) { + SourceLocation startLoc = PID->getLocStart(); + InsertText(startLoc, "// "); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + startGetterSetterLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + } + else + startGetterSetterLoc = IMD ? IMD->getLocEnd() : CID->getLocEnd(); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + + if (!OID) + return; + unsigned Attributes = PD->getPropertyAttributes(); + if (mustSynthesizeSetterGetterMethod(IMD, PD, true /*getter*/)) { + bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && + (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy)); + std::string Getr; + if (GenGetProperty && !objcGetPropertyDefined) { + objcGetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Getr = "\nextern \"C\" __declspec(dllimport) " + "id objc_getProperty(id, SEL, long, bool);\n"; + } + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // See objc-act.c:objc_synthesize_new_getter() for details. + if (GenGetProperty) { + // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) + Getr += "typedef "; + const FunctionType *FPRetType = 0; + RewriteTypeIntoString(PD->getGetterMethodDecl()->getResultType(), Getr, + FPRetType); + Getr += " _TYPE"; + if (FPRetType) { + Getr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast(FPRetType)){ + Getr += "("; + for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { + if (i) Getr += ", "; + std::string ParamStr = FT->getArgType(i).getAsString( + Context->getPrintingPolicy()); + Getr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumArgs()) Getr += ", "; + Getr += "..."; + } + Getr += ")"; + } else + Getr += "()"; + } + Getr += ";\n"; + Getr += "return (_TYPE)"; + Getr += "objc_getProperty(self, _cmd, "; + RewriteIvarOffsetComputation(OID, Getr); + Getr += ", 1)"; + } + else + Getr += "return " + getIvarAccessString(OID); + Getr += "; }"; + InsertText(startGetterSetterLoc, Getr); + } + + if (PD->isReadOnly() || + !mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/)) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy); + if (GenSetProperty && !objcSetPropertyDefined) { + objcSetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Setr = "\nextern \"C\" __declspec(dllimport) " + "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; + } + + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // See objc-act.c:objc_synthesize_new_setter() for details. + if (GenSetProperty) { + Setr += "objc_setProperty (self, _cmd, "; + RewriteIvarOffsetComputation(OID, Setr); + Setr += ", (id)"; + Setr += PD->getName(); + Setr += ", "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) + Setr += "1)"; + else + Setr += "0)"; + } + else { + Setr += getIvarAccessString(OID) + " = "; + Setr += PD->getName(); + } + Setr += "; }\n"; + InsertText(startGetterSetterLoc, Setr); +} + +static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, + std::string &typedefString) { + typedefString += "#ifndef _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "#define _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "typedef struct objc_object "; + typedefString += ForwardDecl->getNameAsString(); + // typedef struct { } _objc_exc_Classname; + typedefString += ";\ntypedef struct {} _objc_exc_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n#endif\n"; +} + +void RewriteModernObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString) { + SourceLocation startLoc = ClassDecl->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); +} + +void RewriteModernObjC::RewriteForwardClassDecl(DeclGroupRef D) { + std::string typedefString; + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + ObjCInterfaceDecl *ForwardDecl = cast(*I); + if (I == D.begin()) { + // Translate to typedef's that forward reference structs with the same name + // as the class. As a convenience, we include the original declaration + // as a comment. + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + DeclGroupRef::iterator I = D.begin(); + RewriteForwardClassEpilogue(cast(*I), typedefString); +} + +void RewriteModernObjC::RewriteForwardClassDecl( + const llvm::SmallVector &D) { + std::string typedefString; + for (unsigned i = 0; i < D.size(); i++) { + ObjCInterfaceDecl *ForwardDecl = cast(D[i]); + if (i == 0) { + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + RewriteForwardClassEpilogue(cast(D[0]), typedefString); +} + +void RewriteModernObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + // When method is a synthesized one, such as a getter/setter there is + // nothing to rewrite. + if (Method->isImplicit()) + return; + SourceLocation LocStart = Method->getLocStart(); + SourceLocation LocEnd = Method->getLocEnd(); + + if (SM->getExpansionLineNumber(LocEnd) > + SM->getExpansionLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n"); + ReplaceText(LocEnd, 1, ";\n#endif\n"); + } else { + InsertText(LocStart, "// "); + } +} + +void RewriteModernObjC::RewriteProperty(ObjCPropertyDecl *prop) { + SourceLocation Loc = prop->getAtLoc(); + + ReplaceText(Loc, 0, "// "); + // FIXME: handle properties that are declared across multiple lines. +} + +void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + SourceLocation LocStart = CatDecl->getLocStart(); + + // FIXME: handle category headers that are declared across multiple lines. + if (CatDecl->getIvarRBraceLoc().isValid()) { + ReplaceText(LocStart, 1, "/** "); + ReplaceText(CatDecl->getIvarRBraceLoc(), 1, "**/ "); + } + else { + ReplaceText(LocStart, 0, "// "); + } + + for (ObjCCategoryDecl::prop_iterator I = CatDecl->prop_begin(), + E = CatDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + + for (ObjCCategoryDecl::instmeth_iterator + I = CatDecl->instmeth_begin(), E = CatDecl->instmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCCategoryDecl::classmeth_iterator + I = CatDecl->classmeth_begin(), E = CatDecl->classmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndRange().getBegin(), + strlen("@end"), "/* @end */"); +} + +void RewriteModernObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getLocStart(); + assert(PDecl->isThisDeclarationADefinition()); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (ObjCProtocolDecl::instmeth_iterator + I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCProtocolDecl::classmeth_iterator + I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + + for (ObjCInterfaceDecl::prop_iterator I = PDecl->prop_begin(), + E = PDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); + ReplaceText(LocEnd, strlen("@end"), "/* @end */"); + + // Must comment out @optional/@required + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + for (const char *p = startBuf; p < endBuf; p++) { + if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); + + } + } +} + +void RewriteModernObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { + SourceLocation LocStart = (*D.begin())->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteModernObjC::RewriteForwardProtocolDecl(const llvm::SmallVector &DG) { + SourceLocation LocStart = DG[0]->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteModernObjC::RewriteLinkageSpec(LinkageSpecDecl *LSD) { + SourceLocation LocStart = LSD->getExternLoc(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid extern SourceLocation"); + + ReplaceText(LocStart, 0, "// "); + if (!LSD->hasBraces()) + return; + // FIXME. We don't rewrite well if '{' is not on same line as 'extern'. + SourceLocation LocRBrace = LSD->getRBraceLoc(); + if (LocRBrace.isInvalid()) + llvm_unreachable("Invalid rbrace SourceLocation"); + ReplaceText(LocRBrace, 0, "// "); +} + +void RewriteModernObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType) { + if (T->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (T->isFunctionPointerType() || + T->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = T; + QualType PointeeTy; + if (const PointerType* PT = retType->getAs()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAs()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAs())) { + ResultStr += FPRetType->getResultType().getAsString( + Context->getPrintingPolicy()); + ResultStr += "(*"; + } + } else + ResultStr += T.getAsString(Context->getPrintingPolicy()); +} + +void RewriteModernObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = 0; + ResultStr += "\nstatic "; + RewriteTypeIntoString(OMD->getResultType(), ResultStr, FPRetType); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += IDecl->getNameAsString(); + NameStr += "_"; + + if (ObjCCategoryImplDecl *CID = + dyn_cast(OMD->getDeclContext())) { + NameStr += CID->getNameAsString(); + NameStr += "_"; + } + // Append selector names, replacing ':' with '_' + { + std::string selString = OMD->getSelector().getAsString(); + int len = selString.size(); + for (int i = 0; i < len; i++) + if (selString[i] == ':') + selString[i] = '_'; + NameStr += selString; + } + // Remember this name for metadata emission + MethodInternalNames[OMD] = NameStr; + ResultStr += NameStr; + + // Rewrite arguments + ResultStr += "("; + + // invisible arguments + if (OMD->isInstanceMethod()) { + QualType selfTy = Context->getObjCInterfaceType(IDecl); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.MicrosoftExt) { + if (ObjCSynthesizedStructs.count(const_cast(IDecl))) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += IDecl->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCClassType().getAsString( + Context->getPrintingPolicy()); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); + ResultStr += " _cmd"; + + // Method arguments. + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + ParmVarDecl *PDecl = *PI; + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + QualType QT = PDecl->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + ResultStr += Name; + } + } + if (OMD->isVariadic()) + ResultStr += ", ..."; + ResultStr += ") "; + + if (FPRetType) { + ResultStr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast(FPRetType)) { + ResultStr += "("; + for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = FT->getArgType(i).getAsString( + Context->getPrintingPolicy()); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumArgs()) ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} +void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast(OID); + ObjCCategoryImplDecl *CID = dyn_cast(OID); + + if (IMD) { + if (IMD->getIvarRBraceLoc().isValid()) { + ReplaceText(IMD->getLocStart(), 1, "/** "); + ReplaceText(IMD->getIvarRBraceLoc(), 1, "**/ "); + } + else { + InsertText(IMD->getLocStart(), "// "); + } + } + else + InsertText(CID->getLocStart(), "// "); + + for (ObjCCategoryImplDecl::instmeth_iterator + I = IMD ? IMD->instmeth_begin() : CID->instmeth_begin(), + E = IMD ? IMD->instmeth_end() : CID->instmeth_end(); + I != E; ++I) { + std::string ResultStr; + ObjCMethodDecl *OMD = *I; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + + for (ObjCCategoryImplDecl::classmeth_iterator + I = IMD ? IMD->classmeth_begin() : CID->classmeth_begin(), + E = IMD ? IMD->classmeth_end() : CID->classmeth_end(); + I != E; ++I) { + std::string ResultStr; + ObjCMethodDecl *OMD = *I; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + for (ObjCCategoryImplDecl::propimpl_iterator + I = IMD ? IMD->propimpl_begin() : CID->propimpl_begin(), + E = IMD ? IMD->propimpl_end() : CID->propimpl_end(); + I != E; ++I) { + RewritePropertyImplDecl(*I, IMD, CID); + } + + InsertText(IMD ? IMD->getLocEnd() : CID->getLocEnd(), "// "); +} + +void RewriteModernObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + // Do not synthesize more than once. + if (ObjCSynthesizedStructs.count(ClassDecl)) + return; + // Make sure super class's are written before current class is written. + ObjCInterfaceDecl *SuperClass = ClassDecl->getSuperClass(); + while (SuperClass) { + RewriteInterfaceDecl(SuperClass); + SuperClass = SuperClass->getSuperClass(); + } + std::string ResultStr; + if (!ObjCWrittenInterfaces.count(ClassDecl->getCanonicalDecl())) { + // we haven't seen a forward decl - generate a typedef. + RewriteOneForwardClassDecl(ClassDecl, ResultStr); + RewriteIvarOffsetSymbols(ClassDecl, ResultStr); + + RewriteObjCInternalStruct(ClassDecl, ResultStr); + // Mark this typedef as having been written into its c++ equivalent. + ObjCWrittenInterfaces.insert(ClassDecl->getCanonicalDecl()); + + for (ObjCInterfaceDecl::prop_iterator I = ClassDecl->prop_begin(), + E = ClassDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + for (ObjCInterfaceDecl::instmeth_iterator + I = ClassDecl->instmeth_begin(), E = ClassDecl->instmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCInterfaceDecl::classmeth_iterator + I = ClassDecl->classmeth_begin(), E = ClassDecl->classmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), + "/* @end */"); + } +} + +Stmt *RewriteModernObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base; + SmallVector Args; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = 0; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast(Base)->getSourceExpr(); + Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + + unsigned numArgs = OldMsg->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + Expr *Arg = OldMsg->getArg(i); + if (isa(Arg)) + Arg = cast(Arg)->getSourceExpr(); + Arg = cast(RewriteFunctionBodyOrGlobalInitializer(Arg)); + Args.push_back(Arg); + } + } + + // TODO: avoid this copy. + SmallVector SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg = 0; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +Stmt *RewriteModernObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = 0; + SmallVector Args; + { + DisableReplaceStmtScope S(*this); + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast(Base)->getSourceExpr(); + Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + unsigned numArgs = OldMsg->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + Expr *Arg = OldMsg->getArg(i); + if (isa(Arg)) + Arg = cast(Arg)->getSourceExpr(); + Arg = cast(RewriteFunctionBodyOrGlobalInitializer(Arg)); + Args.push_back(Arg); + } + } + + // Intentionally empty. + SmallVector SelLocs; + + ObjCMessageExpr *NewMsg = 0; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +/// SynthCountByEnumWithState - To print: +/// ((unsigned int (*) +/// (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) +/// (void *)objc_msgSend)((id)l_collection, +/// sel_registerName( +/// "countByEnumeratingWithState:objects:count:"), +/// &enumState, +/// (id *)__rw_items, (unsigned int)16) +/// +void RewriteModernObjC::SynthCountByEnumWithState(std::string &buf) { + buf += "((unsigned int (*) (id, SEL, struct __objcFastEnumerationState *, " + "id *, unsigned int))(void *)objc_msgSend)"; + buf += "\n\t\t"; + buf += "((id)l_collection,\n\t\t"; + buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; + buf += "\n\t\t"; + buf += "&enumState, " + "(id *)__rw_items, (unsigned int)16)"; +} + +/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach +/// statement to exit to its outer synthesized loop. +/// +Stmt *RewriteModernObjC::RewriteBreakStmt(BreakStmt *S) { + if (Stmts.empty() || !isa(Stmts.back())) + return S; + // replace break with goto __break_label + std::string buf; + + SourceLocation startLoc = S->getLocStart(); + buf = "goto __break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("break"), buf); + + return 0; +} + +void RewriteModernObjC::ConvertSourceLocationToLineDirective( + SourceLocation Loc, + std::string &LineString) { + if (Loc.isFileID()) { + LineString += "\n#line "; + PresumedLoc PLoc = SM->getPresumedLoc(Loc); + LineString += utostr(PLoc.getLine()); + LineString += " \""; + LineString += Lexer::Stringify(PLoc.getFilename()); + LineString += "\"\n"; + } +} + +/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach +/// statement to continue with its inner synthesized loop. +/// +Stmt *RewriteModernObjC::RewriteContinueStmt(ContinueStmt *S) { + if (Stmts.empty() || !isa(Stmts.back())) + return S; + // replace continue with goto __continue_label + std::string buf; + + SourceLocation startLoc = S->getLocStart(); + buf = "goto __continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("continue"), buf); + + return 0; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id __rw_items[16]; +/// id l_collection = (id)collection; +/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]; +/// if (limit) { +/// unsigned long startMutations = *enumState.mutationsPtr; +/// do { +/// unsigned long counter = 0; +/// do { +/// if (startMutations != *enumState.mutationsPtr) +/// objc_enumerationMutation(l_collection); +/// elem = (type)enumState.itemsPtr[counter++]; +/// stmts; +/// __continue_label: ; +/// } while (counter < limit); +/// } while (limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]); +/// elem = nil; +/// __break_label: ; +/// } +/// else +/// elem = nil; +/// } +/// +Stmt *RewriteModernObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd) { + assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); + assert(isa(Stmts.back()) && + "ObjCForCollectionStmt Statement stack mismatch"); + assert(!ObjCBcLabelNo.empty() && + "ObjCForCollectionStmt - Label No stack empty"); + + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + StringRef elementName; + std::string elementTypeAsString; + std::string buf; + // line directive first. + SourceLocation ForEachLoc = S->getForLoc(); + ConvertSourceLocationToLineDirective(ForEachLoc, buf); + buf += "{\n\t"; + if (DeclStmt *DS = dyn_cast(S->getElement())) { + // type elem; + NamedDecl* D = cast(DS->getSingleDecl()); + QualType ElementType = cast(D)->getType(); + if (ElementType->isObjCQualifiedIdType() || + ElementType->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); + buf += elementTypeAsString; + buf += " "; + elementName = D->getName(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast(S->getElement()); + elementName = DR->getDecl()->getName(); + ValueDecl *VD = cast(DR->getDecl()); + if (VD->getType()->isObjCQualifiedIdType() || + VD->getType()->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id __rw_items[16]; + buf += "id __rw_items[16];\n\t"; + // id l_collection = (id) + buf += "id l_collection = (id)"; + // Find start location of 'collection' the hard way! + const char *startCollectionBuf = startBuf; + startCollectionBuf += 3; // skip 'for' + startCollectionBuf = strchr(startCollectionBuf, '('); + startCollectionBuf++; // skip '(' + // find 'in' and skip it. + while (*startCollectionBuf != ' ' || + *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || + (*(startCollectionBuf+3) != ' ' && + *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) + startCollectionBuf++; + startCollectionBuf += 3; + + // Replace: "for (type element in" with string constructed thus far. + ReplaceText(startLoc, startCollectionBuf - startBuf, buf); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:__rw_items count:16]; + // which is synthesized into: + // unsigned int limit = + // ((unsigned int (*) + // (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) + // (void *)objc_msgSend)((id)l_collection, + // sel_registerName( + // "countByEnumeratingWithState:objects:count:"), + // (struct __objcFastEnumerationState *)&state, + // (id *)__rw_items, (unsigned int)16); + buf += "unsigned long limit =\n\t\t"; + SynthCountByEnumWithState(buf); + buf += ";\n\t"; + /// if (limit) { + /// unsigned long startMutations = *enumState.mutationsPtr; + /// do { + /// unsigned long counter = 0; + /// do { + /// if (startMutations != *enumState.mutationsPtr) + /// objc_enumerationMutation(l_collection); + /// elem = (type)enumState.itemsPtr[counter++]; + buf += "if (limit) {\n\t"; + buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; + buf += "do {\n\t\t"; + buf += "unsigned long counter = 0;\n\t\t"; + buf += "do {\n\t\t\t"; + buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; + buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; + buf += elementName; + buf += " = ("; + buf += elementTypeAsString; + buf += ")enumState.itemsPtr[counter++];"; + // Replace ')' in for '(' type elem in collection ')' with all of these. + ReplaceText(lparenLoc, 1, buf); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while (limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:__rw_items count:16]); + /// elem = nil; + /// __break_label: ; + /// } + /// else + /// elem = nil; + /// } + /// + buf = ";\n\t"; + buf += "__continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;"; + buf += "\n\t\t"; + buf += "} while (counter < limit);\n\t"; + buf += "} while (limit = "; + SynthCountByEnumWithState(buf); + buf += ");\n\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "}\n"; + + // Insert all these *after* the statement body. + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (isa(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); + InsertText(endBodyLoc, buf); + } else { + /* Need to treat single statements specially. For example: + * + * for (A *a in b) if (stuff()) break; + * for (A *a in b) xxxyy; + * + * The following code simply scans ahead to the semi to find the actual end. + */ + const char *stmtBuf = SM->getCharacterData(OrigEnd); + const char *semiBuf = strchr(stmtBuf, ';'); + assert(semiBuf && "Can't find ';'"); + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return 0; +} + +static void Write_RethrowObject(std::string &buf) { + buf += "{ struct _FIN { _FIN(id reth) : rethrow(reth) {}\n"; + buf += "\t~_FIN() { if (rethrow) objc_exception_throw(rethrow); }\n"; + buf += "\tid rethrow;\n"; + buf += "\t} _fin_force_rethow(_rethrow);"; +} + +/// RewriteObjCSynchronizedStmt - +/// This routine rewrites @synchronized(expr) stmt; +/// into: +/// objc_sync_enter(expr); +/// @try stmt @finally { objc_sync_exit(expr); } +/// +Stmt *RewriteModernObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @synchronized location"); + + std::string buf; + SourceLocation SynchLoc = S->getAtSynchronizedLoc(); + ConvertSourceLocationToLineDirective(SynchLoc, buf); + buf += "{ id _rethrow = 0; id _sync_obj = "; + + const char *lparenBuf = startBuf; + while (*lparenBuf != '(') lparenBuf++; + ReplaceText(startLoc, lparenBuf-startBuf+1, buf); + + buf = "; objc_sync_enter(_sync_obj);\n"; + buf += "try {\n\tstruct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}"; + buf += "\n\t~_SYNC_EXIT() {objc_sync_exit(sync_exit);}"; + buf += "\n\tid sync_exit;"; + buf += "\n\t} _sync_exit(_sync_obj);\n"; + + // We can't use S->getSynchExpr()->getLocEnd() to find the end location, since + // the sync expression is typically a message expression that's already + // been rewritten! (which implies the SourceLocation's are invalid). + SourceLocation RParenExprLoc = S->getSynchBody()->getLocStart(); + const char *RParenExprLocBuf = SM->getCharacterData(RParenExprLoc); + while (*RParenExprLocBuf != ')') RParenExprLocBuf--; + RParenExprLoc = startLoc.getLocWithOffset(RParenExprLocBuf-startBuf); + + SourceLocation LBranceLoc = S->getSynchBody()->getLocStart(); + const char *LBraceLocBuf = SM->getCharacterData(LBranceLoc); + assert (*LBraceLocBuf == '{'); + ReplaceText(RParenExprLoc, (LBraceLocBuf - SM->getCharacterData(RParenExprLoc) + 1), buf); + + SourceLocation startRBraceLoc = S->getSynchBody()->getLocEnd(); + assert((*SM->getCharacterData(startRBraceLoc) == '}') && + "bogus @synchronized block"); + + buf = "} catch (id e) {_rethrow = e;}\n"; + Write_RethrowObject(buf); + buf += "}\n"; + buf += "}\n"; + + ReplaceText(startRBraceLoc, 1, buf); + + return 0; +} + +void RewriteModernObjC::WarnAboutReturnGotoStmts(Stmt *S) +{ + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) + WarnAboutReturnGotoStmts(*CI); + + if (isa(S) || isa(S)) { + Diags.Report(Context->getFullLoc(S->getLocStart()), + TryFinallyContainsReturnDiag); + } + return; +} + +Stmt *RewriteModernObjC::RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + SourceLocation startLoc = S->getAtLoc(); + ReplaceText(startLoc, strlen("@autoreleasepool"), "/* @autoreleasepool */"); + ReplaceText(S->getSubStmt()->getLocStart(), 1, + "{ __AtAutoreleasePool __autoreleasepool; "); + + return 0; +} + +Stmt *RewriteModernObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { + ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt(); + bool noCatch = S->getNumCatchStmts() == 0; + std::string buf; + SourceLocation TryLocation = S->getAtTryLoc(); + ConvertSourceLocationToLineDirective(TryLocation, buf); + + if (finalStmt) { + if (noCatch) + buf += "{ id volatile _rethrow = 0;\n"; + else { + buf += "{ id volatile _rethrow = 0;\ntry {\n"; + } + } + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @try location"); + if (finalStmt) + ReplaceText(startLoc, 1, buf); + else + // @try -> try + ReplaceText(startLoc, 1, ""); + + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + VarDecl *catchDecl = Catch->getCatchParamDecl(); + + startLoc = Catch->getLocStart(); + bool AtRemoved = false; + if (catchDecl) { + QualType t = catchDecl->getType(); + if (const ObjCObjectPointerType *Ptr = t->getAs()) { + // Should be a pointer to a class. + ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); + if (IDecl) { + std::string Result; + ConvertSourceLocationToLineDirective(Catch->getLocStart(), Result); + + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @catch location"); + SourceLocation rParenLoc = Catch->getRParenLoc(); + const char *rParenBuf = SM->getCharacterData(rParenLoc); + + // _objc_exc_Foo *_e as argument to catch. + Result += "catch (_objc_exc_"; Result += IDecl->getNameAsString(); + Result += " *_"; Result += catchDecl->getNameAsString(); + Result += ")"; + ReplaceText(startLoc, rParenBuf-startBuf+1, Result); + // Foo *e = (Foo *)_e; + Result.clear(); + Result = "{ "; + Result += IDecl->getNameAsString(); + Result += " *"; Result += catchDecl->getNameAsString(); + Result += " = ("; Result += IDecl->getNameAsString(); Result += "*)"; + Result += "_"; Result += catchDecl->getNameAsString(); + + Result += "; "; + SourceLocation lBraceLoc = Catch->getCatchBody()->getLocStart(); + ReplaceText(lBraceLoc, 1, Result); + AtRemoved = true; + } + } + } + if (!AtRemoved) + // @catch -> catch + ReplaceText(startLoc, 1, ""); + + } + if (finalStmt) { + buf.clear(); + SourceLocation FinallyLoc = finalStmt->getLocStart(); + + if (noCatch) { + ConvertSourceLocationToLineDirective(FinallyLoc, buf); + buf += "catch (id e) {_rethrow = e;}\n"; + } + else { + buf += "}\n"; + ConvertSourceLocationToLineDirective(FinallyLoc, buf); + buf += "catch (id e) {_rethrow = e;}\n"; + } + + SourceLocation startFinalLoc = finalStmt->getLocStart(); + ReplaceText(startFinalLoc, 8, buf); + Stmt *body = finalStmt->getFinallyBody(); + SourceLocation startFinalBodyLoc = body->getLocStart(); + buf.clear(); + Write_RethrowObject(buf); + ReplaceText(startFinalBodyLoc, 1, buf); + + SourceLocation endFinalBodyLoc = body->getLocEnd(); + ReplaceText(endFinalBodyLoc, 1, "}\n}"); + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoStmts(S->getTryBody()); + } + + return 0; +} + +// This can't be done with ReplaceStmt(S, ThrowExpr), since +// the throw expression is typically a message expression that's already +// been rewritten! (which implies the SourceLocation's are invalid). +Stmt *RewriteModernObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @throw location"); + + std::string buf; + /* void objc_exception_throw(id) __attribute__((noreturn)); */ + if (S->getThrowExpr()) + buf = "objc_exception_throw("; + else + buf = "throw"; + + // handle "@ throw" correctly. + const char *wBuf = strchr(startBuf, 'w'); + assert((*wBuf == 'w') && "@throw: can't find 'w'"); + ReplaceText(startLoc, wBuf-startBuf+1, buf); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); + if (S->getThrowExpr()) + ReplaceText(semiLoc, 1, ");"); + return 0; +} + +Stmt *RewriteModernObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + QualType StrType = Context->getPointerType(Context->CharTy); + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = StringLiteral::Create(*Context, StrEncoding, + StringLiteral::Ascii, false, + StrType, SourceLocation()); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return Replacement; +} + +Stmt *RewriteModernObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); + // Create a call to sel_registerName("selName"). + SmallVector SelExprs; + QualType argType = Context->getPointerType(Context->CharTy); + SelExprs.push_back(StringLiteral::Create(*Context, + Exp->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size()); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return SelExp; +} + +CallExpr *RewriteModernObjC::SynthesizeCallToFunctionDecl( + FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc, + SourceLocation EndLoc) { + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = FD->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = + new (Context) DeclRefExpr(FD, false, msgSendType, VK_LValue, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, + DRE, 0, VK_RValue); + + const FunctionType *FT = msgSendType->getAs(); + + CallExpr *Exp = + new (Context) CallExpr(*Context, ICE, llvm::makeArrayRef(args, nargs), + FT->getCallResultType(*Context), + VK_RValue, EndLoc); + return Exp; +} + +static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, + const char *&startRef, const char *&endRef) { + while (startBuf < endBuf) { + if (*startBuf == '<') + startRef = startBuf; // mark the start. + if (*startBuf == '>') { + if (startRef && *startRef == '<') { + endRef = startBuf; // mark the end. + return true; + } + return false; + } + startBuf++; + } + return false; +} + +static void scanToNextArgument(const char *&argRef) { + int angle = 0; + while (*argRef != ')' && (*argRef != ',' || angle > 0)) { + if (*argRef == '<') + angle++; + else if (*argRef == '>') + angle--; + argRef++; + } + assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); +} + +bool RewriteModernObjC::needToScanForQualifiers(QualType T) { + if (T->isObjCQualifiedIdType()) + return true; + if (const PointerType *PT = T->getAs()) { + if (PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + if (T->isObjCObjectPointerType()) { + T = T->getPointeeType(); + return T->isObjCQualifiedInterfaceType(); + } + if (T->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(T); + return needToScanForQualifiers(ElemTy); + } + return false; +} + +void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { + QualType Type = E->getType(); + if (needToScanForQualifiers(Type)) { + SourceLocation Loc, EndLoc; + + if (const CStyleCastExpr *ECE = dyn_cast(E)) { + Loc = ECE->getLParenLoc(); + EndLoc = ECE->getRParenLoc(); + } else { + Loc = E->getLocStart(); + EndLoc = E->getLocEnd(); + } + // This will defend against trying to rewrite synthesized expressions. + if (Loc.isInvalid() || EndLoc.isInvalid()) + return; + + const char *startBuf = SM->getCharacterData(Loc); + const char *endBuf = SM->getCharacterData(EndLoc); + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } +} + +void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = 0; + if (VarDecl *VD = dyn_cast(Dcl)) { + Loc = VD->getLocation(); + Type = VD->getType(); + } + else if (FunctionDecl *FD = dyn_cast(Dcl)) { + Loc = FD->getLocation(); + // Check for ObjC 'id' and class types that have been adorned with protocol + // information (id

, C

*). The protocol references need to be rewritten! + const FunctionType *funcType = FD->getType()->getAs(); + assert(funcType && "missing function type"); + proto = dyn_cast(funcType); + if (!proto) + return; + Type = proto->getResultType(); + } + else if (FieldDecl *FD = dyn_cast(Dcl)) { + Loc = FD->getLocation(); + Type = FD->getType(); + } + else + return; + + if (needToScanForQualifiers(Type)) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = SM->getCharacterData(Loc); + const char *startBuf = endBuf; + while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) + startBuf--; // scan backward (from the decl location) for return type. + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } + if (!proto) + return; // most likely, was a variable + // Now check arguments. + const char *startBuf = SM->getCharacterData(Loc); + const char *startFuncBuf = startBuf; + for (unsigned i = 0; i < proto->getNumArgs(); i++) { + if (needToScanForQualifiers(proto->getArgType(i))) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = startBuf; + // scan forward (from the decl location) for argument types. + scanToNextArgument(endBuf); + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + startBuf = ++endBuf; + } + else { + // If the function name is derived from a macro expansion, then the + // argument buffer will not follow the name. Need to speak with Chris. + while (*startBuf && *startBuf != ')' && *startBuf != ',') + startBuf++; // scan forward (from the decl location) for argument types. + startBuf++; + } + } +} + +void RewriteModernObjC::RewriteTypeOfDecl(VarDecl *ND) { + QualType QT = ND->getType(); + const Type* TypePtr = QT->getAs(); + if (!isa(TypePtr)) + return; + while (isa(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + TypePtr = QT->getAs(); + } + // FIXME. This will not work for multiple declarators; as in: + // __typeof__(a) b,c,d; + std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + const char *startBuf = SM->getCharacterData(DeclLoc); + if (ND->getInit()) { + std::string Name(ND->getNameAsString()); + TypeAsString += " " + Name + " = "; + Expr *E = ND->getInit(); + SourceLocation startLoc; + if (const CStyleCastExpr *ECE = dyn_cast(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + const char *endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } + else { + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteModernObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + SmallVector ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getFuncType = + getSimpleFunctionType(Context->getObjCSelType(), &ArgTys[0], ArgTys.size()); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + SelGetUidIdent, getFuncType, 0, + SC_Extern, + SC_None, false); +} + +void RewriteModernObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in + if (FD->getIdentifier() && + FD->getName() == "sel_registerName") { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +void RewriteModernObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + if (!strchr(argPtr, '^')) { + Str += TypeString; + return; + } + while (*argPtr) { + Str += (*argPtr == '^' ? '*' : *argPtr); + argPtr++; + } +} + +// FIXME. Consolidate this routine with RewriteBlockPointerType. +void RewriteModernObjC::RewriteBlockPointerTypeVariable(std::string& Str, + ValueDecl *VD) { + QualType Type = VD->getType(); + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + Str += *argPtr; + paren++; + break; + case ')': + Str += *argPtr; + paren--; + break; + case '^': + Str += '*'; + if (paren == 1) + Str += VD->getNameAsString(); + break; + default: + Str += *argPtr; + break; + } + argPtr++; + } +} + +void RewriteModernObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs(); + const FunctionProtoType *proto = dyn_cast(funcType); + if (!proto) + return; + QualType Type = proto->getResultType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getArgType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + if (FD->isVariadic()) { + FdStr += (numArgs > 0) ? ", ...);\n" : "...);\n"; + } + else + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); +} + +// SynthSuperContructorFunctionDecl - id __rw_objc_super(id obj, id super); +void RewriteModernObjC::SynthSuperContructorFunctionDecl() { + if (SuperContructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size()); + SuperContructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(void); +void RewriteModernObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + SmallVector ArgTys; + ArgTys.push_back(Context->VoidTy); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], 1, + true /*isVariadic*/); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(void); +void RewriteModernObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + SmallVector ArgTys; + ArgTys.push_back(Context->VoidTy); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], 1, + true /*isVariadic*/); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->DoubleTy, + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthGetClassFunctionDecl - Class objc_getClass(const char *name); +void RewriteModernObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + SmallVector ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + &ArgTys[0], ArgTys.size()); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, 0, + SC_Extern, + SC_None, false); +} + +// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); +void RewriteModernObjC::SynthGetSuperClassFunctionDecl() { + IdentifierInfo *getSuperClassIdent = + &Context->Idents.get("class_getSuperclass"); + SmallVector ArgTys; + ArgTys.push_back(Context->getObjCClassType()); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + &ArgTys[0], ArgTys.size()); + GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getSuperClassIdent, + getClassType, 0, + SC_Extern, + SC_None, + false); +} + +// SynthGetMetaClassFunctionDecl - Class objc_getMetaClass(const char *name); +void RewriteModernObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + SmallVector ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + &ArgTys[0], ArgTys.size()); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, 0, + SC_Extern, + SC_None, false); +} + +Stmt *RewriteModernObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + QualType strType = getConstantStringStructType(); + + std::string S = "__NSConstantStringImpl_"; + + std::string tmpName = InFileName; + unsigned i; + for (i=0; i < tmpName.length(); i++) { + char c = tmpName.at(i); + // replace any non alphanumeric characters with '_'. + if (!isalpha(c) && (c < '0' || c > '9')) + tmpName[i] = '_'; + } + S += tmpName; + S += "_"; + S += utostr(NumObjCStringLiterals++); + + Preamble += "static __NSConstantStringImpl " + S; + Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; + Preamble += "0x000007c8,"; // utf8_str + // The pretty printer for StringLiteral handles escape characters properly. + std::string prettyBufS; + llvm::raw_string_ostream prettyBuf(prettyBufS); + Exp->getString()->printPretty(prettyBuf, 0, PrintingPolicy(LangOpts)); + Preamble += prettyBuf.str(); + Preamble += ","; + Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(S), + strType, 0, SC_Static, SC_None); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, strType, VK_LValue, + SourceLocation()); + Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + // cast to NSConstantString * + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), + CK_CPointerToObjCPointerCast, Unop); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return cast; +} + +Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) { + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + + Expr *FlagExp = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, Exp->getValue()), + Context->IntTy, Exp->getLocation()); + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Context->ObjCBuiltinBoolTy, + CK_BitCast, FlagExp); + ParenExpr *PE = new (Context) ParenExpr(Exp->getLocation(), Exp->getExprLoc(), + cast); + ReplaceStmt(Exp, PE); + return PE; +} + +Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getLocStart(); + SourceLocation EndLoc = Exp->getLocEnd(); + + // Synthesize a call to objc_msgSend(). + SmallVector MsgExprs; + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + + // Create a call to objc_getClass(""). It will be the 1st argument. + ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod(); + ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface(); + + IdentifierInfo *clsName = BoxingClass->getIdentifier(); + ClsExprs.push_back(StringLiteral::Create(*Context, + clsName->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName(":"), etc. + // it will be the 2nd argument. + SmallVector SelExprs; + SelExprs.push_back(StringLiteral::Create(*Context, + BoxingMethod->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // User provided sub-expression is the 3rd, and last, argument. + Expr *subExpr = Exp->getSubExpr(); + if (ImplicitCastExpr *ICE = dyn_cast(subExpr)) { + QualType type = ICE->getType(); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK = CK_BitCast; + if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType()) + CK = CK_IntegralToBoolean; + subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr); + } + MsgExprs.push_back(subExpr); + + SmallVector ArgTypes; + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (ObjCMethodDecl::param_iterator PI = BoxingMethod->param_begin(), + E = BoxingMethod->param_end(); PI != E; ++PI) + ArgTypes.push_back((*PI)->getType()); + + QualType returnType = Exp->getType(); + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, + VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + BoxingMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs(); + CallExpr *CE = new (Context) CallExpr(*Context, PE, MsgExprs, + FT->getResultType(), VK_RValue, + EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +Stmt *RewriteModernObjC::RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getLocStart(); + SourceLocation EndLoc = Exp->getLocEnd(); + + // Build the expression: __NSContainer_literal(int, ...).arr + QualType IntQT = Context->IntTy; + QualType NSArrayFType = + getSimpleFunctionType(Context->VoidTy, &IntQT, 1, true); + std::string NSArrayFName("__NSContainer_literal"); + FunctionDecl *NSArrayFD = SynthBlockInitFunctionDecl(NSArrayFName); + DeclRefExpr *NSArrayDRE = + new (Context) DeclRefExpr(NSArrayFD, false, NSArrayFType, VK_RValue, + SourceLocation()); + + SmallVector InitExprs; + unsigned NumElements = Exp->getNumElements(); + unsigned UnsignedIntSize = + static_cast(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *count = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + InitExprs.push_back(count); + for (unsigned i = 0; i < NumElements; i++) + InitExprs.push_back(Exp->getElement(i)); + Expr *NSArrayCallExpr = + new (Context) CallExpr(*Context, NSArrayDRE, InitExprs, + NSArrayFType, VK_LValue, SourceLocation()); + + FieldDecl *ARRFD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("arr"), + Context->getPointerType(Context->VoidPtrTy), 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ArrayLiteralME = + new (Context) MemberExpr(NSArrayCallExpr, false, ARRFD, + SourceLocation(), + ARRFD->getType(), VK_LValue, + OK_Ordinary); + QualType ConstIdT = Context->getObjCIdType().withConst(); + CStyleCastExpr * ArrayLiteralObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + ArrayLiteralME); + + // Synthesize a call to objc_msgSend(). + SmallVector MsgExprs; + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + QualType expType = Exp->getType(); + + // Create a call to objc_getClass("NSArray"). It will be th 1st argument. + ObjCInterfaceDecl *Class = + expType->getPointeeType()->getAs()->getInterface(); + + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(StringLiteral::Create(*Context, + clsName->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("arrayWithObjects:count:"). + // it will be the 2nd argument. + SmallVector SelExprs; + ObjCMethodDecl *ArrayMethod = Exp->getArrayWithObjectsMethod(); + SelExprs.push_back(StringLiteral::Create(*Context, + ArrayMethod->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // (const id [])objects + MsgExprs.push_back(ArrayLiteralObjects); + + // (NSUInteger)cnt + Expr *cnt = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + MsgExprs.push_back(cnt); + + + SmallVector ArgTypes; + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (ObjCMethodDecl::param_iterator PI = ArrayMethod->param_begin(), + E = ArrayMethod->param_end(); PI != E; ++PI) + ArgTypes.push_back((*PI)->getType()); + + QualType returnType = Exp->getType(); + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, + VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + ArrayMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs(); + CallExpr *CE = new (Context) CallExpr(*Context, PE, MsgExprs, + FT->getResultType(), VK_RValue, + EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +Stmt *RewriteModernObjC::RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getLocStart(); + SourceLocation EndLoc = Exp->getLocEnd(); + + // Build the expression: __NSContainer_literal(int, ...).arr + QualType IntQT = Context->IntTy; + QualType NSDictFType = + getSimpleFunctionType(Context->VoidTy, &IntQT, 1, true); + std::string NSDictFName("__NSContainer_literal"); + FunctionDecl *NSDictFD = SynthBlockInitFunctionDecl(NSDictFName); + DeclRefExpr *NSDictDRE = + new (Context) DeclRefExpr(NSDictFD, false, NSDictFType, VK_RValue, + SourceLocation()); + + SmallVector KeyExprs; + SmallVector ValueExprs; + + unsigned NumElements = Exp->getNumElements(); + unsigned UnsignedIntSize = + static_cast(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *count = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + KeyExprs.push_back(count); + ValueExprs.push_back(count); + for (unsigned i = 0; i < NumElements; i++) { + ObjCDictionaryElement Element = Exp->getKeyValueElement(i); + KeyExprs.push_back(Element.Key); + ValueExprs.push_back(Element.Value); + } + + // (const id [])objects + Expr *NSValueCallExpr = + new (Context) CallExpr(*Context, NSDictDRE, ValueExprs, + NSDictFType, VK_LValue, SourceLocation()); + + FieldDecl *ARRFD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("arr"), + Context->getPointerType(Context->VoidPtrTy), 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *DictLiteralValueME = + new (Context) MemberExpr(NSValueCallExpr, false, ARRFD, + SourceLocation(), + ARRFD->getType(), VK_LValue, + OK_Ordinary); + QualType ConstIdT = Context->getObjCIdType().withConst(); + CStyleCastExpr * DictValueObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + DictLiteralValueME); + // (const id [])keys + Expr *NSKeyCallExpr = + new (Context) CallExpr(*Context, NSDictDRE, KeyExprs, + NSDictFType, VK_LValue, SourceLocation()); + + MemberExpr *DictLiteralKeyME = + new (Context) MemberExpr(NSKeyCallExpr, false, ARRFD, + SourceLocation(), + ARRFD->getType(), VK_LValue, + OK_Ordinary); + + CStyleCastExpr * DictKeyObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + DictLiteralKeyME); + + + + // Synthesize a call to objc_msgSend(). + SmallVector MsgExprs; + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + QualType expType = Exp->getType(); + + // Create a call to objc_getClass("NSArray"). It will be th 1st argument. + ObjCInterfaceDecl *Class = + expType->getPointeeType()->getAs()->getInterface(); + + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(StringLiteral::Create(*Context, + clsName->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("arrayWithObjects:count:"). + // it will be the 2nd argument. + SmallVector SelExprs; + ObjCMethodDecl *DictMethod = Exp->getDictWithObjectsMethod(); + SelExprs.push_back(StringLiteral::Create(*Context, + DictMethod->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // (const id [])objects + MsgExprs.push_back(DictValueObjects); + + // (const id [])keys + MsgExprs.push_back(DictKeyObjects); + + // (NSUInteger)cnt + Expr *cnt = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + MsgExprs.push_back(cnt); + + + SmallVector ArgTypes; + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (ObjCMethodDecl::param_iterator PI = DictMethod->param_begin(), + E = DictMethod->param_end(); PI != E; ++PI) { + QualType T = (*PI)->getType(); + if (const PointerType* PT = T->getAs()) { + QualType PointeeTy = PT->getPointeeType(); + convertToUnqualifiedObjCType(PointeeTy); + T = Context->getPointerType(PointeeTy); + } + ArgTypes.push_back(T); + } + + QualType returnType = Exp->getType(); + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, + VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + DictMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs(); + CallExpr *CE = new (Context) CallExpr(*Context, PE, MsgExprs, + FT->getResultType(), VK_RValue, + EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +// struct __rw_objc_super { +// struct objc_object *object; struct objc_object *superClass; +// }; +QualType RewriteModernObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__rw_objc_super")); + QualType FieldTypes[2]; + + // struct objc_object *object; + FieldTypes[0] = Context->getObjCIdType(); + // struct objc_object *superClass; + FieldTypes[1] = Context->getObjCIdType(); + + // Create fields + for (unsigned i = 0; i < 2; ++i) { + SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, + SourceLocation(), + SourceLocation(), 0, + FieldTypes[i], 0, + /*BitWidth=*/0, + /*Mutable=*/false, + ICIS_NoInit)); + } + + SuperStructDecl->completeDefinition(); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteModernObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__NSConstantStringImpl")); + QualType FieldTypes[4]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // int flags; + FieldTypes[1] = Context->IntTy; + // char *str; + FieldTypes[2] = Context->getPointerType(Context->CharTy); + // long length; + FieldTypes[3] = Context->LongTy; + + // Create fields + for (unsigned i = 0; i < 4; ++i) { + ConstantStringDecl->addDecl(FieldDecl::Create(*Context, + ConstantStringDecl, + SourceLocation(), + SourceLocation(), 0, + FieldTypes[i], 0, + /*BitWidth=*/0, + /*Mutable=*/true, + ICIS_NoInit)); + } + + ConstantStringDecl->completeDefinition(); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +/// getFunctionSourceLocation - returns start location of a function +/// definition. Complication arises when function has declared as +/// extern "C" or extern "C" {...} +static SourceLocation getFunctionSourceLocation (RewriteModernObjC &R, + FunctionDecl *FD) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + return LSD->getExternLoc(); + } + if (FD->getStorageClassAsWritten() != SC_None) + R.RewriteBlockLiteralFunctionDecl(FD); + return FD->getTypeSpecStartLoc(); +} + +void RewriteModernObjC::RewriteLineDirective(const Decl *D) { + + SourceLocation Location = D->getLocation(); + + if (Location.isFileID()) { + std::string LineString("\n#line "); + PresumedLoc PLoc = SM->getPresumedLoc(Location); + LineString += utostr(PLoc.getLine()); + LineString += " \""; + LineString += Lexer::Stringify(PLoc.getFilename()); + if (isa(D)) + LineString += "\""; + else LineString += "\"\n"; + + Location = D->getLocStart(); + if (const FunctionDecl *FD = dyn_cast(D)) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + Location = LSD->getExternLoc(); + } + } + InsertText(Location, LineString); + } +} + +/// SynthMsgSendStretCallExpr - This routine translates message expression +/// into a call to objc_msgSend_stret() entry point. Tricky part is that +/// nil check on receiver must be performed before calling objc_msgSend_stret. +/// MsgSendStretFlavor - function declaration objc_msgSend_stret(...) +/// msgSendType - function type of objc_msgSend_stret(...) +/// returnType - Result type of the method being synthesized. +/// ArgTypes - type of the arguments passed to objc_msgSend_stret, starting with receiver type. +/// MsgExprs - list of argument expressions being passed to objc_msgSend_stret, +/// starting with receiver. +/// Method - Method being rewritten. +Expr *RewriteModernObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl &ArgTypes, + SmallVectorImpl &MsgExprs, + ObjCMethodDecl *Method) { + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + Method ? Method->isVariadic() : false); + castType = Context->getPointerType(castType); + + // build type for containing the objc_msgSend_stret object. + static unsigned stretCount=0; + std::string name = "__Stret"; name += utostr(stretCount); + std::string str = + "extern \"C\" void * __cdecl memset(void *_Dst, int _Val, size_t _Size);\n"; + str += "struct "; str += name; + str += " {\n\t"; + str += name; + str += "(id receiver, SEL sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + ArgTypes[i].getAsStringInternal(ArgName, Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + MsgExprs[i]->getType().getAsStringInternal(ArgName, + Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + + str += ") {\n"; + str += "\t if (receiver == 0)\n"; + str += "\t memset((void*)&s, 0, sizeof(s));\n"; + str += "\t else\n"; + str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); + str += ")(void *)objc_msgSend_stret)(receiver, sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + str += ", arg"; str += utostr(i); + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + str += ", arg"; str += utostr(i); + } + + str += ");\n"; + str += "\t}\n"; + str += "\t"; str += returnType.getAsString(Context->getPrintingPolicy()); + str += " s;\n"; + str += "};\n\n"; + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + else { + assert(CurMethodDef && "SynthMsgSendStretCallExpr - CurMethodDef is null"); + FunLocStart = CurMethodDef->getLocStart(); + } + + InsertText(FunLocStart, str); + ++stretCount; + + // AST for __Stretn(receiver, args).s; + IdentifierInfo *ID = &Context->Idents.get(name); + FunctionDecl *FD = FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, castType, 0, SC_Extern, + SC_None, false, false); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, castType, VK_RValue, + SourceLocation()); + CallExpr *STCE = new (Context) CallExpr(*Context, DRE, MsgExprs, + castType, VK_LValue, SourceLocation()); + + FieldDecl *FieldD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("s"), + returnType, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(STCE, false, FieldD, SourceLocation(), + FieldD->getType(), VK_LValue, + OK_Ordinary); + + return ME; +} + +Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetSuperClassFunctionDecl) + SynthGetSuperClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = 0; + if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { + QualType resultType = mDecl->getResultType(); + if (resultType->isRecordType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + SmallVector MsgExprs; + switch (Exp->getReceiverKind()) { + case ObjCMessageExpr::SuperClass: { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + + SmallVector InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, + SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + ClassDecl->getIdentifier()->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + // (Class)objc_getClass("CurrentClass") + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, + EndLoc); + ClsExprs.clear(); + ClsExprs.push_back(Cls); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct __rw_objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperContructorFunctionDecl(); + // Simulate a contructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, + SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct __rw_objc_super) { } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_LValue, + ILE, false); + // struct __rw_objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->getAs()->getInterface(); + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(StringLiteral::Create(*Context, + clsName->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls); + MsgExprs.push_back(ArgExpr); + break; + } + + case ObjCMessageExpr::SuperInstance:{ + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + SmallVector InitExprs; + + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + ClassDecl->getIdentifier()->getName(), + StringLiteral::Ascii, false, argType, + SourceLocation())); + // (Class)objc_getClass("CurrentClass") + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + ClsExprs.clear(); + ClsExprs.push_back(Cls); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct __rw_objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperContructorFunctionDecl(); + // Simulate a contructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct __rw_objc_super) { } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_RValue, ILE, + false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Instance: { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo *. + Expr *recExpr = Exp->getInstanceReceiver(); + while (CStyleCastExpr *CE = dyn_cast(recExpr)) + recExpr = CE->getSubExpr(); + CastKind CK = recExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : recExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + + recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, recExpr); + MsgExprs.push_back(recExpr); + break; + } + } + + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + SmallVector SelExprs; + QualType argType = Context->getPointerType(Context->CharTy); + SelExprs.push_back(StringLiteral::Create(*Context, + Exp->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, + EndLoc); + MsgExprs.push_back(SelExp); + + // Now push any user supplied arguments. + for (unsigned i = 0; i < Exp->getNumArgs(); i++) { + Expr *userExpr = Exp->getArg(i); + // Make all implicit casts explicit...ICE comes in handy:-) + if (ImplicitCastExpr *ICE = dyn_cast(userExpr)) { + // Reuse the ICE type, it is exactly what the doctor ordered. + QualType type = ICE->getType(); + if (needToScanForQualifiers(type)) + type = Context->getObjCIdType(); + // Make sure we convert "type (^)(...)" to "type (*)(...)". + (void)convertBlockPointerToFunctionPointer(type); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK; + if (SubExpr->getType()->isIntegralType(*Context) && + type->isBooleanType()) { + CK = CK_IntegralToBoolean; + } else if (type->isObjCObjectPointerType()) { + if (SubExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (SubExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + } else { + CK = CK_BitCast; + } + + userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + } + // Make id cast into an 'id' cast. + else if (CStyleCastExpr *CE = dyn_cast(userExpr)) { + if (CE->getType()->isObjCQualifiedIdType()) { + while ((CE = dyn_cast(userExpr))) + userExpr = CE->getSubExpr(); + CastKind CK; + if (userExpr->getType()->isIntegralType(*Context)) { + CK = CK_IntegralToPointer; + } else if (userExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (userExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, userExpr); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. For now, we *don't* null + // out the argument in the original expression (since we aren't deleting + // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. + //Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + SmallVector ArgTypes; + QualType returnType; + + // Push 'id' and 'SEL', the 2 implicit arguments. + if (MsgSendFlavor == MsgSendSuperFunctionDecl) + ArgTypes.push_back(Context->getPointerType(getSuperStructType())); + else + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { + // Push any user argument types. + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + QualType t = (*PI)->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : (*PI)->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(t); + ArgTypes.push_back(t); + } + returnType = Exp->getType(); + convertToUnqualifiedObjCType(returnType); + (void)convertBlockPointerToFunctionPointer(returnType); + } else { + returnType = Context->getObjCIdType(); + } + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, + VK_LValue, SourceLocation()); + + // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). + // If we don't do this cast, we get the following bizarre warning/note: + // xx.m:13: warning: function called through a non-compatible type + // xx.m:13: note: if this code is reached, the program will abort + cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + // If we don't have a method decl, force a variadic cast. + Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : true); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs(); + CallExpr *CE = new (Context) CallExpr(*Context, PE, MsgExprs, + FT->getResultType(), VK_RValue, EndLoc); + Stmt *ReplacingStmt = CE; + if (MsgSendStretFlavor) { + // We have the method which returns a struct/union. Must also generate + // call to objc_msgSend_stret and hang both varieties on a conditional + // expression which dictate which one to envoke depending on size of + // method's return type. + + Expr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + msgSendType, returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); + + // Build sizeof(returnType) + UnaryExprOrTypeTraitExpr *sizeofExpr = + new (Context) UnaryExprOrTypeTraitExpr(UETT_SizeOf, + Context->getTrivialTypeSourceInfo(returnType), + Context->getSizeType(), SourceLocation(), + SourceLocation()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + // FIXME: Value of 8 is base on ppc32/x86 ABI for the most common cases. + // For X86 it is more complicated and some kind of target specific routine + // is needed to decide what to do. + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + IntegerLiteral *limit = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, 8), + Context->IntTy, + SourceLocation()); + BinaryOperator *lessThanExpr = + new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, + VK_RValue, OK_Ordinary, SourceLocation(), + false); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(lessThanExpr, + SourceLocation(), CE, + SourceLocation(), STCE, + returnType, VK_RValue, OK_Ordinary); + ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + CondExpr); + } + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +Stmt *RewriteModernObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = SynthMessageExpr(Exp, Exp->getLocStart(), + Exp->getLocEnd()); + + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +// typedef struct objc_object Protocol; +QualType RewriteModernObjC::getProtocolType() { + if (!ProtocolTypeDecl) { + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); + ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("Protocol"), + TInfo); + } + return Context->getTypeDeclType(ProtocolTypeDecl); +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// a synthesized/forward data reference (to the protocol's metadata). +/// The forward references (and metadata) are generated in +/// RewriteModernObjC::HandleTranslationUnit(). +Stmt *RewriteModernObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + std::string Name = "_OBJC_PROTOCOL_REFERENCE_$_" + + Exp->getProtocol()->getNameAsString(); + IdentifierInfo *ID = &Context->Idents.get(Name); + VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, getProtocolType(), 0, + SC_Extern, SC_None); + DeclRefExpr *DRE = new (Context) DeclRefExpr(VD, false, getProtocolType(), + VK_LValue, SourceLocation()); + Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), + CK_BitCast, + DerefExpr); + ReplaceStmt(Exp, castExpr); + ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return castExpr; + +} + +bool RewriteModernObjC::BufferContainsPPDirectives(const char *startBuf, + const char *endBuf) { + while (startBuf < endBuf) { + if (*startBuf == '#') { + // Skip whitespace. + for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf) + ; + if (!strncmp(startBuf, "if", strlen("if")) || + !strncmp(startBuf, "ifdef", strlen("ifdef")) || + !strncmp(startBuf, "ifndef", strlen("ifndef")) || + !strncmp(startBuf, "define", strlen("define")) || + !strncmp(startBuf, "undef", strlen("undef")) || + !strncmp(startBuf, "else", strlen("else")) || + !strncmp(startBuf, "elif", strlen("elif")) || + !strncmp(startBuf, "endif", strlen("endif")) || + !strncmp(startBuf, "pragma", strlen("pragma")) || + !strncmp(startBuf, "include", strlen("include")) || + !strncmp(startBuf, "import", strlen("import")) || + !strncmp(startBuf, "include_next", strlen("include_next"))) + return true; + } + startBuf++; + } + return false; +} + +/// IsTagDefinedInsideClass - This routine checks that a named tagged type +/// is defined inside an objective-c class. If so, it returns true. +bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, + TagDecl *Tag, + bool &IsNamedDefinition) { + if (!IDecl) + return false; + SourceLocation TagLocation; + if (RecordDecl *RD = dyn_cast(Tag)) { + RD = RD->getDefinition(); + if (!RD || !RD->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = RD->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + } + if (EnumDecl *ED = dyn_cast(Tag)) { + if (!ED || !ED->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = ED->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + + } + return false; +} + +/// RewriteObjCFieldDeclType - This routine rewrites a type into the buffer. +/// It handles elaborated types, as well as enum types in the process. +bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, + std::string &Result) { + if (isa(Type)) { + Result += "\t"; + return false; + } + + if (Type->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(Type); + return RewriteObjCFieldDeclType(ElemTy, Result); + } + else if (Type->isRecordType()) { + RecordDecl *RD = Type->getAs()->getDecl(); + if (RD->isCompleteDefinition()) { + if (RD->isStruct()) + Result += "\n\tstruct "; + else if (RD->isUnion()) + Result += "\n\tunion "; + else + assert(false && "class not allowed as an ivar type"); + + Result += RD->getName(); + if (GlobalDefinedTags.count(RD)) { + // struct/union is defined globally, use it. + Result += " "; + return true; + } + Result += " {\n"; + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) { + FieldDecl *FD = *i; + RewriteObjCFieldDecl(FD, Result); + } + Result += "\t} "; + return true; + } + } + else if (Type->isEnumeralType()) { + EnumDecl *ED = Type->getAs()->getDecl(); + if (ED->isCompleteDefinition()) { + Result += "\n\tenum "; + Result += ED->getName(); + if (GlobalDefinedTags.count(ED)) { + // Enum is globall defined, use it. + Result += " "; + return true; + } + + Result += " {\n"; + for (EnumDecl::enumerator_iterator EC = ED->enumerator_begin(), + ECEnd = ED->enumerator_end(); EC != ECEnd; ++EC) { + Result += "\t"; Result += EC->getName(); Result += " = "; + llvm::APSInt Val = EC->getInitVal(); + Result += Val.toString(10); + Result += ",\n"; + } + Result += "\t} "; + return true; + } + } + + Result += "\t"; + convertObjCTypeToCStyleType(Type); + return false; +} + + +/// RewriteObjCFieldDecl - This routine rewrites a field into the buffer. +/// It handles elaborated types, as well as enum types in the process. +void RewriteModernObjC::RewriteObjCFieldDecl(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + std::string Name = fieldDecl->getNameAsString(); + + bool EleboratedType = RewriteObjCFieldDeclType(Type, Result); + if (!EleboratedType) + Type.getAsStringInternal(Name, Context->getPrintingPolicy()); + Result += Name; + if (fieldDecl->isBitField()) { + Result += " : "; Result += utostr(fieldDecl->getBitWidthValue(*Context)); + } + else if (EleboratedType && Type->isArrayType()) { + CanQualType CType = Context->getCanonicalType(Type); + while (isa(CType)) { + if (const ConstantArrayType *CAT = Context->getAsConstantArrayType(CType)) { + Result += "["; + llvm::APInt Dim = CAT->getSize(); + Result += utostr(Dim.getZExtValue()); + Result += "]"; + } + CType = CType->getAs()->getElementType(); + } + } + + Result += ";\n"; +} + +/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined +/// named aggregate types into the input buffer. +void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + if (isa(Type)) + return; + if (Type->isArrayType()) + Type = Context->getBaseElementType(Type); + ObjCContainerDecl *IDecl = + dyn_cast(fieldDecl->getDeclContext()); + + TagDecl *TD = 0; + if (Type->isRecordType()) { + TD = Type->getAs()->getDecl(); + } + else if (Type->isEnumeralType()) { + TD = Type->getAs()->getDecl(); + } + + if (TD) { + if (GlobalDefinedTags.count(TD)) + return; + + bool IsNamedDefinition = false; + if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) { + RewriteObjCFieldDeclType(Type, Result); + Result += ";"; + } + if (IsNamedDefinition) + GlobalDefinedTags.insert(TD); + } + +} + +/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getName() != "" && + "Name missing in SynthesizeObjCInternalStruct"); + ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); + SmallVector IVars; + for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + IVars.push_back(IVD); + + SourceLocation LocStart = CDecl->getLocStart(); + SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // If no ivars and no root or if its root, directly or indirectly, + // have no ivars (thus not synthesized) then no need to synthesize this class. + if ((!CDecl->isThisDeclarationADefinition() || IVars.size() == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + return; + } + + // Insert named struct/union definitions inside class to + // outer scope. This follows semantics of locally defined + // struct/unions in objective-c classes. + for (unsigned i = 0, e = IVars.size(); i < e; i++) + RewriteLocallyDefinedNamedAggregates(IVars[i], Result); + + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + Result += "_IMPL {\n"; + + if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { + Result += "\tstruct "; Result += RCDecl->getNameAsString(); + Result += "_IMPL "; Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n"; + } + + for (unsigned i = 0, e = IVars.size(); i < e; i++) + RewriteObjCFieldDecl(IVars[i], Result); + + Result += "};\n"; + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl)) + llvm_unreachable("struct already synthesize- RewriteObjCInternalStruct"); +} + +/// RewriteIvarOffsetSymbols - Rewrite ivar offset symbols of those ivars which +/// have been referenced in an ivar access expression. +void RewriteModernObjC::RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, + std::string &Result) { + // write out ivar offset symbols which have been referenced in an ivar + // access expression. + llvm::SmallPtrSet Ivars = ReferencedIvars[CDecl]; + if (Ivars.empty()) + return; + for (llvm::SmallPtrSet::iterator i = Ivars.begin(), + e = Ivars.end(); i != e; i++) { + ObjCIvarDecl *IvarDecl = (*i); + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_ivar$B\")) "; + Result += "extern \"C\" "; + if (LangOpts.MicrosoftExt && + IvarDecl->getAccessControl() != ObjCIvarDecl::Private && + IvarDecl->getAccessControl() != ObjCIvarDecl::Package) + Result += "__declspec(dllimport) "; + + Result += "unsigned long "; + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += ";"; + } +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + + +/// RewriteImplementations - This routine rewrites all method implementations +/// and emits meta-data. + +void RewriteModernObjC::RewriteImplementations() { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // Rewrite implemented methods + for (int i = 0; i < ClsDefCount; i++) { + ObjCImplementationDecl *OIMP = ClassImplementation[i]; + ObjCInterfaceDecl *CDecl = OIMP->getClassInterface(); + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + RewriteImplementationDecl(OIMP); + } + + for (int i = 0; i < CatDefCount; i++) { + ObjCCategoryImplDecl *CIMP = CategoryImplementation[i]; + ObjCInterfaceDecl *CDecl = CIMP->getClassInterface(); + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + RewriteImplementationDecl(CIMP); + } +} + +void RewriteModernObjC::RewriteByRefString(std::string &ResultStr, + const std::string &Name, + ValueDecl *VD, bool def) { + assert(BlockByRefDeclNo.count(VD) && + "RewriteByRefString: ByRef decl missing"); + if (def) + ResultStr += "struct "; + ResultStr += "__Block_byref_" + Name + + "_" + utostr(BlockByRefDeclNo[VD]) ; +} + +static bool HasLocalVariableExternalStorage(ValueDecl *VD) { + if (VarDecl *Var = dyn_cast(VD)) + return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); + return false; +} + +std::string RewriteModernObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getResultType(); + std::string StructRef = "struct " + Tag; + SourceLocation BlockLoc = CE->getExprLoc(); + std::string S; + ConvertSourceLocationToLineDirective(BlockLoc, S); + + S += "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + + funcName.str() + "_block_func_" + utostr(i); + + BlockDecl *BD = CE->getBlockDecl(); + + if (isa(AFT)) { + // No user-supplied arguments. Still need to pass in a pointer to the + // block (to reference imported block decl refs). + S += "(" + StructRef + " *__cself)"; + } else if (BD->param_empty()) { + S += "(" + StructRef + " *__cself)"; + } else { + const FunctionProtoType *FT = cast(AFT); + assert(FT && "SynthesizeBlockFunc: No function proto"); + S += '('; + // first add the implicit argument. + S += StructRef + " *__cself, "; + std::string ParamStr; + for (BlockDecl::param_iterator AI = BD->param_begin(), + E = BD->param_end(); AI != E; ++AI) { + if (AI != BD->param_begin()) S += ", "; + ParamStr = (*AI)->getNameAsString(); + QualType QT = (*AI)->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); + S += ParamStr; + } + if (FT->isVariadic()) { + if (!BD->param_empty()) S += ", "; + S += "..."; + } + S += ')'; + } + S += " {\n"; + + // Create local declarations to avoid rewriting all closure decl ref exprs. + // First, emit a declaration for all "by ref" decls. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + std::string TypeString; + RewriteByRefString(TypeString, Name, (*I)); + TypeString += " *"; + Name = TypeString + Name; + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + // Handle nested closure invocation. For example: + // + // void (^myImportedClosure)(void); + // myImportedClosure = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherClosure)(void); + // anotherClosure = ^(void) { + // myImportedClosure(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + RewriteBlockPointerTypeVariable(S, (*I)); + S += " = ("; + RewriteBlockPointerType(S, (*I)->getType()); + S += ")"; + S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + else { + std::string Name = (*I)->getNameAsString(); + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + S += Name + " = __cself->" + + (*I)->getNameAsString() + "; // bound by copy\n"; + } + } + std::string RewrittenStr = RewrittenBlockExprs[CE]; + const char *cstr = RewrittenStr.c_str(); + while (*cstr++ != '{') ; + S += cstr; + S += "\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + std::string StructRef = "struct " + Tag; + std::string S = "static void __"; + + S += funcName; + S += "_block_copy_" + utostr(i); + S += "(" + StructRef; + S += "*dst, " + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + ValueDecl *VD = (*I); + S += "_Block_object_assign((void*)&dst->"; + S += (*I)->getNameAsString(); + S += ", (void*)src->"; + S += (*I)->getNameAsString(); + if (BlockByRefDeclsPtrSet.count((*I))) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + ValueDecl *VD = (*I); + S += "_Block_object_dispose((void*)src->"; + S += (*I)->getNameAsString(); + if (BlockByRefDeclsPtrSet.count((*I))) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + std::string Desc) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + S += " struct " + Desc; + S += "* Desc;\n"; + + Constructor += "(void *fp, "; // Invoke function pointer. + Constructor += "struct " + Desc; // Descriptor pointer. + Constructor += " *desc"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); + QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + { + std::string TypeString; + RewriteByRefString(TypeString, FieldName, (*I)); + TypeString += " *"; + FieldName = TypeString + FieldName; + ArgName = TypeString + ArgName; + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + Constructor += ", int flags=0)"; + // Initialize all "by copy" arguments. + bool firsTime = true; + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + "((struct __block_impl *)_" + Name + ")"; + else + Constructor += Name + "(_" + Name + ")"; + } + // Initialize all "by ref" arguments. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + Constructor += Name + "(_" + Name + "->__forwarding)"; + } + + Constructor += " {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + Constructor += " Desc = desc;\n"; + } else { + // Finish writing the constructor. + Constructor += ", int flags=0) {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + Constructor += " Desc = desc;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, int i, + StringRef FunName, + unsigned hasCopy) { + std::string S = "\nstatic struct " + DescTag; + + S += " {\n size_t reserved;\n"; + S += " size_t Block_size;\n"; + if (hasCopy) { + S += " void (*copy)(struct "; + S += ImplTag; S += "*, struct "; + S += ImplTag; S += "*);\n"; + + S += " void (*dispose)(struct "; + S += ImplTag; S += "*);\n"; + } + S += "} "; + + S += DescTag + "_DATA = { 0, sizeof(struct "; + S += ImplTag + ")"; + if (hasCopy) { + S += ", __" + FunName.str() + "_block_copy_" + utostr(i); + S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); + } + S += "};\n"; + return S; +} + +void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName) { + bool RewriteSC = (GlobalVarDecl && + !Blocks.empty() && + GlobalVarDecl->getStorageClass() == SC_Static && + GlobalVarDecl->getType().getCVRQualifiers()); + if (RewriteSC) { + std::string SC(" void __"); + SC += GlobalVarDecl->getNameAsString(); + SC += "() {}"; + InsertText(FunLocStart, SC); + } + + // Insert closures that were part of the function. + for (unsigned i = 0, count=0; i < Blocks.size(); i++) { + CollectBlockDeclRefInfo(Blocks[i]); + // Need to copy-in the inner copied-in variables not actually used in this + // block. + for (int j = 0; j < InnerDeclRefsCount[i]; j++) { + DeclRefExpr *Exp = InnerDeclRefs[count++]; + ValueDecl *VD = Exp->getDecl(); + BlockDeclRefs.push_back(Exp); + if (!VD->hasAttr()) { + if (!BlockByCopyDeclsPtrSet.count(VD)) { + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + continue; + } + + if (!BlockByRefDeclsPtrSet.count(VD)) { + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + + // imported objects in the inner blocks not used in the outer + // blocks must be copied/disposed in the outer block as well. + if (VD->getType()->isObjCObjectPointerType() || + VD->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(VD); + } + + std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); + std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); + + InsertText(FunLocStart, CI); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); + + InsertText(FunLocStart, CF); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); + InsertText(FunLocStart, HF); + } + std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, + ImportedBlockDecls.size() > 0); + InsertText(FunLocStart, BD); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + } + if (RewriteSC) { + // Must insert any 'const/volatile/static here. Since it has been + // removed as result of rewriting of block literals. + std::string SC; + if (GlobalVarDecl->getStorageClass() == SC_Static) + SC = "static "; + if (GlobalVarDecl->getType().isConstQualified()) + SC += "const "; + if (GlobalVarDecl->getType().isVolatileQualified()) + SC += "volatile "; + if (GlobalVarDecl->getType().isRestrictQualified()) + SC += "restrict "; + InsertText(FunLocStart, SC); + } + if (GlobalConstructionExp) { + // extra fancy dance for global literal expression. + + // Always the latest block expression on the block stack. + std::string Tag = "__"; + Tag += FunName; + Tag += "_block_impl_"; + Tag += utostr(Blocks.size()-1); + std::string globalBuf = "static "; + globalBuf += Tag; globalBuf += " "; + std::string SStr; + + llvm::raw_string_ostream constructorExprBuf(SStr); + GlobalConstructionExp->printPretty(constructorExprBuf, 0, + PrintingPolicy(LangOpts)); + globalBuf += constructorExprBuf.str(); + globalBuf += ";\n"; + InsertText(FunLocStart, globalBuf); + GlobalConstructionExp = 0; + } + + Blocks.clear(); + InnerDeclRefsCount.clear(); + InnerDeclRefs.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteModernObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = + (!Blocks.empty()) ? getFunctionSourceLocation(*this, FD) + : FD->getTypeSpecStartLoc(); + StringRef FuncName = FD->getName(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +static void BuildUniqueMethodName(std::string &Name, + ObjCMethodDecl *MD) { + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + Name = IFace->getName(); + Name += "__" + MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = Name.find(":", loc)) != std::string::npos) + Name.replace(loc, 1, "_"); +} + +void RewriteModernObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + //SourceLocation FunLocStart = MD->getLocStart(); + SourceLocation FunLocStart = MD->getLocStart(); + std::string FuncName; + BuildUniqueMethodName(FuncName, MD); + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteModernObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast(*CI)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(*CI); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast(S)) { + if (DRE->refersToEnclosingLocal()) { + // FIXME: Handle enums. + if (!isa(DRE->getDecl())) + BlockDeclRefs.push_back(DRE); + if (HasLocalVariableExternalStorage(DRE->getDecl())) + BlockDeclRefs.push_back(DRE); + } + } + + return; +} + +void RewriteModernObjC::GetInnerBlockDeclRefExprs(Stmt *S, + SmallVector &InnerBlockDeclRefs, + llvm::SmallPtrSet &InnerContexts) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast(*CI)) { + InnerContexts.insert(cast(CBE->getBlockDecl())); + GetInnerBlockDeclRefExprs(CBE->getBody(), + InnerBlockDeclRefs, + InnerContexts); + } + else + GetInnerBlockDeclRefExprs(*CI, + InnerBlockDeclRefs, + InnerContexts); + + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast(S)) { + if (DRE->refersToEnclosingLocal()) { + if (!isa(DRE->getDecl()) && + !InnerContexts.count(DRE->getDecl()->getDeclContext())) + InnerBlockDeclRefs.push_back(DRE); + if (VarDecl *Var = dyn_cast(DRE->getDecl())) + if (Var->isFunctionOrMethodVarDecl()) + ImportedLocalExternalDecls.insert(Var); + } + } + + return; +} + +/// convertObjCTypeToCStyleType - This routine converts such objc types +/// as qualified objects, and blocks to their closest c/c++ types that +/// it can. It returns true if input type was modified. +bool RewriteModernObjC::convertObjCTypeToCStyleType(QualType &T) { + QualType oldT = T; + convertBlockPointerToFunctionPointer(T); + if (T->isFunctionPointerType()) { + QualType PointeeTy; + if (const PointerType* PT = T->getAs()) { + PointeeTy = PT->getPointeeType(); + if (const FunctionType *FT = PointeeTy->getAs()) { + T = convertFunctionTypeOfBlocks(FT); + T = Context->getPointerType(T); + } + } + } + + convertToUnqualifiedObjCType(T); + return T != oldT; +} + +/// convertFunctionTypeOfBlocks - This routine converts a function type +/// whose result type may be a block pointer or whose argument type(s) +/// might be block pointers to an equivalent function type replacing +/// all block pointers to function pointers. +QualType RewriteModernObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { + const FunctionProtoType *FTP = dyn_cast(FT); + // FTP will be null for closures that don't take arguments. + // Generate a funky cast. + SmallVector ArgTypes; + QualType Res = FT->getResultType(); + bool modified = convertObjCTypeToCStyleType(Res); + + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I && (I != E); ++I) { + QualType t = *I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (convertObjCTypeToCStyleType(t)) + modified = true; + ArgTypes.push_back(t); + } + } + QualType FuncType; + if (modified) + FuncType = getSimpleFunctionType(Res, &ArgTypes[0], ArgTypes.size()); + else FuncType = QualType(FT, 0); + return FuncType; +} + +Stmt *RewriteModernObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { + // Navigate to relevant type information. + const BlockPointerType *CPT = 0; + + if (const DeclRefExpr *DRE = dyn_cast(BlockExp)) { + CPT = DRE->getType()->getAs(); + } else if (const MemberExpr *MExpr = dyn_cast(BlockExp)) { + CPT = MExpr->getType()->getAs(); + } + else if (const ParenExpr *PRE = dyn_cast(BlockExp)) { + return SynthesizeBlockCall(Exp, PRE->getSubExpr()); + } + else if (const ImplicitCastExpr *IEXPR = dyn_cast(BlockExp)) + CPT = IEXPR->getType()->getAs(); + else if (const ConditionalOperator *CEXPR = + dyn_cast(BlockExp)) { + Expr *LHSExp = CEXPR->getLHS(); + Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); + Expr *RHSExp = CEXPR->getRHS(); + Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); + Expr *CONDExp = CEXPR->getCond(); + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(CONDExp, + SourceLocation(), cast(LHSStmt), + SourceLocation(), cast(RHSStmt), + Exp->getType(), VK_RValue, OK_Ordinary); + return CondExpr; + } else if (const ObjCIvarRefExpr *IRE = dyn_cast(BlockExp)) { + CPT = IRE->getType()->getAs(); + } else if (const PseudoObjectExpr *POE + = dyn_cast(BlockExp)) { + CPT = POE->getType()->castAs(); + } else { + assert(1 && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAs(); + assert(FT && "RewriteBlockClass: Bad type"); + const FunctionProtoType *FTP = dyn_cast(FT); + // FTP will be null for closures that don't take arguments. + + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + SmallVector ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I && (I != E); ++I) { + QualType t = *I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (!convertBlockPointerToFunctionPointer(t)) + convertToUnqualifiedObjCType(t); + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType + = getSimpleFunctionType(Exp->getType(), &ArgTypes[0], ArgTypes.size()); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, + CK_BitCast, + const_cast(BlockExp)); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + BlkCast); + //PE->dump(); + + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("FuncPtr"), + Context->VoidPtrTy, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + + CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, + CK_BitCast, ME); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + SmallVector BlkExprs; + // Add the implicit argument. + BlkExprs.push_back(BlkCast); + // Add the user arguments. + for (CallExpr::arg_iterator I = Exp->arg_begin(), + E = Exp->arg_end(); I != E; ++I) { + BlkExprs.push_back(*I); + } + CallExpr *CE = new (Context) CallExpr(*Context, PE, BlkExprs, + Exp->getType(), VK_RValue, + SourceLocation()); + return CE; +} + +// We need to return the rewritten expression to handle cases where the +// DeclRefExpr is embedded in another expression being rewritten. +// For example: +// +// int main() { +// __block Foo *f; +// __block int i; +// +// void (^myblock)() = ^() { +// [f test]; // f is a DeclRefExpr embedded in a message (which is being rewritten). +// i = 77; +// }; +//} +Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { + // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR + // for each DeclRefExp where BYREFVAR is name of the variable. + ValueDecl *VD = DeclRefExp->getDecl(); + bool isArrow = DeclRefExp->refersToEnclosingLocal(); + + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("__forwarding"), + Context->VoidPtrTy, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, + FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + StringRef Name = VD->getName(); + FD = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), + &Context->Idents.get(Name), + Context->VoidPtrTy, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), + DeclRefExp->getType(), VK_LValue, OK_Ordinary); + + + + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), + DeclRefExp->getExprLoc(), + ME); + ReplaceStmt(DeclRefExp, PE); + return PE; +} + +// Rewrites the imported local variable V with external storage +// (static, extern, etc.) as *V +// +Stmt *RewriteModernObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { + ValueDecl *VD = DRE->getDecl(); + if (VarDecl *Var = dyn_cast(VD)) + if (!ImportedLocalExternalDecls.count(Var)) + return DRE; + Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, + DRE->getLocation()); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Exp); + ReplaceStmt(DRE, PE); + return PE; +} + +void RewriteModernObjC::RewriteCastExpr(CStyleCastExpr *CE) { + SourceLocation LocStart = CE->getLParenLoc(); + SourceLocation LocEnd = CE->getRParenLoc(); + + // Need to avoid trying to rewrite synthesized casts. + if (LocStart.isInvalid()) + return; + // Need to avoid trying to rewrite casts contained in macros. + if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) + return; + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + QualType QT = CE->getType(); + const Type* TypePtr = QT->getAs(); + if (isa(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + std::string TypeAsString = "("; + RewriteBlockPointerType(TypeAsString, QT); + TypeAsString += ")"; + ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); + return; + } + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*"); + break; + } + } + return; +} + +void RewriteModernObjC::RewriteImplicitCastObjCExpr(CastExpr *IC) { + CastKind CastKind = IC->getCastKind(); + if (CastKind != CK_BlockPointerToObjCPointerCast && + CastKind != CK_AnyPointerToBlockPointerCast) + return; + + QualType QT = IC->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + std::string TypeString(QT.getAsString(Context->getPrintingPolicy())); + std::string Str = "("; + Str += TypeString; + Str += ")"; + InsertText(IC->getSubExpr()->getLocStart(), &Str[0], Str.size()); + + return; +} + +void RewriteModernObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { + SourceLocation DeclLoc = FD->getLocation(); + unsigned parenCount = 0; + + // We have 1 or more arguments that have closure pointers. + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *startArgList = strchr(startBuf, '('); + + assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); + + parenCount++; + // advance the location to startArgList. + DeclLoc = DeclLoc.getLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*"); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } + return; +} + +bool RewriteModernObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs(); + if (PT) { + FTP = PT->getPointeeType()->getAs(); + } else { + const BlockPointerType *BPT = QT->getAs(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs(); + } + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I != E; ++I) + if (isTopLevelBlockPointerType(*I)) + return true; + } + return false; +} + +bool RewriteModernObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs(); + if (PT) { + FTP = PT->getPointeeType()->getAs(); + } else { + const BlockPointerType *BPT = QT->getAs(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs(); + } + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I != E; ++I) { + if ((*I)->isObjCQualifiedIdType()) + return true; + if ((*I)->isObjCObjectPointerType() && + (*I)->getPointeeType()->isObjCQualifiedInterfaceType()) + return true; + } + + } + return false; +} + +void RewriteModernObjC::GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen) { + const char *argPtr = strchr(Name, '('); + assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); + + LParen = argPtr; // output the start. + argPtr++; // skip past the left paren. + unsigned parenCount = 1; + + while (*argPtr && parenCount) { + switch (*argPtr) { + case '(': parenCount++; break; + case ')': parenCount--; break; + default: break; + } + if (parenCount) argPtr++; + } + assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); + RParen = argPtr; // output the end +} + +void RewriteModernObjC::RewriteBlockPointerDecl(NamedDecl *ND) { + if (FunctionDecl *FD = dyn_cast(ND)) { + RewriteBlockPointerFunctionArgs(FD); + return; + } + // Handle Variables and Typedefs. + SourceLocation DeclLoc = ND->getLocation(); + QualType DeclT; + if (VarDecl *VD = dyn_cast(ND)) + DeclT = VD->getType(); + else if (TypedefNameDecl *TDD = dyn_cast(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast(ND)) + DeclT = FD->getType(); + else + llvm_unreachable("RewriteBlockPointerDecl(): Decl type not yet handled"); + + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *endBuf = startBuf; + // scan backward (from the decl location) for the end of the previous decl. + while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) + startBuf--; + SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); + std::string buf; + unsigned OrigLength=0; + // *startBuf != '^' if we are dealing with a pointer to function that + // may take block argument types (which will be handled below). + if (*startBuf == '^') { + // Replace the '^' with '*', computing a negative offset. + buf = '*'; + startBuf++; + OrigLength++; + } + while (*startBuf != ')') { + buf += *startBuf; + startBuf++; + OrigLength++; + } + buf += ')'; + OrigLength++; + + if (PointerTypeTakesAnyBlockArguments(DeclT) || + PointerTypeTakesAnyObjCQualifiedType(DeclT)) { + // Replace the '^' with '*' for arguments. + // Replace id

with id/*<>*/ + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') + buf += '*'; + else if (*argListBegin == '<') { + buf += "/*"; + buf += *argListBegin++; + OrigLength++; + while (*argListBegin != '>') { + buf += *argListBegin++; + OrigLength++; + } + buf += *argListBegin; + buf += "*/"; + } + else + buf += *argListBegin; + argListBegin++; + OrigLength++; + } + buf += ')'; + OrigLength++; + } + ReplaceText(Start, OrigLength, buf); + + return; +} + + +/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: +/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, +/// struct Block_byref_id_object *src) { +/// _Block_object_assign (&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_assign(&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } +/// And: +/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } + +std::string RewriteModernObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, + int flag) { + std::string S; + if (CopyDestroyCache.count(flag)) + return S; + CopyDestroyCache.insert(flag); + S = "static void __Block_byref_id_object_copy_"; + S += utostr(flag); + S += "(void *dst, void *src) {\n"; + + // offset into the object pointer is computed as: + // void * + void* + int + int + void* + void * + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + unsigned VoidPtrSize = + static_cast(Context->getTypeSize(Context->VoidPtrTy)); + + unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); + S += " _Block_object_assign((char*)dst + "; + S += utostr(offset); + S += ", *(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + + S += "static void __Block_byref_id_object_dispose_"; + S += utostr(flag); + S += "(void *src) {\n"; + S += " _Block_object_dispose(*(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + return S; +} + +/// RewriteByRefVar - For each __block typex ND variable this routine transforms +/// the declaration into: +/// struct __Block_byref_ND { +/// void *__isa; // NULL for everything except __weak pointers +/// struct __Block_byref_ND *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object +/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object +/// typex ND; +/// }; +/// +/// It then replaces declaration of ND variable with: +/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, +/// __size=sizeof(struct __Block_byref_ND), +/// ND=initializer-if-any}; +/// +/// +void RewriteModernObjC::RewriteByRefVar(VarDecl *ND, bool firstDecl, + bool lastDecl) { + int flag = 0; + int isa = 0; + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + if (DeclLoc.isInvalid()) + // If type location is missing, it is because of missing type (a warning). + // Use variable's location which is good for this case. + DeclLoc = ND->getLocation(); + const char *startBuf = SM->getCharacterData(DeclLoc); + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + std::string Name(ND->getNameAsString()); + std::string ByrefType; + RewriteByRefString(ByrefType, Name, ND, true); + ByrefType += " {\n"; + ByrefType += " void *__isa;\n"; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += " *__forwarding;\n"; + ByrefType += " int __flags;\n"; + ByrefType += " int __size;\n"; + // Add void *__Block_byref_id_object_copy; + // void *__Block_byref_id_object_dispose; if needed. + QualType Ty = ND->getType(); + bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty); + if (HasCopyAndDispose) { + ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; + ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; + } + + QualType T = Ty; + (void)convertBlockPointerToFunctionPointer(T); + T.getAsStringInternal(Name, Context->getPrintingPolicy()); + + ByrefType += " " + Name + ";\n"; + ByrefType += "};\n"; + // Insert this type in global scope. It is needed by helper function. + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + else { + assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); + FunLocStart = CurMethodDef->getLocStart(); + } + InsertText(FunLocStart, ByrefType); + + if (Ty.isObjCGCWeak()) { + flag |= BLOCK_FIELD_IS_WEAK; + isa = 1; + } + if (HasCopyAndDispose) { + flag = BLOCK_BYREF_CALLER; + QualType Ty = ND->getType(); + // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. + if (Ty->isBlockPointerType()) + flag |= BLOCK_FIELD_IS_BLOCK; + else + flag |= BLOCK_FIELD_IS_OBJECT; + std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); + if (!HF.empty()) + InsertText(FunLocStart, HF); + } + + // struct __Block_byref_ND ND = + // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), + // initializer-if-any}; + bool hasInit = (ND->getInit() != 0); + // FIXME. rewriter does not support __block c++ objects which + // require construction. + if (hasInit) + if (CXXConstructExpr *CExp = dyn_cast(ND->getInit())) { + CXXConstructorDecl *CXXDecl = CExp->getConstructor(); + if (CXXDecl && CXXDecl->isDefaultConstructor()) + hasInit = false; + } + + unsigned flags = 0; + if (HasCopyAndDispose) + flags |= BLOCK_HAS_COPY_DISPOSE; + Name = ND->getNameAsString(); + ByrefType.clear(); + RewriteByRefString(ByrefType, Name, ND); + std::string ForwardingCastType("("); + ForwardingCastType += ByrefType + " *)"; + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + + if (!firstDecl) { + // In multiple __block declarations, and for all but 1st declaration, + // find location of the separating comma. This would be start location + // where new text is to be inserted. + DeclLoc = ND->getLocation(); + const char *startDeclBuf = SM->getCharacterData(DeclLoc); + const char *commaBuf = startDeclBuf; + while (*commaBuf != ',') + commaBuf--; + assert((*commaBuf == ',') && "RewriteByRefVar: can't find ','"); + DeclLoc = DeclLoc.getLocWithOffset(commaBuf - startDeclBuf); + startBuf = commaBuf; + } + + if (!hasInit) { + ByrefType += "};\n"; + unsigned nameSize = Name.size(); + // for block or function pointer declaration. Name is aleady + // part of the declaration. + if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) + nameSize = 1; + ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); + } + else { + ByrefType += ", "; + SourceLocation startLoc; + Expr *E = ND->getInit(); + if (const CStyleCastExpr *ECE = dyn_cast(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); + + const char separator = lastDecl ? ';' : ','; + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *separatorBuf = strchr(startInitializerBuf, separator); + assert((*separatorBuf == separator) && + "RewriteByRefVar: can't find ';' or ','"); + SourceLocation separatorLoc = + startLoc.getLocWithOffset(separatorBuf-startInitializerBuf); + + InsertText(separatorLoc, lastDecl ? "}" : "};\n"); + } + return; +} + +void RewriteModernObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { + // Add initializers for any closure decl refs. + GetBlockDeclRefExprs(Exp->getBody()); + if (BlockDeclRefs.size()) { + // Unique all "by copy" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (!BlockDeclRefs[i]->getDecl()->hasAttr()) { + if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr()) { + if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr() || + BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + BlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } +} + +FunctionDecl *RewriteModernObjC::SynthBlockInitFunctionDecl(StringRef name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, FType, 0, SC_Extern, + SC_None, false, false); +} + +Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp, + const SmallVector &InnerBlockDeclRefs) { + + const BlockDecl *block = Exp->getBlockDecl(); + + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + + // Add inner imported variables now used in current block. + int countOfInnerDecls = 0; + if (!InnerBlockDeclRefs.empty()) { + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { + DeclRefExpr *Exp = InnerBlockDeclRefs[i]; + ValueDecl *VD = Exp->getDecl(); + if (!VD->hasAttr() && !BlockByCopyDeclsPtrSet.count(VD)) { + // We need to save the copied-in variables in nested + // blocks because it is needed at the end for some of the API generations. + // See SynthesizeBlockLiterals routine. + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr() && !BlockByRefDeclsPtrSet.count(VD)) { + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) + if (InnerBlockDeclRefs[i]->getDecl()->hasAttr() || + InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); + } + InnerDeclRefsCount.push_back(countOfInnerDecls); + + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) + BuildUniqueMethodName(FuncName, CurMethodDef); + else if (GlobalVarDecl) + FuncName = std::string(GlobalVarDecl->getNameAsString()); + + bool GlobalBlockExpr = + block->getDeclContext()->getRedeclContext()->isFileContext(); + + if (GlobalBlockExpr && !GlobalVarDecl) { + Diags.Report(block->getLocation(), GlobalBlockRewriteFailedDiag); + GlobalBlockExpr = false; + } + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + // Get a pointer to the function type so we can cast appropriately. + QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); + QualType FType = Context->getPointerType(BFT); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a contructor call... + std::string Tag; + + if (GlobalBlockExpr) + Tag = "__global_"; + else + Tag = "__"; + Tag += FuncName + "_block_impl_" + BlockNumber; + + FD = SynthBlockInitFunctionDecl(Tag); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, FType, VK_RValue, + SourceLocation()); + + SmallVector InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func); + DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + InitExprs.push_back(castExpr); + + // Initialize the block descriptor. + std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(DescData.c_str()), + Context->VoidPtrTy, 0, + SC_Static, SC_None); + UnaryOperator *DescRefExpr = + new (Context) UnaryOperator(new (Context) DeclRefExpr(NewVD, false, + Context->VoidPtrTy, + VK_LValue, + SourceLocation()), + UO_AddrOf, + Context->getPointerType(Context->VoidPtrTy), + VK_RValue, OK_Ordinary, + SourceLocation()); + InitExprs.push_back(DescRefExpr); + + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + ValueDecl *ND = (*I); + std::string Name(ND->getNameAsString()); + std::string RecName; + RewriteByRefString(RecName, Name, ND, true); + IdentifierInfo *II = &Context->Idents.get(RecName.c_str() + + sizeof("struct")); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + bool isNestedCapturedVar = false; + if (block) + for (BlockDecl::capture_const_iterator ci = block->capture_begin(), + ce = block->capture_end(); ci != ce; ++ci) { + const VarDecl *variable = ci->getVariable(); + if (variable == ND && ci->isNested()) { + assert (ci->isByRef() && + "SynthBlockInitExpr - captured block variable is not byref"); + isNestedCapturedVar = true; + break; + } + } + // captured nested byref variable has its address passed. Do not take + // its address again. + if (!isNestedCapturedVar) + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); + InitExprs.push_back(Exp); + } + } + if (ImportedBlockDecls.size()) { + // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR + int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), + Context->IntTy, SourceLocation()); + InitExprs.push_back(FlagExp); + } + NewRep = new (Context) CallExpr(*Context, DRE, InitExprs, + FType, VK_LValue, SourceLocation()); + + if (GlobalBlockExpr) { + assert (GlobalConstructionExp == 0 && + "SynthBlockInitExpr - GlobalConstructionExp must be null"); + GlobalConstructionExp = NewRep; + NewRep = DRE; + } + + NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, + NewRep); + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +bool RewriteModernObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { + if (const ObjCForCollectionStmt * CS = + dyn_cast(Stmts.back())) + return CS->getElement() == DS; + return false; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { + if (isa(S) || isa(S) || + isa(S) || isa(S)) + Stmts.push_back(S); + else if (isa(S)) { + Stmts.push_back(S); + ObjCBcLabelNo.push_back(++BcLabelCount); + } + + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast(S)) { + if (isa(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + Stmt *childStmt = (*CI); + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); + if (newStmt) { + *CI = newStmt; + } + } + + if (BlockExpr *BE = dyn_cast(S)) { + SmallVector InnerBlockDeclRefs; + llvm::SmallPtrSet InnerContexts; + InnerContexts.insert(BE->getBlockDecl()); + ImportedLocalExternalDecls.clear(); + GetInnerBlockDeclRefExprs(BE->getBody(), + InnerBlockDeclRefs, InnerContexts); + // Rewrite the block body in place. + Stmt *SaveCurrentBody = CurrentBody; + CurrentBody = BE->getBody(); + PropParentMap = 0; + // block literal on rhs of a property-dot-sytax assignment + // must be replaced by its synthesize ast so getRewrittenText + // works as expected. In this case, what actually ends up on RHS + // is the blockTranscribed which is the helper function for the + // block literal; as in: self.c = ^() {[ace ARR];}; + bool saveDisableReplaceStmt = DisableReplaceStmt; + DisableReplaceStmt = false; + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + DisableReplaceStmt = saveDisableReplaceStmt; + CurrentBody = SaveCurrentBody; + PropParentMap = 0; + ImportedLocalExternalDecls.clear(); + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); + + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast(S)) + return RewriteAtEncode(AtEncode); + + if (ObjCSelectorExpr *AtSelector = dyn_cast(S)) + return RewriteAtSelector(AtSelector); + + if (ObjCStringLiteral *AtString = dyn_cast(S)) + return RewriteObjCStringLiteral(AtString); + + if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast(S)) + return RewriteObjCBoolLiteralExpr(BoolLitExpr); + + if (ObjCBoxedExpr *BoxedExpr = dyn_cast(S)) + return RewriteObjCBoxedExpr(BoxedExpr); + + if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast(S)) + return RewriteObjCArrayLiteralExpr(ArrayLitExpr); + + if (ObjCDictionaryLiteral *DictionaryLitExpr = + dyn_cast(S)) + return RewriteObjCDictionaryLiteralExpr(DictionaryLitExpr); + + if (ObjCMessageExpr *MessExpr = dyn_cast(S)) { +#if 0 + // Before we rewrite it, put the original message expression in a comment. + SourceLocation startLoc = MessExpr->getLocStart(); + SourceLocation endLoc = MessExpr->getLocEnd(); + + const char *startBuf = SM->getCharacterData(startLoc); + const char *endBuf = SM->getCharacterData(endLoc); + + std::string messString; + messString += "// "; + messString.append(startBuf, endBuf-startBuf+1); + messString += "\n"; + + // FIXME: Missing definition of + // InsertText(clang::SourceLocation, char const*, unsigned int). + // InsertText(startLoc, messString.c_str(), messString.size()); + // Tried this, but it didn't work either... + // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); +#endif + return RewriteMessageExpr(MessExpr); + } + + if (ObjCAutoreleasePoolStmt *StmtAutoRelease = + dyn_cast(S)) { + return RewriteObjCAutoreleasePoolStmt(StmtAutoRelease); + } + + if (ObjCAtTryStmt *StmtTry = dyn_cast(S)) + return RewriteObjCTryStmt(StmtTry); + + if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast(S)) + return RewriteObjCSynchronizedStmt(StmtTry); + + if (ObjCAtThrowStmt *StmtThrow = dyn_cast(S)) + return RewriteObjCThrowStmt(StmtThrow); + + if (ObjCProtocolExpr *ProtocolExp = dyn_cast(S)) + return RewriteObjCProtocolExpr(ProtocolExp); + + if (ObjCForCollectionStmt *StmtForCollection = + dyn_cast(S)) + return RewriteObjCForCollectionStmt(StmtForCollection, + OrigStmtRange.getEnd()); + if (BreakStmt *StmtBreakStmt = + dyn_cast(S)) + return RewriteBreakStmt(StmtBreakStmt); + if (ContinueStmt *StmtContinueStmt = + dyn_cast(S)) + return RewriteContinueStmt(StmtContinueStmt); + + // Need to check for protocol refs (id

, Foo

*) in variable decls + // and cast exprs. + if (DeclStmt *DS = dyn_cast(S)) { + // FIXME: What we're doing here is modifying the type-specifier that + // precedes the first Decl. In the future the DeclGroup should have + // a separate type-specifier that we can rewrite. + // NOTE: We need to avoid rewriting the DeclStmt if it is within + // the context of an ObjCForCollectionStmt. For example: + // NSArray *someArray; + // for (id index in someArray) ; + // This is because RewriteObjCForCollectionStmt() does textual rewriting + // and it depends on the original text locations/positions. + if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) + RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); + + // Blocks rewrite rules. + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + Decl *SD = *DI; + if (ValueDecl *ND = dyn_cast(SD)) { + if (isTopLevelBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + if (VarDecl *VD = dyn_cast(SD)) { + if (VD->hasAttr()) { + static unsigned uniqueByrefDeclCount = 0; + assert(!BlockByRefDeclNo.count(ND) && + "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); + BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; + RewriteByRefVar(VD, (DI == DS->decl_begin()), ((DI+1) == DE)); + } + else + RewriteTypeOfDecl(VD); + } + } + if (TypedefNameDecl *TD = dyn_cast(SD)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + } + } + + if (CStyleCastExpr *CE = dyn_cast(S)) + RewriteObjCQualifiedInterfaceTypes(CE); + + if (isa(S) || isa(S) || + isa(S) || isa(S)) { + assert(!Stmts.empty() && "Statement stack is empty"); + assert ((isa(Stmts.back()) || isa(Stmts.back()) || + isa(Stmts.back()) || isa(Stmts.back())) + && "Statement stack mismatch"); + Stmts.pop_back(); + } + // Handle blocks rewriting. + if (DeclRefExpr *DRE = dyn_cast(S)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr()) + return RewriteBlockDeclRefExpr(DRE); + if (HasLocalVariableExternalStorage(VD)) + return RewriteLocalVariableExternalStorage(DRE); + } + + if (CallExpr *CE = dyn_cast(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); + ReplaceStmt(S, BlockCall); + return BlockCall; + } + } + if (CStyleCastExpr *CE = dyn_cast(S)) { + RewriteCastExpr(CE); + } + if (ImplicitCastExpr *ICE = dyn_cast(S)) { + RewriteImplicitCastObjCExpr(ICE); + } +#if 0 + + if (ImplicitCastExpr *ICE = dyn_cast(S)) { + CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), + ICE->getSubExpr(), + SourceLocation()); + // Get the new text. + std::string SStr; + llvm::raw_string_ostream Buf(SStr); + Replacement->printPretty(Buf); + const std::string &Str = Buf.str(); + + printf("CAST = %s\n", &Str[0]); + InsertText(ICE->getSubExpr()->getLocStart(), &Str[0], Str.size()); + delete S; + return Replacement; + } +#endif + // Return this stmt unmodified. + return S; +} + +void RewriteModernObjC::RewriteRecordBody(RecordDecl *RD) { + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) { + FieldDecl *FD = *i; + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + if (FD->getType()->isObjCQualifiedIdType() || + FD->getType()->isObjCQualifiedInterfaceType()) + RewriteObjCQualifiedInterfaceTypes(FD); + } +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteModernObjC::HandleDeclInMainFile(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl *FD = cast(D); + if (FD->isOverloadedOperator()) + return; + + // Since function prototypes don't have ParmDecl's, we check the function + // prototype. This enables us to rewrite function declarations and + // definitions using the same code. + RewriteBlocksInFunctionProtoType(FD->getType(), FD); + + if (!FD->isThisDeclarationADefinition()) + break; + + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (CompoundStmt *Body = dyn_cast_or_null(FD->getBody())) { + CurFunctionDef = FD; + CurrentBody = Body; + Body = + cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + RewriteLineDirective(D); + CurFunctionDef = 0; + } + break; + } + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = cast(D); + if (CompoundStmt *Body = MD->getCompoundBody()) { + CurMethodDef = MD; + CurrentBody = Body; + Body = + cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + InsertBlockLiteralsWithinMethod(MD); + RewriteLineDirective(D); + CurMethodDef = 0; + } + break; + } + case Decl::ObjCImplementation: { + ObjCImplementationDecl *CI = cast(D); + ClassImplementation.push_back(CI); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CI = cast(D); + CategoryImplementation.push_back(CI); + break; + } + case Decl::Var: { + VarDecl *VD = cast(D); + RewriteObjCQualifiedInterfaceTypes(VD); + if (isTopLevelBlockPointerType(VD->getType())) + RewriteBlockPointerDecl(VD); + else if (VD->getType()->isFunctionPointerType()) { + CheckFunctionPointerDecl(VD->getType(), VD); + if (VD->getInit()) { + if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } else if (VD->getType()->isRecordType()) { + RecordDecl *RD = VD->getType()->getAs()->getDecl(); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); + GlobalVarDecl = 0; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { + RewriteCastExpr(CE); + } + } + break; + } + case Decl::TypeAlias: + case Decl::Typedef: { + if (TypedefNameDecl *TD = dyn_cast(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + break; + } + case Decl::CXXRecord: + case Decl::Record: { + RecordDecl *RD = cast(D); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + break; + } + default: + break; + } + // Nothing yet. +} + +/// Write_ProtocolExprReferencedMetadata - This routine writer out the +/// protocol reference symbols in the for of: +/// struct _protocol_t *PROTOCOL_REF = &PROTOCOL_METADATA. +static void Write_ProtocolExprReferencedMetadata(ASTContext *Context, + ObjCProtocolDecl *PDecl, + std::string &Result) { + // Also output .objc_protorefs$B section and its meta-data. + if (Context->getLangOpts().MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t *"; + Result += "_OBJC_PROTOCOL_REFERENCE_$_"; + Result += PDecl->getNameAsString(); + Result += " = &"; + Result += "_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); + Result += ";\n"; +} + +void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) { + if (Diags.hasErrorOccurred()) + return; + + RewriteInclude(); + + // Here's a great place to add any extra declarations that may be needed. + // Write out meta data for each @protocol(). + for (llvm::SmallPtrSet::iterator I = ProtocolExprDecls.begin(), + E = ProtocolExprDecls.end(); I != E; ++I) { + RewriteObjCProtocolMetaData(*I, Preamble); + Write_ProtocolExprReferencedMetadata(Context, (*I), Preamble); + } + + InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); + + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + + for (unsigned i = 0, e = ObjCInterfacesSeen.size(); i < e; i++) { + ObjCInterfaceDecl *CDecl = ObjCInterfacesSeen[i]; + // Write struct declaration for the class matching its ivar declarations. + // Note that for modern abi, this is postponed until the end of TU + // because class extensions and the implementation might declare their own + // private ivars. + RewriteInterfaceDecl(CDecl); + } + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + //printf("Changed:\n"); + *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + llvm::errs() << "No changes\n"; + } + + if (ClassImplementation.size() || CategoryImplementation.size() || + ProtocolExprDecls.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + RewriteMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + // Emit ImageInfo; + { + std::string ResultStr; + WriteImageInfo(ResultStr); + *OutFile << ResultStr; + } + OutFile->flush(); +} + +void RewriteModernObjC::Initialize(ASTContext &context) { + InitializeCommon(context); + + Preamble += "#ifndef __OBJC2__\n"; + Preamble += "#define __OBJC2__\n"; + Preamble += "#endif\n"; + + // declaring objc_selector outside the parameter list removes a silly + // scope related warning... + if (IsHeader) + Preamble = "#pragma once\n"; + Preamble += "struct objc_selector; struct objc_class;\n"; + Preamble += "struct __rw_objc_super { \n\tstruct objc_object *object; "; + Preamble += "\n\tstruct objc_object *superClass; "; + // Add a constructor for creating temporary objects. + Preamble += "\n\t__rw_objc_super(struct objc_object *o, struct objc_object *s) "; + Preamble += ": object(o), superClass(s) {} "; + Preamble += "\n};\n"; + + if (LangOpts.MicrosoftExt) { + // Define all sections using syntax that makes sense. + // These are currently generated. + Preamble += "\n#pragma section(\".objc_classlist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_catlist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_imageinfo$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_nlclslist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_nlcatlist$B\", long, read, write)\n"; + // These are generated but not necessary for functionality. + Preamble += "#pragma section(\".cat_cls_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".inst_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".cls_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_ivar$B\", long, read, write)\n"; + + // These need be generated for performance. Currently they are not, + // using API calls instead. + Preamble += "#pragma section(\".objc_selrefs$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_classrefs$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_superrefs$B\", long, read, write)\n"; + + } + Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; + Preamble += "typedef struct objc_object Protocol;\n"; + Preamble += "#define _REWRITER_typedef_Protocol\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; + Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; + } + else + Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; + + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);\n"; + + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; + Preamble += "(struct objc_class *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);\n"; + // @synchronized hooks. + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; + Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; + Preamble += "struct __objcFastEnumerationState {\n\t"; + Preamble += "unsigned long state;\n\t"; + Preamble += "void **itemsPtr;\n\t"; + Preamble += "unsigned long *mutationsPtr;\n\t"; + Preamble += "unsigned long extra[5];\n};\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; + Preamble += "#define __FASTENUMERATIONSTATE\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; + Preamble += "struct __NSConstantStringImpl {\n"; + Preamble += " int *isa;\n"; + Preamble += " int flags;\n"; + Preamble += " char *str;\n"; + Preamble += " long length;\n"; + Preamble += "};\n"; + Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; + Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; + Preamble += "#endif\n"; + Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; + Preamble += "#endif\n"; + // Blocks preamble. + Preamble += "#ifndef BLOCK_IMPL\n"; + Preamble += "#define BLOCK_IMPL\n"; + Preamble += "struct __block_impl {\n"; + Preamble += " void *isa;\n"; + Preamble += " int Flags;\n"; + Preamble += " int Reserved;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; + Preamble += "extern \"C\" __declspec(dllexport) " + "void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. + Preamble += "#define __attribute__(X)\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __weak\n"; + Preamble += "#define __weak\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __block\n"; + Preamble += "#define __block\n"; + Preamble += "#endif\n"; + } + else { + Preamble += "#define __block\n"; + Preamble += "#define __weak\n"; + } + + // Declarations required for modern objective-c array and dictionary literals. + Preamble += "\n#include \n"; + Preamble += "struct __NSContainer_literal {\n"; + Preamble += " void * *arr;\n"; + Preamble += " __NSContainer_literal (unsigned int count, ...) {\n"; + Preamble += "\tva_list marker;\n"; + Preamble += "\tva_start(marker, count);\n"; + Preamble += "\tarr = new void *[count];\n"; + Preamble += "\tfor (unsigned i = 0; i < count; i++)\n"; + Preamble += "\t arr[i] = va_arg(marker, void *);\n"; + Preamble += "\tva_end( marker );\n"; + Preamble += " };\n"; + Preamble += " ~__NSContainer_literal() {\n"; + Preamble += "\tdelete[] arr;\n"; + Preamble += " }\n"; + Preamble += "};\n"; + + // Declaration required for implementation of @autoreleasepool statement. + Preamble += "extern \"C\" __declspec(dllimport) void * objc_autoreleasePoolPush(void);\n"; + Preamble += "extern \"C\" __declspec(dllimport) void objc_autoreleasePoolPop(void *);\n\n"; + Preamble += "struct __AtAutoreleasePool {\n"; + Preamble += " __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}\n"; + Preamble += " ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}\n"; + Preamble += " void * atautoreleasepoolobj;\n"; + Preamble += "};\n"; + + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long + // as this avoids warning in any 64bit/32bit compilation model. + Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; +} + +/// RewriteIvarOffsetComputation - This rutine synthesizes computation of +/// ivar offset. +void RewriteModernObjC::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) { + if (ivar->isBitField()) { + // FIXME: The hack below doesn't work for bitfields. For now, we simply + // place all bitfields at offset 0. + Result += "0"; + } else { + Result += "__OFFSETOFIVAR__(struct "; + Result += ivar->getContainingInterface()->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ", "; + Result += ivar->getNameAsString(); + Result += ")"; + } +} + +/// WriteModernMetadataDeclarations - Writes out metadata declarations for modern ABI. +/// struct _prop_t { +/// const char *name; +/// char *attributes; +/// } + +/// struct _prop_list_t { +/// uint32_t entsize; // sizeof(struct _prop_t) +/// uint32_t count_of_properties; +/// struct _prop_t prop_list[count_of_properties]; +/// } + +/// struct _protocol_t; + +/// struct _protocol_list_t { +/// long protocol_count; // Note, this is 32/64 bit +/// struct _protocol_t * protocol_list[protocol_count]; +/// } + +/// struct _objc_method { +/// SEL _cmd; +/// const char *method_type; +/// char *_imp; +/// } + +/// struct _method_list_t { +/// uint32_t entsize; // sizeof(struct _objc_method) +/// uint32_t method_count; +/// struct _objc_method method_list[method_count]; +/// } + +/// struct _protocol_t { +/// id isa; // NULL +/// const char *protocol_name; +/// const struct _protocol_list_t * protocol_list; // super protocols +/// const struct method_list_t *instance_methods; +/// const struct method_list_t *class_methods; +/// const struct method_list_t *optionalInstanceMethods; +/// const struct method_list_t *optionalClassMethods; +/// const struct _prop_list_t * properties; +/// const uint32_t size; // sizeof(struct _protocol_t) +/// const uint32_t flags; // = 0 +/// const char ** extendedMethodTypes; +/// } + +/// struct _ivar_t { +/// unsigned long int *offset; // pointer to ivar offset location +/// const char *name; +/// const char *type; +/// uint32_t alignment; +/// uint32_t size; +/// } + +/// struct _ivar_list_t { +/// uint32 entsize; // sizeof(struct _ivar_t) +/// uint32 count; +/// struct _ivar_t list[count]; +/// } + +/// struct _class_ro_t { +/// uint32_t flags; +/// uint32_t instanceStart; +/// uint32_t instanceSize; +/// uint32_t reserved; // only when building for 64bit targets +/// const uint8_t *ivarLayout; +/// const char *name; +/// const struct _method_list_t *baseMethods; +/// const struct _protocol_list_t *baseProtocols; +/// const struct _ivar_list_t *ivars; +/// const uint8_t *weakIvarLayout; +/// const struct _prop_list_t *properties; +/// } + +/// struct _class_t { +/// struct _class_t *isa; +/// struct _class_t *superclass; +/// void *cache; +/// IMP *vtable; +/// struct _class_ro_t *ro; +/// } + +/// struct _category_t { +/// const char *name; +/// struct _class_t *cls; +/// const struct _method_list_t *instance_methods; +/// const struct _method_list_t *class_methods; +/// const struct _protocol_list_t *protocols; +/// const struct _prop_list_t *properties; +/// } + +/// MessageRefTy - LLVM for: +/// struct _message_ref_t { +/// IMP messenger; +/// SEL name; +/// }; + +/// SuperMessageRefTy - LLVM for: +/// struct _super_message_ref_t { +/// SUPER_IMP messenger; +/// SEL name; +/// }; + +static void WriteModernMetadataDeclarations(ASTContext *Context, std::string &Result) { + static bool meta_data_declared = false; + if (meta_data_declared) + return; + + Result += "\nstruct _prop_t {\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst char *attributes;\n"; + Result += "};\n"; + + Result += "\nstruct _protocol_t;\n"; + + Result += "\nstruct _objc_method {\n"; + Result += "\tstruct objc_selector * _cmd;\n"; + Result += "\tconst char *method_type;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + Result += "\nstruct _protocol_t {\n"; + Result += "\tvoid * isa; // NULL\n"; + Result += "\tconst char *protocol_name;\n"; + Result += "\tconst struct _protocol_list_t * protocol_list; // super protocols\n"; + Result += "\tconst struct method_list_t *instance_methods;\n"; + Result += "\tconst struct method_list_t *class_methods;\n"; + Result += "\tconst struct method_list_t *optionalInstanceMethods;\n"; + Result += "\tconst struct method_list_t *optionalClassMethods;\n"; + Result += "\tconst struct _prop_list_t * properties;\n"; + Result += "\tconst unsigned int size; // sizeof(struct _protocol_t)\n"; + Result += "\tconst unsigned int flags; // = 0\n"; + Result += "\tconst char ** extendedMethodTypes;\n"; + Result += "};\n"; + + Result += "\nstruct _ivar_t {\n"; + Result += "\tunsigned long int *offset; // pointer to ivar offset location\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst char *type;\n"; + Result += "\tunsigned int alignment;\n"; + Result += "\tunsigned int size;\n"; + Result += "};\n"; + + Result += "\nstruct _class_ro_t {\n"; + Result += "\tunsigned int flags;\n"; + Result += "\tunsigned int instanceStart;\n"; + Result += "\tunsigned int instanceSize;\n"; + const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); + if (Triple.getArch() == llvm::Triple::x86_64) + Result += "\tunsigned int reserved;\n"; + Result += "\tconst unsigned char *ivarLayout;\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst struct _method_list_t *baseMethods;\n"; + Result += "\tconst struct _objc_protocol_list *baseProtocols;\n"; + Result += "\tconst struct _ivar_list_t *ivars;\n"; + Result += "\tconst unsigned char *weakIvarLayout;\n"; + Result += "\tconst struct _prop_list_t *properties;\n"; + Result += "};\n"; + + Result += "\nstruct _class_t {\n"; + Result += "\tstruct _class_t *isa;\n"; + Result += "\tstruct _class_t *superclass;\n"; + Result += "\tvoid *cache;\n"; + Result += "\tvoid *vtable;\n"; + Result += "\tstruct _class_ro_t *ro;\n"; + Result += "};\n"; + + Result += "\nstruct _category_t {\n"; + Result += "\tconst char *name;\n"; + Result += "\tstruct _class_t *cls;\n"; + Result += "\tconst struct _method_list_t *instance_methods;\n"; + Result += "\tconst struct _method_list_t *class_methods;\n"; + Result += "\tconst struct _protocol_list_t *protocols;\n"; + Result += "\tconst struct _prop_list_t *properties;\n"; + Result += "};\n"; + + Result += "extern \"C\" __declspec(dllimport) struct objc_cache _objc_empty_cache;\n"; + Result += "#pragma warning(disable:4273)\n"; + meta_data_declared = true; +} + +static void Write_protocol_list_t_TypeDecl(std::string &Result, + long super_protocol_count) { + Result += "struct /*_protocol_list_t*/"; Result += " {\n"; + Result += "\tlong protocol_count; // Note, this is 32/64 bit\n"; + Result += "\tstruct _protocol_t *super_protocols["; + Result += utostr(super_protocol_count); Result += "];\n"; + Result += "}"; +} + +static void Write_method_list_t_TypeDecl(std::string &Result, + unsigned int method_count) { + Result += "struct /*_method_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _objc_method)\n"; + Result += "\tunsigned int method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(method_count); Result += "];\n"; + Result += "}"; +} + +static void Write__prop_list_t_TypeDecl(std::string &Result, + unsigned int property_count) { + Result += "struct /*_prop_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; + Result += "\tunsigned int count_of_properties;\n"; + Result += "\tstruct _prop_t prop_list["; + Result += utostr(property_count); Result += "];\n"; + Result += "}"; +} + +static void Write__ivar_list_t_TypeDecl(std::string &Result, + unsigned int ivar_count) { + Result += "struct /*_ivar_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; + Result += "\tunsigned int count;\n"; + Result += "\tstruct _ivar_t ivar_list["; + Result += utostr(ivar_count); Result += "];\n"; + Result += "}"; +} + +static void Write_protocol_list_initializer(ASTContext *Context, std::string &Result, + ArrayRef SuperProtocols, + StringRef VarName, + StringRef ProtocolName) { + if (SuperProtocols.size() > 0) { + Result += "\nstatic "; + Write_protocol_list_t_TypeDecl(Result, SuperProtocols.size()); + Result += " "; Result += VarName; + Result += ProtocolName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += utostr(SuperProtocols.size()); Result += ",\n"; + for (unsigned i = 0, e = SuperProtocols.size(); i < e; i++) { + ObjCProtocolDecl *SuperPD = SuperProtocols[i]; + Result += "\t&"; Result += "_OBJC_PROTOCOL_"; + Result += SuperPD->getNameAsString(); + if (i == e-1) + Result += "\n};\n"; + else + Result += ",\n"; + } + } +} + +static void Write_method_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef Methods, + StringRef VarName, + StringRef TopLevelDeclName, + bool MethodImpl) { + if (Methods.size() > 0) { + Result += "\nstatic "; + Write_method_list_t_TypeDecl(Result, Methods.size()); + Result += " "; Result += VarName; + Result += TopLevelDeclName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_objc_method)"; Result += ",\n"; + Result += "\t"; Result += utostr(Methods.size()); Result += ",\n"; + for (unsigned i = 0, e = Methods.size(); i < e; i++) { + ObjCMethodDecl *MD = Methods[i]; + if (i == 0) + Result += "\t{{(struct objc_selector *)\""; + else + Result += "\t{(struct objc_selector *)\""; + Result += (MD)->getSelector().getAsString(); Result += "\""; + Result += ", "; + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(MD, MethodTypeString); + Result += "\""; Result += MethodTypeString; Result += "\""; + Result += ", "; + if (!MethodImpl) + Result += "0"; + else { + Result += "(void *)"; + Result += RewriteObj.MethodInternalNames[MD]; + } + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +static void Write_prop_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef Properties, + const Decl *Container, + StringRef VarName, + StringRef ProtocolName) { + if (Properties.size() > 0) { + Result += "\nstatic "; + Write__prop_list_t_TypeDecl(Result, Properties.size()); + Result += " "; Result += VarName; + Result += ProtocolName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_prop_t)"; Result += ",\n"; + Result += "\t"; Result += utostr(Properties.size()); Result += ",\n"; + for (unsigned i = 0, e = Properties.size(); i < e; i++) { + ObjCPropertyDecl *PropDecl = Properties[i]; + if (i == 0) + Result += "\t{{\""; + else + Result += "\t{\""; + Result += PropDecl->getName(); Result += "\","; + std::string PropertyTypeString, QuotePropertyTypeString; + Context->getObjCEncodingForPropertyDecl(PropDecl, Container, PropertyTypeString); + RewriteObj.QuoteDoublequotes(PropertyTypeString, QuotePropertyTypeString); + Result += "\""; Result += QuotePropertyTypeString; Result += "\""; + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +// Metadata flags +enum MetaDataDlags { + CLS = 0x0, + CLS_META = 0x1, + CLS_ROOT = 0x2, + OBJC2_CLS_HIDDEN = 0x10, + CLS_EXCEPTION = 0x20, + + /// (Obsolete) ARC-specific: this class has a .release_ivars method + CLS_HAS_IVAR_RELEASER = 0x40, + /// class was compiled with -fobjc-arr + CLS_COMPILED_BY_ARC = 0x80 // (1<<7) +}; + +static void Write__class_ro_t_initializer(ASTContext *Context, std::string &Result, + unsigned int flags, + const std::string &InstanceStart, + const std::string &InstanceSize, + ArrayRefbaseMethods, + ArrayRefbaseProtocols, + ArrayRefivars, + ArrayRefProperties, + StringRef VarName, + StringRef ClassName) { + Result += "\nstatic struct _class_ro_t "; + Result += VarName; Result += ClassName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; + Result += llvm::utostr(flags); Result += ", "; + Result += InstanceStart; Result += ", "; + Result += InstanceSize; Result += ", \n"; + Result += "\t"; + const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); + if (Triple.getArch() == llvm::Triple::x86_64) + // uint32_t const reserved; // only when building for 64bit targets + Result += "(unsigned int)0, \n\t"; + // const uint8_t * const ivarLayout; + Result += "0, \n\t"; + Result += "\""; Result += ClassName; Result += "\",\n\t"; + bool metaclass = ((flags & CLS_META) != 0); + if (baseMethods.size() > 0) { + Result += "(const struct _method_list_t *)&"; + if (metaclass) + Result += "_OBJC_$_CLASS_METHODS_"; + else + Result += "_OBJC_$_INSTANCE_METHODS_"; + Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + if (!metaclass && baseProtocols.size() > 0) { + Result += "(const struct _objc_protocol_list *)&"; + Result += "_OBJC_CLASS_PROTOCOLS_$_"; Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + if (!metaclass && ivars.size() > 0) { + Result += "(const struct _ivar_list_t *)&"; + Result += "_OBJC_$_INSTANCE_VARIABLES_"; Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + // weakIvarLayout + Result += "0, \n\t"; + if (!metaclass && Properties.size() > 0) { + Result += "(const struct _prop_list_t *)&"; + Result += "_OBJC_$_PROP_LIST_"; Result += ClassName; + Result += ",\n"; + } + else + Result += "0, \n"; + + Result += "};\n"; +} + +static void Write_class_t(ASTContext *Context, std::string &Result, + StringRef VarName, + const ObjCInterfaceDecl *CDecl, bool metaclass) { + bool rootClass = (!CDecl->getSuperClass()); + const ObjCInterfaceDecl *RootClass = CDecl; + + if (!rootClass) { + // Find the Root class + RootClass = CDecl->getSuperClass(); + while (RootClass->getSuperClass()) { + RootClass = RootClass->getSuperClass(); + } + } + + if (metaclass && rootClass) { + // Need to handle a case of use of forward declaration. + Result += "\n"; + Result += "extern \"C\" "; + if (CDecl->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t OBJC_CLASS_$_"; + Result += CDecl->getNameAsString(); + Result += ";\n"; + } + // Also, for possibility of 'super' metadata class not having been defined yet. + if (!rootClass) { + ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); + Result += "\n"; + Result += "extern \"C\" "; + if (SuperClass->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += VarName; + Result += SuperClass->getNameAsString(); + Result += ";\n"; + + if (metaclass && RootClass != SuperClass) { + Result += "extern \"C\" "; + if (RootClass->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += VarName; + Result += RootClass->getNameAsString(); + Result += ";\n"; + } + } + + Result += "\nextern \"C\" __declspec(dllexport) struct _class_t "; + Result += VarName; Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__objc_data\"))) = {\n"; + Result += "\t"; + if (metaclass) { + if (!rootClass) { + Result += "0, // &"; Result += VarName; + Result += RootClass->getNameAsString(); + Result += ",\n\t"; + Result += "0, // &"; Result += VarName; + Result += CDecl->getSuperClass()->getNameAsString(); + Result += ",\n\t"; + } + else { + Result += "0, // &"; Result += VarName; + Result += CDecl->getNameAsString(); + Result += ",\n\t"; + Result += "0, // &OBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ",\n\t"; + } + } + else { + Result += "0, // &OBJC_METACLASS_$_"; + Result += CDecl->getNameAsString(); + Result += ",\n\t"; + if (!rootClass) { + Result += "0, // &"; Result += VarName; + Result += CDecl->getSuperClass()->getNameAsString(); + Result += ",\n\t"; + } + else + Result += "0,\n\t"; + } + Result += "0, // (void *)&_objc_empty_cache,\n\t"; + Result += "0, // unused, was (void *)&_objc_empty_vtable,\n\t"; + if (metaclass) + Result += "&_OBJC_METACLASS_RO_$_"; + else + Result += "&_OBJC_CLASS_RO_$_"; + Result += CDecl->getNameAsString(); + Result += ",\n};\n"; + + // Add static function to initialize some of the meta-data fields. + // avoid doing it twice. + if (metaclass) + return; + + const ObjCInterfaceDecl *SuperClass = + rootClass ? CDecl : CDecl->getSuperClass(); + + Result += "static void OBJC_CLASS_SETUP_$_"; + Result += CDecl->getNameAsString(); + Result += "(void ) {\n"; + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; + Result += RootClass->getNameAsString(); Result += ";\n"; + + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".superclass = "; + if (rootClass) + Result += "&OBJC_CLASS_$_"; + else + Result += "&OBJC_METACLASS_$_"; + + Result += SuperClass->getNameAsString(); Result += ";\n"; + + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; + + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; + Result += CDecl->getNameAsString(); Result += ";\n"; + + if (!rootClass) { + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".superclass = "; Result += "&OBJC_CLASS_$_"; + Result += SuperClass->getNameAsString(); Result += ";\n"; + } + + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; + Result += "}\n"; +} + +static void Write_category_t(RewriteModernObjC &RewriteObj, ASTContext *Context, + std::string &Result, + ObjCCategoryDecl *CatDecl, + ObjCInterfaceDecl *ClassDecl, + ArrayRef InstanceMethods, + ArrayRef ClassMethods, + ArrayRef RefedProtocols, + ArrayRef ClassProperties) { + StringRef CatName = CatDecl->getName(); + StringRef ClassName = ClassDecl->getName(); + // must declare an extern class object in case this class is not implemented + // in this TU. + Result += "\n"; + Result += "extern \"C\" "; + if (ClassDecl->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += "OBJC_CLASS_$_"; Result += ClassName; + Result += ";\n"; + + Result += "\nstatic struct _category_t "; + Result += "_OBJC_$_CATEGORY_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; + Result += "{\n"; + Result += "\t\""; Result += ClassName; Result += "\",\n"; + Result += "\t0, // &"; Result += "OBJC_CLASS_$_"; Result += ClassName; + Result += ",\n"; + if (InstanceMethods.size() > 0) { + Result += "\t(const struct _method_list_t *)&"; + Result += "_OBJC_$_CATEGORY_INSTANCE_METHODS_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassMethods.size() > 0) { + Result += "\t(const struct _method_list_t *)&"; + Result += "_OBJC_$_CATEGORY_CLASS_METHODS_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (RefedProtocols.size() > 0) { + Result += "\t(const struct _protocol_list_t *)&"; + Result += "_OBJC_CATEGORY_PROTOCOLS_$_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassProperties.size() > 0) { + Result += "\t(const struct _prop_list_t *)&"; Result += "_OBJC_$_PROP_LIST_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + Result += "};\n"; + + // Add static function to initialize the class pointer in the category structure. + Result += "static void OBJC_CATEGORY_SETUP_$_"; + Result += ClassDecl->getNameAsString(); + Result += "_$_"; + Result += CatName; + Result += "(void ) {\n"; + Result += "\t_OBJC_$_CATEGORY_"; + Result += ClassDecl->getNameAsString(); + Result += "_$_"; + Result += CatName; + Result += ".cls = "; Result += "&OBJC_CLASS_$_"; Result += ClassName; + Result += ";\n}\n"; +} + +static void Write__extendedMethodTypes_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef Methods, + StringRef VarName, + StringRef ProtocolName) { + if (Methods.size() == 0) + return; + + Result += "\nstatic const char *"; + Result += VarName; Result += ProtocolName; + Result += " [] __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; + Result += "{\n"; + for (unsigned i = 0, e = Methods.size(); i < e; i++) { + ObjCMethodDecl *MD = Methods[i]; + std::string MethodTypeString, QuoteMethodTypeString; + Context->getObjCEncodingForMethodDecl(MD, MethodTypeString, true); + RewriteObj.QuoteDoublequotes(MethodTypeString, QuoteMethodTypeString); + Result += "\t\""; Result += QuoteMethodTypeString; Result += "\""; + if (i == e-1) + Result += "\n};\n"; + else { + Result += ",\n"; + } + } +} + +static void Write_IvarOffsetVar(RewriteModernObjC &RewriteObj, + ASTContext *Context, + std::string &Result, + ArrayRef Ivars, + ObjCInterfaceDecl *CDecl) { + // FIXME. visibilty of offset symbols may have to be set; for Darwin + // this is what happens: + /** + if (Ivar->getAccessControl() == ObjCIvarDecl::Private || + Ivar->getAccessControl() == ObjCIvarDecl::Package || + Class->getVisibility() == HiddenVisibility) + Visibility shoud be: HiddenVisibility; + else + Visibility shoud be: DefaultVisibility; + */ + + Result += "\n"; + for (unsigned i =0, e = Ivars.size(); i < e; i++) { + ObjCIvarDecl *IvarDecl = Ivars[i]; + if (Context->getLangOpts().MicrosoftExt) + Result += "__declspec(allocate(\".objc_ivar$B\")) "; + + if (!Context->getLangOpts().MicrosoftExt || + IvarDecl->getAccessControl() == ObjCIvarDecl::Private || + IvarDecl->getAccessControl() == ObjCIvarDecl::Package) + Result += "extern \"C\" unsigned long int "; + else + Result += "extern \"C\" __declspec(dllexport) unsigned long int "; + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += " __attribute__ ((used, section (\"__DATA,__objc_ivar\")))"; + Result += " = "; + RewriteObj.RewriteIvarOffsetComputation(IvarDecl, Result); + Result += ";\n"; + } +} + +static void Write__ivar_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef Ivars, + StringRef VarName, + ObjCInterfaceDecl *CDecl) { + if (Ivars.size() > 0) { + Write_IvarOffsetVar(RewriteObj, Context, Result, Ivars, CDecl); + + Result += "\nstatic "; + Write__ivar_list_t_TypeDecl(Result, Ivars.size()); + Result += " "; Result += VarName; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_ivar_t)"; Result += ",\n"; + Result += "\t"; Result += utostr(Ivars.size()); Result += ",\n"; + for (unsigned i =0, e = Ivars.size(); i < e; i++) { + ObjCIvarDecl *IvarDecl = Ivars[i]; + if (i == 0) + Result += "\t{{"; + else + Result += "\t {"; + Result += "(unsigned long int *)&"; + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += ", "; + + Result += "\""; Result += IvarDecl->getName(); Result += "\", "; + std::string IvarTypeString, QuoteIvarTypeString; + Context->getObjCEncodingForType(IvarDecl->getType(), IvarTypeString, + IvarDecl); + RewriteObj.QuoteDoublequotes(IvarTypeString, QuoteIvarTypeString); + Result += "\""; Result += QuoteIvarTypeString; Result += "\", "; + + // FIXME. this alignment represents the host alignment and need be changed to + // represent the target alignment. + unsigned Align = Context->getTypeAlign(IvarDecl->getType())/8; + Align = llvm::Log2_32(Align); + Result += llvm::utostr(Align); Result += ", "; + CharUnits Size = Context->getTypeSizeInChars(IvarDecl->getType()); + Result += llvm::utostr(Size.getQuantity()); + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. +void RewriteModernObjC::RewriteObjCProtocolMetaData(ObjCProtocolDecl *PDecl, + std::string &Result) { + + // Do not synthesize the protocol more than once. + if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) + return; + WriteModernMetadataDeclarations(Context, Result); + + if (ObjCProtocolDecl *Def = PDecl->getDefinition()) + PDecl = Def; + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), + E = PDecl->protocol_end(); I != E; ++I) + RewriteObjCProtocolMetaData(*I, Result); + + // Construct method lists. + std::vector InstanceMethods, ClassMethods; + std::vector OptInstanceMethods, OptClassMethods; + for (ObjCProtocolDecl::instmeth_iterator + I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); + I != E; ++I) { + ObjCMethodDecl *MD = *I; + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { + OptInstanceMethods.push_back(MD); + } else { + InstanceMethods.push_back(MD); + } + } + + for (ObjCProtocolDecl::classmeth_iterator + I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); + I != E; ++I) { + ObjCMethodDecl *MD = *I; + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { + OptClassMethods.push_back(MD); + } else { + ClassMethods.push_back(MD); + } + } + std::vector AllMethods; + for (unsigned i = 0, e = InstanceMethods.size(); i < e; i++) + AllMethods.push_back(InstanceMethods[i]); + for (unsigned i = 0, e = ClassMethods.size(); i < e; i++) + AllMethods.push_back(ClassMethods[i]); + for (unsigned i = 0, e = OptInstanceMethods.size(); i < e; i++) + AllMethods.push_back(OptInstanceMethods[i]); + for (unsigned i = 0, e = OptClassMethods.size(); i < e; i++) + AllMethods.push_back(OptClassMethods[i]); + + Write__extendedMethodTypes_initializer(*this, Context, Result, + AllMethods, + "_OBJC_PROTOCOL_METHOD_TYPES_", + PDecl->getNameAsString()); + // Protocol's super protocol list + std::vector SuperProtocols; + for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), + E = PDecl->protocol_end(); I != E; ++I) + SuperProtocols.push_back(*I); + + Write_protocol_list_initializer(Context, Result, SuperProtocols, + "_OBJC_PROTOCOL_REFS_", + PDecl->getNameAsString()); + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_PROTOCOL_INSTANCE_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_PROTOCOL_CLASS_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, OptInstanceMethods, + "_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, OptClassMethods, + "_OBJC_PROTOCOL_OPT_CLASS_METHODS_", + PDecl->getNameAsString(), false); + + // Protocol's property metadata. + std::vector ProtocolProperties; + for (ObjCContainerDecl::prop_iterator I = PDecl->prop_begin(), + E = PDecl->prop_end(); I != E; ++I) + ProtocolProperties.push_back(*I); + + Write_prop_list_t_initializer(*this, Context, Result, ProtocolProperties, + /* Container */0, + "_OBJC_PROTOCOL_PROPERTIES_", + PDecl->getNameAsString()); + + // Writer out root metadata for current protocol: struct _protocol_t + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t _OBJC_PROTOCOL_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__datacoal_nt,coalesced\"))) = {\n"; + Result += "\t0,\n"; // id is; is null + Result += "\t\""; Result += PDecl->getNameAsString(); Result += "\",\n"; + if (SuperProtocols.size() > 0) { + Result += "\t(const struct _protocol_list_t *)&"; Result += "_OBJC_PROTOCOL_REFS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + if (InstanceMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (OptInstanceMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (OptClassMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ProtocolProperties.size() > 0) { + Result += "\t(const struct _prop_list_t *)&_OBJC_PROTOCOL_PROPERTIES_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + Result += "\t"; Result += "sizeof(_protocol_t)"; Result += ",\n"; + Result += "\t0,\n"; + + if (AllMethods.size() > 0) { + Result += "\t(const char **)&"; Result += "_OBJC_PROTOCOL_METHOD_TYPES_"; + Result += PDecl->getNameAsString(); + Result += "\n};\n"; + } + else + Result += "\t0\n};\n"; + + if (LangOpts.MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t *"; + Result += "_OBJC_LABEL_PROTOCOL_$_"; Result += PDecl->getNameAsString(); + Result += " = &_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); + Result += ";\n"; + + // Mark this protocol as having been generated. + if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl())) + llvm_unreachable("protocol already synthesized"); + +} + +void RewriteModernObjC::RewriteObjCProtocolListMetaData( + const ObjCList &Protocols, + StringRef prefix, StringRef ClassName, + std::string &Result) { + if (Protocols.empty()) return; + + for (unsigned i = 0; i != Protocols.size(); i++) + RewriteObjCProtocolMetaData(Protocols[i], Result); + + // Output the top lovel protocol meta-data for the class. + /* struct _objc_protocol_list { + struct _objc_protocol_list *next; + int protocol_count; + struct _objc_protocol *class_protocols[]; + } + */ + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".cat_cls_meth$B\")) "; + Result += "static struct {\n"; + Result += "\tstruct _objc_protocol_list *next;\n"; + Result += "\tint protocol_count;\n"; + Result += "\tstruct _objc_protocol *class_protocols["; + Result += utostr(Protocols.size()); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += "_PROTOCOLS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t0, "; + Result += utostr(Protocols.size()); + Result += "\n"; + + Result += "\t,{&_OBJC_PROTOCOL_"; + Result += Protocols[0]->getNameAsString(); + Result += " \n"; + + for (unsigned i = 1; i != Protocols.size(); i++) { + Result += "\t ,&_OBJC_PROTOCOL_"; + Result += Protocols[i]->getNameAsString(); + Result += "\n"; + } + Result += "\t }\n};\n"; +} + +/// hasObjCExceptionAttribute - Return true if this class or any super +/// class has the __objc_exception__ attribute. +/// FIXME. Move this to ASTContext.cpp as it is also used for IRGen. +static bool hasObjCExceptionAttribute(ASTContext &Context, + const ObjCInterfaceDecl *OID) { + if (OID->hasAttr()) + return true; + if (const ObjCInterfaceDecl *Super = OID->getSuperClass()) + return hasObjCExceptionAttribute(Context, Super); + return false; +} + +void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explicitly declared @interface's are already synthesized. + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + + WriteModernMetadataDeclarations(Context, Result); + SmallVector IVars; + + for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + // Ignore unnamed bit-fields. + if (!IVD->getDeclName()) + continue; + IVars.push_back(IVD); + } + + Write__ivar_list_t_initializer(*this, Context, Result, IVars, + "_OBJC_$_INSTANCE_VARIABLES_", + CDecl); + + // Build _objc_method_list for class's instance methods if needed + SmallVector + InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), + PropEnd = IDecl->propimpl_end(); + Prop != PropEnd; ++Prop) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/)) + InstanceMethods.push_back(Setter); + } + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_$_INSTANCE_METHODS_", + IDecl->getNameAsString(), true); + + SmallVector + ClassMethods(IDecl->classmeth_begin(), IDecl->classmeth_end()); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_$_CLASS_METHODS_", + IDecl->getNameAsString(), true); + + // Protocols referenced in class declaration? + // Protocol's super protocol list + std::vector RefedProtocols; + const ObjCList &Protocols = CDecl->getReferencedProtocols(); + for (ObjCList::iterator I = Protocols.begin(), + E = Protocols.end(); + I != E; ++I) { + RefedProtocols.push_back(*I); + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + RewriteObjCProtocolMetaData(*I, Result); + } + + Write_protocol_list_initializer(Context, Result, + RefedProtocols, + "_OBJC_CLASS_PROTOCOLS_$_", + IDecl->getNameAsString()); + + // Protocol's property metadata. + std::vector ClassProperties; + for (ObjCContainerDecl::prop_iterator I = CDecl->prop_begin(), + E = CDecl->prop_end(); I != E; ++I) + ClassProperties.push_back(*I); + + Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, + /* Container */IDecl, + "_OBJC_$_PROP_LIST_", + CDecl->getNameAsString()); + + + // Data for initializing _class_ro_t metaclass meta-data + uint32_t flags = CLS_META; + std::string InstanceSize; + std::string InstanceStart; + + + bool classIsHidden = CDecl->getVisibility() == HiddenVisibility; + if (classIsHidden) + flags |= OBJC2_CLS_HIDDEN; + + if (!CDecl->getSuperClass()) + // class is root + flags |= CLS_ROOT; + InstanceSize = "sizeof(struct _class_t)"; + InstanceStart = InstanceSize; + Write__class_ro_t_initializer(Context, Result, flags, + InstanceStart, InstanceSize, + ClassMethods, + 0, + 0, + 0, + "_OBJC_METACLASS_RO_$_", + CDecl->getNameAsString()); + + + // Data for initializing _class_ro_t meta-data + flags = CLS; + if (classIsHidden) + flags |= OBJC2_CLS_HIDDEN; + + if (hasObjCExceptionAttribute(*Context, CDecl)) + flags |= CLS_EXCEPTION; + + if (!CDecl->getSuperClass()) + // class is root + flags |= CLS_ROOT; + + InstanceSize.clear(); + InstanceStart.clear(); + if (!ObjCSynthesizedStructs.count(CDecl)) { + InstanceSize = "0"; + InstanceStart = "0"; + } + else { + InstanceSize = "sizeof(struct "; + InstanceSize += CDecl->getNameAsString(); + InstanceSize += "_IMPL)"; + + ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + if (IVD) { + RewriteIvarOffsetComputation(IVD, InstanceStart); + } + else + InstanceStart = InstanceSize; + } + Write__class_ro_t_initializer(Context, Result, flags, + InstanceStart, InstanceSize, + InstanceMethods, + RefedProtocols, + IVars, + ClassProperties, + "_OBJC_CLASS_RO_$_", + CDecl->getNameAsString()); + + Write_class_t(Context, Result, + "OBJC_METACLASS_$_", + CDecl, /*metaclass*/true); + + Write_class_t(Context, Result, + "OBJC_CLASS_$_", + CDecl, /*metaclass*/false); + + if (ImplementationIsNonLazy(IDecl)) + DefinedNonLazyClasses.push_back(CDecl); + +} + +void RewriteModernObjC::RewriteClassSetupInitHook(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + if (!ClsDefCount) + return; + Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; + Result += "__declspec(allocate(\".objc_inithooks$B\")) "; + Result += "static void *OBJC_CLASS_SETUP[] = {\n"; + for (int i = 0; i < ClsDefCount; i++) { + ObjCImplementationDecl *IDecl = ClassImplementation[i]; + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + Result += "\t(void *)&OBJC_CLASS_SETUP_$_"; + Result += CDecl->getName(); Result += ",\n"; + } + Result += "};\n"; +} + +void RewriteModernObjC::RewriteMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // For each implemented class, write out all its meta data. + for (int i = 0; i < ClsDefCount; i++) + RewriteObjCClassMetaData(ClassImplementation[i], Result); + + RewriteClassSetupInitHook(Result); + + // For each implemented category, write out all its meta data. + for (int i = 0; i < CatDefCount; i++) + RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); + + RewriteCategorySetupInitHook(Result); + + if (ClsDefCount > 0) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_classlist$B\")) "; + Result += "static struct _class_t *L_OBJC_LABEL_CLASS_$ ["; + Result += llvm::utostr(ClsDefCount); Result += "]"; + Result += + " __attribute__((used, section (\"__DATA, __objc_classlist," + "regular,no_dead_strip\")))= {\n"; + for (int i = 0; i < ClsDefCount; i++) { + Result += "\t&OBJC_CLASS_$_"; + Result += ClassImplementation[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + + if (!DefinedNonLazyClasses.empty()) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_nlclslist$B\")) \n"; + Result += "static struct _class_t *_OBJC_LABEL_NONLAZY_CLASS_$[] = {\n\t"; + for (unsigned i = 0, e = DefinedNonLazyClasses.size(); i < e; i++) { + Result += "\t&OBJC_CLASS_$_"; Result += DefinedNonLazyClasses[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } + } + + if (CatDefCount > 0) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_catlist$B\")) "; + Result += "static struct _category_t *L_OBJC_LABEL_CATEGORY_$ ["; + Result += llvm::utostr(CatDefCount); Result += "]"; + Result += + " __attribute__((used, section (\"__DATA, __objc_catlist," + "regular,no_dead_strip\")))= {\n"; + for (int i = 0; i < CatDefCount; i++) { + Result += "\t&_OBJC_$_CATEGORY_"; + Result += + CategoryImplementation[i]->getClassInterface()->getNameAsString(); + Result += "_$_"; + Result += CategoryImplementation[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } + + if (!DefinedNonLazyCategories.empty()) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_nlcatlist$B\")) \n"; + Result += "static struct _category_t *_OBJC_LABEL_NONLAZY_CATEGORY_$[] = {\n\t"; + for (unsigned i = 0, e = DefinedNonLazyCategories.size(); i < e; i++) { + Result += "\t&_OBJC_$_CATEGORY_"; + Result += + DefinedNonLazyCategories[i]->getClassInterface()->getNameAsString(); + Result += "_$_"; + Result += DefinedNonLazyCategories[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } +} + +void RewriteModernObjC::WriteImageInfo(std::string &Result) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_imageinfo$B\")) \n"; + + Result += "static struct IMAGE_INFO { unsigned version; unsigned flag; } "; + // version 0, ObjCABI is 2 + Result += "_OBJC_IMAGE_INFO = { 0, 2 };\n"; +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + WriteModernMetadataDeclarations(Context, Result); + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl=0; + for (CDecl = ClassDecl->getCategoryList(); CDecl; + CDecl = CDecl->getNextClassCategory()) + if (CDecl->getIdentifier() == IDecl->getIdentifier()) + break; + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += "_$_"; + FullCategoryName += CDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + SmallVector + InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), + PropEnd = IDecl->propimpl_end(); + Prop != PropEnd; ++Prop) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + InstanceMethods.push_back(Setter); + } + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_$_CATEGORY_INSTANCE_METHODS_", + FullCategoryName, true); + + SmallVector + ClassMethods(IDecl->classmeth_begin(), IDecl->classmeth_end()); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_$_CATEGORY_CLASS_METHODS_", + FullCategoryName, true); + + // Protocols referenced in class declaration? + // Protocol's super protocol list + std::vector RefedProtocols; + for (ObjCInterfaceDecl::protocol_iterator I = CDecl->protocol_begin(), + E = CDecl->protocol_end(); + + I != E; ++I) { + RefedProtocols.push_back(*I); + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + RewriteObjCProtocolMetaData(*I, Result); + } + + Write_protocol_list_initializer(Context, Result, + RefedProtocols, + "_OBJC_CATEGORY_PROTOCOLS_$_", + FullCategoryName); + + // Protocol's property metadata. + std::vector ClassProperties; + for (ObjCContainerDecl::prop_iterator I = CDecl->prop_begin(), + E = CDecl->prop_end(); I != E; ++I) + ClassProperties.push_back(*I); + + Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, + /* Container */IDecl, + "_OBJC_$_PROP_LIST_", + FullCategoryName); + + Write_category_t(*this, Context, Result, + CDecl, + ClassDecl, + InstanceMethods, + ClassMethods, + RefedProtocols, + ClassProperties); + + // Determine if this category is also "non-lazy". + if (ImplementationIsNonLazy(IDecl)) + DefinedNonLazyCategories.push_back(CDecl); + +} + +void RewriteModernObjC::RewriteCategorySetupInitHook(std::string &Result) { + int CatDefCount = CategoryImplementation.size(); + if (!CatDefCount) + return; + Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; + Result += "__declspec(allocate(\".objc_inithooks$B\")) "; + Result += "static void *OBJC_CATEGORY_SETUP[] = {\n"; + for (int i = 0; i < CatDefCount; i++) { + ObjCCategoryImplDecl *IDecl = CategoryImplementation[i]; + ObjCCategoryDecl *CatDecl= IDecl->getCategoryDecl(); + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + Result += "\t(void *)&OBJC_CATEGORY_SETUP_$_"; + Result += ClassDecl->getName(); + Result += "_$_"; + Result += CatDecl->getName(); + Result += ",\n"; + } + Result += "};\n"; +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +template +void RewriteModernObjC::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + if (!objc_impl_method) { + /* struct _objc_method { + SEL _cmd; + char *method_types; + void *_imp; + } + */ + Result += "\nstruct _objc_method {\n"; + Result += "\tSEL _cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + objc_impl_method = true; + } + + // Build _objc_method_list for class's methods if needed + + /* struct { + struct _objc_method_list *next_method; + int method_count; + struct _objc_method method_list[]; + } + */ + unsigned NumMethods = std::distance(MethodBegin, MethodEnd); + Result += "\n"; + if (LangOpts.MicrosoftExt) { + if (IsInstanceMethod) + Result += "__declspec(allocate(\".inst_meth$B\")) "; + else + Result += "__declspec(allocate(\".cls_meth$B\")) "; + } + Result += "static struct {\n"; + Result += "\tstruct _objc_method_list *next_method;\n"; + Result += "\tint method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; + Result += "_METHODS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __"; + Result += IsInstanceMethod ? "inst" : "cls"; + Result += "_meth\")))= "; + Result += "{\n\t0, " + utostr(NumMethods) + "\n"; + + Result += "\t,{{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { + Result += "\t ,{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + } + Result += "\t }\n};\n"; +} + +Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + + ObjCIvarDecl *D = IV->getDecl(); + + Expr *Replacement = IV; + + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast(BaseExpr->getType()->getPointeeType()); + assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = 0; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + WriteInternalIvarName(clsDeclared, D, IvarOffsetName); + + ReferencedIvars[clsDeclared].insert(D); + + // cast offset to "char *". + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->CharTy), + CK_BitCast, + BaseExpr); + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(IvarOffsetName), + Context->UnsignedLongTy, 0, SC_Extern, SC_None); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, + Context->UnsignedLongTy, VK_LValue, + SourceLocation()); + BinaryOperator *addExpr = + new (Context) BinaryOperator(castExpr, DRE, BO_Add, + Context->getPointerType(Context->CharTy), + VK_RValue, OK_Ordinary, SourceLocation(), false); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), + SourceLocation(), + addExpr); + QualType IvarT = D->getType(); + + if (!isa(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName.c_str())); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + + castExpr = NoTypeInfoCStyleCastExpr(Context, + castT, + CK_BitCast, + PE); + + + Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, + VK_LValue, OK_Ordinary, + SourceLocation()); + PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), + Exp); + + Replacement = PE; + } + + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; +} diff --git a/lib/Rewrite/Frontend/RewriteObjC.cpp b/lib/Rewrite/Frontend/RewriteObjC.cpp new file mode 100644 index 0000000000000..a6dcc6b8d8045 --- /dev/null +++ b/lib/Rewrite/Frontend/RewriteObjC.cpp @@ -0,0 +1,6029 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/DenseSet.h" + +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteObjC : public ASTConsumer { + protected: + + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the + __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose + support routines */ + BLOCK_BYREF_CURRENT_MAX = 256 + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + static const int OBJC_ABI_VERSION = 7; + + Rewriter Rewrite; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + std::string InFileName; + raw_ostream* OutFile; + std::string Preamble; + + TypeDecl *ProtocolTypeDecl; + VarDecl *GlobalVarDecl; + unsigned RewriteFailedDiag; + // ObjC string constant support. + unsigned NumObjCStringLiterals; + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + unsigned TryFinallyContainsReturnDiag; + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *GetSuperClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperContructorFunctionDecl; + FunctionDecl *CurFunctionDef; + FunctionDecl *CurFunctionDeclToDeclareForBlock; + + /* Misc. containers needed for meta-data rewrite. */ + SmallVector ClassImplementation; + SmallVector CategoryImplementation; + llvm::SmallPtrSet ObjCSynthesizedStructs; + llvm::SmallPtrSet ObjCSynthesizedProtocols; + llvm::SmallPtrSet ObjCForwardDecls; + llvm::DenseMap MethodInternalNames; + SmallVector Stmts; + SmallVector ObjCBcLabelNo; + // Remember all the @protocol() expressions. + llvm::SmallPtrSet ProtocolExprDecls; + + llvm::DenseSet CopyDestroyCache; + + // Block expressions. + SmallVector Blocks; + SmallVector InnerDeclRefsCount; + SmallVector InnerDeclRefs; + + SmallVector BlockDeclRefs; + + // Block related declarations. + SmallVector BlockByCopyDecls; + llvm::SmallPtrSet BlockByCopyDeclsPtrSet; + SmallVector BlockByRefDecls; + llvm::SmallPtrSet BlockByRefDeclsPtrSet; + llvm::DenseMap BlockByRefDeclNo; + llvm::SmallPtrSet ImportedBlockDecls; + llvm::SmallPtrSet ImportedLocalExternalDecls; + + llvm::DenseMap RewrittenBlockExprs; + + // This maps an original source AST to it's rewritten form. This allows + // us to avoid rewriting the same node twice (which is very uncommon). + // This is needed to support some of the exotic property rewriting. + llvm::DenseMap ReplacedNodes; + + // Needed for header files being rewritten + bool IsHeader; + bool SilenceRewriteMacroWarning; + bool objc_impl_method; + + bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; + void InitializeCommon(ASTContext &context); + + public: + + // Top Level Driver code. + virtual bool HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *Class = dyn_cast(*I)) { + if (!Class->isThisDeclarationADefinition()) { + RewriteForwardClassDecl(D); + break; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast(*I)) { + if (!Proto->isThisDeclarationADefinition()) { + RewriteForwardProtocolDecl(D); + break; + } + } + + HandleTopLevelSingleDecl(*I); + } + return true; + } + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteObjC(std::string inFile, raw_ostream *OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn); + + ~RewriteObjC() {} + + virtual void HandleTranslationUnit(ASTContext &C); + + void ReplaceStmt(Stmt *Old, Stmt *New) { + Stmt *ReplacingStmt = ReplacedNodes[Old]; + + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceStmt(Old, New)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + if (DisableReplaceStmt) + return; + + // Measure the old text. + int Size = Rewrite.getRangeSize(SrcRange); + if (Size == -1) { + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + return; + } + // Get the new text. + std::string SStr; + llvm::raw_string_ostream S(SStr); + New->printPretty(S, 0, PrintingPolicy(LangOpts)); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, Str, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef Str) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, Str) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewriteRecordBody(RecordDecl *RD); + void RewriteInclude(); + void RewriteForwardClassDecl(DeclGroupRef D); + void RewriteForwardClassDecl(const llvm::SmallVector &DG); + void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString); + void RewriteImplementations(); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType); + void RewriteByRefString(std::string &ResultStr, const std::string &Name, + ValueDecl *VD, bool def=false); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(DeclGroupRef D); + void RewriteForwardProtocolDecl(const llvm::SmallVector &DG); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteBlockPointerType(std::string& Str, QualType Type); + void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteTypeOfDecl(VarDecl *VD); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + void RewriteTryReturnStmts(Stmt *S); + void RewriteSyncReturnStmts(Stmt *S, std::string buf); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void RewriteCastExpr(CStyleCastExpr *CE); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + + // Block specific rewrite rules. + void RewriteBlockPointerDecl(NamedDecl *VD); + void RewriteByRefVar(VarDecl *VD); + Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); + Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + + virtual void Initialize(ASTContext &context) = 0; + + // Metadata Rewriting. + virtual void RewriteMetaDataIntoBuffer(std::string &Result) = 0; + virtual void RewriteObjCProtocolListMetaData(const ObjCList &Prots, + StringRef prefix, + StringRef ClassName, + std::string &Result) = 0; + virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result) = 0; + virtual void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + StringRef prefix, + StringRef ClassName, + std::string &Result) = 0; + virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) = 0; + + // Rewriting ivar access + virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) = 0; + virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) = 0; + + // Misc. AST transformation routines. Sometimes they end up calling + // rewriting routines on the new ASTs. + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + Expr **args, unsigned nargs, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + CallExpr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl &ArgTypes, + SmallVectorImpl &MsgExprs, + ObjCMethodDecl *Method); + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + void SynthCountByEnumWithState(std::string &buf); + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthGetSuperClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperContructorFunctionDecl(); + + std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, + std::string Tag, std::string Desc); + std::string SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, + int i, StringRef funcName, + unsigned hasCopy); + Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName); + FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp, + const SmallVector &InnerBlockDeclRefs); + + // Misc. helper routines. + QualType getProtocolType(); + void WarnAboutReturnGotoStmts(Stmt *S); + void HasReturnStmts(Stmt *S, bool &hasReturns); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + bool IsDeclStmtInForeachHeader(DeclStmt *DS); + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockDeclRefExprs(Stmt *S); + void GetInnerBlockDeclRefExprs(Stmt *S, + SmallVector &InnerBlockDeclRefs, + llvm::SmallPtrSet &InnerContexts); + + // We avoid calling Type::isBlockPointerType(), since it operates on the + // canonical type. We only care if the top-level type is a closure pointer. + bool isTopLevelBlockPointerType(QualType T) { + return isa(T); + } + + /// convertBlockPointerToFunctionPointer - Converts a block-pointer type + /// to a function pointer type and upon success, returns true; false + /// otherwise. + bool convertBlockPointerToFunctionPointer(QualType &T) { + if (isTopLevelBlockPointerType(T)) { + const BlockPointerType *BPT = T->getAs(); + T = Context->getPointerType(BPT->getPointeeType()); + return true; + } + return false; + } + + bool needToScanForQualifiers(QualType T); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + QualType convertFunctionTypeOfBlocks(const FunctionType *FT); + bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); + + void convertToUnqualifiedObjCType(QualType &T) { + if (T->isObjCQualifiedIdType()) + T = Context->getObjCIdType(); + else if (T->isObjCQualifiedClassType()) + T = Context->getObjCClassType(); + else if (T->isObjCObjectPointerType() && + T->getPointeeType()->isObjCQualifiedInterfaceType()) { + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); + T = QualType(IFaceT, 0); + T = Context->getPointerType(T); + } + } + } + + // FIXME: This predicate seems like it would be useful to add to ASTContext. + bool isObjCType(QualType T) { + if (!LangOpts.ObjC1 && !LangOpts.ObjC2) + return false; + + QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); + + if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || + OCT == Context->getCanonicalType(Context->getObjCClassType())) + return true; + + if (const PointerType *PT = OCT->getAs()) { + if (isa(PT->getPointeeType()) || + PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + return false; + } + bool PointerTypeTakesAnyBlockArguments(QualType QT); + bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for (unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + + QualType getSimpleFunctionType(QualType result, + const QualType *args, + unsigned numArgs, + bool variadic = false) { + if (result == Context->getObjCInstanceType()) + result = Context->getObjCIdType(); + FunctionProtoType::ExtProtoInfo fpi; + fpi.Variadic = variadic; + return Context->getFunctionType(result, args, numArgs, fpi); + } + + // Helper function: create a CStyleCastExpr with trivial type source info. + CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, + CastKind Kind, Expr *E) { + TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); + return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, 0, TInfo, + SourceLocation(), SourceLocation()); + } + }; + + class RewriteObjCFragileABI : public RewriteObjC { + public: + + RewriteObjCFragileABI(std::string inFile, raw_ostream *OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) : RewriteObjC(inFile, OS, + D, LOpts, + silenceMacroWarn) {} + + ~RewriteObjCFragileABI() {} + virtual void Initialize(ASTContext &context); + + // Rewriting metadata + template + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result); + virtual void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + StringRef prefix, + StringRef ClassName, + std::string &Result); + virtual void RewriteObjCProtocolListMetaData( + const ObjCList &Prots, + StringRef prefix, StringRef ClassName, std::string &Result); + virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result); + virtual void RewriteMetaDataIntoBuffer(std::string &Result); + virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result); + + // Rewriting ivar + virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result); + virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); + }; +} + +void RewriteObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (const FunctionProtoType *fproto + = dyn_cast(funcType.IgnoreParens())) { + for (FunctionProtoType::arg_type_iterator I = fproto->arg_type_begin(), + E = fproto->arg_type_end(); I && (I != E); ++I) + if (isTopLevelBlockPointerType(*I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAs(); + if (PT && PointerTypeTakesAnyBlockArguments(funcType)) + RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); +} + +static bool IsHeaderFile(const std::string &Filename) { + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + // no file extension + return false; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + return Ext == "h" || Ext == "hh" || Ext == "H"; +} + +RewriteObjC::RewriteObjC(std::string inFile, raw_ostream* OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), + SilenceRewriteMacroWarning(silenceMacroWarn) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + TryFinallyContainsReturnDiag = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +ASTConsumer *clang::CreateObjCRewriter(const std::string& InFile, + raw_ostream* OS, + DiagnosticsEngine &Diags, + const LangOptions &LOpts, + bool SilenceRewriteMacroWarning) { + return new RewriteObjCFragileABI(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning); +} + +void RewriteObjC::InitializeCommon(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = 0; + MsgSendSuperFunctionDecl = 0; + MsgSendStretFunctionDecl = 0; + MsgSendSuperStretFunctionDecl = 0; + MsgSendFpretFunctionDecl = 0; + GetClassFunctionDecl = 0; + GetMetaClassFunctionDecl = 0; + GetSuperClassFunctionDecl = 0; + SelGetUidFunctionDecl = 0; + CFStringFunctionDecl = 0; + ConstantStringClassReference = 0; + NSStringRecord = 0; + CurMethodDef = 0; + CurFunctionDef = 0; + CurFunctionDeclToDeclareForBlock = 0; + GlobalVarDecl = 0; + SuperStructDecl = 0; + ProtocolTypeDecl = 0; + ConstantStringDecl = 0; + BcLabelCount = 0; + SuperContructorFunctionDecl = 0; + NumObjCStringLiterals = 0; + PropParentMap = 0; + CurrentBody = 0; + DisableReplaceStmt = false; + objc_impl_method = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::HandleTopLevelSingleDecl(Decl *D) { + if (Diags.hasErrorOccurred()) + return; + + // Two cases: either the decl could be in the main file, or it could be in a + // #included file. If the former, rewrite it now. If the later, check to see + // if we rewrote the #include/#import. + SourceLocation Loc = D->getLocation(); + Loc = SM->getExpansionLoc(Loc); + + // If this is for a builtin, ignore it. + if (Loc.isInvalid()) return; + + // Look for built-in declarations that we need to refer during the rewrite. + if (FunctionDecl *FD = dyn_cast(D)) { + RewriteFunctionDecl(FD); + } else if (VarDecl *FVD = dyn_cast(D)) { + // declared in + if (FVD->getName() == "_NSConstantStringClassReference") { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCInterfaceDecl *ID = dyn_cast(D)) { + if (ID->isThisDeclarationADefinition()) + RewriteInterfaceDecl(ID); + } else if (ObjCCategoryDecl *CD = dyn_cast(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast(D)) { + if (PD->isThisDeclarationADefinition()) + RewriteProtocolDecl(PD); + } else if (LinkageSpecDecl *LSD = dyn_cast(D)) { + // Recurse into linkage specifications + for (DeclContext::decl_iterator DI = LSD->decls_begin(), + DIEnd = LSD->decls_end(); + DI != DIEnd; ) { + if (ObjCInterfaceDecl *IFace = dyn_cast((*DI))) { + if (!IFace->isThisDeclarationADefinition()) { + SmallVector DG; + SourceLocation StartLoc = IFace->getLocStart(); + do { + if (isa(*DI) && + !cast(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardClassDecl(DG); + continue; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast((*DI))) { + if (!Proto->isThisDeclarationADefinition()) { + SmallVector DG; + SourceLocation StartLoc = Proto->getLocStart(); + do { + if (isa(*DI) && + !cast(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getLocStart()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardProtocolDecl(DG); + continue; + } + } + + HandleTopLevelSingleDecl(*DI); + ++DI; + } + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isFromMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + StringRef MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.begin(); + const char *MainBufEnd = MainBuf.end(); + size_t ImportLen = strlen("import"); + + // Loop over the whole file, looking for includes. + for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { + if (*BufPtr == '#') { + if (++BufPtr == MainBufEnd) + return; + while (*BufPtr == ' ' || *BufPtr == '\t') + if (++BufPtr == MainBufEnd) + return; + if (!strncmp(BufPtr, "import", ImportLen)) { + // replace import with include + SourceLocation ImportLoc = + LocStart.getLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include"); + BufPtr += ImportLen; + } + } + } +} + +static std::string getIvarAccessString(ObjCIvarDecl *OID) { + const ObjCInterfaceDecl *ClassDecl = OID->getContainingInterface(); + std::string S; + S = "((struct "; + S += ClassDecl->getIdentifier()->getName(); + S += "_IMPL *)self)->"; + S += OID->getName(); + return S; +} + +void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + static bool objcGetPropertyDefined = false; + static bool objcSetPropertyDefined = false; + SourceLocation startLoc = PID->getLocStart(); + InsertText(startLoc, "// "); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + SourceLocation onePastSemiLoc = + startLoc.getLocWithOffset(semiBuf-startBuf+1); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + + if (!OID) + return; + unsigned Attributes = PD->getPropertyAttributes(); + if (!PD->getGetterMethodDecl()->isDefined()) { + bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && + (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy)); + std::string Getr; + if (GenGetProperty && !objcGetPropertyDefined) { + objcGetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Getr = "\nextern \"C\" __declspec(dllimport) " + "id objc_getProperty(id, SEL, long, bool);\n"; + } + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // See objc-act.c:objc_synthesize_new_getter() for details. + if (GenGetProperty) { + // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) + Getr += "typedef "; + const FunctionType *FPRetType = 0; + RewriteTypeIntoString(PD->getGetterMethodDecl()->getResultType(), Getr, + FPRetType); + Getr += " _TYPE"; + if (FPRetType) { + Getr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast(FPRetType)){ + Getr += "("; + for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { + if (i) Getr += ", "; + std::string ParamStr = FT->getArgType(i).getAsString( + Context->getPrintingPolicy()); + Getr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumArgs()) Getr += ", "; + Getr += "..."; + } + Getr += ")"; + } else + Getr += "()"; + } + Getr += ";\n"; + Getr += "return (_TYPE)"; + Getr += "objc_getProperty(self, _cmd, "; + RewriteIvarOffsetComputation(OID, Getr); + Getr += ", 1)"; + } + else + Getr += "return " + getIvarAccessString(OID); + Getr += "; }"; + InsertText(onePastSemiLoc, Getr); + } + + if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined()) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy); + if (GenSetProperty && !objcSetPropertyDefined) { + objcSetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Setr = "\nextern \"C\" __declspec(dllimport) " + "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; + } + + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // See objc-act.c:objc_synthesize_new_setter() for details. + if (GenSetProperty) { + Setr += "objc_setProperty (self, _cmd, "; + RewriteIvarOffsetComputation(OID, Setr); + Setr += ", (id)"; + Setr += PD->getName(); + Setr += ", "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) + Setr += "1)"; + else + Setr += "0)"; + } + else { + Setr += getIvarAccessString(OID) + " = "; + Setr += PD->getName(); + } + Setr += "; }"; + InsertText(onePastSemiLoc, Setr); +} + +static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, + std::string &typedefString) { + typedefString += "#ifndef _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "#define _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "typedef struct objc_object "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n#endif\n"; +} + +void RewriteObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString) { + SourceLocation startLoc = ClassDecl->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); +} + +void RewriteObjC::RewriteForwardClassDecl(DeclGroupRef D) { + std::string typedefString; + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + ObjCInterfaceDecl *ForwardDecl = cast(*I); + if (I == D.begin()) { + // Translate to typedef's that forward reference structs with the same name + // as the class. As a convenience, we include the original declaration + // as a comment. + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + DeclGroupRef::iterator I = D.begin(); + RewriteForwardClassEpilogue(cast(*I), typedefString); +} + +void RewriteObjC::RewriteForwardClassDecl( + const llvm::SmallVector &D) { + std::string typedefString; + for (unsigned i = 0; i < D.size(); i++) { + ObjCInterfaceDecl *ForwardDecl = cast(D[i]); + if (i == 0) { + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + RewriteForwardClassEpilogue(cast(D[0]), typedefString); +} + +void RewriteObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + // When method is a synthesized one, such as a getter/setter there is + // nothing to rewrite. + if (Method->isImplicit()) + return; + SourceLocation LocStart = Method->getLocStart(); + SourceLocation LocEnd = Method->getLocEnd(); + + if (SM->getExpansionLineNumber(LocEnd) > + SM->getExpansionLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n"); + ReplaceText(LocEnd, 1, ";\n#endif\n"); + } else { + InsertText(LocStart, "// "); + } +} + +void RewriteObjC::RewriteProperty(ObjCPropertyDecl *prop) { + SourceLocation Loc = prop->getAtLoc(); + + ReplaceText(Loc, 0, "// "); + // FIXME: handle properties that are declared across multiple lines. +} + +void RewriteObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + SourceLocation LocStart = CatDecl->getLocStart(); + + // FIXME: handle category headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (ObjCCategoryDecl::prop_iterator I = CatDecl->prop_begin(), + E = CatDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + + for (ObjCCategoryDecl::instmeth_iterator + I = CatDecl->instmeth_begin(), E = CatDecl->instmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCCategoryDecl::classmeth_iterator + I = CatDecl->classmeth_begin(), E = CatDecl->classmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndRange().getBegin(), + strlen("@end"), "/* @end */"); +} + +void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getLocStart(); + assert(PDecl->isThisDeclarationADefinition()); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (ObjCProtocolDecl::instmeth_iterator + I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCProtocolDecl::classmeth_iterator + I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + + for (ObjCInterfaceDecl::prop_iterator I = PDecl->prop_begin(), + E = PDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); + ReplaceText(LocEnd, strlen("@end"), "/* @end */"); + + // Must comment out @optional/@required + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + for (const char *p = startBuf; p < endBuf; p++) { + if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); + + } + } +} + +void RewriteObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { + SourceLocation LocStart = (*D.begin())->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteObjC::RewriteForwardProtocolDecl(const llvm::SmallVector &DG) { + SourceLocation LocStart = DG[0]->getLocStart(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void RewriteObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType) { + if (T->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (T->isFunctionPointerType() || + T->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = T; + QualType PointeeTy; + if (const PointerType* PT = retType->getAs()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAs()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAs())) { + ResultStr += FPRetType->getResultType().getAsString( + Context->getPrintingPolicy()); + ResultStr += "(*"; + } + } else + ResultStr += T.getAsString(Context->getPrintingPolicy()); +} + +void RewriteObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = 0; + ResultStr += "\nstatic "; + RewriteTypeIntoString(OMD->getResultType(), ResultStr, FPRetType); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += IDecl->getNameAsString(); + NameStr += "_"; + + if (ObjCCategoryImplDecl *CID = + dyn_cast(OMD->getDeclContext())) { + NameStr += CID->getNameAsString(); + NameStr += "_"; + } + // Append selector names, replacing ':' with '_' + { + std::string selString = OMD->getSelector().getAsString(); + int len = selString.size(); + for (int i = 0; i < len; i++) + if (selString[i] == ':') + selString[i] = '_'; + NameStr += selString; + } + // Remember this name for metadata emission + MethodInternalNames[OMD] = NameStr; + ResultStr += NameStr; + + // Rewrite arguments + ResultStr += "("; + + // invisible arguments + if (OMD->isInstanceMethod()) { + QualType selfTy = Context->getObjCInterfaceType(IDecl); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.MicrosoftExt) { + if (ObjCSynthesizedStructs.count(const_cast(IDecl))) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += IDecl->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCClassType().getAsString( + Context->getPrintingPolicy()); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); + ResultStr += " _cmd"; + + // Method arguments. + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + ParmVarDecl *PDecl = *PI; + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + QualType QT = PDecl->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + ResultStr += Name; + } + } + if (OMD->isVariadic()) + ResultStr += ", ..."; + ResultStr += ") "; + + if (FPRetType) { + ResultStr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast(FPRetType)) { + ResultStr += "("; + for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = FT->getArgType(i).getAsString( + Context->getPrintingPolicy()); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumArgs()) ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} +void RewriteObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast(OID); + ObjCCategoryImplDecl *CID = dyn_cast(OID); + + InsertText(IMD ? IMD->getLocStart() : CID->getLocStart(), "// "); + + for (ObjCCategoryImplDecl::instmeth_iterator + I = IMD ? IMD->instmeth_begin() : CID->instmeth_begin(), + E = IMD ? IMD->instmeth_end() : CID->instmeth_end(); + I != E; ++I) { + std::string ResultStr; + ObjCMethodDecl *OMD = *I; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + + for (ObjCCategoryImplDecl::classmeth_iterator + I = IMD ? IMD->classmeth_begin() : CID->classmeth_begin(), + E = IMD ? IMD->classmeth_end() : CID->classmeth_end(); + I != E; ++I) { + std::string ResultStr; + ObjCMethodDecl *OMD = *I; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getLocStart(); + SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + for (ObjCCategoryImplDecl::propimpl_iterator + I = IMD ? IMD->propimpl_begin() : CID->propimpl_begin(), + E = IMD ? IMD->propimpl_end() : CID->propimpl_end(); + I != E; ++I) { + RewritePropertyImplDecl(*I, IMD, CID); + } + + InsertText(IMD ? IMD->getLocEnd() : CID->getLocEnd(), "// "); +} + +void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + std::string ResultStr; + if (!ObjCForwardDecls.count(ClassDecl->getCanonicalDecl())) { + // we haven't seen a forward decl - generate a typedef. + ResultStr = "#ifndef _REWRITER_typedef_"; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += "\n"; + ResultStr += "#define _REWRITER_typedef_"; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += "\n"; + ResultStr += "typedef struct objc_object "; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += ";\n#endif\n"; + // Mark this typedef as having been generated. + ObjCForwardDecls.insert(ClassDecl->getCanonicalDecl()); + } + RewriteObjCInternalStruct(ClassDecl, ResultStr); + + for (ObjCInterfaceDecl::prop_iterator I = ClassDecl->prop_begin(), + E = ClassDecl->prop_end(); I != E; ++I) + RewriteProperty(*I); + for (ObjCInterfaceDecl::instmeth_iterator + I = ClassDecl->instmeth_begin(), E = ClassDecl->instmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + for (ObjCInterfaceDecl::classmeth_iterator + I = ClassDecl->classmeth_begin(), E = ClassDecl->classmeth_end(); + I != E; ++I) + RewriteMethodDeclaration(*I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), + "/* @end */"); +} + +Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base, *RHS; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = 0; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast(Base)->getSourceExpr(); + Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + + // Rebuild the RHS. + RHS = cast(PseudoOp->getSyntacticForm())->getRHS(); + RHS = cast(RHS)->getSourceExpr(); + RHS = cast(RewriteFunctionBodyOrGlobalInitializer(RHS)); + } + + // TODO: avoid this copy. + SmallVector SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg = 0; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = 0; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast(Base)->getSourceExpr(); + Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + } + + // Intentionally empty. + SmallVector SelLocs; + SmallVector Args; + + ObjCMessageExpr *NewMsg = 0; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +/// SynthCountByEnumWithState - To print: +/// ((unsigned int (*) +/// (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) +/// (void *)objc_msgSend)((id)l_collection, +/// sel_registerName( +/// "countByEnumeratingWithState:objects:count:"), +/// &enumState, +/// (id *)__rw_items, (unsigned int)16) +/// +void RewriteObjC::SynthCountByEnumWithState(std::string &buf) { + buf += "((unsigned int (*) (id, SEL, struct __objcFastEnumerationState *, " + "id *, unsigned int))(void *)objc_msgSend)"; + buf += "\n\t\t"; + buf += "((id)l_collection,\n\t\t"; + buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; + buf += "\n\t\t"; + buf += "&enumState, " + "(id *)__rw_items, (unsigned int)16)"; +} + +/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach +/// statement to exit to its outer synthesized loop. +/// +Stmt *RewriteObjC::RewriteBreakStmt(BreakStmt *S) { + if (Stmts.empty() || !isa(Stmts.back())) + return S; + // replace break with goto __break_label + std::string buf; + + SourceLocation startLoc = S->getLocStart(); + buf = "goto __break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("break"), buf); + + return 0; +} + +/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach +/// statement to continue with its inner synthesized loop. +/// +Stmt *RewriteObjC::RewriteContinueStmt(ContinueStmt *S) { + if (Stmts.empty() || !isa(Stmts.back())) + return S; + // replace continue with goto __continue_label + std::string buf; + + SourceLocation startLoc = S->getLocStart(); + buf = "goto __continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("continue"), buf); + + return 0; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id __rw_items[16]; +/// id l_collection = (id)collection; +/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]; +/// if (limit) { +/// unsigned long startMutations = *enumState.mutationsPtr; +/// do { +/// unsigned long counter = 0; +/// do { +/// if (startMutations != *enumState.mutationsPtr) +/// objc_enumerationMutation(l_collection); +/// elem = (type)enumState.itemsPtr[counter++]; +/// stmts; +/// __continue_label: ; +/// } while (counter < limit); +/// } while (limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]); +/// elem = nil; +/// __break_label: ; +/// } +/// else +/// elem = nil; +/// } +/// +Stmt *RewriteObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd) { + assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); + assert(isa(Stmts.back()) && + "ObjCForCollectionStmt Statement stack mismatch"); + assert(!ObjCBcLabelNo.empty() && + "ObjCForCollectionStmt - Label No stack empty"); + + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + StringRef elementName; + std::string elementTypeAsString; + std::string buf; + buf = "\n{\n\t"; + if (DeclStmt *DS = dyn_cast(S->getElement())) { + // type elem; + NamedDecl* D = cast(DS->getSingleDecl()); + QualType ElementType = cast(D)->getType(); + if (ElementType->isObjCQualifiedIdType() || + ElementType->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); + buf += elementTypeAsString; + buf += " "; + elementName = D->getName(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast(S->getElement()); + elementName = DR->getDecl()->getName(); + ValueDecl *VD = cast(DR->getDecl()); + if (VD->getType()->isObjCQualifiedIdType() || + VD->getType()->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id __rw_items[16]; + buf += "id __rw_items[16];\n\t"; + // id l_collection = (id) + buf += "id l_collection = (id)"; + // Find start location of 'collection' the hard way! + const char *startCollectionBuf = startBuf; + startCollectionBuf += 3; // skip 'for' + startCollectionBuf = strchr(startCollectionBuf, '('); + startCollectionBuf++; // skip '(' + // find 'in' and skip it. + while (*startCollectionBuf != ' ' || + *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || + (*(startCollectionBuf+3) != ' ' && + *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) + startCollectionBuf++; + startCollectionBuf += 3; + + // Replace: "for (type element in" with string constructed thus far. + ReplaceText(startLoc, startCollectionBuf - startBuf, buf); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:__rw_items count:16]; + // which is synthesized into: + // unsigned int limit = + // ((unsigned int (*) + // (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) + // (void *)objc_msgSend)((id)l_collection, + // sel_registerName( + // "countByEnumeratingWithState:objects:count:"), + // (struct __objcFastEnumerationState *)&state, + // (id *)__rw_items, (unsigned int)16); + buf += "unsigned long limit =\n\t\t"; + SynthCountByEnumWithState(buf); + buf += ";\n\t"; + /// if (limit) { + /// unsigned long startMutations = *enumState.mutationsPtr; + /// do { + /// unsigned long counter = 0; + /// do { + /// if (startMutations != *enumState.mutationsPtr) + /// objc_enumerationMutation(l_collection); + /// elem = (type)enumState.itemsPtr[counter++]; + buf += "if (limit) {\n\t"; + buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; + buf += "do {\n\t\t"; + buf += "unsigned long counter = 0;\n\t\t"; + buf += "do {\n\t\t\t"; + buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; + buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; + buf += elementName; + buf += " = ("; + buf += elementTypeAsString; + buf += ")enumState.itemsPtr[counter++];"; + // Replace ')' in for '(' type elem in collection ')' with all of these. + ReplaceText(lparenLoc, 1, buf); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while (limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:__rw_items count:16]); + /// elem = nil; + /// __break_label: ; + /// } + /// else + /// elem = nil; + /// } + /// + buf = ";\n\t"; + buf += "__continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;"; + buf += "\n\t\t"; + buf += "} while (counter < limit);\n\t"; + buf += "} while (limit = "; + SynthCountByEnumWithState(buf); + buf += ");\n\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "}\n"; + + // Insert all these *after* the statement body. + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (isa(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); + InsertText(endBodyLoc, buf); + } else { + /* Need to treat single statements specially. For example: + * + * for (A *a in b) if (stuff()) break; + * for (A *a in b) xxxyy; + * + * The following code simply scans ahead to the semi to find the actual end. + */ + const char *stmtBuf = SM->getCharacterData(OrigEnd); + const char *semiBuf = strchr(stmtBuf, ';'); + assert(semiBuf && "Can't find ';'"); + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return 0; +} + +/// RewriteObjCSynchronizedStmt - +/// This routine rewrites @synchronized(expr) stmt; +/// into: +/// objc_sync_enter(expr); +/// @try stmt @finally { objc_sync_exit(expr); } +/// +Stmt *RewriteObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @synchronized location"); + + std::string buf; + buf = "objc_sync_enter((id)"; + const char *lparenBuf = startBuf; + while (*lparenBuf != '(') lparenBuf++; + ReplaceText(startLoc, lparenBuf-startBuf+1, buf); + // We can't use S->getSynchExpr()->getLocEnd() to find the end location, since + // the sync expression is typically a message expression that's already + // been rewritten! (which implies the SourceLocation's are invalid). + SourceLocation endLoc = S->getSynchBody()->getLocStart(); + const char *endBuf = SM->getCharacterData(endLoc); + while (*endBuf != ')') endBuf--; + SourceLocation rparenLoc = startLoc.getLocWithOffset(endBuf-startBuf); + buf = ");\n"; + // declare a new scope with two variables, _stack and _rethrow. + buf += "/* @try scope begin */ \n{ struct _objc_exception_data {\n"; + buf += "int buf[18/*32-bit i386*/];\n"; + buf += "char *pointers[4];} _stack;\n"; + buf += "id volatile _rethrow = 0;\n"; + buf += "objc_exception_try_enter(&_stack);\n"; + buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; + ReplaceText(rparenLoc, 1, buf); + startLoc = S->getSynchBody()->getLocEnd(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @synchronized block"); + SourceLocation lastCurlyLoc = startLoc; + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += "}\n"; + buf += "{ /* implicit finally clause */\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + + std::string syncBuf; + syncBuf += " objc_sync_exit("; + + Expr *syncExpr = S->getSynchExpr(); + CastKind CK = syncExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : + syncExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + syncExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, syncExpr); + std::string syncExprBufS; + llvm::raw_string_ostream syncExprBuf(syncExprBufS); + syncExpr->printPretty(syncExprBuf, 0, PrintingPolicy(LangOpts)); + syncBuf += syncExprBuf.str(); + syncBuf += ");"; + + buf += syncBuf; + buf += "\n if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}\n"; + buf += "}"; + + ReplaceText(lastCurlyLoc, 1, buf); + + bool hasReturns = false; + HasReturnStmts(S->getSynchBody(), hasReturns); + if (hasReturns) + RewriteSyncReturnStmts(S->getSynchBody(), syncBuf); + + return 0; +} + +void RewriteObjC::WarnAboutReturnGotoStmts(Stmt *S) +{ + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) + WarnAboutReturnGotoStmts(*CI); + + if (isa(S) || isa(S)) { + Diags.Report(Context->getFullLoc(S->getLocStart()), + TryFinallyContainsReturnDiag); + } + return; +} + +void RewriteObjC::HasReturnStmts(Stmt *S, bool &hasReturns) +{ + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) + HasReturnStmts(*CI, hasReturns); + + if (isa(S)) + hasReturns = true; + return; +} + +void RewriteObjC::RewriteTryReturnStmts(Stmt *S) { + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + RewriteTryReturnStmts(*CI); + } + if (isa(S)) { + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "RewriteTryReturnStmts: can't find ';'"); + SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + + std::string buf; + buf = "{ objc_exception_try_exit(&_stack); return"; + + ReplaceText(startLoc, 6, buf); + InsertText(onePastSemiLoc, "}"); + } + return; +} + +void RewriteObjC::RewriteSyncReturnStmts(Stmt *S, std::string syncExitBuf) { + // Perform a bottom up traversal of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + RewriteSyncReturnStmts(*CI, syncExitBuf); + } + if (isa(S)) { + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "RewriteSyncReturnStmts: can't find ';'"); + SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + + std::string buf; + buf = "{ objc_exception_try_exit(&_stack);"; + buf += syncExitBuf; + buf += " return"; + + ReplaceText(startLoc, 6, buf); + InsertText(onePastSemiLoc, "}"); + } + return; +} + +Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @try location"); + + std::string buf; + // declare a new scope with two variables, _stack and _rethrow. + buf = "/* @try scope begin */ { struct _objc_exception_data {\n"; + buf += "int buf[18/*32-bit i386*/];\n"; + buf += "char *pointers[4];} _stack;\n"; + buf += "id volatile _rethrow = 0;\n"; + buf += "objc_exception_try_enter(&_stack);\n"; + buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; + + ReplaceText(startLoc, 4, buf); + + startLoc = S->getTryBody()->getLocEnd(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @try block"); + + SourceLocation lastCurlyLoc = startLoc; + if (S->getNumCatchStmts()) { + startLoc = startLoc.getLocWithOffset(1); + buf = " /* @catch begin */ else {\n"; + buf += " id _caught = objc_exception_extract(&_stack);\n"; + buf += " objc_exception_try_enter (&_stack);\n"; + buf += " if (_setjmp(_stack.buf))\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += " else { /* @catch continue */"; + + InsertText(startLoc, buf); + } else { /* no catch list */ + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf); + } + Stmt *lastCatchBody = 0; + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + VarDecl *catchDecl = Catch->getCatchParamDecl(); + + if (I == 0) + buf = "if ("; // we are generating code for the first catch clause + else + buf = "else if ("; + startLoc = Catch->getLocStart(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @catch location"); + + const char *lParenLoc = strchr(startBuf, '('); + + if (Catch->hasEllipsis()) { + // Now rewrite the body... + lastCatchBody = Catch->getCatchBody(); + SourceLocation bodyLoc = lastCatchBody->getLocStart(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + assert(*SM->getCharacterData(Catch->getRParenLoc()) == ')' && + "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + buf += "1) { id _tmp = _caught;"; + Rewrite.ReplaceText(startLoc, bodyBuf-startBuf+1, buf); + } else if (catchDecl) { + QualType t = catchDecl->getType(); + if (t == Context->getObjCIdType()) { + buf += "1) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf); + } else if (const ObjCObjectPointerType *Ptr = + t->getAs()) { + // Should be a pointer to a class. + ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); + if (IDecl) { + buf += "objc_exception_match((struct objc_class *)objc_getClass(\""; + buf += IDecl->getNameAsString(); + buf += "\"), (struct objc_object *)_caught)) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf); + } + } + // Now rewrite the body... + lastCatchBody = Catch->getCatchBody(); + SourceLocation rParenLoc = Catch->getRParenLoc(); + SourceLocation bodyLoc = lastCatchBody->getLocStart(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + const char *rParenBuf = SM->getCharacterData(rParenLoc); + assert((*rParenBuf == ')') && "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + // Here we replace ") {" with "= _caught;" (which initializes and + // declares the @catch parameter). + ReplaceText(rParenLoc, bodyBuf-rParenBuf+1, " = _caught;"); + } else { + llvm_unreachable("@catch rewrite bug"); + } + } + // Complete the catch list... + if (lastCatchBody) { + SourceLocation bodyLoc = lastCatchBody->getLocEnd(); + assert(*SM->getCharacterData(bodyLoc) == '}' && + "bogus @catch body location"); + + // Insert the last (implicit) else clause *before* the right curly brace. + bodyLoc = bodyLoc.getLocWithOffset(-1); + buf = "} /* last catch end */\n"; + buf += "else {\n"; + buf += " _rethrow = _caught;\n"; + buf += " objc_exception_try_exit(&_stack);\n"; + buf += "} } /* @catch end */\n"; + if (!S->getFinallyStmt()) + buf += "}\n"; + InsertText(bodyLoc, buf); + + // Set lastCurlyLoc + lastCurlyLoc = lastCatchBody->getLocEnd(); + } + if (ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt()) { + startLoc = finalStmt->getLocStart(); + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @finally start"); + + ReplaceText(startLoc, 8, "/* @finally */"); + + Stmt *body = finalStmt->getFinallyBody(); + SourceLocation startLoc = body->getLocStart(); + SourceLocation endLoc = body->getLocEnd(); + assert(*SM->getCharacterData(startLoc) == '{' && + "bogus @finally body location"); + assert(*SM->getCharacterData(endLoc) == '}' && + "bogus @finally body location"); + + startLoc = startLoc.getLocWithOffset(1); + InsertText(startLoc, " if (!_rethrow) objc_exception_try_exit(&_stack);\n"); + endLoc = endLoc.getLocWithOffset(-1); + InsertText(endLoc, " if (_rethrow) objc_exception_throw(_rethrow);\n"); + + // Set lastCurlyLoc + lastCurlyLoc = body->getLocEnd(); + + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoStmts(S->getTryBody()); + } else { /* no finally clause - make sure we synthesize an implicit one */ + buf = "{ /* implicit finally clause */\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + buf += " if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf); + + // Now check for any return/continue/go statements within the @try. + // The implicit finally clause won't called if the @try contains any + // jump statements. + bool hasReturns = false; + HasReturnStmts(S->getTryBody(), hasReturns); + if (hasReturns) + RewriteTryReturnStmts(S->getTryBody()); + } + // Now emit the final closing curly brace... + lastCurlyLoc = lastCurlyLoc.getLocWithOffset(1); + InsertText(lastCurlyLoc, " } /* @try scope end */\n"); + return 0; +} + +// This can't be done with ReplaceStmt(S, ThrowExpr), since +// the throw expression is typically a message expression that's already +// been rewritten! (which implies the SourceLocation's are invalid). +Stmt *RewriteObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getLocStart(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @throw location"); + + std::string buf; + /* void objc_exception_throw(id) __attribute__((noreturn)); */ + if (S->getThrowExpr()) + buf = "objc_exception_throw("; + else // add an implicit argument + buf = "objc_exception_throw(_caught"; + + // handle "@ throw" correctly. + const char *wBuf = strchr(startBuf, 'w'); + assert((*wBuf == 'w') && "@throw: can't find 'w'"); + ReplaceText(startLoc, wBuf-startBuf+1, buf); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); + ReplaceText(semiLoc, 1, ");"); + return 0; +} + +Stmt *RewriteObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + QualType StrType = Context->getPointerType(Context->CharTy); + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = StringLiteral::Create(*Context, StrEncoding, + StringLiteral::Ascii, false, + StrType, SourceLocation()); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return Replacement; +} + +Stmt *RewriteObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); + // Create a call to sel_registerName("selName"). + SmallVector SelExprs; + QualType argType = Context->getPointerType(Context->CharTy); + SelExprs.push_back(StringLiteral::Create(*Context, + Exp->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size()); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return SelExp; +} + +CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl( + FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc, + SourceLocation EndLoc) { + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = FD->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, msgSendType, + VK_LValue, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, + DRE, 0, VK_RValue); + + const FunctionType *FT = msgSendType->getAs(); + + CallExpr *Exp = + new (Context) CallExpr(*Context, ICE, llvm::makeArrayRef(args, nargs), + FT->getCallResultType(*Context), + VK_RValue, EndLoc); + return Exp; +} + +static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, + const char *&startRef, const char *&endRef) { + while (startBuf < endBuf) { + if (*startBuf == '<') + startRef = startBuf; // mark the start. + if (*startBuf == '>') { + if (startRef && *startRef == '<') { + endRef = startBuf; // mark the end. + return true; + } + return false; + } + startBuf++; + } + return false; +} + +static void scanToNextArgument(const char *&argRef) { + int angle = 0; + while (*argRef != ')' && (*argRef != ',' || angle > 0)) { + if (*argRef == '<') + angle++; + else if (*argRef == '>') + angle--; + argRef++; + } + assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); +} + +bool RewriteObjC::needToScanForQualifiers(QualType T) { + if (T->isObjCQualifiedIdType()) + return true; + if (const PointerType *PT = T->getAs()) { + if (PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + if (T->isObjCObjectPointerType()) { + T = T->getPointeeType(); + return T->isObjCQualifiedInterfaceType(); + } + if (T->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(T); + return needToScanForQualifiers(ElemTy); + } + return false; +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { + QualType Type = E->getType(); + if (needToScanForQualifiers(Type)) { + SourceLocation Loc, EndLoc; + + if (const CStyleCastExpr *ECE = dyn_cast(E)) { + Loc = ECE->getLParenLoc(); + EndLoc = ECE->getRParenLoc(); + } else { + Loc = E->getLocStart(); + EndLoc = E->getLocEnd(); + } + // This will defend against trying to rewrite synthesized expressions. + if (Loc.isInvalid() || EndLoc.isInvalid()) + return; + + const char *startBuf = SM->getCharacterData(Loc); + const char *endBuf = SM->getCharacterData(EndLoc); + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = 0; + if (VarDecl *VD = dyn_cast(Dcl)) { + Loc = VD->getLocation(); + Type = VD->getType(); + } + else if (FunctionDecl *FD = dyn_cast(Dcl)) { + Loc = FD->getLocation(); + // Check for ObjC 'id' and class types that have been adorned with protocol + // information (id

, C

*). The protocol references need to be rewritten! + const FunctionType *funcType = FD->getType()->getAs(); + assert(funcType && "missing function type"); + proto = dyn_cast(funcType); + if (!proto) + return; + Type = proto->getResultType(); + } + else if (FieldDecl *FD = dyn_cast(Dcl)) { + Loc = FD->getLocation(); + Type = FD->getType(); + } + else + return; + + if (needToScanForQualifiers(Type)) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = SM->getCharacterData(Loc); + const char *startBuf = endBuf; + while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) + startBuf--; // scan backward (from the decl location) for return type. + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } + if (!proto) + return; // most likely, was a variable + // Now check arguments. + const char *startBuf = SM->getCharacterData(Loc); + const char *startFuncBuf = startBuf; + for (unsigned i = 0; i < proto->getNumArgs(); i++) { + if (needToScanForQualifiers(proto->getArgType(i))) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = startBuf; + // scan forward (from the decl location) for argument types. + scanToNextArgument(endBuf); + const char *startRef = 0, *endRef = 0; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + startBuf = ++endBuf; + } + else { + // If the function name is derived from a macro expansion, then the + // argument buffer will not follow the name. Need to speak with Chris. + while (*startBuf && *startBuf != ')' && *startBuf != ',') + startBuf++; // scan forward (from the decl location) for argument types. + startBuf++; + } + } +} + +void RewriteObjC::RewriteTypeOfDecl(VarDecl *ND) { + QualType QT = ND->getType(); + const Type* TypePtr = QT->getAs(); + if (!isa(TypePtr)) + return; + while (isa(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + TypePtr = QT->getAs(); + } + // FIXME. This will not work for multiple declarators; as in: + // __typeof__(a) b,c,d; + std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + const char *startBuf = SM->getCharacterData(DeclLoc); + if (ND->getInit()) { + std::string Name(ND->getNameAsString()); + TypeAsString += " " + Name + " = "; + Expr *E = ND->getInit(); + SourceLocation startLoc; + if (const CStyleCastExpr *ECE = dyn_cast(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + const char *endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } + else { + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + SmallVector ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getFuncType = + getSimpleFunctionType(Context->getObjCSelType(), &ArgTys[0], ArgTys.size()); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + SelGetUidIdent, getFuncType, 0, + SC_Extern, + SC_None, false); +} + +void RewriteObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in + if (FD->getIdentifier() && + FD->getName() == "sel_registerName") { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +void RewriteObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + if (!strchr(argPtr, '^')) { + Str += TypeString; + return; + } + while (*argPtr) { + Str += (*argPtr == '^' ? '*' : *argPtr); + argPtr++; + } +} + +// FIXME. Consolidate this routine with RewriteBlockPointerType. +void RewriteObjC::RewriteBlockPointerTypeVariable(std::string& Str, + ValueDecl *VD) { + QualType Type = VD->getType(); + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + Str += *argPtr; + paren++; + break; + case ')': + Str += *argPtr; + paren--; + break; + case '^': + Str += '*'; + if (paren == 1) + Str += VD->getNameAsString(); + break; + default: + Str += *argPtr; + break; + } + argPtr++; + } +} + + +void RewriteObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs(); + const FunctionProtoType *proto = dyn_cast(funcType); + if (!proto) + return; + QualType Type = proto->getResultType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getArgType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); + CurFunctionDeclToDeclareForBlock = 0; +} + +// SynthSuperContructorFunctionDecl - id objc_super(id obj, id super); +void RewriteObjC::SynthSuperContructorFunctionDecl() { + if (SuperContructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size()); + SuperContructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + SmallVector ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("objc_super")); + QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); + assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + SmallVector ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("objc_super")); + QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); + assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + SmallVector ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->DoubleTy, + &ArgTys[0], ArgTys.size(), + true /*isVariadic*/); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, 0, + SC_Extern, + SC_None, false); +} + +// SynthGetClassFunctionDecl - id objc_getClass(const char *name); +void RewriteObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + SmallVector ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size()); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, 0, + SC_Extern, + SC_None, false); +} + +// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); +void RewriteObjC::SynthGetSuperClassFunctionDecl() { + IdentifierInfo *getSuperClassIdent = + &Context->Idents.get("class_getSuperclass"); + SmallVector ArgTys; + ArgTys.push_back(Context->getObjCClassType()); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + &ArgTys[0], ArgTys.size()); + GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getSuperClassIdent, + getClassType, 0, + SC_Extern, + SC_None, + false); +} + +// SynthGetMetaClassFunctionDecl - id objc_getMetaClass(const char *name); +void RewriteObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + SmallVector ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + &ArgTys[0], ArgTys.size()); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, 0, + SC_Extern, + SC_None, false); +} + +Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + QualType strType = getConstantStringStructType(); + + std::string S = "__NSConstantStringImpl_"; + + std::string tmpName = InFileName; + unsigned i; + for (i=0; i < tmpName.length(); i++) { + char c = tmpName.at(i); + // replace any non alphanumeric characters with '_'. + if (!isalpha(c) && (c < '0' || c > '9')) + tmpName[i] = '_'; + } + S += tmpName; + S += "_"; + S += utostr(NumObjCStringLiterals++); + + Preamble += "static __NSConstantStringImpl " + S; + Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; + Preamble += "0x000007c8,"; // utf8_str + // The pretty printer for StringLiteral handles escape characters properly. + std::string prettyBufS; + llvm::raw_string_ostream prettyBuf(prettyBufS); + Exp->getString()->printPretty(prettyBuf, 0, PrintingPolicy(LangOpts)); + Preamble += prettyBuf.str(); + Preamble += ","; + Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(S), + strType, 0, SC_Static, SC_None); + DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, strType, VK_LValue, + SourceLocation()); + Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + // cast to NSConstantString * + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), + CK_CPointerToObjCPointerCast, Unop); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return cast; +} + +// struct objc_super { struct objc_object *receiver; struct objc_class *super; }; +QualType RewriteObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("objc_super")); + QualType FieldTypes[2]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // struct objc_class *super; + FieldTypes[1] = Context->getObjCClassType(); + + // Create fields + for (unsigned i = 0; i < 2; ++i) { + SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, + SourceLocation(), + SourceLocation(), 0, + FieldTypes[i], 0, + /*BitWidth=*/0, + /*Mutable=*/false, + ICIS_NoInit)); + } + + SuperStructDecl->completeDefinition(); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__NSConstantStringImpl")); + QualType FieldTypes[4]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // int flags; + FieldTypes[1] = Context->IntTy; + // char *str; + FieldTypes[2] = Context->getPointerType(Context->CharTy); + // long length; + FieldTypes[3] = Context->LongTy; + + // Create fields + for (unsigned i = 0; i < 4; ++i) { + ConstantStringDecl->addDecl(FieldDecl::Create(*Context, + ConstantStringDecl, + SourceLocation(), + SourceLocation(), 0, + FieldTypes[i], 0, + /*BitWidth=*/0, + /*Mutable=*/true, + ICIS_NoInit)); + } + + ConstantStringDecl->completeDefinition(); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +CallExpr *RewriteObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl &ArgTypes, + SmallVectorImpl &MsgExprs, + ObjCMethodDecl *Method) { + // Create a reference to the objc_msgSend_stret() declaration. + DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, + false, msgSendType, + VK_LValue, SourceLocation()); + // Need to cast objc_msgSend_stret to "void *" (see above comment). + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, STDRE); + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + Method ? Method->isVariadic() : false); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); + + const FunctionType *FT = msgSendType->getAs(); + CallExpr *STCE = new (Context) CallExpr(*Context, PE, MsgExprs, + FT->getResultType(), VK_RValue, + SourceLocation()); + return STCE; + +} + + +Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetSuperClassFunctionDecl) + SynthGetSuperClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = 0; + if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { + QualType resultType = mDecl->getResultType(); + if (resultType->isRecordType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + SmallVector MsgExprs; + switch (Exp->getReceiverKind()) { + case ObjCMessageExpr::SuperClass: { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + + SmallVector InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, + SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + ClassDecl->getIdentifier()->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, + EndLoc); + // (Class)objc_getClass("CurrentClass") + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCClassType(), + CK_BitCast, Cls); + ClsExprs.clear(); + ClsExprs.push_back(ArgExpr); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperContructorFunctionDecl(); + // Simulate a contructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, + SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct objc_super) { } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_LValue, + ILE, false); + // struct objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->getAs()->getInterface(); + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(StringLiteral::Create(*Context, + clsName->getName(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + break; + } + + case ObjCMessageExpr::SuperInstance:{ + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + SmallVector InitExprs; + + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector ClsExprs; + QualType argType = Context->getPointerType(Context->CharTy); + ClsExprs.push_back(StringLiteral::Create(*Context, + ClassDecl->getIdentifier()->getName(), + StringLiteral::Ascii, false, argType, + SourceLocation())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, + &ClsExprs[0], + ClsExprs.size(), + StartLoc, EndLoc); + // (Class)objc_getClass("CurrentClass") + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCClassType(), + CK_BitCast, Cls); + ClsExprs.clear(); + ClsExprs.push_back(ArgExpr); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, + &ClsExprs[0], ClsExprs.size(), + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperContructorFunctionDecl(); + // Simulate a contructor call... + DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, + false, superType, VK_LValue, + SourceLocation()); + SuperRep = new (Context) CallExpr(*Context, DRE, InitExprs, + superType, VK_LValue, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation()); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct objc_super) { } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_RValue, ILE, + false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Instance: { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo *. + Expr *recExpr = Exp->getInstanceReceiver(); + while (CStyleCastExpr *CE = dyn_cast(recExpr)) + recExpr = CE->getSubExpr(); + CastKind CK = recExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : recExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + + recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, recExpr); + MsgExprs.push_back(recExpr); + break; + } + } + + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + SmallVector SelExprs; + QualType argType = Context->getPointerType(Context->CharTy); + SelExprs.push_back(StringLiteral::Create(*Context, + Exp->getSelector().getAsString(), + StringLiteral::Ascii, false, + argType, SourceLocation())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + &SelExprs[0], SelExprs.size(), + StartLoc, + EndLoc); + MsgExprs.push_back(SelExp); + + // Now push any user supplied arguments. + for (unsigned i = 0; i < Exp->getNumArgs(); i++) { + Expr *userExpr = Exp->getArg(i); + // Make all implicit casts explicit...ICE comes in handy:-) + if (ImplicitCastExpr *ICE = dyn_cast(userExpr)) { + // Reuse the ICE type, it is exactly what the doctor ordered. + QualType type = ICE->getType(); + if (needToScanForQualifiers(type)) + type = Context->getObjCIdType(); + // Make sure we convert "type (^)(...)" to "type (*)(...)". + (void)convertBlockPointerToFunctionPointer(type); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK; + if (SubExpr->getType()->isIntegralType(*Context) && + type->isBooleanType()) { + CK = CK_IntegralToBoolean; + } else if (type->isObjCObjectPointerType()) { + if (SubExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (SubExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + } else { + CK = CK_BitCast; + } + + userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + } + // Make id cast into an 'id' cast. + else if (CStyleCastExpr *CE = dyn_cast(userExpr)) { + if (CE->getType()->isObjCQualifiedIdType()) { + while ((CE = dyn_cast(userExpr))) + userExpr = CE->getSubExpr(); + CastKind CK; + if (userExpr->getType()->isIntegralType(*Context)) { + CK = CK_IntegralToPointer; + } else if (userExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (userExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, userExpr); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. For now, we *don't* null + // out the argument in the original expression (since we aren't deleting + // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. + //Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + SmallVector ArgTypes; + QualType returnType; + + // Push 'id' and 'SEL', the 2 implicit arguments. + if (MsgSendFlavor == MsgSendSuperFunctionDecl) + ArgTypes.push_back(Context->getPointerType(getSuperStructType())); + else + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { + // Push any user argument types. + for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), + E = OMD->param_end(); PI != E; ++PI) { + QualType t = (*PI)->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : (*PI)->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(t); + ArgTypes.push_back(t); + } + returnType = Exp->getType(); + convertToUnqualifiedObjCType(returnType); + (void)convertBlockPointerToFunctionPointer(returnType); + } else { + returnType = Context->getObjCIdType(); + } + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, + VK_LValue, SourceLocation()); + + // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). + // If we don't do this cast, we get the following bizarre warning/note: + // xx.m:13: warning: function called through a non-compatible type + // xx.m:13: note: if this code is reached, the program will abort + cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), + // If we don't have a method decl, force a variadic cast. + Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : true); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs(); + CallExpr *CE = new (Context) CallExpr(*Context, PE, MsgExprs, + FT->getResultType(), VK_RValue, + EndLoc); + Stmt *ReplacingStmt = CE; + if (MsgSendStretFlavor) { + // We have the method which returns a struct/union. Must also generate + // call to objc_msgSend_stret and hang both varieties on a conditional + // expression which dictate which one to envoke depending on size of + // method's return type. + + CallExpr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + msgSendType, returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); + + // Build sizeof(returnType) + UnaryExprOrTypeTraitExpr *sizeofExpr = + new (Context) UnaryExprOrTypeTraitExpr(UETT_SizeOf, + Context->getTrivialTypeSourceInfo(returnType), + Context->getSizeType(), SourceLocation(), + SourceLocation()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + // FIXME: Value of 8 is base on ppc32/x86 ABI for the most common cases. + // For X86 it is more complicated and some kind of target specific routine + // is needed to decide what to do. + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + IntegerLiteral *limit = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, 8), + Context->IntTy, + SourceLocation()); + BinaryOperator *lessThanExpr = + new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, + VK_RValue, OK_Ordinary, SourceLocation(), + false); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(lessThanExpr, + SourceLocation(), CE, + SourceLocation(), STCE, + returnType, VK_RValue, OK_Ordinary); + ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + CondExpr); + } + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +Stmt *RewriteObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = SynthMessageExpr(Exp, Exp->getLocStart(), + Exp->getLocEnd()); + + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +// typedef struct objc_object Protocol; +QualType RewriteObjC::getProtocolType() { + if (!ProtocolTypeDecl) { + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); + ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("Protocol"), + TInfo); + } + return Context->getTypeDeclType(ProtocolTypeDecl); +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// a synthesized/forward data reference (to the protocol's metadata). +/// The forward references (and metadata) are generated in +/// RewriteObjC::HandleTranslationUnit(). +Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + std::string Name = "_OBJC_PROTOCOL_" + Exp->getProtocol()->getNameAsString(); + IdentifierInfo *ID = &Context->Idents.get(Name); + VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, getProtocolType(), 0, + SC_Extern, SC_None); + DeclRefExpr *DRE = new (Context) DeclRefExpr(VD, false, getProtocolType(), + VK_LValue, SourceLocation()); + Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), + CK_BitCast, + DerefExpr); + ReplaceStmt(Exp, castExpr); + ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return castExpr; + +} + +bool RewriteObjC::BufferContainsPPDirectives(const char *startBuf, + const char *endBuf) { + while (startBuf < endBuf) { + if (*startBuf == '#') { + // Skip whitespace. + for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf) + ; + if (!strncmp(startBuf, "if", strlen("if")) || + !strncmp(startBuf, "ifdef", strlen("ifdef")) || + !strncmp(startBuf, "ifndef", strlen("ifndef")) || + !strncmp(startBuf, "define", strlen("define")) || + !strncmp(startBuf, "undef", strlen("undef")) || + !strncmp(startBuf, "else", strlen("else")) || + !strncmp(startBuf, "elif", strlen("elif")) || + !strncmp(startBuf, "endif", strlen("endif")) || + !strncmp(startBuf, "pragma", strlen("pragma")) || + !strncmp(startBuf, "include", strlen("include")) || + !strncmp(startBuf, "import", strlen("import")) || + !strncmp(startBuf, "include_next", strlen("include_next"))) + return true; + } + startBuf++; + } + return false; +} + +/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getName() != "" && + "Name missing in SynthesizeObjCInternalStruct"); + // Do not synthesize more than once. + if (ObjCSynthesizedStructs.count(CDecl)) + return; + ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); + int NumIvars = CDecl->ivar_size(); + SourceLocation LocStart = CDecl->getLocStart(); + SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // If no ivars and no root or if its root, directly or indirectly, + // have no ivars (thus not synthesized) then no need to synthesize this class. + if ((!CDecl->isThisDeclarationADefinition() || NumIvars == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + return; + } + + // FIXME: This has potential of causing problem. If + // SynthesizeObjCInternalStruct is ever called recursively. + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + + if (NumIvars > 0) { + const char *cursor = strchr(startBuf, '{'); + assert((cursor && endBuf) + && "SynthesizeObjCInternalStruct - malformed @interface"); + // If the buffer contains preprocessor directives, we do more fine-grained + // rewrites. This is intended to fix code that looks like (which occurs in + // NSURL.h, for example): + // + // #ifdef XYZ + // @interface Foo : NSObject + // #else + // @interface FooBar : NSObject + // #endif + // { + // int i; + // } + // @end + // + // This clause is segregated to avoid breaking the common case. + if (BufferContainsPPDirectives(startBuf, cursor)) { + SourceLocation L = RCDecl ? CDecl->getSuperClassLoc() : + CDecl->getAtStartLoc(); + const char *endHeader = SM->getCharacterData(L); + endHeader += Lexer::MeasureTokenLength(L, *SM, LangOpts); + + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + // advance to the end of the referenced protocols. + while (endHeader < cursor && *endHeader != '>') endHeader++; + endHeader++; + } + // rewrite the original header + ReplaceText(LocStart, endHeader-startBuf, Result); + } else { + // rewrite the original header *without* disturbing the '{' + ReplaceText(LocStart, cursor-startBuf, Result); + } + if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { + Result = "\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n"; + + // insert the super class structure definition. + SourceLocation OnePastCurly = + LocStart.getLocWithOffset(cursor-startBuf+1); + InsertText(OnePastCurly, Result); + } + cursor++; // past '{' + + // Now comment out any visibility specifiers. + while (cursor < endBuf) { + if (*cursor == '@') { + SourceLocation atLoc = LocStart.getLocWithOffset(cursor-startBuf); + // Skip whitespace. + for (++cursor; cursor[0] == ' ' || cursor[0] == '\t'; ++cursor) + /*scan*/; + + // FIXME: presence of @public, etc. inside comment results in + // this transformation as well, which is still correct c-code. + if (!strncmp(cursor, "public", strlen("public")) || + !strncmp(cursor, "private", strlen("private")) || + !strncmp(cursor, "package", strlen("package")) || + !strncmp(cursor, "protected", strlen("protected"))) + InsertText(atLoc, "// "); + } + // FIXME: If there are cases where '<' is used in ivar declaration part + // of user code, then scan the ivar list and use needToScanForQualifiers + // for type checking. + else if (*cursor == '<') { + SourceLocation atLoc = LocStart.getLocWithOffset(cursor-startBuf); + InsertText(atLoc, "/* "); + cursor = strchr(cursor, '>'); + cursor++; + atLoc = LocStart.getLocWithOffset(cursor-startBuf); + InsertText(atLoc, " */"); + } else if (*cursor == '^') { // rewrite block specifier. + SourceLocation caretLoc = LocStart.getLocWithOffset(cursor-startBuf); + ReplaceText(caretLoc, 1, "*"); + } + cursor++; + } + // Don't forget to add a ';'!! + InsertText(LocEnd.getLocWithOffset(1), ";"); + } else { // we don't have any instance variables - insert super struct. + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + Result += " {\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n};\n"; + ReplaceText(LocStart, endBuf-startBuf, Result); + } + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl)) + llvm_unreachable("struct already synthesize- SynthesizeObjCInternalStruct"); +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + + +/// RewriteImplementations - This routine rewrites all method implementations +/// and emits meta-data. + +void RewriteObjC::RewriteImplementations() { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // Rewrite implemented methods + for (int i = 0; i < ClsDefCount; i++) + RewriteImplementationDecl(ClassImplementation[i]); + + for (int i = 0; i < CatDefCount; i++) + RewriteImplementationDecl(CategoryImplementation[i]); +} + +void RewriteObjC::RewriteByRefString(std::string &ResultStr, + const std::string &Name, + ValueDecl *VD, bool def) { + assert(BlockByRefDeclNo.count(VD) && + "RewriteByRefString: ByRef decl missing"); + if (def) + ResultStr += "struct "; + ResultStr += "__Block_byref_" + Name + + "_" + utostr(BlockByRefDeclNo[VD]) ; +} + +static bool HasLocalVariableExternalStorage(ValueDecl *VD) { + if (VarDecl *Var = dyn_cast(VD)) + return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); + return false; +} + +std::string RewriteObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getResultType(); + std::string StructRef = "struct " + Tag; + std::string S = "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + + funcName.str() + "_" + "block_func_" + utostr(i); + + BlockDecl *BD = CE->getBlockDecl(); + + if (isa(AFT)) { + // No user-supplied arguments. Still need to pass in a pointer to the + // block (to reference imported block decl refs). + S += "(" + StructRef + " *__cself)"; + } else if (BD->param_empty()) { + S += "(" + StructRef + " *__cself)"; + } else { + const FunctionProtoType *FT = cast(AFT); + assert(FT && "SynthesizeBlockFunc: No function proto"); + S += '('; + // first add the implicit argument. + S += StructRef + " *__cself, "; + std::string ParamStr; + for (BlockDecl::param_iterator AI = BD->param_begin(), + E = BD->param_end(); AI != E; ++AI) { + if (AI != BD->param_begin()) S += ", "; + ParamStr = (*AI)->getNameAsString(); + QualType QT = (*AI)->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); + S += ParamStr; + } + if (FT->isVariadic()) { + if (!BD->param_empty()) S += ", "; + S += "..."; + } + S += ')'; + } + S += " {\n"; + + // Create local declarations to avoid rewriting all closure decl ref exprs. + // First, emit a declaration for all "by ref" decls. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + std::string TypeString; + RewriteByRefString(TypeString, Name, (*I)); + TypeString += " *"; + Name = TypeString + Name; + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + // Handle nested closure invocation. For example: + // + // void (^myImportedClosure)(void); + // myImportedClosure = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherClosure)(void); + // anotherClosure = ^(void) { + // myImportedClosure(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + RewriteBlockPointerTypeVariable(S, (*I)); + S += " = ("; + RewriteBlockPointerType(S, (*I)->getType()); + S += ")"; + S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + else { + std::string Name = (*I)->getNameAsString(); + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + S += Name + " = __cself->" + + (*I)->getNameAsString() + "; // bound by copy\n"; + } + } + std::string RewrittenStr = RewrittenBlockExprs[CE]; + const char *cstr = RewrittenStr.c_str(); + while (*cstr++ != '{') ; + S += cstr; + S += "\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + std::string StructRef = "struct " + Tag; + std::string S = "static void __"; + + S += funcName; + S += "_block_copy_" + utostr(i); + S += "(" + StructRef; + S += "*dst, " + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + ValueDecl *VD = (*I); + S += "_Block_object_assign((void*)&dst->"; + S += (*I)->getNameAsString(); + S += ", (void*)src->"; + S += (*I)->getNameAsString(); + if (BlockByRefDeclsPtrSet.count((*I))) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), + E = ImportedBlockDecls.end(); I != E; ++I) { + ValueDecl *VD = (*I); + S += "_Block_object_dispose((void*)src->"; + S += (*I)->getNameAsString(); + if (BlockByRefDeclsPtrSet.count((*I))) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + std::string Desc) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + S += " struct " + Desc; + S += "* Desc;\n"; + + Constructor += "(void *fp, "; // Invoke function pointer. + Constructor += "struct " + Desc; // Descriptor pointer. + Constructor += " *desc"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); + QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + { + std::string TypeString; + RewriteByRefString(TypeString, FieldName, (*I)); + TypeString += " *"; + FieldName = TypeString + FieldName; + ArgName = TypeString + ArgName; + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + Constructor += ", int flags=0)"; + // Initialize all "by copy" arguments. + bool firsTime = true; + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + "((struct __block_impl *)_" + Name + ")"; + else + Constructor += Name + "(_" + Name + ")"; + } + // Initialize all "by ref" arguments. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + Constructor += Name + "(_" + Name + "->__forwarding)"; + } + + Constructor += " {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + Constructor += " Desc = desc;\n"; + } else { + // Finish writing the constructor. + Constructor += ", int flags=0) {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + Constructor += " Desc = desc;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, int i, + StringRef FunName, + unsigned hasCopy) { + std::string S = "\nstatic struct " + DescTag; + + S += " {\n unsigned long reserved;\n"; + S += " unsigned long Block_size;\n"; + if (hasCopy) { + S += " void (*copy)(struct "; + S += ImplTag; S += "*, struct "; + S += ImplTag; S += "*);\n"; + + S += " void (*dispose)(struct "; + S += ImplTag; S += "*);\n"; + } + S += "} "; + + S += DescTag + "_DATA = { 0, sizeof(struct "; + S += ImplTag + ")"; + if (hasCopy) { + S += ", __" + FunName.str() + "_block_copy_" + utostr(i); + S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); + } + S += "};\n"; + return S; +} + +void RewriteObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName) { + // Insert declaration for the function in which block literal is used. + if (CurFunctionDeclToDeclareForBlock && !Blocks.empty()) + RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); + bool RewriteSC = (GlobalVarDecl && + !Blocks.empty() && + GlobalVarDecl->getStorageClass() == SC_Static && + GlobalVarDecl->getType().getCVRQualifiers()); + if (RewriteSC) { + std::string SC(" void __"); + SC += GlobalVarDecl->getNameAsString(); + SC += "() {}"; + InsertText(FunLocStart, SC); + } + + // Insert closures that were part of the function. + for (unsigned i = 0, count=0; i < Blocks.size(); i++) { + CollectBlockDeclRefInfo(Blocks[i]); + // Need to copy-in the inner copied-in variables not actually used in this + // block. + for (int j = 0; j < InnerDeclRefsCount[i]; j++) { + DeclRefExpr *Exp = InnerDeclRefs[count++]; + ValueDecl *VD = Exp->getDecl(); + BlockDeclRefs.push_back(Exp); + if (!VD->hasAttr() && !BlockByCopyDeclsPtrSet.count(VD)) { + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr() && !BlockByRefDeclsPtrSet.count(VD)) { + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + // imported objects in the inner blocks not used in the outer + // blocks must be copied/disposed in the outer block as well. + if (VD->hasAttr() || + VD->getType()->isObjCObjectPointerType() || + VD->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(VD); + } + + std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); + std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); + + InsertText(FunLocStart, CI); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); + + InsertText(FunLocStart, CF); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); + InsertText(FunLocStart, HF); + } + std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, + ImportedBlockDecls.size() > 0); + InsertText(FunLocStart, BD); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + } + if (RewriteSC) { + // Must insert any 'const/volatile/static here. Since it has been + // removed as result of rewriting of block literals. + std::string SC; + if (GlobalVarDecl->getStorageClass() == SC_Static) + SC = "static "; + if (GlobalVarDecl->getType().isConstQualified()) + SC += "const "; + if (GlobalVarDecl->getType().isVolatileQualified()) + SC += "volatile "; + if (GlobalVarDecl->getType().isRestrictQualified()) + SC += "restrict "; + InsertText(FunLocStart, SC); + } + + Blocks.clear(); + InnerDeclRefsCount.clear(); + InnerDeclRefs.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + StringRef FuncName = FD->getName(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +static void BuildUniqueMethodName(std::string &Name, + ObjCMethodDecl *MD) { + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + Name = IFace->getName(); + Name += "__" + MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = Name.find(":", loc)) != std::string::npos) + Name.replace(loc, 1, "_"); +} + +void RewriteObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + //SourceLocation FunLocStart = MD->getLocStart(); + SourceLocation FunLocStart = MD->getLocStart(); + std::string FuncName; + BuildUniqueMethodName(FuncName, MD); + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast(*CI)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(*CI); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast(S)) { + if (DRE->refersToEnclosingLocal()) { + // FIXME: Handle enums. + if (!isa(DRE->getDecl())) + BlockDeclRefs.push_back(DRE); + if (HasLocalVariableExternalStorage(DRE->getDecl())) + BlockDeclRefs.push_back(DRE); + } + } + + return; +} + +void RewriteObjC::GetInnerBlockDeclRefExprs(Stmt *S, + SmallVector &InnerBlockDeclRefs, + llvm::SmallPtrSet &InnerContexts) { + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + if (BlockExpr *CBE = dyn_cast(*CI)) { + InnerContexts.insert(cast(CBE->getBlockDecl())); + GetInnerBlockDeclRefExprs(CBE->getBody(), + InnerBlockDeclRefs, + InnerContexts); + } + else + GetInnerBlockDeclRefExprs(*CI, + InnerBlockDeclRefs, + InnerContexts); + + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast(S)) { + if (DRE->refersToEnclosingLocal()) { + if (!isa(DRE->getDecl()) && + !InnerContexts.count(DRE->getDecl()->getDeclContext())) + InnerBlockDeclRefs.push_back(DRE); + if (VarDecl *Var = dyn_cast(DRE->getDecl())) + if (Var->isFunctionOrMethodVarDecl()) + ImportedLocalExternalDecls.insert(Var); + } + } + + return; +} + +/// convertFunctionTypeOfBlocks - This routine converts a function type +/// whose result type may be a block pointer or whose argument type(s) +/// might be block pointers to an equivalent function type replacing +/// all block pointers to function pointers. +QualType RewriteObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { + const FunctionProtoType *FTP = dyn_cast(FT); + // FTP will be null for closures that don't take arguments. + // Generate a funky cast. + SmallVector ArgTypes; + QualType Res = FT->getResultType(); + bool HasBlockType = convertBlockPointerToFunctionPointer(Res); + + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I && (I != E); ++I) { + QualType t = *I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (convertBlockPointerToFunctionPointer(t)) + HasBlockType = true; + ArgTypes.push_back(t); + } + } + QualType FuncType; + // FIXME. Does this work if block takes no argument but has a return type + // which is of block type? + if (HasBlockType) + FuncType = getSimpleFunctionType(Res, &ArgTypes[0], ArgTypes.size()); + else FuncType = QualType(FT, 0); + return FuncType; +} + +Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { + // Navigate to relevant type information. + const BlockPointerType *CPT = 0; + + if (const DeclRefExpr *DRE = dyn_cast(BlockExp)) { + CPT = DRE->getType()->getAs(); + } else if (const MemberExpr *MExpr = dyn_cast(BlockExp)) { + CPT = MExpr->getType()->getAs(); + } + else if (const ParenExpr *PRE = dyn_cast(BlockExp)) { + return SynthesizeBlockCall(Exp, PRE->getSubExpr()); + } + else if (const ImplicitCastExpr *IEXPR = dyn_cast(BlockExp)) + CPT = IEXPR->getType()->getAs(); + else if (const ConditionalOperator *CEXPR = + dyn_cast(BlockExp)) { + Expr *LHSExp = CEXPR->getLHS(); + Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); + Expr *RHSExp = CEXPR->getRHS(); + Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); + Expr *CONDExp = CEXPR->getCond(); + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(CONDExp, + SourceLocation(), cast(LHSStmt), + SourceLocation(), cast(RHSStmt), + Exp->getType(), VK_RValue, OK_Ordinary); + return CondExpr; + } else if (const ObjCIvarRefExpr *IRE = dyn_cast(BlockExp)) { + CPT = IRE->getType()->getAs(); + } else if (const PseudoObjectExpr *POE + = dyn_cast(BlockExp)) { + CPT = POE->getType()->castAs(); + } else { + assert(1 && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAs(); + assert(FT && "RewriteBlockClass: Bad type"); + const FunctionProtoType *FTP = dyn_cast(FT); + // FTP will be null for closures that don't take arguments. + + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + SmallVector ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I && (I != E); ++I) { + QualType t = *I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (!convertBlockPointerToFunctionPointer(t)) + convertToUnqualifiedObjCType(t); + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType + = getSimpleFunctionType(Exp->getType(), &ArgTypes[0], ArgTypes.size()); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, + CK_BitCast, + const_cast(BlockExp)); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + BlkCast); + //PE->dump(); + + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("FuncPtr"), + Context->VoidPtrTy, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + + CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, + CK_BitCast, ME); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + SmallVector BlkExprs; + // Add the implicit argument. + BlkExprs.push_back(BlkCast); + // Add the user arguments. + for (CallExpr::arg_iterator I = Exp->arg_begin(), + E = Exp->arg_end(); I != E; ++I) { + BlkExprs.push_back(*I); + } + CallExpr *CE = new (Context) CallExpr(*Context, PE, BlkExprs, + Exp->getType(), VK_RValue, + SourceLocation()); + return CE; +} + +// We need to return the rewritten expression to handle cases where the +// BlockDeclRefExpr is embedded in another expression being rewritten. +// For example: +// +// int main() { +// __block Foo *f; +// __block int i; +// +// void (^myblock)() = ^() { +// [f test]; // f is a BlockDeclRefExpr embedded in a message (which is being rewritten). +// i = 77; +// }; +//} +Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { + // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR + // for each DeclRefExp where BYREFVAR is name of the variable. + ValueDecl *VD = DeclRefExp->getDecl(); + bool isArrow = DeclRefExp->refersToEnclosingLocal(); + + FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), + SourceLocation(), + &Context->Idents.get("__forwarding"), + Context->VoidPtrTy, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, + FD, SourceLocation(), + FD->getType(), VK_LValue, + OK_Ordinary); + + StringRef Name = VD->getName(); + FD = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), + &Context->Idents.get(Name), + Context->VoidPtrTy, 0, + /*BitWidth=*/0, /*Mutable=*/true, + ICIS_NoInit); + ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), + DeclRefExp->getType(), VK_LValue, OK_Ordinary); + + + + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), + DeclRefExp->getExprLoc(), + ME); + ReplaceStmt(DeclRefExp, PE); + return PE; +} + +// Rewrites the imported local variable V with external storage +// (static, extern, etc.) as *V +// +Stmt *RewriteObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { + ValueDecl *VD = DRE->getDecl(); + if (VarDecl *Var = dyn_cast(VD)) + if (!ImportedLocalExternalDecls.count(Var)) + return DRE; + Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, + DRE->getLocation()); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Exp); + ReplaceStmt(DRE, PE); + return PE; +} + +void RewriteObjC::RewriteCastExpr(CStyleCastExpr *CE) { + SourceLocation LocStart = CE->getLParenLoc(); + SourceLocation LocEnd = CE->getRParenLoc(); + + // Need to avoid trying to rewrite synthesized casts. + if (LocStart.isInvalid()) + return; + // Need to avoid trying to rewrite casts contained in macros. + if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) + return; + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + QualType QT = CE->getType(); + const Type* TypePtr = QT->getAs(); + if (isa(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + std::string TypeAsString = "("; + RewriteBlockPointerType(TypeAsString, QT); + TypeAsString += ")"; + ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); + return; + } + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*"); + break; + } + } + return; +} + +void RewriteObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { + SourceLocation DeclLoc = FD->getLocation(); + unsigned parenCount = 0; + + // We have 1 or more arguments that have closure pointers. + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *startArgList = strchr(startBuf, '('); + + assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); + + parenCount++; + // advance the location to startArgList. + DeclLoc = DeclLoc.getLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*"); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } + return; +} + +bool RewriteObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs(); + if (PT) { + FTP = PT->getPointeeType()->getAs(); + } else { + const BlockPointerType *BPT = QT->getAs(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs(); + } + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I != E; ++I) + if (isTopLevelBlockPointerType(*I)) + return true; + } + return false; +} + +bool RewriteObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs(); + if (PT) { + FTP = PT->getPointeeType()->getAs(); + } else { + const BlockPointerType *BPT = QT->getAs(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs(); + } + if (FTP) { + for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), + E = FTP->arg_type_end(); I != E; ++I) { + if ((*I)->isObjCQualifiedIdType()) + return true; + if ((*I)->isObjCObjectPointerType() && + (*I)->getPointeeType()->isObjCQualifiedInterfaceType()) + return true; + } + + } + return false; +} + +void RewriteObjC::GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen) { + const char *argPtr = strchr(Name, '('); + assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); + + LParen = argPtr; // output the start. + argPtr++; // skip past the left paren. + unsigned parenCount = 1; + + while (*argPtr && parenCount) { + switch (*argPtr) { + case '(': parenCount++; break; + case ')': parenCount--; break; + default: break; + } + if (parenCount) argPtr++; + } + assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); + RParen = argPtr; // output the end +} + +void RewriteObjC::RewriteBlockPointerDecl(NamedDecl *ND) { + if (FunctionDecl *FD = dyn_cast(ND)) { + RewriteBlockPointerFunctionArgs(FD); + return; + } + // Handle Variables and Typedefs. + SourceLocation DeclLoc = ND->getLocation(); + QualType DeclT; + if (VarDecl *VD = dyn_cast(ND)) + DeclT = VD->getType(); + else if (TypedefNameDecl *TDD = dyn_cast(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast(ND)) + DeclT = FD->getType(); + else + llvm_unreachable("RewriteBlockPointerDecl(): Decl type not yet handled"); + + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *endBuf = startBuf; + // scan backward (from the decl location) for the end of the previous decl. + while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) + startBuf--; + SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); + std::string buf; + unsigned OrigLength=0; + // *startBuf != '^' if we are dealing with a pointer to function that + // may take block argument types (which will be handled below). + if (*startBuf == '^') { + // Replace the '^' with '*', computing a negative offset. + buf = '*'; + startBuf++; + OrigLength++; + } + while (*startBuf != ')') { + buf += *startBuf; + startBuf++; + OrigLength++; + } + buf += ')'; + OrigLength++; + + if (PointerTypeTakesAnyBlockArguments(DeclT) || + PointerTypeTakesAnyObjCQualifiedType(DeclT)) { + // Replace the '^' with '*' for arguments. + // Replace id

with id/*<>*/ + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') + buf += '*'; + else if (*argListBegin == '<') { + buf += "/*"; + buf += *argListBegin++; + OrigLength++; + while (*argListBegin != '>') { + buf += *argListBegin++; + OrigLength++; + } + buf += *argListBegin; + buf += "*/"; + } + else + buf += *argListBegin; + argListBegin++; + OrigLength++; + } + buf += ')'; + OrigLength++; + } + ReplaceText(Start, OrigLength, buf); + + return; +} + + +/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: +/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, +/// struct Block_byref_id_object *src) { +/// _Block_object_assign (&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_assign(&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } +/// And: +/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } + +std::string RewriteObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, + int flag) { + std::string S; + if (CopyDestroyCache.count(flag)) + return S; + CopyDestroyCache.insert(flag); + S = "static void __Block_byref_id_object_copy_"; + S += utostr(flag); + S += "(void *dst, void *src) {\n"; + + // offset into the object pointer is computed as: + // void * + void* + int + int + void* + void * + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + unsigned VoidPtrSize = + static_cast(Context->getTypeSize(Context->VoidPtrTy)); + + unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); + S += " _Block_object_assign((char*)dst + "; + S += utostr(offset); + S += ", *(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + + S += "static void __Block_byref_id_object_dispose_"; + S += utostr(flag); + S += "(void *src) {\n"; + S += " _Block_object_dispose(*(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + return S; +} + +/// RewriteByRefVar - For each __block typex ND variable this routine transforms +/// the declaration into: +/// struct __Block_byref_ND { +/// void *__isa; // NULL for everything except __weak pointers +/// struct __Block_byref_ND *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object +/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object +/// typex ND; +/// }; +/// +/// It then replaces declaration of ND variable with: +/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, +/// __size=sizeof(struct __Block_byref_ND), +/// ND=initializer-if-any}; +/// +/// +void RewriteObjC::RewriteByRefVar(VarDecl *ND) { + // Insert declaration for the function in which block literal is + // used. + if (CurFunctionDeclToDeclareForBlock) + RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); + int flag = 0; + int isa = 0; + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + if (DeclLoc.isInvalid()) + // If type location is missing, it is because of missing type (a warning). + // Use variable's location which is good for this case. + DeclLoc = ND->getLocation(); + const char *startBuf = SM->getCharacterData(DeclLoc); + SourceLocation X = ND->getLocEnd(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + std::string Name(ND->getNameAsString()); + std::string ByrefType; + RewriteByRefString(ByrefType, Name, ND, true); + ByrefType += " {\n"; + ByrefType += " void *__isa;\n"; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += " *__forwarding;\n"; + ByrefType += " int __flags;\n"; + ByrefType += " int __size;\n"; + // Add void *__Block_byref_id_object_copy; + // void *__Block_byref_id_object_dispose; if needed. + QualType Ty = ND->getType(); + bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty); + if (HasCopyAndDispose) { + ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; + ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; + } + + QualType T = Ty; + (void)convertBlockPointerToFunctionPointer(T); + T.getAsStringInternal(Name, Context->getPrintingPolicy()); + + ByrefType += " " + Name + ";\n"; + ByrefType += "};\n"; + // Insert this type in global scope. It is needed by helper function. + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = CurFunctionDef->getTypeSpecStartLoc(); + else { + assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); + FunLocStart = CurMethodDef->getLocStart(); + } + InsertText(FunLocStart, ByrefType); + if (Ty.isObjCGCWeak()) { + flag |= BLOCK_FIELD_IS_WEAK; + isa = 1; + } + + if (HasCopyAndDispose) { + flag = BLOCK_BYREF_CALLER; + QualType Ty = ND->getType(); + // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. + if (Ty->isBlockPointerType()) + flag |= BLOCK_FIELD_IS_BLOCK; + else + flag |= BLOCK_FIELD_IS_OBJECT; + std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); + if (!HF.empty()) + InsertText(FunLocStart, HF); + } + + // struct __Block_byref_ND ND = + // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), + // initializer-if-any}; + bool hasInit = (ND->getInit() != 0); + unsigned flags = 0; + if (HasCopyAndDispose) + flags |= BLOCK_HAS_COPY_DISPOSE; + Name = ND->getNameAsString(); + ByrefType.clear(); + RewriteByRefString(ByrefType, Name, ND); + std::string ForwardingCastType("("); + ForwardingCastType += ByrefType + " *)"; + if (!hasInit) { + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + ByrefType += "};\n"; + unsigned nameSize = Name.size(); + // for block or function pointer declaration. Name is aleady + // part of the declaration. + if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) + nameSize = 1; + ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); + } + else { + SourceLocation startLoc; + Expr *E = ND->getInit(); + if (const CStyleCastExpr *ECE = dyn_cast(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getLocStart(); + startLoc = SM->getExpansionLoc(startLoc); + endBuf = SM->getCharacterData(startLoc); + ByrefType += " " + Name; + ByrefType += " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += "), "; + if (HasCopyAndDispose) { + ByrefType += "__Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + ByrefType += ", "; + } + ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); + + // Complete the newly synthesized compound expression by inserting a right + // curly brace before the end of the declaration. + // FIXME: This approach avoids rewriting the initializer expression. It + // also assumes there is only one declarator. For example, the following + // isn't currently supported by this routine (in general): + // + // double __block BYREFVAR = 1.34, BYREFVAR2 = 1.37; + // + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *semiBuf = strchr(startInitializerBuf, ';'); + assert((*semiBuf == ';') && "RewriteByRefVar: can't find ';'"); + SourceLocation semiLoc = + startLoc.getLocWithOffset(semiBuf-startInitializerBuf); + + InsertText(semiLoc, "}"); + } + return; +} + +void RewriteObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { + // Add initializers for any closure decl refs. + GetBlockDeclRefExprs(Exp->getBody()); + if (BlockDeclRefs.size()) { + // Unique all "by copy" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (!BlockDeclRefs[i]->getDecl()->hasAttr()) { + if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr()) { + if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr() || + BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + BlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } +} + +FunctionDecl *RewriteObjC::SynthBlockInitFunctionDecl(StringRef name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, FType, 0, SC_Extern, + SC_None, false, false); +} + +Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp, + const SmallVector &InnerBlockDeclRefs) { + const BlockDecl *block = Exp->getBlockDecl(); + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + + // Add inner imported variables now used in current block. + int countOfInnerDecls = 0; + if (!InnerBlockDeclRefs.empty()) { + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { + DeclRefExpr *Exp = InnerBlockDeclRefs[i]; + ValueDecl *VD = Exp->getDecl(); + if (!VD->hasAttr() && !BlockByCopyDeclsPtrSet.count(VD)) { + // We need to save the copied-in variables in nested + // blocks because it is needed at the end for some of the API generations. + // See SynthesizeBlockLiterals routine. + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr() && !BlockByRefDeclsPtrSet.count(VD)) { + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) + if (InnerBlockDeclRefs[i]->getDecl()->hasAttr() || + InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); + } + InnerDeclRefsCount.push_back(countOfInnerDecls); + + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) + BuildUniqueMethodName(FuncName, CurMethodDef); + else if (GlobalVarDecl) + FuncName = std::string(GlobalVarDecl->getNameAsString()); + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Tag = "__" + FuncName + "_block_impl_" + BlockNumber; + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + // Get a pointer to the function type so we can cast appropriately. + QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); + QualType FType = Context->getPointerType(BFT); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a contructor call... + FD = SynthBlockInitFunctionDecl(Tag); + DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, FType, VK_RValue, + SourceLocation()); + + SmallVector InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func); + DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), + VK_LValue, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + InitExprs.push_back(castExpr); + + // Initialize the block descriptor. + std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get(DescData.c_str()), + Context->VoidPtrTy, 0, + SC_Static, SC_None); + UnaryOperator *DescRefExpr = + new (Context) UnaryOperator(new (Context) DeclRefExpr(NewVD, false, + Context->VoidPtrTy, + VK_LValue, + SourceLocation()), + UO_AddrOf, + Context->getPointerType(Context->VoidPtrTy), + VK_RValue, OK_Ordinary, + SourceLocation()); + InitExprs.push_back(DescRefExpr); + + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (SmallVector::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation()); + } + + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (SmallVector::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + ValueDecl *ND = (*I); + std::string Name(ND->getNameAsString()); + std::string RecName; + RewriteByRefString(RecName, Name, ND, true); + IdentifierInfo *II = &Context->Idents.get(RecName.c_str() + + sizeof("struct")); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, + SourceLocation()); + bool isNestedCapturedVar = false; + if (block) + for (BlockDecl::capture_const_iterator ci = block->capture_begin(), + ce = block->capture_end(); ci != ce; ++ci) { + const VarDecl *variable = ci->getVariable(); + if (variable == ND && ci->isNested()) { + assert (ci->isByRef() && + "SynthBlockInitExpr - captured block variable is not byref"); + isNestedCapturedVar = true; + break; + } + } + // captured nested byref variable has its address passed. Do not take + // its address again. + if (!isNestedCapturedVar) + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); + InitExprs.push_back(Exp); + } + } + if (ImportedBlockDecls.size()) { + // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR + int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); + unsigned IntSize = + static_cast(Context->getTypeSize(Context->IntTy)); + Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), + Context->IntTy, SourceLocation()); + InitExprs.push_back(FlagExp); + } + NewRep = new (Context) CallExpr(*Context, DRE, InitExprs, + FType, VK_LValue, SourceLocation()); + NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), + VK_RValue, OK_Ordinary, SourceLocation()); + NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, + NewRep); + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { + if (const ObjCForCollectionStmt * CS = + dyn_cast(Stmts.back())) + return CS->getElement() == DS; + return false; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { + if (isa(S) || isa(S) || + isa(S) || isa(S)) + Stmts.push_back(S); + else if (isa(S)) { + Stmts.push_back(S); + ObjCBcLabelNo.push_back(++BcLabelCount); + } + + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast(S)) { + if (isa(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt::child_range CI = S->children(); CI; ++CI) + if (*CI) { + Stmt *childStmt = (*CI); + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); + if (newStmt) { + *CI = newStmt; + } + } + + if (BlockExpr *BE = dyn_cast(S)) { + SmallVector InnerBlockDeclRefs; + llvm::SmallPtrSet InnerContexts; + InnerContexts.insert(BE->getBlockDecl()); + ImportedLocalExternalDecls.clear(); + GetInnerBlockDeclRefExprs(BE->getBody(), + InnerBlockDeclRefs, InnerContexts); + // Rewrite the block body in place. + Stmt *SaveCurrentBody = CurrentBody; + CurrentBody = BE->getBody(); + PropParentMap = 0; + // block literal on rhs of a property-dot-sytax assignment + // must be replaced by its synthesize ast so getRewrittenText + // works as expected. In this case, what actually ends up on RHS + // is the blockTranscribed which is the helper function for the + // block literal; as in: self.c = ^() {[ace ARR];}; + bool saveDisableReplaceStmt = DisableReplaceStmt; + DisableReplaceStmt = false; + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + DisableReplaceStmt = saveDisableReplaceStmt; + CurrentBody = SaveCurrentBody; + PropParentMap = 0; + ImportedLocalExternalDecls.clear(); + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); + + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast(S)) + return RewriteAtEncode(AtEncode); + + if (ObjCSelectorExpr *AtSelector = dyn_cast(S)) + return RewriteAtSelector(AtSelector); + + if (ObjCStringLiteral *AtString = dyn_cast(S)) + return RewriteObjCStringLiteral(AtString); + + if (ObjCMessageExpr *MessExpr = dyn_cast(S)) { +#if 0 + // Before we rewrite it, put the original message expression in a comment. + SourceLocation startLoc = MessExpr->getLocStart(); + SourceLocation endLoc = MessExpr->getLocEnd(); + + const char *startBuf = SM->getCharacterData(startLoc); + const char *endBuf = SM->getCharacterData(endLoc); + + std::string messString; + messString += "// "; + messString.append(startBuf, endBuf-startBuf+1); + messString += "\n"; + + // FIXME: Missing definition of + // InsertText(clang::SourceLocation, char const*, unsigned int). + // InsertText(startLoc, messString.c_str(), messString.size()); + // Tried this, but it didn't work either... + // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); +#endif + return RewriteMessageExpr(MessExpr); + } + + if (ObjCAtTryStmt *StmtTry = dyn_cast(S)) + return RewriteObjCTryStmt(StmtTry); + + if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast(S)) + return RewriteObjCSynchronizedStmt(StmtTry); + + if (ObjCAtThrowStmt *StmtThrow = dyn_cast(S)) + return RewriteObjCThrowStmt(StmtThrow); + + if (ObjCProtocolExpr *ProtocolExp = dyn_cast(S)) + return RewriteObjCProtocolExpr(ProtocolExp); + + if (ObjCForCollectionStmt *StmtForCollection = + dyn_cast(S)) + return RewriteObjCForCollectionStmt(StmtForCollection, + OrigStmtRange.getEnd()); + if (BreakStmt *StmtBreakStmt = + dyn_cast(S)) + return RewriteBreakStmt(StmtBreakStmt); + if (ContinueStmt *StmtContinueStmt = + dyn_cast(S)) + return RewriteContinueStmt(StmtContinueStmt); + + // Need to check for protocol refs (id

, Foo

*) in variable decls + // and cast exprs. + if (DeclStmt *DS = dyn_cast(S)) { + // FIXME: What we're doing here is modifying the type-specifier that + // precedes the first Decl. In the future the DeclGroup should have + // a separate type-specifier that we can rewrite. + // NOTE: We need to avoid rewriting the DeclStmt if it is within + // the context of an ObjCForCollectionStmt. For example: + // NSArray *someArray; + // for (id index in someArray) ; + // This is because RewriteObjCForCollectionStmt() does textual rewriting + // and it depends on the original text locations/positions. + if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) + RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); + + // Blocks rewrite rules. + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + Decl *SD = *DI; + if (ValueDecl *ND = dyn_cast(SD)) { + if (isTopLevelBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + if (VarDecl *VD = dyn_cast(SD)) { + if (VD->hasAttr()) { + static unsigned uniqueByrefDeclCount = 0; + assert(!BlockByRefDeclNo.count(ND) && + "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); + BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; + RewriteByRefVar(VD); + } + else + RewriteTypeOfDecl(VD); + } + } + if (TypedefNameDecl *TD = dyn_cast(SD)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + } + } + + if (CStyleCastExpr *CE = dyn_cast(S)) + RewriteObjCQualifiedInterfaceTypes(CE); + + if (isa(S) || isa(S) || + isa(S) || isa(S)) { + assert(!Stmts.empty() && "Statement stack is empty"); + assert ((isa(Stmts.back()) || isa(Stmts.back()) || + isa(Stmts.back()) || isa(Stmts.back())) + && "Statement stack mismatch"); + Stmts.pop_back(); + } + // Handle blocks rewriting. + if (DeclRefExpr *DRE = dyn_cast(S)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr()) + return RewriteBlockDeclRefExpr(DRE); + if (HasLocalVariableExternalStorage(VD)) + return RewriteLocalVariableExternalStorage(DRE); + } + + if (CallExpr *CE = dyn_cast(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); + ReplaceStmt(S, BlockCall); + return BlockCall; + } + } + if (CStyleCastExpr *CE = dyn_cast(S)) { + RewriteCastExpr(CE); + } +#if 0 + if (ImplicitCastExpr *ICE = dyn_cast(S)) { + CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), + ICE->getSubExpr(), + SourceLocation()); + // Get the new text. + std::string SStr; + llvm::raw_string_ostream Buf(SStr); + Replacement->printPretty(Buf); + const std::string &Str = Buf.str(); + + printf("CAST = %s\n", &Str[0]); + InsertText(ICE->getSubExpr()->getLocStart(), &Str[0], Str.size()); + delete S; + return Replacement; + } +#endif + // Return this stmt unmodified. + return S; +} + +void RewriteObjC::RewriteRecordBody(RecordDecl *RD) { + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) { + FieldDecl *FD = *i; + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + if (FD->getType()->isObjCQualifiedIdType() || + FD->getType()->isObjCQualifiedInterfaceType()) + RewriteObjCQualifiedInterfaceTypes(FD); + } +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteObjC::HandleDeclInMainFile(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl *FD = cast(D); + if (FD->isOverloadedOperator()) + return; + + // Since function prototypes don't have ParmDecl's, we check the function + // prototype. This enables us to rewrite function declarations and + // definitions using the same code. + RewriteBlocksInFunctionProtoType(FD->getType(), FD); + + if (!FD->isThisDeclarationADefinition()) + break; + + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (CompoundStmt *Body = dyn_cast_or_null(FD->getBody())) { + CurFunctionDef = FD; + CurFunctionDeclToDeclareForBlock = FD; + CurrentBody = Body; + Body = + cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + CurFunctionDef = 0; + CurFunctionDeclToDeclareForBlock = 0; + } + break; + } + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = cast(D); + if (CompoundStmt *Body = MD->getCompoundBody()) { + CurMethodDef = MD; + CurrentBody = Body; + Body = + cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + InsertBlockLiteralsWithinMethod(MD); + CurMethodDef = 0; + } + break; + } + case Decl::ObjCImplementation: { + ObjCImplementationDecl *CI = cast(D); + ClassImplementation.push_back(CI); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CI = cast(D); + CategoryImplementation.push_back(CI); + break; + } + case Decl::Var: { + VarDecl *VD = cast(D); + RewriteObjCQualifiedInterfaceTypes(VD); + if (isTopLevelBlockPointerType(VD->getType())) + RewriteBlockPointerDecl(VD); + else if (VD->getType()->isFunctionPointerType()) { + CheckFunctionPointerDecl(VD->getType(), VD); + if (VD->getInit()) { + if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } else if (VD->getType()->isRecordType()) { + RecordDecl *RD = VD->getType()->getAs()->getDecl(); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = 0; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = 0; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); + GlobalVarDecl = 0; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { + RewriteCastExpr(CE); + } + } + break; + } + case Decl::TypeAlias: + case Decl::Typedef: { + if (TypedefNameDecl *TD = dyn_cast(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + break; + } + case Decl::CXXRecord: + case Decl::Record: { + RecordDecl *RD = cast(D); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + break; + } + default: + break; + } + // Nothing yet. +} + +void RewriteObjC::HandleTranslationUnit(ASTContext &C) { + if (Diags.hasErrorOccurred()) + return; + + RewriteInclude(); + + // Here's a great place to add any extra declarations that may be needed. + // Write out meta data for each @protocol(). + for (llvm::SmallPtrSet::iterator I = ProtocolExprDecls.begin(), + E = ProtocolExprDecls.end(); I != E; ++I) + RewriteObjCProtocolMetaData(*I, "", "", Preamble); + + InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + //printf("Changed:\n"); + *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + llvm::errs() << "No changes\n"; + } + + if (ClassImplementation.size() || CategoryImplementation.size() || + ProtocolExprDecls.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + RewriteMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + OutFile->flush(); +} + +void RewriteObjCFragileABI::Initialize(ASTContext &context) { + InitializeCommon(context); + + // declaring objc_selector outside the parameter list removes a silly + // scope related warning... + if (IsHeader) + Preamble = "#pragma once\n"; + Preamble += "struct objc_selector; struct objc_class;\n"; + Preamble += "struct __rw_objc_super { struct objc_object *object; "; + Preamble += "struct objc_object *superClass; "; + if (LangOpts.MicrosoftExt) { + // Add a constructor for creating temporary objects. + Preamble += "__rw_objc_super(struct objc_object *o, struct objc_object *s) " + ": "; + Preamble += "object(o), superClass(s) {} "; + } + Preamble += "};\n"; + Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; + Preamble += "typedef struct objc_object Protocol;\n"; + Preamble += "#define _REWRITER_typedef_Protocol\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; + Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; + } else + Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper"; + Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object* objc_msgSend_stret"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object* objc_msgSendSuper_stret"; + Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT double objc_msgSend_fpret"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; + Preamble += "(struct objc_class *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_enter(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_exit(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_exception_extract(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_exception_match"; + Preamble += "(struct objc_class *, struct objc_object *);\n"; + // @synchronized hooks. + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_enter(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_exit(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; + Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; + Preamble += "struct __objcFastEnumerationState {\n\t"; + Preamble += "unsigned long state;\n\t"; + Preamble += "void **itemsPtr;\n\t"; + Preamble += "unsigned long *mutationsPtr;\n\t"; + Preamble += "unsigned long extra[5];\n};\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; + Preamble += "#define __FASTENUMERATIONSTATE\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; + Preamble += "struct __NSConstantStringImpl {\n"; + Preamble += " int *isa;\n"; + Preamble += " int flags;\n"; + Preamble += " char *str;\n"; + Preamble += " long length;\n"; + Preamble += "};\n"; + Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; + Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; + Preamble += "#endif\n"; + Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; + Preamble += "#endif\n"; + // Blocks preamble. + Preamble += "#ifndef BLOCK_IMPL\n"; + Preamble += "#define BLOCK_IMPL\n"; + Preamble += "struct __block_impl {\n"; + Preamble += " void *isa;\n"; + Preamble += " int Flags;\n"; + Preamble += " int Reserved;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; + Preamble += "extern \"C\" __declspec(dllexport) " + "void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. + Preamble += "#define __attribute__(X)\n"; + Preamble += "#endif\n"; + Preamble += "#define __weak\n"; + } + else { + Preamble += "#define __block\n"; + Preamble += "#define __weak\n"; + } + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long + // as this avoids warning in any 64bit/32bit compilation model. + Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; +} + +/// RewriteIvarOffsetComputation - This rutine synthesizes computation of +/// ivar offset. +void RewriteObjCFragileABI::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) { + if (ivar->isBitField()) { + // FIXME: The hack below doesn't work for bitfields. For now, we simply + // place all bitfields at offset 0. + Result += "0"; + } else { + Result += "__OFFSETOFIVAR__(struct "; + Result += ivar->getContainingInterface()->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ", "; + Result += ivar->getNameAsString(); + Result += ")"; + } +} + +/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. +void RewriteObjCFragileABI::RewriteObjCProtocolMetaData( + ObjCProtocolDecl *PDecl, StringRef prefix, + StringRef ClassName, std::string &Result) { + static bool objc_protocol_methods = false; + + // Output struct protocol_methods holder of method selector and type. + if (!objc_protocol_methods && PDecl->hasDefinition()) { + /* struct protocol_methods { + SEL _cmd; + char *method_types; + } + */ + Result += "\nstruct _protocol_methods {\n"; + Result += "\tstruct objc_selector *_cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "};\n"; + + objc_protocol_methods = true; + } + // Do not synthesize the protocol more than once. + if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) + return; + + if (ObjCProtocolDecl *Def = PDecl->getDefinition()) + PDecl = Def; + + if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { + unsigned NumMethods = std::distance(PDecl->instmeth_begin(), + PDecl->instmeth_end()); + /* struct _objc_protocol_method_list { + int protocol_method_count; + struct protocol_methods protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tint protocol_method_count;\n"; + Result += "\tstruct _protocol_methods protocol_methods["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __cat_inst_meth\")))= " + "{\n\t" + utostr(NumMethods) + "\n"; + + // Output instance methods declared in this protocol. + for (ObjCProtocolDecl::instmeth_iterator + I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); + I != E; ++I) { + if (I == PDecl->instmeth_begin()) + Result += "\t ,{{(struct objc_selector *)\""; + else + Result += "\t ,{(struct objc_selector *)\""; + Result += (*I)->getSelector().getAsString(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl((*I), MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\"}\n"; + } + Result += "\t }\n};\n"; + } + + // Output class methods declared in this protocol. + unsigned NumMethods = std::distance(PDecl->classmeth_begin(), + PDecl->classmeth_end()); + if (NumMethods > 0) { + /* struct _objc_protocol_method_list { + int protocol_method_count; + struct protocol_methods protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tint protocol_method_count;\n"; + Result += "\tstruct _protocol_methods protocol_methods["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t"; + Result += utostr(NumMethods); + Result += "\n"; + + // Output instance methods declared in this protocol. + for (ObjCProtocolDecl::classmeth_iterator + I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); + I != E; ++I) { + if (I == PDecl->classmeth_begin()) + Result += "\t ,{{(struct objc_selector *)\""; + else + Result += "\t ,{(struct objc_selector *)\""; + Result += (*I)->getSelector().getAsString(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl((*I), MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\"}\n"; + } + Result += "\t }\n};\n"; + } + + // Output: + /* struct _objc_protocol { + // Objective-C 1.0 extensions + struct _objc_protocol_extension *isa; + char *protocol_name; + struct _objc_protocol **protocol_list; + struct _objc_protocol_method_list *instance_methods; + struct _objc_protocol_method_list *class_methods; + }; + */ + static bool objc_protocol = false; + if (!objc_protocol) { + Result += "\nstruct _objc_protocol {\n"; + Result += "\tstruct _objc_protocol_extension *isa;\n"; + Result += "\tchar *protocol_name;\n"; + Result += "\tstruct _objc_protocol **protocol_list;\n"; + Result += "\tstruct _objc_protocol_method_list *instance_methods;\n"; + Result += "\tstruct _objc_protocol_method_list *class_methods;\n"; + Result += "};\n"; + + objc_protocol = true; + } + + Result += "\nstatic struct _objc_protocol _OBJC_PROTOCOL_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __protocol\")))= " + "{\n\t0, \""; + Result += PDecl->getNameAsString(); + Result += "\", 0, "; + if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { + Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); + Result += ", "; + } + else + Result += "0, "; + if (PDecl->classmeth_begin() != PDecl->classmeth_end()) { + Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); + Result += "\n"; + } + else + Result += "0\n"; + Result += "};\n"; + + // Mark this protocol as having been generated. + if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl())) + llvm_unreachable("protocol already synthesized"); + +} + +void RewriteObjCFragileABI::RewriteObjCProtocolListMetaData( + const ObjCList &Protocols, + StringRef prefix, StringRef ClassName, + std::string &Result) { + if (Protocols.empty()) return; + + for (unsigned i = 0; i != Protocols.size(); i++) + RewriteObjCProtocolMetaData(Protocols[i], prefix, ClassName, Result); + + // Output the top lovel protocol meta-data for the class. + /* struct _objc_protocol_list { + struct _objc_protocol_list *next; + int protocol_count; + struct _objc_protocol *class_protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tstruct _objc_protocol_list *next;\n"; + Result += "\tint protocol_count;\n"; + Result += "\tstruct _objc_protocol *class_protocols["; + Result += utostr(Protocols.size()); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += "_PROTOCOLS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t0, "; + Result += utostr(Protocols.size()); + Result += "\n"; + + Result += "\t,{&_OBJC_PROTOCOL_"; + Result += Protocols[0]->getNameAsString(); + Result += " \n"; + + for (unsigned i = 1; i != Protocols.size(); i++) { + Result += "\t ,&_OBJC_PROTOCOL_"; + Result += Protocols[i]->getNameAsString(); + Result += "\n"; + } + Result += "\t }\n};\n"; +} + +void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explicitly declared @interface's are already synthesized. + if (CDecl->isImplicitInterfaceDecl()) { + // FIXME: Implementation of a class with no @interface (legacy) does not + // produce correct synthesis as yet. + RewriteObjCInternalStruct(CDecl, Result); + } + + // Build _objc_ivar_list metadata for classes ivars if needed + unsigned NumIvars = !IDecl->ivar_empty() + ? IDecl->ivar_size() + : (CDecl ? CDecl->ivar_size() : 0); + if (NumIvars > 0) { + static bool objc_ivar = false; + if (!objc_ivar) { + /* struct _objc_ivar { + char *ivar_name; + char *ivar_type; + int ivar_offset; + }; + */ + Result += "\nstruct _objc_ivar {\n"; + Result += "\tchar *ivar_name;\n"; + Result += "\tchar *ivar_type;\n"; + Result += "\tint ivar_offset;\n"; + Result += "};\n"; + + objc_ivar = true; + } + + /* struct { + int ivar_count; + struct _objc_ivar ivar_list[nIvars]; + }; + */ + Result += "\nstatic struct {\n"; + Result += "\tint ivar_count;\n"; + Result += "\tstruct _objc_ivar ivar_list["; + Result += utostr(NumIvars); + Result += "];\n} _OBJC_INSTANCE_VARIABLES_"; + Result += IDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __instance_vars\")))= " + "{\n\t"; + Result += utostr(NumIvars); + Result += "\n"; + + ObjCInterfaceDecl::ivar_iterator IVI, IVE; + SmallVector IVars; + if (!IDecl->ivar_empty()) { + for (ObjCInterfaceDecl::ivar_iterator + IV = IDecl->ivar_begin(), IVEnd = IDecl->ivar_end(); + IV != IVEnd; ++IV) + IVars.push_back(*IV); + IVI = IDecl->ivar_begin(); + IVE = IDecl->ivar_end(); + } else { + IVI = CDecl->ivar_begin(); + IVE = CDecl->ivar_end(); + } + Result += "\t,{{\""; + Result += IVI->getNameAsString(); + Result += "\", \""; + std::string TmpString, StrEncoding; + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); + QuoteDoublequotes(TmpString, StrEncoding); + Result += StrEncoding; + Result += "\", "; + RewriteIvarOffsetComputation(*IVI, Result); + Result += "}\n"; + for (++IVI; IVI != IVE; ++IVI) { + Result += "\t ,{\""; + Result += IVI->getNameAsString(); + Result += "\", \""; + std::string TmpString, StrEncoding; + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); + QuoteDoublequotes(TmpString, StrEncoding); + Result += StrEncoding; + Result += "\", "; + RewriteIvarOffsetComputation(*IVI, Result); + Result += "}\n"; + } + + Result += "\t }\n};\n"; + } + + // Build _objc_method_list for class's instance methods if needed + SmallVector + InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), + PropEnd = IDecl->propimpl_end(); + Prop != PropEnd; ++Prop) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (!Getter->isDefined()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (!Setter->isDefined()) + InstanceMethods.push_back(Setter); + } + RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), + true, "", IDecl->getName(), Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "", IDecl->getName(), Result); + + // Protocols referenced in class declaration? + RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), + "CLASS", CDecl->getName(), Result); + + // Declaration of class/meta-class metadata + /* struct _objc_class { + struct _objc_class *isa; // or const char *root_class_name when metadata + const char *super_class_name; + char *name; + long version; + long info; + long instance_size; + struct _objc_ivar_list *ivars; + struct _objc_method_list *methods; + struct objc_cache *cache; + struct objc_protocol_list *protocols; + const char *ivar_layout; + struct _objc_class_ext *ext; + }; + */ + static bool objc_class = false; + if (!objc_class) { + Result += "\nstruct _objc_class {\n"; + Result += "\tstruct _objc_class *isa;\n"; + Result += "\tconst char *super_class_name;\n"; + Result += "\tchar *name;\n"; + Result += "\tlong version;\n"; + Result += "\tlong info;\n"; + Result += "\tlong instance_size;\n"; + Result += "\tstruct _objc_ivar_list *ivars;\n"; + Result += "\tstruct _objc_method_list *methods;\n"; + Result += "\tstruct objc_cache *cache;\n"; + Result += "\tstruct _objc_protocol_list *protocols;\n"; + Result += "\tconst char *ivar_layout;\n"; + Result += "\tstruct _objc_class_ext *ext;\n"; + Result += "};\n"; + objc_class = true; + } + + // Meta-class metadata generation. + ObjCInterfaceDecl *RootClass = 0; + ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); + while (SuperClass) { + RootClass = SuperClass; + SuperClass = SuperClass->getSuperClass(); + } + SuperClass = CDecl->getSuperClass(); + + Result += "\nstatic struct _objc_class _OBJC_METACLASS_"; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __meta_class\")))= " + "{\n\t(struct _objc_class *)\""; + Result += (RootClass ? RootClass->getNameAsString() : CDecl->getNameAsString()); + Result += "\""; + + if (SuperClass) { + Result += ", \""; + Result += SuperClass->getNameAsString(); + Result += "\", \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + else { + Result += ", 0, \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + // Set 'ivars' field for root class to 0. ObjC1 runtime does not use it. + // 'info' field is initialized to CLS_META(2) for metaclass + Result += ", 0,2, sizeof(struct _objc_class), 0"; + if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { + Result += "\n\t, (struct _objc_method_list *)&_OBJC_CLASS_METHODS_"; + Result += IDecl->getNameAsString(); + Result += "\n"; + } + else + Result += ", 0\n"; + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += "\t,0, (struct _objc_protocol_list *)&_OBJC_CLASS_PROTOCOLS_"; + Result += CDecl->getNameAsString(); + Result += ",0,0\n"; + } + else + Result += "\t,0,0,0,0\n"; + Result += "};\n"; + + // class metadata generation. + Result += "\nstatic struct _objc_class _OBJC_CLASS_"; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __class\")))= " + "{\n\t&_OBJC_METACLASS_"; + Result += CDecl->getNameAsString(); + if (SuperClass) { + Result += ", \""; + Result += SuperClass->getNameAsString(); + Result += "\", \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + else { + Result += ", 0, \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + // 'info' field is initialized to CLS_CLASS(1) for class + Result += ", 0,1"; + if (!ObjCSynthesizedStructs.count(CDecl)) + Result += ",0"; + else { + // class has size. Must synthesize its size. + Result += ",sizeof(struct "; + Result += CDecl->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ")"; + } + if (NumIvars > 0) { + Result += ", (struct _objc_ivar_list *)&_OBJC_INSTANCE_VARIABLES_"; + Result += CDecl->getNameAsString(); + Result += "\n\t"; + } + else + Result += ",0"; + if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { + Result += ", (struct _objc_method_list *)&_OBJC_INSTANCE_METHODS_"; + Result += CDecl->getNameAsString(); + Result += ", 0\n\t"; + } + else + Result += ",0,0"; + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += ", (struct _objc_protocol_list*)&_OBJC_CLASS_PROTOCOLS_"; + Result += CDecl->getNameAsString(); + Result += ", 0,0\n"; + } + else + Result += ",0,0,0\n"; + Result += "};\n"; +} + +void RewriteObjCFragileABI::RewriteMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // For each implemented class, write out all its meta data. + for (int i = 0; i < ClsDefCount; i++) + RewriteObjCClassMetaData(ClassImplementation[i], Result); + + // For each implemented category, write out all its meta data. + for (int i = 0; i < CatDefCount; i++) + RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); + + // Write objc_symtab metadata + /* + struct _objc_symtab + { + long sel_ref_cnt; + SEL *refs; + short cls_def_cnt; + short cat_def_cnt; + void *defs[cls_def_cnt + cat_def_cnt]; + }; + */ + + Result += "\nstruct _objc_symtab {\n"; + Result += "\tlong sel_ref_cnt;\n"; + Result += "\tSEL *refs;\n"; + Result += "\tshort cls_def_cnt;\n"; + Result += "\tshort cat_def_cnt;\n"; + Result += "\tvoid *defs[" + utostr(ClsDefCount + CatDefCount)+ "];\n"; + Result += "};\n\n"; + + Result += "static struct _objc_symtab " + "_OBJC_SYMBOLS __attribute__((used, section (\"__OBJC, __symbols\")))= {\n"; + Result += "\t0, 0, " + utostr(ClsDefCount) + + ", " + utostr(CatDefCount) + "\n"; + for (int i = 0; i < ClsDefCount; i++) { + Result += "\t,&_OBJC_CLASS_"; + Result += ClassImplementation[i]->getNameAsString(); + Result += "\n"; + } + + for (int i = 0; i < CatDefCount; i++) { + Result += "\t,&_OBJC_CATEGORY_"; + Result += CategoryImplementation[i]->getClassInterface()->getNameAsString(); + Result += "_"; + Result += CategoryImplementation[i]->getNameAsString(); + Result += "\n"; + } + + Result += "};\n\n"; + + // Write objc_module metadata + + /* + struct _objc_module { + long version; + long size; + const char *name; + struct _objc_symtab *symtab; + } + */ + + Result += "\nstruct _objc_module {\n"; + Result += "\tlong version;\n"; + Result += "\tlong size;\n"; + Result += "\tconst char *name;\n"; + Result += "\tstruct _objc_symtab *symtab;\n"; + Result += "};\n\n"; + Result += "static struct _objc_module " + "_OBJC_MODULES __attribute__ ((used, section (\"__OBJC, __module_info\")))= {\n"; + Result += "\t" + utostr(OBJC_ABI_VERSION) + + ", sizeof(struct _objc_module), \"\", &_OBJC_SYMBOLS\n"; + Result += "};\n\n"; + + if (LangOpts.MicrosoftExt) { + if (ProtocolExprDecls.size()) { + Result += "#pragma section(\".objc_protocol$B\",long,read,write)\n"; + Result += "#pragma data_seg(push, \".objc_protocol$B\")\n"; + for (llvm::SmallPtrSet::iterator I = ProtocolExprDecls.begin(), + E = ProtocolExprDecls.end(); I != E; ++I) { + Result += "static struct _objc_protocol *_POINTER_OBJC_PROTOCOL_"; + Result += (*I)->getNameAsString(); + Result += " = &_OBJC_PROTOCOL_"; + Result += (*I)->getNameAsString(); + Result += ";\n"; + } + Result += "#pragma data_seg(pop)\n\n"; + } + Result += "#pragma section(\".objc_module_info$B\",long,read,write)\n"; + Result += "#pragma data_seg(push, \".objc_module_info$B\")\n"; + Result += "static struct _objc_module *_POINTER_OBJC_MODULES = "; + Result += "&_OBJC_MODULES;\n"; + Result += "#pragma data_seg(pop)\n\n"; + } +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl; + for (CDecl = ClassDecl->getCategoryList(); CDecl; + CDecl = CDecl->getNextClassCategory()) + if (CDecl->getIdentifier() == IDecl->getIdentifier()) + break; + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += '_'; + FullCategoryName += IDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + SmallVector + InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), + PropEnd = IDecl->propimpl_end(); + Prop != PropEnd; ++Prop) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + InstanceMethods.push_back(Setter); + } + RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), + true, "CATEGORY_", FullCategoryName.c_str(), + Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "CATEGORY_", FullCategoryName.c_str(), + Result); + + // Protocols referenced in class declaration? + // Null CDecl is case of a category implementation with no category interface + if (CDecl) + RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), "CATEGORY", + FullCategoryName, Result); + /* struct _objc_category { + char *category_name; + char *class_name; + struct _objc_method_list *instance_methods; + struct _objc_method_list *class_methods; + struct _objc_protocol_list *protocols; + // Objective-C 1.0 extensions + uint32_t size; // sizeof (struct _objc_category) + struct _objc_property_list *instance_properties; // category's own + // @property decl. + }; + */ + + static bool objc_category = false; + if (!objc_category) { + Result += "\nstruct _objc_category {\n"; + Result += "\tchar *category_name;\n"; + Result += "\tchar *class_name;\n"; + Result += "\tstruct _objc_method_list *instance_methods;\n"; + Result += "\tstruct _objc_method_list *class_methods;\n"; + Result += "\tstruct _objc_protocol_list *protocols;\n"; + Result += "\tunsigned int size;\n"; + Result += "\tstruct _objc_property_list *instance_properties;\n"; + Result += "};\n"; + objc_category = true; + } + Result += "\nstatic struct _objc_category _OBJC_CATEGORY_"; + Result += FullCategoryName; + Result += " __attribute__ ((used, section (\"__OBJC, __category\")))= {\n\t\""; + Result += IDecl->getNameAsString(); + Result += "\"\n\t, \""; + Result += ClassDecl->getNameAsString(); + Result += "\"\n"; + + if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { + Result += "\t, (struct _objc_method_list *)" + "&_OBJC_CATEGORY_INSTANCE_METHODS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { + Result += "\t, (struct _objc_method_list *)" + "&_OBJC_CATEGORY_CLASS_METHODS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + + if (CDecl && CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += "\t, (struct _objc_protocol_list *)&_OBJC_CATEGORY_PROTOCOLS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + Result += "\t, sizeof(struct _objc_category), 0\n};\n"; +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +template +void RewriteObjCFragileABI::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + if (!objc_impl_method) { + /* struct _objc_method { + SEL _cmd; + char *method_types; + void *_imp; + } + */ + Result += "\nstruct _objc_method {\n"; + Result += "\tSEL _cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + objc_impl_method = true; + } + + // Build _objc_method_list for class's methods if needed + + /* struct { + struct _objc_method_list *next_method; + int method_count; + struct _objc_method method_list[]; + } + */ + unsigned NumMethods = std::distance(MethodBegin, MethodEnd); + Result += "\nstatic struct {\n"; + Result += "\tstruct _objc_method_list *next_method;\n"; + Result += "\tint method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; + Result += "_METHODS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __"; + Result += IsInstanceMethod ? "inst" : "cls"; + Result += "_meth\")))= "; + Result += "{\n\t0, " + utostr(NumMethods) + "\n"; + + Result += "\t,{{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { + Result += "\t ,{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + } + Result += "\t }\n};\n"; +} + +Stmt *RewriteObjCFragileABI::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + + ObjCIvarDecl *D = IV->getDecl(); + + Expr *Replacement = IV; + if (CurMethodDef) { + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast(BaseExpr->getType()->getPointeeType()); + assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = 0; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Synthesize an explicit cast to gain access to the ivar. + std::string RecName = clsDeclared->getIdentifier()->getName(); + RecName += "_IMPL"; + IdentifierInfo *II = &Context->Idents.get(RecName); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, + CK_BitCast, + IV->getBase()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), + castExpr); + if (IV->isFreeIvar() && + declaresSameEntity(CurMethodDef->getClassInterface(), iFaceDecl->getDecl())) { + MemberExpr *ME = new (Context) MemberExpr(PE, true, D, + IV->getLocation(), + D->getType(), + VK_LValue, OK_Ordinary); + Replacement = ME; + } else { + IV->setBase(PE); + } + } + } else { // we are outside a method. + assert(!IV->isFreeIvar() && "Cannot have a free standing ivar outside a method"); + + // Explicit ivar refs need to have a cast inserted. + // FIXME: consider sharing some of this code with the code above. + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast(BaseExpr->getType()->getPointeeType()); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = 0; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Synthesize an explicit cast to gain access to the ivar. + std::string RecName = clsDeclared->getIdentifier()->getName(); + RecName += "_IMPL"; + IdentifierInfo *II = &Context->Idents.get(RecName); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, + CK_BitCast, + IV->getBase()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), + IV->getBase()->getLocEnd(), castExpr); + // Cannot delete IV->getBase(), since PE points to it. + // Replace the old base with the cast. This is important when doing + // embedded rewrites. For example, [newInv->_container addObject:0]. + IV->setBase(PE); + } + } + + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; +} diff --git a/lib/Rewrite/Frontend/RewriteTest.cpp b/lib/Rewrite/Frontend/RewriteTest.cpp new file mode 100644 index 0000000000000..722c5e80b4430 --- /dev/null +++ b/lib/Rewrite/Frontend/RewriteTest.cpp @@ -0,0 +1,39 @@ +//===--- RewriteTest.cpp - Rewriter playground ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a testbed. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/TokenRewriter.h" +#include "llvm/Support/raw_ostream.h" + +void clang::DoRewriteTest(Preprocessor &PP, raw_ostream* OS) { + SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + TokenRewriter Rewriter(SM.getMainFileID(), SM, LangOpts); + + // Throw tags around comments. + for (TokenRewriter::token_iterator I = Rewriter.token_begin(), + E = Rewriter.token_end(); I != E; ++I) { + if (I->isNot(tok::comment)) continue; + + Rewriter.AddTokenBefore(I, ""); + Rewriter.AddTokenAfter(I, ""); + } + + + // Print out the output. + for (TokenRewriter::token_iterator I = Rewriter.token_begin(), + E = Rewriter.token_end(); I != E; ++I) + *OS << PP.getSpelling(*I); +} diff --git a/lib/Rewrite/FrontendActions.cpp b/lib/Rewrite/FrontendActions.cpp deleted file mode 100644 index 9bc218e994fe8..0000000000000 --- a/lib/Rewrite/FrontendActions.cpp +++ /dev/null @@ -1,192 +0,0 @@ -//===--- FrontendActions.cpp ----------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/FrontendActions.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Parse/Parser.h" -#include "clang/Basic/FileManager.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendDiagnostic.h" -#include "clang/Frontend/Utils.h" -#include "clang/Rewrite/ASTConsumers.h" -#include "clang/Rewrite/FixItRewriter.h" -#include "clang/Rewrite/Rewriters.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/FileSystem.h" - -using namespace clang; - -//===----------------------------------------------------------------------===// -// AST Consumer Actions -//===----------------------------------------------------------------------===// - -ASTConsumer *HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { - if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) - return CreateHTMLPrinter(OS, CI.getPreprocessor()); - return 0; -} - -FixItAction::FixItAction() {} -FixItAction::~FixItAction() {} - -ASTConsumer *FixItAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { - return new ASTConsumer(); -} - -namespace { -class FixItRewriteInPlace : public FixItOptions { -public: - std::string RewriteFilename(const std::string &Filename, int &fd) { - fd = -1; - return Filename; - } -}; - -class FixItActionSuffixInserter : public FixItOptions { - std::string NewSuffix; - -public: - FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) - : NewSuffix(NewSuffix) { - this->FixWhatYouCan = FixWhatYouCan; - } - - std::string RewriteFilename(const std::string &Filename, int &fd) { - fd = -1; - SmallString<128> Path(Filename); - llvm::sys::path::replace_extension(Path, - NewSuffix + llvm::sys::path::extension(Path)); - return Path.str(); - } -}; - -class FixItRewriteToTemp : public FixItOptions { -public: - std::string RewriteFilename(const std::string &Filename, int &fd) { - SmallString<128> Path; - Path = llvm::sys::path::filename(Filename); - Path += "-%%%%%%%%"; - Path += llvm::sys::path::extension(Filename); - SmallString<128> NewPath; - llvm::sys::fs::unique_file(Path.str(), fd, NewPath); - return NewPath.str(); - } -}; -} // end anonymous namespace - -bool FixItAction::BeginSourceFileAction(CompilerInstance &CI, - StringRef Filename) { - const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts(); - if (!FEOpts.FixItSuffix.empty()) { - FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix, - FEOpts.FixWhatYouCan)); - } else { - FixItOpts.reset(new FixItRewriteInPlace); - FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; - } - Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), - CI.getLangOpts(), FixItOpts.get())); - return true; -} - -void FixItAction::EndSourceFileAction() { - // Otherwise rewrite all files. - Rewriter->WriteFixedFiles(); -} - -bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { - - std::vector > RewrittenFiles; - bool err = false; - { - const FrontendOptions &FEOpts = CI.getFrontendOpts(); - OwningPtr FixAction(new SyntaxOnlyAction()); - if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) { - OwningPtr FixItOpts; - if (FEOpts.FixToTemporaries) - FixItOpts.reset(new FixItRewriteToTemp()); - else - FixItOpts.reset(new FixItRewriteInPlace()); - FixItOpts->Silent = true; - FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; - FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings; - FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(), - CI.getLangOpts(), FixItOpts.get()); - FixAction->Execute(); - - err = Rewriter.WriteFixedFiles(&RewrittenFiles); - - FixAction->EndSourceFile(); - CI.setSourceManager(0); - CI.setFileManager(0); - } else { - err = true; - } - } - if (err) - return false; - CI.getDiagnosticClient().clear(); - CI.getDiagnostics().Reset(); - - PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); - PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(), - RewrittenFiles.begin(), RewrittenFiles.end()); - PPOpts.RemappedFilesKeepOriginalName = false; - - return true; -} - -//===----------------------------------------------------------------------===// -// Preprocessor Actions -//===----------------------------------------------------------------------===// - -ASTConsumer *RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { - if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "cpp")) { - if (CI.getLangOpts().ObjCRuntime.isNonFragile()) - return CreateModernObjCRewriter(InFile, OS, - CI.getDiagnostics(), CI.getLangOpts(), - CI.getDiagnosticOpts().NoRewriteMacros); - return CreateObjCRewriter(InFile, OS, - CI.getDiagnostics(), CI.getLangOpts(), - CI.getDiagnosticOpts().NoRewriteMacros); - } - return 0; -} - -void RewriteMacrosAction::ExecuteAction() { - CompilerInstance &CI = getCompilerInstance(); - raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); - if (!OS) return; - - RewriteMacrosInInput(CI.getPreprocessor(), OS); -} - -void RewriteTestAction::ExecuteAction() { - CompilerInstance &CI = getCompilerInstance(); - raw_ostream *OS = CI.createDefaultOutputFile(false, getCurrentFile()); - if (!OS) return; - - DoRewriteTest(CI.getPreprocessor(), OS); -} - -void RewriteIncludesAction::ExecuteAction() { - CompilerInstance &CI = getCompilerInstance(); - raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); - if (!OS) return; - - RewriteIncludesInInput(CI.getPreprocessor(), OS, - CI.getPreprocessorOutputOpts()); -} diff --git a/lib/Rewrite/HTMLPrint.cpp b/lib/Rewrite/HTMLPrint.cpp deleted file mode 100644 index 3d190abffcf83..0000000000000 --- a/lib/Rewrite/HTMLPrint.cpp +++ /dev/null @@ -1,94 +0,0 @@ -//===--- HTMLPrint.cpp - Source code -> HTML pretty-printing --------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Pretty-printing of source code to HTML. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/ASTConsumers.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Rewrite/HTMLRewrite.h" -#include "clang/Rewrite/Rewriter.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -using namespace clang; - -//===----------------------------------------------------------------------===// -// Functional HTML pretty-printing. -//===----------------------------------------------------------------------===// - -namespace { - class HTMLPrinter : public ASTConsumer { - Rewriter R; - raw_ostream *Out; - Preprocessor &PP; - bool SyntaxHighlight, HighlightMacros; - - public: - HTMLPrinter(raw_ostream *OS, Preprocessor &pp, - bool _SyntaxHighlight, bool _HighlightMacros) - : Out(OS), PP(pp), SyntaxHighlight(_SyntaxHighlight), - HighlightMacros(_HighlightMacros) {} - - void Initialize(ASTContext &context); - void HandleTranslationUnit(ASTContext &Ctx); - }; -} - -ASTConsumer* clang::CreateHTMLPrinter(raw_ostream *OS, - Preprocessor &PP, - bool SyntaxHighlight, - bool HighlightMacros) { - return new HTMLPrinter(OS, PP, SyntaxHighlight, HighlightMacros); -} - -void HTMLPrinter::Initialize(ASTContext &context) { - R.setSourceMgr(context.getSourceManager(), context.getLangOpts()); -} - -void HTMLPrinter::HandleTranslationUnit(ASTContext &Ctx) { - if (PP.getDiagnostics().hasErrorOccurred()) - return; - - // Format the file. - FileID FID = R.getSourceMgr().getMainFileID(); - const FileEntry* Entry = R.getSourceMgr().getFileEntryForID(FID); - const char* Name; - // In some cases, in particular the case where the input is from stdin, - // there is no entry. Fall back to the memory buffer for a name in those - // cases. - if (Entry) - Name = Entry->getName(); - else - Name = R.getSourceMgr().getBuffer(FID)->getBufferIdentifier(); - - html::AddLineNumbers(R, FID); - html::AddHeaderFooterInternalBuiltinCSS(R, FID, Name); - - // If we have a preprocessor, relex the file and syntax highlight. - // We might not have a preprocessor if we come from a deserialized AST file, - // for example. - - if (SyntaxHighlight) html::SyntaxHighlight(R, FID, PP); - if (HighlightMacros) html::HighlightMacros(R, FID, PP); - html::EscapeText(R, FID, false, true); - - // Emit the HTML. - const RewriteBuffer &RewriteBuf = R.getEditBuffer(FID); - char *Buffer = (char*)malloc(RewriteBuf.size()); - std::copy(RewriteBuf.begin(), RewriteBuf.end(), Buffer); - Out->write(Buffer, RewriteBuf.size()); - free(Buffer); -} diff --git a/lib/Rewrite/HTMLRewrite.cpp b/lib/Rewrite/HTMLRewrite.cpp deleted file mode 100644 index 236b98fc2828d..0000000000000 --- a/lib/Rewrite/HTMLRewrite.cpp +++ /dev/null @@ -1,583 +0,0 @@ -//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the HTMLRewriter clas, which is used to translate the -// text of a source file into prettified HTML. -// -//===----------------------------------------------------------------------===// - -#include "clang/Lex/Preprocessor.h" -#include "clang/Rewrite/Rewriter.h" -#include "clang/Rewrite/HTMLRewrite.h" -#include "clang/Lex/TokenConcatenation.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -using namespace clang; - - -/// HighlightRange - Highlight a range in the source code with the specified -/// start/end tags. B/E must be in the same file. This ensures that -/// start/end tags are placed at the start/end of each line if the range is -/// multiline. -void html::HighlightRange(Rewriter &R, SourceLocation B, SourceLocation E, - const char *StartTag, const char *EndTag) { - SourceManager &SM = R.getSourceMgr(); - B = SM.getExpansionLoc(B); - E = SM.getExpansionLoc(E); - FileID FID = SM.getFileID(B); - assert(SM.getFileID(E) == FID && "B/E not in the same file!"); - - unsigned BOffset = SM.getFileOffset(B); - unsigned EOffset = SM.getFileOffset(E); - - // Include the whole end token in the range. - EOffset += Lexer::MeasureTokenLength(E, R.getSourceMgr(), R.getLangOpts()); - - bool Invalid = false; - const char *BufferStart = SM.getBufferData(FID, &Invalid).data(); - if (Invalid) - return; - - HighlightRange(R.getEditBuffer(FID), BOffset, EOffset, - BufferStart, StartTag, EndTag); -} - -/// HighlightRange - This is the same as the above method, but takes -/// decomposed file locations. -void html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E, - const char *BufferStart, - const char *StartTag, const char *EndTag) { - // Insert the tag at the absolute start/end of the range. - RB.InsertTextAfter(B, StartTag); - RB.InsertTextBefore(E, EndTag); - - // Scan the range to see if there is a \r or \n. If so, and if the line is - // not blank, insert tags on that line as well. - bool HadOpenTag = true; - - unsigned LastNonWhiteSpace = B; - for (unsigned i = B; i != E; ++i) { - switch (BufferStart[i]) { - case '\r': - case '\n': - // Okay, we found a newline in the range. If we have an open tag, we need - // to insert a close tag at the first non-whitespace before the newline. - if (HadOpenTag) - RB.InsertTextBefore(LastNonWhiteSpace+1, EndTag); - - // Instead of inserting an open tag immediately after the newline, we - // wait until we see a non-whitespace character. This prevents us from - // inserting tags around blank lines, and also allows the open tag to - // be put *after* whitespace on a non-blank line. - HadOpenTag = false; - break; - case '\0': - case ' ': - case '\t': - case '\f': - case '\v': - // Ignore whitespace. - break; - - default: - // If there is no tag open, do it now. - if (!HadOpenTag) { - RB.InsertTextAfter(i, StartTag); - HadOpenTag = true; - } - - // Remember this character. - LastNonWhiteSpace = i; - break; - } - } -} - -void html::EscapeText(Rewriter &R, FileID FID, - bool EscapeSpaces, bool ReplaceTabs) { - - const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); - const char* C = Buf->getBufferStart(); - const char* FileEnd = Buf->getBufferEnd(); - - assert (C <= FileEnd); - - RewriteBuffer &RB = R.getEditBuffer(FID); - - unsigned ColNo = 0; - for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) { - switch (*C) { - default: ++ColNo; break; - case '\n': - case '\r': - ColNo = 0; - break; - - case ' ': - if (EscapeSpaces) - RB.ReplaceText(FilePos, 1, " "); - ++ColNo; - break; - case '\f': - RB.ReplaceText(FilePos, 1, "


"); - ColNo = 0; - break; - - case '\t': { - if (!ReplaceTabs) - break; - unsigned NumSpaces = 8-(ColNo&7); - if (EscapeSpaces) - RB.ReplaceText(FilePos, 1, - StringRef("     " - "   ", 6*NumSpaces)); - else - RB.ReplaceText(FilePos, 1, StringRef(" ", NumSpaces)); - ColNo += NumSpaces; - break; - } - case '<': - RB.ReplaceText(FilePos, 1, "<"); - ++ColNo; - break; - - case '>': - RB.ReplaceText(FilePos, 1, ">"); - ++ColNo; - break; - - case '&': - RB.ReplaceText(FilePos, 1, "&"); - ++ColNo; - break; - } - } -} - -std::string html::EscapeText(const std::string& s, bool EscapeSpaces, - bool ReplaceTabs) { - - unsigned len = s.size(); - std::string Str; - llvm::raw_string_ostream os(Str); - - for (unsigned i = 0 ; i < len; ++i) { - - char c = s[i]; - switch (c) { - default: - os << c; break; - - case ' ': - if (EscapeSpaces) os << " "; - else os << ' '; - break; - - case '\t': - if (ReplaceTabs) { - if (EscapeSpaces) - for (unsigned i = 0; i < 4; ++i) - os << " "; - else - for (unsigned i = 0; i < 4; ++i) - os << " "; - } - else - os << c; - - break; - - case '<': os << "<"; break; - case '>': os << ">"; break; - case '&': os << "&"; break; - } - } - - return os.str(); -} - -static void AddLineNumber(RewriteBuffer &RB, unsigned LineNo, - unsigned B, unsigned E) { - SmallString<256> Str; - llvm::raw_svector_ostream OS(Str); - - OS << "" - << LineNo << ""; - - if (B == E) { // Handle empty lines. - OS << " "; - RB.InsertTextBefore(B, OS.str()); - } else { - RB.InsertTextBefore(B, OS.str()); - RB.InsertTextBefore(E, ""); - } -} - -void html::AddLineNumbers(Rewriter& R, FileID FID) { - - const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); - const char* FileBeg = Buf->getBufferStart(); - const char* FileEnd = Buf->getBufferEnd(); - const char* C = FileBeg; - RewriteBuffer &RB = R.getEditBuffer(FID); - - assert (C <= FileEnd); - - unsigned LineNo = 0; - unsigned FilePos = 0; - - while (C != FileEnd) { - - ++LineNo; - unsigned LineStartPos = FilePos; - unsigned LineEndPos = FileEnd - FileBeg; - - assert (FilePos <= LineEndPos); - assert (C < FileEnd); - - // Scan until the newline (or end-of-file). - - while (C != FileEnd) { - char c = *C; - ++C; - - if (c == '\n') { - LineEndPos = FilePos++; - break; - } - - ++FilePos; - } - - AddLineNumber(RB, LineNo, LineStartPos, LineEndPos); - } - - // Add one big table tag that surrounds all of the code. - RB.InsertTextBefore(0, "\n"); - RB.InsertTextAfter(FileEnd - FileBeg, "
"); -} - -void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, FileID FID, - const char *title) { - - const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FID); - const char* FileStart = Buf->getBufferStart(); - const char* FileEnd = Buf->getBufferEnd(); - - SourceLocation StartLoc = R.getSourceMgr().getLocForStartOfFile(FID); - SourceLocation EndLoc = StartLoc.getLocWithOffset(FileEnd-FileStart); - - std::string s; - llvm::raw_string_ostream os(s); - os << "\n" // Use HTML 5 doctype - "\n\n"; - - if (title) - os << "" << html::EscapeText(title) << "\n"; - - os << "\n\n"; - - // Generate header - R.InsertTextBefore(StartLoc, os.str()); - // Generate footer - - R.InsertTextAfter(EndLoc, "\n"); -} - -/// SyntaxHighlight - Relex the specified FileID and annotate the HTML with -/// information about keywords, macro expansions etc. This uses the macro -/// table state from the end of the file, so it won't be perfectly perfect, -/// but it will be reasonably close. -void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) { - RewriteBuffer &RB = R.getEditBuffer(FID); - - const SourceManager &SM = PP.getSourceManager(); - const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); - Lexer L(FID, FromFile, SM, PP.getLangOpts()); - const char *BufferStart = L.getBufferStart(); - - // Inform the preprocessor that we want to retain comments as tokens, so we - // can highlight them. - L.SetCommentRetentionState(true); - - // Lex all the tokens in raw mode, to avoid entering #includes or expanding - // macros. - Token Tok; - L.LexFromRawLexer(Tok); - - while (Tok.isNot(tok::eof)) { - // Since we are lexing unexpanded tokens, all tokens are from the main - // FileID. - unsigned TokOffs = SM.getFileOffset(Tok.getLocation()); - unsigned TokLen = Tok.getLength(); - switch (Tok.getKind()) { - default: break; - case tok::identifier: - llvm_unreachable("tok::identifier in raw lexing mode!"); - case tok::raw_identifier: { - // Fill in Result.IdentifierInfo and update the token kind, - // looking up the identifier in the identifier table. - PP.LookUpIdentifierInfo(Tok); - - // If this is a pp-identifier, for a keyword, highlight it as such. - if (Tok.isNot(tok::identifier)) - HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart, - "", ""); - break; - } - case tok::comment: - HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart, - "", ""); - break; - case tok::utf8_string_literal: - // Chop off the u part of u8 prefix - ++TokOffs; - --TokLen; - // FALL THROUGH to chop the 8 - case tok::wide_string_literal: - case tok::utf16_string_literal: - case tok::utf32_string_literal: - // Chop off the L, u, U or 8 prefix - ++TokOffs; - --TokLen; - // FALL THROUGH. - case tok::string_literal: - // FIXME: Exclude the optional ud-suffix from the highlighted range. - HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart, - "", ""); - break; - case tok::hash: { - // If this is a preprocessor directive, all tokens to end of line are too. - if (!Tok.isAtStartOfLine()) - break; - - // Eat all of the tokens until we get to the next one at the start of - // line. - unsigned TokEnd = TokOffs+TokLen; - L.LexFromRawLexer(Tok); - while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) { - TokEnd = SM.getFileOffset(Tok.getLocation())+Tok.getLength(); - L.LexFromRawLexer(Tok); - } - - // Find end of line. This is a hack. - HighlightRange(RB, TokOffs, TokEnd, BufferStart, - "", ""); - - // Don't skip the next token. - continue; - } - } - - L.LexFromRawLexer(Tok); - } -} - -/// HighlightMacros - This uses the macro table state from the end of the -/// file, to re-expand macros and insert (into the HTML) information about the -/// macro expansions. This won't be perfectly perfect, but it will be -/// reasonably close. -void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) { - // Re-lex the raw token stream into a token buffer. - const SourceManager &SM = PP.getSourceManager(); - std::vector TokenStream; - - const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); - Lexer L(FID, FromFile, SM, PP.getLangOpts()); - - // Lex all the tokens in raw mode, to avoid entering #includes or expanding - // macros. - while (1) { - Token Tok; - L.LexFromRawLexer(Tok); - - // If this is a # at the start of a line, discard it from the token stream. - // We don't want the re-preprocess step to see #defines, #includes or other - // preprocessor directives. - if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) - continue; - - // If this is a ## token, change its kind to unknown so that repreprocessing - // it will not produce an error. - if (Tok.is(tok::hashhash)) - Tok.setKind(tok::unknown); - - // If this raw token is an identifier, the raw lexer won't have looked up - // the corresponding identifier info for it. Do this now so that it will be - // macro expanded when we re-preprocess it. - if (Tok.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(Tok); - - TokenStream.push_back(Tok); - - if (Tok.is(tok::eof)) break; - } - - // Temporarily change the diagnostics object so that we ignore any generated - // diagnostics from this pass. - DiagnosticsEngine TmpDiags(PP.getDiagnostics().getDiagnosticIDs(), - new IgnoringDiagConsumer); - - // FIXME: This is a huge hack; we reuse the input preprocessor because we want - // its state, but we aren't actually changing it (we hope). This should really - // construct a copy of the preprocessor. - Preprocessor &TmpPP = const_cast(PP); - DiagnosticsEngine *OldDiags = &TmpPP.getDiagnostics(); - TmpPP.setDiagnostics(TmpDiags); - - // Inform the preprocessor that we don't want comments. - TmpPP.SetCommentRetentionState(false, false); - - // We don't want pragmas either. Although we filtered out #pragma, removing - // _Pragma and __pragma is much harder. - bool PragmasPreviouslyEnabled = TmpPP.getPragmasEnabled(); - TmpPP.setPragmasEnabled(false); - - // Enter the tokens we just lexed. This will cause them to be macro expanded - // but won't enter sub-files (because we removed #'s). - TmpPP.EnterTokenStream(&TokenStream[0], TokenStream.size(), false, false); - - TokenConcatenation ConcatInfo(TmpPP); - - // Lex all the tokens. - Token Tok; - TmpPP.Lex(Tok); - while (Tok.isNot(tok::eof)) { - // Ignore non-macro tokens. - if (!Tok.getLocation().isMacroID()) { - TmpPP.Lex(Tok); - continue; - } - - // Okay, we have the first token of a macro expansion: highlight the - // expansion by inserting a start tag before the macro expansion and - // end tag after it. - std::pair LLoc = - SM.getExpansionRange(Tok.getLocation()); - - // Ignore tokens whose instantiation location was not the main file. - if (SM.getFileID(LLoc.first) != FID) { - TmpPP.Lex(Tok); - continue; - } - - assert(SM.getFileID(LLoc.second) == FID && - "Start and end of expansion must be in the same ultimate file!"); - - std::string Expansion = EscapeText(TmpPP.getSpelling(Tok)); - unsigned LineLen = Expansion.size(); - - Token PrevPrevTok; - Token PrevTok = Tok; - // Okay, eat this token, getting the next one. - TmpPP.Lex(Tok); - - // Skip all the rest of the tokens that are part of this macro - // instantiation. It would be really nice to pop up a window with all the - // spelling of the tokens or something. - while (!Tok.is(tok::eof) && - SM.getExpansionLoc(Tok.getLocation()) == LLoc.first) { - // Insert a newline if the macro expansion is getting large. - if (LineLen > 60) { - Expansion += "
"; - LineLen = 0; - } - - LineLen -= Expansion.size(); - - // If the tokens were already space separated, or if they must be to avoid - // them being implicitly pasted, add a space between them. - if (Tok.hasLeadingSpace() || - ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok)) - Expansion += ' '; - - // Escape any special characters in the token text. - Expansion += EscapeText(TmpPP.getSpelling(Tok)); - LineLen += Expansion.size(); - - PrevPrevTok = PrevTok; - PrevTok = Tok; - TmpPP.Lex(Tok); - } - - - // Insert the expansion as the end tag, so that multi-line macros all get - // highlighted. - Expansion = "" + Expansion + "
"; - - HighlightRange(R, LLoc.first, LLoc.second, - "", Expansion.c_str()); - } - - // Restore the preprocessor's old state. - TmpPP.setDiagnostics(*OldDiags); - TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled); -} diff --git a/lib/Rewrite/InclusionRewriter.cpp b/lib/Rewrite/InclusionRewriter.cpp deleted file mode 100644 index 3dfc3b0089872..0000000000000 --- a/lib/Rewrite/InclusionRewriter.cpp +++ /dev/null @@ -1,361 +0,0 @@ -//===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This code rewrites include invocations into their expansions. This gives you -// a file with all included files merged into it. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/Rewriters.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Frontend/PreprocessorOutputOptions.h" -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace llvm; - -namespace { - -class InclusionRewriter : public PPCallbacks { - /// Information about which #includes were actually performed, - /// created by preprocessor callbacks. - struct FileChange { - SourceLocation From; - FileID Id; - SrcMgr::CharacteristicKind FileType; - FileChange(SourceLocation From) : From(From) { - } - }; - Preprocessor &PP; ///< Used to find inclusion directives. - SourceManager &SM; ///< Used to read and manage source files. - raw_ostream &OS; ///< The destination stream for rewritten contents. - bool ShowLineMarkers; ///< Show #line markers. - bool UseLineDirective; ///< Use of line directives or line markers. - typedef std::map FileChangeMap; - FileChangeMap FileChanges; /// Tracks which files were included where. - /// Used transitively for building up the FileChanges mapping over the - /// various \c PPCallbacks callbacks. - FileChangeMap::iterator LastInsertedFileChange; -public: - InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers); - bool Process(FileID FileId, SrcMgr::CharacteristicKind FileType); -private: - virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, - SrcMgr::CharacteristicKind FileType, - FileID PrevFID); - virtual void FileSkipped(const FileEntry &ParentFile, - const Token &FilenameTok, - SrcMgr::CharacteristicKind FileType); - virtual void InclusionDirective(SourceLocation HashLoc, - const Token &IncludeTok, - StringRef FileName, - bool IsAngled, - const FileEntry *File, - SourceLocation EndLoc, - StringRef SearchPath, - StringRef RelativePath); - void WriteLineInfo(const char *Filename, int Line, - SrcMgr::CharacteristicKind FileType, - StringRef EOL, StringRef Extra = StringRef()); - void OutputContentUpTo(const MemoryBuffer &FromFile, - unsigned &WriteFrom, unsigned WriteTo, - StringRef EOL, int &lines, - bool EnsureNewline = false); - void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, - const MemoryBuffer &FromFile, StringRef EOL, - unsigned &NextToWrite, int &Lines); - const FileChange *FindFileChangeLocation(SourceLocation Loc) const; - StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); -}; - -} // end anonymous namespace - -/// Initializes an InclusionRewriter with a \p PP source and \p OS destination. -InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS, - bool ShowLineMarkers) - : PP(PP), SM(PP.getSourceManager()), OS(OS), - ShowLineMarkers(ShowLineMarkers), - LastInsertedFileChange(FileChanges.end()) { - // If we're in microsoft mode, use normal #line instead of line markers. - UseLineDirective = PP.getLangOpts().MicrosoftExt; -} - -/// Write appropriate line information as either #line directives or GNU line -/// markers depending on what mode we're in, including the \p Filename and -/// \p Line we are located at, using the specified \p EOL line separator, and -/// any \p Extra context specifiers in GNU line directives. -void InclusionRewriter::WriteLineInfo(const char *Filename, int Line, - SrcMgr::CharacteristicKind FileType, - StringRef EOL, StringRef Extra) { - if (!ShowLineMarkers) - return; - if (UseLineDirective) { - OS << "#line" << ' ' << Line << ' ' << '"' << Filename << '"'; - } else { - // Use GNU linemarkers as described here: - // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html - OS << '#' << ' ' << Line << ' ' << '"' << Filename << '"'; - if (!Extra.empty()) - OS << Extra; - if (FileType == SrcMgr::C_System) - // "`3' This indicates that the following text comes from a system header - // file, so certain warnings should be suppressed." - OS << " 3"; - else if (FileType == SrcMgr::C_ExternCSystem) - // as above for `3', plus "`4' This indicates that the following text - // should be treated as being wrapped in an implicit extern "C" block." - OS << " 3 4"; - } - OS << EOL; -} - -/// FileChanged - Whenever the preprocessor enters or exits a #include file -/// it invokes this handler. -void InclusionRewriter::FileChanged(SourceLocation Loc, - FileChangeReason Reason, - SrcMgr::CharacteristicKind NewFileType, - FileID) { - if (Reason != EnterFile) - return; - if (LastInsertedFileChange == FileChanges.end()) - // we didn't reach this file (eg: the main file) via an inclusion directive - return; - LastInsertedFileChange->second.Id = FullSourceLoc(Loc, SM).getFileID(); - LastInsertedFileChange->second.FileType = NewFileType; - LastInsertedFileChange = FileChanges.end(); -} - -/// Called whenever an inclusion is skipped due to canonical header protection -/// macros. -void InclusionRewriter::FileSkipped(const FileEntry &/*ParentFile*/, - const Token &/*FilenameTok*/, - SrcMgr::CharacteristicKind /*FileType*/) { - assert(LastInsertedFileChange != FileChanges.end() && "A file, that wasn't " - "found via an inclusion directive, was skipped"); - FileChanges.erase(LastInsertedFileChange); - LastInsertedFileChange = FileChanges.end(); -} - -/// This should be called whenever the preprocessor encounters include -/// directives. It does not say whether the file has been included, but it -/// provides more information about the directive (hash location instead -/// of location inside the included file). It is assumed that the matching -/// FileChanged() or FileSkipped() is called after this. -void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, - const Token &/*IncludeTok*/, - StringRef /*FileName*/, - bool /*IsAngled*/, - const FileEntry * /*File*/, - SourceLocation /*EndLoc*/, - StringRef /*SearchPath*/, - StringRef /*RelativePath*/) { - assert(LastInsertedFileChange == FileChanges.end() && "Another inclusion " - "directive was found before the previous one was processed"); - std::pair p = FileChanges.insert( - std::make_pair(HashLoc.getRawEncoding(), FileChange(HashLoc))); - assert(p.second && "Unexpected revisitation of the same include directive"); - LastInsertedFileChange = p.first; -} - -/// Simple lookup for a SourceLocation (specifically one denoting the hash in -/// an inclusion directive) in the map of inclusion information, FileChanges. -const InclusionRewriter::FileChange * -InclusionRewriter::FindFileChangeLocation(SourceLocation Loc) const { - FileChangeMap::const_iterator I = FileChanges.find(Loc.getRawEncoding()); - if (I != FileChanges.end()) - return &I->second; - return NULL; -} - -/// Detect the likely line ending style of \p FromFile by examining the first -/// newline found within it. -static StringRef DetectEOL(const MemoryBuffer &FromFile) { - // detect what line endings the file uses, so that added content does not mix - // the style - const char *Pos = strchr(FromFile.getBufferStart(), '\n'); - if (Pos == NULL) - return "\n"; - if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r') - return "\n\r"; - if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r') - return "\r\n"; - return "\n"; -} - -/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at -/// \p WriteTo - 1. -void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile, - unsigned &WriteFrom, unsigned WriteTo, - StringRef EOL, int &Line, - bool EnsureNewline) { - if (WriteTo <= WriteFrom) - return; - OS.write(FromFile.getBufferStart() + WriteFrom, WriteTo - WriteFrom); - // count lines manually, it's faster than getPresumedLoc() - Line += std::count(FromFile.getBufferStart() + WriteFrom, - FromFile.getBufferStart() + WriteTo, '\n'); - if (EnsureNewline) { - char LastChar = FromFile.getBufferStart()[WriteTo - 1]; - if (LastChar != '\n' && LastChar != '\r') - OS << EOL; - } - WriteFrom = WriteTo; -} - -/// Print characters from \p FromFile starting at \p NextToWrite up until the -/// inclusion directive at \p StartToken, then print out the inclusion -/// inclusion directive disabled by a #if directive, updating \p NextToWrite -/// and \p Line to track the number of source lines visited and the progress -/// through the \p FromFile buffer. -void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex, - const Token &StartToken, - const MemoryBuffer &FromFile, - StringRef EOL, - unsigned &NextToWrite, int &Line) { - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(StartToken.getLocation()), EOL, Line); - Token DirectiveToken; - do { - DirectiveLex.LexFromRawLexer(DirectiveToken); - } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof)); - OS << "#if 0 /* expanded by -frewrite-includes */" << EOL; - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(DirectiveToken.getLocation()) + DirectiveToken.getLength(), - EOL, Line); - OS << "#endif /* expanded by -frewrite-includes */" << EOL; -} - -/// Find the next identifier in the pragma directive specified by \p RawToken. -StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, - Token &RawToken) { - RawLex.LexFromRawLexer(RawToken); - if (RawToken.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(RawToken); - if (RawToken.is(tok::identifier)) - return RawToken.getIdentifierInfo()->getName(); - return StringRef(); -} - -/// Use a raw lexer to analyze \p FileId, inccrementally copying parts of it -/// and including content of included files recursively. -bool InclusionRewriter::Process(FileID FileId, - SrcMgr::CharacteristicKind FileType) -{ - bool Invalid; - const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid); - if (Invalid) // invalid inclusion - return true; - const char *FileName = FromFile.getBufferIdentifier(); - Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts()); - RawLex.SetCommentRetentionState(false); - - StringRef EOL = DetectEOL(FromFile); - - // Per the GNU docs: "1" indicates the start of a new file. - WriteLineInfo(FileName, 1, FileType, EOL, " 1"); - - if (SM.getFileIDSize(FileId) == 0) - return true; - - // The next byte to be copied from the source file - unsigned NextToWrite = 0; - int Line = 1; // The current input file line number. - - Token RawToken; - RawLex.LexFromRawLexer(RawToken); - - // TODO: Consider adding a switch that strips possibly unimportant content, - // such as comments, to reduce the size of repro files. - while (RawToken.isNot(tok::eof)) { - if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) { - RawLex.setParsingPreprocessorDirective(true); - Token HashToken = RawToken; - RawLex.LexFromRawLexer(RawToken); - if (RawToken.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(RawToken); - if (RawToken.is(tok::identifier)) { - switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { - case tok::pp_include: - case tok::pp_include_next: - case tok::pp_import: { - CommentOutDirective(RawLex, HashToken, FromFile, EOL, NextToWrite, - Line); - if (const FileChange *Change = FindFileChangeLocation( - HashToken.getLocation())) { - // now include and recursively process the file - if (Process(Change->Id, Change->FileType)) - // and set lineinfo back to this file, if the nested one was - // actually included - // `2' indicates returning to a file (after having included - // another file. - WriteLineInfo(FileName, Line, FileType, EOL, " 2"); - } else - // fix up lineinfo (since commented out directive changed line - // numbers) for inclusions that were skipped due to header guards - WriteLineInfo(FileName, Line, FileType, EOL); - break; - } - case tok::pp_pragma: { - StringRef Identifier = NextIdentifierName(RawLex, RawToken); - if (Identifier == "clang" || Identifier == "GCC") { - if (NextIdentifierName(RawLex, RawToken) == "system_header") { - // keep the directive in, commented out - CommentOutDirective(RawLex, HashToken, FromFile, EOL, - NextToWrite, Line); - // update our own type - FileType = SM.getFileCharacteristic(RawToken.getLocation()); - WriteLineInfo(FileName, Line, FileType, EOL); - } - } else if (Identifier == "once") { - // keep the directive in, commented out - CommentOutDirective(RawLex, HashToken, FromFile, EOL, - NextToWrite, Line); - WriteLineInfo(FileName, Line, FileType, EOL); - } - break; - } - default: - break; - } - } - RawLex.setParsingPreprocessorDirective(false); - } - RawLex.LexFromRawLexer(RawToken); - } - OutputContentUpTo(FromFile, NextToWrite, - SM.getFileOffset(SM.getLocForEndOfFile(FileId)) + 1, EOL, Line, - /*EnsureNewline*/true); - return true; -} - -/// InclusionRewriterInInput - Implement -frewrite-includes mode. -void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, - const PreprocessorOutputOptions &Opts) { - SourceManager &SM = PP.getSourceManager(); - InclusionRewriter *Rewrite = new InclusionRewriter(PP, *OS, - Opts.ShowLineMarkers); - PP.addPPCallbacks(Rewrite); - - // First let the preprocessor process the entire file and call callbacks. - // Callbacks will record which #include's were actually performed. - PP.EnterMainSourceFile(); - Token Tok; - // Only preprocessor directives matter here, so disable macro expansion - // everywhere else as an optimization. - // TODO: It would be even faster if the preprocessor could be switched - // to a mode where it would parse only preprocessor directives and comments, - // nothing else matters for parsing or processing. - PP.SetMacroExpansionOnlyInDirectives(); - do { - PP.Lex(Tok); - } while (Tok.isNot(tok::eof)); - Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User); - OS->flush(); -} diff --git a/lib/Rewrite/Makefile b/lib/Rewrite/Makefile index 5fef9b2c0d386..0be84d406405b 100644 --- a/lib/Rewrite/Makefile +++ b/lib/Rewrite/Makefile @@ -1,4 +1,4 @@ -##===- clang/lib/Rewrite/Makefile --------------------------*- Makefile -*-===## +##===- clang/lib/StaticAnalyzer/Makefile -------------------*- Makefile -*-===## # # The LLVM Compiler Infrastructure # @@ -6,13 +6,9 @@ # License. See LICENSE.TXT for details. # ##===----------------------------------------------------------------------===## -# -# This implements code transformation / rewriting facilities. -# -##===----------------------------------------------------------------------===## CLANG_LEVEL := ../.. -LIBRARYNAME := clangRewrite +DIRS := Frontend +PARALLEL_DIRS := Core include $(CLANG_LEVEL)/Makefile - diff --git a/lib/Rewrite/RewriteMacros.cpp b/lib/Rewrite/RewriteMacros.cpp deleted file mode 100644 index 3fa0bdb745342..0000000000000 --- a/lib/Rewrite/RewriteMacros.cpp +++ /dev/null @@ -1,217 +0,0 @@ -//===--- RewriteMacros.cpp - Rewrite macros into their expansions ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This code rewrites macro invocations into their expansions. This gives you -// a macro expanded file that retains comments and #includes. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/Rewriters.h" -#include "clang/Rewrite/Rewriter.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Basic/SourceManager.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Path.h" -#include "llvm/ADT/OwningPtr.h" -#include - -using namespace clang; - -/// isSameToken - Return true if the two specified tokens start have the same -/// content. -static bool isSameToken(Token &RawTok, Token &PPTok) { - // If two tokens have the same kind and the same identifier info, they are - // obviously the same. - if (PPTok.getKind() == RawTok.getKind() && - PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) - return true; - - // Otherwise, if they are different but have the same identifier info, they - // are also considered to be the same. This allows keywords and raw lexed - // identifiers with the same name to be treated the same. - if (PPTok.getIdentifierInfo() && - PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) - return true; - - return false; -} - - -/// GetNextRawTok - Return the next raw token in the stream, skipping over -/// comments if ReturnComment is false. -static const Token &GetNextRawTok(const std::vector &RawTokens, - unsigned &CurTok, bool ReturnComment) { - assert(CurTok < RawTokens.size() && "Overran eof!"); - - // If the client doesn't want comments and we have one, skip it. - if (!ReturnComment && RawTokens[CurTok].is(tok::comment)) - ++CurTok; - - return RawTokens[CurTok++]; -} - - -/// LexRawTokensFromMainFile - Lets all the raw tokens from the main file into -/// the specified vector. -static void LexRawTokensFromMainFile(Preprocessor &PP, - std::vector &RawTokens) { - SourceManager &SM = PP.getSourceManager(); - - // Create a lexer to lex all the tokens of the main file in raw mode. Even - // though it is in raw mode, it will not return comments. - const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); - Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts()); - - // Switch on comment lexing because we really do want them. - RawLex.SetCommentRetentionState(true); - - Token RawTok; - do { - RawLex.LexFromRawLexer(RawTok); - - // If we have an identifier with no identifier info for our raw token, look - // up the indentifier info. This is important for equality comparison of - // identifier tokens. - if (RawTok.is(tok::raw_identifier)) - PP.LookUpIdentifierInfo(RawTok); - - RawTokens.push_back(RawTok); - } while (RawTok.isNot(tok::eof)); -} - - -/// RewriteMacrosInInput - Implement -rewrite-macros mode. -void clang::RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS) { - SourceManager &SM = PP.getSourceManager(); - - Rewriter Rewrite; - Rewrite.setSourceMgr(SM, PP.getLangOpts()); - RewriteBuffer &RB = Rewrite.getEditBuffer(SM.getMainFileID()); - - std::vector RawTokens; - LexRawTokensFromMainFile(PP, RawTokens); - unsigned CurRawTok = 0; - Token RawTok = GetNextRawTok(RawTokens, CurRawTok, false); - - - // Get the first preprocessing token. - PP.EnterMainSourceFile(); - Token PPTok; - PP.Lex(PPTok); - - // Preprocess the input file in parallel with raw lexing the main file. Ignore - // all tokens that are preprocessed from a file other than the main file (e.g. - // a header). If we see tokens that are in the preprocessed file but not the - // lexed file, we have a macro expansion. If we see tokens in the lexed file - // that aren't in the preprocessed view, we have macros that expand to no - // tokens, or macro arguments etc. - while (RawTok.isNot(tok::eof) || PPTok.isNot(tok::eof)) { - SourceLocation PPLoc = SM.getExpansionLoc(PPTok.getLocation()); - - // If PPTok is from a different source file, ignore it. - if (!SM.isFromMainFile(PPLoc)) { - PP.Lex(PPTok); - continue; - } - - // If the raw file hits a preprocessor directive, they will be extra tokens - // in the raw file that don't exist in the preprocsesed file. However, we - // choose to preserve them in the output file and otherwise handle them - // specially. - if (RawTok.is(tok::hash) && RawTok.isAtStartOfLine()) { - // If this is a #warning directive or #pragma mark (GNU extensions), - // comment the line out. - if (RawTokens[CurRawTok].is(tok::identifier)) { - const IdentifierInfo *II = RawTokens[CurRawTok].getIdentifierInfo(); - if (II->getName() == "warning") { - // Comment out #warning. - RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); - } else if (II->getName() == "pragma" && - RawTokens[CurRawTok+1].is(tok::identifier) && - (RawTokens[CurRawTok+1].getIdentifierInfo()->getName() == - "mark")) { - // Comment out #pragma mark. - RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); - } - } - - // Otherwise, if this is a #include or some other directive, just leave it - // in the file by skipping over the line. - RawTok = GetNextRawTok(RawTokens, CurRawTok, false); - while (!RawTok.isAtStartOfLine() && RawTok.isNot(tok::eof)) - RawTok = GetNextRawTok(RawTokens, CurRawTok, false); - continue; - } - - // Okay, both tokens are from the same file. Get their offsets from the - // start of the file. - unsigned PPOffs = SM.getFileOffset(PPLoc); - unsigned RawOffs = SM.getFileOffset(RawTok.getLocation()); - - // If the offsets are the same and the token kind is the same, ignore them. - if (PPOffs == RawOffs && isSameToken(RawTok, PPTok)) { - RawTok = GetNextRawTok(RawTokens, CurRawTok, false); - PP.Lex(PPTok); - continue; - } - - // If the PP token is farther along than the raw token, something was - // deleted. Comment out the raw token. - if (RawOffs <= PPOffs) { - // Comment out a whole run of tokens instead of bracketing each one with - // comments. Add a leading space if RawTok didn't have one. - bool HasSpace = RawTok.hasLeadingSpace(); - RB.InsertTextAfter(RawOffs, &" /*"[HasSpace]); - unsigned EndPos; - - do { - EndPos = RawOffs+RawTok.getLength(); - - RawTok = GetNextRawTok(RawTokens, CurRawTok, true); - RawOffs = SM.getFileOffset(RawTok.getLocation()); - - if (RawTok.is(tok::comment)) { - // Skip past the comment. - RawTok = GetNextRawTok(RawTokens, CurRawTok, false); - break; - } - - } while (RawOffs <= PPOffs && !RawTok.isAtStartOfLine() && - (PPOffs != RawOffs || !isSameToken(RawTok, PPTok))); - - RB.InsertTextBefore(EndPos, "*/"); - continue; - } - - // Otherwise, there was a replacement an expansion. Insert the new token - // in the output buffer. Insert the whole run of new tokens at once to get - // them in the right order. - unsigned InsertPos = PPOffs; - std::string Expansion; - while (PPOffs < RawOffs) { - Expansion += ' ' + PP.getSpelling(PPTok); - PP.Lex(PPTok); - PPLoc = SM.getExpansionLoc(PPTok.getLocation()); - PPOffs = SM.getFileOffset(PPLoc); - } - Expansion += ' '; - RB.InsertTextBefore(InsertPos, Expansion); - } - - // Get the buffer corresponding to MainFileID. If we haven't changed it, then - // we are done. - if (const RewriteBuffer *RewriteBuf = - Rewrite.getRewriteBufferFor(SM.getMainFileID())) { - //printf("Changed:\n"); - *OS << std::string(RewriteBuf->begin(), RewriteBuf->end()); - } else { - fprintf(stderr, "No changes\n"); - } - OS->flush(); -} diff --git a/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp deleted file mode 100644 index dcd003f501607..0000000000000 --- a/lib/Rewrite/RewriteModernObjC.cpp +++ /dev/null @@ -1,7543 +0,0 @@ -//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Hacks and fun related to the code rewriter. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/ASTConsumers.h" -#include "clang/Rewrite/Rewriter.h" -#include "clang/AST/AST.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/ParentMap.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Basic/IdentifierTable.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Lex/Lexer.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/DenseSet.h" - -using namespace clang; -using llvm::utostr; - -namespace { - class RewriteModernObjC : public ASTConsumer { - protected: - - enum { - BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), - block, ... */ - BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ - BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the - __block variable */ - BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy - helpers */ - BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose - support routines */ - BLOCK_BYREF_CURRENT_MAX = 256 - }; - - enum { - BLOCK_NEEDS_FREE = (1 << 24), - BLOCK_HAS_COPY_DISPOSE = (1 << 25), - BLOCK_HAS_CXX_OBJ = (1 << 26), - BLOCK_IS_GC = (1 << 27), - BLOCK_IS_GLOBAL = (1 << 28), - BLOCK_HAS_DESCRIPTOR = (1 << 29) - }; - static const int OBJC_ABI_VERSION = 7; - - Rewriter Rewrite; - DiagnosticsEngine &Diags; - const LangOptions &LangOpts; - ASTContext *Context; - SourceManager *SM; - TranslationUnitDecl *TUDecl; - FileID MainFileID; - const char *MainFileStart, *MainFileEnd; - Stmt *CurrentBody; - ParentMap *PropParentMap; // created lazily. - std::string InFileName; - raw_ostream* OutFile; - std::string Preamble; - - TypeDecl *ProtocolTypeDecl; - VarDecl *GlobalVarDecl; - Expr *GlobalConstructionExp; - unsigned RewriteFailedDiag; - unsigned GlobalBlockRewriteFailedDiag; - // ObjC string constant support. - unsigned NumObjCStringLiterals; - VarDecl *ConstantStringClassReference; - RecordDecl *NSStringRecord; - - // ObjC foreach break/continue generation support. - int BcLabelCount; - - unsigned TryFinallyContainsReturnDiag; - // Needed for super. - ObjCMethodDecl *CurMethodDef; - RecordDecl *SuperStructDecl; - RecordDecl *ConstantStringDecl; - - FunctionDecl *MsgSendFunctionDecl; - FunctionDecl *MsgSendSuperFunctionDecl; - FunctionDecl *MsgSendStretFunctionDecl; - FunctionDecl *MsgSendSuperStretFunctionDecl; - FunctionDecl *MsgSendFpretFunctionDecl; - FunctionDecl *GetClassFunctionDecl; - FunctionDecl *GetMetaClassFunctionDecl; - FunctionDecl *GetSuperClassFunctionDecl; - FunctionDecl *SelGetUidFunctionDecl; - FunctionDecl *CFStringFunctionDecl; - FunctionDecl *SuperContructorFunctionDecl; - FunctionDecl *CurFunctionDef; - - /* Misc. containers needed for meta-data rewrite. */ - SmallVector ClassImplementation; - SmallVector CategoryImplementation; - llvm::SmallPtrSet ObjCSynthesizedStructs; - llvm::SmallPtrSet ObjCSynthesizedProtocols; - llvm::SmallPtrSet ObjCWrittenInterfaces; - llvm::SmallPtrSet GlobalDefinedTags; - SmallVector ObjCInterfacesSeen; - /// DefinedNonLazyClasses - List of defined "non-lazy" classes. - SmallVector DefinedNonLazyClasses; - - /// DefinedNonLazyCategories - List of defined "non-lazy" categories. - llvm::SmallVector DefinedNonLazyCategories; - - SmallVector Stmts; - SmallVector ObjCBcLabelNo; - // Remember all the @protocol() expressions. - llvm::SmallPtrSet ProtocolExprDecls; - - llvm::DenseSet CopyDestroyCache; - - // Block expressions. - SmallVector Blocks; - SmallVector InnerDeclRefsCount; - SmallVector InnerDeclRefs; - - SmallVector BlockDeclRefs; - - // Block related declarations. - SmallVector BlockByCopyDecls; - llvm::SmallPtrSet BlockByCopyDeclsPtrSet; - SmallVector BlockByRefDecls; - llvm::SmallPtrSet BlockByRefDeclsPtrSet; - llvm::DenseMap BlockByRefDeclNo; - llvm::SmallPtrSet ImportedBlockDecls; - llvm::SmallPtrSet ImportedLocalExternalDecls; - - llvm::DenseMap RewrittenBlockExprs; - llvm::DenseMap > ReferencedIvars; - - // This maps an original source AST to it's rewritten form. This allows - // us to avoid rewriting the same node twice (which is very uncommon). - // This is needed to support some of the exotic property rewriting. - llvm::DenseMap ReplacedNodes; - - // Needed for header files being rewritten - bool IsHeader; - bool SilenceRewriteMacroWarning; - bool objc_impl_method; - - bool DisableReplaceStmt; - class DisableReplaceStmtScope { - RewriteModernObjC &R; - bool SavedValue; - - public: - DisableReplaceStmtScope(RewriteModernObjC &R) - : R(R), SavedValue(R.DisableReplaceStmt) { - R.DisableReplaceStmt = true; - } - ~DisableReplaceStmtScope() { - R.DisableReplaceStmt = SavedValue; - } - }; - void InitializeCommon(ASTContext &context); - - public: - llvm::DenseMap MethodInternalNames; - // Top Level Driver code. - virtual bool HandleTopLevelDecl(DeclGroupRef D) { - for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { - if (ObjCInterfaceDecl *Class = dyn_cast(*I)) { - if (!Class->isThisDeclarationADefinition()) { - RewriteForwardClassDecl(D); - break; - } else { - // Keep track of all interface declarations seen. - ObjCInterfacesSeen.push_back(Class); - break; - } - } - - if (ObjCProtocolDecl *Proto = dyn_cast(*I)) { - if (!Proto->isThisDeclarationADefinition()) { - RewriteForwardProtocolDecl(D); - break; - } - } - - HandleTopLevelSingleDecl(*I); - } - return true; - } - void HandleTopLevelSingleDecl(Decl *D); - void HandleDeclInMainFile(Decl *D); - RewriteModernObjC(std::string inFile, raw_ostream *OS, - DiagnosticsEngine &D, const LangOptions &LOpts, - bool silenceMacroWarn); - - ~RewriteModernObjC() {} - - virtual void HandleTranslationUnit(ASTContext &C); - - void ReplaceStmt(Stmt *Old, Stmt *New) { - Stmt *ReplacingStmt = ReplacedNodes[Old]; - - if (ReplacingStmt) - return; // We can't rewrite the same node twice. - - if (DisableReplaceStmt) - return; - - // If replacement succeeded or warning disabled return with no warning. - if (!Rewrite.ReplaceStmt(Old, New)) { - ReplacedNodes[Old] = New; - return; - } - if (SilenceRewriteMacroWarning) - return; - Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) - << Old->getSourceRange(); - } - - void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { - if (DisableReplaceStmt) - return; - - // Measure the old text. - int Size = Rewrite.getRangeSize(SrcRange); - if (Size == -1) { - Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) - << Old->getSourceRange(); - return; - } - // Get the new text. - std::string SStr; - llvm::raw_string_ostream S(SStr); - New->printPretty(S, 0, PrintingPolicy(LangOpts)); - const std::string &Str = S.str(); - - // If replacement succeeded or warning disabled return with no warning. - if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { - ReplacedNodes[Old] = New; - return; - } - if (SilenceRewriteMacroWarning) - return; - Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) - << Old->getSourceRange(); - } - - void InsertText(SourceLocation Loc, StringRef Str, - bool InsertAfter = true) { - // If insertion succeeded or warning disabled return with no warning. - if (!Rewrite.InsertText(Loc, Str, InsertAfter) || - SilenceRewriteMacroWarning) - return; - - Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); - } - - void ReplaceText(SourceLocation Start, unsigned OrigLength, - StringRef Str) { - // If removal succeeded or warning disabled return with no warning. - if (!Rewrite.ReplaceText(Start, OrigLength, Str) || - SilenceRewriteMacroWarning) - return; - - Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); - } - - // Syntactic Rewriting. - void RewriteRecordBody(RecordDecl *RD); - void RewriteInclude(); - void RewriteForwardClassDecl(DeclGroupRef D); - void RewriteForwardClassDecl(const llvm::SmallVector &DG); - void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, - const std::string &typedefString); - void RewriteImplementations(); - void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, - ObjCImplementationDecl *IMD, - ObjCCategoryImplDecl *CID); - void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); - void RewriteImplementationDecl(Decl *Dcl); - void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, - ObjCMethodDecl *MDecl, std::string &ResultStr); - void RewriteTypeIntoString(QualType T, std::string &ResultStr, - const FunctionType *&FPRetType); - void RewriteByRefString(std::string &ResultStr, const std::string &Name, - ValueDecl *VD, bool def=false); - void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); - void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); - void RewriteForwardProtocolDecl(DeclGroupRef D); - void RewriteForwardProtocolDecl(const llvm::SmallVector &DG); - void RewriteMethodDeclaration(ObjCMethodDecl *Method); - void RewriteProperty(ObjCPropertyDecl *prop); - void RewriteFunctionDecl(FunctionDecl *FD); - void RewriteBlockPointerType(std::string& Str, QualType Type); - void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); - void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); - void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); - void RewriteTypeOfDecl(VarDecl *VD); - void RewriteObjCQualifiedInterfaceTypes(Expr *E); - - std::string getIvarAccessString(ObjCIvarDecl *D); - - // Expression Rewriting. - Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); - Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); - Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); - Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); - Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); - Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); - Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); - Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp); - Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp); - Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp); - Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp); - Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); - Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); - Stmt *RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); - Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); - Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); - Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, - SourceLocation OrigEnd); - Stmt *RewriteBreakStmt(BreakStmt *S); - Stmt *RewriteContinueStmt(ContinueStmt *S); - void RewriteCastExpr(CStyleCastExpr *CE); - void RewriteImplicitCastObjCExpr(CastExpr *IE); - void RewriteLinkageSpec(LinkageSpecDecl *LSD); - - // Block rewriting. - void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); - - // Block specific rewrite rules. - void RewriteBlockPointerDecl(NamedDecl *VD); - void RewriteByRefVar(VarDecl *VD, bool firstDecl, bool lastDecl); - Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); - Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); - void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); - - void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, - std::string &Result); - - void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result); - bool IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, TagDecl *Tag, - bool &IsNamedDefinition); - void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, - std::string &Result); - - bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result); - - void RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, - std::string &Result); - - virtual void Initialize(ASTContext &context); - - // Misc. AST transformation routines. Sometimes they end up calling - // rewriting routines on the new ASTs. - CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, - Expr **args, unsigned nargs, - SourceLocation StartLoc=SourceLocation(), - SourceLocation EndLoc=SourceLocation()); - - Expr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, - QualType msgSendType, - QualType returnType, - SmallVectorImpl &ArgTypes, - SmallVectorImpl &MsgExprs, - ObjCMethodDecl *Method); - - Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, - SourceLocation StartLoc=SourceLocation(), - SourceLocation EndLoc=SourceLocation()); - - void SynthCountByEnumWithState(std::string &buf); - void SynthMsgSendFunctionDecl(); - void SynthMsgSendSuperFunctionDecl(); - void SynthMsgSendStretFunctionDecl(); - void SynthMsgSendFpretFunctionDecl(); - void SynthMsgSendSuperStretFunctionDecl(); - void SynthGetClassFunctionDecl(); - void SynthGetMetaClassFunctionDecl(); - void SynthGetSuperClassFunctionDecl(); - void SynthSelGetUidFunctionDecl(); - void SynthSuperContructorFunctionDecl(); - - // Rewriting metadata - template - void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, - MethodIterator MethodEnd, - bool IsInstanceMethod, - StringRef prefix, - StringRef ClassName, - std::string &Result); - void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, - std::string &Result); - void RewriteObjCProtocolListMetaData( - const ObjCList &Prots, - StringRef prefix, StringRef ClassName, std::string &Result); - void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, - std::string &Result); - void RewriteClassSetupInitHook(std::string &Result); - - void RewriteMetaDataIntoBuffer(std::string &Result); - void WriteImageInfo(std::string &Result); - void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, - std::string &Result); - void RewriteCategorySetupInitHook(std::string &Result); - - // Rewriting ivar - void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, - std::string &Result); - Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); - - - std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); - std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, - StringRef funcName, std::string Tag); - std::string SynthesizeBlockFunc(BlockExpr *CE, int i, - StringRef funcName, std::string Tag); - std::string SynthesizeBlockImpl(BlockExpr *CE, - std::string Tag, std::string Desc); - std::string SynthesizeBlockDescriptor(std::string DescTag, - std::string ImplTag, - int i, StringRef funcName, - unsigned hasCopy); - Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); - void SynthesizeBlockLiterals(SourceLocation FunLocStart, - StringRef FunName); - FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); - Stmt *SynthBlockInitExpr(BlockExpr *Exp, - const SmallVector &InnerBlockDeclRefs); - - // Misc. helper routines. - QualType getProtocolType(); - void WarnAboutReturnGotoStmts(Stmt *S); - void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); - void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); - void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); - - bool IsDeclStmtInForeachHeader(DeclStmt *DS); - void CollectBlockDeclRefInfo(BlockExpr *Exp); - void GetBlockDeclRefExprs(Stmt *S); - void GetInnerBlockDeclRefExprs(Stmt *S, - SmallVector &InnerBlockDeclRefs, - llvm::SmallPtrSet &InnerContexts); - - // We avoid calling Type::isBlockPointerType(), since it operates on the - // canonical type. We only care if the top-level type is a closure pointer. - bool isTopLevelBlockPointerType(QualType T) { - return isa(T); - } - - /// convertBlockPointerToFunctionPointer - Converts a block-pointer type - /// to a function pointer type and upon success, returns true; false - /// otherwise. - bool convertBlockPointerToFunctionPointer(QualType &T) { - if (isTopLevelBlockPointerType(T)) { - const BlockPointerType *BPT = T->getAs(); - T = Context->getPointerType(BPT->getPointeeType()); - return true; - } - return false; - } - - bool convertObjCTypeToCStyleType(QualType &T); - - bool needToScanForQualifiers(QualType T); - QualType getSuperStructType(); - QualType getConstantStringStructType(); - QualType convertFunctionTypeOfBlocks(const FunctionType *FT); - bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); - - void convertToUnqualifiedObjCType(QualType &T) { - if (T->isObjCQualifiedIdType()) { - bool isConst = T.isConstQualified(); - T = isConst ? Context->getObjCIdType().withConst() - : Context->getObjCIdType(); - } - else if (T->isObjCQualifiedClassType()) - T = Context->getObjCClassType(); - else if (T->isObjCObjectPointerType() && - T->getPointeeType()->isObjCQualifiedInterfaceType()) { - if (const ObjCObjectPointerType * OBJPT = - T->getAsObjCInterfacePointerType()) { - const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); - T = QualType(IFaceT, 0); - T = Context->getPointerType(T); - } - } - } - - // FIXME: This predicate seems like it would be useful to add to ASTContext. - bool isObjCType(QualType T) { - if (!LangOpts.ObjC1 && !LangOpts.ObjC2) - return false; - - QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); - - if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || - OCT == Context->getCanonicalType(Context->getObjCClassType())) - return true; - - if (const PointerType *PT = OCT->getAs()) { - if (isa(PT->getPointeeType()) || - PT->getPointeeType()->isObjCQualifiedIdType()) - return true; - } - return false; - } - bool PointerTypeTakesAnyBlockArguments(QualType QT); - bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); - void GetExtentOfArgList(const char *Name, const char *&LParen, - const char *&RParen); - - void QuoteDoublequotes(std::string &From, std::string &To) { - for (unsigned i = 0; i < From.length(); i++) { - if (From[i] == '"') - To += "\\\""; - else - To += From[i]; - } - } - - QualType getSimpleFunctionType(QualType result, - const QualType *args, - unsigned numArgs, - bool variadic = false) { - if (result == Context->getObjCInstanceType()) - result = Context->getObjCIdType(); - FunctionProtoType::ExtProtoInfo fpi; - fpi.Variadic = variadic; - return Context->getFunctionType(result, args, numArgs, fpi); - } - - // Helper function: create a CStyleCastExpr with trivial type source info. - CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, - CastKind Kind, Expr *E) { - TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); - return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, 0, TInfo, - SourceLocation(), SourceLocation()); - } - - bool ImplementationIsNonLazy(const ObjCImplDecl *OD) const { - IdentifierInfo* II = &Context->Idents.get("load"); - Selector LoadSel = Context->Selectors.getSelector(0, &II); - return OD->getClassMethod(LoadSel) != 0; - } - }; - -} - -void RewriteModernObjC::RewriteBlocksInFunctionProtoType(QualType funcType, - NamedDecl *D) { - if (const FunctionProtoType *fproto - = dyn_cast(funcType.IgnoreParens())) { - for (FunctionProtoType::arg_type_iterator I = fproto->arg_type_begin(), - E = fproto->arg_type_end(); I && (I != E); ++I) - if (isTopLevelBlockPointerType(*I)) { - // All the args are checked/rewritten. Don't call twice! - RewriteBlockPointerDecl(D); - break; - } - } -} - -void RewriteModernObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { - const PointerType *PT = funcType->getAs(); - if (PT && PointerTypeTakesAnyBlockArguments(funcType)) - RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); -} - -static bool IsHeaderFile(const std::string &Filename) { - std::string::size_type DotPos = Filename.rfind('.'); - - if (DotPos == std::string::npos) { - // no file extension - return false; - } - - std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); - // C header: .h - // C++ header: .hh or .H; - return Ext == "h" || Ext == "hh" || Ext == "H"; -} - -RewriteModernObjC::RewriteModernObjC(std::string inFile, raw_ostream* OS, - DiagnosticsEngine &D, const LangOptions &LOpts, - bool silenceMacroWarn) - : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), - SilenceRewriteMacroWarning(silenceMacroWarn) { - IsHeader = IsHeaderFile(inFile); - RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, - "rewriting sub-expression within a macro (may not be correct)"); - // FIXME. This should be an error. But if block is not called, it is OK. And it - // may break including some headers. - GlobalBlockRewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, - "rewriting block literal declared in global scope is not implemented"); - - TryFinallyContainsReturnDiag = Diags.getCustomDiagID( - DiagnosticsEngine::Warning, - "rewriter doesn't support user-specified control flow semantics " - "for @try/@finally (code may not execute properly)"); -} - -ASTConsumer *clang::CreateModernObjCRewriter(const std::string& InFile, - raw_ostream* OS, - DiagnosticsEngine &Diags, - const LangOptions &LOpts, - bool SilenceRewriteMacroWarning) { - return new RewriteModernObjC(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning); -} - -void RewriteModernObjC::InitializeCommon(ASTContext &context) { - Context = &context; - SM = &Context->getSourceManager(); - TUDecl = Context->getTranslationUnitDecl(); - MsgSendFunctionDecl = 0; - MsgSendSuperFunctionDecl = 0; - MsgSendStretFunctionDecl = 0; - MsgSendSuperStretFunctionDecl = 0; - MsgSendFpretFunctionDecl = 0; - GetClassFunctionDecl = 0; - GetMetaClassFunctionDecl = 0; - GetSuperClassFunctionDecl = 0; - SelGetUidFunctionDecl = 0; - CFStringFunctionDecl = 0; - ConstantStringClassReference = 0; - NSStringRecord = 0; - CurMethodDef = 0; - CurFunctionDef = 0; - GlobalVarDecl = 0; - GlobalConstructionExp = 0; - SuperStructDecl = 0; - ProtocolTypeDecl = 0; - ConstantStringDecl = 0; - BcLabelCount = 0; - SuperContructorFunctionDecl = 0; - NumObjCStringLiterals = 0; - PropParentMap = 0; - CurrentBody = 0; - DisableReplaceStmt = false; - objc_impl_method = false; - - // Get the ID and start/end of the main file. - MainFileID = SM->getMainFileID(); - const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); - MainFileStart = MainBuf->getBufferStart(); - MainFileEnd = MainBuf->getBufferEnd(); - - Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); -} - -//===----------------------------------------------------------------------===// -// Top Level Driver Code -//===----------------------------------------------------------------------===// - -void RewriteModernObjC::HandleTopLevelSingleDecl(Decl *D) { - if (Diags.hasErrorOccurred()) - return; - - // Two cases: either the decl could be in the main file, or it could be in a - // #included file. If the former, rewrite it now. If the later, check to see - // if we rewrote the #include/#import. - SourceLocation Loc = D->getLocation(); - Loc = SM->getExpansionLoc(Loc); - - // If this is for a builtin, ignore it. - if (Loc.isInvalid()) return; - - // Look for built-in declarations that we need to refer during the rewrite. - if (FunctionDecl *FD = dyn_cast(D)) { - RewriteFunctionDecl(FD); - } else if (VarDecl *FVD = dyn_cast(D)) { - // declared in - if (FVD->getName() == "_NSConstantStringClassReference") { - ConstantStringClassReference = FVD; - return; - } - } else if (ObjCCategoryDecl *CD = dyn_cast(D)) { - RewriteCategoryDecl(CD); - } else if (ObjCProtocolDecl *PD = dyn_cast(D)) { - if (PD->isThisDeclarationADefinition()) - RewriteProtocolDecl(PD); - } else if (LinkageSpecDecl *LSD = dyn_cast(D)) { - // FIXME. This will not work in all situations and leaving it out - // is harmless. - // RewriteLinkageSpec(LSD); - - // Recurse into linkage specifications - for (DeclContext::decl_iterator DI = LSD->decls_begin(), - DIEnd = LSD->decls_end(); - DI != DIEnd; ) { - if (ObjCInterfaceDecl *IFace = dyn_cast((*DI))) { - if (!IFace->isThisDeclarationADefinition()) { - SmallVector DG; - SourceLocation StartLoc = IFace->getLocStart(); - do { - if (isa(*DI) && - !cast(*DI)->isThisDeclarationADefinition() && - StartLoc == (*DI)->getLocStart()) - DG.push_back(*DI); - else - break; - - ++DI; - } while (DI != DIEnd); - RewriteForwardClassDecl(DG); - continue; - } - else { - // Keep track of all interface declarations seen. - ObjCInterfacesSeen.push_back(IFace); - ++DI; - continue; - } - } - - if (ObjCProtocolDecl *Proto = dyn_cast((*DI))) { - if (!Proto->isThisDeclarationADefinition()) { - SmallVector DG; - SourceLocation StartLoc = Proto->getLocStart(); - do { - if (isa(*DI) && - !cast(*DI)->isThisDeclarationADefinition() && - StartLoc == (*DI)->getLocStart()) - DG.push_back(*DI); - else - break; - - ++DI; - } while (DI != DIEnd); - RewriteForwardProtocolDecl(DG); - continue; - } - } - - HandleTopLevelSingleDecl(*DI); - ++DI; - } - } - // If we have a decl in the main file, see if we should rewrite it. - if (SM->isFromMainFile(Loc)) - return HandleDeclInMainFile(D); -} - -//===----------------------------------------------------------------------===// -// Syntactic (non-AST) Rewriting Code -//===----------------------------------------------------------------------===// - -void RewriteModernObjC::RewriteInclude() { - SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); - StringRef MainBuf = SM->getBufferData(MainFileID); - const char *MainBufStart = MainBuf.begin(); - const char *MainBufEnd = MainBuf.end(); - size_t ImportLen = strlen("import"); - - // Loop over the whole file, looking for includes. - for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { - if (*BufPtr == '#') { - if (++BufPtr == MainBufEnd) - return; - while (*BufPtr == ' ' || *BufPtr == '\t') - if (++BufPtr == MainBufEnd) - return; - if (!strncmp(BufPtr, "import", ImportLen)) { - // replace import with include - SourceLocation ImportLoc = - LocStart.getLocWithOffset(BufPtr-MainBufStart); - ReplaceText(ImportLoc, ImportLen, "include"); - BufPtr += ImportLen; - } - } - } -} - -static void WriteInternalIvarName(const ObjCInterfaceDecl *IDecl, - ObjCIvarDecl *IvarDecl, std::string &Result) { - Result += "OBJC_IVAR_$_"; - Result += IDecl->getName(); - Result += "$"; - Result += IvarDecl->getName(); -} - -std::string -RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) { - const ObjCInterfaceDecl *ClassDecl = D->getContainingInterface(); - - // Build name of symbol holding ivar offset. - std::string IvarOffsetName; - WriteInternalIvarName(ClassDecl, D, IvarOffsetName); - - - std::string S = "(*("; - QualType IvarT = D->getType(); - - if (!isa(IvarT) && IvarT->isRecordType()) { - RecordDecl *RD = IvarT->getAs()->getDecl(); - RD = RD->getDefinition(); - if (RD && !RD->getDeclName().getAsIdentifierInfo()) { - // decltype(((Foo_IMPL*)0)->bar) * - ObjCContainerDecl *CDecl = - dyn_cast(D->getDeclContext()); - // ivar in class extensions requires special treatment. - if (ObjCCategoryDecl *CatDecl = dyn_cast(CDecl)) - CDecl = CatDecl->getClassInterface(); - std::string RecName = CDecl->getName(); - RecName += "_IMPL"; - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get(RecName.c_str())); - QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); - unsigned UnsignedIntSize = - static_cast(Context->getTypeSize(Context->UnsignedIntTy)); - Expr *Zero = IntegerLiteral::Create(*Context, - llvm::APInt(UnsignedIntSize, 0), - Context->UnsignedIntTy, SourceLocation()); - Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - Zero); - FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get(D->getNameAsString()), - IvarT, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), - FD->getType(), VK_LValue, - OK_Ordinary); - IvarT = Context->getDecltypeType(ME, ME->getType()); - } - } - convertObjCTypeToCStyleType(IvarT); - QualType castT = Context->getPointerType(IvarT); - std::string TypeString(castT.getAsString(Context->getPrintingPolicy())); - S += TypeString; - S += ")"; - - // ((char *)self + IVAR_OFFSET_SYMBOL_NAME) - S += "((char *)self + "; - S += IvarOffsetName; - S += "))"; - ReferencedIvars[const_cast(ClassDecl)].insert(D); - return S; -} - -/// mustSynthesizeSetterGetterMethod - returns true if setter or getter has not -/// been found in the class implementation. In this case, it must be synthesized. -static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP, - ObjCPropertyDecl *PD, - bool getter) { - return getter ? !IMP->getInstanceMethod(PD->getGetterName()) - : !IMP->getInstanceMethod(PD->getSetterName()); - -} - -void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, - ObjCImplementationDecl *IMD, - ObjCCategoryImplDecl *CID) { - static bool objcGetPropertyDefined = false; - static bool objcSetPropertyDefined = false; - SourceLocation startGetterSetterLoc; - - if (PID->getLocStart().isValid()) { - SourceLocation startLoc = PID->getLocStart(); - InsertText(startLoc, "// "); - const char *startBuf = SM->getCharacterData(startLoc); - assert((*startBuf == '@') && "bogus @synthesize location"); - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "@synthesize: can't find ';'"); - startGetterSetterLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); - } - else - startGetterSetterLoc = IMD ? IMD->getLocEnd() : CID->getLocEnd(); - - if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - return; // FIXME: is this correct? - - // Generate the 'getter' function. - ObjCPropertyDecl *PD = PID->getPropertyDecl(); - ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); - - if (!OID) - return; - unsigned Attributes = PD->getPropertyAttributes(); - if (mustSynthesizeSetterGetterMethod(IMD, PD, true /*getter*/)) { - bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && - (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | - ObjCPropertyDecl::OBJC_PR_copy)); - std::string Getr; - if (GenGetProperty && !objcGetPropertyDefined) { - objcGetPropertyDefined = true; - // FIXME. Is this attribute correct in all cases? - Getr = "\nextern \"C\" __declspec(dllimport) " - "id objc_getProperty(id, SEL, long, bool);\n"; - } - RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getGetterMethodDecl(), Getr); - Getr += "{ "; - // Synthesize an explicit cast to gain access to the ivar. - // See objc-act.c:objc_synthesize_new_getter() for details. - if (GenGetProperty) { - // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) - Getr += "typedef "; - const FunctionType *FPRetType = 0; - RewriteTypeIntoString(PD->getGetterMethodDecl()->getResultType(), Getr, - FPRetType); - Getr += " _TYPE"; - if (FPRetType) { - Getr += ")"; // close the precedence "scope" for "*". - - // Now, emit the argument types (if any). - if (const FunctionProtoType *FT = dyn_cast(FPRetType)){ - Getr += "("; - for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { - if (i) Getr += ", "; - std::string ParamStr = FT->getArgType(i).getAsString( - Context->getPrintingPolicy()); - Getr += ParamStr; - } - if (FT->isVariadic()) { - if (FT->getNumArgs()) Getr += ", "; - Getr += "..."; - } - Getr += ")"; - } else - Getr += "()"; - } - Getr += ";\n"; - Getr += "return (_TYPE)"; - Getr += "objc_getProperty(self, _cmd, "; - RewriteIvarOffsetComputation(OID, Getr); - Getr += ", 1)"; - } - else - Getr += "return " + getIvarAccessString(OID); - Getr += "; }"; - InsertText(startGetterSetterLoc, Getr); - } - - if (PD->isReadOnly() || - !mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/)) - return; - - // Generate the 'setter' function. - std::string Setr; - bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | - ObjCPropertyDecl::OBJC_PR_copy); - if (GenSetProperty && !objcSetPropertyDefined) { - objcSetPropertyDefined = true; - // FIXME. Is this attribute correct in all cases? - Setr = "\nextern \"C\" __declspec(dllimport) " - "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; - } - - RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getSetterMethodDecl(), Setr); - Setr += "{ "; - // Synthesize an explicit cast to initialize the ivar. - // See objc-act.c:objc_synthesize_new_setter() for details. - if (GenSetProperty) { - Setr += "objc_setProperty (self, _cmd, "; - RewriteIvarOffsetComputation(OID, Setr); - Setr += ", (id)"; - Setr += PD->getName(); - Setr += ", "; - if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) - Setr += "0, "; - else - Setr += "1, "; - if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) - Setr += "1)"; - else - Setr += "0)"; - } - else { - Setr += getIvarAccessString(OID) + " = "; - Setr += PD->getName(); - } - Setr += "; }\n"; - InsertText(startGetterSetterLoc, Setr); -} - -static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, - std::string &typedefString) { - typedefString += "#ifndef _REWRITER_typedef_"; - typedefString += ForwardDecl->getNameAsString(); - typedefString += "\n"; - typedefString += "#define _REWRITER_typedef_"; - typedefString += ForwardDecl->getNameAsString(); - typedefString += "\n"; - typedefString += "typedef struct objc_object "; - typedefString += ForwardDecl->getNameAsString(); - // typedef struct { } _objc_exc_Classname; - typedefString += ";\ntypedef struct {} _objc_exc_"; - typedefString += ForwardDecl->getNameAsString(); - typedefString += ";\n#endif\n"; -} - -void RewriteModernObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, - const std::string &typedefString) { - SourceLocation startLoc = ClassDecl->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - const char *semiPtr = strchr(startBuf, ';'); - // Replace the @class with typedefs corresponding to the classes. - ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); -} - -void RewriteModernObjC::RewriteForwardClassDecl(DeclGroupRef D) { - std::string typedefString; - for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { - ObjCInterfaceDecl *ForwardDecl = cast(*I); - if (I == D.begin()) { - // Translate to typedef's that forward reference structs with the same name - // as the class. As a convenience, we include the original declaration - // as a comment. - typedefString += "// @class "; - typedefString += ForwardDecl->getNameAsString(); - typedefString += ";\n"; - } - RewriteOneForwardClassDecl(ForwardDecl, typedefString); - } - DeclGroupRef::iterator I = D.begin(); - RewriteForwardClassEpilogue(cast(*I), typedefString); -} - -void RewriteModernObjC::RewriteForwardClassDecl( - const llvm::SmallVector &D) { - std::string typedefString; - for (unsigned i = 0; i < D.size(); i++) { - ObjCInterfaceDecl *ForwardDecl = cast(D[i]); - if (i == 0) { - typedefString += "// @class "; - typedefString += ForwardDecl->getNameAsString(); - typedefString += ";\n"; - } - RewriteOneForwardClassDecl(ForwardDecl, typedefString); - } - RewriteForwardClassEpilogue(cast(D[0]), typedefString); -} - -void RewriteModernObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { - // When method is a synthesized one, such as a getter/setter there is - // nothing to rewrite. - if (Method->isImplicit()) - return; - SourceLocation LocStart = Method->getLocStart(); - SourceLocation LocEnd = Method->getLocEnd(); - - if (SM->getExpansionLineNumber(LocEnd) > - SM->getExpansionLineNumber(LocStart)) { - InsertText(LocStart, "#if 0\n"); - ReplaceText(LocEnd, 1, ";\n#endif\n"); - } else { - InsertText(LocStart, "// "); - } -} - -void RewriteModernObjC::RewriteProperty(ObjCPropertyDecl *prop) { - SourceLocation Loc = prop->getAtLoc(); - - ReplaceText(Loc, 0, "// "); - // FIXME: handle properties that are declared across multiple lines. -} - -void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { - SourceLocation LocStart = CatDecl->getLocStart(); - - // FIXME: handle category headers that are declared across multiple lines. - if (CatDecl->getIvarRBraceLoc().isValid()) { - ReplaceText(LocStart, 1, "/** "); - ReplaceText(CatDecl->getIvarRBraceLoc(), 1, "**/ "); - } - else { - ReplaceText(LocStart, 0, "// "); - } - - for (ObjCCategoryDecl::prop_iterator I = CatDecl->prop_begin(), - E = CatDecl->prop_end(); I != E; ++I) - RewriteProperty(*I); - - for (ObjCCategoryDecl::instmeth_iterator - I = CatDecl->instmeth_begin(), E = CatDecl->instmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - for (ObjCCategoryDecl::classmeth_iterator - I = CatDecl->classmeth_begin(), E = CatDecl->classmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - - // Lastly, comment out the @end. - ReplaceText(CatDecl->getAtEndRange().getBegin(), - strlen("@end"), "/* @end */"); -} - -void RewriteModernObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { - SourceLocation LocStart = PDecl->getLocStart(); - assert(PDecl->isThisDeclarationADefinition()); - - // FIXME: handle protocol headers that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); - - for (ObjCProtocolDecl::instmeth_iterator - I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - for (ObjCProtocolDecl::classmeth_iterator - I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - - for (ObjCInterfaceDecl::prop_iterator I = PDecl->prop_begin(), - E = PDecl->prop_end(); I != E; ++I) - RewriteProperty(*I); - - // Lastly, comment out the @end. - SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); - ReplaceText(LocEnd, strlen("@end"), "/* @end */"); - - // Must comment out @optional/@required - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - for (const char *p = startBuf; p < endBuf; p++) { - if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { - SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); - ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); - - } - else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { - SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); - ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); - - } - } -} - -void RewriteModernObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { - SourceLocation LocStart = (*D.begin())->getLocStart(); - if (LocStart.isInvalid()) - llvm_unreachable("Invalid SourceLocation"); - // FIXME: handle forward protocol that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); -} - -void -RewriteModernObjC::RewriteForwardProtocolDecl(const llvm::SmallVector &DG) { - SourceLocation LocStart = DG[0]->getLocStart(); - if (LocStart.isInvalid()) - llvm_unreachable("Invalid SourceLocation"); - // FIXME: handle forward protocol that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); -} - -void -RewriteModernObjC::RewriteLinkageSpec(LinkageSpecDecl *LSD) { - SourceLocation LocStart = LSD->getExternLoc(); - if (LocStart.isInvalid()) - llvm_unreachable("Invalid extern SourceLocation"); - - ReplaceText(LocStart, 0, "// "); - if (!LSD->hasBraces()) - return; - // FIXME. We don't rewrite well if '{' is not on same line as 'extern'. - SourceLocation LocRBrace = LSD->getRBraceLoc(); - if (LocRBrace.isInvalid()) - llvm_unreachable("Invalid rbrace SourceLocation"); - ReplaceText(LocRBrace, 0, "// "); -} - -void RewriteModernObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, - const FunctionType *&FPRetType) { - if (T->isObjCQualifiedIdType()) - ResultStr += "id"; - else if (T->isFunctionPointerType() || - T->isBlockPointerType()) { - // needs special handling, since pointer-to-functions have special - // syntax (where a decaration models use). - QualType retType = T; - QualType PointeeTy; - if (const PointerType* PT = retType->getAs()) - PointeeTy = PT->getPointeeType(); - else if (const BlockPointerType *BPT = retType->getAs()) - PointeeTy = BPT->getPointeeType(); - if ((FPRetType = PointeeTy->getAs())) { - ResultStr += FPRetType->getResultType().getAsString( - Context->getPrintingPolicy()); - ResultStr += "(*"; - } - } else - ResultStr += T.getAsString(Context->getPrintingPolicy()); -} - -void RewriteModernObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, - ObjCMethodDecl *OMD, - std::string &ResultStr) { - //fprintf(stderr,"In RewriteObjCMethodDecl\n"); - const FunctionType *FPRetType = 0; - ResultStr += "\nstatic "; - RewriteTypeIntoString(OMD->getResultType(), ResultStr, FPRetType); - ResultStr += " "; - - // Unique method name - std::string NameStr; - - if (OMD->isInstanceMethod()) - NameStr += "_I_"; - else - NameStr += "_C_"; - - NameStr += IDecl->getNameAsString(); - NameStr += "_"; - - if (ObjCCategoryImplDecl *CID = - dyn_cast(OMD->getDeclContext())) { - NameStr += CID->getNameAsString(); - NameStr += "_"; - } - // Append selector names, replacing ':' with '_' - { - std::string selString = OMD->getSelector().getAsString(); - int len = selString.size(); - for (int i = 0; i < len; i++) - if (selString[i] == ':') - selString[i] = '_'; - NameStr += selString; - } - // Remember this name for metadata emission - MethodInternalNames[OMD] = NameStr; - ResultStr += NameStr; - - // Rewrite arguments - ResultStr += "("; - - // invisible arguments - if (OMD->isInstanceMethod()) { - QualType selfTy = Context->getObjCInterfaceType(IDecl); - selfTy = Context->getPointerType(selfTy); - if (!LangOpts.MicrosoftExt) { - if (ObjCSynthesizedStructs.count(const_cast(IDecl))) - ResultStr += "struct "; - } - // When rewriting for Microsoft, explicitly omit the structure name. - ResultStr += IDecl->getNameAsString(); - ResultStr += " *"; - } - else - ResultStr += Context->getObjCClassType().getAsString( - Context->getPrintingPolicy()); - - ResultStr += " self, "; - ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); - ResultStr += " _cmd"; - - // Method arguments. - for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), - E = OMD->param_end(); PI != E; ++PI) { - ParmVarDecl *PDecl = *PI; - ResultStr += ", "; - if (PDecl->getType()->isObjCQualifiedIdType()) { - ResultStr += "id "; - ResultStr += PDecl->getNameAsString(); - } else { - std::string Name = PDecl->getNameAsString(); - QualType QT = PDecl->getType(); - // Make sure we convert "t (^)(...)" to "t (*)(...)". - (void)convertBlockPointerToFunctionPointer(QT); - QT.getAsStringInternal(Name, Context->getPrintingPolicy()); - ResultStr += Name; - } - } - if (OMD->isVariadic()) - ResultStr += ", ..."; - ResultStr += ") "; - - if (FPRetType) { - ResultStr += ")"; // close the precedence "scope" for "*". - - // Now, emit the argument types (if any). - if (const FunctionProtoType *FT = dyn_cast(FPRetType)) { - ResultStr += "("; - for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { - if (i) ResultStr += ", "; - std::string ParamStr = FT->getArgType(i).getAsString( - Context->getPrintingPolicy()); - ResultStr += ParamStr; - } - if (FT->isVariadic()) { - if (FT->getNumArgs()) ResultStr += ", "; - ResultStr += "..."; - } - ResultStr += ")"; - } else { - ResultStr += "()"; - } - } -} -void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { - ObjCImplementationDecl *IMD = dyn_cast(OID); - ObjCCategoryImplDecl *CID = dyn_cast(OID); - - if (IMD) { - if (IMD->getIvarRBraceLoc().isValid()) { - ReplaceText(IMD->getLocStart(), 1, "/** "); - ReplaceText(IMD->getIvarRBraceLoc(), 1, "**/ "); - } - else { - InsertText(IMD->getLocStart(), "// "); - } - } - else - InsertText(CID->getLocStart(), "// "); - - for (ObjCCategoryImplDecl::instmeth_iterator - I = IMD ? IMD->instmeth_begin() : CID->instmeth_begin(), - E = IMD ? IMD->instmeth_end() : CID->instmeth_end(); - I != E; ++I) { - std::string ResultStr; - ObjCMethodDecl *OMD = *I; - RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); - SourceLocation LocStart = OMD->getLocStart(); - SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - ReplaceText(LocStart, endBuf-startBuf, ResultStr); - } - - for (ObjCCategoryImplDecl::classmeth_iterator - I = IMD ? IMD->classmeth_begin() : CID->classmeth_begin(), - E = IMD ? IMD->classmeth_end() : CID->classmeth_end(); - I != E; ++I) { - std::string ResultStr; - ObjCMethodDecl *OMD = *I; - RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); - SourceLocation LocStart = OMD->getLocStart(); - SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - ReplaceText(LocStart, endBuf-startBuf, ResultStr); - } - for (ObjCCategoryImplDecl::propimpl_iterator - I = IMD ? IMD->propimpl_begin() : CID->propimpl_begin(), - E = IMD ? IMD->propimpl_end() : CID->propimpl_end(); - I != E; ++I) { - RewritePropertyImplDecl(*I, IMD, CID); - } - - InsertText(IMD ? IMD->getLocEnd() : CID->getLocEnd(), "// "); -} - -void RewriteModernObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { - // Do not synthesize more than once. - if (ObjCSynthesizedStructs.count(ClassDecl)) - return; - // Make sure super class's are written before current class is written. - ObjCInterfaceDecl *SuperClass = ClassDecl->getSuperClass(); - while (SuperClass) { - RewriteInterfaceDecl(SuperClass); - SuperClass = SuperClass->getSuperClass(); - } - std::string ResultStr; - if (!ObjCWrittenInterfaces.count(ClassDecl->getCanonicalDecl())) { - // we haven't seen a forward decl - generate a typedef. - RewriteOneForwardClassDecl(ClassDecl, ResultStr); - RewriteIvarOffsetSymbols(ClassDecl, ResultStr); - - RewriteObjCInternalStruct(ClassDecl, ResultStr); - // Mark this typedef as having been written into its c++ equivalent. - ObjCWrittenInterfaces.insert(ClassDecl->getCanonicalDecl()); - - for (ObjCInterfaceDecl::prop_iterator I = ClassDecl->prop_begin(), - E = ClassDecl->prop_end(); I != E; ++I) - RewriteProperty(*I); - for (ObjCInterfaceDecl::instmeth_iterator - I = ClassDecl->instmeth_begin(), E = ClassDecl->instmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - for (ObjCInterfaceDecl::classmeth_iterator - I = ClassDecl->classmeth_begin(), E = ClassDecl->classmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - - // Lastly, comment out the @end. - ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), - "/* @end */"); - } -} - -Stmt *RewriteModernObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { - SourceRange OldRange = PseudoOp->getSourceRange(); - - // We just magically know some things about the structure of this - // expression. - ObjCMessageExpr *OldMsg = - cast(PseudoOp->getSemanticExpr( - PseudoOp->getNumSemanticExprs() - 1)); - - // Because the rewriter doesn't allow us to rewrite rewritten code, - // we need to suppress rewriting the sub-statements. - Expr *Base; - SmallVector Args; - { - DisableReplaceStmtScope S(*this); - - // Rebuild the base expression if we have one. - Base = 0; - if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { - Base = OldMsg->getInstanceReceiver(); - Base = cast(Base)->getSourceExpr(); - Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); - } - - unsigned numArgs = OldMsg->getNumArgs(); - for (unsigned i = 0; i < numArgs; i++) { - Expr *Arg = OldMsg->getArg(i); - if (isa(Arg)) - Arg = cast(Arg)->getSourceExpr(); - Arg = cast(RewriteFunctionBodyOrGlobalInitializer(Arg)); - Args.push_back(Arg); - } - } - - // TODO: avoid this copy. - SmallVector SelLocs; - OldMsg->getSelectorLocs(SelLocs); - - ObjCMessageExpr *NewMsg = 0; - switch (OldMsg->getReceiverKind()) { - case ObjCMessageExpr::Class: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getClassReceiverTypeInfo(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::Instance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - Base, - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::SuperClass: - case ObjCMessageExpr::SuperInstance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getSuperLoc(), - OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, - OldMsg->getSuperType(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - } - - Stmt *Replacement = SynthMessageExpr(NewMsg); - ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); - return Replacement; -} - -Stmt *RewriteModernObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { - SourceRange OldRange = PseudoOp->getSourceRange(); - - // We just magically know some things about the structure of this - // expression. - ObjCMessageExpr *OldMsg = - cast(PseudoOp->getResultExpr()->IgnoreImplicit()); - - // Because the rewriter doesn't allow us to rewrite rewritten code, - // we need to suppress rewriting the sub-statements. - Expr *Base = 0; - SmallVector Args; - { - DisableReplaceStmtScope S(*this); - // Rebuild the base expression if we have one. - if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { - Base = OldMsg->getInstanceReceiver(); - Base = cast(Base)->getSourceExpr(); - Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); - } - unsigned numArgs = OldMsg->getNumArgs(); - for (unsigned i = 0; i < numArgs; i++) { - Expr *Arg = OldMsg->getArg(i); - if (isa(Arg)) - Arg = cast(Arg)->getSourceExpr(); - Arg = cast(RewriteFunctionBodyOrGlobalInitializer(Arg)); - Args.push_back(Arg); - } - } - - // Intentionally empty. - SmallVector SelLocs; - - ObjCMessageExpr *NewMsg = 0; - switch (OldMsg->getReceiverKind()) { - case ObjCMessageExpr::Class: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getClassReceiverTypeInfo(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::Instance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - Base, - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::SuperClass: - case ObjCMessageExpr::SuperInstance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getSuperLoc(), - OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, - OldMsg->getSuperType(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - } - - Stmt *Replacement = SynthMessageExpr(NewMsg); - ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); - return Replacement; -} - -/// SynthCountByEnumWithState - To print: -/// ((unsigned int (*) -/// (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) -/// (void *)objc_msgSend)((id)l_collection, -/// sel_registerName( -/// "countByEnumeratingWithState:objects:count:"), -/// &enumState, -/// (id *)__rw_items, (unsigned int)16) -/// -void RewriteModernObjC::SynthCountByEnumWithState(std::string &buf) { - buf += "((unsigned int (*) (id, SEL, struct __objcFastEnumerationState *, " - "id *, unsigned int))(void *)objc_msgSend)"; - buf += "\n\t\t"; - buf += "((id)l_collection,\n\t\t"; - buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; - buf += "\n\t\t"; - buf += "&enumState, " - "(id *)__rw_items, (unsigned int)16)"; -} - -/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach -/// statement to exit to its outer synthesized loop. -/// -Stmt *RewriteModernObjC::RewriteBreakStmt(BreakStmt *S) { - if (Stmts.empty() || !isa(Stmts.back())) - return S; - // replace break with goto __break_label - std::string buf; - - SourceLocation startLoc = S->getLocStart(); - buf = "goto __break_label_"; - buf += utostr(ObjCBcLabelNo.back()); - ReplaceText(startLoc, strlen("break"), buf); - - return 0; -} - -/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach -/// statement to continue with its inner synthesized loop. -/// -Stmt *RewriteModernObjC::RewriteContinueStmt(ContinueStmt *S) { - if (Stmts.empty() || !isa(Stmts.back())) - return S; - // replace continue with goto __continue_label - std::string buf; - - SourceLocation startLoc = S->getLocStart(); - buf = "goto __continue_label_"; - buf += utostr(ObjCBcLabelNo.back()); - ReplaceText(startLoc, strlen("continue"), buf); - - return 0; -} - -/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. -/// It rewrites: -/// for ( type elem in collection) { stmts; } - -/// Into: -/// { -/// type elem; -/// struct __objcFastEnumerationState enumState = { 0 }; -/// id __rw_items[16]; -/// id l_collection = (id)collection; -/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState -/// objects:__rw_items count:16]; -/// if (limit) { -/// unsigned long startMutations = *enumState.mutationsPtr; -/// do { -/// unsigned long counter = 0; -/// do { -/// if (startMutations != *enumState.mutationsPtr) -/// objc_enumerationMutation(l_collection); -/// elem = (type)enumState.itemsPtr[counter++]; -/// stmts; -/// __continue_label: ; -/// } while (counter < limit); -/// } while (limit = [l_collection countByEnumeratingWithState:&enumState -/// objects:__rw_items count:16]); -/// elem = nil; -/// __break_label: ; -/// } -/// else -/// elem = nil; -/// } -/// -Stmt *RewriteModernObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, - SourceLocation OrigEnd) { - assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); - assert(isa(Stmts.back()) && - "ObjCForCollectionStmt Statement stack mismatch"); - assert(!ObjCBcLabelNo.empty() && - "ObjCForCollectionStmt - Label No stack empty"); - - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - StringRef elementName; - std::string elementTypeAsString; - std::string buf; - buf = "\n{\n\t"; - if (DeclStmt *DS = dyn_cast(S->getElement())) { - // type elem; - NamedDecl* D = cast(DS->getSingleDecl()); - QualType ElementType = cast(D)->getType(); - if (ElementType->isObjCQualifiedIdType() || - ElementType->isObjCQualifiedInterfaceType()) - // Simply use 'id' for all qualified types. - elementTypeAsString = "id"; - else - elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); - buf += elementTypeAsString; - buf += " "; - elementName = D->getName(); - buf += elementName; - buf += ";\n\t"; - } - else { - DeclRefExpr *DR = cast(S->getElement()); - elementName = DR->getDecl()->getName(); - ValueDecl *VD = cast(DR->getDecl()); - if (VD->getType()->isObjCQualifiedIdType() || - VD->getType()->isObjCQualifiedInterfaceType()) - // Simply use 'id' for all qualified types. - elementTypeAsString = "id"; - else - elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); - } - - // struct __objcFastEnumerationState enumState = { 0 }; - buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; - // id __rw_items[16]; - buf += "id __rw_items[16];\n\t"; - // id l_collection = (id) - buf += "id l_collection = (id)"; - // Find start location of 'collection' the hard way! - const char *startCollectionBuf = startBuf; - startCollectionBuf += 3; // skip 'for' - startCollectionBuf = strchr(startCollectionBuf, '('); - startCollectionBuf++; // skip '(' - // find 'in' and skip it. - while (*startCollectionBuf != ' ' || - *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || - (*(startCollectionBuf+3) != ' ' && - *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) - startCollectionBuf++; - startCollectionBuf += 3; - - // Replace: "for (type element in" with string constructed thus far. - ReplaceText(startLoc, startCollectionBuf - startBuf, buf); - // Replace ')' in for '(' type elem in collection ')' with ';' - SourceLocation rightParenLoc = S->getRParenLoc(); - const char *rparenBuf = SM->getCharacterData(rightParenLoc); - SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); - buf = ";\n\t"; - - // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState - // objects:__rw_items count:16]; - // which is synthesized into: - // unsigned int limit = - // ((unsigned int (*) - // (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) - // (void *)objc_msgSend)((id)l_collection, - // sel_registerName( - // "countByEnumeratingWithState:objects:count:"), - // (struct __objcFastEnumerationState *)&state, - // (id *)__rw_items, (unsigned int)16); - buf += "unsigned long limit =\n\t\t"; - SynthCountByEnumWithState(buf); - buf += ";\n\t"; - /// if (limit) { - /// unsigned long startMutations = *enumState.mutationsPtr; - /// do { - /// unsigned long counter = 0; - /// do { - /// if (startMutations != *enumState.mutationsPtr) - /// objc_enumerationMutation(l_collection); - /// elem = (type)enumState.itemsPtr[counter++]; - buf += "if (limit) {\n\t"; - buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; - buf += "do {\n\t\t"; - buf += "unsigned long counter = 0;\n\t\t"; - buf += "do {\n\t\t\t"; - buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; - buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; - buf += elementName; - buf += " = ("; - buf += elementTypeAsString; - buf += ")enumState.itemsPtr[counter++];"; - // Replace ')' in for '(' type elem in collection ')' with all of these. - ReplaceText(lparenLoc, 1, buf); - - /// __continue_label: ; - /// } while (counter < limit); - /// } while (limit = [l_collection countByEnumeratingWithState:&enumState - /// objects:__rw_items count:16]); - /// elem = nil; - /// __break_label: ; - /// } - /// else - /// elem = nil; - /// } - /// - buf = ";\n\t"; - buf += "__continue_label_"; - buf += utostr(ObjCBcLabelNo.back()); - buf += ": ;"; - buf += "\n\t\t"; - buf += "} while (counter < limit);\n\t"; - buf += "} while (limit = "; - SynthCountByEnumWithState(buf); - buf += ");\n\t"; - buf += elementName; - buf += " = (("; - buf += elementTypeAsString; - buf += ")0);\n\t"; - buf += "__break_label_"; - buf += utostr(ObjCBcLabelNo.back()); - buf += ": ;\n\t"; - buf += "}\n\t"; - buf += "else\n\t\t"; - buf += elementName; - buf += " = (("; - buf += elementTypeAsString; - buf += ")0);\n\t"; - buf += "}\n"; - - // Insert all these *after* the statement body. - // FIXME: If this should support Obj-C++, support CXXTryStmt - if (isa(S->getBody())) { - SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); - InsertText(endBodyLoc, buf); - } else { - /* Need to treat single statements specially. For example: - * - * for (A *a in b) if (stuff()) break; - * for (A *a in b) xxxyy; - * - * The following code simply scans ahead to the semi to find the actual end. - */ - const char *stmtBuf = SM->getCharacterData(OrigEnd); - const char *semiBuf = strchr(stmtBuf, ';'); - assert(semiBuf && "Can't find ';'"); - SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(semiBuf-stmtBuf+1); - InsertText(endBodyLoc, buf); - } - Stmts.pop_back(); - ObjCBcLabelNo.pop_back(); - return 0; -} - -static void Write_RethrowObject(std::string &buf) { - buf += "{ struct _FIN { _FIN(id reth) : rethrow(reth) {}\n"; - buf += "\t~_FIN() { if (rethrow) objc_exception_throw(rethrow); }\n"; - buf += "\tid rethrow;\n"; - buf += "\t} _fin_force_rethow(_rethrow);"; -} - -/// RewriteObjCSynchronizedStmt - -/// This routine rewrites @synchronized(expr) stmt; -/// into: -/// objc_sync_enter(expr); -/// @try stmt @finally { objc_sync_exit(expr); } -/// -Stmt *RewriteModernObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { - // Get the start location and compute the semi location. - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @synchronized location"); - - std::string buf; - buf = "{ id _rethrow = 0; id _sync_obj = "; - - const char *lparenBuf = startBuf; - while (*lparenBuf != '(') lparenBuf++; - ReplaceText(startLoc, lparenBuf-startBuf+1, buf); - - buf = "; objc_sync_enter(_sync_obj);\n"; - buf += "try {\n\tstruct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}"; - buf += "\n\t~_SYNC_EXIT() {objc_sync_exit(sync_exit);}"; - buf += "\n\tid sync_exit;"; - buf += "\n\t} _sync_exit(_sync_obj);\n"; - - // We can't use S->getSynchExpr()->getLocEnd() to find the end location, since - // the sync expression is typically a message expression that's already - // been rewritten! (which implies the SourceLocation's are invalid). - SourceLocation RParenExprLoc = S->getSynchBody()->getLocStart(); - const char *RParenExprLocBuf = SM->getCharacterData(RParenExprLoc); - while (*RParenExprLocBuf != ')') RParenExprLocBuf--; - RParenExprLoc = startLoc.getLocWithOffset(RParenExprLocBuf-startBuf); - - SourceLocation LBranceLoc = S->getSynchBody()->getLocStart(); - const char *LBraceLocBuf = SM->getCharacterData(LBranceLoc); - assert (*LBraceLocBuf == '{'); - ReplaceText(RParenExprLoc, (LBraceLocBuf - SM->getCharacterData(RParenExprLoc) + 1), buf); - - SourceLocation startRBraceLoc = S->getSynchBody()->getLocEnd(); - assert((*SM->getCharacterData(startRBraceLoc) == '}') && - "bogus @synchronized block"); - - buf = "} catch (id e) {_rethrow = e;}\n"; - Write_RethrowObject(buf); - buf += "}\n"; - buf += "}\n"; - - ReplaceText(startRBraceLoc, 1, buf); - - return 0; -} - -void RewriteModernObjC::WarnAboutReturnGotoStmts(Stmt *S) -{ - // Perform a bottom up traversal of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) - WarnAboutReturnGotoStmts(*CI); - - if (isa(S) || isa(S)) { - Diags.Report(Context->getFullLoc(S->getLocStart()), - TryFinallyContainsReturnDiag); - } - return; -} - -Stmt *RewriteModernObjC::RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { - SourceLocation startLoc = S->getAtLoc(); - ReplaceText(startLoc, strlen("@autoreleasepool"), "/* @autoreleasepool */"); - ReplaceText(S->getSubStmt()->getLocStart(), 1, - "{ __AtAutoreleasePool __autoreleasepool; "); - - return 0; -} - -Stmt *RewriteModernObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { - ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt(); - bool noCatch = S->getNumCatchStmts() == 0; - std::string buf; - - if (finalStmt) { - if (noCatch) - buf = "{ id volatile _rethrow = 0;\n"; - else { - buf = "{ id volatile _rethrow = 0;\ntry {\n"; - } - } - // Get the start location and compute the semi location. - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @try location"); - if (finalStmt) - ReplaceText(startLoc, 1, buf); - else - // @try -> try - ReplaceText(startLoc, 1, ""); - - for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { - ObjCAtCatchStmt *Catch = S->getCatchStmt(I); - VarDecl *catchDecl = Catch->getCatchParamDecl(); - - startLoc = Catch->getLocStart(); - bool AtRemoved = false; - if (catchDecl) { - QualType t = catchDecl->getType(); - if (const ObjCObjectPointerType *Ptr = t->getAs()) { - // Should be a pointer to a class. - ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); - if (IDecl) { - std::string Result; - startBuf = SM->getCharacterData(startLoc); - assert((*startBuf == '@') && "bogus @catch location"); - SourceLocation rParenLoc = Catch->getRParenLoc(); - const char *rParenBuf = SM->getCharacterData(rParenLoc); - - // _objc_exc_Foo *_e as argument to catch. - Result = "catch (_objc_exc_"; Result += IDecl->getNameAsString(); - Result += " *_"; Result += catchDecl->getNameAsString(); - Result += ")"; - ReplaceText(startLoc, rParenBuf-startBuf+1, Result); - // Foo *e = (Foo *)_e; - Result.clear(); - Result = "{ "; - Result += IDecl->getNameAsString(); - Result += " *"; Result += catchDecl->getNameAsString(); - Result += " = ("; Result += IDecl->getNameAsString(); Result += "*)"; - Result += "_"; Result += catchDecl->getNameAsString(); - - Result += "; "; - SourceLocation lBraceLoc = Catch->getCatchBody()->getLocStart(); - ReplaceText(lBraceLoc, 1, Result); - AtRemoved = true; - } - } - } - if (!AtRemoved) - // @catch -> catch - ReplaceText(startLoc, 1, ""); - - } - if (finalStmt) { - buf.clear(); - if (noCatch) - buf = "catch (id e) {_rethrow = e;}\n"; - else - buf = "}\ncatch (id e) {_rethrow = e;}\n"; - - SourceLocation startFinalLoc = finalStmt->getLocStart(); - ReplaceText(startFinalLoc, 8, buf); - Stmt *body = finalStmt->getFinallyBody(); - SourceLocation startFinalBodyLoc = body->getLocStart(); - buf.clear(); - Write_RethrowObject(buf); - ReplaceText(startFinalBodyLoc, 1, buf); - - SourceLocation endFinalBodyLoc = body->getLocEnd(); - ReplaceText(endFinalBodyLoc, 1, "}\n}"); - // Now check for any return/continue/go statements within the @try. - WarnAboutReturnGotoStmts(S->getTryBody()); - } - - return 0; -} - -// This can't be done with ReplaceStmt(S, ThrowExpr), since -// the throw expression is typically a message expression that's already -// been rewritten! (which implies the SourceLocation's are invalid). -Stmt *RewriteModernObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { - // Get the start location and compute the semi location. - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @throw location"); - - std::string buf; - /* void objc_exception_throw(id) __attribute__((noreturn)); */ - if (S->getThrowExpr()) - buf = "objc_exception_throw("; - else - buf = "throw"; - - // handle "@ throw" correctly. - const char *wBuf = strchr(startBuf, 'w'); - assert((*wBuf == 'w') && "@throw: can't find 'w'"); - ReplaceText(startLoc, wBuf-startBuf+1, buf); - - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "@throw: can't find ';'"); - SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); - if (S->getThrowExpr()) - ReplaceText(semiLoc, 1, ");"); - return 0; -} - -Stmt *RewriteModernObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { - // Create a new string expression. - QualType StrType = Context->getPointerType(Context->CharTy); - std::string StrEncoding; - Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); - Expr *Replacement = StringLiteral::Create(*Context, StrEncoding, - StringLiteral::Ascii, false, - StrType, SourceLocation()); - ReplaceStmt(Exp, Replacement); - - // Replace this subexpr in the parent. - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return Replacement; -} - -Stmt *RewriteModernObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); - // Create a call to sel_registerName("selName"). - SmallVector SelExprs; - QualType argType = Context->getPointerType(Context->CharTy); - SelExprs.push_back(StringLiteral::Create(*Context, - Exp->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size()); - ReplaceStmt(Exp, SelExp); - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return SelExp; -} - -CallExpr *RewriteModernObjC::SynthesizeCallToFunctionDecl( - FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc, - SourceLocation EndLoc) { - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = FD->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = - new (Context) DeclRefExpr(FD, false, msgSendType, VK_LValue, SourceLocation()); - - // Now, we cast the reference to a pointer to the objc_msgSend type. - QualType pToFunc = Context->getPointerType(msgSendType); - ImplicitCastExpr *ICE = - ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, - DRE, 0, VK_RValue); - - const FunctionType *FT = msgSendType->getAs(); - - CallExpr *Exp = - new (Context) CallExpr(*Context, ICE, args, nargs, - FT->getCallResultType(*Context), - VK_RValue, EndLoc); - return Exp; -} - -static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, - const char *&startRef, const char *&endRef) { - while (startBuf < endBuf) { - if (*startBuf == '<') - startRef = startBuf; // mark the start. - if (*startBuf == '>') { - if (startRef && *startRef == '<') { - endRef = startBuf; // mark the end. - return true; - } - return false; - } - startBuf++; - } - return false; -} - -static void scanToNextArgument(const char *&argRef) { - int angle = 0; - while (*argRef != ')' && (*argRef != ',' || angle > 0)) { - if (*argRef == '<') - angle++; - else if (*argRef == '>') - angle--; - argRef++; - } - assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); -} - -bool RewriteModernObjC::needToScanForQualifiers(QualType T) { - if (T->isObjCQualifiedIdType()) - return true; - if (const PointerType *PT = T->getAs()) { - if (PT->getPointeeType()->isObjCQualifiedIdType()) - return true; - } - if (T->isObjCObjectPointerType()) { - T = T->getPointeeType(); - return T->isObjCQualifiedInterfaceType(); - } - if (T->isArrayType()) { - QualType ElemTy = Context->getBaseElementType(T); - return needToScanForQualifiers(ElemTy); - } - return false; -} - -void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { - QualType Type = E->getType(); - if (needToScanForQualifiers(Type)) { - SourceLocation Loc, EndLoc; - - if (const CStyleCastExpr *ECE = dyn_cast(E)) { - Loc = ECE->getLParenLoc(); - EndLoc = ECE->getRParenLoc(); - } else { - Loc = E->getLocStart(); - EndLoc = E->getLocEnd(); - } - // This will defend against trying to rewrite synthesized expressions. - if (Loc.isInvalid() || EndLoc.isInvalid()) - return; - - const char *startBuf = SM->getCharacterData(Loc); - const char *endBuf = SM->getCharacterData(EndLoc); - const char *startRef = 0, *endRef = 0; - if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { - // Get the locations of the startRef, endRef. - SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); - SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); - // Comment out the protocol references. - InsertText(LessLoc, "/*"); - InsertText(GreaterLoc, "*/"); - } - } -} - -void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { - SourceLocation Loc; - QualType Type; - const FunctionProtoType *proto = 0; - if (VarDecl *VD = dyn_cast(Dcl)) { - Loc = VD->getLocation(); - Type = VD->getType(); - } - else if (FunctionDecl *FD = dyn_cast(Dcl)) { - Loc = FD->getLocation(); - // Check for ObjC 'id' and class types that have been adorned with protocol - // information (id

, C

*). The protocol references need to be rewritten! - const FunctionType *funcType = FD->getType()->getAs(); - assert(funcType && "missing function type"); - proto = dyn_cast(funcType); - if (!proto) - return; - Type = proto->getResultType(); - } - else if (FieldDecl *FD = dyn_cast(Dcl)) { - Loc = FD->getLocation(); - Type = FD->getType(); - } - else - return; - - if (needToScanForQualifiers(Type)) { - // Since types are unique, we need to scan the buffer. - - const char *endBuf = SM->getCharacterData(Loc); - const char *startBuf = endBuf; - while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) - startBuf--; // scan backward (from the decl location) for return type. - const char *startRef = 0, *endRef = 0; - if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { - // Get the locations of the startRef, endRef. - SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); - SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); - // Comment out the protocol references. - InsertText(LessLoc, "/*"); - InsertText(GreaterLoc, "*/"); - } - } - if (!proto) - return; // most likely, was a variable - // Now check arguments. - const char *startBuf = SM->getCharacterData(Loc); - const char *startFuncBuf = startBuf; - for (unsigned i = 0; i < proto->getNumArgs(); i++) { - if (needToScanForQualifiers(proto->getArgType(i))) { - // Since types are unique, we need to scan the buffer. - - const char *endBuf = startBuf; - // scan forward (from the decl location) for argument types. - scanToNextArgument(endBuf); - const char *startRef = 0, *endRef = 0; - if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { - // Get the locations of the startRef, endRef. - SourceLocation LessLoc = - Loc.getLocWithOffset(startRef-startFuncBuf); - SourceLocation GreaterLoc = - Loc.getLocWithOffset(endRef-startFuncBuf+1); - // Comment out the protocol references. - InsertText(LessLoc, "/*"); - InsertText(GreaterLoc, "*/"); - } - startBuf = ++endBuf; - } - else { - // If the function name is derived from a macro expansion, then the - // argument buffer will not follow the name. Need to speak with Chris. - while (*startBuf && *startBuf != ')' && *startBuf != ',') - startBuf++; // scan forward (from the decl location) for argument types. - startBuf++; - } - } -} - -void RewriteModernObjC::RewriteTypeOfDecl(VarDecl *ND) { - QualType QT = ND->getType(); - const Type* TypePtr = QT->getAs(); - if (!isa(TypePtr)) - return; - while (isa(TypePtr)) { - const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); - QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); - TypePtr = QT->getAs(); - } - // FIXME. This will not work for multiple declarators; as in: - // __typeof__(a) b,c,d; - std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); - SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); - const char *startBuf = SM->getCharacterData(DeclLoc); - if (ND->getInit()) { - std::string Name(ND->getNameAsString()); - TypeAsString += " " + Name + " = "; - Expr *E = ND->getInit(); - SourceLocation startLoc; - if (const CStyleCastExpr *ECE = dyn_cast(E)) - startLoc = ECE->getLParenLoc(); - else - startLoc = E->getLocStart(); - startLoc = SM->getExpansionLoc(startLoc); - const char *endBuf = SM->getCharacterData(startLoc); - ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); - } - else { - SourceLocation X = ND->getLocEnd(); - X = SM->getExpansionLoc(X); - const char *endBuf = SM->getCharacterData(X); - ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); - } -} - -// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); -void RewriteModernObjC::SynthSelGetUidFunctionDecl() { - IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); - SmallVector ArgTys; - ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getFuncType = - getSimpleFunctionType(Context->getObjCSelType(), &ArgTys[0], ArgTys.size()); - SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - SelGetUidIdent, getFuncType, 0, - SC_Extern, - SC_None, false); -} - -void RewriteModernObjC::RewriteFunctionDecl(FunctionDecl *FD) { - // declared in - if (FD->getIdentifier() && - FD->getName() == "sel_registerName") { - SelGetUidFunctionDecl = FD; - return; - } - RewriteObjCQualifiedInterfaceTypes(FD); -} - -void RewriteModernObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { - std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); - const char *argPtr = TypeString.c_str(); - if (!strchr(argPtr, '^')) { - Str += TypeString; - return; - } - while (*argPtr) { - Str += (*argPtr == '^' ? '*' : *argPtr); - argPtr++; - } -} - -// FIXME. Consolidate this routine with RewriteBlockPointerType. -void RewriteModernObjC::RewriteBlockPointerTypeVariable(std::string& Str, - ValueDecl *VD) { - QualType Type = VD->getType(); - std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); - const char *argPtr = TypeString.c_str(); - int paren = 0; - while (*argPtr) { - switch (*argPtr) { - case '(': - Str += *argPtr; - paren++; - break; - case ')': - Str += *argPtr; - paren--; - break; - case '^': - Str += '*'; - if (paren == 1) - Str += VD->getNameAsString(); - break; - default: - Str += *argPtr; - break; - } - argPtr++; - } -} - -void RewriteModernObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { - SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); - const FunctionType *funcType = FD->getType()->getAs(); - const FunctionProtoType *proto = dyn_cast(funcType); - if (!proto) - return; - QualType Type = proto->getResultType(); - std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); - FdStr += " "; - FdStr += FD->getName(); - FdStr += "("; - unsigned numArgs = proto->getNumArgs(); - for (unsigned i = 0; i < numArgs; i++) { - QualType ArgType = proto->getArgType(i); - RewriteBlockPointerType(FdStr, ArgType); - if (i+1 < numArgs) - FdStr += ", "; - } - if (FD->isVariadic()) { - FdStr += (numArgs > 0) ? ", ...);\n" : "...);\n"; - } - else - FdStr += ");\n"; - InsertText(FunLocStart, FdStr); -} - -// SynthSuperContructorFunctionDecl - id __rw_objc_super(id obj, id super); -void RewriteModernObjC::SynthSuperContructorFunctionDecl() { - if (SuperContructorFunctionDecl) - return; - IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size()); - SuperContructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); -void RewriteModernObjC::SynthMsgSendFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(void); -void RewriteModernObjC::SynthMsgSendSuperFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); - SmallVector ArgTys; - ArgTys.push_back(Context->VoidTy); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], 1, - true /*isVariadic*/); - MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); -void RewriteModernObjC::SynthMsgSendStretFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendSuperStretFunctionDecl - -// id objc_msgSendSuper_stret(void); -void RewriteModernObjC::SynthMsgSendSuperStretFunctionDecl() { - IdentifierInfo *msgSendIdent = - &Context->Idents.get("objc_msgSendSuper_stret"); - SmallVector ArgTys; - ArgTys.push_back(Context->VoidTy); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], 1, - true /*isVariadic*/); - MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); -void RewriteModernObjC::SynthMsgSendFpretFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->DoubleTy, - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthGetClassFunctionDecl - Class objc_getClass(const char *name); -void RewriteModernObjC::SynthGetClassFunctionDecl() { - IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); - SmallVector ArgTys; - ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), - &ArgTys[0], ArgTys.size()); - GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - getClassIdent, getClassType, 0, - SC_Extern, - SC_None, false); -} - -// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); -void RewriteModernObjC::SynthGetSuperClassFunctionDecl() { - IdentifierInfo *getSuperClassIdent = - &Context->Idents.get("class_getSuperclass"); - SmallVector ArgTys; - ArgTys.push_back(Context->getObjCClassType()); - QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), - &ArgTys[0], ArgTys.size()); - GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - getSuperClassIdent, - getClassType, 0, - SC_Extern, - SC_None, - false); -} - -// SynthGetMetaClassFunctionDecl - Class objc_getMetaClass(const char *name); -void RewriteModernObjC::SynthGetMetaClassFunctionDecl() { - IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); - SmallVector ArgTys; - ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), - &ArgTys[0], ArgTys.size()); - GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - getClassIdent, getClassType, 0, - SC_Extern, - SC_None, false); -} - -Stmt *RewriteModernObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { - QualType strType = getConstantStringStructType(); - - std::string S = "__NSConstantStringImpl_"; - - std::string tmpName = InFileName; - unsigned i; - for (i=0; i < tmpName.length(); i++) { - char c = tmpName.at(i); - // replace any non alphanumeric characters with '_'. - if (!isalpha(c) && (c < '0' || c > '9')) - tmpName[i] = '_'; - } - S += tmpName; - S += "_"; - S += utostr(NumObjCStringLiterals++); - - Preamble += "static __NSConstantStringImpl " + S; - Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; - Preamble += "0x000007c8,"; // utf8_str - // The pretty printer for StringLiteral handles escape characters properly. - std::string prettyBufS; - llvm::raw_string_ostream prettyBuf(prettyBufS); - Exp->getString()->printPretty(prettyBuf, 0, PrintingPolicy(LangOpts)); - Preamble += prettyBuf.str(); - Preamble += ","; - Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; - - VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), &Context->Idents.get(S), - strType, 0, SC_Static, SC_None); - DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, strType, VK_LValue, - SourceLocation()); - Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, - Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - // cast to NSConstantString * - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), - CK_CPointerToObjCPointerCast, Unop); - ReplaceStmt(Exp, cast); - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return cast; -} - -Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) { - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - - Expr *FlagExp = IntegerLiteral::Create(*Context, - llvm::APInt(IntSize, Exp->getValue()), - Context->IntTy, Exp->getLocation()); - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Context->ObjCBuiltinBoolTy, - CK_BitCast, FlagExp); - ParenExpr *PE = new (Context) ParenExpr(Exp->getLocation(), Exp->getExprLoc(), - cast); - ReplaceStmt(Exp, PE); - return PE; -} - -Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) { - // synthesize declaration of helper functions needed in this routine. - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - // use objc_msgSend() for all. - if (!MsgSendFunctionDecl) - SynthMsgSendFunctionDecl(); - if (!GetClassFunctionDecl) - SynthGetClassFunctionDecl(); - - FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; - SourceLocation StartLoc = Exp->getLocStart(); - SourceLocation EndLoc = Exp->getLocEnd(); - - // Synthesize a call to objc_msgSend(). - SmallVector MsgExprs; - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - - // Create a call to objc_getClass(""). It will be the 1st argument. - ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod(); - ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface(); - - IdentifierInfo *clsName = BoxingClass->getIdentifier(); - ClsExprs.push_back(StringLiteral::Create(*Context, - clsName->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(Cls); - - // Create a call to sel_registerName(":"), etc. - // it will be the 2nd argument. - SmallVector SelExprs; - SelExprs.push_back(StringLiteral::Create(*Context, - BoxingMethod->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(SelExp); - - // User provided sub-expression is the 3rd, and last, argument. - Expr *subExpr = Exp->getSubExpr(); - if (ImplicitCastExpr *ICE = dyn_cast(subExpr)) { - QualType type = ICE->getType(); - const Expr *SubExpr = ICE->IgnoreParenImpCasts(); - CastKind CK = CK_BitCast; - if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType()) - CK = CK_IntegralToBoolean; - subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr); - } - MsgExprs.push_back(subExpr); - - SmallVector ArgTypes; - ArgTypes.push_back(Context->getObjCIdType()); - ArgTypes.push_back(Context->getObjCSelType()); - for (ObjCMethodDecl::param_iterator PI = BoxingMethod->param_begin(), - E = BoxingMethod->param_end(); PI != E; ++PI) - ArgTypes.push_back((*PI)->getType()); - - QualType returnType = Exp->getType(); - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = MsgSendFlavor->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, - VK_LValue, SourceLocation()); - - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); - - // Now do the "normal" pointer to function cast. - QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - BoxingMethod->isVariadic()); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); - - const FunctionType *FT = msgSendType->getAs(); - CallExpr *CE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - EndLoc); - ReplaceStmt(Exp, CE); - return CE; -} - -Stmt *RewriteModernObjC::RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp) { - // synthesize declaration of helper functions needed in this routine. - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - // use objc_msgSend() for all. - if (!MsgSendFunctionDecl) - SynthMsgSendFunctionDecl(); - if (!GetClassFunctionDecl) - SynthGetClassFunctionDecl(); - - FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; - SourceLocation StartLoc = Exp->getLocStart(); - SourceLocation EndLoc = Exp->getLocEnd(); - - // Build the expression: __NSContainer_literal(int, ...).arr - QualType IntQT = Context->IntTy; - QualType NSArrayFType = - getSimpleFunctionType(Context->VoidTy, &IntQT, 1, true); - std::string NSArrayFName("__NSContainer_literal"); - FunctionDecl *NSArrayFD = SynthBlockInitFunctionDecl(NSArrayFName); - DeclRefExpr *NSArrayDRE = - new (Context) DeclRefExpr(NSArrayFD, false, NSArrayFType, VK_RValue, - SourceLocation()); - - SmallVector InitExprs; - unsigned NumElements = Exp->getNumElements(); - unsigned UnsignedIntSize = - static_cast(Context->getTypeSize(Context->UnsignedIntTy)); - Expr *count = IntegerLiteral::Create(*Context, - llvm::APInt(UnsignedIntSize, NumElements), - Context->UnsignedIntTy, SourceLocation()); - InitExprs.push_back(count); - for (unsigned i = 0; i < NumElements; i++) - InitExprs.push_back(Exp->getElement(i)); - Expr *NSArrayCallExpr = - new (Context) CallExpr(*Context, NSArrayDRE, &InitExprs[0], InitExprs.size(), - NSArrayFType, VK_LValue, SourceLocation()); - - FieldDecl *ARRFD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("arr"), - Context->getPointerType(Context->VoidPtrTy), 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ArrayLiteralME = - new (Context) MemberExpr(NSArrayCallExpr, false, ARRFD, - SourceLocation(), - ARRFD->getType(), VK_LValue, - OK_Ordinary); - QualType ConstIdT = Context->getObjCIdType().withConst(); - CStyleCastExpr * ArrayLiteralObjects = - NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(ConstIdT), - CK_BitCast, - ArrayLiteralME); - - // Synthesize a call to objc_msgSend(). - SmallVector MsgExprs; - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - QualType expType = Exp->getType(); - - // Create a call to objc_getClass("NSArray"). It will be th 1st argument. - ObjCInterfaceDecl *Class = - expType->getPointeeType()->getAs()->getInterface(); - - IdentifierInfo *clsName = Class->getIdentifier(); - ClsExprs.push_back(StringLiteral::Create(*Context, - clsName->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(Cls); - - // Create a call to sel_registerName("arrayWithObjects:count:"). - // it will be the 2nd argument. - SmallVector SelExprs; - ObjCMethodDecl *ArrayMethod = Exp->getArrayWithObjectsMethod(); - SelExprs.push_back(StringLiteral::Create(*Context, - ArrayMethod->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(SelExp); - - // (const id [])objects - MsgExprs.push_back(ArrayLiteralObjects); - - // (NSUInteger)cnt - Expr *cnt = IntegerLiteral::Create(*Context, - llvm::APInt(UnsignedIntSize, NumElements), - Context->UnsignedIntTy, SourceLocation()); - MsgExprs.push_back(cnt); - - - SmallVector ArgTypes; - ArgTypes.push_back(Context->getObjCIdType()); - ArgTypes.push_back(Context->getObjCSelType()); - for (ObjCMethodDecl::param_iterator PI = ArrayMethod->param_begin(), - E = ArrayMethod->param_end(); PI != E; ++PI) - ArgTypes.push_back((*PI)->getType()); - - QualType returnType = Exp->getType(); - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = MsgSendFlavor->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, - VK_LValue, SourceLocation()); - - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); - - // Now do the "normal" pointer to function cast. - QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - ArrayMethod->isVariadic()); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); - - const FunctionType *FT = msgSendType->getAs(); - CallExpr *CE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - EndLoc); - ReplaceStmt(Exp, CE); - return CE; -} - -Stmt *RewriteModernObjC::RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp) { - // synthesize declaration of helper functions needed in this routine. - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - // use objc_msgSend() for all. - if (!MsgSendFunctionDecl) - SynthMsgSendFunctionDecl(); - if (!GetClassFunctionDecl) - SynthGetClassFunctionDecl(); - - FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; - SourceLocation StartLoc = Exp->getLocStart(); - SourceLocation EndLoc = Exp->getLocEnd(); - - // Build the expression: __NSContainer_literal(int, ...).arr - QualType IntQT = Context->IntTy; - QualType NSDictFType = - getSimpleFunctionType(Context->VoidTy, &IntQT, 1, true); - std::string NSDictFName("__NSContainer_literal"); - FunctionDecl *NSDictFD = SynthBlockInitFunctionDecl(NSDictFName); - DeclRefExpr *NSDictDRE = - new (Context) DeclRefExpr(NSDictFD, false, NSDictFType, VK_RValue, - SourceLocation()); - - SmallVector KeyExprs; - SmallVector ValueExprs; - - unsigned NumElements = Exp->getNumElements(); - unsigned UnsignedIntSize = - static_cast(Context->getTypeSize(Context->UnsignedIntTy)); - Expr *count = IntegerLiteral::Create(*Context, - llvm::APInt(UnsignedIntSize, NumElements), - Context->UnsignedIntTy, SourceLocation()); - KeyExprs.push_back(count); - ValueExprs.push_back(count); - for (unsigned i = 0; i < NumElements; i++) { - ObjCDictionaryElement Element = Exp->getKeyValueElement(i); - KeyExprs.push_back(Element.Key); - ValueExprs.push_back(Element.Value); - } - - // (const id [])objects - Expr *NSValueCallExpr = - new (Context) CallExpr(*Context, NSDictDRE, &ValueExprs[0], ValueExprs.size(), - NSDictFType, VK_LValue, SourceLocation()); - - FieldDecl *ARRFD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("arr"), - Context->getPointerType(Context->VoidPtrTy), 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *DictLiteralValueME = - new (Context) MemberExpr(NSValueCallExpr, false, ARRFD, - SourceLocation(), - ARRFD->getType(), VK_LValue, - OK_Ordinary); - QualType ConstIdT = Context->getObjCIdType().withConst(); - CStyleCastExpr * DictValueObjects = - NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(ConstIdT), - CK_BitCast, - DictLiteralValueME); - // (const id [])keys - Expr *NSKeyCallExpr = - new (Context) CallExpr(*Context, NSDictDRE, &KeyExprs[0], KeyExprs.size(), - NSDictFType, VK_LValue, SourceLocation()); - - MemberExpr *DictLiteralKeyME = - new (Context) MemberExpr(NSKeyCallExpr, false, ARRFD, - SourceLocation(), - ARRFD->getType(), VK_LValue, - OK_Ordinary); - - CStyleCastExpr * DictKeyObjects = - NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(ConstIdT), - CK_BitCast, - DictLiteralKeyME); - - - - // Synthesize a call to objc_msgSend(). - SmallVector MsgExprs; - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - QualType expType = Exp->getType(); - - // Create a call to objc_getClass("NSArray"). It will be th 1st argument. - ObjCInterfaceDecl *Class = - expType->getPointeeType()->getAs()->getInterface(); - - IdentifierInfo *clsName = Class->getIdentifier(); - ClsExprs.push_back(StringLiteral::Create(*Context, - clsName->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(Cls); - - // Create a call to sel_registerName("arrayWithObjects:count:"). - // it will be the 2nd argument. - SmallVector SelExprs; - ObjCMethodDecl *DictMethod = Exp->getDictWithObjectsMethod(); - SelExprs.push_back(StringLiteral::Create(*Context, - DictMethod->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(SelExp); - - // (const id [])objects - MsgExprs.push_back(DictValueObjects); - - // (const id [])keys - MsgExprs.push_back(DictKeyObjects); - - // (NSUInteger)cnt - Expr *cnt = IntegerLiteral::Create(*Context, - llvm::APInt(UnsignedIntSize, NumElements), - Context->UnsignedIntTy, SourceLocation()); - MsgExprs.push_back(cnt); - - - SmallVector ArgTypes; - ArgTypes.push_back(Context->getObjCIdType()); - ArgTypes.push_back(Context->getObjCSelType()); - for (ObjCMethodDecl::param_iterator PI = DictMethod->param_begin(), - E = DictMethod->param_end(); PI != E; ++PI) { - QualType T = (*PI)->getType(); - if (const PointerType* PT = T->getAs()) { - QualType PointeeTy = PT->getPointeeType(); - convertToUnqualifiedObjCType(PointeeTy); - T = Context->getPointerType(PointeeTy); - } - ArgTypes.push_back(T); - } - - QualType returnType = Exp->getType(); - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = MsgSendFlavor->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, - VK_LValue, SourceLocation()); - - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); - - // Now do the "normal" pointer to function cast. - QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - DictMethod->isVariadic()); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); - - const FunctionType *FT = msgSendType->getAs(); - CallExpr *CE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - EndLoc); - ReplaceStmt(Exp, CE); - return CE; -} - -// struct __rw_objc_super { -// struct objc_object *object; struct objc_object *superClass; -// }; -QualType RewriteModernObjC::getSuperStructType() { - if (!SuperStructDecl) { - SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("__rw_objc_super")); - QualType FieldTypes[2]; - - // struct objc_object *object; - FieldTypes[0] = Context->getObjCIdType(); - // struct objc_object *superClass; - FieldTypes[1] = Context->getObjCIdType(); - - // Create fields - for (unsigned i = 0; i < 2; ++i) { - SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, - SourceLocation(), - SourceLocation(), 0, - FieldTypes[i], 0, - /*BitWidth=*/0, - /*Mutable=*/false, - ICIS_NoInit)); - } - - SuperStructDecl->completeDefinition(); - } - return Context->getTagDeclType(SuperStructDecl); -} - -QualType RewriteModernObjC::getConstantStringStructType() { - if (!ConstantStringDecl) { - ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("__NSConstantStringImpl")); - QualType FieldTypes[4]; - - // struct objc_object *receiver; - FieldTypes[0] = Context->getObjCIdType(); - // int flags; - FieldTypes[1] = Context->IntTy; - // char *str; - FieldTypes[2] = Context->getPointerType(Context->CharTy); - // long length; - FieldTypes[3] = Context->LongTy; - - // Create fields - for (unsigned i = 0; i < 4; ++i) { - ConstantStringDecl->addDecl(FieldDecl::Create(*Context, - ConstantStringDecl, - SourceLocation(), - SourceLocation(), 0, - FieldTypes[i], 0, - /*BitWidth=*/0, - /*Mutable=*/true, - ICIS_NoInit)); - } - - ConstantStringDecl->completeDefinition(); - } - return Context->getTagDeclType(ConstantStringDecl); -} - -/// getFunctionSourceLocation - returns start location of a function -/// definition. Complication arises when function has declared as -/// extern "C" or extern "C" {...} -static SourceLocation getFunctionSourceLocation (RewriteModernObjC &R, - FunctionDecl *FD) { - if (FD->isExternC() && !FD->isMain()) { - const DeclContext *DC = FD->getDeclContext(); - if (const LinkageSpecDecl *LSD = dyn_cast(DC)) - // if it is extern "C" {...}, return function decl's own location. - if (!LSD->getRBraceLoc().isValid()) - return LSD->getExternLoc(); - } - if (FD->getStorageClassAsWritten() != SC_None) - R.RewriteBlockLiteralFunctionDecl(FD); - return FD->getTypeSpecStartLoc(); -} - -/// SynthMsgSendStretCallExpr - This routine translates message expression -/// into a call to objc_msgSend_stret() entry point. Tricky part is that -/// nil check on receiver must be performed before calling objc_msgSend_stret. -/// MsgSendStretFlavor - function declaration objc_msgSend_stret(...) -/// msgSendType - function type of objc_msgSend_stret(...) -/// returnType - Result type of the method being synthesized. -/// ArgTypes - type of the arguments passed to objc_msgSend_stret, starting with receiver type. -/// MsgExprs - list of argument expressions being passed to objc_msgSend_stret, -/// starting with receiver. -/// Method - Method being rewritten. -Expr *RewriteModernObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, - QualType msgSendType, - QualType returnType, - SmallVectorImpl &ArgTypes, - SmallVectorImpl &MsgExprs, - ObjCMethodDecl *Method) { - // Now do the "normal" pointer to function cast. - QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - Method ? Method->isVariadic() : false); - castType = Context->getPointerType(castType); - - // build type for containing the objc_msgSend_stret object. - static unsigned stretCount=0; - std::string name = "__Stret"; name += utostr(stretCount); - std::string str = - "extern \"C\" void * __cdecl memset(void *_Dst, int _Val, size_t _Size);\n"; - str += "struct "; str += name; - str += " {\n\t"; - str += name; - str += "(id receiver, SEL sel"; - for (unsigned i = 2; i < ArgTypes.size(); i++) { - std::string ArgName = "arg"; ArgName += utostr(i); - ArgTypes[i].getAsStringInternal(ArgName, Context->getPrintingPolicy()); - str += ", "; str += ArgName; - } - // could be vararg. - for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { - std::string ArgName = "arg"; ArgName += utostr(i); - MsgExprs[i]->getType().getAsStringInternal(ArgName, - Context->getPrintingPolicy()); - str += ", "; str += ArgName; - } - - str += ") {\n"; - str += "\t if (receiver == 0)\n"; - str += "\t memset((void*)&s, 0, sizeof(s));\n"; - str += "\t else\n"; - str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); - str += ")(void *)objc_msgSend_stret)(receiver, sel"; - for (unsigned i = 2; i < ArgTypes.size(); i++) { - str += ", arg"; str += utostr(i); - } - // could be vararg. - for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { - str += ", arg"; str += utostr(i); - } - - str += ");\n"; - str += "\t}\n"; - str += "\t"; str += returnType.getAsString(Context->getPrintingPolicy()); - str += " s;\n"; - str += "};\n\n"; - SourceLocation FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); - InsertText(FunLocStart, str); - ++stretCount; - - // AST for __Stretn(receiver, args).s; - IdentifierInfo *ID = &Context->Idents.get(name); - FunctionDecl *FD = FunctionDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), ID, castType, 0, SC_Extern, - SC_None, false, false); - DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, castType, VK_RValue, - SourceLocation()); - CallExpr *STCE = new (Context) CallExpr(*Context, DRE, &MsgExprs[0], MsgExprs.size(), - castType, VK_LValue, SourceLocation()); - - FieldDecl *FieldD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("s"), - returnType, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(STCE, false, FieldD, SourceLocation(), - FieldD->getType(), VK_LValue, - OK_Ordinary); - - return ME; -} - -Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, - SourceLocation StartLoc, - SourceLocation EndLoc) { - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - if (!MsgSendFunctionDecl) - SynthMsgSendFunctionDecl(); - if (!MsgSendSuperFunctionDecl) - SynthMsgSendSuperFunctionDecl(); - if (!MsgSendStretFunctionDecl) - SynthMsgSendStretFunctionDecl(); - if (!MsgSendSuperStretFunctionDecl) - SynthMsgSendSuperStretFunctionDecl(); - if (!MsgSendFpretFunctionDecl) - SynthMsgSendFpretFunctionDecl(); - if (!GetClassFunctionDecl) - SynthGetClassFunctionDecl(); - if (!GetSuperClassFunctionDecl) - SynthGetSuperClassFunctionDecl(); - if (!GetMetaClassFunctionDecl) - SynthGetMetaClassFunctionDecl(); - - // default to objc_msgSend(). - FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; - // May need to use objc_msgSend_stret() as well. - FunctionDecl *MsgSendStretFlavor = 0; - if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { - QualType resultType = mDecl->getResultType(); - if (resultType->isRecordType()) - MsgSendStretFlavor = MsgSendStretFunctionDecl; - else if (resultType->isRealFloatingType()) - MsgSendFlavor = MsgSendFpretFunctionDecl; - } - - // Synthesize a call to objc_msgSend(). - SmallVector MsgExprs; - switch (Exp->getReceiverKind()) { - case ObjCMessageExpr::SuperClass: { - MsgSendFlavor = MsgSendSuperFunctionDecl; - if (MsgSendStretFlavor) - MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; - assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); - - ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); - - SmallVector InitExprs; - - // set the receiver to self, the first argument to all methods. - InitExprs.push_back( - NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK_BitCast, - new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), - false, - Context->getObjCIdType(), - VK_RValue, - SourceLocation())) - ); // set the 'receiver'. - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - ClsExprs.push_back(StringLiteral::Create(*Context, - ClassDecl->getIdentifier()->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - // (Class)objc_getClass("CurrentClass") - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, - EndLoc); - ClsExprs.clear(); - ClsExprs.push_back(Cls); - Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, - &ClsExprs[0], ClsExprs.size(), - StartLoc, EndLoc); - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - // To turn off a warning, type-cast to 'id' - InitExprs.push_back( // set 'super class', using class_getSuperclass(). - NoTypeInfoCStyleCastExpr(Context, - Context->getObjCIdType(), - CK_BitCast, Cls)); - // struct __rw_objc_super - QualType superType = getSuperStructType(); - Expr *SuperRep; - - if (LangOpts.MicrosoftExt) { - SynthSuperContructorFunctionDecl(); - // Simulate a contructor call... - DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, - false, superType, VK_LValue, - SourceLocation()); - SuperRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], - InitExprs.size(), - superType, VK_LValue, - SourceLocation()); - // The code for super is a little tricky to prevent collision with - // the structure definition in the header. The rewriter has it's own - // internal definition (__rw_objc_super) that is uses. This is why - // we need the cast below. For example: - // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) - // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - SuperRep = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(superType), - CK_BitCast, SuperRep); - } else { - // (struct __rw_objc_super) { } - InitListExpr *ILE = - new (Context) InitListExpr(*Context, SourceLocation(), - &InitExprs[0], InitExprs.size(), - SourceLocation()); - TypeSourceInfo *superTInfo - = Context->getTrivialTypeSourceInfo(superType); - SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, - superType, VK_LValue, - ILE, false); - // struct __rw_objc_super * - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - } - MsgExprs.push_back(SuperRep); - break; - } - - case ObjCMessageExpr::Class: { - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - ObjCInterfaceDecl *Class - = Exp->getClassReceiver()->getAs()->getInterface(); - IdentifierInfo *clsName = Class->getIdentifier(); - ClsExprs.push_back(StringLiteral::Create(*Context, - clsName->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, - Context->getObjCIdType(), - CK_BitCast, Cls); - MsgExprs.push_back(ArgExpr); - break; - } - - case ObjCMessageExpr::SuperInstance:{ - MsgSendFlavor = MsgSendSuperFunctionDecl; - if (MsgSendStretFlavor) - MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; - assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); - ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); - SmallVector InitExprs; - - InitExprs.push_back( - NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK_BitCast, - new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), - false, - Context->getObjCIdType(), - VK_RValue, SourceLocation())) - ); // set the 'receiver'. - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - ClsExprs.push_back(StringLiteral::Create(*Context, - ClassDecl->getIdentifier()->getName(), - StringLiteral::Ascii, false, argType, - SourceLocation())); - // (Class)objc_getClass("CurrentClass") - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - ClsExprs.clear(); - ClsExprs.push_back(Cls); - Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, - &ClsExprs[0], ClsExprs.size(), - StartLoc, EndLoc); - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - // To turn off a warning, type-cast to 'id' - InitExprs.push_back( - // set 'super class', using class_getSuperclass(). - NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK_BitCast, Cls)); - // struct __rw_objc_super - QualType superType = getSuperStructType(); - Expr *SuperRep; - - if (LangOpts.MicrosoftExt) { - SynthSuperContructorFunctionDecl(); - // Simulate a contructor call... - DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, - false, superType, VK_LValue, - SourceLocation()); - SuperRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], - InitExprs.size(), - superType, VK_LValue, SourceLocation()); - // The code for super is a little tricky to prevent collision with - // the structure definition in the header. The rewriter has it's own - // internal definition (__rw_objc_super) that is uses. This is why - // we need the cast below. For example: - // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) - // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - SuperRep = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(superType), - CK_BitCast, SuperRep); - } else { - // (struct __rw_objc_super) { } - InitListExpr *ILE = - new (Context) InitListExpr(*Context, SourceLocation(), - &InitExprs[0], InitExprs.size(), - SourceLocation()); - TypeSourceInfo *superTInfo - = Context->getTrivialTypeSourceInfo(superType); - SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, - superType, VK_RValue, ILE, - false); - } - MsgExprs.push_back(SuperRep); - break; - } - - case ObjCMessageExpr::Instance: { - // Remove all type-casts because it may contain objc-style types; e.g. - // Foo *. - Expr *recExpr = Exp->getInstanceReceiver(); - while (CStyleCastExpr *CE = dyn_cast(recExpr)) - recExpr = CE->getSubExpr(); - CastKind CK = recExpr->getType()->isObjCObjectPointerType() - ? CK_BitCast : recExpr->getType()->isBlockPointerType() - ? CK_BlockPointerToObjCPointerCast - : CK_CPointerToObjCPointerCast; - - recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK, recExpr); - MsgExprs.push_back(recExpr); - break; - } - } - - // Create a call to sel_registerName("selName"), it will be the 2nd argument. - SmallVector SelExprs; - QualType argType = Context->getPointerType(Context->CharTy); - SelExprs.push_back(StringLiteral::Create(*Context, - Exp->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size(), - StartLoc, - EndLoc); - MsgExprs.push_back(SelExp); - - // Now push any user supplied arguments. - for (unsigned i = 0; i < Exp->getNumArgs(); i++) { - Expr *userExpr = Exp->getArg(i); - // Make all implicit casts explicit...ICE comes in handy:-) - if (ImplicitCastExpr *ICE = dyn_cast(userExpr)) { - // Reuse the ICE type, it is exactly what the doctor ordered. - QualType type = ICE->getType(); - if (needToScanForQualifiers(type)) - type = Context->getObjCIdType(); - // Make sure we convert "type (^)(...)" to "type (*)(...)". - (void)convertBlockPointerToFunctionPointer(type); - const Expr *SubExpr = ICE->IgnoreParenImpCasts(); - CastKind CK; - if (SubExpr->getType()->isIntegralType(*Context) && - type->isBooleanType()) { - CK = CK_IntegralToBoolean; - } else if (type->isObjCObjectPointerType()) { - if (SubExpr->getType()->isBlockPointerType()) { - CK = CK_BlockPointerToObjCPointerCast; - } else if (SubExpr->getType()->isPointerType()) { - CK = CK_CPointerToObjCPointerCast; - } else { - CK = CK_BitCast; - } - } else { - CK = CK_BitCast; - } - - userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); - } - // Make id cast into an 'id' cast. - else if (CStyleCastExpr *CE = dyn_cast(userExpr)) { - if (CE->getType()->isObjCQualifiedIdType()) { - while ((CE = dyn_cast(userExpr))) - userExpr = CE->getSubExpr(); - CastKind CK; - if (userExpr->getType()->isIntegralType(*Context)) { - CK = CK_IntegralToPointer; - } else if (userExpr->getType()->isBlockPointerType()) { - CK = CK_BlockPointerToObjCPointerCast; - } else if (userExpr->getType()->isPointerType()) { - CK = CK_CPointerToObjCPointerCast; - } else { - CK = CK_BitCast; - } - userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK, userExpr); - } - } - MsgExprs.push_back(userExpr); - // We've transferred the ownership to MsgExprs. For now, we *don't* null - // out the argument in the original expression (since we aren't deleting - // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. - //Exp->setArg(i, 0); - } - // Generate the funky cast. - CastExpr *cast; - SmallVector ArgTypes; - QualType returnType; - - // Push 'id' and 'SEL', the 2 implicit arguments. - if (MsgSendFlavor == MsgSendSuperFunctionDecl) - ArgTypes.push_back(Context->getPointerType(getSuperStructType())); - else - ArgTypes.push_back(Context->getObjCIdType()); - ArgTypes.push_back(Context->getObjCSelType()); - if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { - // Push any user argument types. - for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), - E = OMD->param_end(); PI != E; ++PI) { - QualType t = (*PI)->getType()->isObjCQualifiedIdType() - ? Context->getObjCIdType() - : (*PI)->getType(); - // Make sure we convert "t (^)(...)" to "t (*)(...)". - (void)convertBlockPointerToFunctionPointer(t); - ArgTypes.push_back(t); - } - returnType = Exp->getType(); - convertToUnqualifiedObjCType(returnType); - (void)convertBlockPointerToFunctionPointer(returnType); - } else { - returnType = Context->getObjCIdType(); - } - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = MsgSendFlavor->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, - VK_LValue, SourceLocation()); - - // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). - // If we don't do this cast, we get the following bizarre warning/note: - // xx.m:13: warning: function called through a non-compatible type - // xx.m:13: note: if this code is reached, the program will abort - cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); - - // Now do the "normal" pointer to function cast. - QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - // If we don't have a method decl, force a variadic cast. - Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : true); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); - - const FunctionType *FT = msgSendType->getAs(); - CallExpr *CE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - EndLoc); - Stmt *ReplacingStmt = CE; - if (MsgSendStretFlavor) { - // We have the method which returns a struct/union. Must also generate - // call to objc_msgSend_stret and hang both varieties on a conditional - // expression which dictate which one to envoke depending on size of - // method's return type. - - Expr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, - msgSendType, returnType, - ArgTypes, MsgExprs, - Exp->getMethodDecl()); - - // Build sizeof(returnType) - UnaryExprOrTypeTraitExpr *sizeofExpr = - new (Context) UnaryExprOrTypeTraitExpr(UETT_SizeOf, - Context->getTrivialTypeSourceInfo(returnType), - Context->getSizeType(), SourceLocation(), - SourceLocation()); - // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) - // FIXME: Value of 8 is base on ppc32/x86 ABI for the most common cases. - // For X86 it is more complicated and some kind of target specific routine - // is needed to decide what to do. - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - IntegerLiteral *limit = IntegerLiteral::Create(*Context, - llvm::APInt(IntSize, 8), - Context->IntTy, - SourceLocation()); - BinaryOperator *lessThanExpr = - new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, - VK_RValue, OK_Ordinary, SourceLocation()); - // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) - ConditionalOperator *CondExpr = - new (Context) ConditionalOperator(lessThanExpr, - SourceLocation(), CE, - SourceLocation(), STCE, - returnType, VK_RValue, OK_Ordinary); - ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - CondExpr); - } - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return ReplacingStmt; -} - -Stmt *RewriteModernObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { - Stmt *ReplacingStmt = SynthMessageExpr(Exp, Exp->getLocStart(), - Exp->getLocEnd()); - - // Now do the actual rewrite. - ReplaceStmt(Exp, ReplacingStmt); - - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return ReplacingStmt; -} - -// typedef struct objc_object Protocol; -QualType RewriteModernObjC::getProtocolType() { - if (!ProtocolTypeDecl) { - TypeSourceInfo *TInfo - = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); - ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("Protocol"), - TInfo); - } - return Context->getTypeDeclType(ProtocolTypeDecl); -} - -/// RewriteObjCProtocolExpr - Rewrite a protocol expression into -/// a synthesized/forward data reference (to the protocol's metadata). -/// The forward references (and metadata) are generated in -/// RewriteModernObjC::HandleTranslationUnit(). -Stmt *RewriteModernObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { - std::string Name = "_OBJC_PROTOCOL_REFERENCE_$_" + - Exp->getProtocol()->getNameAsString(); - IdentifierInfo *ID = &Context->Idents.get(Name); - VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), ID, getProtocolType(), 0, - SC_Extern, SC_None); - DeclRefExpr *DRE = new (Context) DeclRefExpr(VD, false, getProtocolType(), - VK_LValue, SourceLocation()); - Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, - Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, SourceLocation()); - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), - CK_BitCast, - DerefExpr); - ReplaceStmt(Exp, castExpr); - ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return castExpr; - -} - -bool RewriteModernObjC::BufferContainsPPDirectives(const char *startBuf, - const char *endBuf) { - while (startBuf < endBuf) { - if (*startBuf == '#') { - // Skip whitespace. - for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf) - ; - if (!strncmp(startBuf, "if", strlen("if")) || - !strncmp(startBuf, "ifdef", strlen("ifdef")) || - !strncmp(startBuf, "ifndef", strlen("ifndef")) || - !strncmp(startBuf, "define", strlen("define")) || - !strncmp(startBuf, "undef", strlen("undef")) || - !strncmp(startBuf, "else", strlen("else")) || - !strncmp(startBuf, "elif", strlen("elif")) || - !strncmp(startBuf, "endif", strlen("endif")) || - !strncmp(startBuf, "pragma", strlen("pragma")) || - !strncmp(startBuf, "include", strlen("include")) || - !strncmp(startBuf, "import", strlen("import")) || - !strncmp(startBuf, "include_next", strlen("include_next"))) - return true; - } - startBuf++; - } - return false; -} - -/// IsTagDefinedInsideClass - This routine checks that a named tagged type -/// is defined inside an objective-c class. If so, it returns true. -bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, - TagDecl *Tag, - bool &IsNamedDefinition) { - if (!IDecl) - return false; - SourceLocation TagLocation; - if (RecordDecl *RD = dyn_cast(Tag)) { - RD = RD->getDefinition(); - if (!RD || !RD->getDeclName().getAsIdentifierInfo()) - return false; - IsNamedDefinition = true; - TagLocation = RD->getLocation(); - return Context->getSourceManager().isBeforeInTranslationUnit( - IDecl->getLocation(), TagLocation); - } - if (EnumDecl *ED = dyn_cast(Tag)) { - if (!ED || !ED->getDeclName().getAsIdentifierInfo()) - return false; - IsNamedDefinition = true; - TagLocation = ED->getLocation(); - return Context->getSourceManager().isBeforeInTranslationUnit( - IDecl->getLocation(), TagLocation); - - } - return false; -} - -/// RewriteObjCFieldDeclType - This routine rewrites a type into the buffer. -/// It handles elaborated types, as well as enum types in the process. -bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, - std::string &Result) { - if (isa(Type)) { - Result += "\t"; - return false; - } - - if (Type->isArrayType()) { - QualType ElemTy = Context->getBaseElementType(Type); - return RewriteObjCFieldDeclType(ElemTy, Result); - } - else if (Type->isRecordType()) { - RecordDecl *RD = Type->getAs()->getDecl(); - if (RD->isCompleteDefinition()) { - if (RD->isStruct()) - Result += "\n\tstruct "; - else if (RD->isUnion()) - Result += "\n\tunion "; - else - assert(false && "class not allowed as an ivar type"); - - Result += RD->getName(); - if (GlobalDefinedTags.count(RD)) { - // struct/union is defined globally, use it. - Result += " "; - return true; - } - Result += " {\n"; - for (RecordDecl::field_iterator i = RD->field_begin(), - e = RD->field_end(); i != e; ++i) { - FieldDecl *FD = *i; - RewriteObjCFieldDecl(FD, Result); - } - Result += "\t} "; - return true; - } - } - else if (Type->isEnumeralType()) { - EnumDecl *ED = Type->getAs()->getDecl(); - if (ED->isCompleteDefinition()) { - Result += "\n\tenum "; - Result += ED->getName(); - if (GlobalDefinedTags.count(ED)) { - // Enum is globall defined, use it. - Result += " "; - return true; - } - - Result += " {\n"; - for (EnumDecl::enumerator_iterator EC = ED->enumerator_begin(), - ECEnd = ED->enumerator_end(); EC != ECEnd; ++EC) { - Result += "\t"; Result += EC->getName(); Result += " = "; - llvm::APSInt Val = EC->getInitVal(); - Result += Val.toString(10); - Result += ",\n"; - } - Result += "\t} "; - return true; - } - } - - Result += "\t"; - convertObjCTypeToCStyleType(Type); - return false; -} - - -/// RewriteObjCFieldDecl - This routine rewrites a field into the buffer. -/// It handles elaborated types, as well as enum types in the process. -void RewriteModernObjC::RewriteObjCFieldDecl(FieldDecl *fieldDecl, - std::string &Result) { - QualType Type = fieldDecl->getType(); - std::string Name = fieldDecl->getNameAsString(); - - bool EleboratedType = RewriteObjCFieldDeclType(Type, Result); - if (!EleboratedType) - Type.getAsStringInternal(Name, Context->getPrintingPolicy()); - Result += Name; - if (fieldDecl->isBitField()) { - Result += " : "; Result += utostr(fieldDecl->getBitWidthValue(*Context)); - } - else if (EleboratedType && Type->isArrayType()) { - CanQualType CType = Context->getCanonicalType(Type); - while (isa(CType)) { - if (const ConstantArrayType *CAT = Context->getAsConstantArrayType(CType)) { - Result += "["; - llvm::APInt Dim = CAT->getSize(); - Result += utostr(Dim.getZExtValue()); - Result += "]"; - } - CType = CType->getAs()->getElementType(); - } - } - - Result += ";\n"; -} - -/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined -/// named aggregate types into the input buffer. -void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, - std::string &Result) { - QualType Type = fieldDecl->getType(); - if (isa(Type)) - return; - if (Type->isArrayType()) - Type = Context->getBaseElementType(Type); - ObjCContainerDecl *IDecl = - dyn_cast(fieldDecl->getDeclContext()); - - TagDecl *TD = 0; - if (Type->isRecordType()) { - TD = Type->getAs()->getDecl(); - } - else if (Type->isEnumeralType()) { - TD = Type->getAs()->getDecl(); - } - - if (TD) { - if (GlobalDefinedTags.count(TD)) - return; - - bool IsNamedDefinition = false; - if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) { - RewriteObjCFieldDeclType(Type, Result); - Result += ";"; - } - if (IsNamedDefinition) - GlobalDefinedTags.insert(TD); - } - -} - -/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to -/// an objective-c class with ivars. -void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, - std::string &Result) { - assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); - assert(CDecl->getName() != "" && - "Name missing in SynthesizeObjCInternalStruct"); - ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); - SmallVector IVars; - for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); - IVD; IVD = IVD->getNextIvar()) - IVars.push_back(IVD); - - SourceLocation LocStart = CDecl->getLocStart(); - SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - - // If no ivars and no root or if its root, directly or indirectly, - // have no ivars (thus not synthesized) then no need to synthesize this class. - if ((!CDecl->isThisDeclarationADefinition() || IVars.size() == 0) && - (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { - endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); - ReplaceText(LocStart, endBuf-startBuf, Result); - return; - } - - // Insert named struct/union definitions inside class to - // outer scope. This follows semantics of locally defined - // struct/unions in objective-c classes. - for (unsigned i = 0, e = IVars.size(); i < e; i++) - RewriteLocallyDefinedNamedAggregates(IVars[i], Result); - - Result += "\nstruct "; - Result += CDecl->getNameAsString(); - Result += "_IMPL {\n"; - - if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { - Result += "\tstruct "; Result += RCDecl->getNameAsString(); - Result += "_IMPL "; Result += RCDecl->getNameAsString(); - Result += "_IVARS;\n"; - } - - for (unsigned i = 0, e = IVars.size(); i < e; i++) - RewriteObjCFieldDecl(IVars[i], Result); - - Result += "};\n"; - endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); - ReplaceText(LocStart, endBuf-startBuf, Result); - // Mark this struct as having been generated. - if (!ObjCSynthesizedStructs.insert(CDecl)) - llvm_unreachable("struct already synthesize- RewriteObjCInternalStruct"); -} - -/// RewriteIvarOffsetSymbols - Rewrite ivar offset symbols of those ivars which -/// have been referenced in an ivar access expression. -void RewriteModernObjC::RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, - std::string &Result) { - // write out ivar offset symbols which have been referenced in an ivar - // access expression. - llvm::SmallPtrSet Ivars = ReferencedIvars[CDecl]; - if (Ivars.empty()) - return; - for (llvm::SmallPtrSet::iterator i = Ivars.begin(), - e = Ivars.end(); i != e; i++) { - ObjCIvarDecl *IvarDecl = (*i); - Result += "\n"; - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".objc_ivar$B\")) "; - Result += "extern \"C\" "; - if (LangOpts.MicrosoftExt && - IvarDecl->getAccessControl() != ObjCIvarDecl::Private && - IvarDecl->getAccessControl() != ObjCIvarDecl::Package) - Result += "__declspec(dllimport) "; - - Result += "unsigned long "; - WriteInternalIvarName(CDecl, IvarDecl, Result); - Result += ";"; - } -} - -//===----------------------------------------------------------------------===// -// Meta Data Emission -//===----------------------------------------------------------------------===// - - -/// RewriteImplementations - This routine rewrites all method implementations -/// and emits meta-data. - -void RewriteModernObjC::RewriteImplementations() { - int ClsDefCount = ClassImplementation.size(); - int CatDefCount = CategoryImplementation.size(); - - // Rewrite implemented methods - for (int i = 0; i < ClsDefCount; i++) { - ObjCImplementationDecl *OIMP = ClassImplementation[i]; - ObjCInterfaceDecl *CDecl = OIMP->getClassInterface(); - if (CDecl->isImplicitInterfaceDecl()) - assert(false && - "Legacy implicit interface rewriting not supported in moder abi"); - RewriteImplementationDecl(OIMP); - } - - for (int i = 0; i < CatDefCount; i++) { - ObjCCategoryImplDecl *CIMP = CategoryImplementation[i]; - ObjCInterfaceDecl *CDecl = CIMP->getClassInterface(); - if (CDecl->isImplicitInterfaceDecl()) - assert(false && - "Legacy implicit interface rewriting not supported in moder abi"); - RewriteImplementationDecl(CIMP); - } -} - -void RewriteModernObjC::RewriteByRefString(std::string &ResultStr, - const std::string &Name, - ValueDecl *VD, bool def) { - assert(BlockByRefDeclNo.count(VD) && - "RewriteByRefString: ByRef decl missing"); - if (def) - ResultStr += "struct "; - ResultStr += "__Block_byref_" + Name + - "_" + utostr(BlockByRefDeclNo[VD]) ; -} - -static bool HasLocalVariableExternalStorage(ValueDecl *VD) { - if (VarDecl *Var = dyn_cast(VD)) - return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); - return false; -} - -std::string RewriteModernObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, - StringRef funcName, - std::string Tag) { - const FunctionType *AFT = CE->getFunctionType(); - QualType RT = AFT->getResultType(); - std::string StructRef = "struct " + Tag; - std::string S = "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + - funcName.str() + "_block_func_" + utostr(i); - - BlockDecl *BD = CE->getBlockDecl(); - - if (isa(AFT)) { - // No user-supplied arguments. Still need to pass in a pointer to the - // block (to reference imported block decl refs). - S += "(" + StructRef + " *__cself)"; - } else if (BD->param_empty()) { - S += "(" + StructRef + " *__cself)"; - } else { - const FunctionProtoType *FT = cast(AFT); - assert(FT && "SynthesizeBlockFunc: No function proto"); - S += '('; - // first add the implicit argument. - S += StructRef + " *__cself, "; - std::string ParamStr; - for (BlockDecl::param_iterator AI = BD->param_begin(), - E = BD->param_end(); AI != E; ++AI) { - if (AI != BD->param_begin()) S += ", "; - ParamStr = (*AI)->getNameAsString(); - QualType QT = (*AI)->getType(); - (void)convertBlockPointerToFunctionPointer(QT); - QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); - S += ParamStr; - } - if (FT->isVariadic()) { - if (!BD->param_empty()) S += ", "; - S += "..."; - } - S += ')'; - } - S += " {\n"; - - // Create local declarations to avoid rewriting all closure decl ref exprs. - // First, emit a declaration for all "by ref" decls. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - S += " "; - std::string Name = (*I)->getNameAsString(); - std::string TypeString; - RewriteByRefString(TypeString, Name, (*I)); - TypeString += " *"; - Name = TypeString + Name; - S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; - } - // Next, emit a declaration for all "by copy" declarations. - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - S += " "; - // Handle nested closure invocation. For example: - // - // void (^myImportedClosure)(void); - // myImportedClosure = ^(void) { setGlobalInt(x + y); }; - // - // void (^anotherClosure)(void); - // anotherClosure = ^(void) { - // myImportedClosure(); // import and invoke the closure - // }; - // - if (isTopLevelBlockPointerType((*I)->getType())) { - RewriteBlockPointerTypeVariable(S, (*I)); - S += " = ("; - RewriteBlockPointerType(S, (*I)->getType()); - S += ")"; - S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; - } - else { - std::string Name = (*I)->getNameAsString(); - QualType QT = (*I)->getType(); - if (HasLocalVariableExternalStorage(*I)) - QT = Context->getPointerType(QT); - QT.getAsStringInternal(Name, Context->getPrintingPolicy()); - S += Name + " = __cself->" + - (*I)->getNameAsString() + "; // bound by copy\n"; - } - } - std::string RewrittenStr = RewrittenBlockExprs[CE]; - const char *cstr = RewrittenStr.c_str(); - while (*cstr++ != '{') ; - S += cstr; - S += "\n"; - return S; -} - -std::string RewriteModernObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, - StringRef funcName, - std::string Tag) { - std::string StructRef = "struct " + Tag; - std::string S = "static void __"; - - S += funcName; - S += "_block_copy_" + utostr(i); - S += "(" + StructRef; - S += "*dst, " + StructRef; - S += "*src) {"; - for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), - E = ImportedBlockDecls.end(); I != E; ++I) { - ValueDecl *VD = (*I); - S += "_Block_object_assign((void*)&dst->"; - S += (*I)->getNameAsString(); - S += ", (void*)src->"; - S += (*I)->getNameAsString(); - if (BlockByRefDeclsPtrSet.count((*I))) - S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; - else if (VD->getType()->isBlockPointerType()) - S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; - else - S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; - } - S += "}\n"; - - S += "\nstatic void __"; - S += funcName; - S += "_block_dispose_" + utostr(i); - S += "(" + StructRef; - S += "*src) {"; - for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), - E = ImportedBlockDecls.end(); I != E; ++I) { - ValueDecl *VD = (*I); - S += "_Block_object_dispose((void*)src->"; - S += (*I)->getNameAsString(); - if (BlockByRefDeclsPtrSet.count((*I))) - S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; - else if (VD->getType()->isBlockPointerType()) - S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; - else - S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; - } - S += "}\n"; - return S; -} - -std::string RewriteModernObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, - std::string Desc) { - std::string S = "\nstruct " + Tag; - std::string Constructor = " " + Tag; - - S += " {\n struct __block_impl impl;\n"; - S += " struct " + Desc; - S += "* Desc;\n"; - - Constructor += "(void *fp, "; // Invoke function pointer. - Constructor += "struct " + Desc; // Descriptor pointer. - Constructor += " *desc"; - - if (BlockDeclRefs.size()) { - // Output all "by copy" declarations. - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - S += " "; - std::string FieldName = (*I)->getNameAsString(); - std::string ArgName = "_" + FieldName; - // Handle nested closure invocation. For example: - // - // void (^myImportedBlock)(void); - // myImportedBlock = ^(void) { setGlobalInt(x + y); }; - // - // void (^anotherBlock)(void); - // anotherBlock = ^(void) { - // myImportedBlock(); // import and invoke the closure - // }; - // - if (isTopLevelBlockPointerType((*I)->getType())) { - S += "struct __block_impl *"; - Constructor += ", void *" + ArgName; - } else { - QualType QT = (*I)->getType(); - if (HasLocalVariableExternalStorage(*I)) - QT = Context->getPointerType(QT); - QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); - QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); - Constructor += ", " + ArgName; - } - S += FieldName + ";\n"; - } - // Output all "by ref" declarations. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - S += " "; - std::string FieldName = (*I)->getNameAsString(); - std::string ArgName = "_" + FieldName; - { - std::string TypeString; - RewriteByRefString(TypeString, FieldName, (*I)); - TypeString += " *"; - FieldName = TypeString + FieldName; - ArgName = TypeString + ArgName; - Constructor += ", " + ArgName; - } - S += FieldName + "; // by ref\n"; - } - // Finish writing the constructor. - Constructor += ", int flags=0)"; - // Initialize all "by copy" arguments. - bool firsTime = true; - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - std::string Name = (*I)->getNameAsString(); - if (firsTime) { - Constructor += " : "; - firsTime = false; - } - else - Constructor += ", "; - if (isTopLevelBlockPointerType((*I)->getType())) - Constructor += Name + "((struct __block_impl *)_" + Name + ")"; - else - Constructor += Name + "(_" + Name + ")"; - } - // Initialize all "by ref" arguments. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - std::string Name = (*I)->getNameAsString(); - if (firsTime) { - Constructor += " : "; - firsTime = false; - } - else - Constructor += ", "; - Constructor += Name + "(_" + Name + "->__forwarding)"; - } - - Constructor += " {\n"; - if (GlobalVarDecl) - Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; - else - Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; - Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; - - Constructor += " Desc = desc;\n"; - } else { - // Finish writing the constructor. - Constructor += ", int flags=0) {\n"; - if (GlobalVarDecl) - Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; - else - Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; - Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; - Constructor += " Desc = desc;\n"; - } - Constructor += " "; - Constructor += "}\n"; - S += Constructor; - S += "};\n"; - return S; -} - -std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag, - std::string ImplTag, int i, - StringRef FunName, - unsigned hasCopy) { - std::string S = "\nstatic struct " + DescTag; - - S += " {\n size_t reserved;\n"; - S += " size_t Block_size;\n"; - if (hasCopy) { - S += " void (*copy)(struct "; - S += ImplTag; S += "*, struct "; - S += ImplTag; S += "*);\n"; - - S += " void (*dispose)(struct "; - S += ImplTag; S += "*);\n"; - } - S += "} "; - - S += DescTag + "_DATA = { 0, sizeof(struct "; - S += ImplTag + ")"; - if (hasCopy) { - S += ", __" + FunName.str() + "_block_copy_" + utostr(i); - S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); - } - S += "};\n"; - return S; -} - -void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, - StringRef FunName) { - bool RewriteSC = (GlobalVarDecl && - !Blocks.empty() && - GlobalVarDecl->getStorageClass() == SC_Static && - GlobalVarDecl->getType().getCVRQualifiers()); - if (RewriteSC) { - std::string SC(" void __"); - SC += GlobalVarDecl->getNameAsString(); - SC += "() {}"; - InsertText(FunLocStart, SC); - } - - // Insert closures that were part of the function. - for (unsigned i = 0, count=0; i < Blocks.size(); i++) { - CollectBlockDeclRefInfo(Blocks[i]); - // Need to copy-in the inner copied-in variables not actually used in this - // block. - for (int j = 0; j < InnerDeclRefsCount[i]; j++) { - DeclRefExpr *Exp = InnerDeclRefs[count++]; - ValueDecl *VD = Exp->getDecl(); - BlockDeclRefs.push_back(Exp); - if (!VD->hasAttr()) { - if (!BlockByCopyDeclsPtrSet.count(VD)) { - BlockByCopyDeclsPtrSet.insert(VD); - BlockByCopyDecls.push_back(VD); - } - continue; - } - - if (!BlockByRefDeclsPtrSet.count(VD)) { - BlockByRefDeclsPtrSet.insert(VD); - BlockByRefDecls.push_back(VD); - } - - // imported objects in the inner blocks not used in the outer - // blocks must be copied/disposed in the outer block as well. - if (VD->getType()->isObjCObjectPointerType() || - VD->getType()->isBlockPointerType()) - ImportedBlockDecls.insert(VD); - } - - std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); - std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); - - std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); - - InsertText(FunLocStart, CI); - - std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); - - InsertText(FunLocStart, CF); - - if (ImportedBlockDecls.size()) { - std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); - InsertText(FunLocStart, HF); - } - std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, - ImportedBlockDecls.size() > 0); - InsertText(FunLocStart, BD); - - BlockDeclRefs.clear(); - BlockByRefDecls.clear(); - BlockByRefDeclsPtrSet.clear(); - BlockByCopyDecls.clear(); - BlockByCopyDeclsPtrSet.clear(); - ImportedBlockDecls.clear(); - } - if (RewriteSC) { - // Must insert any 'const/volatile/static here. Since it has been - // removed as result of rewriting of block literals. - std::string SC; - if (GlobalVarDecl->getStorageClass() == SC_Static) - SC = "static "; - if (GlobalVarDecl->getType().isConstQualified()) - SC += "const "; - if (GlobalVarDecl->getType().isVolatileQualified()) - SC += "volatile "; - if (GlobalVarDecl->getType().isRestrictQualified()) - SC += "restrict "; - InsertText(FunLocStart, SC); - } - if (GlobalConstructionExp) { - // extra fancy dance for global literal expression. - - // Always the latest block expression on the block stack. - std::string Tag = "__"; - Tag += FunName; - Tag += "_block_impl_"; - Tag += utostr(Blocks.size()-1); - std::string globalBuf = "static "; - globalBuf += Tag; globalBuf += " "; - std::string SStr; - - llvm::raw_string_ostream constructorExprBuf(SStr); - GlobalConstructionExp->printPretty(constructorExprBuf, 0, - PrintingPolicy(LangOpts)); - globalBuf += constructorExprBuf.str(); - globalBuf += ";\n"; - InsertText(FunLocStart, globalBuf); - GlobalConstructionExp = 0; - } - - Blocks.clear(); - InnerDeclRefsCount.clear(); - InnerDeclRefs.clear(); - RewrittenBlockExprs.clear(); -} - -void RewriteModernObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { - SourceLocation FunLocStart = - (!Blocks.empty()) ? getFunctionSourceLocation(*this, FD) - : FD->getTypeSpecStartLoc(); - StringRef FuncName = FD->getName(); - - SynthesizeBlockLiterals(FunLocStart, FuncName); -} - -static void BuildUniqueMethodName(std::string &Name, - ObjCMethodDecl *MD) { - ObjCInterfaceDecl *IFace = MD->getClassInterface(); - Name = IFace->getName(); - Name += "__" + MD->getSelector().getAsString(); - // Convert colons to underscores. - std::string::size_type loc = 0; - while ((loc = Name.find(":", loc)) != std::string::npos) - Name.replace(loc, 1, "_"); -} - -void RewriteModernObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { - //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); - //SourceLocation FunLocStart = MD->getLocStart(); - SourceLocation FunLocStart = MD->getLocStart(); - std::string FuncName; - BuildUniqueMethodName(FuncName, MD); - SynthesizeBlockLiterals(FunLocStart, FuncName); -} - -void RewriteModernObjC::GetBlockDeclRefExprs(Stmt *S) { - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - if (BlockExpr *CBE = dyn_cast(*CI)) - GetBlockDeclRefExprs(CBE->getBody()); - else - GetBlockDeclRefExprs(*CI); - } - // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - // FIXME: Handle enums. - if (!isa(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - if (HasLocalVariableExternalStorage(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - } - } - - return; -} - -void RewriteModernObjC::GetInnerBlockDeclRefExprs(Stmt *S, - SmallVector &InnerBlockDeclRefs, - llvm::SmallPtrSet &InnerContexts) { - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - if (BlockExpr *CBE = dyn_cast(*CI)) { - InnerContexts.insert(cast(CBE->getBlockDecl())); - GetInnerBlockDeclRefExprs(CBE->getBody(), - InnerBlockDeclRefs, - InnerContexts); - } - else - GetInnerBlockDeclRefExprs(*CI, - InnerBlockDeclRefs, - InnerContexts); - - } - // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - if (!isa(DRE->getDecl()) && - !InnerContexts.count(DRE->getDecl()->getDeclContext())) - InnerBlockDeclRefs.push_back(DRE); - if (VarDecl *Var = dyn_cast(DRE->getDecl())) - if (Var->isFunctionOrMethodVarDecl()) - ImportedLocalExternalDecls.insert(Var); - } - } - - return; -} - -/// convertObjCTypeToCStyleType - This routine converts such objc types -/// as qualified objects, and blocks to their closest c/c++ types that -/// it can. It returns true if input type was modified. -bool RewriteModernObjC::convertObjCTypeToCStyleType(QualType &T) { - QualType oldT = T; - convertBlockPointerToFunctionPointer(T); - if (T->isFunctionPointerType()) { - QualType PointeeTy; - if (const PointerType* PT = T->getAs()) { - PointeeTy = PT->getPointeeType(); - if (const FunctionType *FT = PointeeTy->getAs()) { - T = convertFunctionTypeOfBlocks(FT); - T = Context->getPointerType(T); - } - } - } - - convertToUnqualifiedObjCType(T); - return T != oldT; -} - -/// convertFunctionTypeOfBlocks - This routine converts a function type -/// whose result type may be a block pointer or whose argument type(s) -/// might be block pointers to an equivalent function type replacing -/// all block pointers to function pointers. -QualType RewriteModernObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { - const FunctionProtoType *FTP = dyn_cast(FT); - // FTP will be null for closures that don't take arguments. - // Generate a funky cast. - SmallVector ArgTypes; - QualType Res = FT->getResultType(); - bool modified = convertObjCTypeToCStyleType(Res); - - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I && (I != E); ++I) { - QualType t = *I; - // Make sure we convert "t (^)(...)" to "t (*)(...)". - if (convertObjCTypeToCStyleType(t)) - modified = true; - ArgTypes.push_back(t); - } - } - QualType FuncType; - if (modified) - FuncType = getSimpleFunctionType(Res, &ArgTypes[0], ArgTypes.size()); - else FuncType = QualType(FT, 0); - return FuncType; -} - -Stmt *RewriteModernObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { - // Navigate to relevant type information. - const BlockPointerType *CPT = 0; - - if (const DeclRefExpr *DRE = dyn_cast(BlockExp)) { - CPT = DRE->getType()->getAs(); - } else if (const MemberExpr *MExpr = dyn_cast(BlockExp)) { - CPT = MExpr->getType()->getAs(); - } - else if (const ParenExpr *PRE = dyn_cast(BlockExp)) { - return SynthesizeBlockCall(Exp, PRE->getSubExpr()); - } - else if (const ImplicitCastExpr *IEXPR = dyn_cast(BlockExp)) - CPT = IEXPR->getType()->getAs(); - else if (const ConditionalOperator *CEXPR = - dyn_cast(BlockExp)) { - Expr *LHSExp = CEXPR->getLHS(); - Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); - Expr *RHSExp = CEXPR->getRHS(); - Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); - Expr *CONDExp = CEXPR->getCond(); - ConditionalOperator *CondExpr = - new (Context) ConditionalOperator(CONDExp, - SourceLocation(), cast(LHSStmt), - SourceLocation(), cast(RHSStmt), - Exp->getType(), VK_RValue, OK_Ordinary); - return CondExpr; - } else if (const ObjCIvarRefExpr *IRE = dyn_cast(BlockExp)) { - CPT = IRE->getType()->getAs(); - } else if (const PseudoObjectExpr *POE - = dyn_cast(BlockExp)) { - CPT = POE->getType()->castAs(); - } else { - assert(1 && "RewriteBlockClass: Bad type"); - } - assert(CPT && "RewriteBlockClass: Bad type"); - const FunctionType *FT = CPT->getPointeeType()->getAs(); - assert(FT && "RewriteBlockClass: Bad type"); - const FunctionProtoType *FTP = dyn_cast(FT); - // FTP will be null for closures that don't take arguments. - - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("__block_impl")); - QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); - - // Generate a funky cast. - SmallVector ArgTypes; - - // Push the block argument type. - ArgTypes.push_back(PtrBlock); - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I && (I != E); ++I) { - QualType t = *I; - // Make sure we convert "t (^)(...)" to "t (*)(...)". - if (!convertBlockPointerToFunctionPointer(t)) - convertToUnqualifiedObjCType(t); - ArgTypes.push_back(t); - } - } - // Now do the pointer to function cast. - QualType PtrToFuncCastType - = getSimpleFunctionType(Exp->getType(), &ArgTypes[0], ArgTypes.size()); - - PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); - - CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, - CK_BitCast, - const_cast(BlockExp)); - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - BlkCast); - //PE->dump(); - - FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("FuncPtr"), - Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), - FD->getType(), VK_LValue, - OK_Ordinary); - - - CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, - CK_BitCast, ME); - PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); - - SmallVector BlkExprs; - // Add the implicit argument. - BlkExprs.push_back(BlkCast); - // Add the user arguments. - for (CallExpr::arg_iterator I = Exp->arg_begin(), - E = Exp->arg_end(); I != E; ++I) { - BlkExprs.push_back(*I); - } - CallExpr *CE = new (Context) CallExpr(*Context, PE, &BlkExprs[0], - BlkExprs.size(), - Exp->getType(), VK_RValue, - SourceLocation()); - return CE; -} - -// We need to return the rewritten expression to handle cases where the -// DeclRefExpr is embedded in another expression being rewritten. -// For example: -// -// int main() { -// __block Foo *f; -// __block int i; -// -// void (^myblock)() = ^() { -// [f test]; // f is a DeclRefExpr embedded in a message (which is being rewritten). -// i = 77; -// }; -//} -Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { - // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR - // for each DeclRefExp where BYREFVAR is name of the variable. - ValueDecl *VD = DeclRefExp->getDecl(); - bool isArrow = DeclRefExp->refersToEnclosingLocal(); - - FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("__forwarding"), - Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, - FD, SourceLocation(), - FD->getType(), VK_LValue, - OK_Ordinary); - - StringRef Name = VD->getName(); - FD = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), - &Context->Idents.get(Name), - Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), - DeclRefExp->getType(), VK_LValue, OK_Ordinary); - - - - // Need parens to enforce precedence. - ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), - DeclRefExp->getExprLoc(), - ME); - ReplaceStmt(DeclRefExp, PE); - return PE; -} - -// Rewrites the imported local variable V with external storage -// (static, extern, etc.) as *V -// -Stmt *RewriteModernObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { - ValueDecl *VD = DRE->getDecl(); - if (VarDecl *Var = dyn_cast(VD)) - if (!ImportedLocalExternalDecls.count(Var)) - return DRE; - Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), - VK_LValue, OK_Ordinary, - DRE->getLocation()); - // Need parens to enforce precedence. - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - Exp); - ReplaceStmt(DRE, PE); - return PE; -} - -void RewriteModernObjC::RewriteCastExpr(CStyleCastExpr *CE) { - SourceLocation LocStart = CE->getLParenLoc(); - SourceLocation LocEnd = CE->getRParenLoc(); - - // Need to avoid trying to rewrite synthesized casts. - if (LocStart.isInvalid()) - return; - // Need to avoid trying to rewrite casts contained in macros. - if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) - return; - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - QualType QT = CE->getType(); - const Type* TypePtr = QT->getAs(); - if (isa(TypePtr)) { - const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); - QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); - std::string TypeAsString = "("; - RewriteBlockPointerType(TypeAsString, QT); - TypeAsString += ")"; - ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); - return; - } - // advance the location to startArgList. - const char *argPtr = startBuf; - - while (*argPtr++ && (argPtr < endBuf)) { - switch (*argPtr) { - case '^': - // Replace the '^' with '*'. - LocStart = LocStart.getLocWithOffset(argPtr-startBuf); - ReplaceText(LocStart, 1, "*"); - break; - } - } - return; -} - -void RewriteModernObjC::RewriteImplicitCastObjCExpr(CastExpr *IC) { - CastKind CastKind = IC->getCastKind(); - if (CastKind != CK_BlockPointerToObjCPointerCast && - CastKind != CK_AnyPointerToBlockPointerCast) - return; - - QualType QT = IC->getType(); - (void)convertBlockPointerToFunctionPointer(QT); - std::string TypeString(QT.getAsString(Context->getPrintingPolicy())); - std::string Str = "("; - Str += TypeString; - Str += ")"; - InsertText(IC->getSubExpr()->getLocStart(), &Str[0], Str.size()); - - return; -} - -void RewriteModernObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { - SourceLocation DeclLoc = FD->getLocation(); - unsigned parenCount = 0; - - // We have 1 or more arguments that have closure pointers. - const char *startBuf = SM->getCharacterData(DeclLoc); - const char *startArgList = strchr(startBuf, '('); - - assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); - - parenCount++; - // advance the location to startArgList. - DeclLoc = DeclLoc.getLocWithOffset(startArgList-startBuf); - assert((DeclLoc.isValid()) && "Invalid DeclLoc"); - - const char *argPtr = startArgList; - - while (*argPtr++ && parenCount) { - switch (*argPtr) { - case '^': - // Replace the '^' with '*'. - DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); - ReplaceText(DeclLoc, 1, "*"); - break; - case '(': - parenCount++; - break; - case ')': - parenCount--; - break; - } - } - return; -} - -bool RewriteModernObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { - const FunctionProtoType *FTP; - const PointerType *PT = QT->getAs(); - if (PT) { - FTP = PT->getPointeeType()->getAs(); - } else { - const BlockPointerType *BPT = QT->getAs(); - assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); - FTP = BPT->getPointeeType()->getAs(); - } - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I != E; ++I) - if (isTopLevelBlockPointerType(*I)) - return true; - } - return false; -} - -bool RewriteModernObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { - const FunctionProtoType *FTP; - const PointerType *PT = QT->getAs(); - if (PT) { - FTP = PT->getPointeeType()->getAs(); - } else { - const BlockPointerType *BPT = QT->getAs(); - assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); - FTP = BPT->getPointeeType()->getAs(); - } - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I != E; ++I) { - if ((*I)->isObjCQualifiedIdType()) - return true; - if ((*I)->isObjCObjectPointerType() && - (*I)->getPointeeType()->isObjCQualifiedInterfaceType()) - return true; - } - - } - return false; -} - -void RewriteModernObjC::GetExtentOfArgList(const char *Name, const char *&LParen, - const char *&RParen) { - const char *argPtr = strchr(Name, '('); - assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); - - LParen = argPtr; // output the start. - argPtr++; // skip past the left paren. - unsigned parenCount = 1; - - while (*argPtr && parenCount) { - switch (*argPtr) { - case '(': parenCount++; break; - case ')': parenCount--; break; - default: break; - } - if (parenCount) argPtr++; - } - assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); - RParen = argPtr; // output the end -} - -void RewriteModernObjC::RewriteBlockPointerDecl(NamedDecl *ND) { - if (FunctionDecl *FD = dyn_cast(ND)) { - RewriteBlockPointerFunctionArgs(FD); - return; - } - // Handle Variables and Typedefs. - SourceLocation DeclLoc = ND->getLocation(); - QualType DeclT; - if (VarDecl *VD = dyn_cast(ND)) - DeclT = VD->getType(); - else if (TypedefNameDecl *TDD = dyn_cast(ND)) - DeclT = TDD->getUnderlyingType(); - else if (FieldDecl *FD = dyn_cast(ND)) - DeclT = FD->getType(); - else - llvm_unreachable("RewriteBlockPointerDecl(): Decl type not yet handled"); - - const char *startBuf = SM->getCharacterData(DeclLoc); - const char *endBuf = startBuf; - // scan backward (from the decl location) for the end of the previous decl. - while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) - startBuf--; - SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); - std::string buf; - unsigned OrigLength=0; - // *startBuf != '^' if we are dealing with a pointer to function that - // may take block argument types (which will be handled below). - if (*startBuf == '^') { - // Replace the '^' with '*', computing a negative offset. - buf = '*'; - startBuf++; - OrigLength++; - } - while (*startBuf != ')') { - buf += *startBuf; - startBuf++; - OrigLength++; - } - buf += ')'; - OrigLength++; - - if (PointerTypeTakesAnyBlockArguments(DeclT) || - PointerTypeTakesAnyObjCQualifiedType(DeclT)) { - // Replace the '^' with '*' for arguments. - // Replace id

with id/*<>*/ - DeclLoc = ND->getLocation(); - startBuf = SM->getCharacterData(DeclLoc); - const char *argListBegin, *argListEnd; - GetExtentOfArgList(startBuf, argListBegin, argListEnd); - while (argListBegin < argListEnd) { - if (*argListBegin == '^') - buf += '*'; - else if (*argListBegin == '<') { - buf += "/*"; - buf += *argListBegin++; - OrigLength++;; - while (*argListBegin != '>') { - buf += *argListBegin++; - OrigLength++; - } - buf += *argListBegin; - buf += "*/"; - } - else - buf += *argListBegin; - argListBegin++; - OrigLength++; - } - buf += ')'; - OrigLength++; - } - ReplaceText(Start, OrigLength, buf); - - return; -} - - -/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: -/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, -/// struct Block_byref_id_object *src) { -/// _Block_object_assign (&_dest->object, _src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT -/// [|BLOCK_FIELD_IS_WEAK]) // object -/// _Block_object_assign(&_dest->object, _src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK -/// [|BLOCK_FIELD_IS_WEAK]) // block -/// } -/// And: -/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { -/// _Block_object_dispose(_src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT -/// [|BLOCK_FIELD_IS_WEAK]) // object -/// _Block_object_dispose(_src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK -/// [|BLOCK_FIELD_IS_WEAK]) // block -/// } - -std::string RewriteModernObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, - int flag) { - std::string S; - if (CopyDestroyCache.count(flag)) - return S; - CopyDestroyCache.insert(flag); - S = "static void __Block_byref_id_object_copy_"; - S += utostr(flag); - S += "(void *dst, void *src) {\n"; - - // offset into the object pointer is computed as: - // void * + void* + int + int + void* + void * - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - unsigned VoidPtrSize = - static_cast(Context->getTypeSize(Context->VoidPtrTy)); - - unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); - S += " _Block_object_assign((char*)dst + "; - S += utostr(offset); - S += ", *(void * *) ((char*)src + "; - S += utostr(offset); - S += "), "; - S += utostr(flag); - S += ");\n}\n"; - - S += "static void __Block_byref_id_object_dispose_"; - S += utostr(flag); - S += "(void *src) {\n"; - S += " _Block_object_dispose(*(void * *) ((char*)src + "; - S += utostr(offset); - S += "), "; - S += utostr(flag); - S += ");\n}\n"; - return S; -} - -/// RewriteByRefVar - For each __block typex ND variable this routine transforms -/// the declaration into: -/// struct __Block_byref_ND { -/// void *__isa; // NULL for everything except __weak pointers -/// struct __Block_byref_ND *__forwarding; -/// int32_t __flags; -/// int32_t __size; -/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object -/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object -/// typex ND; -/// }; -/// -/// It then replaces declaration of ND variable with: -/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, -/// __size=sizeof(struct __Block_byref_ND), -/// ND=initializer-if-any}; -/// -/// -void RewriteModernObjC::RewriteByRefVar(VarDecl *ND, bool firstDecl, - bool lastDecl) { - int flag = 0; - int isa = 0; - SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); - if (DeclLoc.isInvalid()) - // If type location is missing, it is because of missing type (a warning). - // Use variable's location which is good for this case. - DeclLoc = ND->getLocation(); - const char *startBuf = SM->getCharacterData(DeclLoc); - SourceLocation X = ND->getLocEnd(); - X = SM->getExpansionLoc(X); - const char *endBuf = SM->getCharacterData(X); - std::string Name(ND->getNameAsString()); - std::string ByrefType; - RewriteByRefString(ByrefType, Name, ND, true); - ByrefType += " {\n"; - ByrefType += " void *__isa;\n"; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += " *__forwarding;\n"; - ByrefType += " int __flags;\n"; - ByrefType += " int __size;\n"; - // Add void *__Block_byref_id_object_copy; - // void *__Block_byref_id_object_dispose; if needed. - QualType Ty = ND->getType(); - bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty); - if (HasCopyAndDispose) { - ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; - ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; - } - - QualType T = Ty; - (void)convertBlockPointerToFunctionPointer(T); - T.getAsStringInternal(Name, Context->getPrintingPolicy()); - - ByrefType += " " + Name + ";\n"; - ByrefType += "};\n"; - // Insert this type in global scope. It is needed by helper function. - SourceLocation FunLocStart; - if (CurFunctionDef) - FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); - else { - assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); - FunLocStart = CurMethodDef->getLocStart(); - } - InsertText(FunLocStart, ByrefType); - - if (Ty.isObjCGCWeak()) { - flag |= BLOCK_FIELD_IS_WEAK; - isa = 1; - } - if (HasCopyAndDispose) { - flag = BLOCK_BYREF_CALLER; - QualType Ty = ND->getType(); - // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. - if (Ty->isBlockPointerType()) - flag |= BLOCK_FIELD_IS_BLOCK; - else - flag |= BLOCK_FIELD_IS_OBJECT; - std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); - if (!HF.empty()) - InsertText(FunLocStart, HF); - } - - // struct __Block_byref_ND ND = - // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), - // initializer-if-any}; - bool hasInit = (ND->getInit() != 0); - // FIXME. rewriter does not support __block c++ objects which - // require construction. - if (hasInit) - if (CXXConstructExpr *CExp = dyn_cast(ND->getInit())) { - CXXConstructorDecl *CXXDecl = CExp->getConstructor(); - if (CXXDecl && CXXDecl->isDefaultConstructor()) - hasInit = false; - } - - unsigned flags = 0; - if (HasCopyAndDispose) - flags |= BLOCK_HAS_COPY_DISPOSE; - Name = ND->getNameAsString(); - ByrefType.clear(); - RewriteByRefString(ByrefType, Name, ND); - std::string ForwardingCastType("("); - ForwardingCastType += ByrefType + " *)"; - ByrefType += " " + Name + " = {(void*)"; - ByrefType += utostr(isa); - ByrefType += "," + ForwardingCastType + "&" + Name + ", "; - ByrefType += utostr(flags); - ByrefType += ", "; - ByrefType += "sizeof("; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += ")"; - if (HasCopyAndDispose) { - ByrefType += ", __Block_byref_id_object_copy_"; - ByrefType += utostr(flag); - ByrefType += ", __Block_byref_id_object_dispose_"; - ByrefType += utostr(flag); - } - - if (!firstDecl) { - // In multiple __block declarations, and for all but 1st declaration, - // find location of the separating comma. This would be start location - // where new text is to be inserted. - DeclLoc = ND->getLocation(); - const char *startDeclBuf = SM->getCharacterData(DeclLoc); - const char *commaBuf = startDeclBuf; - while (*commaBuf != ',') - commaBuf--; - assert((*commaBuf == ',') && "RewriteByRefVar: can't find ','"); - DeclLoc = DeclLoc.getLocWithOffset(commaBuf - startDeclBuf); - startBuf = commaBuf; - } - - if (!hasInit) { - ByrefType += "};\n"; - unsigned nameSize = Name.size(); - // for block or function pointer declaration. Name is aleady - // part of the declaration. - if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) - nameSize = 1; - ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); - } - else { - ByrefType += ", "; - SourceLocation startLoc; - Expr *E = ND->getInit(); - if (const CStyleCastExpr *ECE = dyn_cast(E)) - startLoc = ECE->getLParenLoc(); - else - startLoc = E->getLocStart(); - startLoc = SM->getExpansionLoc(startLoc); - endBuf = SM->getCharacterData(startLoc); - ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); - - const char separator = lastDecl ? ';' : ','; - const char *startInitializerBuf = SM->getCharacterData(startLoc); - const char *separatorBuf = strchr(startInitializerBuf, separator); - assert((*separatorBuf == separator) && - "RewriteByRefVar: can't find ';' or ','"); - SourceLocation separatorLoc = - startLoc.getLocWithOffset(separatorBuf-startInitializerBuf); - - InsertText(separatorLoc, lastDecl ? "}" : "};\n"); - } - return; -} - -void RewriteModernObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { - // Add initializers for any closure decl refs. - GetBlockDeclRefExprs(Exp->getBody()); - if (BlockDeclRefs.size()) { - // Unique all "by copy" declarations. - for (unsigned i = 0; i < BlockDeclRefs.size(); i++) - if (!BlockDeclRefs[i]->getDecl()->hasAttr()) { - if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { - BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); - BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); - } - } - // Unique all "by ref" declarations. - for (unsigned i = 0; i < BlockDeclRefs.size(); i++) - if (BlockDeclRefs[i]->getDecl()->hasAttr()) { - if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { - BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); - BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); - } - } - // Find any imported blocks...they will need special attention. - for (unsigned i = 0; i < BlockDeclRefs.size(); i++) - if (BlockDeclRefs[i]->getDecl()->hasAttr() || - BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || - BlockDeclRefs[i]->getType()->isBlockPointerType()) - ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); - } -} - -FunctionDecl *RewriteModernObjC::SynthBlockInitFunctionDecl(StringRef name) { - IdentifierInfo *ID = &Context->Idents.get(name); - QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); - return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), ID, FType, 0, SC_Extern, - SC_None, false, false); -} - -Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp, - const SmallVector &InnerBlockDeclRefs) { - - const BlockDecl *block = Exp->getBlockDecl(); - - Blocks.push_back(Exp); - - CollectBlockDeclRefInfo(Exp); - - // Add inner imported variables now used in current block. - int countOfInnerDecls = 0; - if (!InnerBlockDeclRefs.empty()) { - for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { - DeclRefExpr *Exp = InnerBlockDeclRefs[i]; - ValueDecl *VD = Exp->getDecl(); - if (!VD->hasAttr() && !BlockByCopyDeclsPtrSet.count(VD)) { - // We need to save the copied-in variables in nested - // blocks because it is needed at the end for some of the API generations. - // See SynthesizeBlockLiterals routine. - InnerDeclRefs.push_back(Exp); countOfInnerDecls++; - BlockDeclRefs.push_back(Exp); - BlockByCopyDeclsPtrSet.insert(VD); - BlockByCopyDecls.push_back(VD); - } - if (VD->hasAttr() && !BlockByRefDeclsPtrSet.count(VD)) { - InnerDeclRefs.push_back(Exp); countOfInnerDecls++; - BlockDeclRefs.push_back(Exp); - BlockByRefDeclsPtrSet.insert(VD); - BlockByRefDecls.push_back(VD); - } - } - // Find any imported blocks...they will need special attention. - for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) - if (InnerBlockDeclRefs[i]->getDecl()->hasAttr() || - InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || - InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) - ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); - } - InnerDeclRefsCount.push_back(countOfInnerDecls); - - std::string FuncName; - - if (CurFunctionDef) - FuncName = CurFunctionDef->getNameAsString(); - else if (CurMethodDef) - BuildUniqueMethodName(FuncName, CurMethodDef); - else if (GlobalVarDecl) - FuncName = std::string(GlobalVarDecl->getNameAsString()); - - bool GlobalBlockExpr = - block->getDeclContext()->getRedeclContext()->isFileContext(); - - if (GlobalBlockExpr && !GlobalVarDecl) { - Diags.Report(block->getLocation(), GlobalBlockRewriteFailedDiag); - GlobalBlockExpr = false; - } - - std::string BlockNumber = utostr(Blocks.size()-1); - - std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; - - // Get a pointer to the function type so we can cast appropriately. - QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); - QualType FType = Context->getPointerType(BFT); - - FunctionDecl *FD; - Expr *NewRep; - - // Simulate a contructor call... - std::string Tag; - - if (GlobalBlockExpr) - Tag = "__global_"; - else - Tag = "__"; - Tag += FuncName + "_block_impl_" + BlockNumber; - - FD = SynthBlockInitFunctionDecl(Tag); - DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, FType, VK_RValue, - SourceLocation()); - - SmallVector InitExprs; - - // Initialize the block function. - FD = SynthBlockInitFunctionDecl(Func); - DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), - VK_LValue, SourceLocation()); - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, - CK_BitCast, Arg); - InitExprs.push_back(castExpr); - - // Initialize the block descriptor. - std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; - - VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get(DescData.c_str()), - Context->VoidPtrTy, 0, - SC_Static, SC_None); - UnaryOperator *DescRefExpr = - new (Context) UnaryOperator(new (Context) DeclRefExpr(NewVD, false, - Context->VoidPtrTy, - VK_LValue, - SourceLocation()), - UO_AddrOf, - Context->getPointerType(Context->VoidPtrTy), - VK_RValue, OK_Ordinary, - SourceLocation()); - InitExprs.push_back(DescRefExpr); - - // Add initializers for any closure decl refs. - if (BlockDeclRefs.size()) { - Expr *Exp; - // Output all "by copy" declarations. - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - if (isObjCType((*I)->getType())) { - // FIXME: Conform to ABI ([[obj retain] autorelease]). - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), - VK_LValue, SourceLocation()); - if (HasLocalVariableExternalStorage(*I)) { - QualType QT = (*I)->getType(); - QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation()); - } - } else if (isTopLevelBlockPointerType((*I)->getType())) { - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), - VK_LValue, SourceLocation()); - Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, - CK_BitCast, Arg); - } else { - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), - VK_LValue, SourceLocation()); - if (HasLocalVariableExternalStorage(*I)) { - QualType QT = (*I)->getType(); - QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation()); - } - - } - InitExprs.push_back(Exp); - } - // Output all "by ref" declarations. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - ValueDecl *ND = (*I); - std::string Name(ND->getNameAsString()); - std::string RecName; - RewriteByRefString(RecName, Name, ND, true); - IdentifierInfo *II = &Context->Idents.get(RecName.c_str() - + sizeof("struct")); - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - II); - assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); - QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); - - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, - SourceLocation()); - bool isNestedCapturedVar = false; - if (block) - for (BlockDecl::capture_const_iterator ci = block->capture_begin(), - ce = block->capture_end(); ci != ce; ++ci) { - const VarDecl *variable = ci->getVariable(); - if (variable == ND && ci->isNested()) { - assert (ci->isByRef() && - "SynthBlockInitExpr - captured block variable is not byref"); - isNestedCapturedVar = true; - break; - } - } - // captured nested byref variable has its address passed. Do not take - // its address again. - if (!isNestedCapturedVar) - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, - Context->getPointerType(Exp->getType()), - VK_RValue, OK_Ordinary, SourceLocation()); - Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); - InitExprs.push_back(Exp); - } - } - if (ImportedBlockDecls.size()) { - // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR - int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), - Context->IntTy, SourceLocation()); - InitExprs.push_back(FlagExp); - } - NewRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], InitExprs.size(), - FType, VK_LValue, SourceLocation()); - - if (GlobalBlockExpr) { - assert (GlobalConstructionExp == 0 && - "SynthBlockInitExpr - GlobalConstructionExp must be null"); - GlobalConstructionExp = NewRep; - NewRep = DRE; - } - - NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, - Context->getPointerType(NewRep->getType()), - VK_RValue, OK_Ordinary, SourceLocation()); - NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, - NewRep); - BlockDeclRefs.clear(); - BlockByRefDecls.clear(); - BlockByRefDeclsPtrSet.clear(); - BlockByCopyDecls.clear(); - BlockByCopyDeclsPtrSet.clear(); - ImportedBlockDecls.clear(); - return NewRep; -} - -bool RewriteModernObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { - if (const ObjCForCollectionStmt * CS = - dyn_cast(Stmts.back())) - return CS->getElement() == DS; - return false; -} - -//===----------------------------------------------------------------------===// -// Function Body / Expression rewriting -//===----------------------------------------------------------------------===// - -Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { - if (isa(S) || isa(S) || - isa(S) || isa(S)) - Stmts.push_back(S); - else if (isa(S)) { - Stmts.push_back(S); - ObjCBcLabelNo.push_back(++BcLabelCount); - } - - // Pseudo-object operations and ivar references need special - // treatment because we're going to recursively rewrite them. - if (PseudoObjectExpr *PseudoOp = dyn_cast(S)) { - if (isa(PseudoOp->getSyntacticForm())) { - return RewritePropertyOrImplicitSetter(PseudoOp); - } else { - return RewritePropertyOrImplicitGetter(PseudoOp); - } - } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast(S)) { - return RewriteObjCIvarRefExpr(IvarRefExpr); - } - - SourceRange OrigStmtRange = S->getSourceRange(); - - // Perform a bottom up rewrite of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - Stmt *childStmt = (*CI); - Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); - if (newStmt) { - *CI = newStmt; - } - } - - if (BlockExpr *BE = dyn_cast(S)) { - SmallVector InnerBlockDeclRefs; - llvm::SmallPtrSet InnerContexts; - InnerContexts.insert(BE->getBlockDecl()); - ImportedLocalExternalDecls.clear(); - GetInnerBlockDeclRefExprs(BE->getBody(), - InnerBlockDeclRefs, InnerContexts); - // Rewrite the block body in place. - Stmt *SaveCurrentBody = CurrentBody; - CurrentBody = BE->getBody(); - PropParentMap = 0; - // block literal on rhs of a property-dot-sytax assignment - // must be replaced by its synthesize ast so getRewrittenText - // works as expected. In this case, what actually ends up on RHS - // is the blockTranscribed which is the helper function for the - // block literal; as in: self.c = ^() {[ace ARR];}; - bool saveDisableReplaceStmt = DisableReplaceStmt; - DisableReplaceStmt = false; - RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); - DisableReplaceStmt = saveDisableReplaceStmt; - CurrentBody = SaveCurrentBody; - PropParentMap = 0; - ImportedLocalExternalDecls.clear(); - // Now we snarf the rewritten text and stash it away for later use. - std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); - RewrittenBlockExprs[BE] = Str; - - Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); - - //blockTranscribed->dump(); - ReplaceStmt(S, blockTranscribed); - return blockTranscribed; - } - // Handle specific things. - if (ObjCEncodeExpr *AtEncode = dyn_cast(S)) - return RewriteAtEncode(AtEncode); - - if (ObjCSelectorExpr *AtSelector = dyn_cast(S)) - return RewriteAtSelector(AtSelector); - - if (ObjCStringLiteral *AtString = dyn_cast(S)) - return RewriteObjCStringLiteral(AtString); - - if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast(S)) - return RewriteObjCBoolLiteralExpr(BoolLitExpr); - - if (ObjCBoxedExpr *BoxedExpr = dyn_cast(S)) - return RewriteObjCBoxedExpr(BoxedExpr); - - if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast(S)) - return RewriteObjCArrayLiteralExpr(ArrayLitExpr); - - if (ObjCDictionaryLiteral *DictionaryLitExpr = - dyn_cast(S)) - return RewriteObjCDictionaryLiteralExpr(DictionaryLitExpr); - - if (ObjCMessageExpr *MessExpr = dyn_cast(S)) { -#if 0 - // Before we rewrite it, put the original message expression in a comment. - SourceLocation startLoc = MessExpr->getLocStart(); - SourceLocation endLoc = MessExpr->getLocEnd(); - - const char *startBuf = SM->getCharacterData(startLoc); - const char *endBuf = SM->getCharacterData(endLoc); - - std::string messString; - messString += "// "; - messString.append(startBuf, endBuf-startBuf+1); - messString += "\n"; - - // FIXME: Missing definition of - // InsertText(clang::SourceLocation, char const*, unsigned int). - // InsertText(startLoc, messString.c_str(), messString.size()); - // Tried this, but it didn't work either... - // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); -#endif - return RewriteMessageExpr(MessExpr); - } - - if (ObjCAutoreleasePoolStmt *StmtAutoRelease = - dyn_cast(S)) { - return RewriteObjCAutoreleasePoolStmt(StmtAutoRelease); - } - - if (ObjCAtTryStmt *StmtTry = dyn_cast(S)) - return RewriteObjCTryStmt(StmtTry); - - if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast(S)) - return RewriteObjCSynchronizedStmt(StmtTry); - - if (ObjCAtThrowStmt *StmtThrow = dyn_cast(S)) - return RewriteObjCThrowStmt(StmtThrow); - - if (ObjCProtocolExpr *ProtocolExp = dyn_cast(S)) - return RewriteObjCProtocolExpr(ProtocolExp); - - if (ObjCForCollectionStmt *StmtForCollection = - dyn_cast(S)) - return RewriteObjCForCollectionStmt(StmtForCollection, - OrigStmtRange.getEnd()); - if (BreakStmt *StmtBreakStmt = - dyn_cast(S)) - return RewriteBreakStmt(StmtBreakStmt); - if (ContinueStmt *StmtContinueStmt = - dyn_cast(S)) - return RewriteContinueStmt(StmtContinueStmt); - - // Need to check for protocol refs (id

, Foo

*) in variable decls - // and cast exprs. - if (DeclStmt *DS = dyn_cast(S)) { - // FIXME: What we're doing here is modifying the type-specifier that - // precedes the first Decl. In the future the DeclGroup should have - // a separate type-specifier that we can rewrite. - // NOTE: We need to avoid rewriting the DeclStmt if it is within - // the context of an ObjCForCollectionStmt. For example: - // NSArray *someArray; - // for (id index in someArray) ; - // This is because RewriteObjCForCollectionStmt() does textual rewriting - // and it depends on the original text locations/positions. - if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) - RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); - - // Blocks rewrite rules. - for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); - DI != DE; ++DI) { - Decl *SD = *DI; - if (ValueDecl *ND = dyn_cast(SD)) { - if (isTopLevelBlockPointerType(ND->getType())) - RewriteBlockPointerDecl(ND); - else if (ND->getType()->isFunctionPointerType()) - CheckFunctionPointerDecl(ND->getType(), ND); - if (VarDecl *VD = dyn_cast(SD)) { - if (VD->hasAttr()) { - static unsigned uniqueByrefDeclCount = 0; - assert(!BlockByRefDeclNo.count(ND) && - "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); - BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; - RewriteByRefVar(VD, (DI == DS->decl_begin()), ((DI+1) == DE)); - } - else - RewriteTypeOfDecl(VD); - } - } - if (TypedefNameDecl *TD = dyn_cast(SD)) { - if (isTopLevelBlockPointerType(TD->getUnderlyingType())) - RewriteBlockPointerDecl(TD); - else if (TD->getUnderlyingType()->isFunctionPointerType()) - CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); - } - } - } - - if (CStyleCastExpr *CE = dyn_cast(S)) - RewriteObjCQualifiedInterfaceTypes(CE); - - if (isa(S) || isa(S) || - isa(S) || isa(S)) { - assert(!Stmts.empty() && "Statement stack is empty"); - assert ((isa(Stmts.back()) || isa(Stmts.back()) || - isa(Stmts.back()) || isa(Stmts.back())) - && "Statement stack mismatch"); - Stmts.pop_back(); - } - // Handle blocks rewriting. - if (DeclRefExpr *DRE = dyn_cast(S)) { - ValueDecl *VD = DRE->getDecl(); - if (VD->hasAttr()) - return RewriteBlockDeclRefExpr(DRE); - if (HasLocalVariableExternalStorage(VD)) - return RewriteLocalVariableExternalStorage(DRE); - } - - if (CallExpr *CE = dyn_cast(S)) { - if (CE->getCallee()->getType()->isBlockPointerType()) { - Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); - ReplaceStmt(S, BlockCall); - return BlockCall; - } - } - if (CStyleCastExpr *CE = dyn_cast(S)) { - RewriteCastExpr(CE); - } - if (ImplicitCastExpr *ICE = dyn_cast(S)) { - RewriteImplicitCastObjCExpr(ICE); - } -#if 0 - - if (ImplicitCastExpr *ICE = dyn_cast(S)) { - CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), - ICE->getSubExpr(), - SourceLocation()); - // Get the new text. - std::string SStr; - llvm::raw_string_ostream Buf(SStr); - Replacement->printPretty(Buf); - const std::string &Str = Buf.str(); - - printf("CAST = %s\n", &Str[0]); - InsertText(ICE->getSubExpr()->getLocStart(), &Str[0], Str.size()); - delete S; - return Replacement; - } -#endif - // Return this stmt unmodified. - return S; -} - -void RewriteModernObjC::RewriteRecordBody(RecordDecl *RD) { - for (RecordDecl::field_iterator i = RD->field_begin(), - e = RD->field_end(); i != e; ++i) { - FieldDecl *FD = *i; - if (isTopLevelBlockPointerType(FD->getType())) - RewriteBlockPointerDecl(FD); - if (FD->getType()->isObjCQualifiedIdType() || - FD->getType()->isObjCQualifiedInterfaceType()) - RewriteObjCQualifiedInterfaceTypes(FD); - } -} - -/// HandleDeclInMainFile - This is called for each top-level decl defined in the -/// main file of the input. -void RewriteModernObjC::HandleDeclInMainFile(Decl *D) { - switch (D->getKind()) { - case Decl::Function: { - FunctionDecl *FD = cast(D); - if (FD->isOverloadedOperator()) - return; - - // Since function prototypes don't have ParmDecl's, we check the function - // prototype. This enables us to rewrite function declarations and - // definitions using the same code. - RewriteBlocksInFunctionProtoType(FD->getType(), FD); - - if (!FD->isThisDeclarationADefinition()) - break; - - // FIXME: If this should support Obj-C++, support CXXTryStmt - if (CompoundStmt *Body = dyn_cast_or_null(FD->getBody())) { - CurFunctionDef = FD; - CurrentBody = Body; - Body = - cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); - FD->setBody(Body); - CurrentBody = 0; - if (PropParentMap) { - delete PropParentMap; - PropParentMap = 0; - } - // This synthesizes and inserts the block "impl" struct, invoke function, - // and any copy/dispose helper functions. - InsertBlockLiteralsWithinFunction(FD); - CurFunctionDef = 0; - } - break; - } - case Decl::ObjCMethod: { - ObjCMethodDecl *MD = cast(D); - if (CompoundStmt *Body = MD->getCompoundBody()) { - CurMethodDef = MD; - CurrentBody = Body; - Body = - cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); - MD->setBody(Body); - CurrentBody = 0; - if (PropParentMap) { - delete PropParentMap; - PropParentMap = 0; - } - InsertBlockLiteralsWithinMethod(MD); - CurMethodDef = 0; - } - break; - } - case Decl::ObjCImplementation: { - ObjCImplementationDecl *CI = cast(D); - ClassImplementation.push_back(CI); - break; - } - case Decl::ObjCCategoryImpl: { - ObjCCategoryImplDecl *CI = cast(D); - CategoryImplementation.push_back(CI); - break; - } - case Decl::Var: { - VarDecl *VD = cast(D); - RewriteObjCQualifiedInterfaceTypes(VD); - if (isTopLevelBlockPointerType(VD->getType())) - RewriteBlockPointerDecl(VD); - else if (VD->getType()->isFunctionPointerType()) { - CheckFunctionPointerDecl(VD->getType(), VD); - if (VD->getInit()) { - if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { - RewriteCastExpr(CE); - } - } - } else if (VD->getType()->isRecordType()) { - RecordDecl *RD = VD->getType()->getAs()->getDecl(); - if (RD->isCompleteDefinition()) - RewriteRecordBody(RD); - } - if (VD->getInit()) { - GlobalVarDecl = VD; - CurrentBody = VD->getInit(); - RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); - CurrentBody = 0; - if (PropParentMap) { - delete PropParentMap; - PropParentMap = 0; - } - SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); - GlobalVarDecl = 0; - - // This is needed for blocks. - if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { - RewriteCastExpr(CE); - } - } - break; - } - case Decl::TypeAlias: - case Decl::Typedef: { - if (TypedefNameDecl *TD = dyn_cast(D)) { - if (isTopLevelBlockPointerType(TD->getUnderlyingType())) - RewriteBlockPointerDecl(TD); - else if (TD->getUnderlyingType()->isFunctionPointerType()) - CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); - } - break; - } - case Decl::CXXRecord: - case Decl::Record: { - RecordDecl *RD = cast(D); - if (RD->isCompleteDefinition()) - RewriteRecordBody(RD); - break; - } - default: - break; - } - // Nothing yet. -} - -/// Write_ProtocolExprReferencedMetadata - This routine writer out the -/// protocol reference symbols in the for of: -/// struct _protocol_t *PROTOCOL_REF = &PROTOCOL_METADATA. -static void Write_ProtocolExprReferencedMetadata(ASTContext *Context, - ObjCProtocolDecl *PDecl, - std::string &Result) { - // Also output .objc_protorefs$B section and its meta-data. - if (Context->getLangOpts().MicrosoftExt) - Result += "static "; - Result += "struct _protocol_t *"; - Result += "_OBJC_PROTOCOL_REFERENCE_$_"; - Result += PDecl->getNameAsString(); - Result += " = &"; - Result += "_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); - Result += ";\n"; -} - -void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) { - if (Diags.hasErrorOccurred()) - return; - - RewriteInclude(); - - // Here's a great place to add any extra declarations that may be needed. - // Write out meta data for each @protocol(). - for (llvm::SmallPtrSet::iterator I = ProtocolExprDecls.begin(), - E = ProtocolExprDecls.end(); I != E; ++I) { - RewriteObjCProtocolMetaData(*I, Preamble); - Write_ProtocolExprReferencedMetadata(Context, (*I), Preamble); - } - - InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); - - if (ClassImplementation.size() || CategoryImplementation.size()) - RewriteImplementations(); - - for (unsigned i = 0, e = ObjCInterfacesSeen.size(); i < e; i++) { - ObjCInterfaceDecl *CDecl = ObjCInterfacesSeen[i]; - // Write struct declaration for the class matching its ivar declarations. - // Note that for modern abi, this is postponed until the end of TU - // because class extensions and the implementation might declare their own - // private ivars. - RewriteInterfaceDecl(CDecl); - } - - // Get the buffer corresponding to MainFileID. If we haven't changed it, then - // we are done. - if (const RewriteBuffer *RewriteBuf = - Rewrite.getRewriteBufferFor(MainFileID)) { - //printf("Changed:\n"); - *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); - } else { - llvm::errs() << "No changes\n"; - } - - if (ClassImplementation.size() || CategoryImplementation.size() || - ProtocolExprDecls.size()) { - // Rewrite Objective-c meta data* - std::string ResultStr; - RewriteMetaDataIntoBuffer(ResultStr); - // Emit metadata. - *OutFile << ResultStr; - } - // Emit ImageInfo; - { - std::string ResultStr; - WriteImageInfo(ResultStr); - *OutFile << ResultStr; - } - OutFile->flush(); -} - -void RewriteModernObjC::Initialize(ASTContext &context) { - InitializeCommon(context); - - Preamble += "#ifndef __OBJC2__\n"; - Preamble += "#define __OBJC2__\n"; - Preamble += "#endif\n"; - - // declaring objc_selector outside the parameter list removes a silly - // scope related warning... - if (IsHeader) - Preamble = "#pragma once\n"; - Preamble += "struct objc_selector; struct objc_class;\n"; - Preamble += "struct __rw_objc_super { \n\tstruct objc_object *object; "; - Preamble += "\n\tstruct objc_object *superClass; "; - // Add a constructor for creating temporary objects. - Preamble += "\n\t__rw_objc_super(struct objc_object *o, struct objc_object *s) "; - Preamble += ": object(o), superClass(s) {} "; - Preamble += "\n};\n"; - - if (LangOpts.MicrosoftExt) { - // Define all sections using syntax that makes sense. - // These are currently generated. - Preamble += "\n#pragma section(\".objc_classlist$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_catlist$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_imageinfo$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_nlclslist$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_nlcatlist$B\", long, read, write)\n"; - // These are generated but not necessary for functionality. - Preamble += "#pragma section(\".cat_cls_meth$B\", long, read, write)\n"; - Preamble += "#pragma section(\".inst_meth$B\", long, read, write)\n"; - Preamble += "#pragma section(\".cls_meth$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_ivar$B\", long, read, write)\n"; - - // These need be generated for performance. Currently they are not, - // using API calls instead. - Preamble += "#pragma section(\".objc_selrefs$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_classrefs$B\", long, read, write)\n"; - Preamble += "#pragma section(\".objc_superrefs$B\", long, read, write)\n"; - - } - Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; - Preamble += "typedef struct objc_object Protocol;\n"; - Preamble += "#define _REWRITER_typedef_Protocol\n"; - Preamble += "#endif\n"; - if (LangOpts.MicrosoftExt) { - Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; - Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; - } - else - Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; - - Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend(void);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);\n"; - - Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass"; - Preamble += "(const char *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; - Preamble += "(struct objc_class *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass"; - Preamble += "(const char *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);\n"; - // @synchronized hooks. - Preamble += "__OBJC_RW_DLLIMPORT void objc_sync_enter( struct objc_object *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_sync_exit( struct objc_object *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; - Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; - Preamble += "struct __objcFastEnumerationState {\n\t"; - Preamble += "unsigned long state;\n\t"; - Preamble += "void **itemsPtr;\n\t"; - Preamble += "unsigned long *mutationsPtr;\n\t"; - Preamble += "unsigned long extra[5];\n};\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; - Preamble += "#define __FASTENUMERATIONSTATE\n"; - Preamble += "#endif\n"; - Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; - Preamble += "struct __NSConstantStringImpl {\n"; - Preamble += " int *isa;\n"; - Preamble += " int flags;\n"; - Preamble += " char *str;\n"; - Preamble += " long length;\n"; - Preamble += "};\n"; - Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; - Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; - Preamble += "#else\n"; - Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; - Preamble += "#endif\n"; - Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; - Preamble += "#endif\n"; - // Blocks preamble. - Preamble += "#ifndef BLOCK_IMPL\n"; - Preamble += "#define BLOCK_IMPL\n"; - Preamble += "struct __block_impl {\n"; - Preamble += " void *isa;\n"; - Preamble += " int Flags;\n"; - Preamble += " int Reserved;\n"; - Preamble += " void *FuncPtr;\n"; - Preamble += "};\n"; - Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; - Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; - Preamble += "extern \"C\" __declspec(dllexport) " - "void _Block_object_assign(void *, const void *, const int);\n"; - Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; - Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; - Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; - Preamble += "#else\n"; - Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; - Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; - Preamble += "#endif\n"; - Preamble += "#endif\n"; - if (LangOpts.MicrosoftExt) { - Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; - Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; - Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. - Preamble += "#define __attribute__(X)\n"; - Preamble += "#endif\n"; - Preamble += "#ifndef __weak\n"; - Preamble += "#define __weak\n"; - Preamble += "#endif\n"; - Preamble += "#ifndef __block\n"; - Preamble += "#define __block\n"; - Preamble += "#endif\n"; - } - else { - Preamble += "#define __block\n"; - Preamble += "#define __weak\n"; - } - - // Declarations required for modern objective-c array and dictionary literals. - Preamble += "\n#include \n"; - Preamble += "struct __NSContainer_literal {\n"; - Preamble += " void * *arr;\n"; - Preamble += " __NSContainer_literal (unsigned int count, ...) {\n"; - Preamble += "\tva_list marker;\n"; - Preamble += "\tva_start(marker, count);\n"; - Preamble += "\tarr = new void *[count];\n"; - Preamble += "\tfor (unsigned i = 0; i < count; i++)\n"; - Preamble += "\t arr[i] = va_arg(marker, void *);\n"; - Preamble += "\tva_end( marker );\n"; - Preamble += " };\n"; - Preamble += " ~__NSContainer_literal() {\n"; - Preamble += "\tdelete[] arr;\n"; - Preamble += " }\n"; - Preamble += "};\n"; - - // Declaration required for implementation of @autoreleasepool statement. - Preamble += "extern \"C\" __declspec(dllimport) void * objc_autoreleasePoolPush(void);\n"; - Preamble += "extern \"C\" __declspec(dllimport) void objc_autoreleasePoolPop(void *);\n\n"; - Preamble += "struct __AtAutoreleasePool {\n"; - Preamble += " __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}\n"; - Preamble += " ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}\n"; - Preamble += " void * atautoreleasepoolobj;\n"; - Preamble += "};\n"; - - // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long - // as this avoids warning in any 64bit/32bit compilation model. - Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; -} - -/// RewriteIvarOffsetComputation - This rutine synthesizes computation of -/// ivar offset. -void RewriteModernObjC::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, - std::string &Result) { - if (ivar->isBitField()) { - // FIXME: The hack below doesn't work for bitfields. For now, we simply - // place all bitfields at offset 0. - Result += "0"; - } else { - Result += "__OFFSETOFIVAR__(struct "; - Result += ivar->getContainingInterface()->getNameAsString(); - if (LangOpts.MicrosoftExt) - Result += "_IMPL"; - Result += ", "; - Result += ivar->getNameAsString(); - Result += ")"; - } -} - -/// WriteModernMetadataDeclarations - Writes out metadata declarations for modern ABI. -/// struct _prop_t { -/// const char *name; -/// char *attributes; -/// } - -/// struct _prop_list_t { -/// uint32_t entsize; // sizeof(struct _prop_t) -/// uint32_t count_of_properties; -/// struct _prop_t prop_list[count_of_properties]; -/// } - -/// struct _protocol_t; - -/// struct _protocol_list_t { -/// long protocol_count; // Note, this is 32/64 bit -/// struct _protocol_t * protocol_list[protocol_count]; -/// } - -/// struct _objc_method { -/// SEL _cmd; -/// const char *method_type; -/// char *_imp; -/// } - -/// struct _method_list_t { -/// uint32_t entsize; // sizeof(struct _objc_method) -/// uint32_t method_count; -/// struct _objc_method method_list[method_count]; -/// } - -/// struct _protocol_t { -/// id isa; // NULL -/// const char *protocol_name; -/// const struct _protocol_list_t * protocol_list; // super protocols -/// const struct method_list_t *instance_methods; -/// const struct method_list_t *class_methods; -/// const struct method_list_t *optionalInstanceMethods; -/// const struct method_list_t *optionalClassMethods; -/// const struct _prop_list_t * properties; -/// const uint32_t size; // sizeof(struct _protocol_t) -/// const uint32_t flags; // = 0 -/// const char ** extendedMethodTypes; -/// } - -/// struct _ivar_t { -/// unsigned long int *offset; // pointer to ivar offset location -/// const char *name; -/// const char *type; -/// uint32_t alignment; -/// uint32_t size; -/// } - -/// struct _ivar_list_t { -/// uint32 entsize; // sizeof(struct _ivar_t) -/// uint32 count; -/// struct _ivar_t list[count]; -/// } - -/// struct _class_ro_t { -/// uint32_t flags; -/// uint32_t instanceStart; -/// uint32_t instanceSize; -/// uint32_t reserved; // only when building for 64bit targets -/// const uint8_t *ivarLayout; -/// const char *name; -/// const struct _method_list_t *baseMethods; -/// const struct _protocol_list_t *baseProtocols; -/// const struct _ivar_list_t *ivars; -/// const uint8_t *weakIvarLayout; -/// const struct _prop_list_t *properties; -/// } - -/// struct _class_t { -/// struct _class_t *isa; -/// struct _class_t *superclass; -/// void *cache; -/// IMP *vtable; -/// struct _class_ro_t *ro; -/// } - -/// struct _category_t { -/// const char *name; -/// struct _class_t *cls; -/// const struct _method_list_t *instance_methods; -/// const struct _method_list_t *class_methods; -/// const struct _protocol_list_t *protocols; -/// const struct _prop_list_t *properties; -/// } - -/// MessageRefTy - LLVM for: -/// struct _message_ref_t { -/// IMP messenger; -/// SEL name; -/// }; - -/// SuperMessageRefTy - LLVM for: -/// struct _super_message_ref_t { -/// SUPER_IMP messenger; -/// SEL name; -/// }; - -static void WriteModernMetadataDeclarations(ASTContext *Context, std::string &Result) { - static bool meta_data_declared = false; - if (meta_data_declared) - return; - - Result += "\nstruct _prop_t {\n"; - Result += "\tconst char *name;\n"; - Result += "\tconst char *attributes;\n"; - Result += "};\n"; - - Result += "\nstruct _protocol_t;\n"; - - Result += "\nstruct _objc_method {\n"; - Result += "\tstruct objc_selector * _cmd;\n"; - Result += "\tconst char *method_type;\n"; - Result += "\tvoid *_imp;\n"; - Result += "};\n"; - - Result += "\nstruct _protocol_t {\n"; - Result += "\tvoid * isa; // NULL\n"; - Result += "\tconst char *protocol_name;\n"; - Result += "\tconst struct _protocol_list_t * protocol_list; // super protocols\n"; - Result += "\tconst struct method_list_t *instance_methods;\n"; - Result += "\tconst struct method_list_t *class_methods;\n"; - Result += "\tconst struct method_list_t *optionalInstanceMethods;\n"; - Result += "\tconst struct method_list_t *optionalClassMethods;\n"; - Result += "\tconst struct _prop_list_t * properties;\n"; - Result += "\tconst unsigned int size; // sizeof(struct _protocol_t)\n"; - Result += "\tconst unsigned int flags; // = 0\n"; - Result += "\tconst char ** extendedMethodTypes;\n"; - Result += "};\n"; - - Result += "\nstruct _ivar_t {\n"; - Result += "\tunsigned long int *offset; // pointer to ivar offset location\n"; - Result += "\tconst char *name;\n"; - Result += "\tconst char *type;\n"; - Result += "\tunsigned int alignment;\n"; - Result += "\tunsigned int size;\n"; - Result += "};\n"; - - Result += "\nstruct _class_ro_t {\n"; - Result += "\tunsigned int flags;\n"; - Result += "\tunsigned int instanceStart;\n"; - Result += "\tunsigned int instanceSize;\n"; - const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); - if (Triple.getArch() == llvm::Triple::x86_64) - Result += "\tunsigned int reserved;\n"; - Result += "\tconst unsigned char *ivarLayout;\n"; - Result += "\tconst char *name;\n"; - Result += "\tconst struct _method_list_t *baseMethods;\n"; - Result += "\tconst struct _objc_protocol_list *baseProtocols;\n"; - Result += "\tconst struct _ivar_list_t *ivars;\n"; - Result += "\tconst unsigned char *weakIvarLayout;\n"; - Result += "\tconst struct _prop_list_t *properties;\n"; - Result += "};\n"; - - Result += "\nstruct _class_t {\n"; - Result += "\tstruct _class_t *isa;\n"; - Result += "\tstruct _class_t *superclass;\n"; - Result += "\tvoid *cache;\n"; - Result += "\tvoid *vtable;\n"; - Result += "\tstruct _class_ro_t *ro;\n"; - Result += "};\n"; - - Result += "\nstruct _category_t {\n"; - Result += "\tconst char *name;\n"; - Result += "\tstruct _class_t *cls;\n"; - Result += "\tconst struct _method_list_t *instance_methods;\n"; - Result += "\tconst struct _method_list_t *class_methods;\n"; - Result += "\tconst struct _protocol_list_t *protocols;\n"; - Result += "\tconst struct _prop_list_t *properties;\n"; - Result += "};\n"; - - Result += "extern \"C\" __declspec(dllimport) struct objc_cache _objc_empty_cache;\n"; - Result += "#pragma warning(disable:4273)\n"; - meta_data_declared = true; -} - -static void Write_protocol_list_t_TypeDecl(std::string &Result, - long super_protocol_count) { - Result += "struct /*_protocol_list_t*/"; Result += " {\n"; - Result += "\tlong protocol_count; // Note, this is 32/64 bit\n"; - Result += "\tstruct _protocol_t *super_protocols["; - Result += utostr(super_protocol_count); Result += "];\n"; - Result += "}"; -} - -static void Write_method_list_t_TypeDecl(std::string &Result, - unsigned int method_count) { - Result += "struct /*_method_list_t*/"; Result += " {\n"; - Result += "\tunsigned int entsize; // sizeof(struct _objc_method)\n"; - Result += "\tunsigned int method_count;\n"; - Result += "\tstruct _objc_method method_list["; - Result += utostr(method_count); Result += "];\n"; - Result += "}"; -} - -static void Write__prop_list_t_TypeDecl(std::string &Result, - unsigned int property_count) { - Result += "struct /*_prop_list_t*/"; Result += " {\n"; - Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; - Result += "\tunsigned int count_of_properties;\n"; - Result += "\tstruct _prop_t prop_list["; - Result += utostr(property_count); Result += "];\n"; - Result += "}"; -} - -static void Write__ivar_list_t_TypeDecl(std::string &Result, - unsigned int ivar_count) { - Result += "struct /*_ivar_list_t*/"; Result += " {\n"; - Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; - Result += "\tunsigned int count;\n"; - Result += "\tstruct _ivar_t ivar_list["; - Result += utostr(ivar_count); Result += "];\n"; - Result += "}"; -} - -static void Write_protocol_list_initializer(ASTContext *Context, std::string &Result, - ArrayRef SuperProtocols, - StringRef VarName, - StringRef ProtocolName) { - if (SuperProtocols.size() > 0) { - Result += "\nstatic "; - Write_protocol_list_t_TypeDecl(Result, SuperProtocols.size()); - Result += " "; Result += VarName; - Result += ProtocolName; - Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; - Result += "\t"; Result += utostr(SuperProtocols.size()); Result += ",\n"; - for (unsigned i = 0, e = SuperProtocols.size(); i < e; i++) { - ObjCProtocolDecl *SuperPD = SuperProtocols[i]; - Result += "\t&"; Result += "_OBJC_PROTOCOL_"; - Result += SuperPD->getNameAsString(); - if (i == e-1) - Result += "\n};\n"; - else - Result += ",\n"; - } - } -} - -static void Write_method_list_t_initializer(RewriteModernObjC &RewriteObj, - ASTContext *Context, std::string &Result, - ArrayRef Methods, - StringRef VarName, - StringRef TopLevelDeclName, - bool MethodImpl) { - if (Methods.size() > 0) { - Result += "\nstatic "; - Write_method_list_t_TypeDecl(Result, Methods.size()); - Result += " "; Result += VarName; - Result += TopLevelDeclName; - Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; - Result += "\t"; Result += "sizeof(_objc_method)"; Result += ",\n"; - Result += "\t"; Result += utostr(Methods.size()); Result += ",\n"; - for (unsigned i = 0, e = Methods.size(); i < e; i++) { - ObjCMethodDecl *MD = Methods[i]; - if (i == 0) - Result += "\t{{(struct objc_selector *)\""; - else - Result += "\t{(struct objc_selector *)\""; - Result += (MD)->getSelector().getAsString(); Result += "\""; - Result += ", "; - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl(MD, MethodTypeString); - Result += "\""; Result += MethodTypeString; Result += "\""; - Result += ", "; - if (!MethodImpl) - Result += "0"; - else { - Result += "(void *)"; - Result += RewriteObj.MethodInternalNames[MD]; - } - if (i == e-1) - Result += "}}\n"; - else - Result += "},\n"; - } - Result += "};\n"; - } -} - -static void Write_prop_list_t_initializer(RewriteModernObjC &RewriteObj, - ASTContext *Context, std::string &Result, - ArrayRef Properties, - const Decl *Container, - StringRef VarName, - StringRef ProtocolName) { - if (Properties.size() > 0) { - Result += "\nstatic "; - Write__prop_list_t_TypeDecl(Result, Properties.size()); - Result += " "; Result += VarName; - Result += ProtocolName; - Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; - Result += "\t"; Result += "sizeof(_prop_t)"; Result += ",\n"; - Result += "\t"; Result += utostr(Properties.size()); Result += ",\n"; - for (unsigned i = 0, e = Properties.size(); i < e; i++) { - ObjCPropertyDecl *PropDecl = Properties[i]; - if (i == 0) - Result += "\t{{\""; - else - Result += "\t{\""; - Result += PropDecl->getName(); Result += "\","; - std::string PropertyTypeString, QuotePropertyTypeString; - Context->getObjCEncodingForPropertyDecl(PropDecl, Container, PropertyTypeString); - RewriteObj.QuoteDoublequotes(PropertyTypeString, QuotePropertyTypeString); - Result += "\""; Result += QuotePropertyTypeString; Result += "\""; - if (i == e-1) - Result += "}}\n"; - else - Result += "},\n"; - } - Result += "};\n"; - } -} - -// Metadata flags -enum MetaDataDlags { - CLS = 0x0, - CLS_META = 0x1, - CLS_ROOT = 0x2, - OBJC2_CLS_HIDDEN = 0x10, - CLS_EXCEPTION = 0x20, - - /// (Obsolete) ARC-specific: this class has a .release_ivars method - CLS_HAS_IVAR_RELEASER = 0x40, - /// class was compiled with -fobjc-arr - CLS_COMPILED_BY_ARC = 0x80 // (1<<7) -}; - -static void Write__class_ro_t_initializer(ASTContext *Context, std::string &Result, - unsigned int flags, - const std::string &InstanceStart, - const std::string &InstanceSize, - ArrayRefbaseMethods, - ArrayRefbaseProtocols, - ArrayRefivars, - ArrayRefProperties, - StringRef VarName, - StringRef ClassName) { - Result += "\nstatic struct _class_ro_t "; - Result += VarName; Result += ClassName; - Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; - Result += "\t"; - Result += llvm::utostr(flags); Result += ", "; - Result += InstanceStart; Result += ", "; - Result += InstanceSize; Result += ", \n"; - Result += "\t"; - const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); - if (Triple.getArch() == llvm::Triple::x86_64) - // uint32_t const reserved; // only when building for 64bit targets - Result += "(unsigned int)0, \n\t"; - // const uint8_t * const ivarLayout; - Result += "0, \n\t"; - Result += "\""; Result += ClassName; Result += "\",\n\t"; - bool metaclass = ((flags & CLS_META) != 0); - if (baseMethods.size() > 0) { - Result += "(const struct _method_list_t *)&"; - if (metaclass) - Result += "_OBJC_$_CLASS_METHODS_"; - else - Result += "_OBJC_$_INSTANCE_METHODS_"; - Result += ClassName; - Result += ",\n\t"; - } - else - Result += "0, \n\t"; - - if (!metaclass && baseProtocols.size() > 0) { - Result += "(const struct _objc_protocol_list *)&"; - Result += "_OBJC_CLASS_PROTOCOLS_$_"; Result += ClassName; - Result += ",\n\t"; - } - else - Result += "0, \n\t"; - - if (!metaclass && ivars.size() > 0) { - Result += "(const struct _ivar_list_t *)&"; - Result += "_OBJC_$_INSTANCE_VARIABLES_"; Result += ClassName; - Result += ",\n\t"; - } - else - Result += "0, \n\t"; - - // weakIvarLayout - Result += "0, \n\t"; - if (!metaclass && Properties.size() > 0) { - Result += "(const struct _prop_list_t *)&"; - Result += "_OBJC_$_PROP_LIST_"; Result += ClassName; - Result += ",\n"; - } - else - Result += "0, \n"; - - Result += "};\n"; -} - -static void Write_class_t(ASTContext *Context, std::string &Result, - StringRef VarName, - const ObjCInterfaceDecl *CDecl, bool metaclass) { - bool rootClass = (!CDecl->getSuperClass()); - const ObjCInterfaceDecl *RootClass = CDecl; - - if (!rootClass) { - // Find the Root class - RootClass = CDecl->getSuperClass(); - while (RootClass->getSuperClass()) { - RootClass = RootClass->getSuperClass(); - } - } - - if (metaclass && rootClass) { - // Need to handle a case of use of forward declaration. - Result += "\n"; - Result += "extern \"C\" "; - if (CDecl->getImplementation()) - Result += "__declspec(dllexport) "; - else - Result += "__declspec(dllimport) "; - - Result += "struct _class_t OBJC_CLASS_$_"; - Result += CDecl->getNameAsString(); - Result += ";\n"; - } - // Also, for possibility of 'super' metadata class not having been defined yet. - if (!rootClass) { - ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); - Result += "\n"; - Result += "extern \"C\" "; - if (SuperClass->getImplementation()) - Result += "__declspec(dllexport) "; - else - Result += "__declspec(dllimport) "; - - Result += "struct _class_t "; - Result += VarName; - Result += SuperClass->getNameAsString(); - Result += ";\n"; - - if (metaclass && RootClass != SuperClass) { - Result += "extern \"C\" "; - if (RootClass->getImplementation()) - Result += "__declspec(dllexport) "; - else - Result += "__declspec(dllimport) "; - - Result += "struct _class_t "; - Result += VarName; - Result += RootClass->getNameAsString(); - Result += ";\n"; - } - } - - Result += "\nextern \"C\" __declspec(dllexport) struct _class_t "; - Result += VarName; Result += CDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__DATA,__objc_data\"))) = {\n"; - Result += "\t"; - if (metaclass) { - if (!rootClass) { - Result += "0, // &"; Result += VarName; - Result += RootClass->getNameAsString(); - Result += ",\n\t"; - Result += "0, // &"; Result += VarName; - Result += CDecl->getSuperClass()->getNameAsString(); - Result += ",\n\t"; - } - else { - Result += "0, // &"; Result += VarName; - Result += CDecl->getNameAsString(); - Result += ",\n\t"; - Result += "0, // &OBJC_CLASS_$_"; Result += CDecl->getNameAsString(); - Result += ",\n\t"; - } - } - else { - Result += "0, // &OBJC_METACLASS_$_"; - Result += CDecl->getNameAsString(); - Result += ",\n\t"; - if (!rootClass) { - Result += "0, // &"; Result += VarName; - Result += CDecl->getSuperClass()->getNameAsString(); - Result += ",\n\t"; - } - else - Result += "0,\n\t"; - } - Result += "0, // (void *)&_objc_empty_cache,\n\t"; - Result += "0, // unused, was (void *)&_objc_empty_vtable,\n\t"; - if (metaclass) - Result += "&_OBJC_METACLASS_RO_$_"; - else - Result += "&_OBJC_CLASS_RO_$_"; - Result += CDecl->getNameAsString(); - Result += ",\n};\n"; - - // Add static function to initialize some of the meta-data fields. - // avoid doing it twice. - if (metaclass) - return; - - const ObjCInterfaceDecl *SuperClass = - rootClass ? CDecl : CDecl->getSuperClass(); - - Result += "static void OBJC_CLASS_SETUP_$_"; - Result += CDecl->getNameAsString(); - Result += "(void ) {\n"; - Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); - Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; - Result += RootClass->getNameAsString(); Result += ";\n"; - - Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); - Result += ".superclass = "; - if (rootClass) - Result += "&OBJC_CLASS_$_"; - else - Result += "&OBJC_METACLASS_$_"; - - Result += SuperClass->getNameAsString(); Result += ";\n"; - - Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); - Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; - - Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); - Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; - Result += CDecl->getNameAsString(); Result += ";\n"; - - if (!rootClass) { - Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); - Result += ".superclass = "; Result += "&OBJC_CLASS_$_"; - Result += SuperClass->getNameAsString(); Result += ";\n"; - } - - Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); - Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; - Result += "}\n"; -} - -static void Write_category_t(RewriteModernObjC &RewriteObj, ASTContext *Context, - std::string &Result, - ObjCCategoryDecl *CatDecl, - ObjCInterfaceDecl *ClassDecl, - ArrayRef InstanceMethods, - ArrayRef ClassMethods, - ArrayRef RefedProtocols, - ArrayRef ClassProperties) { - StringRef CatName = CatDecl->getName(); - StringRef ClassName = ClassDecl->getName(); - // must declare an extern class object in case this class is not implemented - // in this TU. - Result += "\n"; - Result += "extern \"C\" "; - if (ClassDecl->getImplementation()) - Result += "__declspec(dllexport) "; - else - Result += "__declspec(dllimport) "; - - Result += "struct _class_t "; - Result += "OBJC_CLASS_$_"; Result += ClassName; - Result += ";\n"; - - Result += "\nstatic struct _category_t "; - Result += "_OBJC_$_CATEGORY_"; - Result += ClassName; Result += "_$_"; Result += CatName; - Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; - Result += "{\n"; - Result += "\t\""; Result += ClassName; Result += "\",\n"; - Result += "\t0, // &"; Result += "OBJC_CLASS_$_"; Result += ClassName; - Result += ",\n"; - if (InstanceMethods.size() > 0) { - Result += "\t(const struct _method_list_t *)&"; - Result += "_OBJC_$_CATEGORY_INSTANCE_METHODS_"; - Result += ClassName; Result += "_$_"; Result += CatName; - Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (ClassMethods.size() > 0) { - Result += "\t(const struct _method_list_t *)&"; - Result += "_OBJC_$_CATEGORY_CLASS_METHODS_"; - Result += ClassName; Result += "_$_"; Result += CatName; - Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (RefedProtocols.size() > 0) { - Result += "\t(const struct _protocol_list_t *)&"; - Result += "_OBJC_CATEGORY_PROTOCOLS_$_"; - Result += ClassName; Result += "_$_"; Result += CatName; - Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (ClassProperties.size() > 0) { - Result += "\t(const struct _prop_list_t *)&"; Result += "_OBJC_$_PROP_LIST_"; - Result += ClassName; Result += "_$_"; Result += CatName; - Result += ",\n"; - } - else - Result += "\t0,\n"; - - Result += "};\n"; - - // Add static function to initialize the class pointer in the category structure. - Result += "static void OBJC_CATEGORY_SETUP_$_"; - Result += ClassDecl->getNameAsString(); - Result += "_$_"; - Result += CatName; - Result += "(void ) {\n"; - Result += "\t_OBJC_$_CATEGORY_"; - Result += ClassDecl->getNameAsString(); - Result += "_$_"; - Result += CatName; - Result += ".cls = "; Result += "&OBJC_CLASS_$_"; Result += ClassName; - Result += ";\n}\n"; -} - -static void Write__extendedMethodTypes_initializer(RewriteModernObjC &RewriteObj, - ASTContext *Context, std::string &Result, - ArrayRef Methods, - StringRef VarName, - StringRef ProtocolName) { - if (Methods.size() == 0) - return; - - Result += "\nstatic const char *"; - Result += VarName; Result += ProtocolName; - Result += " [] __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; - Result += "{\n"; - for (unsigned i = 0, e = Methods.size(); i < e; i++) { - ObjCMethodDecl *MD = Methods[i]; - std::string MethodTypeString, QuoteMethodTypeString; - Context->getObjCEncodingForMethodDecl(MD, MethodTypeString, true); - RewriteObj.QuoteDoublequotes(MethodTypeString, QuoteMethodTypeString); - Result += "\t\""; Result += QuoteMethodTypeString; Result += "\""; - if (i == e-1) - Result += "\n};\n"; - else { - Result += ",\n"; - } - } -} - -static void Write_IvarOffsetVar(RewriteModernObjC &RewriteObj, - ASTContext *Context, - std::string &Result, - ArrayRef Ivars, - ObjCInterfaceDecl *CDecl) { - // FIXME. visibilty of offset symbols may have to be set; for Darwin - // this is what happens: - /** - if (Ivar->getAccessControl() == ObjCIvarDecl::Private || - Ivar->getAccessControl() == ObjCIvarDecl::Package || - Class->getVisibility() == HiddenVisibility) - Visibility shoud be: HiddenVisibility; - else - Visibility shoud be: DefaultVisibility; - */ - - Result += "\n"; - for (unsigned i =0, e = Ivars.size(); i < e; i++) { - ObjCIvarDecl *IvarDecl = Ivars[i]; - if (Context->getLangOpts().MicrosoftExt) - Result += "__declspec(allocate(\".objc_ivar$B\")) "; - - if (!Context->getLangOpts().MicrosoftExt || - IvarDecl->getAccessControl() == ObjCIvarDecl::Private || - IvarDecl->getAccessControl() == ObjCIvarDecl::Package) - Result += "extern \"C\" unsigned long int "; - else - Result += "extern \"C\" __declspec(dllexport) unsigned long int "; - WriteInternalIvarName(CDecl, IvarDecl, Result); - Result += " __attribute__ ((used, section (\"__DATA,__objc_ivar\")))"; - Result += " = "; - RewriteObj.RewriteIvarOffsetComputation(IvarDecl, Result); - Result += ";\n"; - } -} - -static void Write__ivar_list_t_initializer(RewriteModernObjC &RewriteObj, - ASTContext *Context, std::string &Result, - ArrayRef Ivars, - StringRef VarName, - ObjCInterfaceDecl *CDecl) { - if (Ivars.size() > 0) { - Write_IvarOffsetVar(RewriteObj, Context, Result, Ivars, CDecl); - - Result += "\nstatic "; - Write__ivar_list_t_TypeDecl(Result, Ivars.size()); - Result += " "; Result += VarName; - Result += CDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; - Result += "\t"; Result += "sizeof(_ivar_t)"; Result += ",\n"; - Result += "\t"; Result += utostr(Ivars.size()); Result += ",\n"; - for (unsigned i =0, e = Ivars.size(); i < e; i++) { - ObjCIvarDecl *IvarDecl = Ivars[i]; - if (i == 0) - Result += "\t{{"; - else - Result += "\t {"; - Result += "(unsigned long int *)&"; - WriteInternalIvarName(CDecl, IvarDecl, Result); - Result += ", "; - - Result += "\""; Result += IvarDecl->getName(); Result += "\", "; - std::string IvarTypeString, QuoteIvarTypeString; - Context->getObjCEncodingForType(IvarDecl->getType(), IvarTypeString, - IvarDecl); - RewriteObj.QuoteDoublequotes(IvarTypeString, QuoteIvarTypeString); - Result += "\""; Result += QuoteIvarTypeString; Result += "\", "; - - // FIXME. this alignment represents the host alignment and need be changed to - // represent the target alignment. - unsigned Align = Context->getTypeAlign(IvarDecl->getType())/8; - Align = llvm::Log2_32(Align); - Result += llvm::utostr(Align); Result += ", "; - CharUnits Size = Context->getTypeSizeInChars(IvarDecl->getType()); - Result += llvm::utostr(Size.getQuantity()); - if (i == e-1) - Result += "}}\n"; - else - Result += "},\n"; - } - Result += "};\n"; - } -} - -/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. -void RewriteModernObjC::RewriteObjCProtocolMetaData(ObjCProtocolDecl *PDecl, - std::string &Result) { - - // Do not synthesize the protocol more than once. - if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) - return; - WriteModernMetadataDeclarations(Context, Result); - - if (ObjCProtocolDecl *Def = PDecl->getDefinition()) - PDecl = Def; - // Must write out all protocol definitions in current qualifier list, - // and in their nested qualifiers before writing out current definition. - for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), - E = PDecl->protocol_end(); I != E; ++I) - RewriteObjCProtocolMetaData(*I, Result); - - // Construct method lists. - std::vector InstanceMethods, ClassMethods; - std::vector OptInstanceMethods, OptClassMethods; - for (ObjCProtocolDecl::instmeth_iterator - I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); - I != E; ++I) { - ObjCMethodDecl *MD = *I; - if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { - OptInstanceMethods.push_back(MD); - } else { - InstanceMethods.push_back(MD); - } - } - - for (ObjCProtocolDecl::classmeth_iterator - I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); - I != E; ++I) { - ObjCMethodDecl *MD = *I; - if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { - OptClassMethods.push_back(MD); - } else { - ClassMethods.push_back(MD); - } - } - std::vector AllMethods; - for (unsigned i = 0, e = InstanceMethods.size(); i < e; i++) - AllMethods.push_back(InstanceMethods[i]); - for (unsigned i = 0, e = ClassMethods.size(); i < e; i++) - AllMethods.push_back(ClassMethods[i]); - for (unsigned i = 0, e = OptInstanceMethods.size(); i < e; i++) - AllMethods.push_back(OptInstanceMethods[i]); - for (unsigned i = 0, e = OptClassMethods.size(); i < e; i++) - AllMethods.push_back(OptClassMethods[i]); - - Write__extendedMethodTypes_initializer(*this, Context, Result, - AllMethods, - "_OBJC_PROTOCOL_METHOD_TYPES_", - PDecl->getNameAsString()); - // Protocol's super protocol list - std::vector SuperProtocols; - for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), - E = PDecl->protocol_end(); I != E; ++I) - SuperProtocols.push_back(*I); - - Write_protocol_list_initializer(Context, Result, SuperProtocols, - "_OBJC_PROTOCOL_REFS_", - PDecl->getNameAsString()); - - Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, - "_OBJC_PROTOCOL_INSTANCE_METHODS_", - PDecl->getNameAsString(), false); - - Write_method_list_t_initializer(*this, Context, Result, ClassMethods, - "_OBJC_PROTOCOL_CLASS_METHODS_", - PDecl->getNameAsString(), false); - - Write_method_list_t_initializer(*this, Context, Result, OptInstanceMethods, - "_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_", - PDecl->getNameAsString(), false); - - Write_method_list_t_initializer(*this, Context, Result, OptClassMethods, - "_OBJC_PROTOCOL_OPT_CLASS_METHODS_", - PDecl->getNameAsString(), false); - - // Protocol's property metadata. - std::vector ProtocolProperties; - for (ObjCContainerDecl::prop_iterator I = PDecl->prop_begin(), - E = PDecl->prop_end(); I != E; ++I) - ProtocolProperties.push_back(*I); - - Write_prop_list_t_initializer(*this, Context, Result, ProtocolProperties, - /* Container */0, - "_OBJC_PROTOCOL_PROPERTIES_", - PDecl->getNameAsString()); - - // Writer out root metadata for current protocol: struct _protocol_t - Result += "\n"; - if (LangOpts.MicrosoftExt) - Result += "static "; - Result += "struct _protocol_t _OBJC_PROTOCOL_"; - Result += PDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__DATA,__datacoal_nt,coalesced\"))) = {\n"; - Result += "\t0,\n"; // id is; is null - Result += "\t\""; Result += PDecl->getNameAsString(); Result += "\",\n"; - if (SuperProtocols.size() > 0) { - Result += "\t(const struct _protocol_list_t *)&"; Result += "_OBJC_PROTOCOL_REFS_"; - Result += PDecl->getNameAsString(); Result += ",\n"; - } - else - Result += "\t0,\n"; - if (InstanceMethods.size() > 0) { - Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; - Result += PDecl->getNameAsString(); Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (ClassMethods.size() > 0) { - Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_"; - Result += PDecl->getNameAsString(); Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (OptInstanceMethods.size() > 0) { - Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_"; - Result += PDecl->getNameAsString(); Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (OptClassMethods.size() > 0) { - Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_CLASS_METHODS_"; - Result += PDecl->getNameAsString(); Result += ",\n"; - } - else - Result += "\t0,\n"; - - if (ProtocolProperties.size() > 0) { - Result += "\t(const struct _prop_list_t *)&_OBJC_PROTOCOL_PROPERTIES_"; - Result += PDecl->getNameAsString(); Result += ",\n"; - } - else - Result += "\t0,\n"; - - Result += "\t"; Result += "sizeof(_protocol_t)"; Result += ",\n"; - Result += "\t0,\n"; - - if (AllMethods.size() > 0) { - Result += "\t(const char **)&"; Result += "_OBJC_PROTOCOL_METHOD_TYPES_"; - Result += PDecl->getNameAsString(); - Result += "\n};\n"; - } - else - Result += "\t0\n};\n"; - - if (LangOpts.MicrosoftExt) - Result += "static "; - Result += "struct _protocol_t *"; - Result += "_OBJC_LABEL_PROTOCOL_$_"; Result += PDecl->getNameAsString(); - Result += " = &_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); - Result += ";\n"; - - // Mark this protocol as having been generated. - if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl())) - llvm_unreachable("protocol already synthesized"); - -} - -void RewriteModernObjC::RewriteObjCProtocolListMetaData( - const ObjCList &Protocols, - StringRef prefix, StringRef ClassName, - std::string &Result) { - if (Protocols.empty()) return; - - for (unsigned i = 0; i != Protocols.size(); i++) - RewriteObjCProtocolMetaData(Protocols[i], Result); - - // Output the top lovel protocol meta-data for the class. - /* struct _objc_protocol_list { - struct _objc_protocol_list *next; - int protocol_count; - struct _objc_protocol *class_protocols[]; - } - */ - Result += "\n"; - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".cat_cls_meth$B\")) "; - Result += "static struct {\n"; - Result += "\tstruct _objc_protocol_list *next;\n"; - Result += "\tint protocol_count;\n"; - Result += "\tstruct _objc_protocol *class_protocols["; - Result += utostr(Protocols.size()); - Result += "];\n} _OBJC_"; - Result += prefix; - Result += "_PROTOCOLS_"; - Result += ClassName; - Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " - "{\n\t0, "; - Result += utostr(Protocols.size()); - Result += "\n"; - - Result += "\t,{&_OBJC_PROTOCOL_"; - Result += Protocols[0]->getNameAsString(); - Result += " \n"; - - for (unsigned i = 1; i != Protocols.size(); i++) { - Result += "\t ,&_OBJC_PROTOCOL_"; - Result += Protocols[i]->getNameAsString(); - Result += "\n"; - } - Result += "\t }\n};\n"; -} - -/// hasObjCExceptionAttribute - Return true if this class or any super -/// class has the __objc_exception__ attribute. -/// FIXME. Move this to ASTContext.cpp as it is also used for IRGen. -static bool hasObjCExceptionAttribute(ASTContext &Context, - const ObjCInterfaceDecl *OID) { - if (OID->hasAttr()) - return true; - if (const ObjCInterfaceDecl *Super = OID->getSuperClass()) - return hasObjCExceptionAttribute(Context, Super); - return false; -} - -void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, - std::string &Result) { - ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); - - // Explicitly declared @interface's are already synthesized. - if (CDecl->isImplicitInterfaceDecl()) - assert(false && - "Legacy implicit interface rewriting not supported in moder abi"); - - WriteModernMetadataDeclarations(Context, Result); - SmallVector IVars; - - for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); - IVD; IVD = IVD->getNextIvar()) { - // Ignore unnamed bit-fields. - if (!IVD->getDeclName()) - continue; - IVars.push_back(IVD); - } - - Write__ivar_list_t_initializer(*this, Context, Result, IVars, - "_OBJC_$_INSTANCE_VARIABLES_", - CDecl); - - // Build _objc_method_list for class's instance methods if needed - SmallVector - InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); - - // If any of our property implementations have associated getters or - // setters, produce metadata for them as well. - for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), - PropEnd = IDecl->propimpl_end(); - Prop != PropEnd; ++Prop) { - if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - continue; - if (!Prop->getPropertyIvarDecl()) - continue; - ObjCPropertyDecl *PD = Prop->getPropertyDecl(); - if (!PD) - continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) - InstanceMethods.push_back(Getter); - if (PD->isReadOnly()) - continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) - if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/)) - InstanceMethods.push_back(Setter); - } - - Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, - "_OBJC_$_INSTANCE_METHODS_", - IDecl->getNameAsString(), true); - - SmallVector - ClassMethods(IDecl->classmeth_begin(), IDecl->classmeth_end()); - - Write_method_list_t_initializer(*this, Context, Result, ClassMethods, - "_OBJC_$_CLASS_METHODS_", - IDecl->getNameAsString(), true); - - // Protocols referenced in class declaration? - // Protocol's super protocol list - std::vector RefedProtocols; - const ObjCList &Protocols = CDecl->getReferencedProtocols(); - for (ObjCList::iterator I = Protocols.begin(), - E = Protocols.end(); - I != E; ++I) { - RefedProtocols.push_back(*I); - // Must write out all protocol definitions in current qualifier list, - // and in their nested qualifiers before writing out current definition. - RewriteObjCProtocolMetaData(*I, Result); - } - - Write_protocol_list_initializer(Context, Result, - RefedProtocols, - "_OBJC_CLASS_PROTOCOLS_$_", - IDecl->getNameAsString()); - - // Protocol's property metadata. - std::vector ClassProperties; - for (ObjCContainerDecl::prop_iterator I = CDecl->prop_begin(), - E = CDecl->prop_end(); I != E; ++I) - ClassProperties.push_back(*I); - - Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, - /* Container */IDecl, - "_OBJC_$_PROP_LIST_", - CDecl->getNameAsString()); - - - // Data for initializing _class_ro_t metaclass meta-data - uint32_t flags = CLS_META; - std::string InstanceSize; - std::string InstanceStart; - - - bool classIsHidden = CDecl->getVisibility() == HiddenVisibility; - if (classIsHidden) - flags |= OBJC2_CLS_HIDDEN; - - if (!CDecl->getSuperClass()) - // class is root - flags |= CLS_ROOT; - InstanceSize = "sizeof(struct _class_t)"; - InstanceStart = InstanceSize; - Write__class_ro_t_initializer(Context, Result, flags, - InstanceStart, InstanceSize, - ClassMethods, - 0, - 0, - 0, - "_OBJC_METACLASS_RO_$_", - CDecl->getNameAsString()); - - - // Data for initializing _class_ro_t meta-data - flags = CLS; - if (classIsHidden) - flags |= OBJC2_CLS_HIDDEN; - - if (hasObjCExceptionAttribute(*Context, CDecl)) - flags |= CLS_EXCEPTION; - - if (!CDecl->getSuperClass()) - // class is root - flags |= CLS_ROOT; - - InstanceSize.clear(); - InstanceStart.clear(); - if (!ObjCSynthesizedStructs.count(CDecl)) { - InstanceSize = "0"; - InstanceStart = "0"; - } - else { - InstanceSize = "sizeof(struct "; - InstanceSize += CDecl->getNameAsString(); - InstanceSize += "_IMPL)"; - - ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); - if (IVD) { - RewriteIvarOffsetComputation(IVD, InstanceStart); - } - else - InstanceStart = InstanceSize; - } - Write__class_ro_t_initializer(Context, Result, flags, - InstanceStart, InstanceSize, - InstanceMethods, - RefedProtocols, - IVars, - ClassProperties, - "_OBJC_CLASS_RO_$_", - CDecl->getNameAsString()); - - Write_class_t(Context, Result, - "OBJC_METACLASS_$_", - CDecl, /*metaclass*/true); - - Write_class_t(Context, Result, - "OBJC_CLASS_$_", - CDecl, /*metaclass*/false); - - if (ImplementationIsNonLazy(IDecl)) - DefinedNonLazyClasses.push_back(CDecl); - -} - -void RewriteModernObjC::RewriteClassSetupInitHook(std::string &Result) { - int ClsDefCount = ClassImplementation.size(); - if (!ClsDefCount) - return; - Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; - Result += "__declspec(allocate(\".objc_inithooks$B\")) "; - Result += "static void *OBJC_CLASS_SETUP[] = {\n"; - for (int i = 0; i < ClsDefCount; i++) { - ObjCImplementationDecl *IDecl = ClassImplementation[i]; - ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); - Result += "\t(void *)&OBJC_CLASS_SETUP_$_"; - Result += CDecl->getName(); Result += ",\n"; - } - Result += "};\n"; -} - -void RewriteModernObjC::RewriteMetaDataIntoBuffer(std::string &Result) { - int ClsDefCount = ClassImplementation.size(); - int CatDefCount = CategoryImplementation.size(); - - // For each implemented class, write out all its meta data. - for (int i = 0; i < ClsDefCount; i++) - RewriteObjCClassMetaData(ClassImplementation[i], Result); - - RewriteClassSetupInitHook(Result); - - // For each implemented category, write out all its meta data. - for (int i = 0; i < CatDefCount; i++) - RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); - - RewriteCategorySetupInitHook(Result); - - if (ClsDefCount > 0) { - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".objc_classlist$B\")) "; - Result += "static struct _class_t *L_OBJC_LABEL_CLASS_$ ["; - Result += llvm::utostr(ClsDefCount); Result += "]"; - Result += - " __attribute__((used, section (\"__DATA, __objc_classlist," - "regular,no_dead_strip\")))= {\n"; - for (int i = 0; i < ClsDefCount; i++) { - Result += "\t&OBJC_CLASS_$_"; - Result += ClassImplementation[i]->getNameAsString(); - Result += ",\n"; - } - Result += "};\n"; - - if (!DefinedNonLazyClasses.empty()) { - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".objc_nlclslist$B\")) \n"; - Result += "static struct _class_t *_OBJC_LABEL_NONLAZY_CLASS_$[] = {\n\t"; - for (unsigned i = 0, e = DefinedNonLazyClasses.size(); i < e; i++) { - Result += "\t&OBJC_CLASS_$_"; Result += DefinedNonLazyClasses[i]->getNameAsString(); - Result += ",\n"; - } - Result += "};\n"; - } - } - - if (CatDefCount > 0) { - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".objc_catlist$B\")) "; - Result += "static struct _category_t *L_OBJC_LABEL_CATEGORY_$ ["; - Result += llvm::utostr(CatDefCount); Result += "]"; - Result += - " __attribute__((used, section (\"__DATA, __objc_catlist," - "regular,no_dead_strip\")))= {\n"; - for (int i = 0; i < CatDefCount; i++) { - Result += "\t&_OBJC_$_CATEGORY_"; - Result += - CategoryImplementation[i]->getClassInterface()->getNameAsString(); - Result += "_$_"; - Result += CategoryImplementation[i]->getNameAsString(); - Result += ",\n"; - } - Result += "};\n"; - } - - if (!DefinedNonLazyCategories.empty()) { - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".objc_nlcatlist$B\")) \n"; - Result += "static struct _category_t *_OBJC_LABEL_NONLAZY_CATEGORY_$[] = {\n\t"; - for (unsigned i = 0, e = DefinedNonLazyCategories.size(); i < e; i++) { - Result += "\t&_OBJC_$_CATEGORY_"; - Result += - DefinedNonLazyCategories[i]->getClassInterface()->getNameAsString(); - Result += "_$_"; - Result += DefinedNonLazyCategories[i]->getNameAsString(); - Result += ",\n"; - } - Result += "};\n"; - } -} - -void RewriteModernObjC::WriteImageInfo(std::string &Result) { - if (LangOpts.MicrosoftExt) - Result += "__declspec(allocate(\".objc_imageinfo$B\")) \n"; - - Result += "static struct IMAGE_INFO { unsigned version; unsigned flag; } "; - // version 0, ObjCABI is 2 - Result += "_OBJC_IMAGE_INFO = { 0, 2 };\n"; -} - -/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category -/// implementation. -void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, - std::string &Result) { - WriteModernMetadataDeclarations(Context, Result); - ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); - // Find category declaration for this implementation. - ObjCCategoryDecl *CDecl=0; - for (CDecl = ClassDecl->getCategoryList(); CDecl; - CDecl = CDecl->getNextClassCategory()) - if (CDecl->getIdentifier() == IDecl->getIdentifier()) - break; - - std::string FullCategoryName = ClassDecl->getNameAsString(); - FullCategoryName += "_$_"; - FullCategoryName += CDecl->getNameAsString(); - - // Build _objc_method_list for class's instance methods if needed - SmallVector - InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); - - // If any of our property implementations have associated getters or - // setters, produce metadata for them as well. - for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), - PropEnd = IDecl->propimpl_end(); - Prop != PropEnd; ++Prop) { - if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - continue; - if (!Prop->getPropertyIvarDecl()) - continue; - ObjCPropertyDecl *PD = Prop->getPropertyDecl(); - if (!PD) - continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - InstanceMethods.push_back(Getter); - if (PD->isReadOnly()) - continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) - InstanceMethods.push_back(Setter); - } - - Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, - "_OBJC_$_CATEGORY_INSTANCE_METHODS_", - FullCategoryName, true); - - SmallVector - ClassMethods(IDecl->classmeth_begin(), IDecl->classmeth_end()); - - Write_method_list_t_initializer(*this, Context, Result, ClassMethods, - "_OBJC_$_CATEGORY_CLASS_METHODS_", - FullCategoryName, true); - - // Protocols referenced in class declaration? - // Protocol's super protocol list - std::vector RefedProtocols; - for (ObjCInterfaceDecl::protocol_iterator I = CDecl->protocol_begin(), - E = CDecl->protocol_end(); - - I != E; ++I) { - RefedProtocols.push_back(*I); - // Must write out all protocol definitions in current qualifier list, - // and in their nested qualifiers before writing out current definition. - RewriteObjCProtocolMetaData(*I, Result); - } - - Write_protocol_list_initializer(Context, Result, - RefedProtocols, - "_OBJC_CATEGORY_PROTOCOLS_$_", - FullCategoryName); - - // Protocol's property metadata. - std::vector ClassProperties; - for (ObjCContainerDecl::prop_iterator I = CDecl->prop_begin(), - E = CDecl->prop_end(); I != E; ++I) - ClassProperties.push_back(*I); - - Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, - /* Container */IDecl, - "_OBJC_$_PROP_LIST_", - FullCategoryName); - - Write_category_t(*this, Context, Result, - CDecl, - ClassDecl, - InstanceMethods, - ClassMethods, - RefedProtocols, - ClassProperties); - - // Determine if this category is also "non-lazy". - if (ImplementationIsNonLazy(IDecl)) - DefinedNonLazyCategories.push_back(CDecl); - -} - -void RewriteModernObjC::RewriteCategorySetupInitHook(std::string &Result) { - int CatDefCount = CategoryImplementation.size(); - if (!CatDefCount) - return; - Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; - Result += "__declspec(allocate(\".objc_inithooks$B\")) "; - Result += "static void *OBJC_CATEGORY_SETUP[] = {\n"; - for (int i = 0; i < CatDefCount; i++) { - ObjCCategoryImplDecl *IDecl = CategoryImplementation[i]; - ObjCCategoryDecl *CatDecl= IDecl->getCategoryDecl(); - ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); - Result += "\t(void *)&OBJC_CATEGORY_SETUP_$_"; - Result += ClassDecl->getName(); - Result += "_$_"; - Result += CatDecl->getName(); - Result += ",\n"; - } - Result += "};\n"; -} - -// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or -/// class methods. -template -void RewriteModernObjC::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, - MethodIterator MethodEnd, - bool IsInstanceMethod, - StringRef prefix, - StringRef ClassName, - std::string &Result) { - if (MethodBegin == MethodEnd) return; - - if (!objc_impl_method) { - /* struct _objc_method { - SEL _cmd; - char *method_types; - void *_imp; - } - */ - Result += "\nstruct _objc_method {\n"; - Result += "\tSEL _cmd;\n"; - Result += "\tchar *method_types;\n"; - Result += "\tvoid *_imp;\n"; - Result += "};\n"; - - objc_impl_method = true; - } - - // Build _objc_method_list for class's methods if needed - - /* struct { - struct _objc_method_list *next_method; - int method_count; - struct _objc_method method_list[]; - } - */ - unsigned NumMethods = std::distance(MethodBegin, MethodEnd); - Result += "\n"; - if (LangOpts.MicrosoftExt) { - if (IsInstanceMethod) - Result += "__declspec(allocate(\".inst_meth$B\")) "; - else - Result += "__declspec(allocate(\".cls_meth$B\")) "; - } - Result += "static struct {\n"; - Result += "\tstruct _objc_method_list *next_method;\n"; - Result += "\tint method_count;\n"; - Result += "\tstruct _objc_method method_list["; - Result += utostr(NumMethods); - Result += "];\n} _OBJC_"; - Result += prefix; - Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; - Result += "_METHODS_"; - Result += ClassName; - Result += " __attribute__ ((used, section (\"__OBJC, __"; - Result += IsInstanceMethod ? "inst" : "cls"; - Result += "_meth\")))= "; - Result += "{\n\t0, " + utostr(NumMethods) + "\n"; - - Result += "\t,{{(SEL)\""; - Result += (*MethodBegin)->getSelector().getAsString().c_str(); - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); - Result += "\", \""; - Result += MethodTypeString; - Result += "\", (void *)"; - Result += MethodInternalNames[*MethodBegin]; - Result += "}\n"; - for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { - Result += "\t ,{(SEL)\""; - Result += (*MethodBegin)->getSelector().getAsString().c_str(); - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); - Result += "\", \""; - Result += MethodTypeString; - Result += "\", (void *)"; - Result += MethodInternalNames[*MethodBegin]; - Result += "}\n"; - } - Result += "\t }\n};\n"; -} - -Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { - SourceRange OldRange = IV->getSourceRange(); - Expr *BaseExpr = IV->getBase(); - - // Rewrite the base, but without actually doing replaces. - { - DisableReplaceStmtScope S(*this); - BaseExpr = cast(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); - IV->setBase(BaseExpr); - } - - ObjCIvarDecl *D = IV->getDecl(); - - Expr *Replacement = IV; - - if (BaseExpr->getType()->isObjCObjectPointerType()) { - const ObjCInterfaceType *iFaceDecl = - dyn_cast(BaseExpr->getType()->getPointeeType()); - assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); - // lookup which class implements the instance variable. - ObjCInterfaceDecl *clsDeclared = 0; - iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), - clsDeclared); - assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); - - // Build name of symbol holding ivar offset. - std::string IvarOffsetName; - WriteInternalIvarName(clsDeclared, D, IvarOffsetName); - - ReferencedIvars[clsDeclared].insert(D); - - // cast offset to "char *". - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->CharTy), - CK_BitCast, - BaseExpr); - VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), &Context->Idents.get(IvarOffsetName), - Context->UnsignedLongTy, 0, SC_Extern, SC_None); - DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, - Context->UnsignedLongTy, VK_LValue, - SourceLocation()); - BinaryOperator *addExpr = - new (Context) BinaryOperator(castExpr, DRE, BO_Add, - Context->getPointerType(Context->CharTy), - VK_RValue, OK_Ordinary, SourceLocation()); - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), - SourceLocation(), - addExpr); - QualType IvarT = D->getType(); - - if (!isa(IvarT) && IvarT->isRecordType()) { - RecordDecl *RD = IvarT->getAs()->getDecl(); - RD = RD->getDefinition(); - if (RD && !RD->getDeclName().getAsIdentifierInfo()) { - // decltype(((Foo_IMPL*)0)->bar) * - ObjCContainerDecl *CDecl = - dyn_cast(D->getDeclContext()); - // ivar in class extensions requires special treatment. - if (ObjCCategoryDecl *CatDecl = dyn_cast(CDecl)) - CDecl = CatDecl->getClassInterface(); - std::string RecName = CDecl->getName(); - RecName += "_IMPL"; - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get(RecName.c_str())); - QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); - unsigned UnsignedIntSize = - static_cast(Context->getTypeSize(Context->UnsignedIntTy)); - Expr *Zero = IntegerLiteral::Create(*Context, - llvm::APInt(UnsignedIntSize, 0), - Context->UnsignedIntTy, SourceLocation()); - Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - Zero); - FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get(D->getNameAsString()), - IvarT, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), - FD->getType(), VK_LValue, - OK_Ordinary); - IvarT = Context->getDecltypeType(ME, ME->getType()); - } - } - convertObjCTypeToCStyleType(IvarT); - QualType castT = Context->getPointerType(IvarT); - - castExpr = NoTypeInfoCStyleCastExpr(Context, - castT, - CK_BitCast, - PE); - - - Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, - VK_LValue, OK_Ordinary, - SourceLocation()); - PE = new (Context) ParenExpr(OldRange.getBegin(), - OldRange.getEnd(), - Exp); - - Replacement = PE; - } - - ReplaceStmtWithRange(IV, Replacement, OldRange); - return Replacement; -} diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp deleted file mode 100644 index 37c17e63db947..0000000000000 --- a/lib/Rewrite/RewriteObjC.cpp +++ /dev/null @@ -1,6035 +0,0 @@ -//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Hacks and fun related to the code rewriter. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/ASTConsumers.h" -#include "clang/Rewrite/Rewriter.h" -#include "clang/AST/AST.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/ParentMap.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Basic/IdentifierTable.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Lex/Lexer.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/DenseSet.h" - -using namespace clang; -using llvm::utostr; - -namespace { - class RewriteObjC : public ASTConsumer { - protected: - - enum { - BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), - block, ... */ - BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ - BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the - __block variable */ - BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy - helpers */ - BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose - support routines */ - BLOCK_BYREF_CURRENT_MAX = 256 - }; - - enum { - BLOCK_NEEDS_FREE = (1 << 24), - BLOCK_HAS_COPY_DISPOSE = (1 << 25), - BLOCK_HAS_CXX_OBJ = (1 << 26), - BLOCK_IS_GC = (1 << 27), - BLOCK_IS_GLOBAL = (1 << 28), - BLOCK_HAS_DESCRIPTOR = (1 << 29) - }; - static const int OBJC_ABI_VERSION = 7; - - Rewriter Rewrite; - DiagnosticsEngine &Diags; - const LangOptions &LangOpts; - ASTContext *Context; - SourceManager *SM; - TranslationUnitDecl *TUDecl; - FileID MainFileID; - const char *MainFileStart, *MainFileEnd; - Stmt *CurrentBody; - ParentMap *PropParentMap; // created lazily. - std::string InFileName; - raw_ostream* OutFile; - std::string Preamble; - - TypeDecl *ProtocolTypeDecl; - VarDecl *GlobalVarDecl; - unsigned RewriteFailedDiag; - // ObjC string constant support. - unsigned NumObjCStringLiterals; - VarDecl *ConstantStringClassReference; - RecordDecl *NSStringRecord; - - // ObjC foreach break/continue generation support. - int BcLabelCount; - - unsigned TryFinallyContainsReturnDiag; - // Needed for super. - ObjCMethodDecl *CurMethodDef; - RecordDecl *SuperStructDecl; - RecordDecl *ConstantStringDecl; - - FunctionDecl *MsgSendFunctionDecl; - FunctionDecl *MsgSendSuperFunctionDecl; - FunctionDecl *MsgSendStretFunctionDecl; - FunctionDecl *MsgSendSuperStretFunctionDecl; - FunctionDecl *MsgSendFpretFunctionDecl; - FunctionDecl *GetClassFunctionDecl; - FunctionDecl *GetMetaClassFunctionDecl; - FunctionDecl *GetSuperClassFunctionDecl; - FunctionDecl *SelGetUidFunctionDecl; - FunctionDecl *CFStringFunctionDecl; - FunctionDecl *SuperContructorFunctionDecl; - FunctionDecl *CurFunctionDef; - FunctionDecl *CurFunctionDeclToDeclareForBlock; - - /* Misc. containers needed for meta-data rewrite. */ - SmallVector ClassImplementation; - SmallVector CategoryImplementation; - llvm::SmallPtrSet ObjCSynthesizedStructs; - llvm::SmallPtrSet ObjCSynthesizedProtocols; - llvm::SmallPtrSet ObjCForwardDecls; - llvm::DenseMap MethodInternalNames; - SmallVector Stmts; - SmallVector ObjCBcLabelNo; - // Remember all the @protocol() expressions. - llvm::SmallPtrSet ProtocolExprDecls; - - llvm::DenseSet CopyDestroyCache; - - // Block expressions. - SmallVector Blocks; - SmallVector InnerDeclRefsCount; - SmallVector InnerDeclRefs; - - SmallVector BlockDeclRefs; - - // Block related declarations. - SmallVector BlockByCopyDecls; - llvm::SmallPtrSet BlockByCopyDeclsPtrSet; - SmallVector BlockByRefDecls; - llvm::SmallPtrSet BlockByRefDeclsPtrSet; - llvm::DenseMap BlockByRefDeclNo; - llvm::SmallPtrSet ImportedBlockDecls; - llvm::SmallPtrSet ImportedLocalExternalDecls; - - llvm::DenseMap RewrittenBlockExprs; - - // This maps an original source AST to it's rewritten form. This allows - // us to avoid rewriting the same node twice (which is very uncommon). - // This is needed to support some of the exotic property rewriting. - llvm::DenseMap ReplacedNodes; - - // Needed for header files being rewritten - bool IsHeader; - bool SilenceRewriteMacroWarning; - bool objc_impl_method; - - bool DisableReplaceStmt; - class DisableReplaceStmtScope { - RewriteObjC &R; - bool SavedValue; - - public: - DisableReplaceStmtScope(RewriteObjC &R) - : R(R), SavedValue(R.DisableReplaceStmt) { - R.DisableReplaceStmt = true; - } - ~DisableReplaceStmtScope() { - R.DisableReplaceStmt = SavedValue; - } - }; - void InitializeCommon(ASTContext &context); - - public: - - // Top Level Driver code. - virtual bool HandleTopLevelDecl(DeclGroupRef D) { - for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { - if (ObjCInterfaceDecl *Class = dyn_cast(*I)) { - if (!Class->isThisDeclarationADefinition()) { - RewriteForwardClassDecl(D); - break; - } - } - - if (ObjCProtocolDecl *Proto = dyn_cast(*I)) { - if (!Proto->isThisDeclarationADefinition()) { - RewriteForwardProtocolDecl(D); - break; - } - } - - HandleTopLevelSingleDecl(*I); - } - return true; - } - void HandleTopLevelSingleDecl(Decl *D); - void HandleDeclInMainFile(Decl *D); - RewriteObjC(std::string inFile, raw_ostream *OS, - DiagnosticsEngine &D, const LangOptions &LOpts, - bool silenceMacroWarn); - - ~RewriteObjC() {} - - virtual void HandleTranslationUnit(ASTContext &C); - - void ReplaceStmt(Stmt *Old, Stmt *New) { - Stmt *ReplacingStmt = ReplacedNodes[Old]; - - if (ReplacingStmt) - return; // We can't rewrite the same node twice. - - if (DisableReplaceStmt) - return; - - // If replacement succeeded or warning disabled return with no warning. - if (!Rewrite.ReplaceStmt(Old, New)) { - ReplacedNodes[Old] = New; - return; - } - if (SilenceRewriteMacroWarning) - return; - Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) - << Old->getSourceRange(); - } - - void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { - if (DisableReplaceStmt) - return; - - // Measure the old text. - int Size = Rewrite.getRangeSize(SrcRange); - if (Size == -1) { - Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) - << Old->getSourceRange(); - return; - } - // Get the new text. - std::string SStr; - llvm::raw_string_ostream S(SStr); - New->printPretty(S, 0, PrintingPolicy(LangOpts)); - const std::string &Str = S.str(); - - // If replacement succeeded or warning disabled return with no warning. - if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { - ReplacedNodes[Old] = New; - return; - } - if (SilenceRewriteMacroWarning) - return; - Diags.Report(Context->getFullLoc(Old->getLocStart()), RewriteFailedDiag) - << Old->getSourceRange(); - } - - void InsertText(SourceLocation Loc, StringRef Str, - bool InsertAfter = true) { - // If insertion succeeded or warning disabled return with no warning. - if (!Rewrite.InsertText(Loc, Str, InsertAfter) || - SilenceRewriteMacroWarning) - return; - - Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); - } - - void ReplaceText(SourceLocation Start, unsigned OrigLength, - StringRef Str) { - // If removal succeeded or warning disabled return with no warning. - if (!Rewrite.ReplaceText(Start, OrigLength, Str) || - SilenceRewriteMacroWarning) - return; - - Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); - } - - // Syntactic Rewriting. - void RewriteRecordBody(RecordDecl *RD); - void RewriteInclude(); - void RewriteForwardClassDecl(DeclGroupRef D); - void RewriteForwardClassDecl(const llvm::SmallVector &DG); - void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, - const std::string &typedefString); - void RewriteImplementations(); - void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, - ObjCImplementationDecl *IMD, - ObjCCategoryImplDecl *CID); - void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); - void RewriteImplementationDecl(Decl *Dcl); - void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, - ObjCMethodDecl *MDecl, std::string &ResultStr); - void RewriteTypeIntoString(QualType T, std::string &ResultStr, - const FunctionType *&FPRetType); - void RewriteByRefString(std::string &ResultStr, const std::string &Name, - ValueDecl *VD, bool def=false); - void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); - void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); - void RewriteForwardProtocolDecl(DeclGroupRef D); - void RewriteForwardProtocolDecl(const llvm::SmallVector &DG); - void RewriteMethodDeclaration(ObjCMethodDecl *Method); - void RewriteProperty(ObjCPropertyDecl *prop); - void RewriteFunctionDecl(FunctionDecl *FD); - void RewriteBlockPointerType(std::string& Str, QualType Type); - void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); - void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); - void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); - void RewriteTypeOfDecl(VarDecl *VD); - void RewriteObjCQualifiedInterfaceTypes(Expr *E); - - // Expression Rewriting. - Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); - Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); - Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); - Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); - Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); - Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); - Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); - Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); - void RewriteTryReturnStmts(Stmt *S); - void RewriteSyncReturnStmts(Stmt *S, std::string buf); - Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); - Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); - Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); - Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, - SourceLocation OrigEnd); - Stmt *RewriteBreakStmt(BreakStmt *S); - Stmt *RewriteContinueStmt(ContinueStmt *S); - void RewriteCastExpr(CStyleCastExpr *CE); - - // Block rewriting. - void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); - - // Block specific rewrite rules. - void RewriteBlockPointerDecl(NamedDecl *VD); - void RewriteByRefVar(VarDecl *VD); - Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); - Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); - void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); - - void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, - std::string &Result); - - virtual void Initialize(ASTContext &context) = 0; - - // Metadata Rewriting. - virtual void RewriteMetaDataIntoBuffer(std::string &Result) = 0; - virtual void RewriteObjCProtocolListMetaData(const ObjCList &Prots, - StringRef prefix, - StringRef ClassName, - std::string &Result) = 0; - virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, - std::string &Result) = 0; - virtual void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, - StringRef prefix, - StringRef ClassName, - std::string &Result) = 0; - virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, - std::string &Result) = 0; - - // Rewriting ivar access - virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) = 0; - virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, - std::string &Result) = 0; - - // Misc. AST transformation routines. Sometimes they end up calling - // rewriting routines on the new ASTs. - CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, - Expr **args, unsigned nargs, - SourceLocation StartLoc=SourceLocation(), - SourceLocation EndLoc=SourceLocation()); - CallExpr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, - QualType msgSendType, - QualType returnType, - SmallVectorImpl &ArgTypes, - SmallVectorImpl &MsgExprs, - ObjCMethodDecl *Method); - Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, - SourceLocation StartLoc=SourceLocation(), - SourceLocation EndLoc=SourceLocation()); - - void SynthCountByEnumWithState(std::string &buf); - void SynthMsgSendFunctionDecl(); - void SynthMsgSendSuperFunctionDecl(); - void SynthMsgSendStretFunctionDecl(); - void SynthMsgSendFpretFunctionDecl(); - void SynthMsgSendSuperStretFunctionDecl(); - void SynthGetClassFunctionDecl(); - void SynthGetMetaClassFunctionDecl(); - void SynthGetSuperClassFunctionDecl(); - void SynthSelGetUidFunctionDecl(); - void SynthSuperContructorFunctionDecl(); - - std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); - std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, - StringRef funcName, std::string Tag); - std::string SynthesizeBlockFunc(BlockExpr *CE, int i, - StringRef funcName, std::string Tag); - std::string SynthesizeBlockImpl(BlockExpr *CE, - std::string Tag, std::string Desc); - std::string SynthesizeBlockDescriptor(std::string DescTag, - std::string ImplTag, - int i, StringRef funcName, - unsigned hasCopy); - Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); - void SynthesizeBlockLiterals(SourceLocation FunLocStart, - StringRef FunName); - FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); - Stmt *SynthBlockInitExpr(BlockExpr *Exp, - const SmallVector &InnerBlockDeclRefs); - - // Misc. helper routines. - QualType getProtocolType(); - void WarnAboutReturnGotoStmts(Stmt *S); - void HasReturnStmts(Stmt *S, bool &hasReturns); - void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); - void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); - void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); - - bool IsDeclStmtInForeachHeader(DeclStmt *DS); - void CollectBlockDeclRefInfo(BlockExpr *Exp); - void GetBlockDeclRefExprs(Stmt *S); - void GetInnerBlockDeclRefExprs(Stmt *S, - SmallVector &InnerBlockDeclRefs, - llvm::SmallPtrSet &InnerContexts); - - // We avoid calling Type::isBlockPointerType(), since it operates on the - // canonical type. We only care if the top-level type is a closure pointer. - bool isTopLevelBlockPointerType(QualType T) { - return isa(T); - } - - /// convertBlockPointerToFunctionPointer - Converts a block-pointer type - /// to a function pointer type and upon success, returns true; false - /// otherwise. - bool convertBlockPointerToFunctionPointer(QualType &T) { - if (isTopLevelBlockPointerType(T)) { - const BlockPointerType *BPT = T->getAs(); - T = Context->getPointerType(BPT->getPointeeType()); - return true; - } - return false; - } - - bool needToScanForQualifiers(QualType T); - QualType getSuperStructType(); - QualType getConstantStringStructType(); - QualType convertFunctionTypeOfBlocks(const FunctionType *FT); - bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); - - void convertToUnqualifiedObjCType(QualType &T) { - if (T->isObjCQualifiedIdType()) - T = Context->getObjCIdType(); - else if (T->isObjCQualifiedClassType()) - T = Context->getObjCClassType(); - else if (T->isObjCObjectPointerType() && - T->getPointeeType()->isObjCQualifiedInterfaceType()) { - if (const ObjCObjectPointerType * OBJPT = - T->getAsObjCInterfacePointerType()) { - const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); - T = QualType(IFaceT, 0); - T = Context->getPointerType(T); - } - } - } - - // FIXME: This predicate seems like it would be useful to add to ASTContext. - bool isObjCType(QualType T) { - if (!LangOpts.ObjC1 && !LangOpts.ObjC2) - return false; - - QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); - - if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || - OCT == Context->getCanonicalType(Context->getObjCClassType())) - return true; - - if (const PointerType *PT = OCT->getAs()) { - if (isa(PT->getPointeeType()) || - PT->getPointeeType()->isObjCQualifiedIdType()) - return true; - } - return false; - } - bool PointerTypeTakesAnyBlockArguments(QualType QT); - bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); - void GetExtentOfArgList(const char *Name, const char *&LParen, - const char *&RParen); - - void QuoteDoublequotes(std::string &From, std::string &To) { - for (unsigned i = 0; i < From.length(); i++) { - if (From[i] == '"') - To += "\\\""; - else - To += From[i]; - } - } - - QualType getSimpleFunctionType(QualType result, - const QualType *args, - unsigned numArgs, - bool variadic = false) { - if (result == Context->getObjCInstanceType()) - result = Context->getObjCIdType(); - FunctionProtoType::ExtProtoInfo fpi; - fpi.Variadic = variadic; - return Context->getFunctionType(result, args, numArgs, fpi); - } - - // Helper function: create a CStyleCastExpr with trivial type source info. - CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, - CastKind Kind, Expr *E) { - TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); - return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, 0, TInfo, - SourceLocation(), SourceLocation()); - } - }; - - class RewriteObjCFragileABI : public RewriteObjC { - public: - - RewriteObjCFragileABI(std::string inFile, raw_ostream *OS, - DiagnosticsEngine &D, const LangOptions &LOpts, - bool silenceMacroWarn) : RewriteObjC(inFile, OS, - D, LOpts, - silenceMacroWarn) {} - - ~RewriteObjCFragileABI() {} - virtual void Initialize(ASTContext &context); - - // Rewriting metadata - template - void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, - MethodIterator MethodEnd, - bool IsInstanceMethod, - StringRef prefix, - StringRef ClassName, - std::string &Result); - virtual void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, - StringRef prefix, - StringRef ClassName, - std::string &Result); - virtual void RewriteObjCProtocolListMetaData( - const ObjCList &Prots, - StringRef prefix, StringRef ClassName, std::string &Result); - virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, - std::string &Result); - virtual void RewriteMetaDataIntoBuffer(std::string &Result); - virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, - std::string &Result); - - // Rewriting ivar - virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, - std::string &Result); - virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); - }; -} - -void RewriteObjC::RewriteBlocksInFunctionProtoType(QualType funcType, - NamedDecl *D) { - if (const FunctionProtoType *fproto - = dyn_cast(funcType.IgnoreParens())) { - for (FunctionProtoType::arg_type_iterator I = fproto->arg_type_begin(), - E = fproto->arg_type_end(); I && (I != E); ++I) - if (isTopLevelBlockPointerType(*I)) { - // All the args are checked/rewritten. Don't call twice! - RewriteBlockPointerDecl(D); - break; - } - } -} - -void RewriteObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { - const PointerType *PT = funcType->getAs(); - if (PT && PointerTypeTakesAnyBlockArguments(funcType)) - RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); -} - -static bool IsHeaderFile(const std::string &Filename) { - std::string::size_type DotPos = Filename.rfind('.'); - - if (DotPos == std::string::npos) { - // no file extension - return false; - } - - std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); - // C header: .h - // C++ header: .hh or .H; - return Ext == "h" || Ext == "hh" || Ext == "H"; -} - -RewriteObjC::RewriteObjC(std::string inFile, raw_ostream* OS, - DiagnosticsEngine &D, const LangOptions &LOpts, - bool silenceMacroWarn) - : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(OS), - SilenceRewriteMacroWarning(silenceMacroWarn) { - IsHeader = IsHeaderFile(inFile); - RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, - "rewriting sub-expression within a macro (may not be correct)"); - TryFinallyContainsReturnDiag = Diags.getCustomDiagID( - DiagnosticsEngine::Warning, - "rewriter doesn't support user-specified control flow semantics " - "for @try/@finally (code may not execute properly)"); -} - -ASTConsumer *clang::CreateObjCRewriter(const std::string& InFile, - raw_ostream* OS, - DiagnosticsEngine &Diags, - const LangOptions &LOpts, - bool SilenceRewriteMacroWarning) { - return new RewriteObjCFragileABI(InFile, OS, Diags, LOpts, SilenceRewriteMacroWarning); -} - -void RewriteObjC::InitializeCommon(ASTContext &context) { - Context = &context; - SM = &Context->getSourceManager(); - TUDecl = Context->getTranslationUnitDecl(); - MsgSendFunctionDecl = 0; - MsgSendSuperFunctionDecl = 0; - MsgSendStretFunctionDecl = 0; - MsgSendSuperStretFunctionDecl = 0; - MsgSendFpretFunctionDecl = 0; - GetClassFunctionDecl = 0; - GetMetaClassFunctionDecl = 0; - GetSuperClassFunctionDecl = 0; - SelGetUidFunctionDecl = 0; - CFStringFunctionDecl = 0; - ConstantStringClassReference = 0; - NSStringRecord = 0; - CurMethodDef = 0; - CurFunctionDef = 0; - CurFunctionDeclToDeclareForBlock = 0; - GlobalVarDecl = 0; - SuperStructDecl = 0; - ProtocolTypeDecl = 0; - ConstantStringDecl = 0; - BcLabelCount = 0; - SuperContructorFunctionDecl = 0; - NumObjCStringLiterals = 0; - PropParentMap = 0; - CurrentBody = 0; - DisableReplaceStmt = false; - objc_impl_method = false; - - // Get the ID and start/end of the main file. - MainFileID = SM->getMainFileID(); - const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); - MainFileStart = MainBuf->getBufferStart(); - MainFileEnd = MainBuf->getBufferEnd(); - - Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); -} - -//===----------------------------------------------------------------------===// -// Top Level Driver Code -//===----------------------------------------------------------------------===// - -void RewriteObjC::HandleTopLevelSingleDecl(Decl *D) { - if (Diags.hasErrorOccurred()) - return; - - // Two cases: either the decl could be in the main file, or it could be in a - // #included file. If the former, rewrite it now. If the later, check to see - // if we rewrote the #include/#import. - SourceLocation Loc = D->getLocation(); - Loc = SM->getExpansionLoc(Loc); - - // If this is for a builtin, ignore it. - if (Loc.isInvalid()) return; - - // Look for built-in declarations that we need to refer during the rewrite. - if (FunctionDecl *FD = dyn_cast(D)) { - RewriteFunctionDecl(FD); - } else if (VarDecl *FVD = dyn_cast(D)) { - // declared in - if (FVD->getName() == "_NSConstantStringClassReference") { - ConstantStringClassReference = FVD; - return; - } - } else if (ObjCInterfaceDecl *ID = dyn_cast(D)) { - if (ID->isThisDeclarationADefinition()) - RewriteInterfaceDecl(ID); - } else if (ObjCCategoryDecl *CD = dyn_cast(D)) { - RewriteCategoryDecl(CD); - } else if (ObjCProtocolDecl *PD = dyn_cast(D)) { - if (PD->isThisDeclarationADefinition()) - RewriteProtocolDecl(PD); - } else if (LinkageSpecDecl *LSD = dyn_cast(D)) { - // Recurse into linkage specifications - for (DeclContext::decl_iterator DI = LSD->decls_begin(), - DIEnd = LSD->decls_end(); - DI != DIEnd; ) { - if (ObjCInterfaceDecl *IFace = dyn_cast((*DI))) { - if (!IFace->isThisDeclarationADefinition()) { - SmallVector DG; - SourceLocation StartLoc = IFace->getLocStart(); - do { - if (isa(*DI) && - !cast(*DI)->isThisDeclarationADefinition() && - StartLoc == (*DI)->getLocStart()) - DG.push_back(*DI); - else - break; - - ++DI; - } while (DI != DIEnd); - RewriteForwardClassDecl(DG); - continue; - } - } - - if (ObjCProtocolDecl *Proto = dyn_cast((*DI))) { - if (!Proto->isThisDeclarationADefinition()) { - SmallVector DG; - SourceLocation StartLoc = Proto->getLocStart(); - do { - if (isa(*DI) && - !cast(*DI)->isThisDeclarationADefinition() && - StartLoc == (*DI)->getLocStart()) - DG.push_back(*DI); - else - break; - - ++DI; - } while (DI != DIEnd); - RewriteForwardProtocolDecl(DG); - continue; - } - } - - HandleTopLevelSingleDecl(*DI); - ++DI; - } - } - // If we have a decl in the main file, see if we should rewrite it. - if (SM->isFromMainFile(Loc)) - return HandleDeclInMainFile(D); -} - -//===----------------------------------------------------------------------===// -// Syntactic (non-AST) Rewriting Code -//===----------------------------------------------------------------------===// - -void RewriteObjC::RewriteInclude() { - SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); - StringRef MainBuf = SM->getBufferData(MainFileID); - const char *MainBufStart = MainBuf.begin(); - const char *MainBufEnd = MainBuf.end(); - size_t ImportLen = strlen("import"); - - // Loop over the whole file, looking for includes. - for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { - if (*BufPtr == '#') { - if (++BufPtr == MainBufEnd) - return; - while (*BufPtr == ' ' || *BufPtr == '\t') - if (++BufPtr == MainBufEnd) - return; - if (!strncmp(BufPtr, "import", ImportLen)) { - // replace import with include - SourceLocation ImportLoc = - LocStart.getLocWithOffset(BufPtr-MainBufStart); - ReplaceText(ImportLoc, ImportLen, "include"); - BufPtr += ImportLen; - } - } - } -} - -static std::string getIvarAccessString(ObjCIvarDecl *OID) { - const ObjCInterfaceDecl *ClassDecl = OID->getContainingInterface(); - std::string S; - S = "((struct "; - S += ClassDecl->getIdentifier()->getName(); - S += "_IMPL *)self)->"; - S += OID->getName(); - return S; -} - -void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, - ObjCImplementationDecl *IMD, - ObjCCategoryImplDecl *CID) { - static bool objcGetPropertyDefined = false; - static bool objcSetPropertyDefined = false; - SourceLocation startLoc = PID->getLocStart(); - InsertText(startLoc, "// "); - const char *startBuf = SM->getCharacterData(startLoc); - assert((*startBuf == '@') && "bogus @synthesize location"); - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "@synthesize: can't find ';'"); - SourceLocation onePastSemiLoc = - startLoc.getLocWithOffset(semiBuf-startBuf+1); - - if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - return; // FIXME: is this correct? - - // Generate the 'getter' function. - ObjCPropertyDecl *PD = PID->getPropertyDecl(); - ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); - - if (!OID) - return; - unsigned Attributes = PD->getPropertyAttributes(); - if (!PD->getGetterMethodDecl()->isDefined()) { - bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && - (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | - ObjCPropertyDecl::OBJC_PR_copy)); - std::string Getr; - if (GenGetProperty && !objcGetPropertyDefined) { - objcGetPropertyDefined = true; - // FIXME. Is this attribute correct in all cases? - Getr = "\nextern \"C\" __declspec(dllimport) " - "id objc_getProperty(id, SEL, long, bool);\n"; - } - RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getGetterMethodDecl(), Getr); - Getr += "{ "; - // Synthesize an explicit cast to gain access to the ivar. - // See objc-act.c:objc_synthesize_new_getter() for details. - if (GenGetProperty) { - // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) - Getr += "typedef "; - const FunctionType *FPRetType = 0; - RewriteTypeIntoString(PD->getGetterMethodDecl()->getResultType(), Getr, - FPRetType); - Getr += " _TYPE"; - if (FPRetType) { - Getr += ")"; // close the precedence "scope" for "*". - - // Now, emit the argument types (if any). - if (const FunctionProtoType *FT = dyn_cast(FPRetType)){ - Getr += "("; - for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { - if (i) Getr += ", "; - std::string ParamStr = FT->getArgType(i).getAsString( - Context->getPrintingPolicy()); - Getr += ParamStr; - } - if (FT->isVariadic()) { - if (FT->getNumArgs()) Getr += ", "; - Getr += "..."; - } - Getr += ")"; - } else - Getr += "()"; - } - Getr += ";\n"; - Getr += "return (_TYPE)"; - Getr += "objc_getProperty(self, _cmd, "; - RewriteIvarOffsetComputation(OID, Getr); - Getr += ", 1)"; - } - else - Getr += "return " + getIvarAccessString(OID); - Getr += "; }"; - InsertText(onePastSemiLoc, Getr); - } - - if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined()) - return; - - // Generate the 'setter' function. - std::string Setr; - bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | - ObjCPropertyDecl::OBJC_PR_copy); - if (GenSetProperty && !objcSetPropertyDefined) { - objcSetPropertyDefined = true; - // FIXME. Is this attribute correct in all cases? - Setr = "\nextern \"C\" __declspec(dllimport) " - "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; - } - - RewriteObjCMethodDecl(OID->getContainingInterface(), - PD->getSetterMethodDecl(), Setr); - Setr += "{ "; - // Synthesize an explicit cast to initialize the ivar. - // See objc-act.c:objc_synthesize_new_setter() for details. - if (GenSetProperty) { - Setr += "objc_setProperty (self, _cmd, "; - RewriteIvarOffsetComputation(OID, Setr); - Setr += ", (id)"; - Setr += PD->getName(); - Setr += ", "; - if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) - Setr += "0, "; - else - Setr += "1, "; - if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) - Setr += "1)"; - else - Setr += "0)"; - } - else { - Setr += getIvarAccessString(OID) + " = "; - Setr += PD->getName(); - } - Setr += "; }"; - InsertText(onePastSemiLoc, Setr); -} - -static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, - std::string &typedefString) { - typedefString += "#ifndef _REWRITER_typedef_"; - typedefString += ForwardDecl->getNameAsString(); - typedefString += "\n"; - typedefString += "#define _REWRITER_typedef_"; - typedefString += ForwardDecl->getNameAsString(); - typedefString += "\n"; - typedefString += "typedef struct objc_object "; - typedefString += ForwardDecl->getNameAsString(); - typedefString += ";\n#endif\n"; -} - -void RewriteObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, - const std::string &typedefString) { - SourceLocation startLoc = ClassDecl->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - const char *semiPtr = strchr(startBuf, ';'); - // Replace the @class with typedefs corresponding to the classes. - ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); -} - -void RewriteObjC::RewriteForwardClassDecl(DeclGroupRef D) { - std::string typedefString; - for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { - ObjCInterfaceDecl *ForwardDecl = cast(*I); - if (I == D.begin()) { - // Translate to typedef's that forward reference structs with the same name - // as the class. As a convenience, we include the original declaration - // as a comment. - typedefString += "// @class "; - typedefString += ForwardDecl->getNameAsString(); - typedefString += ";\n"; - } - RewriteOneForwardClassDecl(ForwardDecl, typedefString); - } - DeclGroupRef::iterator I = D.begin(); - RewriteForwardClassEpilogue(cast(*I), typedefString); -} - -void RewriteObjC::RewriteForwardClassDecl( - const llvm::SmallVector &D) { - std::string typedefString; - for (unsigned i = 0; i < D.size(); i++) { - ObjCInterfaceDecl *ForwardDecl = cast(D[i]); - if (i == 0) { - typedefString += "// @class "; - typedefString += ForwardDecl->getNameAsString(); - typedefString += ";\n"; - } - RewriteOneForwardClassDecl(ForwardDecl, typedefString); - } - RewriteForwardClassEpilogue(cast(D[0]), typedefString); -} - -void RewriteObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { - // When method is a synthesized one, such as a getter/setter there is - // nothing to rewrite. - if (Method->isImplicit()) - return; - SourceLocation LocStart = Method->getLocStart(); - SourceLocation LocEnd = Method->getLocEnd(); - - if (SM->getExpansionLineNumber(LocEnd) > - SM->getExpansionLineNumber(LocStart)) { - InsertText(LocStart, "#if 0\n"); - ReplaceText(LocEnd, 1, ";\n#endif\n"); - } else { - InsertText(LocStart, "// "); - } -} - -void RewriteObjC::RewriteProperty(ObjCPropertyDecl *prop) { - SourceLocation Loc = prop->getAtLoc(); - - ReplaceText(Loc, 0, "// "); - // FIXME: handle properties that are declared across multiple lines. -} - -void RewriteObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { - SourceLocation LocStart = CatDecl->getLocStart(); - - // FIXME: handle category headers that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); - - for (ObjCCategoryDecl::prop_iterator I = CatDecl->prop_begin(), - E = CatDecl->prop_end(); I != E; ++I) - RewriteProperty(*I); - - for (ObjCCategoryDecl::instmeth_iterator - I = CatDecl->instmeth_begin(), E = CatDecl->instmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - for (ObjCCategoryDecl::classmeth_iterator - I = CatDecl->classmeth_begin(), E = CatDecl->classmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - - // Lastly, comment out the @end. - ReplaceText(CatDecl->getAtEndRange().getBegin(), - strlen("@end"), "/* @end */"); -} - -void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { - SourceLocation LocStart = PDecl->getLocStart(); - assert(PDecl->isThisDeclarationADefinition()); - - // FIXME: handle protocol headers that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); - - for (ObjCProtocolDecl::instmeth_iterator - I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - for (ObjCProtocolDecl::classmeth_iterator - I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - - for (ObjCInterfaceDecl::prop_iterator I = PDecl->prop_begin(), - E = PDecl->prop_end(); I != E; ++I) - RewriteProperty(*I); - - // Lastly, comment out the @end. - SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); - ReplaceText(LocEnd, strlen("@end"), "/* @end */"); - - // Must comment out @optional/@required - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - for (const char *p = startBuf; p < endBuf; p++) { - if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { - SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); - ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); - - } - else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { - SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); - ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); - - } - } -} - -void RewriteObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { - SourceLocation LocStart = (*D.begin())->getLocStart(); - if (LocStart.isInvalid()) - llvm_unreachable("Invalid SourceLocation"); - // FIXME: handle forward protocol that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); -} - -void -RewriteObjC::RewriteForwardProtocolDecl(const llvm::SmallVector &DG) { - SourceLocation LocStart = DG[0]->getLocStart(); - if (LocStart.isInvalid()) - llvm_unreachable("Invalid SourceLocation"); - // FIXME: handle forward protocol that are declared across multiple lines. - ReplaceText(LocStart, 0, "// "); -} - -void RewriteObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, - const FunctionType *&FPRetType) { - if (T->isObjCQualifiedIdType()) - ResultStr += "id"; - else if (T->isFunctionPointerType() || - T->isBlockPointerType()) { - // needs special handling, since pointer-to-functions have special - // syntax (where a decaration models use). - QualType retType = T; - QualType PointeeTy; - if (const PointerType* PT = retType->getAs()) - PointeeTy = PT->getPointeeType(); - else if (const BlockPointerType *BPT = retType->getAs()) - PointeeTy = BPT->getPointeeType(); - if ((FPRetType = PointeeTy->getAs())) { - ResultStr += FPRetType->getResultType().getAsString( - Context->getPrintingPolicy()); - ResultStr += "(*"; - } - } else - ResultStr += T.getAsString(Context->getPrintingPolicy()); -} - -void RewriteObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, - ObjCMethodDecl *OMD, - std::string &ResultStr) { - //fprintf(stderr,"In RewriteObjCMethodDecl\n"); - const FunctionType *FPRetType = 0; - ResultStr += "\nstatic "; - RewriteTypeIntoString(OMD->getResultType(), ResultStr, FPRetType); - ResultStr += " "; - - // Unique method name - std::string NameStr; - - if (OMD->isInstanceMethod()) - NameStr += "_I_"; - else - NameStr += "_C_"; - - NameStr += IDecl->getNameAsString(); - NameStr += "_"; - - if (ObjCCategoryImplDecl *CID = - dyn_cast(OMD->getDeclContext())) { - NameStr += CID->getNameAsString(); - NameStr += "_"; - } - // Append selector names, replacing ':' with '_' - { - std::string selString = OMD->getSelector().getAsString(); - int len = selString.size(); - for (int i = 0; i < len; i++) - if (selString[i] == ':') - selString[i] = '_'; - NameStr += selString; - } - // Remember this name for metadata emission - MethodInternalNames[OMD] = NameStr; - ResultStr += NameStr; - - // Rewrite arguments - ResultStr += "("; - - // invisible arguments - if (OMD->isInstanceMethod()) { - QualType selfTy = Context->getObjCInterfaceType(IDecl); - selfTy = Context->getPointerType(selfTy); - if (!LangOpts.MicrosoftExt) { - if (ObjCSynthesizedStructs.count(const_cast(IDecl))) - ResultStr += "struct "; - } - // When rewriting for Microsoft, explicitly omit the structure name. - ResultStr += IDecl->getNameAsString(); - ResultStr += " *"; - } - else - ResultStr += Context->getObjCClassType().getAsString( - Context->getPrintingPolicy()); - - ResultStr += " self, "; - ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); - ResultStr += " _cmd"; - - // Method arguments. - for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), - E = OMD->param_end(); PI != E; ++PI) { - ParmVarDecl *PDecl = *PI; - ResultStr += ", "; - if (PDecl->getType()->isObjCQualifiedIdType()) { - ResultStr += "id "; - ResultStr += PDecl->getNameAsString(); - } else { - std::string Name = PDecl->getNameAsString(); - QualType QT = PDecl->getType(); - // Make sure we convert "t (^)(...)" to "t (*)(...)". - (void)convertBlockPointerToFunctionPointer(QT); - QT.getAsStringInternal(Name, Context->getPrintingPolicy()); - ResultStr += Name; - } - } - if (OMD->isVariadic()) - ResultStr += ", ..."; - ResultStr += ") "; - - if (FPRetType) { - ResultStr += ")"; // close the precedence "scope" for "*". - - // Now, emit the argument types (if any). - if (const FunctionProtoType *FT = dyn_cast(FPRetType)) { - ResultStr += "("; - for (unsigned i = 0, e = FT->getNumArgs(); i != e; ++i) { - if (i) ResultStr += ", "; - std::string ParamStr = FT->getArgType(i).getAsString( - Context->getPrintingPolicy()); - ResultStr += ParamStr; - } - if (FT->isVariadic()) { - if (FT->getNumArgs()) ResultStr += ", "; - ResultStr += "..."; - } - ResultStr += ")"; - } else { - ResultStr += "()"; - } - } -} -void RewriteObjC::RewriteImplementationDecl(Decl *OID) { - ObjCImplementationDecl *IMD = dyn_cast(OID); - ObjCCategoryImplDecl *CID = dyn_cast(OID); - - InsertText(IMD ? IMD->getLocStart() : CID->getLocStart(), "// "); - - for (ObjCCategoryImplDecl::instmeth_iterator - I = IMD ? IMD->instmeth_begin() : CID->instmeth_begin(), - E = IMD ? IMD->instmeth_end() : CID->instmeth_end(); - I != E; ++I) { - std::string ResultStr; - ObjCMethodDecl *OMD = *I; - RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); - SourceLocation LocStart = OMD->getLocStart(); - SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - ReplaceText(LocStart, endBuf-startBuf, ResultStr); - } - - for (ObjCCategoryImplDecl::classmeth_iterator - I = IMD ? IMD->classmeth_begin() : CID->classmeth_begin(), - E = IMD ? IMD->classmeth_end() : CID->classmeth_end(); - I != E; ++I) { - std::string ResultStr; - ObjCMethodDecl *OMD = *I; - RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); - SourceLocation LocStart = OMD->getLocStart(); - SourceLocation LocEnd = OMD->getCompoundBody()->getLocStart(); - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - ReplaceText(LocStart, endBuf-startBuf, ResultStr); - } - for (ObjCCategoryImplDecl::propimpl_iterator - I = IMD ? IMD->propimpl_begin() : CID->propimpl_begin(), - E = IMD ? IMD->propimpl_end() : CID->propimpl_end(); - I != E; ++I) { - RewritePropertyImplDecl(*I, IMD, CID); - } - - InsertText(IMD ? IMD->getLocEnd() : CID->getLocEnd(), "// "); -} - -void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { - std::string ResultStr; - if (!ObjCForwardDecls.count(ClassDecl->getCanonicalDecl())) { - // we haven't seen a forward decl - generate a typedef. - ResultStr = "#ifndef _REWRITER_typedef_"; - ResultStr += ClassDecl->getNameAsString(); - ResultStr += "\n"; - ResultStr += "#define _REWRITER_typedef_"; - ResultStr += ClassDecl->getNameAsString(); - ResultStr += "\n"; - ResultStr += "typedef struct objc_object "; - ResultStr += ClassDecl->getNameAsString(); - ResultStr += ";\n#endif\n"; - // Mark this typedef as having been generated. - ObjCForwardDecls.insert(ClassDecl->getCanonicalDecl()); - } - RewriteObjCInternalStruct(ClassDecl, ResultStr); - - for (ObjCInterfaceDecl::prop_iterator I = ClassDecl->prop_begin(), - E = ClassDecl->prop_end(); I != E; ++I) - RewriteProperty(*I); - for (ObjCInterfaceDecl::instmeth_iterator - I = ClassDecl->instmeth_begin(), E = ClassDecl->instmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - for (ObjCInterfaceDecl::classmeth_iterator - I = ClassDecl->classmeth_begin(), E = ClassDecl->classmeth_end(); - I != E; ++I) - RewriteMethodDeclaration(*I); - - // Lastly, comment out the @end. - ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), - "/* @end */"); -} - -Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { - SourceRange OldRange = PseudoOp->getSourceRange(); - - // We just magically know some things about the structure of this - // expression. - ObjCMessageExpr *OldMsg = - cast(PseudoOp->getSemanticExpr( - PseudoOp->getNumSemanticExprs() - 1)); - - // Because the rewriter doesn't allow us to rewrite rewritten code, - // we need to suppress rewriting the sub-statements. - Expr *Base, *RHS; - { - DisableReplaceStmtScope S(*this); - - // Rebuild the base expression if we have one. - Base = 0; - if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { - Base = OldMsg->getInstanceReceiver(); - Base = cast(Base)->getSourceExpr(); - Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); - } - - // Rebuild the RHS. - RHS = cast(PseudoOp->getSyntacticForm())->getRHS(); - RHS = cast(RHS)->getSourceExpr(); - RHS = cast(RewriteFunctionBodyOrGlobalInitializer(RHS)); - } - - // TODO: avoid this copy. - SmallVector SelLocs; - OldMsg->getSelectorLocs(SelLocs); - - ObjCMessageExpr *NewMsg = 0; - switch (OldMsg->getReceiverKind()) { - case ObjCMessageExpr::Class: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getClassReceiverTypeInfo(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - RHS, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::Instance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - Base, - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - RHS, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::SuperClass: - case ObjCMessageExpr::SuperInstance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getSuperLoc(), - OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, - OldMsg->getSuperType(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - RHS, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - } - - Stmt *Replacement = SynthMessageExpr(NewMsg); - ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); - return Replacement; -} - -Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { - SourceRange OldRange = PseudoOp->getSourceRange(); - - // We just magically know some things about the structure of this - // expression. - ObjCMessageExpr *OldMsg = - cast(PseudoOp->getResultExpr()->IgnoreImplicit()); - - // Because the rewriter doesn't allow us to rewrite rewritten code, - // we need to suppress rewriting the sub-statements. - Expr *Base = 0; - { - DisableReplaceStmtScope S(*this); - - // Rebuild the base expression if we have one. - if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { - Base = OldMsg->getInstanceReceiver(); - Base = cast(Base)->getSourceExpr(); - Base = cast(RewriteFunctionBodyOrGlobalInitializer(Base)); - } - } - - // Intentionally empty. - SmallVector SelLocs; - SmallVector Args; - - ObjCMessageExpr *NewMsg = 0; - switch (OldMsg->getReceiverKind()) { - case ObjCMessageExpr::Class: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getClassReceiverTypeInfo(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::Instance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - Base, - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - - case ObjCMessageExpr::SuperClass: - case ObjCMessageExpr::SuperInstance: - NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), - OldMsg->getValueKind(), - OldMsg->getLeftLoc(), - OldMsg->getSuperLoc(), - OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, - OldMsg->getSuperType(), - OldMsg->getSelector(), - SelLocs, - OldMsg->getMethodDecl(), - Args, - OldMsg->getRightLoc(), - OldMsg->isImplicit()); - break; - } - - Stmt *Replacement = SynthMessageExpr(NewMsg); - ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); - return Replacement; -} - -/// SynthCountByEnumWithState - To print: -/// ((unsigned int (*) -/// (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) -/// (void *)objc_msgSend)((id)l_collection, -/// sel_registerName( -/// "countByEnumeratingWithState:objects:count:"), -/// &enumState, -/// (id *)__rw_items, (unsigned int)16) -/// -void RewriteObjC::SynthCountByEnumWithState(std::string &buf) { - buf += "((unsigned int (*) (id, SEL, struct __objcFastEnumerationState *, " - "id *, unsigned int))(void *)objc_msgSend)"; - buf += "\n\t\t"; - buf += "((id)l_collection,\n\t\t"; - buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; - buf += "\n\t\t"; - buf += "&enumState, " - "(id *)__rw_items, (unsigned int)16)"; -} - -/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach -/// statement to exit to its outer synthesized loop. -/// -Stmt *RewriteObjC::RewriteBreakStmt(BreakStmt *S) { - if (Stmts.empty() || !isa(Stmts.back())) - return S; - // replace break with goto __break_label - std::string buf; - - SourceLocation startLoc = S->getLocStart(); - buf = "goto __break_label_"; - buf += utostr(ObjCBcLabelNo.back()); - ReplaceText(startLoc, strlen("break"), buf); - - return 0; -} - -/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach -/// statement to continue with its inner synthesized loop. -/// -Stmt *RewriteObjC::RewriteContinueStmt(ContinueStmt *S) { - if (Stmts.empty() || !isa(Stmts.back())) - return S; - // replace continue with goto __continue_label - std::string buf; - - SourceLocation startLoc = S->getLocStart(); - buf = "goto __continue_label_"; - buf += utostr(ObjCBcLabelNo.back()); - ReplaceText(startLoc, strlen("continue"), buf); - - return 0; -} - -/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. -/// It rewrites: -/// for ( type elem in collection) { stmts; } - -/// Into: -/// { -/// type elem; -/// struct __objcFastEnumerationState enumState = { 0 }; -/// id __rw_items[16]; -/// id l_collection = (id)collection; -/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState -/// objects:__rw_items count:16]; -/// if (limit) { -/// unsigned long startMutations = *enumState.mutationsPtr; -/// do { -/// unsigned long counter = 0; -/// do { -/// if (startMutations != *enumState.mutationsPtr) -/// objc_enumerationMutation(l_collection); -/// elem = (type)enumState.itemsPtr[counter++]; -/// stmts; -/// __continue_label: ; -/// } while (counter < limit); -/// } while (limit = [l_collection countByEnumeratingWithState:&enumState -/// objects:__rw_items count:16]); -/// elem = nil; -/// __break_label: ; -/// } -/// else -/// elem = nil; -/// } -/// -Stmt *RewriteObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, - SourceLocation OrigEnd) { - assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); - assert(isa(Stmts.back()) && - "ObjCForCollectionStmt Statement stack mismatch"); - assert(!ObjCBcLabelNo.empty() && - "ObjCForCollectionStmt - Label No stack empty"); - - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - StringRef elementName; - std::string elementTypeAsString; - std::string buf; - buf = "\n{\n\t"; - if (DeclStmt *DS = dyn_cast(S->getElement())) { - // type elem; - NamedDecl* D = cast(DS->getSingleDecl()); - QualType ElementType = cast(D)->getType(); - if (ElementType->isObjCQualifiedIdType() || - ElementType->isObjCQualifiedInterfaceType()) - // Simply use 'id' for all qualified types. - elementTypeAsString = "id"; - else - elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); - buf += elementTypeAsString; - buf += " "; - elementName = D->getName(); - buf += elementName; - buf += ";\n\t"; - } - else { - DeclRefExpr *DR = cast(S->getElement()); - elementName = DR->getDecl()->getName(); - ValueDecl *VD = cast(DR->getDecl()); - if (VD->getType()->isObjCQualifiedIdType() || - VD->getType()->isObjCQualifiedInterfaceType()) - // Simply use 'id' for all qualified types. - elementTypeAsString = "id"; - else - elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); - } - - // struct __objcFastEnumerationState enumState = { 0 }; - buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; - // id __rw_items[16]; - buf += "id __rw_items[16];\n\t"; - // id l_collection = (id) - buf += "id l_collection = (id)"; - // Find start location of 'collection' the hard way! - const char *startCollectionBuf = startBuf; - startCollectionBuf += 3; // skip 'for' - startCollectionBuf = strchr(startCollectionBuf, '('); - startCollectionBuf++; // skip '(' - // find 'in' and skip it. - while (*startCollectionBuf != ' ' || - *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || - (*(startCollectionBuf+3) != ' ' && - *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) - startCollectionBuf++; - startCollectionBuf += 3; - - // Replace: "for (type element in" with string constructed thus far. - ReplaceText(startLoc, startCollectionBuf - startBuf, buf); - // Replace ')' in for '(' type elem in collection ')' with ';' - SourceLocation rightParenLoc = S->getRParenLoc(); - const char *rparenBuf = SM->getCharacterData(rightParenLoc); - SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); - buf = ";\n\t"; - - // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState - // objects:__rw_items count:16]; - // which is synthesized into: - // unsigned int limit = - // ((unsigned int (*) - // (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) - // (void *)objc_msgSend)((id)l_collection, - // sel_registerName( - // "countByEnumeratingWithState:objects:count:"), - // (struct __objcFastEnumerationState *)&state, - // (id *)__rw_items, (unsigned int)16); - buf += "unsigned long limit =\n\t\t"; - SynthCountByEnumWithState(buf); - buf += ";\n\t"; - /// if (limit) { - /// unsigned long startMutations = *enumState.mutationsPtr; - /// do { - /// unsigned long counter = 0; - /// do { - /// if (startMutations != *enumState.mutationsPtr) - /// objc_enumerationMutation(l_collection); - /// elem = (type)enumState.itemsPtr[counter++]; - buf += "if (limit) {\n\t"; - buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; - buf += "do {\n\t\t"; - buf += "unsigned long counter = 0;\n\t\t"; - buf += "do {\n\t\t\t"; - buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; - buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; - buf += elementName; - buf += " = ("; - buf += elementTypeAsString; - buf += ")enumState.itemsPtr[counter++];"; - // Replace ')' in for '(' type elem in collection ')' with all of these. - ReplaceText(lparenLoc, 1, buf); - - /// __continue_label: ; - /// } while (counter < limit); - /// } while (limit = [l_collection countByEnumeratingWithState:&enumState - /// objects:__rw_items count:16]); - /// elem = nil; - /// __break_label: ; - /// } - /// else - /// elem = nil; - /// } - /// - buf = ";\n\t"; - buf += "__continue_label_"; - buf += utostr(ObjCBcLabelNo.back()); - buf += ": ;"; - buf += "\n\t\t"; - buf += "} while (counter < limit);\n\t"; - buf += "} while (limit = "; - SynthCountByEnumWithState(buf); - buf += ");\n\t"; - buf += elementName; - buf += " = (("; - buf += elementTypeAsString; - buf += ")0);\n\t"; - buf += "__break_label_"; - buf += utostr(ObjCBcLabelNo.back()); - buf += ": ;\n\t"; - buf += "}\n\t"; - buf += "else\n\t\t"; - buf += elementName; - buf += " = (("; - buf += elementTypeAsString; - buf += ")0);\n\t"; - buf += "}\n"; - - // Insert all these *after* the statement body. - // FIXME: If this should support Obj-C++, support CXXTryStmt - if (isa(S->getBody())) { - SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); - InsertText(endBodyLoc, buf); - } else { - /* Need to treat single statements specially. For example: - * - * for (A *a in b) if (stuff()) break; - * for (A *a in b) xxxyy; - * - * The following code simply scans ahead to the semi to find the actual end. - */ - const char *stmtBuf = SM->getCharacterData(OrigEnd); - const char *semiBuf = strchr(stmtBuf, ';'); - assert(semiBuf && "Can't find ';'"); - SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(semiBuf-stmtBuf+1); - InsertText(endBodyLoc, buf); - } - Stmts.pop_back(); - ObjCBcLabelNo.pop_back(); - return 0; -} - -/// RewriteObjCSynchronizedStmt - -/// This routine rewrites @synchronized(expr) stmt; -/// into: -/// objc_sync_enter(expr); -/// @try stmt @finally { objc_sync_exit(expr); } -/// -Stmt *RewriteObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { - // Get the start location and compute the semi location. - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @synchronized location"); - - std::string buf; - buf = "objc_sync_enter((id)"; - const char *lparenBuf = startBuf; - while (*lparenBuf != '(') lparenBuf++; - ReplaceText(startLoc, lparenBuf-startBuf+1, buf); - // We can't use S->getSynchExpr()->getLocEnd() to find the end location, since - // the sync expression is typically a message expression that's already - // been rewritten! (which implies the SourceLocation's are invalid). - SourceLocation endLoc = S->getSynchBody()->getLocStart(); - const char *endBuf = SM->getCharacterData(endLoc); - while (*endBuf != ')') endBuf--; - SourceLocation rparenLoc = startLoc.getLocWithOffset(endBuf-startBuf); - buf = ");\n"; - // declare a new scope with two variables, _stack and _rethrow. - buf += "/* @try scope begin */ \n{ struct _objc_exception_data {\n"; - buf += "int buf[18/*32-bit i386*/];\n"; - buf += "char *pointers[4];} _stack;\n"; - buf += "id volatile _rethrow = 0;\n"; - buf += "objc_exception_try_enter(&_stack);\n"; - buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; - ReplaceText(rparenLoc, 1, buf); - startLoc = S->getSynchBody()->getLocEnd(); - startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '}') && "bogus @synchronized block"); - SourceLocation lastCurlyLoc = startLoc; - buf = "}\nelse {\n"; - buf += " _rethrow = objc_exception_extract(&_stack);\n"; - buf += "}\n"; - buf += "{ /* implicit finally clause */\n"; - buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; - - std::string syncBuf; - syncBuf += " objc_sync_exit("; - - Expr *syncExpr = S->getSynchExpr(); - CastKind CK = syncExpr->getType()->isObjCObjectPointerType() - ? CK_BitCast : - syncExpr->getType()->isBlockPointerType() - ? CK_BlockPointerToObjCPointerCast - : CK_CPointerToObjCPointerCast; - syncExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK, syncExpr); - std::string syncExprBufS; - llvm::raw_string_ostream syncExprBuf(syncExprBufS); - syncExpr->printPretty(syncExprBuf, 0, PrintingPolicy(LangOpts)); - syncBuf += syncExprBuf.str(); - syncBuf += ");"; - - buf += syncBuf; - buf += "\n if (_rethrow) objc_exception_throw(_rethrow);\n"; - buf += "}\n"; - buf += "}"; - - ReplaceText(lastCurlyLoc, 1, buf); - - bool hasReturns = false; - HasReturnStmts(S->getSynchBody(), hasReturns); - if (hasReturns) - RewriteSyncReturnStmts(S->getSynchBody(), syncBuf); - - return 0; -} - -void RewriteObjC::WarnAboutReturnGotoStmts(Stmt *S) -{ - // Perform a bottom up traversal of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) - WarnAboutReturnGotoStmts(*CI); - - if (isa(S) || isa(S)) { - Diags.Report(Context->getFullLoc(S->getLocStart()), - TryFinallyContainsReturnDiag); - } - return; -} - -void RewriteObjC::HasReturnStmts(Stmt *S, bool &hasReturns) -{ - // Perform a bottom up traversal of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) - HasReturnStmts(*CI, hasReturns); - - if (isa(S)) - hasReturns = true; - return; -} - -void RewriteObjC::RewriteTryReturnStmts(Stmt *S) { - // Perform a bottom up traversal of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - RewriteTryReturnStmts(*CI); - } - if (isa(S)) { - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "RewriteTryReturnStmts: can't find ';'"); - SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); - - std::string buf; - buf = "{ objc_exception_try_exit(&_stack); return"; - - ReplaceText(startLoc, 6, buf); - InsertText(onePastSemiLoc, "}"); - } - return; -} - -void RewriteObjC::RewriteSyncReturnStmts(Stmt *S, std::string syncExitBuf) { - // Perform a bottom up traversal of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - RewriteSyncReturnStmts(*CI, syncExitBuf); - } - if (isa(S)) { - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "RewriteSyncReturnStmts: can't find ';'"); - SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); - - std::string buf; - buf = "{ objc_exception_try_exit(&_stack);"; - buf += syncExitBuf; - buf += " return"; - - ReplaceText(startLoc, 6, buf); - InsertText(onePastSemiLoc, "}"); - } - return; -} - -Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { - // Get the start location and compute the semi location. - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @try location"); - - std::string buf; - // declare a new scope with two variables, _stack and _rethrow. - buf = "/* @try scope begin */ { struct _objc_exception_data {\n"; - buf += "int buf[18/*32-bit i386*/];\n"; - buf += "char *pointers[4];} _stack;\n"; - buf += "id volatile _rethrow = 0;\n"; - buf += "objc_exception_try_enter(&_stack);\n"; - buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; - - ReplaceText(startLoc, 4, buf); - - startLoc = S->getTryBody()->getLocEnd(); - startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '}') && "bogus @try block"); - - SourceLocation lastCurlyLoc = startLoc; - if (S->getNumCatchStmts()) { - startLoc = startLoc.getLocWithOffset(1); - buf = " /* @catch begin */ else {\n"; - buf += " id _caught = objc_exception_extract(&_stack);\n"; - buf += " objc_exception_try_enter (&_stack);\n"; - buf += " if (_setjmp(_stack.buf))\n"; - buf += " _rethrow = objc_exception_extract(&_stack);\n"; - buf += " else { /* @catch continue */"; - - InsertText(startLoc, buf); - } else { /* no catch list */ - buf = "}\nelse {\n"; - buf += " _rethrow = objc_exception_extract(&_stack);\n"; - buf += "}"; - ReplaceText(lastCurlyLoc, 1, buf); - } - Stmt *lastCatchBody = 0; - for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { - ObjCAtCatchStmt *Catch = S->getCatchStmt(I); - VarDecl *catchDecl = Catch->getCatchParamDecl(); - - if (I == 0) - buf = "if ("; // we are generating code for the first catch clause - else - buf = "else if ("; - startLoc = Catch->getLocStart(); - startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @catch location"); - - const char *lParenLoc = strchr(startBuf, '('); - - if (Catch->hasEllipsis()) { - // Now rewrite the body... - lastCatchBody = Catch->getCatchBody(); - SourceLocation bodyLoc = lastCatchBody->getLocStart(); - const char *bodyBuf = SM->getCharacterData(bodyLoc); - assert(*SM->getCharacterData(Catch->getRParenLoc()) == ')' && - "bogus @catch paren location"); - assert((*bodyBuf == '{') && "bogus @catch body location"); - - buf += "1) { id _tmp = _caught;"; - Rewrite.ReplaceText(startLoc, bodyBuf-startBuf+1, buf); - } else if (catchDecl) { - QualType t = catchDecl->getType(); - if (t == Context->getObjCIdType()) { - buf += "1) { "; - ReplaceText(startLoc, lParenLoc-startBuf+1, buf); - } else if (const ObjCObjectPointerType *Ptr = - t->getAs()) { - // Should be a pointer to a class. - ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); - if (IDecl) { - buf += "objc_exception_match((struct objc_class *)objc_getClass(\""; - buf += IDecl->getNameAsString(); - buf += "\"), (struct objc_object *)_caught)) { "; - ReplaceText(startLoc, lParenLoc-startBuf+1, buf); - } - } - // Now rewrite the body... - lastCatchBody = Catch->getCatchBody(); - SourceLocation rParenLoc = Catch->getRParenLoc(); - SourceLocation bodyLoc = lastCatchBody->getLocStart(); - const char *bodyBuf = SM->getCharacterData(bodyLoc); - const char *rParenBuf = SM->getCharacterData(rParenLoc); - assert((*rParenBuf == ')') && "bogus @catch paren location"); - assert((*bodyBuf == '{') && "bogus @catch body location"); - - // Here we replace ") {" with "= _caught;" (which initializes and - // declares the @catch parameter). - ReplaceText(rParenLoc, bodyBuf-rParenBuf+1, " = _caught;"); - } else { - llvm_unreachable("@catch rewrite bug"); - } - } - // Complete the catch list... - if (lastCatchBody) { - SourceLocation bodyLoc = lastCatchBody->getLocEnd(); - assert(*SM->getCharacterData(bodyLoc) == '}' && - "bogus @catch body location"); - - // Insert the last (implicit) else clause *before* the right curly brace. - bodyLoc = bodyLoc.getLocWithOffset(-1); - buf = "} /* last catch end */\n"; - buf += "else {\n"; - buf += " _rethrow = _caught;\n"; - buf += " objc_exception_try_exit(&_stack);\n"; - buf += "} } /* @catch end */\n"; - if (!S->getFinallyStmt()) - buf += "}\n"; - InsertText(bodyLoc, buf); - - // Set lastCurlyLoc - lastCurlyLoc = lastCatchBody->getLocEnd(); - } - if (ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt()) { - startLoc = finalStmt->getLocStart(); - startBuf = SM->getCharacterData(startLoc); - assert((*startBuf == '@') && "bogus @finally start"); - - ReplaceText(startLoc, 8, "/* @finally */"); - - Stmt *body = finalStmt->getFinallyBody(); - SourceLocation startLoc = body->getLocStart(); - SourceLocation endLoc = body->getLocEnd(); - assert(*SM->getCharacterData(startLoc) == '{' && - "bogus @finally body location"); - assert(*SM->getCharacterData(endLoc) == '}' && - "bogus @finally body location"); - - startLoc = startLoc.getLocWithOffset(1); - InsertText(startLoc, " if (!_rethrow) objc_exception_try_exit(&_stack);\n"); - endLoc = endLoc.getLocWithOffset(-1); - InsertText(endLoc, " if (_rethrow) objc_exception_throw(_rethrow);\n"); - - // Set lastCurlyLoc - lastCurlyLoc = body->getLocEnd(); - - // Now check for any return/continue/go statements within the @try. - WarnAboutReturnGotoStmts(S->getTryBody()); - } else { /* no finally clause - make sure we synthesize an implicit one */ - buf = "{ /* implicit finally clause */\n"; - buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; - buf += " if (_rethrow) objc_exception_throw(_rethrow);\n"; - buf += "}"; - ReplaceText(lastCurlyLoc, 1, buf); - - // Now check for any return/continue/go statements within the @try. - // The implicit finally clause won't called if the @try contains any - // jump statements. - bool hasReturns = false; - HasReturnStmts(S->getTryBody(), hasReturns); - if (hasReturns) - RewriteTryReturnStmts(S->getTryBody()); - } - // Now emit the final closing curly brace... - lastCurlyLoc = lastCurlyLoc.getLocWithOffset(1); - InsertText(lastCurlyLoc, " } /* @try scope end */\n"); - return 0; -} - -// This can't be done with ReplaceStmt(S, ThrowExpr), since -// the throw expression is typically a message expression that's already -// been rewritten! (which implies the SourceLocation's are invalid). -Stmt *RewriteObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { - // Get the start location and compute the semi location. - SourceLocation startLoc = S->getLocStart(); - const char *startBuf = SM->getCharacterData(startLoc); - - assert((*startBuf == '@') && "bogus @throw location"); - - std::string buf; - /* void objc_exception_throw(id) __attribute__((noreturn)); */ - if (S->getThrowExpr()) - buf = "objc_exception_throw("; - else // add an implicit argument - buf = "objc_exception_throw(_caught"; - - // handle "@ throw" correctly. - const char *wBuf = strchr(startBuf, 'w'); - assert((*wBuf == 'w') && "@throw: can't find 'w'"); - ReplaceText(startLoc, wBuf-startBuf+1, buf); - - const char *semiBuf = strchr(startBuf, ';'); - assert((*semiBuf == ';') && "@throw: can't find ';'"); - SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); - ReplaceText(semiLoc, 1, ");"); - return 0; -} - -Stmt *RewriteObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { - // Create a new string expression. - QualType StrType = Context->getPointerType(Context->CharTy); - std::string StrEncoding; - Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); - Expr *Replacement = StringLiteral::Create(*Context, StrEncoding, - StringLiteral::Ascii, false, - StrType, SourceLocation()); - ReplaceStmt(Exp, Replacement); - - // Replace this subexpr in the parent. - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return Replacement; -} - -Stmt *RewriteObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); - // Create a call to sel_registerName("selName"). - SmallVector SelExprs; - QualType argType = Context->getPointerType(Context->CharTy); - SelExprs.push_back(StringLiteral::Create(*Context, - Exp->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size()); - ReplaceStmt(Exp, SelExp); - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return SelExp; -} - -CallExpr *RewriteObjC::SynthesizeCallToFunctionDecl( - FunctionDecl *FD, Expr **args, unsigned nargs, SourceLocation StartLoc, - SourceLocation EndLoc) { - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = FD->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, msgSendType, - VK_LValue, SourceLocation()); - - // Now, we cast the reference to a pointer to the objc_msgSend type. - QualType pToFunc = Context->getPointerType(msgSendType); - ImplicitCastExpr *ICE = - ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, - DRE, 0, VK_RValue); - - const FunctionType *FT = msgSendType->getAs(); - - CallExpr *Exp = - new (Context) CallExpr(*Context, ICE, args, nargs, - FT->getCallResultType(*Context), - VK_RValue, EndLoc); - return Exp; -} - -static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, - const char *&startRef, const char *&endRef) { - while (startBuf < endBuf) { - if (*startBuf == '<') - startRef = startBuf; // mark the start. - if (*startBuf == '>') { - if (startRef && *startRef == '<') { - endRef = startBuf; // mark the end. - return true; - } - return false; - } - startBuf++; - } - return false; -} - -static void scanToNextArgument(const char *&argRef) { - int angle = 0; - while (*argRef != ')' && (*argRef != ',' || angle > 0)) { - if (*argRef == '<') - angle++; - else if (*argRef == '>') - angle--; - argRef++; - } - assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); -} - -bool RewriteObjC::needToScanForQualifiers(QualType T) { - if (T->isObjCQualifiedIdType()) - return true; - if (const PointerType *PT = T->getAs()) { - if (PT->getPointeeType()->isObjCQualifiedIdType()) - return true; - } - if (T->isObjCObjectPointerType()) { - T = T->getPointeeType(); - return T->isObjCQualifiedInterfaceType(); - } - if (T->isArrayType()) { - QualType ElemTy = Context->getBaseElementType(T); - return needToScanForQualifiers(ElemTy); - } - return false; -} - -void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { - QualType Type = E->getType(); - if (needToScanForQualifiers(Type)) { - SourceLocation Loc, EndLoc; - - if (const CStyleCastExpr *ECE = dyn_cast(E)) { - Loc = ECE->getLParenLoc(); - EndLoc = ECE->getRParenLoc(); - } else { - Loc = E->getLocStart(); - EndLoc = E->getLocEnd(); - } - // This will defend against trying to rewrite synthesized expressions. - if (Loc.isInvalid() || EndLoc.isInvalid()) - return; - - const char *startBuf = SM->getCharacterData(Loc); - const char *endBuf = SM->getCharacterData(EndLoc); - const char *startRef = 0, *endRef = 0; - if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { - // Get the locations of the startRef, endRef. - SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); - SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); - // Comment out the protocol references. - InsertText(LessLoc, "/*"); - InsertText(GreaterLoc, "*/"); - } - } -} - -void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { - SourceLocation Loc; - QualType Type; - const FunctionProtoType *proto = 0; - if (VarDecl *VD = dyn_cast(Dcl)) { - Loc = VD->getLocation(); - Type = VD->getType(); - } - else if (FunctionDecl *FD = dyn_cast(Dcl)) { - Loc = FD->getLocation(); - // Check for ObjC 'id' and class types that have been adorned with protocol - // information (id

, C

*). The protocol references need to be rewritten! - const FunctionType *funcType = FD->getType()->getAs(); - assert(funcType && "missing function type"); - proto = dyn_cast(funcType); - if (!proto) - return; - Type = proto->getResultType(); - } - else if (FieldDecl *FD = dyn_cast(Dcl)) { - Loc = FD->getLocation(); - Type = FD->getType(); - } - else - return; - - if (needToScanForQualifiers(Type)) { - // Since types are unique, we need to scan the buffer. - - const char *endBuf = SM->getCharacterData(Loc); - const char *startBuf = endBuf; - while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) - startBuf--; // scan backward (from the decl location) for return type. - const char *startRef = 0, *endRef = 0; - if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { - // Get the locations of the startRef, endRef. - SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); - SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); - // Comment out the protocol references. - InsertText(LessLoc, "/*"); - InsertText(GreaterLoc, "*/"); - } - } - if (!proto) - return; // most likely, was a variable - // Now check arguments. - const char *startBuf = SM->getCharacterData(Loc); - const char *startFuncBuf = startBuf; - for (unsigned i = 0; i < proto->getNumArgs(); i++) { - if (needToScanForQualifiers(proto->getArgType(i))) { - // Since types are unique, we need to scan the buffer. - - const char *endBuf = startBuf; - // scan forward (from the decl location) for argument types. - scanToNextArgument(endBuf); - const char *startRef = 0, *endRef = 0; - if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { - // Get the locations of the startRef, endRef. - SourceLocation LessLoc = - Loc.getLocWithOffset(startRef-startFuncBuf); - SourceLocation GreaterLoc = - Loc.getLocWithOffset(endRef-startFuncBuf+1); - // Comment out the protocol references. - InsertText(LessLoc, "/*"); - InsertText(GreaterLoc, "*/"); - } - startBuf = ++endBuf; - } - else { - // If the function name is derived from a macro expansion, then the - // argument buffer will not follow the name. Need to speak with Chris. - while (*startBuf && *startBuf != ')' && *startBuf != ',') - startBuf++; // scan forward (from the decl location) for argument types. - startBuf++; - } - } -} - -void RewriteObjC::RewriteTypeOfDecl(VarDecl *ND) { - QualType QT = ND->getType(); - const Type* TypePtr = QT->getAs(); - if (!isa(TypePtr)) - return; - while (isa(TypePtr)) { - const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); - QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); - TypePtr = QT->getAs(); - } - // FIXME. This will not work for multiple declarators; as in: - // __typeof__(a) b,c,d; - std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); - SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); - const char *startBuf = SM->getCharacterData(DeclLoc); - if (ND->getInit()) { - std::string Name(ND->getNameAsString()); - TypeAsString += " " + Name + " = "; - Expr *E = ND->getInit(); - SourceLocation startLoc; - if (const CStyleCastExpr *ECE = dyn_cast(E)) - startLoc = ECE->getLParenLoc(); - else - startLoc = E->getLocStart(); - startLoc = SM->getExpansionLoc(startLoc); - const char *endBuf = SM->getCharacterData(startLoc); - ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); - } - else { - SourceLocation X = ND->getLocEnd(); - X = SM->getExpansionLoc(X); - const char *endBuf = SM->getCharacterData(X); - ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); - } -} - -// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); -void RewriteObjC::SynthSelGetUidFunctionDecl() { - IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); - SmallVector ArgTys; - ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getFuncType = - getSimpleFunctionType(Context->getObjCSelType(), &ArgTys[0], ArgTys.size()); - SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - SelGetUidIdent, getFuncType, 0, - SC_Extern, - SC_None, false); -} - -void RewriteObjC::RewriteFunctionDecl(FunctionDecl *FD) { - // declared in - if (FD->getIdentifier() && - FD->getName() == "sel_registerName") { - SelGetUidFunctionDecl = FD; - return; - } - RewriteObjCQualifiedInterfaceTypes(FD); -} - -void RewriteObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { - std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); - const char *argPtr = TypeString.c_str(); - if (!strchr(argPtr, '^')) { - Str += TypeString; - return; - } - while (*argPtr) { - Str += (*argPtr == '^' ? '*' : *argPtr); - argPtr++; - } -} - -// FIXME. Consolidate this routine with RewriteBlockPointerType. -void RewriteObjC::RewriteBlockPointerTypeVariable(std::string& Str, - ValueDecl *VD) { - QualType Type = VD->getType(); - std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); - const char *argPtr = TypeString.c_str(); - int paren = 0; - while (*argPtr) { - switch (*argPtr) { - case '(': - Str += *argPtr; - paren++; - break; - case ')': - Str += *argPtr; - paren--; - break; - case '^': - Str += '*'; - if (paren == 1) - Str += VD->getNameAsString(); - break; - default: - Str += *argPtr; - break; - } - argPtr++; - } -} - - -void RewriteObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { - SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); - const FunctionType *funcType = FD->getType()->getAs(); - const FunctionProtoType *proto = dyn_cast(funcType); - if (!proto) - return; - QualType Type = proto->getResultType(); - std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); - FdStr += " "; - FdStr += FD->getName(); - FdStr += "("; - unsigned numArgs = proto->getNumArgs(); - for (unsigned i = 0; i < numArgs; i++) { - QualType ArgType = proto->getArgType(i); - RewriteBlockPointerType(FdStr, ArgType); - if (i+1 < numArgs) - FdStr += ", "; - } - FdStr += ");\n"; - InsertText(FunLocStart, FdStr); - CurFunctionDeclToDeclareForBlock = 0; -} - -// SynthSuperContructorFunctionDecl - id objc_super(id obj, id super); -void RewriteObjC::SynthSuperContructorFunctionDecl() { - if (SuperContructorFunctionDecl) - return; - IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size()); - SuperContructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); -void RewriteObjC::SynthMsgSendFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(struct objc_super *, SEL op, ...); -void RewriteObjC::SynthMsgSendSuperFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); - SmallVector ArgTys; - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("objc_super")); - QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); - assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); -void RewriteObjC::SynthMsgSendStretFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendSuperStretFunctionDecl - -// id objc_msgSendSuper_stret(struct objc_super *, SEL op, ...); -void RewriteObjC::SynthMsgSendSuperStretFunctionDecl() { - IdentifierInfo *msgSendIdent = - &Context->Idents.get("objc_msgSendSuper_stret"); - SmallVector ArgTys; - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("objc_super")); - QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); - assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); -void RewriteObjC::SynthMsgSendFpretFunctionDecl() { - IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); - SmallVector ArgTys; - QualType argT = Context->getObjCIdType(); - assert(!argT.isNull() && "Can't find 'id' type"); - ArgTys.push_back(argT); - argT = Context->getObjCSelType(); - assert(!argT.isNull() && "Can't find 'SEL' type"); - ArgTys.push_back(argT); - QualType msgSendType = getSimpleFunctionType(Context->DoubleTy, - &ArgTys[0], ArgTys.size(), - true /*isVariadic*/); - MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - msgSendIdent, msgSendType, 0, - SC_Extern, - SC_None, false); -} - -// SynthGetClassFunctionDecl - id objc_getClass(const char *name); -void RewriteObjC::SynthGetClassFunctionDecl() { - IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); - SmallVector ArgTys; - ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size()); - GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - getClassIdent, getClassType, 0, - SC_Extern, - SC_None, false); -} - -// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); -void RewriteObjC::SynthGetSuperClassFunctionDecl() { - IdentifierInfo *getSuperClassIdent = - &Context->Idents.get("class_getSuperclass"); - SmallVector ArgTys; - ArgTys.push_back(Context->getObjCClassType()); - QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), - &ArgTys[0], ArgTys.size()); - GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - getSuperClassIdent, - getClassType, 0, - SC_Extern, - SC_None, - false); -} - -// SynthGetMetaClassFunctionDecl - id objc_getMetaClass(const char *name); -void RewriteObjC::SynthGetMetaClassFunctionDecl() { - IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); - SmallVector ArgTys; - ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); - QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), - &ArgTys[0], ArgTys.size()); - GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, - SourceLocation(), - SourceLocation(), - getClassIdent, getClassType, 0, - SC_Extern, - SC_None, false); -} - -Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { - QualType strType = getConstantStringStructType(); - - std::string S = "__NSConstantStringImpl_"; - - std::string tmpName = InFileName; - unsigned i; - for (i=0; i < tmpName.length(); i++) { - char c = tmpName.at(i); - // replace any non alphanumeric characters with '_'. - if (!isalpha(c) && (c < '0' || c > '9')) - tmpName[i] = '_'; - } - S += tmpName; - S += "_"; - S += utostr(NumObjCStringLiterals++); - - Preamble += "static __NSConstantStringImpl " + S; - Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; - Preamble += "0x000007c8,"; // utf8_str - // The pretty printer for StringLiteral handles escape characters properly. - std::string prettyBufS; - llvm::raw_string_ostream prettyBuf(prettyBufS); - Exp->getString()->printPretty(prettyBuf, 0, PrintingPolicy(LangOpts)); - Preamble += prettyBuf.str(); - Preamble += ","; - Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; - - VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), &Context->Idents.get(S), - strType, 0, SC_Static, SC_None); - DeclRefExpr *DRE = new (Context) DeclRefExpr(NewVD, false, strType, VK_LValue, - SourceLocation()); - Expr *Unop = new (Context) UnaryOperator(DRE, UO_AddrOf, - Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - // cast to NSConstantString * - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), - CK_CPointerToObjCPointerCast, Unop); - ReplaceStmt(Exp, cast); - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return cast; -} - -// struct objc_super { struct objc_object *receiver; struct objc_class *super; }; -QualType RewriteObjC::getSuperStructType() { - if (!SuperStructDecl) { - SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("objc_super")); - QualType FieldTypes[2]; - - // struct objc_object *receiver; - FieldTypes[0] = Context->getObjCIdType(); - // struct objc_class *super; - FieldTypes[1] = Context->getObjCClassType(); - - // Create fields - for (unsigned i = 0; i < 2; ++i) { - SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, - SourceLocation(), - SourceLocation(), 0, - FieldTypes[i], 0, - /*BitWidth=*/0, - /*Mutable=*/false, - ICIS_NoInit)); - } - - SuperStructDecl->completeDefinition(); - } - return Context->getTagDeclType(SuperStructDecl); -} - -QualType RewriteObjC::getConstantStringStructType() { - if (!ConstantStringDecl) { - ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("__NSConstantStringImpl")); - QualType FieldTypes[4]; - - // struct objc_object *receiver; - FieldTypes[0] = Context->getObjCIdType(); - // int flags; - FieldTypes[1] = Context->IntTy; - // char *str; - FieldTypes[2] = Context->getPointerType(Context->CharTy); - // long length; - FieldTypes[3] = Context->LongTy; - - // Create fields - for (unsigned i = 0; i < 4; ++i) { - ConstantStringDecl->addDecl(FieldDecl::Create(*Context, - ConstantStringDecl, - SourceLocation(), - SourceLocation(), 0, - FieldTypes[i], 0, - /*BitWidth=*/0, - /*Mutable=*/true, - ICIS_NoInit)); - } - - ConstantStringDecl->completeDefinition(); - } - return Context->getTagDeclType(ConstantStringDecl); -} - -CallExpr *RewriteObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, - QualType msgSendType, - QualType returnType, - SmallVectorImpl &ArgTypes, - SmallVectorImpl &MsgExprs, - ObjCMethodDecl *Method) { - // Create a reference to the objc_msgSend_stret() declaration. - DeclRefExpr *STDRE = new (Context) DeclRefExpr(MsgSendStretFlavor, - false, msgSendType, - VK_LValue, SourceLocation()); - // Need to cast objc_msgSend_stret to "void *" (see above comment). - CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, STDRE); - // Now do the "normal" pointer to function cast. - QualType castType = getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - Method ? Method->isVariadic() : false); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); - - const FunctionType *FT = msgSendType->getAs(); - CallExpr *STCE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - SourceLocation()); - return STCE; - -} - - -Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, - SourceLocation StartLoc, - SourceLocation EndLoc) { - if (!SelGetUidFunctionDecl) - SynthSelGetUidFunctionDecl(); - if (!MsgSendFunctionDecl) - SynthMsgSendFunctionDecl(); - if (!MsgSendSuperFunctionDecl) - SynthMsgSendSuperFunctionDecl(); - if (!MsgSendStretFunctionDecl) - SynthMsgSendStretFunctionDecl(); - if (!MsgSendSuperStretFunctionDecl) - SynthMsgSendSuperStretFunctionDecl(); - if (!MsgSendFpretFunctionDecl) - SynthMsgSendFpretFunctionDecl(); - if (!GetClassFunctionDecl) - SynthGetClassFunctionDecl(); - if (!GetSuperClassFunctionDecl) - SynthGetSuperClassFunctionDecl(); - if (!GetMetaClassFunctionDecl) - SynthGetMetaClassFunctionDecl(); - - // default to objc_msgSend(). - FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; - // May need to use objc_msgSend_stret() as well. - FunctionDecl *MsgSendStretFlavor = 0; - if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { - QualType resultType = mDecl->getResultType(); - if (resultType->isRecordType()) - MsgSendStretFlavor = MsgSendStretFunctionDecl; - else if (resultType->isRealFloatingType()) - MsgSendFlavor = MsgSendFpretFunctionDecl; - } - - // Synthesize a call to objc_msgSend(). - SmallVector MsgExprs; - switch (Exp->getReceiverKind()) { - case ObjCMessageExpr::SuperClass: { - MsgSendFlavor = MsgSendSuperFunctionDecl; - if (MsgSendStretFlavor) - MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; - assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); - - ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); - - SmallVector InitExprs; - - // set the receiver to self, the first argument to all methods. - InitExprs.push_back( - NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK_BitCast, - new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), - false, - Context->getObjCIdType(), - VK_RValue, - SourceLocation())) - ); // set the 'receiver'. - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - ClsExprs.push_back(StringLiteral::Create(*Context, - ClassDecl->getIdentifier()->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, - EndLoc); - // (Class)objc_getClass("CurrentClass") - CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, - Context->getObjCClassType(), - CK_BitCast, Cls); - ClsExprs.clear(); - ClsExprs.push_back(ArgExpr); - Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, - &ClsExprs[0], ClsExprs.size(), - StartLoc, EndLoc); - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - // To turn off a warning, type-cast to 'id' - InitExprs.push_back( // set 'super class', using class_getSuperclass(). - NoTypeInfoCStyleCastExpr(Context, - Context->getObjCIdType(), - CK_BitCast, Cls)); - // struct objc_super - QualType superType = getSuperStructType(); - Expr *SuperRep; - - if (LangOpts.MicrosoftExt) { - SynthSuperContructorFunctionDecl(); - // Simulate a contructor call... - DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, - false, superType, VK_LValue, - SourceLocation()); - SuperRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], - InitExprs.size(), - superType, VK_LValue, - SourceLocation()); - // The code for super is a little tricky to prevent collision with - // the structure definition in the header. The rewriter has it's own - // internal definition (__rw_objc_super) that is uses. This is why - // we need the cast below. For example: - // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) - // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - SuperRep = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(superType), - CK_BitCast, SuperRep); - } else { - // (struct objc_super) { } - InitListExpr *ILE = - new (Context) InitListExpr(*Context, SourceLocation(), - &InitExprs[0], InitExprs.size(), - SourceLocation()); - TypeSourceInfo *superTInfo - = Context->getTrivialTypeSourceInfo(superType); - SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, - superType, VK_LValue, - ILE, false); - // struct objc_super * - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - } - MsgExprs.push_back(SuperRep); - break; - } - - case ObjCMessageExpr::Class: { - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - ObjCInterfaceDecl *Class - = Exp->getClassReceiver()->getAs()->getInterface(); - IdentifierInfo *clsName = Class->getIdentifier(); - ClsExprs.push_back(StringLiteral::Create(*Context, - clsName->getName(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - MsgExprs.push_back(Cls); - break; - } - - case ObjCMessageExpr::SuperInstance:{ - MsgSendFlavor = MsgSendSuperFunctionDecl; - if (MsgSendStretFlavor) - MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; - assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); - ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); - SmallVector InitExprs; - - InitExprs.push_back( - NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK_BitCast, - new (Context) DeclRefExpr(CurMethodDef->getSelfDecl(), - false, - Context->getObjCIdType(), - VK_RValue, SourceLocation())) - ); // set the 'receiver'. - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - SmallVector ClsExprs; - QualType argType = Context->getPointerType(Context->CharTy); - ClsExprs.push_back(StringLiteral::Create(*Context, - ClassDecl->getIdentifier()->getName(), - StringLiteral::Ascii, false, argType, - SourceLocation())); - CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, - &ClsExprs[0], - ClsExprs.size(), - StartLoc, EndLoc); - // (Class)objc_getClass("CurrentClass") - CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, - Context->getObjCClassType(), - CK_BitCast, Cls); - ClsExprs.clear(); - ClsExprs.push_back(ArgExpr); - Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, - &ClsExprs[0], ClsExprs.size(), - StartLoc, EndLoc); - - // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) - // To turn off a warning, type-cast to 'id' - InitExprs.push_back( - // set 'super class', using class_getSuperclass(). - NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK_BitCast, Cls)); - // struct objc_super - QualType superType = getSuperStructType(); - Expr *SuperRep; - - if (LangOpts.MicrosoftExt) { - SynthSuperContructorFunctionDecl(); - // Simulate a contructor call... - DeclRefExpr *DRE = new (Context) DeclRefExpr(SuperContructorFunctionDecl, - false, superType, VK_LValue, - SourceLocation()); - SuperRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], - InitExprs.size(), - superType, VK_LValue, SourceLocation()); - // The code for super is a little tricky to prevent collision with - // the structure definition in the header. The rewriter has it's own - // internal definition (__rw_objc_super) that is uses. This is why - // we need the cast below. For example: - // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) - // - SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, - Context->getPointerType(SuperRep->getType()), - VK_RValue, OK_Ordinary, - SourceLocation()); - SuperRep = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(superType), - CK_BitCast, SuperRep); - } else { - // (struct objc_super) { } - InitListExpr *ILE = - new (Context) InitListExpr(*Context, SourceLocation(), - &InitExprs[0], InitExprs.size(), - SourceLocation()); - TypeSourceInfo *superTInfo - = Context->getTrivialTypeSourceInfo(superType); - SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, - superType, VK_RValue, ILE, - false); - } - MsgExprs.push_back(SuperRep); - break; - } - - case ObjCMessageExpr::Instance: { - // Remove all type-casts because it may contain objc-style types; e.g. - // Foo *. - Expr *recExpr = Exp->getInstanceReceiver(); - while (CStyleCastExpr *CE = dyn_cast(recExpr)) - recExpr = CE->getSubExpr(); - CastKind CK = recExpr->getType()->isObjCObjectPointerType() - ? CK_BitCast : recExpr->getType()->isBlockPointerType() - ? CK_BlockPointerToObjCPointerCast - : CK_CPointerToObjCPointerCast; - - recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK, recExpr); - MsgExprs.push_back(recExpr); - break; - } - } - - // Create a call to sel_registerName("selName"), it will be the 2nd argument. - SmallVector SelExprs; - QualType argType = Context->getPointerType(Context->CharTy); - SelExprs.push_back(StringLiteral::Create(*Context, - Exp->getSelector().getAsString(), - StringLiteral::Ascii, false, - argType, SourceLocation())); - CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, - &SelExprs[0], SelExprs.size(), - StartLoc, - EndLoc); - MsgExprs.push_back(SelExp); - - // Now push any user supplied arguments. - for (unsigned i = 0; i < Exp->getNumArgs(); i++) { - Expr *userExpr = Exp->getArg(i); - // Make all implicit casts explicit...ICE comes in handy:-) - if (ImplicitCastExpr *ICE = dyn_cast(userExpr)) { - // Reuse the ICE type, it is exactly what the doctor ordered. - QualType type = ICE->getType(); - if (needToScanForQualifiers(type)) - type = Context->getObjCIdType(); - // Make sure we convert "type (^)(...)" to "type (*)(...)". - (void)convertBlockPointerToFunctionPointer(type); - const Expr *SubExpr = ICE->IgnoreParenImpCasts(); - CastKind CK; - if (SubExpr->getType()->isIntegralType(*Context) && - type->isBooleanType()) { - CK = CK_IntegralToBoolean; - } else if (type->isObjCObjectPointerType()) { - if (SubExpr->getType()->isBlockPointerType()) { - CK = CK_BlockPointerToObjCPointerCast; - } else if (SubExpr->getType()->isPointerType()) { - CK = CK_CPointerToObjCPointerCast; - } else { - CK = CK_BitCast; - } - } else { - CK = CK_BitCast; - } - - userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); - } - // Make id cast into an 'id' cast. - else if (CStyleCastExpr *CE = dyn_cast(userExpr)) { - if (CE->getType()->isObjCQualifiedIdType()) { - while ((CE = dyn_cast(userExpr))) - userExpr = CE->getSubExpr(); - CastKind CK; - if (userExpr->getType()->isIntegralType(*Context)) { - CK = CK_IntegralToPointer; - } else if (userExpr->getType()->isBlockPointerType()) { - CK = CK_BlockPointerToObjCPointerCast; - } else if (userExpr->getType()->isPointerType()) { - CK = CK_CPointerToObjCPointerCast; - } else { - CK = CK_BitCast; - } - userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), - CK, userExpr); - } - } - MsgExprs.push_back(userExpr); - // We've transferred the ownership to MsgExprs. For now, we *don't* null - // out the argument in the original expression (since we aren't deleting - // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. - //Exp->setArg(i, 0); - } - // Generate the funky cast. - CastExpr *cast; - SmallVector ArgTypes; - QualType returnType; - - // Push 'id' and 'SEL', the 2 implicit arguments. - if (MsgSendFlavor == MsgSendSuperFunctionDecl) - ArgTypes.push_back(Context->getPointerType(getSuperStructType())); - else - ArgTypes.push_back(Context->getObjCIdType()); - ArgTypes.push_back(Context->getObjCSelType()); - if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { - // Push any user argument types. - for (ObjCMethodDecl::param_iterator PI = OMD->param_begin(), - E = OMD->param_end(); PI != E; ++PI) { - QualType t = (*PI)->getType()->isObjCQualifiedIdType() - ? Context->getObjCIdType() - : (*PI)->getType(); - // Make sure we convert "t (^)(...)" to "t (*)(...)". - (void)convertBlockPointerToFunctionPointer(t); - ArgTypes.push_back(t); - } - returnType = Exp->getType(); - convertToUnqualifiedObjCType(returnType); - (void)convertBlockPointerToFunctionPointer(returnType); - } else { - returnType = Context->getObjCIdType(); - } - // Get the type, we will need to reference it in a couple spots. - QualType msgSendType = MsgSendFlavor->getType(); - - // Create a reference to the objc_msgSend() declaration. - DeclRefExpr *DRE = new (Context) DeclRefExpr(MsgSendFlavor, false, msgSendType, - VK_LValue, SourceLocation()); - - // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). - // If we don't do this cast, we get the following bizarre warning/note: - // xx.m:13: warning: function called through a non-compatible type - // xx.m:13: note: if this code is reached, the program will abort - cast = NoTypeInfoCStyleCastExpr(Context, - Context->getPointerType(Context->VoidTy), - CK_BitCast, DRE); - - // Now do the "normal" pointer to function cast. - QualType castType = - getSimpleFunctionType(returnType, &ArgTypes[0], ArgTypes.size(), - // If we don't have a method decl, force a variadic cast. - Exp->getMethodDecl() ? Exp->getMethodDecl()->isVariadic() : true); - castType = Context->getPointerType(castType); - cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, - cast); - - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); - - const FunctionType *FT = msgSendType->getAs(); - CallExpr *CE = new (Context) CallExpr(*Context, PE, &MsgExprs[0], - MsgExprs.size(), - FT->getResultType(), VK_RValue, - EndLoc); - Stmt *ReplacingStmt = CE; - if (MsgSendStretFlavor) { - // We have the method which returns a struct/union. Must also generate - // call to objc_msgSend_stret and hang both varieties on a conditional - // expression which dictate which one to envoke depending on size of - // method's return type. - - CallExpr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, - msgSendType, returnType, - ArgTypes, MsgExprs, - Exp->getMethodDecl()); - - // Build sizeof(returnType) - UnaryExprOrTypeTraitExpr *sizeofExpr = - new (Context) UnaryExprOrTypeTraitExpr(UETT_SizeOf, - Context->getTrivialTypeSourceInfo(returnType), - Context->getSizeType(), SourceLocation(), - SourceLocation()); - // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) - // FIXME: Value of 8 is base on ppc32/x86 ABI for the most common cases. - // For X86 it is more complicated and some kind of target specific routine - // is needed to decide what to do. - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - IntegerLiteral *limit = IntegerLiteral::Create(*Context, - llvm::APInt(IntSize, 8), - Context->IntTy, - SourceLocation()); - BinaryOperator *lessThanExpr = - new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, - VK_RValue, OK_Ordinary, SourceLocation()); - // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) - ConditionalOperator *CondExpr = - new (Context) ConditionalOperator(lessThanExpr, - SourceLocation(), CE, - SourceLocation(), STCE, - returnType, VK_RValue, OK_Ordinary); - ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - CondExpr); - } - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return ReplacingStmt; -} - -Stmt *RewriteObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { - Stmt *ReplacingStmt = SynthMessageExpr(Exp, Exp->getLocStart(), - Exp->getLocEnd()); - - // Now do the actual rewrite. - ReplaceStmt(Exp, ReplacingStmt); - - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return ReplacingStmt; -} - -// typedef struct objc_object Protocol; -QualType RewriteObjC::getProtocolType() { - if (!ProtocolTypeDecl) { - TypeSourceInfo *TInfo - = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); - ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("Protocol"), - TInfo); - } - return Context->getTypeDeclType(ProtocolTypeDecl); -} - -/// RewriteObjCProtocolExpr - Rewrite a protocol expression into -/// a synthesized/forward data reference (to the protocol's metadata). -/// The forward references (and metadata) are generated in -/// RewriteObjC::HandleTranslationUnit(). -Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { - std::string Name = "_OBJC_PROTOCOL_" + Exp->getProtocol()->getNameAsString(); - IdentifierInfo *ID = &Context->Idents.get(Name); - VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), ID, getProtocolType(), 0, - SC_Extern, SC_None); - DeclRefExpr *DRE = new (Context) DeclRefExpr(VD, false, getProtocolType(), - VK_LValue, SourceLocation()); - Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, - Context->getPointerType(DRE->getType()), - VK_RValue, OK_Ordinary, SourceLocation()); - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), - CK_BitCast, - DerefExpr); - ReplaceStmt(Exp, castExpr); - ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); - // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. - return castExpr; - -} - -bool RewriteObjC::BufferContainsPPDirectives(const char *startBuf, - const char *endBuf) { - while (startBuf < endBuf) { - if (*startBuf == '#') { - // Skip whitespace. - for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf) - ; - if (!strncmp(startBuf, "if", strlen("if")) || - !strncmp(startBuf, "ifdef", strlen("ifdef")) || - !strncmp(startBuf, "ifndef", strlen("ifndef")) || - !strncmp(startBuf, "define", strlen("define")) || - !strncmp(startBuf, "undef", strlen("undef")) || - !strncmp(startBuf, "else", strlen("else")) || - !strncmp(startBuf, "elif", strlen("elif")) || - !strncmp(startBuf, "endif", strlen("endif")) || - !strncmp(startBuf, "pragma", strlen("pragma")) || - !strncmp(startBuf, "include", strlen("include")) || - !strncmp(startBuf, "import", strlen("import")) || - !strncmp(startBuf, "include_next", strlen("include_next"))) - return true; - } - startBuf++; - } - return false; -} - -/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to -/// an objective-c class with ivars. -void RewriteObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, - std::string &Result) { - assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); - assert(CDecl->getName() != "" && - "Name missing in SynthesizeObjCInternalStruct"); - // Do not synthesize more than once. - if (ObjCSynthesizedStructs.count(CDecl)) - return; - ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); - int NumIvars = CDecl->ivar_size(); - SourceLocation LocStart = CDecl->getLocStart(); - SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - - // If no ivars and no root or if its root, directly or indirectly, - // have no ivars (thus not synthesized) then no need to synthesize this class. - if ((!CDecl->isThisDeclarationADefinition() || NumIvars == 0) && - (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { - endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); - ReplaceText(LocStart, endBuf-startBuf, Result); - return; - } - - // FIXME: This has potential of causing problem. If - // SynthesizeObjCInternalStruct is ever called recursively. - Result += "\nstruct "; - Result += CDecl->getNameAsString(); - if (LangOpts.MicrosoftExt) - Result += "_IMPL"; - - if (NumIvars > 0) { - const char *cursor = strchr(startBuf, '{'); - assert((cursor && endBuf) - && "SynthesizeObjCInternalStruct - malformed @interface"); - // If the buffer contains preprocessor directives, we do more fine-grained - // rewrites. This is intended to fix code that looks like (which occurs in - // NSURL.h, for example): - // - // #ifdef XYZ - // @interface Foo : NSObject - // #else - // @interface FooBar : NSObject - // #endif - // { - // int i; - // } - // @end - // - // This clause is segregated to avoid breaking the common case. - if (BufferContainsPPDirectives(startBuf, cursor)) { - SourceLocation L = RCDecl ? CDecl->getSuperClassLoc() : - CDecl->getAtStartLoc(); - const char *endHeader = SM->getCharacterData(L); - endHeader += Lexer::MeasureTokenLength(L, *SM, LangOpts); - - if (CDecl->protocol_begin() != CDecl->protocol_end()) { - // advance to the end of the referenced protocols. - while (endHeader < cursor && *endHeader != '>') endHeader++; - endHeader++; - } - // rewrite the original header - ReplaceText(LocStart, endHeader-startBuf, Result); - } else { - // rewrite the original header *without* disturbing the '{' - ReplaceText(LocStart, cursor-startBuf, Result); - } - if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { - Result = "\n struct "; - Result += RCDecl->getNameAsString(); - Result += "_IMPL "; - Result += RCDecl->getNameAsString(); - Result += "_IVARS;\n"; - - // insert the super class structure definition. - SourceLocation OnePastCurly = - LocStart.getLocWithOffset(cursor-startBuf+1); - InsertText(OnePastCurly, Result); - } - cursor++; // past '{' - - // Now comment out any visibility specifiers. - while (cursor < endBuf) { - if (*cursor == '@') { - SourceLocation atLoc = LocStart.getLocWithOffset(cursor-startBuf); - // Skip whitespace. - for (++cursor; cursor[0] == ' ' || cursor[0] == '\t'; ++cursor) - /*scan*/; - - // FIXME: presence of @public, etc. inside comment results in - // this transformation as well, which is still correct c-code. - if (!strncmp(cursor, "public", strlen("public")) || - !strncmp(cursor, "private", strlen("private")) || - !strncmp(cursor, "package", strlen("package")) || - !strncmp(cursor, "protected", strlen("protected"))) - InsertText(atLoc, "// "); - } - // FIXME: If there are cases where '<' is used in ivar declaration part - // of user code, then scan the ivar list and use needToScanForQualifiers - // for type checking. - else if (*cursor == '<') { - SourceLocation atLoc = LocStart.getLocWithOffset(cursor-startBuf); - InsertText(atLoc, "/* "); - cursor = strchr(cursor, '>'); - cursor++; - atLoc = LocStart.getLocWithOffset(cursor-startBuf); - InsertText(atLoc, " */"); - } else if (*cursor == '^') { // rewrite block specifier. - SourceLocation caretLoc = LocStart.getLocWithOffset(cursor-startBuf); - ReplaceText(caretLoc, 1, "*"); - } - cursor++; - } - // Don't forget to add a ';'!! - InsertText(LocEnd.getLocWithOffset(1), ";"); - } else { // we don't have any instance variables - insert super struct. - endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); - Result += " {\n struct "; - Result += RCDecl->getNameAsString(); - Result += "_IMPL "; - Result += RCDecl->getNameAsString(); - Result += "_IVARS;\n};\n"; - ReplaceText(LocStart, endBuf-startBuf, Result); - } - // Mark this struct as having been generated. - if (!ObjCSynthesizedStructs.insert(CDecl)) - llvm_unreachable("struct already synthesize- SynthesizeObjCInternalStruct"); -} - -//===----------------------------------------------------------------------===// -// Meta Data Emission -//===----------------------------------------------------------------------===// - - -/// RewriteImplementations - This routine rewrites all method implementations -/// and emits meta-data. - -void RewriteObjC::RewriteImplementations() { - int ClsDefCount = ClassImplementation.size(); - int CatDefCount = CategoryImplementation.size(); - - // Rewrite implemented methods - for (int i = 0; i < ClsDefCount; i++) - RewriteImplementationDecl(ClassImplementation[i]); - - for (int i = 0; i < CatDefCount; i++) - RewriteImplementationDecl(CategoryImplementation[i]); -} - -void RewriteObjC::RewriteByRefString(std::string &ResultStr, - const std::string &Name, - ValueDecl *VD, bool def) { - assert(BlockByRefDeclNo.count(VD) && - "RewriteByRefString: ByRef decl missing"); - if (def) - ResultStr += "struct "; - ResultStr += "__Block_byref_" + Name + - "_" + utostr(BlockByRefDeclNo[VD]) ; -} - -static bool HasLocalVariableExternalStorage(ValueDecl *VD) { - if (VarDecl *Var = dyn_cast(VD)) - return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); - return false; -} - -std::string RewriteObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, - StringRef funcName, - std::string Tag) { - const FunctionType *AFT = CE->getFunctionType(); - QualType RT = AFT->getResultType(); - std::string StructRef = "struct " + Tag; - std::string S = "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + - funcName.str() + "_" + "block_func_" + utostr(i); - - BlockDecl *BD = CE->getBlockDecl(); - - if (isa(AFT)) { - // No user-supplied arguments. Still need to pass in a pointer to the - // block (to reference imported block decl refs). - S += "(" + StructRef + " *__cself)"; - } else if (BD->param_empty()) { - S += "(" + StructRef + " *__cself)"; - } else { - const FunctionProtoType *FT = cast(AFT); - assert(FT && "SynthesizeBlockFunc: No function proto"); - S += '('; - // first add the implicit argument. - S += StructRef + " *__cself, "; - std::string ParamStr; - for (BlockDecl::param_iterator AI = BD->param_begin(), - E = BD->param_end(); AI != E; ++AI) { - if (AI != BD->param_begin()) S += ", "; - ParamStr = (*AI)->getNameAsString(); - QualType QT = (*AI)->getType(); - (void)convertBlockPointerToFunctionPointer(QT); - QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); - S += ParamStr; - } - if (FT->isVariadic()) { - if (!BD->param_empty()) S += ", "; - S += "..."; - } - S += ')'; - } - S += " {\n"; - - // Create local declarations to avoid rewriting all closure decl ref exprs. - // First, emit a declaration for all "by ref" decls. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - S += " "; - std::string Name = (*I)->getNameAsString(); - std::string TypeString; - RewriteByRefString(TypeString, Name, (*I)); - TypeString += " *"; - Name = TypeString + Name; - S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; - } - // Next, emit a declaration for all "by copy" declarations. - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - S += " "; - // Handle nested closure invocation. For example: - // - // void (^myImportedClosure)(void); - // myImportedClosure = ^(void) { setGlobalInt(x + y); }; - // - // void (^anotherClosure)(void); - // anotherClosure = ^(void) { - // myImportedClosure(); // import and invoke the closure - // }; - // - if (isTopLevelBlockPointerType((*I)->getType())) { - RewriteBlockPointerTypeVariable(S, (*I)); - S += " = ("; - RewriteBlockPointerType(S, (*I)->getType()); - S += ")"; - S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; - } - else { - std::string Name = (*I)->getNameAsString(); - QualType QT = (*I)->getType(); - if (HasLocalVariableExternalStorage(*I)) - QT = Context->getPointerType(QT); - QT.getAsStringInternal(Name, Context->getPrintingPolicy()); - S += Name + " = __cself->" + - (*I)->getNameAsString() + "; // bound by copy\n"; - } - } - std::string RewrittenStr = RewrittenBlockExprs[CE]; - const char *cstr = RewrittenStr.c_str(); - while (*cstr++ != '{') ; - S += cstr; - S += "\n"; - return S; -} - -std::string RewriteObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, - StringRef funcName, - std::string Tag) { - std::string StructRef = "struct " + Tag; - std::string S = "static void __"; - - S += funcName; - S += "_block_copy_" + utostr(i); - S += "(" + StructRef; - S += "*dst, " + StructRef; - S += "*src) {"; - for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), - E = ImportedBlockDecls.end(); I != E; ++I) { - ValueDecl *VD = (*I); - S += "_Block_object_assign((void*)&dst->"; - S += (*I)->getNameAsString(); - S += ", (void*)src->"; - S += (*I)->getNameAsString(); - if (BlockByRefDeclsPtrSet.count((*I))) - S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; - else if (VD->getType()->isBlockPointerType()) - S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; - else - S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; - } - S += "}\n"; - - S += "\nstatic void __"; - S += funcName; - S += "_block_dispose_" + utostr(i); - S += "(" + StructRef; - S += "*src) {"; - for (llvm::SmallPtrSet::iterator I = ImportedBlockDecls.begin(), - E = ImportedBlockDecls.end(); I != E; ++I) { - ValueDecl *VD = (*I); - S += "_Block_object_dispose((void*)src->"; - S += (*I)->getNameAsString(); - if (BlockByRefDeclsPtrSet.count((*I))) - S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; - else if (VD->getType()->isBlockPointerType()) - S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; - else - S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; - } - S += "}\n"; - return S; -} - -std::string RewriteObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, - std::string Desc) { - std::string S = "\nstruct " + Tag; - std::string Constructor = " " + Tag; - - S += " {\n struct __block_impl impl;\n"; - S += " struct " + Desc; - S += "* Desc;\n"; - - Constructor += "(void *fp, "; // Invoke function pointer. - Constructor += "struct " + Desc; // Descriptor pointer. - Constructor += " *desc"; - - if (BlockDeclRefs.size()) { - // Output all "by copy" declarations. - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - S += " "; - std::string FieldName = (*I)->getNameAsString(); - std::string ArgName = "_" + FieldName; - // Handle nested closure invocation. For example: - // - // void (^myImportedBlock)(void); - // myImportedBlock = ^(void) { setGlobalInt(x + y); }; - // - // void (^anotherBlock)(void); - // anotherBlock = ^(void) { - // myImportedBlock(); // import and invoke the closure - // }; - // - if (isTopLevelBlockPointerType((*I)->getType())) { - S += "struct __block_impl *"; - Constructor += ", void *" + ArgName; - } else { - QualType QT = (*I)->getType(); - if (HasLocalVariableExternalStorage(*I)) - QT = Context->getPointerType(QT); - QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); - QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); - Constructor += ", " + ArgName; - } - S += FieldName + ";\n"; - } - // Output all "by ref" declarations. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - S += " "; - std::string FieldName = (*I)->getNameAsString(); - std::string ArgName = "_" + FieldName; - { - std::string TypeString; - RewriteByRefString(TypeString, FieldName, (*I)); - TypeString += " *"; - FieldName = TypeString + FieldName; - ArgName = TypeString + ArgName; - Constructor += ", " + ArgName; - } - S += FieldName + "; // by ref\n"; - } - // Finish writing the constructor. - Constructor += ", int flags=0)"; - // Initialize all "by copy" arguments. - bool firsTime = true; - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - std::string Name = (*I)->getNameAsString(); - if (firsTime) { - Constructor += " : "; - firsTime = false; - } - else - Constructor += ", "; - if (isTopLevelBlockPointerType((*I)->getType())) - Constructor += Name + "((struct __block_impl *)_" + Name + ")"; - else - Constructor += Name + "(_" + Name + ")"; - } - // Initialize all "by ref" arguments. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - std::string Name = (*I)->getNameAsString(); - if (firsTime) { - Constructor += " : "; - firsTime = false; - } - else - Constructor += ", "; - Constructor += Name + "(_" + Name + "->__forwarding)"; - } - - Constructor += " {\n"; - if (GlobalVarDecl) - Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; - else - Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; - Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; - - Constructor += " Desc = desc;\n"; - } else { - // Finish writing the constructor. - Constructor += ", int flags=0) {\n"; - if (GlobalVarDecl) - Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; - else - Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; - Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; - Constructor += " Desc = desc;\n"; - } - Constructor += " "; - Constructor += "}\n"; - S += Constructor; - S += "};\n"; - return S; -} - -std::string RewriteObjC::SynthesizeBlockDescriptor(std::string DescTag, - std::string ImplTag, int i, - StringRef FunName, - unsigned hasCopy) { - std::string S = "\nstatic struct " + DescTag; - - S += " {\n unsigned long reserved;\n"; - S += " unsigned long Block_size;\n"; - if (hasCopy) { - S += " void (*copy)(struct "; - S += ImplTag; S += "*, struct "; - S += ImplTag; S += "*);\n"; - - S += " void (*dispose)(struct "; - S += ImplTag; S += "*);\n"; - } - S += "} "; - - S += DescTag + "_DATA = { 0, sizeof(struct "; - S += ImplTag + ")"; - if (hasCopy) { - S += ", __" + FunName.str() + "_block_copy_" + utostr(i); - S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); - } - S += "};\n"; - return S; -} - -void RewriteObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, - StringRef FunName) { - // Insert declaration for the function in which block literal is used. - if (CurFunctionDeclToDeclareForBlock && !Blocks.empty()) - RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); - bool RewriteSC = (GlobalVarDecl && - !Blocks.empty() && - GlobalVarDecl->getStorageClass() == SC_Static && - GlobalVarDecl->getType().getCVRQualifiers()); - if (RewriteSC) { - std::string SC(" void __"); - SC += GlobalVarDecl->getNameAsString(); - SC += "() {}"; - InsertText(FunLocStart, SC); - } - - // Insert closures that were part of the function. - for (unsigned i = 0, count=0; i < Blocks.size(); i++) { - CollectBlockDeclRefInfo(Blocks[i]); - // Need to copy-in the inner copied-in variables not actually used in this - // block. - for (int j = 0; j < InnerDeclRefsCount[i]; j++) { - DeclRefExpr *Exp = InnerDeclRefs[count++]; - ValueDecl *VD = Exp->getDecl(); - BlockDeclRefs.push_back(Exp); - if (!VD->hasAttr() && !BlockByCopyDeclsPtrSet.count(VD)) { - BlockByCopyDeclsPtrSet.insert(VD); - BlockByCopyDecls.push_back(VD); - } - if (VD->hasAttr() && !BlockByRefDeclsPtrSet.count(VD)) { - BlockByRefDeclsPtrSet.insert(VD); - BlockByRefDecls.push_back(VD); - } - // imported objects in the inner blocks not used in the outer - // blocks must be copied/disposed in the outer block as well. - if (VD->hasAttr() || - VD->getType()->isObjCObjectPointerType() || - VD->getType()->isBlockPointerType()) - ImportedBlockDecls.insert(VD); - } - - std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); - std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); - - std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); - - InsertText(FunLocStart, CI); - - std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); - - InsertText(FunLocStart, CF); - - if (ImportedBlockDecls.size()) { - std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); - InsertText(FunLocStart, HF); - } - std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, - ImportedBlockDecls.size() > 0); - InsertText(FunLocStart, BD); - - BlockDeclRefs.clear(); - BlockByRefDecls.clear(); - BlockByRefDeclsPtrSet.clear(); - BlockByCopyDecls.clear(); - BlockByCopyDeclsPtrSet.clear(); - ImportedBlockDecls.clear(); - } - if (RewriteSC) { - // Must insert any 'const/volatile/static here. Since it has been - // removed as result of rewriting of block literals. - std::string SC; - if (GlobalVarDecl->getStorageClass() == SC_Static) - SC = "static "; - if (GlobalVarDecl->getType().isConstQualified()) - SC += "const "; - if (GlobalVarDecl->getType().isVolatileQualified()) - SC += "volatile "; - if (GlobalVarDecl->getType().isRestrictQualified()) - SC += "restrict "; - InsertText(FunLocStart, SC); - } - - Blocks.clear(); - InnerDeclRefsCount.clear(); - InnerDeclRefs.clear(); - RewrittenBlockExprs.clear(); -} - -void RewriteObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { - SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); - StringRef FuncName = FD->getName(); - - SynthesizeBlockLiterals(FunLocStart, FuncName); -} - -static void BuildUniqueMethodName(std::string &Name, - ObjCMethodDecl *MD) { - ObjCInterfaceDecl *IFace = MD->getClassInterface(); - Name = IFace->getName(); - Name += "__" + MD->getSelector().getAsString(); - // Convert colons to underscores. - std::string::size_type loc = 0; - while ((loc = Name.find(":", loc)) != std::string::npos) - Name.replace(loc, 1, "_"); -} - -void RewriteObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { - //fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); - //SourceLocation FunLocStart = MD->getLocStart(); - SourceLocation FunLocStart = MD->getLocStart(); - std::string FuncName; - BuildUniqueMethodName(FuncName, MD); - SynthesizeBlockLiterals(FunLocStart, FuncName); -} - -void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) { - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - if (BlockExpr *CBE = dyn_cast(*CI)) - GetBlockDeclRefExprs(CBE->getBody()); - else - GetBlockDeclRefExprs(*CI); - } - // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - // FIXME: Handle enums. - if (!isa(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - if (HasLocalVariableExternalStorage(DRE->getDecl())) - BlockDeclRefs.push_back(DRE); - } - } - - return; -} - -void RewriteObjC::GetInnerBlockDeclRefExprs(Stmt *S, - SmallVector &InnerBlockDeclRefs, - llvm::SmallPtrSet &InnerContexts) { - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - if (BlockExpr *CBE = dyn_cast(*CI)) { - InnerContexts.insert(cast(CBE->getBlockDecl())); - GetInnerBlockDeclRefExprs(CBE->getBody(), - InnerBlockDeclRefs, - InnerContexts); - } - else - GetInnerBlockDeclRefExprs(*CI, - InnerBlockDeclRefs, - InnerContexts); - - } - // Handle specific things. - if (DeclRefExpr *DRE = dyn_cast(S)) { - if (DRE->refersToEnclosingLocal()) { - if (!isa(DRE->getDecl()) && - !InnerContexts.count(DRE->getDecl()->getDeclContext())) - InnerBlockDeclRefs.push_back(DRE); - if (VarDecl *Var = dyn_cast(DRE->getDecl())) - if (Var->isFunctionOrMethodVarDecl()) - ImportedLocalExternalDecls.insert(Var); - } - } - - return; -} - -/// convertFunctionTypeOfBlocks - This routine converts a function type -/// whose result type may be a block pointer or whose argument type(s) -/// might be block pointers to an equivalent function type replacing -/// all block pointers to function pointers. -QualType RewriteObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { - const FunctionProtoType *FTP = dyn_cast(FT); - // FTP will be null for closures that don't take arguments. - // Generate a funky cast. - SmallVector ArgTypes; - QualType Res = FT->getResultType(); - bool HasBlockType = convertBlockPointerToFunctionPointer(Res); - - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I && (I != E); ++I) { - QualType t = *I; - // Make sure we convert "t (^)(...)" to "t (*)(...)". - if (convertBlockPointerToFunctionPointer(t)) - HasBlockType = true; - ArgTypes.push_back(t); - } - } - QualType FuncType; - // FIXME. Does this work if block takes no argument but has a return type - // which is of block type? - if (HasBlockType) - FuncType = getSimpleFunctionType(Res, &ArgTypes[0], ArgTypes.size()); - else FuncType = QualType(FT, 0); - return FuncType; -} - -Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { - // Navigate to relevant type information. - const BlockPointerType *CPT = 0; - - if (const DeclRefExpr *DRE = dyn_cast(BlockExp)) { - CPT = DRE->getType()->getAs(); - } else if (const MemberExpr *MExpr = dyn_cast(BlockExp)) { - CPT = MExpr->getType()->getAs(); - } - else if (const ParenExpr *PRE = dyn_cast(BlockExp)) { - return SynthesizeBlockCall(Exp, PRE->getSubExpr()); - } - else if (const ImplicitCastExpr *IEXPR = dyn_cast(BlockExp)) - CPT = IEXPR->getType()->getAs(); - else if (const ConditionalOperator *CEXPR = - dyn_cast(BlockExp)) { - Expr *LHSExp = CEXPR->getLHS(); - Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); - Expr *RHSExp = CEXPR->getRHS(); - Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); - Expr *CONDExp = CEXPR->getCond(); - ConditionalOperator *CondExpr = - new (Context) ConditionalOperator(CONDExp, - SourceLocation(), cast(LHSStmt), - SourceLocation(), cast(RHSStmt), - Exp->getType(), VK_RValue, OK_Ordinary); - return CondExpr; - } else if (const ObjCIvarRefExpr *IRE = dyn_cast(BlockExp)) { - CPT = IRE->getType()->getAs(); - } else if (const PseudoObjectExpr *POE - = dyn_cast(BlockExp)) { - CPT = POE->getType()->castAs(); - } else { - assert(1 && "RewriteBlockClass: Bad type"); - } - assert(CPT && "RewriteBlockClass: Bad type"); - const FunctionType *FT = CPT->getPointeeType()->getAs(); - assert(FT && "RewriteBlockClass: Bad type"); - const FunctionProtoType *FTP = dyn_cast(FT); - // FTP will be null for closures that don't take arguments. - - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get("__block_impl")); - QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); - - // Generate a funky cast. - SmallVector ArgTypes; - - // Push the block argument type. - ArgTypes.push_back(PtrBlock); - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I && (I != E); ++I) { - QualType t = *I; - // Make sure we convert "t (^)(...)" to "t (*)(...)". - if (!convertBlockPointerToFunctionPointer(t)) - convertToUnqualifiedObjCType(t); - ArgTypes.push_back(t); - } - } - // Now do the pointer to function cast. - QualType PtrToFuncCastType - = getSimpleFunctionType(Exp->getType(), &ArgTypes[0], ArgTypes.size()); - - PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); - - CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, - CK_BitCast, - const_cast(BlockExp)); - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - BlkCast); - //PE->dump(); - - FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("FuncPtr"), - Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(PE, true, FD, SourceLocation(), - FD->getType(), VK_LValue, - OK_Ordinary); - - - CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, - CK_BitCast, ME); - PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); - - SmallVector BlkExprs; - // Add the implicit argument. - BlkExprs.push_back(BlkCast); - // Add the user arguments. - for (CallExpr::arg_iterator I = Exp->arg_begin(), - E = Exp->arg_end(); I != E; ++I) { - BlkExprs.push_back(*I); - } - CallExpr *CE = new (Context) CallExpr(*Context, PE, &BlkExprs[0], - BlkExprs.size(), - Exp->getType(), VK_RValue, - SourceLocation()); - return CE; -} - -// We need to return the rewritten expression to handle cases where the -// BlockDeclRefExpr is embedded in another expression being rewritten. -// For example: -// -// int main() { -// __block Foo *f; -// __block int i; -// -// void (^myblock)() = ^() { -// [f test]; // f is a BlockDeclRefExpr embedded in a message (which is being rewritten). -// i = 77; -// }; -//} -Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { - // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR - // for each DeclRefExp where BYREFVAR is name of the variable. - ValueDecl *VD = DeclRefExp->getDecl(); - bool isArrow = DeclRefExp->refersToEnclosingLocal(); - - FieldDecl *FD = FieldDecl::Create(*Context, 0, SourceLocation(), - SourceLocation(), - &Context->Idents.get("__forwarding"), - Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - MemberExpr *ME = new (Context) MemberExpr(DeclRefExp, isArrow, - FD, SourceLocation(), - FD->getType(), VK_LValue, - OK_Ordinary); - - StringRef Name = VD->getName(); - FD = FieldDecl::Create(*Context, 0, SourceLocation(), SourceLocation(), - &Context->Idents.get(Name), - Context->VoidPtrTy, 0, - /*BitWidth=*/0, /*Mutable=*/true, - ICIS_NoInit); - ME = new (Context) MemberExpr(ME, true, FD, SourceLocation(), - DeclRefExp->getType(), VK_LValue, OK_Ordinary); - - - - // Need parens to enforce precedence. - ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), - DeclRefExp->getExprLoc(), - ME); - ReplaceStmt(DeclRefExp, PE); - return PE; -} - -// Rewrites the imported local variable V with external storage -// (static, extern, etc.) as *V -// -Stmt *RewriteObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { - ValueDecl *VD = DRE->getDecl(); - if (VarDecl *Var = dyn_cast(VD)) - if (!ImportedLocalExternalDecls.count(Var)) - return DRE; - Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), - VK_LValue, OK_Ordinary, - DRE->getLocation()); - // Need parens to enforce precedence. - ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), - Exp); - ReplaceStmt(DRE, PE); - return PE; -} - -void RewriteObjC::RewriteCastExpr(CStyleCastExpr *CE) { - SourceLocation LocStart = CE->getLParenLoc(); - SourceLocation LocEnd = CE->getRParenLoc(); - - // Need to avoid trying to rewrite synthesized casts. - if (LocStart.isInvalid()) - return; - // Need to avoid trying to rewrite casts contained in macros. - if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) - return; - - const char *startBuf = SM->getCharacterData(LocStart); - const char *endBuf = SM->getCharacterData(LocEnd); - QualType QT = CE->getType(); - const Type* TypePtr = QT->getAs(); - if (isa(TypePtr)) { - const TypeOfExprType *TypeOfExprTypePtr = cast(TypePtr); - QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); - std::string TypeAsString = "("; - RewriteBlockPointerType(TypeAsString, QT); - TypeAsString += ")"; - ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); - return; - } - // advance the location to startArgList. - const char *argPtr = startBuf; - - while (*argPtr++ && (argPtr < endBuf)) { - switch (*argPtr) { - case '^': - // Replace the '^' with '*'. - LocStart = LocStart.getLocWithOffset(argPtr-startBuf); - ReplaceText(LocStart, 1, "*"); - break; - } - } - return; -} - -void RewriteObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { - SourceLocation DeclLoc = FD->getLocation(); - unsigned parenCount = 0; - - // We have 1 or more arguments that have closure pointers. - const char *startBuf = SM->getCharacterData(DeclLoc); - const char *startArgList = strchr(startBuf, '('); - - assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); - - parenCount++; - // advance the location to startArgList. - DeclLoc = DeclLoc.getLocWithOffset(startArgList-startBuf); - assert((DeclLoc.isValid()) && "Invalid DeclLoc"); - - const char *argPtr = startArgList; - - while (*argPtr++ && parenCount) { - switch (*argPtr) { - case '^': - // Replace the '^' with '*'. - DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); - ReplaceText(DeclLoc, 1, "*"); - break; - case '(': - parenCount++; - break; - case ')': - parenCount--; - break; - } - } - return; -} - -bool RewriteObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { - const FunctionProtoType *FTP; - const PointerType *PT = QT->getAs(); - if (PT) { - FTP = PT->getPointeeType()->getAs(); - } else { - const BlockPointerType *BPT = QT->getAs(); - assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); - FTP = BPT->getPointeeType()->getAs(); - } - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I != E; ++I) - if (isTopLevelBlockPointerType(*I)) - return true; - } - return false; -} - -bool RewriteObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { - const FunctionProtoType *FTP; - const PointerType *PT = QT->getAs(); - if (PT) { - FTP = PT->getPointeeType()->getAs(); - } else { - const BlockPointerType *BPT = QT->getAs(); - assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); - FTP = BPT->getPointeeType()->getAs(); - } - if (FTP) { - for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), - E = FTP->arg_type_end(); I != E; ++I) { - if ((*I)->isObjCQualifiedIdType()) - return true; - if ((*I)->isObjCObjectPointerType() && - (*I)->getPointeeType()->isObjCQualifiedInterfaceType()) - return true; - } - - } - return false; -} - -void RewriteObjC::GetExtentOfArgList(const char *Name, const char *&LParen, - const char *&RParen) { - const char *argPtr = strchr(Name, '('); - assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); - - LParen = argPtr; // output the start. - argPtr++; // skip past the left paren. - unsigned parenCount = 1; - - while (*argPtr && parenCount) { - switch (*argPtr) { - case '(': parenCount++; break; - case ')': parenCount--; break; - default: break; - } - if (parenCount) argPtr++; - } - assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); - RParen = argPtr; // output the end -} - -void RewriteObjC::RewriteBlockPointerDecl(NamedDecl *ND) { - if (FunctionDecl *FD = dyn_cast(ND)) { - RewriteBlockPointerFunctionArgs(FD); - return; - } - // Handle Variables and Typedefs. - SourceLocation DeclLoc = ND->getLocation(); - QualType DeclT; - if (VarDecl *VD = dyn_cast(ND)) - DeclT = VD->getType(); - else if (TypedefNameDecl *TDD = dyn_cast(ND)) - DeclT = TDD->getUnderlyingType(); - else if (FieldDecl *FD = dyn_cast(ND)) - DeclT = FD->getType(); - else - llvm_unreachable("RewriteBlockPointerDecl(): Decl type not yet handled"); - - const char *startBuf = SM->getCharacterData(DeclLoc); - const char *endBuf = startBuf; - // scan backward (from the decl location) for the end of the previous decl. - while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) - startBuf--; - SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); - std::string buf; - unsigned OrigLength=0; - // *startBuf != '^' if we are dealing with a pointer to function that - // may take block argument types (which will be handled below). - if (*startBuf == '^') { - // Replace the '^' with '*', computing a negative offset. - buf = '*'; - startBuf++; - OrigLength++; - } - while (*startBuf != ')') { - buf += *startBuf; - startBuf++; - OrigLength++; - } - buf += ')'; - OrigLength++; - - if (PointerTypeTakesAnyBlockArguments(DeclT) || - PointerTypeTakesAnyObjCQualifiedType(DeclT)) { - // Replace the '^' with '*' for arguments. - // Replace id

with id/*<>*/ - DeclLoc = ND->getLocation(); - startBuf = SM->getCharacterData(DeclLoc); - const char *argListBegin, *argListEnd; - GetExtentOfArgList(startBuf, argListBegin, argListEnd); - while (argListBegin < argListEnd) { - if (*argListBegin == '^') - buf += '*'; - else if (*argListBegin == '<') { - buf += "/*"; - buf += *argListBegin++; - OrigLength++;; - while (*argListBegin != '>') { - buf += *argListBegin++; - OrigLength++; - } - buf += *argListBegin; - buf += "*/"; - } - else - buf += *argListBegin; - argListBegin++; - OrigLength++; - } - buf += ')'; - OrigLength++; - } - ReplaceText(Start, OrigLength, buf); - - return; -} - - -/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: -/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, -/// struct Block_byref_id_object *src) { -/// _Block_object_assign (&_dest->object, _src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT -/// [|BLOCK_FIELD_IS_WEAK]) // object -/// _Block_object_assign(&_dest->object, _src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK -/// [|BLOCK_FIELD_IS_WEAK]) // block -/// } -/// And: -/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { -/// _Block_object_dispose(_src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT -/// [|BLOCK_FIELD_IS_WEAK]) // object -/// _Block_object_dispose(_src->object, -/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK -/// [|BLOCK_FIELD_IS_WEAK]) // block -/// } - -std::string RewriteObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, - int flag) { - std::string S; - if (CopyDestroyCache.count(flag)) - return S; - CopyDestroyCache.insert(flag); - S = "static void __Block_byref_id_object_copy_"; - S += utostr(flag); - S += "(void *dst, void *src) {\n"; - - // offset into the object pointer is computed as: - // void * + void* + int + int + void* + void * - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - unsigned VoidPtrSize = - static_cast(Context->getTypeSize(Context->VoidPtrTy)); - - unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); - S += " _Block_object_assign((char*)dst + "; - S += utostr(offset); - S += ", *(void * *) ((char*)src + "; - S += utostr(offset); - S += "), "; - S += utostr(flag); - S += ");\n}\n"; - - S += "static void __Block_byref_id_object_dispose_"; - S += utostr(flag); - S += "(void *src) {\n"; - S += " _Block_object_dispose(*(void * *) ((char*)src + "; - S += utostr(offset); - S += "), "; - S += utostr(flag); - S += ");\n}\n"; - return S; -} - -/// RewriteByRefVar - For each __block typex ND variable this routine transforms -/// the declaration into: -/// struct __Block_byref_ND { -/// void *__isa; // NULL for everything except __weak pointers -/// struct __Block_byref_ND *__forwarding; -/// int32_t __flags; -/// int32_t __size; -/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object -/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object -/// typex ND; -/// }; -/// -/// It then replaces declaration of ND variable with: -/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, -/// __size=sizeof(struct __Block_byref_ND), -/// ND=initializer-if-any}; -/// -/// -void RewriteObjC::RewriteByRefVar(VarDecl *ND) { - // Insert declaration for the function in which block literal is - // used. - if (CurFunctionDeclToDeclareForBlock) - RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); - int flag = 0; - int isa = 0; - SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); - if (DeclLoc.isInvalid()) - // If type location is missing, it is because of missing type (a warning). - // Use variable's location which is good for this case. - DeclLoc = ND->getLocation(); - const char *startBuf = SM->getCharacterData(DeclLoc); - SourceLocation X = ND->getLocEnd(); - X = SM->getExpansionLoc(X); - const char *endBuf = SM->getCharacterData(X); - std::string Name(ND->getNameAsString()); - std::string ByrefType; - RewriteByRefString(ByrefType, Name, ND, true); - ByrefType += " {\n"; - ByrefType += " void *__isa;\n"; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += " *__forwarding;\n"; - ByrefType += " int __flags;\n"; - ByrefType += " int __size;\n"; - // Add void *__Block_byref_id_object_copy; - // void *__Block_byref_id_object_dispose; if needed. - QualType Ty = ND->getType(); - bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty); - if (HasCopyAndDispose) { - ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; - ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; - } - - QualType T = Ty; - (void)convertBlockPointerToFunctionPointer(T); - T.getAsStringInternal(Name, Context->getPrintingPolicy()); - - ByrefType += " " + Name + ";\n"; - ByrefType += "};\n"; - // Insert this type in global scope. It is needed by helper function. - SourceLocation FunLocStart; - if (CurFunctionDef) - FunLocStart = CurFunctionDef->getTypeSpecStartLoc(); - else { - assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); - FunLocStart = CurMethodDef->getLocStart(); - } - InsertText(FunLocStart, ByrefType); - if (Ty.isObjCGCWeak()) { - flag |= BLOCK_FIELD_IS_WEAK; - isa = 1; - } - - if (HasCopyAndDispose) { - flag = BLOCK_BYREF_CALLER; - QualType Ty = ND->getType(); - // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. - if (Ty->isBlockPointerType()) - flag |= BLOCK_FIELD_IS_BLOCK; - else - flag |= BLOCK_FIELD_IS_OBJECT; - std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); - if (!HF.empty()) - InsertText(FunLocStart, HF); - } - - // struct __Block_byref_ND ND = - // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), - // initializer-if-any}; - bool hasInit = (ND->getInit() != 0); - unsigned flags = 0; - if (HasCopyAndDispose) - flags |= BLOCK_HAS_COPY_DISPOSE; - Name = ND->getNameAsString(); - ByrefType.clear(); - RewriteByRefString(ByrefType, Name, ND); - std::string ForwardingCastType("("); - ForwardingCastType += ByrefType + " *)"; - if (!hasInit) { - ByrefType += " " + Name + " = {(void*)"; - ByrefType += utostr(isa); - ByrefType += "," + ForwardingCastType + "&" + Name + ", "; - ByrefType += utostr(flags); - ByrefType += ", "; - ByrefType += "sizeof("; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += ")"; - if (HasCopyAndDispose) { - ByrefType += ", __Block_byref_id_object_copy_"; - ByrefType += utostr(flag); - ByrefType += ", __Block_byref_id_object_dispose_"; - ByrefType += utostr(flag); - } - ByrefType += "};\n"; - unsigned nameSize = Name.size(); - // for block or function pointer declaration. Name is aleady - // part of the declaration. - if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) - nameSize = 1; - ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); - } - else { - SourceLocation startLoc; - Expr *E = ND->getInit(); - if (const CStyleCastExpr *ECE = dyn_cast(E)) - startLoc = ECE->getLParenLoc(); - else - startLoc = E->getLocStart(); - startLoc = SM->getExpansionLoc(startLoc); - endBuf = SM->getCharacterData(startLoc); - ByrefType += " " + Name; - ByrefType += " = {(void*)"; - ByrefType += utostr(isa); - ByrefType += "," + ForwardingCastType + "&" + Name + ", "; - ByrefType += utostr(flags); - ByrefType += ", "; - ByrefType += "sizeof("; - RewriteByRefString(ByrefType, Name, ND); - ByrefType += "), "; - if (HasCopyAndDispose) { - ByrefType += "__Block_byref_id_object_copy_"; - ByrefType += utostr(flag); - ByrefType += ", __Block_byref_id_object_dispose_"; - ByrefType += utostr(flag); - ByrefType += ", "; - } - ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); - - // Complete the newly synthesized compound expression by inserting a right - // curly brace before the end of the declaration. - // FIXME: This approach avoids rewriting the initializer expression. It - // also assumes there is only one declarator. For example, the following - // isn't currently supported by this routine (in general): - // - // double __block BYREFVAR = 1.34, BYREFVAR2 = 1.37; - // - const char *startInitializerBuf = SM->getCharacterData(startLoc); - const char *semiBuf = strchr(startInitializerBuf, ';'); - assert((*semiBuf == ';') && "RewriteByRefVar: can't find ';'"); - SourceLocation semiLoc = - startLoc.getLocWithOffset(semiBuf-startInitializerBuf); - - InsertText(semiLoc, "}"); - } - return; -} - -void RewriteObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { - // Add initializers for any closure decl refs. - GetBlockDeclRefExprs(Exp->getBody()); - if (BlockDeclRefs.size()) { - // Unique all "by copy" declarations. - for (unsigned i = 0; i < BlockDeclRefs.size(); i++) - if (!BlockDeclRefs[i]->getDecl()->hasAttr()) { - if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { - BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); - BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); - } - } - // Unique all "by ref" declarations. - for (unsigned i = 0; i < BlockDeclRefs.size(); i++) - if (BlockDeclRefs[i]->getDecl()->hasAttr()) { - if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { - BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); - BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); - } - } - // Find any imported blocks...they will need special attention. - for (unsigned i = 0; i < BlockDeclRefs.size(); i++) - if (BlockDeclRefs[i]->getDecl()->hasAttr() || - BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || - BlockDeclRefs[i]->getType()->isBlockPointerType()) - ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); - } -} - -FunctionDecl *RewriteObjC::SynthBlockInitFunctionDecl(StringRef name) { - IdentifierInfo *ID = &Context->Idents.get(name); - QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); - return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), - SourceLocation(), ID, FType, 0, SC_Extern, - SC_None, false, false); -} - -Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp, - const SmallVector &InnerBlockDeclRefs) { - const BlockDecl *block = Exp->getBlockDecl(); - Blocks.push_back(Exp); - - CollectBlockDeclRefInfo(Exp); - - // Add inner imported variables now used in current block. - int countOfInnerDecls = 0; - if (!InnerBlockDeclRefs.empty()) { - for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { - DeclRefExpr *Exp = InnerBlockDeclRefs[i]; - ValueDecl *VD = Exp->getDecl(); - if (!VD->hasAttr() && !BlockByCopyDeclsPtrSet.count(VD)) { - // We need to save the copied-in variables in nested - // blocks because it is needed at the end for some of the API generations. - // See SynthesizeBlockLiterals routine. - InnerDeclRefs.push_back(Exp); countOfInnerDecls++; - BlockDeclRefs.push_back(Exp); - BlockByCopyDeclsPtrSet.insert(VD); - BlockByCopyDecls.push_back(VD); - } - if (VD->hasAttr() && !BlockByRefDeclsPtrSet.count(VD)) { - InnerDeclRefs.push_back(Exp); countOfInnerDecls++; - BlockDeclRefs.push_back(Exp); - BlockByRefDeclsPtrSet.insert(VD); - BlockByRefDecls.push_back(VD); - } - } - // Find any imported blocks...they will need special attention. - for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) - if (InnerBlockDeclRefs[i]->getDecl()->hasAttr() || - InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || - InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) - ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); - } - InnerDeclRefsCount.push_back(countOfInnerDecls); - - std::string FuncName; - - if (CurFunctionDef) - FuncName = CurFunctionDef->getNameAsString(); - else if (CurMethodDef) - BuildUniqueMethodName(FuncName, CurMethodDef); - else if (GlobalVarDecl) - FuncName = std::string(GlobalVarDecl->getNameAsString()); - - std::string BlockNumber = utostr(Blocks.size()-1); - - std::string Tag = "__" + FuncName + "_block_impl_" + BlockNumber; - std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; - - // Get a pointer to the function type so we can cast appropriately. - QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); - QualType FType = Context->getPointerType(BFT); - - FunctionDecl *FD; - Expr *NewRep; - - // Simulate a contructor call... - FD = SynthBlockInitFunctionDecl(Tag); - DeclRefExpr *DRE = new (Context) DeclRefExpr(FD, false, FType, VK_RValue, - SourceLocation()); - - SmallVector InitExprs; - - // Initialize the block function. - FD = SynthBlockInitFunctionDecl(Func); - DeclRefExpr *Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), - VK_LValue, SourceLocation()); - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, - CK_BitCast, Arg); - InitExprs.push_back(castExpr); - - // Initialize the block descriptor. - std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; - - VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, - SourceLocation(), SourceLocation(), - &Context->Idents.get(DescData.c_str()), - Context->VoidPtrTy, 0, - SC_Static, SC_None); - UnaryOperator *DescRefExpr = - new (Context) UnaryOperator(new (Context) DeclRefExpr(NewVD, false, - Context->VoidPtrTy, - VK_LValue, - SourceLocation()), - UO_AddrOf, - Context->getPointerType(Context->VoidPtrTy), - VK_RValue, OK_Ordinary, - SourceLocation()); - InitExprs.push_back(DescRefExpr); - - // Add initializers for any closure decl refs. - if (BlockDeclRefs.size()) { - Expr *Exp; - // Output all "by copy" declarations. - for (SmallVector::iterator I = BlockByCopyDecls.begin(), - E = BlockByCopyDecls.end(); I != E; ++I) { - if (isObjCType((*I)->getType())) { - // FIXME: Conform to ABI ([[obj retain] autorelease]). - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, - SourceLocation()); - if (HasLocalVariableExternalStorage(*I)) { - QualType QT = (*I)->getType(); - QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation()); - } - } else if (isTopLevelBlockPointerType((*I)->getType())) { - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Arg = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, - SourceLocation()); - Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, - CK_BitCast, Arg); - } else { - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, - SourceLocation()); - if (HasLocalVariableExternalStorage(*I)) { - QualType QT = (*I)->getType(); - QT = Context->getPointerType(QT); - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, - OK_Ordinary, SourceLocation()); - } - - } - InitExprs.push_back(Exp); - } - // Output all "by ref" declarations. - for (SmallVector::iterator I = BlockByRefDecls.begin(), - E = BlockByRefDecls.end(); I != E; ++I) { - ValueDecl *ND = (*I); - std::string Name(ND->getNameAsString()); - std::string RecName; - RewriteByRefString(RecName, Name, ND, true); - IdentifierInfo *II = &Context->Idents.get(RecName.c_str() - + sizeof("struct")); - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - II); - assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); - QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); - - FD = SynthBlockInitFunctionDecl((*I)->getName()); - Exp = new (Context) DeclRefExpr(FD, false, FD->getType(), VK_LValue, - SourceLocation()); - bool isNestedCapturedVar = false; - if (block) - for (BlockDecl::capture_const_iterator ci = block->capture_begin(), - ce = block->capture_end(); ci != ce; ++ci) { - const VarDecl *variable = ci->getVariable(); - if (variable == ND && ci->isNested()) { - assert (ci->isByRef() && - "SynthBlockInitExpr - captured block variable is not byref"); - isNestedCapturedVar = true; - break; - } - } - // captured nested byref variable has its address passed. Do not take - // its address again. - if (!isNestedCapturedVar) - Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, - Context->getPointerType(Exp->getType()), - VK_RValue, OK_Ordinary, SourceLocation()); - Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); - InitExprs.push_back(Exp); - } - } - if (ImportedBlockDecls.size()) { - // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR - int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); - unsigned IntSize = - static_cast(Context->getTypeSize(Context->IntTy)); - Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), - Context->IntTy, SourceLocation()); - InitExprs.push_back(FlagExp); - } - NewRep = new (Context) CallExpr(*Context, DRE, &InitExprs[0], InitExprs.size(), - FType, VK_LValue, SourceLocation()); - NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, - Context->getPointerType(NewRep->getType()), - VK_RValue, OK_Ordinary, SourceLocation()); - NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, - NewRep); - BlockDeclRefs.clear(); - BlockByRefDecls.clear(); - BlockByRefDeclsPtrSet.clear(); - BlockByCopyDecls.clear(); - BlockByCopyDeclsPtrSet.clear(); - ImportedBlockDecls.clear(); - return NewRep; -} - -bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { - if (const ObjCForCollectionStmt * CS = - dyn_cast(Stmts.back())) - return CS->getElement() == DS; - return false; -} - -//===----------------------------------------------------------------------===// -// Function Body / Expression rewriting -//===----------------------------------------------------------------------===// - -Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { - if (isa(S) || isa(S) || - isa(S) || isa(S)) - Stmts.push_back(S); - else if (isa(S)) { - Stmts.push_back(S); - ObjCBcLabelNo.push_back(++BcLabelCount); - } - - // Pseudo-object operations and ivar references need special - // treatment because we're going to recursively rewrite them. - if (PseudoObjectExpr *PseudoOp = dyn_cast(S)) { - if (isa(PseudoOp->getSyntacticForm())) { - return RewritePropertyOrImplicitSetter(PseudoOp); - } else { - return RewritePropertyOrImplicitGetter(PseudoOp); - } - } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast(S)) { - return RewriteObjCIvarRefExpr(IvarRefExpr); - } - - SourceRange OrigStmtRange = S->getSourceRange(); - - // Perform a bottom up rewrite of all children. - for (Stmt::child_range CI = S->children(); CI; ++CI) - if (*CI) { - Stmt *childStmt = (*CI); - Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); - if (newStmt) { - *CI = newStmt; - } - } - - if (BlockExpr *BE = dyn_cast(S)) { - SmallVector InnerBlockDeclRefs; - llvm::SmallPtrSet InnerContexts; - InnerContexts.insert(BE->getBlockDecl()); - ImportedLocalExternalDecls.clear(); - GetInnerBlockDeclRefExprs(BE->getBody(), - InnerBlockDeclRefs, InnerContexts); - // Rewrite the block body in place. - Stmt *SaveCurrentBody = CurrentBody; - CurrentBody = BE->getBody(); - PropParentMap = 0; - // block literal on rhs of a property-dot-sytax assignment - // must be replaced by its synthesize ast so getRewrittenText - // works as expected. In this case, what actually ends up on RHS - // is the blockTranscribed which is the helper function for the - // block literal; as in: self.c = ^() {[ace ARR];}; - bool saveDisableReplaceStmt = DisableReplaceStmt; - DisableReplaceStmt = false; - RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); - DisableReplaceStmt = saveDisableReplaceStmt; - CurrentBody = SaveCurrentBody; - PropParentMap = 0; - ImportedLocalExternalDecls.clear(); - // Now we snarf the rewritten text and stash it away for later use. - std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); - RewrittenBlockExprs[BE] = Str; - - Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); - - //blockTranscribed->dump(); - ReplaceStmt(S, blockTranscribed); - return blockTranscribed; - } - // Handle specific things. - if (ObjCEncodeExpr *AtEncode = dyn_cast(S)) - return RewriteAtEncode(AtEncode); - - if (ObjCSelectorExpr *AtSelector = dyn_cast(S)) - return RewriteAtSelector(AtSelector); - - if (ObjCStringLiteral *AtString = dyn_cast(S)) - return RewriteObjCStringLiteral(AtString); - - if (ObjCMessageExpr *MessExpr = dyn_cast(S)) { -#if 0 - // Before we rewrite it, put the original message expression in a comment. - SourceLocation startLoc = MessExpr->getLocStart(); - SourceLocation endLoc = MessExpr->getLocEnd(); - - const char *startBuf = SM->getCharacterData(startLoc); - const char *endBuf = SM->getCharacterData(endLoc); - - std::string messString; - messString += "// "; - messString.append(startBuf, endBuf-startBuf+1); - messString += "\n"; - - // FIXME: Missing definition of - // InsertText(clang::SourceLocation, char const*, unsigned int). - // InsertText(startLoc, messString.c_str(), messString.size()); - // Tried this, but it didn't work either... - // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); -#endif - return RewriteMessageExpr(MessExpr); - } - - if (ObjCAtTryStmt *StmtTry = dyn_cast(S)) - return RewriteObjCTryStmt(StmtTry); - - if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast(S)) - return RewriteObjCSynchronizedStmt(StmtTry); - - if (ObjCAtThrowStmt *StmtThrow = dyn_cast(S)) - return RewriteObjCThrowStmt(StmtThrow); - - if (ObjCProtocolExpr *ProtocolExp = dyn_cast(S)) - return RewriteObjCProtocolExpr(ProtocolExp); - - if (ObjCForCollectionStmt *StmtForCollection = - dyn_cast(S)) - return RewriteObjCForCollectionStmt(StmtForCollection, - OrigStmtRange.getEnd()); - if (BreakStmt *StmtBreakStmt = - dyn_cast(S)) - return RewriteBreakStmt(StmtBreakStmt); - if (ContinueStmt *StmtContinueStmt = - dyn_cast(S)) - return RewriteContinueStmt(StmtContinueStmt); - - // Need to check for protocol refs (id

, Foo

*) in variable decls - // and cast exprs. - if (DeclStmt *DS = dyn_cast(S)) { - // FIXME: What we're doing here is modifying the type-specifier that - // precedes the first Decl. In the future the DeclGroup should have - // a separate type-specifier that we can rewrite. - // NOTE: We need to avoid rewriting the DeclStmt if it is within - // the context of an ObjCForCollectionStmt. For example: - // NSArray *someArray; - // for (id index in someArray) ; - // This is because RewriteObjCForCollectionStmt() does textual rewriting - // and it depends on the original text locations/positions. - if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) - RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); - - // Blocks rewrite rules. - for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); - DI != DE; ++DI) { - Decl *SD = *DI; - if (ValueDecl *ND = dyn_cast(SD)) { - if (isTopLevelBlockPointerType(ND->getType())) - RewriteBlockPointerDecl(ND); - else if (ND->getType()->isFunctionPointerType()) - CheckFunctionPointerDecl(ND->getType(), ND); - if (VarDecl *VD = dyn_cast(SD)) { - if (VD->hasAttr()) { - static unsigned uniqueByrefDeclCount = 0; - assert(!BlockByRefDeclNo.count(ND) && - "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); - BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; - RewriteByRefVar(VD); - } - else - RewriteTypeOfDecl(VD); - } - } - if (TypedefNameDecl *TD = dyn_cast(SD)) { - if (isTopLevelBlockPointerType(TD->getUnderlyingType())) - RewriteBlockPointerDecl(TD); - else if (TD->getUnderlyingType()->isFunctionPointerType()) - CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); - } - } - } - - if (CStyleCastExpr *CE = dyn_cast(S)) - RewriteObjCQualifiedInterfaceTypes(CE); - - if (isa(S) || isa(S) || - isa(S) || isa(S)) { - assert(!Stmts.empty() && "Statement stack is empty"); - assert ((isa(Stmts.back()) || isa(Stmts.back()) || - isa(Stmts.back()) || isa(Stmts.back())) - && "Statement stack mismatch"); - Stmts.pop_back(); - } - // Handle blocks rewriting. - if (DeclRefExpr *DRE = dyn_cast(S)) { - ValueDecl *VD = DRE->getDecl(); - if (VD->hasAttr()) - return RewriteBlockDeclRefExpr(DRE); - if (HasLocalVariableExternalStorage(VD)) - return RewriteLocalVariableExternalStorage(DRE); - } - - if (CallExpr *CE = dyn_cast(S)) { - if (CE->getCallee()->getType()->isBlockPointerType()) { - Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); - ReplaceStmt(S, BlockCall); - return BlockCall; - } - } - if (CStyleCastExpr *CE = dyn_cast(S)) { - RewriteCastExpr(CE); - } -#if 0 - if (ImplicitCastExpr *ICE = dyn_cast(S)) { - CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), - ICE->getSubExpr(), - SourceLocation()); - // Get the new text. - std::string SStr; - llvm::raw_string_ostream Buf(SStr); - Replacement->printPretty(Buf); - const std::string &Str = Buf.str(); - - printf("CAST = %s\n", &Str[0]); - InsertText(ICE->getSubExpr()->getLocStart(), &Str[0], Str.size()); - delete S; - return Replacement; - } -#endif - // Return this stmt unmodified. - return S; -} - -void RewriteObjC::RewriteRecordBody(RecordDecl *RD) { - for (RecordDecl::field_iterator i = RD->field_begin(), - e = RD->field_end(); i != e; ++i) { - FieldDecl *FD = *i; - if (isTopLevelBlockPointerType(FD->getType())) - RewriteBlockPointerDecl(FD); - if (FD->getType()->isObjCQualifiedIdType() || - FD->getType()->isObjCQualifiedInterfaceType()) - RewriteObjCQualifiedInterfaceTypes(FD); - } -} - -/// HandleDeclInMainFile - This is called for each top-level decl defined in the -/// main file of the input. -void RewriteObjC::HandleDeclInMainFile(Decl *D) { - switch (D->getKind()) { - case Decl::Function: { - FunctionDecl *FD = cast(D); - if (FD->isOverloadedOperator()) - return; - - // Since function prototypes don't have ParmDecl's, we check the function - // prototype. This enables us to rewrite function declarations and - // definitions using the same code. - RewriteBlocksInFunctionProtoType(FD->getType(), FD); - - if (!FD->isThisDeclarationADefinition()) - break; - - // FIXME: If this should support Obj-C++, support CXXTryStmt - if (CompoundStmt *Body = dyn_cast_or_null(FD->getBody())) { - CurFunctionDef = FD; - CurFunctionDeclToDeclareForBlock = FD; - CurrentBody = Body; - Body = - cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); - FD->setBody(Body); - CurrentBody = 0; - if (PropParentMap) { - delete PropParentMap; - PropParentMap = 0; - } - // This synthesizes and inserts the block "impl" struct, invoke function, - // and any copy/dispose helper functions. - InsertBlockLiteralsWithinFunction(FD); - CurFunctionDef = 0; - CurFunctionDeclToDeclareForBlock = 0; - } - break; - } - case Decl::ObjCMethod: { - ObjCMethodDecl *MD = cast(D); - if (CompoundStmt *Body = MD->getCompoundBody()) { - CurMethodDef = MD; - CurrentBody = Body; - Body = - cast_or_null(RewriteFunctionBodyOrGlobalInitializer(Body)); - MD->setBody(Body); - CurrentBody = 0; - if (PropParentMap) { - delete PropParentMap; - PropParentMap = 0; - } - InsertBlockLiteralsWithinMethod(MD); - CurMethodDef = 0; - } - break; - } - case Decl::ObjCImplementation: { - ObjCImplementationDecl *CI = cast(D); - ClassImplementation.push_back(CI); - break; - } - case Decl::ObjCCategoryImpl: { - ObjCCategoryImplDecl *CI = cast(D); - CategoryImplementation.push_back(CI); - break; - } - case Decl::Var: { - VarDecl *VD = cast(D); - RewriteObjCQualifiedInterfaceTypes(VD); - if (isTopLevelBlockPointerType(VD->getType())) - RewriteBlockPointerDecl(VD); - else if (VD->getType()->isFunctionPointerType()) { - CheckFunctionPointerDecl(VD->getType(), VD); - if (VD->getInit()) { - if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { - RewriteCastExpr(CE); - } - } - } else if (VD->getType()->isRecordType()) { - RecordDecl *RD = VD->getType()->getAs()->getDecl(); - if (RD->isCompleteDefinition()) - RewriteRecordBody(RD); - } - if (VD->getInit()) { - GlobalVarDecl = VD; - CurrentBody = VD->getInit(); - RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); - CurrentBody = 0; - if (PropParentMap) { - delete PropParentMap; - PropParentMap = 0; - } - SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); - GlobalVarDecl = 0; - - // This is needed for blocks. - if (CStyleCastExpr *CE = dyn_cast(VD->getInit())) { - RewriteCastExpr(CE); - } - } - break; - } - case Decl::TypeAlias: - case Decl::Typedef: { - if (TypedefNameDecl *TD = dyn_cast(D)) { - if (isTopLevelBlockPointerType(TD->getUnderlyingType())) - RewriteBlockPointerDecl(TD); - else if (TD->getUnderlyingType()->isFunctionPointerType()) - CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); - } - break; - } - case Decl::CXXRecord: - case Decl::Record: { - RecordDecl *RD = cast(D); - if (RD->isCompleteDefinition()) - RewriteRecordBody(RD); - break; - } - default: - break; - } - // Nothing yet. -} - -void RewriteObjC::HandleTranslationUnit(ASTContext &C) { - if (Diags.hasErrorOccurred()) - return; - - RewriteInclude(); - - // Here's a great place to add any extra declarations that may be needed. - // Write out meta data for each @protocol(). - for (llvm::SmallPtrSet::iterator I = ProtocolExprDecls.begin(), - E = ProtocolExprDecls.end(); I != E; ++I) - RewriteObjCProtocolMetaData(*I, "", "", Preamble); - - InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); - if (ClassImplementation.size() || CategoryImplementation.size()) - RewriteImplementations(); - - // Get the buffer corresponding to MainFileID. If we haven't changed it, then - // we are done. - if (const RewriteBuffer *RewriteBuf = - Rewrite.getRewriteBufferFor(MainFileID)) { - //printf("Changed:\n"); - *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); - } else { - llvm::errs() << "No changes\n"; - } - - if (ClassImplementation.size() || CategoryImplementation.size() || - ProtocolExprDecls.size()) { - // Rewrite Objective-c meta data* - std::string ResultStr; - RewriteMetaDataIntoBuffer(ResultStr); - // Emit metadata. - *OutFile << ResultStr; - } - OutFile->flush(); -} - -void RewriteObjCFragileABI::Initialize(ASTContext &context) { - InitializeCommon(context); - - // declaring objc_selector outside the parameter list removes a silly - // scope related warning... - if (IsHeader) - Preamble = "#pragma once\n"; - Preamble += "struct objc_selector; struct objc_class;\n"; - Preamble += "struct __rw_objc_super { struct objc_object *object; "; - Preamble += "struct objc_object *superClass; "; - if (LangOpts.MicrosoftExt) { - // Add a constructor for creating temporary objects. - Preamble += "__rw_objc_super(struct objc_object *o, struct objc_object *s) " - ": "; - Preamble += "object(o), superClass(s) {} "; - } - Preamble += "};\n"; - Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; - Preamble += "typedef struct objc_object Protocol;\n"; - Preamble += "#define _REWRITER_typedef_Protocol\n"; - Preamble += "#endif\n"; - if (LangOpts.MicrosoftExt) { - Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; - Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; - } else - Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend"; - Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper"; - Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object* objc_msgSend_stret"; - Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object* objc_msgSendSuper_stret"; - Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; - Preamble += "__OBJC_RW_DLLIMPORT double objc_msgSend_fpret"; - Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass"; - Preamble += "(const char *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; - Preamble += "(struct objc_class *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass"; - Preamble += "(const char *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw(struct objc_object *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_enter(void *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_exit(void *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_exception_extract(void *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT int objc_exception_match"; - Preamble += "(struct objc_class *, struct objc_object *);\n"; - // @synchronized hooks. - Preamble += "__OBJC_RW_DLLIMPORT void objc_sync_enter(struct objc_object *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_sync_exit(struct objc_object *);\n"; - Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; - Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; - Preamble += "struct __objcFastEnumerationState {\n\t"; - Preamble += "unsigned long state;\n\t"; - Preamble += "void **itemsPtr;\n\t"; - Preamble += "unsigned long *mutationsPtr;\n\t"; - Preamble += "unsigned long extra[5];\n};\n"; - Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; - Preamble += "#define __FASTENUMERATIONSTATE\n"; - Preamble += "#endif\n"; - Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; - Preamble += "struct __NSConstantStringImpl {\n"; - Preamble += " int *isa;\n"; - Preamble += " int flags;\n"; - Preamble += " char *str;\n"; - Preamble += " long length;\n"; - Preamble += "};\n"; - Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; - Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; - Preamble += "#else\n"; - Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; - Preamble += "#endif\n"; - Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; - Preamble += "#endif\n"; - // Blocks preamble. - Preamble += "#ifndef BLOCK_IMPL\n"; - Preamble += "#define BLOCK_IMPL\n"; - Preamble += "struct __block_impl {\n"; - Preamble += " void *isa;\n"; - Preamble += " int Flags;\n"; - Preamble += " int Reserved;\n"; - Preamble += " void *FuncPtr;\n"; - Preamble += "};\n"; - Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; - Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; - Preamble += "extern \"C\" __declspec(dllexport) " - "void _Block_object_assign(void *, const void *, const int);\n"; - Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; - Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; - Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; - Preamble += "#else\n"; - Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; - Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; - Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; - Preamble += "#endif\n"; - Preamble += "#endif\n"; - if (LangOpts.MicrosoftExt) { - Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; - Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; - Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. - Preamble += "#define __attribute__(X)\n"; - Preamble += "#endif\n"; - Preamble += "#define __weak\n"; - } - else { - Preamble += "#define __block\n"; - Preamble += "#define __weak\n"; - } - // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long - // as this avoids warning in any 64bit/32bit compilation model. - Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; -} - -/// RewriteIvarOffsetComputation - This rutine synthesizes computation of -/// ivar offset. -void RewriteObjCFragileABI::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, - std::string &Result) { - if (ivar->isBitField()) { - // FIXME: The hack below doesn't work for bitfields. For now, we simply - // place all bitfields at offset 0. - Result += "0"; - } else { - Result += "__OFFSETOFIVAR__(struct "; - Result += ivar->getContainingInterface()->getNameAsString(); - if (LangOpts.MicrosoftExt) - Result += "_IMPL"; - Result += ", "; - Result += ivar->getNameAsString(); - Result += ")"; - } -} - -/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. -void RewriteObjCFragileABI::RewriteObjCProtocolMetaData( - ObjCProtocolDecl *PDecl, StringRef prefix, - StringRef ClassName, std::string &Result) { - static bool objc_protocol_methods = false; - - // Output struct protocol_methods holder of method selector and type. - if (!objc_protocol_methods && PDecl->hasDefinition()) { - /* struct protocol_methods { - SEL _cmd; - char *method_types; - } - */ - Result += "\nstruct _protocol_methods {\n"; - Result += "\tstruct objc_selector *_cmd;\n"; - Result += "\tchar *method_types;\n"; - Result += "};\n"; - - objc_protocol_methods = true; - } - // Do not synthesize the protocol more than once. - if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) - return; - - if (ObjCProtocolDecl *Def = PDecl->getDefinition()) - PDecl = Def; - - if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { - unsigned NumMethods = std::distance(PDecl->instmeth_begin(), - PDecl->instmeth_end()); - /* struct _objc_protocol_method_list { - int protocol_method_count; - struct protocol_methods protocols[]; - } - */ - Result += "\nstatic struct {\n"; - Result += "\tint protocol_method_count;\n"; - Result += "\tstruct _protocol_methods protocol_methods["; - Result += utostr(NumMethods); - Result += "];\n} _OBJC_PROTOCOL_INSTANCE_METHODS_"; - Result += PDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__OBJC, __cat_inst_meth\")))= " - "{\n\t" + utostr(NumMethods) + "\n"; - - // Output instance methods declared in this protocol. - for (ObjCProtocolDecl::instmeth_iterator - I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); - I != E; ++I) { - if (I == PDecl->instmeth_begin()) - Result += "\t ,{{(struct objc_selector *)\""; - else - Result += "\t ,{(struct objc_selector *)\""; - Result += (*I)->getSelector().getAsString(); - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl((*I), MethodTypeString); - Result += "\", \""; - Result += MethodTypeString; - Result += "\"}\n"; - } - Result += "\t }\n};\n"; - } - - // Output class methods declared in this protocol. - unsigned NumMethods = std::distance(PDecl->classmeth_begin(), - PDecl->classmeth_end()); - if (NumMethods > 0) { - /* struct _objc_protocol_method_list { - int protocol_method_count; - struct protocol_methods protocols[]; - } - */ - Result += "\nstatic struct {\n"; - Result += "\tint protocol_method_count;\n"; - Result += "\tstruct _protocol_methods protocol_methods["; - Result += utostr(NumMethods); - Result += "];\n} _OBJC_PROTOCOL_CLASS_METHODS_"; - Result += PDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " - "{\n\t"; - Result += utostr(NumMethods); - Result += "\n"; - - // Output instance methods declared in this protocol. - for (ObjCProtocolDecl::classmeth_iterator - I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); - I != E; ++I) { - if (I == PDecl->classmeth_begin()) - Result += "\t ,{{(struct objc_selector *)\""; - else - Result += "\t ,{(struct objc_selector *)\""; - Result += (*I)->getSelector().getAsString(); - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl((*I), MethodTypeString); - Result += "\", \""; - Result += MethodTypeString; - Result += "\"}\n"; - } - Result += "\t }\n};\n"; - } - - // Output: - /* struct _objc_protocol { - // Objective-C 1.0 extensions - struct _objc_protocol_extension *isa; - char *protocol_name; - struct _objc_protocol **protocol_list; - struct _objc_protocol_method_list *instance_methods; - struct _objc_protocol_method_list *class_methods; - }; - */ - static bool objc_protocol = false; - if (!objc_protocol) { - Result += "\nstruct _objc_protocol {\n"; - Result += "\tstruct _objc_protocol_extension *isa;\n"; - Result += "\tchar *protocol_name;\n"; - Result += "\tstruct _objc_protocol **protocol_list;\n"; - Result += "\tstruct _objc_protocol_method_list *instance_methods;\n"; - Result += "\tstruct _objc_protocol_method_list *class_methods;\n"; - Result += "};\n"; - - objc_protocol = true; - } - - Result += "\nstatic struct _objc_protocol _OBJC_PROTOCOL_"; - Result += PDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__OBJC, __protocol\")))= " - "{\n\t0, \""; - Result += PDecl->getNameAsString(); - Result += "\", 0, "; - if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { - Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; - Result += PDecl->getNameAsString(); - Result += ", "; - } - else - Result += "0, "; - if (PDecl->classmeth_begin() != PDecl->classmeth_end()) { - Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_CLASS_METHODS_"; - Result += PDecl->getNameAsString(); - Result += "\n"; - } - else - Result += "0\n"; - Result += "};\n"; - - // Mark this protocol as having been generated. - if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl())) - llvm_unreachable("protocol already synthesized"); - -} - -void RewriteObjCFragileABI::RewriteObjCProtocolListMetaData( - const ObjCList &Protocols, - StringRef prefix, StringRef ClassName, - std::string &Result) { - if (Protocols.empty()) return; - - for (unsigned i = 0; i != Protocols.size(); i++) - RewriteObjCProtocolMetaData(Protocols[i], prefix, ClassName, Result); - - // Output the top lovel protocol meta-data for the class. - /* struct _objc_protocol_list { - struct _objc_protocol_list *next; - int protocol_count; - struct _objc_protocol *class_protocols[]; - } - */ - Result += "\nstatic struct {\n"; - Result += "\tstruct _objc_protocol_list *next;\n"; - Result += "\tint protocol_count;\n"; - Result += "\tstruct _objc_protocol *class_protocols["; - Result += utostr(Protocols.size()); - Result += "];\n} _OBJC_"; - Result += prefix; - Result += "_PROTOCOLS_"; - Result += ClassName; - Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " - "{\n\t0, "; - Result += utostr(Protocols.size()); - Result += "\n"; - - Result += "\t,{&_OBJC_PROTOCOL_"; - Result += Protocols[0]->getNameAsString(); - Result += " \n"; - - for (unsigned i = 1; i != Protocols.size(); i++) { - Result += "\t ,&_OBJC_PROTOCOL_"; - Result += Protocols[i]->getNameAsString(); - Result += "\n"; - } - Result += "\t }\n};\n"; -} - -void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, - std::string &Result) { - ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); - - // Explicitly declared @interface's are already synthesized. - if (CDecl->isImplicitInterfaceDecl()) { - // FIXME: Implementation of a class with no @interface (legacy) does not - // produce correct synthesis as yet. - RewriteObjCInternalStruct(CDecl, Result); - } - - // Build _objc_ivar_list metadata for classes ivars if needed - unsigned NumIvars = !IDecl->ivar_empty() - ? IDecl->ivar_size() - : (CDecl ? CDecl->ivar_size() : 0); - if (NumIvars > 0) { - static bool objc_ivar = false; - if (!objc_ivar) { - /* struct _objc_ivar { - char *ivar_name; - char *ivar_type; - int ivar_offset; - }; - */ - Result += "\nstruct _objc_ivar {\n"; - Result += "\tchar *ivar_name;\n"; - Result += "\tchar *ivar_type;\n"; - Result += "\tint ivar_offset;\n"; - Result += "};\n"; - - objc_ivar = true; - } - - /* struct { - int ivar_count; - struct _objc_ivar ivar_list[nIvars]; - }; - */ - Result += "\nstatic struct {\n"; - Result += "\tint ivar_count;\n"; - Result += "\tstruct _objc_ivar ivar_list["; - Result += utostr(NumIvars); - Result += "];\n} _OBJC_INSTANCE_VARIABLES_"; - Result += IDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__OBJC, __instance_vars\")))= " - "{\n\t"; - Result += utostr(NumIvars); - Result += "\n"; - - ObjCInterfaceDecl::ivar_iterator IVI, IVE; - SmallVector IVars; - if (!IDecl->ivar_empty()) { - for (ObjCInterfaceDecl::ivar_iterator - IV = IDecl->ivar_begin(), IVEnd = IDecl->ivar_end(); - IV != IVEnd; ++IV) - IVars.push_back(*IV); - IVI = IDecl->ivar_begin(); - IVE = IDecl->ivar_end(); - } else { - IVI = CDecl->ivar_begin(); - IVE = CDecl->ivar_end(); - } - Result += "\t,{{\""; - Result += IVI->getNameAsString(); - Result += "\", \""; - std::string TmpString, StrEncoding; - Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); - QuoteDoublequotes(TmpString, StrEncoding); - Result += StrEncoding; - Result += "\", "; - RewriteIvarOffsetComputation(*IVI, Result); - Result += "}\n"; - for (++IVI; IVI != IVE; ++IVI) { - Result += "\t ,{\""; - Result += IVI->getNameAsString(); - Result += "\", \""; - std::string TmpString, StrEncoding; - Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); - QuoteDoublequotes(TmpString, StrEncoding); - Result += StrEncoding; - Result += "\", "; - RewriteIvarOffsetComputation(*IVI, Result); - Result += "}\n"; - } - - Result += "\t }\n};\n"; - } - - // Build _objc_method_list for class's instance methods if needed - SmallVector - InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); - - // If any of our property implementations have associated getters or - // setters, produce metadata for them as well. - for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), - PropEnd = IDecl->propimpl_end(); - Prop != PropEnd; ++Prop) { - if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - continue; - if (!Prop->getPropertyIvarDecl()) - continue; - ObjCPropertyDecl *PD = Prop->getPropertyDecl(); - if (!PD) - continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - if (!Getter->isDefined()) - InstanceMethods.push_back(Getter); - if (PD->isReadOnly()) - continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) - if (!Setter->isDefined()) - InstanceMethods.push_back(Setter); - } - RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), - true, "", IDecl->getName(), Result); - - // Build _objc_method_list for class's class methods if needed - RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), - false, "", IDecl->getName(), Result); - - // Protocols referenced in class declaration? - RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), - "CLASS", CDecl->getName(), Result); - - // Declaration of class/meta-class metadata - /* struct _objc_class { - struct _objc_class *isa; // or const char *root_class_name when metadata - const char *super_class_name; - char *name; - long version; - long info; - long instance_size; - struct _objc_ivar_list *ivars; - struct _objc_method_list *methods; - struct objc_cache *cache; - struct objc_protocol_list *protocols; - const char *ivar_layout; - struct _objc_class_ext *ext; - }; - */ - static bool objc_class = false; - if (!objc_class) { - Result += "\nstruct _objc_class {\n"; - Result += "\tstruct _objc_class *isa;\n"; - Result += "\tconst char *super_class_name;\n"; - Result += "\tchar *name;\n"; - Result += "\tlong version;\n"; - Result += "\tlong info;\n"; - Result += "\tlong instance_size;\n"; - Result += "\tstruct _objc_ivar_list *ivars;\n"; - Result += "\tstruct _objc_method_list *methods;\n"; - Result += "\tstruct objc_cache *cache;\n"; - Result += "\tstruct _objc_protocol_list *protocols;\n"; - Result += "\tconst char *ivar_layout;\n"; - Result += "\tstruct _objc_class_ext *ext;\n"; - Result += "};\n"; - objc_class = true; - } - - // Meta-class metadata generation. - ObjCInterfaceDecl *RootClass = 0; - ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); - while (SuperClass) { - RootClass = SuperClass; - SuperClass = SuperClass->getSuperClass(); - } - SuperClass = CDecl->getSuperClass(); - - Result += "\nstatic struct _objc_class _OBJC_METACLASS_"; - Result += CDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__OBJC, __meta_class\")))= " - "{\n\t(struct _objc_class *)\""; - Result += (RootClass ? RootClass->getNameAsString() : CDecl->getNameAsString()); - Result += "\""; - - if (SuperClass) { - Result += ", \""; - Result += SuperClass->getNameAsString(); - Result += "\", \""; - Result += CDecl->getNameAsString(); - Result += "\""; - } - else { - Result += ", 0, \""; - Result += CDecl->getNameAsString(); - Result += "\""; - } - // Set 'ivars' field for root class to 0. ObjC1 runtime does not use it. - // 'info' field is initialized to CLS_META(2) for metaclass - Result += ", 0,2, sizeof(struct _objc_class), 0"; - if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { - Result += "\n\t, (struct _objc_method_list *)&_OBJC_CLASS_METHODS_"; - Result += IDecl->getNameAsString(); - Result += "\n"; - } - else - Result += ", 0\n"; - if (CDecl->protocol_begin() != CDecl->protocol_end()) { - Result += "\t,0, (struct _objc_protocol_list *)&_OBJC_CLASS_PROTOCOLS_"; - Result += CDecl->getNameAsString(); - Result += ",0,0\n"; - } - else - Result += "\t,0,0,0,0\n"; - Result += "};\n"; - - // class metadata generation. - Result += "\nstatic struct _objc_class _OBJC_CLASS_"; - Result += CDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__OBJC, __class\")))= " - "{\n\t&_OBJC_METACLASS_"; - Result += CDecl->getNameAsString(); - if (SuperClass) { - Result += ", \""; - Result += SuperClass->getNameAsString(); - Result += "\", \""; - Result += CDecl->getNameAsString(); - Result += "\""; - } - else { - Result += ", 0, \""; - Result += CDecl->getNameAsString(); - Result += "\""; - } - // 'info' field is initialized to CLS_CLASS(1) for class - Result += ", 0,1"; - if (!ObjCSynthesizedStructs.count(CDecl)) - Result += ",0"; - else { - // class has size. Must synthesize its size. - Result += ",sizeof(struct "; - Result += CDecl->getNameAsString(); - if (LangOpts.MicrosoftExt) - Result += "_IMPL"; - Result += ")"; - } - if (NumIvars > 0) { - Result += ", (struct _objc_ivar_list *)&_OBJC_INSTANCE_VARIABLES_"; - Result += CDecl->getNameAsString(); - Result += "\n\t"; - } - else - Result += ",0"; - if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { - Result += ", (struct _objc_method_list *)&_OBJC_INSTANCE_METHODS_"; - Result += CDecl->getNameAsString(); - Result += ", 0\n\t"; - } - else - Result += ",0,0"; - if (CDecl->protocol_begin() != CDecl->protocol_end()) { - Result += ", (struct _objc_protocol_list*)&_OBJC_CLASS_PROTOCOLS_"; - Result += CDecl->getNameAsString(); - Result += ", 0,0\n"; - } - else - Result += ",0,0,0\n"; - Result += "};\n"; -} - -void RewriteObjCFragileABI::RewriteMetaDataIntoBuffer(std::string &Result) { - int ClsDefCount = ClassImplementation.size(); - int CatDefCount = CategoryImplementation.size(); - - // For each implemented class, write out all its meta data. - for (int i = 0; i < ClsDefCount; i++) - RewriteObjCClassMetaData(ClassImplementation[i], Result); - - // For each implemented category, write out all its meta data. - for (int i = 0; i < CatDefCount; i++) - RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); - - // Write objc_symtab metadata - /* - struct _objc_symtab - { - long sel_ref_cnt; - SEL *refs; - short cls_def_cnt; - short cat_def_cnt; - void *defs[cls_def_cnt + cat_def_cnt]; - }; - */ - - Result += "\nstruct _objc_symtab {\n"; - Result += "\tlong sel_ref_cnt;\n"; - Result += "\tSEL *refs;\n"; - Result += "\tshort cls_def_cnt;\n"; - Result += "\tshort cat_def_cnt;\n"; - Result += "\tvoid *defs[" + utostr(ClsDefCount + CatDefCount)+ "];\n"; - Result += "};\n\n"; - - Result += "static struct _objc_symtab " - "_OBJC_SYMBOLS __attribute__((used, section (\"__OBJC, __symbols\")))= {\n"; - Result += "\t0, 0, " + utostr(ClsDefCount) - + ", " + utostr(CatDefCount) + "\n"; - for (int i = 0; i < ClsDefCount; i++) { - Result += "\t,&_OBJC_CLASS_"; - Result += ClassImplementation[i]->getNameAsString(); - Result += "\n"; - } - - for (int i = 0; i < CatDefCount; i++) { - Result += "\t,&_OBJC_CATEGORY_"; - Result += CategoryImplementation[i]->getClassInterface()->getNameAsString(); - Result += "_"; - Result += CategoryImplementation[i]->getNameAsString(); - Result += "\n"; - } - - Result += "};\n\n"; - - // Write objc_module metadata - - /* - struct _objc_module { - long version; - long size; - const char *name; - struct _objc_symtab *symtab; - } - */ - - Result += "\nstruct _objc_module {\n"; - Result += "\tlong version;\n"; - Result += "\tlong size;\n"; - Result += "\tconst char *name;\n"; - Result += "\tstruct _objc_symtab *symtab;\n"; - Result += "};\n\n"; - Result += "static struct _objc_module " - "_OBJC_MODULES __attribute__ ((used, section (\"__OBJC, __module_info\")))= {\n"; - Result += "\t" + utostr(OBJC_ABI_VERSION) + - ", sizeof(struct _objc_module), \"\", &_OBJC_SYMBOLS\n"; - Result += "};\n\n"; - - if (LangOpts.MicrosoftExt) { - if (ProtocolExprDecls.size()) { - Result += "#pragma section(\".objc_protocol$B\",long,read,write)\n"; - Result += "#pragma data_seg(push, \".objc_protocol$B\")\n"; - for (llvm::SmallPtrSet::iterator I = ProtocolExprDecls.begin(), - E = ProtocolExprDecls.end(); I != E; ++I) { - Result += "static struct _objc_protocol *_POINTER_OBJC_PROTOCOL_"; - Result += (*I)->getNameAsString(); - Result += " = &_OBJC_PROTOCOL_"; - Result += (*I)->getNameAsString(); - Result += ";\n"; - } - Result += "#pragma data_seg(pop)\n\n"; - } - Result += "#pragma section(\".objc_module_info$B\",long,read,write)\n"; - Result += "#pragma data_seg(push, \".objc_module_info$B\")\n"; - Result += "static struct _objc_module *_POINTER_OBJC_MODULES = "; - Result += "&_OBJC_MODULES;\n"; - Result += "#pragma data_seg(pop)\n\n"; - } -} - -/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category -/// implementation. -void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, - std::string &Result) { - ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); - // Find category declaration for this implementation. - ObjCCategoryDecl *CDecl; - for (CDecl = ClassDecl->getCategoryList(); CDecl; - CDecl = CDecl->getNextClassCategory()) - if (CDecl->getIdentifier() == IDecl->getIdentifier()) - break; - - std::string FullCategoryName = ClassDecl->getNameAsString(); - FullCategoryName += '_'; - FullCategoryName += IDecl->getNameAsString(); - - // Build _objc_method_list for class's instance methods if needed - SmallVector - InstanceMethods(IDecl->instmeth_begin(), IDecl->instmeth_end()); - - // If any of our property implementations have associated getters or - // setters, produce metadata for them as well. - for (ObjCImplDecl::propimpl_iterator Prop = IDecl->propimpl_begin(), - PropEnd = IDecl->propimpl_end(); - Prop != PropEnd; ++Prop) { - if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - continue; - if (!Prop->getPropertyIvarDecl()) - continue; - ObjCPropertyDecl *PD = Prop->getPropertyDecl(); - if (!PD) - continue; - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - InstanceMethods.push_back(Getter); - if (PD->isReadOnly()) - continue; - if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) - InstanceMethods.push_back(Setter); - } - RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), - true, "CATEGORY_", FullCategoryName.c_str(), - Result); - - // Build _objc_method_list for class's class methods if needed - RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), - false, "CATEGORY_", FullCategoryName.c_str(), - Result); - - // Protocols referenced in class declaration? - // Null CDecl is case of a category implementation with no category interface - if (CDecl) - RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), "CATEGORY", - FullCategoryName, Result); - /* struct _objc_category { - char *category_name; - char *class_name; - struct _objc_method_list *instance_methods; - struct _objc_method_list *class_methods; - struct _objc_protocol_list *protocols; - // Objective-C 1.0 extensions - uint32_t size; // sizeof (struct _objc_category) - struct _objc_property_list *instance_properties; // category's own - // @property decl. - }; - */ - - static bool objc_category = false; - if (!objc_category) { - Result += "\nstruct _objc_category {\n"; - Result += "\tchar *category_name;\n"; - Result += "\tchar *class_name;\n"; - Result += "\tstruct _objc_method_list *instance_methods;\n"; - Result += "\tstruct _objc_method_list *class_methods;\n"; - Result += "\tstruct _objc_protocol_list *protocols;\n"; - Result += "\tunsigned int size;\n"; - Result += "\tstruct _objc_property_list *instance_properties;\n"; - Result += "};\n"; - objc_category = true; - } - Result += "\nstatic struct _objc_category _OBJC_CATEGORY_"; - Result += FullCategoryName; - Result += " __attribute__ ((used, section (\"__OBJC, __category\")))= {\n\t\""; - Result += IDecl->getNameAsString(); - Result += "\"\n\t, \""; - Result += ClassDecl->getNameAsString(); - Result += "\"\n"; - - if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { - Result += "\t, (struct _objc_method_list *)" - "&_OBJC_CATEGORY_INSTANCE_METHODS_"; - Result += FullCategoryName; - Result += "\n"; - } - else - Result += "\t, 0\n"; - if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { - Result += "\t, (struct _objc_method_list *)" - "&_OBJC_CATEGORY_CLASS_METHODS_"; - Result += FullCategoryName; - Result += "\n"; - } - else - Result += "\t, 0\n"; - - if (CDecl && CDecl->protocol_begin() != CDecl->protocol_end()) { - Result += "\t, (struct _objc_protocol_list *)&_OBJC_CATEGORY_PROTOCOLS_"; - Result += FullCategoryName; - Result += "\n"; - } - else - Result += "\t, 0\n"; - Result += "\t, sizeof(struct _objc_category), 0\n};\n"; -} - -// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or -/// class methods. -template -void RewriteObjCFragileABI::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, - MethodIterator MethodEnd, - bool IsInstanceMethod, - StringRef prefix, - StringRef ClassName, - std::string &Result) { - if (MethodBegin == MethodEnd) return; - - if (!objc_impl_method) { - /* struct _objc_method { - SEL _cmd; - char *method_types; - void *_imp; - } - */ - Result += "\nstruct _objc_method {\n"; - Result += "\tSEL _cmd;\n"; - Result += "\tchar *method_types;\n"; - Result += "\tvoid *_imp;\n"; - Result += "};\n"; - - objc_impl_method = true; - } - - // Build _objc_method_list for class's methods if needed - - /* struct { - struct _objc_method_list *next_method; - int method_count; - struct _objc_method method_list[]; - } - */ - unsigned NumMethods = std::distance(MethodBegin, MethodEnd); - Result += "\nstatic struct {\n"; - Result += "\tstruct _objc_method_list *next_method;\n"; - Result += "\tint method_count;\n"; - Result += "\tstruct _objc_method method_list["; - Result += utostr(NumMethods); - Result += "];\n} _OBJC_"; - Result += prefix; - Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; - Result += "_METHODS_"; - Result += ClassName; - Result += " __attribute__ ((used, section (\"__OBJC, __"; - Result += IsInstanceMethod ? "inst" : "cls"; - Result += "_meth\")))= "; - Result += "{\n\t0, " + utostr(NumMethods) + "\n"; - - Result += "\t,{{(SEL)\""; - Result += (*MethodBegin)->getSelector().getAsString().c_str(); - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); - Result += "\", \""; - Result += MethodTypeString; - Result += "\", (void *)"; - Result += MethodInternalNames[*MethodBegin]; - Result += "}\n"; - for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { - Result += "\t ,{(SEL)\""; - Result += (*MethodBegin)->getSelector().getAsString().c_str(); - std::string MethodTypeString; - Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); - Result += "\", \""; - Result += MethodTypeString; - Result += "\", (void *)"; - Result += MethodInternalNames[*MethodBegin]; - Result += "}\n"; - } - Result += "\t }\n};\n"; -} - -Stmt *RewriteObjCFragileABI::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { - SourceRange OldRange = IV->getSourceRange(); - Expr *BaseExpr = IV->getBase(); - - // Rewrite the base, but without actually doing replaces. - { - DisableReplaceStmtScope S(*this); - BaseExpr = cast(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); - IV->setBase(BaseExpr); - } - - ObjCIvarDecl *D = IV->getDecl(); - - Expr *Replacement = IV; - if (CurMethodDef) { - if (BaseExpr->getType()->isObjCObjectPointerType()) { - const ObjCInterfaceType *iFaceDecl = - dyn_cast(BaseExpr->getType()->getPointeeType()); - assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); - // lookup which class implements the instance variable. - ObjCInterfaceDecl *clsDeclared = 0; - iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), - clsDeclared); - assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); - - // Synthesize an explicit cast to gain access to the ivar. - std::string RecName = clsDeclared->getIdentifier()->getName(); - RecName += "_IMPL"; - IdentifierInfo *II = &Context->Idents.get(RecName); - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - II); - assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); - QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, - CK_BitCast, - IV->getBase()); - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(), - OldRange.getEnd(), - castExpr); - if (IV->isFreeIvar() && - declaresSameEntity(CurMethodDef->getClassInterface(), iFaceDecl->getDecl())) { - MemberExpr *ME = new (Context) MemberExpr(PE, true, D, - IV->getLocation(), - D->getType(), - VK_LValue, OK_Ordinary); - Replacement = ME; - } else { - IV->setBase(PE); - } - } - } else { // we are outside a method. - assert(!IV->isFreeIvar() && "Cannot have a free standing ivar outside a method"); - - // Explicit ivar refs need to have a cast inserted. - // FIXME: consider sharing some of this code with the code above. - if (BaseExpr->getType()->isObjCObjectPointerType()) { - const ObjCInterfaceType *iFaceDecl = - dyn_cast(BaseExpr->getType()->getPointeeType()); - // lookup which class implements the instance variable. - ObjCInterfaceDecl *clsDeclared = 0; - iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), - clsDeclared); - assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); - - // Synthesize an explicit cast to gain access to the ivar. - std::string RecName = clsDeclared->getIdentifier()->getName(); - RecName += "_IMPL"; - IdentifierInfo *II = &Context->Idents.get(RecName); - RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, - SourceLocation(), SourceLocation(), - II); - assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); - QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); - CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, - CK_BitCast, - IV->getBase()); - // Don't forget the parens to enforce the proper binding. - ParenExpr *PE = new (Context) ParenExpr(IV->getBase()->getLocStart(), - IV->getBase()->getLocEnd(), castExpr); - // Cannot delete IV->getBase(), since PE points to it. - // Replace the old base with the cast. This is important when doing - // embedded rewrites. For example, [newInv->_container addObject:0]. - IV->setBase(PE); - } - } - - ReplaceStmtWithRange(IV, Replacement, OldRange); - return Replacement; -} diff --git a/lib/Rewrite/RewriteRope.cpp b/lib/Rewrite/RewriteRope.cpp deleted file mode 100644 index cc8de1b11a18b..0000000000000 --- a/lib/Rewrite/RewriteRope.cpp +++ /dev/null @@ -1,811 +0,0 @@ -//===--- RewriteRope.cpp - Rope specialized for rewriter --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the RewriteRope class, which is a powerful string. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/RewriteRope.h" -#include "clang/Basic/LLVM.h" -#include -using namespace clang; - -/// RewriteRope is a "strong" string class, designed to make insertions and -/// deletions in the middle of the string nearly constant time (really, they are -/// O(log N), but with a very low constant factor). -/// -/// The implementation of this datastructure is a conceptual linear sequence of -/// RopePiece elements. Each RopePiece represents a view on a separately -/// allocated and reference counted string. This means that splitting a very -/// long string can be done in constant time by splitting a RopePiece that -/// references the whole string into two rope pieces that reference each half. -/// Once split, another string can be inserted in between the two halves by -/// inserting a RopePiece in between the two others. All of this is very -/// inexpensive: it takes time proportional to the number of RopePieces, not the -/// length of the strings they represent. -/// -/// While a linear sequences of RopePieces is the conceptual model, the actual -/// implementation captures them in an adapted B+ Tree. Using a B+ tree (which -/// is a tree that keeps the values in the leaves and has where each node -/// contains a reasonable number of pointers to children/values) allows us to -/// maintain efficient operation when the RewriteRope contains a *huge* number -/// of RopePieces. The basic idea of the B+ Tree is that it allows us to find -/// the RopePiece corresponding to some offset very efficiently, and it -/// automatically balances itself on insertions of RopePieces (which can happen -/// for both insertions and erases of string ranges). -/// -/// The one wrinkle on the theory is that we don't attempt to keep the tree -/// properly balanced when erases happen. Erases of string data can both insert -/// new RopePieces (e.g. when the middle of some other rope piece is deleted, -/// which results in two rope pieces, which is just like an insert) or it can -/// reduce the number of RopePieces maintained by the B+Tree. In the case when -/// the number of RopePieces is reduced, we don't attempt to maintain the -/// standard 'invariant' that each node in the tree contains at least -/// 'WidthFactor' children/values. For our use cases, this doesn't seem to -/// matter. -/// -/// The implementation below is primarily implemented in terms of three classes: -/// RopePieceBTreeNode - Common base class for: -/// -/// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece -/// nodes. This directly represents a chunk of the string with those -/// RopePieces contatenated. -/// RopePieceBTreeInterior - An interior node in the B+ Tree, which manages -/// up to '2*WidthFactor' other nodes in the tree. - - -//===----------------------------------------------------------------------===// -// RopePieceBTreeNode Class -//===----------------------------------------------------------------------===// - -namespace { - /// RopePieceBTreeNode - Common base class of RopePieceBTreeLeaf and - /// RopePieceBTreeInterior. This provides some 'virtual' dispatching methods - /// and a flag that determines which subclass the instance is. Also - /// important, this node knows the full extend of the node, including any - /// children that it has. This allows efficient skipping over entire subtrees - /// when looking for an offset in the BTree. - class RopePieceBTreeNode { - protected: - /// WidthFactor - This controls the number of K/V slots held in the BTree: - /// how wide it is. Each level of the BTree is guaranteed to have at least - /// 'WidthFactor' elements in it (either ropepieces or children), (except - /// the root, which may have less) and may have at most 2*WidthFactor - /// elements. - enum { WidthFactor = 8 }; - - /// Size - This is the number of bytes of file this node (including any - /// potential children) covers. - unsigned Size; - - /// IsLeaf - True if this is an instance of RopePieceBTreeLeaf, false if it - /// is an instance of RopePieceBTreeInterior. - bool IsLeaf; - - RopePieceBTreeNode(bool isLeaf) : Size(0), IsLeaf(isLeaf) {} - ~RopePieceBTreeNode() {} - public: - - bool isLeaf() const { return IsLeaf; } - unsigned size() const { return Size; } - - void Destroy(); - - /// split - Split the range containing the specified offset so that we are - /// guaranteed that there is a place to do an insertion at the specified - /// offset. The offset is relative, so "0" is the start of the node. - /// - /// If there is no space in this subtree for the extra piece, the extra tree - /// node is returned and must be inserted into a parent. - RopePieceBTreeNode *split(unsigned Offset); - - /// insert - Insert the specified ropepiece into this tree node at the - /// specified offset. The offset is relative, so "0" is the start of the - /// node. - /// - /// If there is no space in this subtree for the extra piece, the extra tree - /// node is returned and must be inserted into a parent. - RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); - - /// erase - Remove NumBytes from this node at the specified offset. We are - /// guaranteed that there is a split at Offset. - void erase(unsigned Offset, unsigned NumBytes); - - //static inline bool classof(const RopePieceBTreeNode *) { return true; } - - }; -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// RopePieceBTreeLeaf Class -//===----------------------------------------------------------------------===// - -namespace { - /// RopePieceBTreeLeaf - Directly manages up to '2*WidthFactor' RopePiece - /// nodes. This directly represents a chunk of the string with those - /// RopePieces contatenated. Since this is a B+Tree, all values (in this case - /// instances of RopePiece) are stored in leaves like this. To make iteration - /// over the leaves efficient, they maintain a singly linked list through the - /// NextLeaf field. This allows the B+Tree forward iterator to be constant - /// time for all increments. - class RopePieceBTreeLeaf : public RopePieceBTreeNode { - /// NumPieces - This holds the number of rope pieces currently active in the - /// Pieces array. - unsigned char NumPieces; - - /// Pieces - This tracks the file chunks currently in this leaf. - /// - RopePiece Pieces[2*WidthFactor]; - - /// NextLeaf - This is a pointer to the next leaf in the tree, allowing - /// efficient in-order forward iteration of the tree without traversal. - RopePieceBTreeLeaf **PrevLeaf, *NextLeaf; - public: - RopePieceBTreeLeaf() : RopePieceBTreeNode(true), NumPieces(0), - PrevLeaf(0), NextLeaf(0) {} - ~RopePieceBTreeLeaf() { - if (PrevLeaf || NextLeaf) - removeFromLeafInOrder(); - clear(); - } - - bool isFull() const { return NumPieces == 2*WidthFactor; } - - /// clear - Remove all rope pieces from this leaf. - void clear() { - while (NumPieces) - Pieces[--NumPieces] = RopePiece(); - Size = 0; - } - - unsigned getNumPieces() const { return NumPieces; } - - const RopePiece &getPiece(unsigned i) const { - assert(i < getNumPieces() && "Invalid piece ID"); - return Pieces[i]; - } - - const RopePieceBTreeLeaf *getNextLeafInOrder() const { return NextLeaf; } - void insertAfterLeafInOrder(RopePieceBTreeLeaf *Node) { - assert(PrevLeaf == 0 && NextLeaf == 0 && "Already in ordering"); - - NextLeaf = Node->NextLeaf; - if (NextLeaf) - NextLeaf->PrevLeaf = &NextLeaf; - PrevLeaf = &Node->NextLeaf; - Node->NextLeaf = this; - } - - void removeFromLeafInOrder() { - if (PrevLeaf) { - *PrevLeaf = NextLeaf; - if (NextLeaf) - NextLeaf->PrevLeaf = PrevLeaf; - } else if (NextLeaf) { - NextLeaf->PrevLeaf = 0; - } - } - - /// FullRecomputeSizeLocally - This method recomputes the 'Size' field by - /// summing the size of all RopePieces. - void FullRecomputeSizeLocally() { - Size = 0; - for (unsigned i = 0, e = getNumPieces(); i != e; ++i) - Size += getPiece(i).size(); - } - - /// split - Split the range containing the specified offset so that we are - /// guaranteed that there is a place to do an insertion at the specified - /// offset. The offset is relative, so "0" is the start of the node. - /// - /// If there is no space in this subtree for the extra piece, the extra tree - /// node is returned and must be inserted into a parent. - RopePieceBTreeNode *split(unsigned Offset); - - /// insert - Insert the specified ropepiece into this tree node at the - /// specified offset. The offset is relative, so "0" is the start of the - /// node. - /// - /// If there is no space in this subtree for the extra piece, the extra tree - /// node is returned and must be inserted into a parent. - RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); - - - /// erase - Remove NumBytes from this node at the specified offset. We are - /// guaranteed that there is a split at Offset. - void erase(unsigned Offset, unsigned NumBytes); - - //static inline bool classof(const RopePieceBTreeLeaf *) { return true; } - static inline bool classof(const RopePieceBTreeNode *N) { - return N->isLeaf(); - } - }; -} // end anonymous namespace - -/// split - Split the range containing the specified offset so that we are -/// guaranteed that there is a place to do an insertion at the specified -/// offset. The offset is relative, so "0" is the start of the node. -/// -/// If there is no space in this subtree for the extra piece, the extra tree -/// node is returned and must be inserted into a parent. -RopePieceBTreeNode *RopePieceBTreeLeaf::split(unsigned Offset) { - // Find the insertion point. We are guaranteed that there is a split at the - // specified offset so find it. - if (Offset == 0 || Offset == size()) { - // Fastpath for a common case. There is already a splitpoint at the end. - return 0; - } - - // Find the piece that this offset lands in. - unsigned PieceOffs = 0; - unsigned i = 0; - while (Offset >= PieceOffs+Pieces[i].size()) { - PieceOffs += Pieces[i].size(); - ++i; - } - - // If there is already a split point at the specified offset, just return - // success. - if (PieceOffs == Offset) - return 0; - - // Otherwise, we need to split piece 'i' at Offset-PieceOffs. Convert Offset - // to being Piece relative. - unsigned IntraPieceOffset = Offset-PieceOffs; - - // We do this by shrinking the RopePiece and then doing an insert of the tail. - RopePiece Tail(Pieces[i].StrData, Pieces[i].StartOffs+IntraPieceOffset, - Pieces[i].EndOffs); - Size -= Pieces[i].size(); - Pieces[i].EndOffs = Pieces[i].StartOffs+IntraPieceOffset; - Size += Pieces[i].size(); - - return insert(Offset, Tail); -} - - -/// insert - Insert the specified RopePiece into this tree node at the -/// specified offset. The offset is relative, so "0" is the start of the node. -/// -/// If there is no space in this subtree for the extra piece, the extra tree -/// node is returned and must be inserted into a parent. -RopePieceBTreeNode *RopePieceBTreeLeaf::insert(unsigned Offset, - const RopePiece &R) { - // If this node is not full, insert the piece. - if (!isFull()) { - // Find the insertion point. We are guaranteed that there is a split at the - // specified offset so find it. - unsigned i = 0, e = getNumPieces(); - if (Offset == size()) { - // Fastpath for a common case. - i = e; - } else { - unsigned SlotOffs = 0; - for (; Offset > SlotOffs; ++i) - SlotOffs += getPiece(i).size(); - assert(SlotOffs == Offset && "Split didn't occur before insertion!"); - } - - // For an insertion into a non-full leaf node, just insert the value in - // its sorted position. This requires moving later values over. - for (; i != e; --e) - Pieces[e] = Pieces[e-1]; - Pieces[i] = R; - ++NumPieces; - Size += R.size(); - return 0; - } - - // Otherwise, if this is leaf is full, split it in two halves. Since this - // node is full, it contains 2*WidthFactor values. We move the first - // 'WidthFactor' values to the LHS child (which we leave in this node) and - // move the last 'WidthFactor' values into the RHS child. - - // Create the new node. - RopePieceBTreeLeaf *NewNode = new RopePieceBTreeLeaf(); - - // Move over the last 'WidthFactor' values from here to NewNode. - std::copy(&Pieces[WidthFactor], &Pieces[2*WidthFactor], - &NewNode->Pieces[0]); - // Replace old pieces with null RopePieces to drop refcounts. - std::fill(&Pieces[WidthFactor], &Pieces[2*WidthFactor], RopePiece()); - - // Decrease the number of values in the two nodes. - NewNode->NumPieces = NumPieces = WidthFactor; - - // Recompute the two nodes' size. - NewNode->FullRecomputeSizeLocally(); - FullRecomputeSizeLocally(); - - // Update the list of leaves. - NewNode->insertAfterLeafInOrder(this); - - // These insertions can't fail. - if (this->size() >= Offset) - this->insert(Offset, R); - else - NewNode->insert(Offset - this->size(), R); - return NewNode; -} - -/// erase - Remove NumBytes from this node at the specified offset. We are -/// guaranteed that there is a split at Offset. -void RopePieceBTreeLeaf::erase(unsigned Offset, unsigned NumBytes) { - // Since we are guaranteed that there is a split at Offset, we start by - // finding the Piece that starts there. - unsigned PieceOffs = 0; - unsigned i = 0; - for (; Offset > PieceOffs; ++i) - PieceOffs += getPiece(i).size(); - assert(PieceOffs == Offset && "Split didn't occur before erase!"); - - unsigned StartPiece = i; - - // Figure out how many pieces completely cover 'NumBytes'. We want to remove - // all of them. - for (; Offset+NumBytes > PieceOffs+getPiece(i).size(); ++i) - PieceOffs += getPiece(i).size(); - - // If we exactly include the last one, include it in the region to delete. - if (Offset+NumBytes == PieceOffs+getPiece(i).size()) - PieceOffs += getPiece(i).size(), ++i; - - // If we completely cover some RopePieces, erase them now. - if (i != StartPiece) { - unsigned NumDeleted = i-StartPiece; - for (; i != getNumPieces(); ++i) - Pieces[i-NumDeleted] = Pieces[i]; - - // Drop references to dead rope pieces. - std::fill(&Pieces[getNumPieces()-NumDeleted], &Pieces[getNumPieces()], - RopePiece()); - NumPieces -= NumDeleted; - - unsigned CoverBytes = PieceOffs-Offset; - NumBytes -= CoverBytes; - Size -= CoverBytes; - } - - // If we completely removed some stuff, we could be done. - if (NumBytes == 0) return; - - // Okay, now might be erasing part of some Piece. If this is the case, then - // move the start point of the piece. - assert(getPiece(StartPiece).size() > NumBytes); - Pieces[StartPiece].StartOffs += NumBytes; - - // The size of this node just shrunk by NumBytes. - Size -= NumBytes; -} - -//===----------------------------------------------------------------------===// -// RopePieceBTreeInterior Class -//===----------------------------------------------------------------------===// - -namespace { - /// RopePieceBTreeInterior - This represents an interior node in the B+Tree, - /// which holds up to 2*WidthFactor pointers to child nodes. - class RopePieceBTreeInterior : public RopePieceBTreeNode { - /// NumChildren - This holds the number of children currently active in the - /// Children array. - unsigned char NumChildren; - RopePieceBTreeNode *Children[2*WidthFactor]; - public: - RopePieceBTreeInterior() : RopePieceBTreeNode(false), NumChildren(0) {} - - RopePieceBTreeInterior(RopePieceBTreeNode *LHS, RopePieceBTreeNode *RHS) - : RopePieceBTreeNode(false) { - Children[0] = LHS; - Children[1] = RHS; - NumChildren = 2; - Size = LHS->size() + RHS->size(); - } - - ~RopePieceBTreeInterior() { - for (unsigned i = 0, e = getNumChildren(); i != e; ++i) - Children[i]->Destroy(); - } - - bool isFull() const { return NumChildren == 2*WidthFactor; } - - unsigned getNumChildren() const { return NumChildren; } - const RopePieceBTreeNode *getChild(unsigned i) const { - assert(i < NumChildren && "invalid child #"); - return Children[i]; - } - RopePieceBTreeNode *getChild(unsigned i) { - assert(i < NumChildren && "invalid child #"); - return Children[i]; - } - - /// FullRecomputeSizeLocally - Recompute the Size field of this node by - /// summing up the sizes of the child nodes. - void FullRecomputeSizeLocally() { - Size = 0; - for (unsigned i = 0, e = getNumChildren(); i != e; ++i) - Size += getChild(i)->size(); - } - - - /// split - Split the range containing the specified offset so that we are - /// guaranteed that there is a place to do an insertion at the specified - /// offset. The offset is relative, so "0" is the start of the node. - /// - /// If there is no space in this subtree for the extra piece, the extra tree - /// node is returned and must be inserted into a parent. - RopePieceBTreeNode *split(unsigned Offset); - - - /// insert - Insert the specified ropepiece into this tree node at the - /// specified offset. The offset is relative, so "0" is the start of the - /// node. - /// - /// If there is no space in this subtree for the extra piece, the extra tree - /// node is returned and must be inserted into a parent. - RopePieceBTreeNode *insert(unsigned Offset, const RopePiece &R); - - /// HandleChildPiece - A child propagated an insertion result up to us. - /// Insert the new child, and/or propagate the result further up the tree. - RopePieceBTreeNode *HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS); - - /// erase - Remove NumBytes from this node at the specified offset. We are - /// guaranteed that there is a split at Offset. - void erase(unsigned Offset, unsigned NumBytes); - - //static inline bool classof(const RopePieceBTreeInterior *) { return true; } - static inline bool classof(const RopePieceBTreeNode *N) { - return !N->isLeaf(); - } - }; -} // end anonymous namespace - -/// split - Split the range containing the specified offset so that we are -/// guaranteed that there is a place to do an insertion at the specified -/// offset. The offset is relative, so "0" is the start of the node. -/// -/// If there is no space in this subtree for the extra piece, the extra tree -/// node is returned and must be inserted into a parent. -RopePieceBTreeNode *RopePieceBTreeInterior::split(unsigned Offset) { - // Figure out which child to split. - if (Offset == 0 || Offset == size()) - return 0; // If we have an exact offset, we're already split. - - unsigned ChildOffset = 0; - unsigned i = 0; - for (; Offset >= ChildOffset+getChild(i)->size(); ++i) - ChildOffset += getChild(i)->size(); - - // If already split there, we're done. - if (ChildOffset == Offset) - return 0; - - // Otherwise, recursively split the child. - if (RopePieceBTreeNode *RHS = getChild(i)->split(Offset-ChildOffset)) - return HandleChildPiece(i, RHS); - return 0; // Done! -} - -/// insert - Insert the specified ropepiece into this tree node at the -/// specified offset. The offset is relative, so "0" is the start of the -/// node. -/// -/// If there is no space in this subtree for the extra piece, the extra tree -/// node is returned and must be inserted into a parent. -RopePieceBTreeNode *RopePieceBTreeInterior::insert(unsigned Offset, - const RopePiece &R) { - // Find the insertion point. We are guaranteed that there is a split at the - // specified offset so find it. - unsigned i = 0, e = getNumChildren(); - - unsigned ChildOffs = 0; - if (Offset == size()) { - // Fastpath for a common case. Insert at end of last child. - i = e-1; - ChildOffs = size()-getChild(i)->size(); - } else { - for (; Offset > ChildOffs+getChild(i)->size(); ++i) - ChildOffs += getChild(i)->size(); - } - - Size += R.size(); - - // Insert at the end of this child. - if (RopePieceBTreeNode *RHS = getChild(i)->insert(Offset-ChildOffs, R)) - return HandleChildPiece(i, RHS); - - return 0; -} - -/// HandleChildPiece - A child propagated an insertion result up to us. -/// Insert the new child, and/or propagate the result further up the tree. -RopePieceBTreeNode * -RopePieceBTreeInterior::HandleChildPiece(unsigned i, RopePieceBTreeNode *RHS) { - // Otherwise the child propagated a subtree up to us as a new child. See if - // we have space for it here. - if (!isFull()) { - // Insert RHS after child 'i'. - if (i + 1 != getNumChildren()) - memmove(&Children[i+2], &Children[i+1], - (getNumChildren()-i-1)*sizeof(Children[0])); - Children[i+1] = RHS; - ++NumChildren; - return 0; - } - - // Okay, this node is full. Split it in half, moving WidthFactor children to - // a newly allocated interior node. - - // Create the new node. - RopePieceBTreeInterior *NewNode = new RopePieceBTreeInterior(); - - // Move over the last 'WidthFactor' values from here to NewNode. - memcpy(&NewNode->Children[0], &Children[WidthFactor], - WidthFactor*sizeof(Children[0])); - - // Decrease the number of values in the two nodes. - NewNode->NumChildren = NumChildren = WidthFactor; - - // Finally, insert the two new children in the side the can (now) hold them. - // These insertions can't fail. - if (i < WidthFactor) - this->HandleChildPiece(i, RHS); - else - NewNode->HandleChildPiece(i-WidthFactor, RHS); - - // Recompute the two nodes' size. - NewNode->FullRecomputeSizeLocally(); - FullRecomputeSizeLocally(); - return NewNode; -} - -/// erase - Remove NumBytes from this node at the specified offset. We are -/// guaranteed that there is a split at Offset. -void RopePieceBTreeInterior::erase(unsigned Offset, unsigned NumBytes) { - // This will shrink this node by NumBytes. - Size -= NumBytes; - - // Find the first child that overlaps with Offset. - unsigned i = 0; - for (; Offset >= getChild(i)->size(); ++i) - Offset -= getChild(i)->size(); - - // Propagate the delete request into overlapping children, or completely - // delete the children as appropriate. - while (NumBytes) { - RopePieceBTreeNode *CurChild = getChild(i); - - // If we are deleting something contained entirely in the child, pass on the - // request. - if (Offset+NumBytes < CurChild->size()) { - CurChild->erase(Offset, NumBytes); - return; - } - - // If this deletion request starts somewhere in the middle of the child, it - // must be deleting to the end of the child. - if (Offset) { - unsigned BytesFromChild = CurChild->size()-Offset; - CurChild->erase(Offset, BytesFromChild); - NumBytes -= BytesFromChild; - // Start at the beginning of the next child. - Offset = 0; - ++i; - continue; - } - - // If the deletion request completely covers the child, delete it and move - // the rest down. - NumBytes -= CurChild->size(); - CurChild->Destroy(); - --NumChildren; - if (i != getNumChildren()) - memmove(&Children[i], &Children[i+1], - (getNumChildren()-i)*sizeof(Children[0])); - } -} - -//===----------------------------------------------------------------------===// -// RopePieceBTreeNode Implementation -//===----------------------------------------------------------------------===// - -void RopePieceBTreeNode::Destroy() { - if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) - delete Leaf; - else - delete cast(this); -} - -/// split - Split the range containing the specified offset so that we are -/// guaranteed that there is a place to do an insertion at the specified -/// offset. The offset is relative, so "0" is the start of the node. -/// -/// If there is no space in this subtree for the extra piece, the extra tree -/// node is returned and must be inserted into a parent. -RopePieceBTreeNode *RopePieceBTreeNode::split(unsigned Offset) { - assert(Offset <= size() && "Invalid offset to split!"); - if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) - return Leaf->split(Offset); - return cast(this)->split(Offset); -} - -/// insert - Insert the specified ropepiece into this tree node at the -/// specified offset. The offset is relative, so "0" is the start of the -/// node. -/// -/// If there is no space in this subtree for the extra piece, the extra tree -/// node is returned and must be inserted into a parent. -RopePieceBTreeNode *RopePieceBTreeNode::insert(unsigned Offset, - const RopePiece &R) { - assert(Offset <= size() && "Invalid offset to insert!"); - if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) - return Leaf->insert(Offset, R); - return cast(this)->insert(Offset, R); -} - -/// erase - Remove NumBytes from this node at the specified offset. We are -/// guaranteed that there is a split at Offset. -void RopePieceBTreeNode::erase(unsigned Offset, unsigned NumBytes) { - assert(Offset+NumBytes <= size() && "Invalid offset to erase!"); - if (RopePieceBTreeLeaf *Leaf = dyn_cast(this)) - return Leaf->erase(Offset, NumBytes); - return cast(this)->erase(Offset, NumBytes); -} - - -//===----------------------------------------------------------------------===// -// RopePieceBTreeIterator Implementation -//===----------------------------------------------------------------------===// - -static const RopePieceBTreeLeaf *getCN(const void *P) { - return static_cast(P); -} - -// begin iterator. -RopePieceBTreeIterator::RopePieceBTreeIterator(const void *n) { - const RopePieceBTreeNode *N = static_cast(n); - - // Walk down the left side of the tree until we get to a leaf. - while (const RopePieceBTreeInterior *IN = dyn_cast(N)) - N = IN->getChild(0); - - // We must have at least one leaf. - CurNode = cast(N); - - // If we found a leaf that happens to be empty, skip over it until we get - // to something full. - while (CurNode && getCN(CurNode)->getNumPieces() == 0) - CurNode = getCN(CurNode)->getNextLeafInOrder(); - - if (CurNode != 0) - CurPiece = &getCN(CurNode)->getPiece(0); - else // Empty tree, this is an end() iterator. - CurPiece = 0; - CurChar = 0; -} - -void RopePieceBTreeIterator::MoveToNextPiece() { - if (CurPiece != &getCN(CurNode)->getPiece(getCN(CurNode)->getNumPieces()-1)) { - CurChar = 0; - ++CurPiece; - return; - } - - // Find the next non-empty leaf node. - do - CurNode = getCN(CurNode)->getNextLeafInOrder(); - while (CurNode && getCN(CurNode)->getNumPieces() == 0); - - if (CurNode != 0) - CurPiece = &getCN(CurNode)->getPiece(0); - else // Hit end(). - CurPiece = 0; - CurChar = 0; -} - -//===----------------------------------------------------------------------===// -// RopePieceBTree Implementation -//===----------------------------------------------------------------------===// - -static RopePieceBTreeNode *getRoot(void *P) { - return static_cast(P); -} - -RopePieceBTree::RopePieceBTree() { - Root = new RopePieceBTreeLeaf(); -} -RopePieceBTree::RopePieceBTree(const RopePieceBTree &RHS) { - assert(RHS.empty() && "Can't copy non-empty tree yet"); - Root = new RopePieceBTreeLeaf(); -} -RopePieceBTree::~RopePieceBTree() { - getRoot(Root)->Destroy(); -} - -unsigned RopePieceBTree::size() const { - return getRoot(Root)->size(); -} - -void RopePieceBTree::clear() { - if (RopePieceBTreeLeaf *Leaf = dyn_cast(getRoot(Root))) - Leaf->clear(); - else { - getRoot(Root)->Destroy(); - Root = new RopePieceBTreeLeaf(); - } -} - -void RopePieceBTree::insert(unsigned Offset, const RopePiece &R) { - // #1. Split at Offset. - if (RopePieceBTreeNode *RHS = getRoot(Root)->split(Offset)) - Root = new RopePieceBTreeInterior(getRoot(Root), RHS); - - // #2. Do the insertion. - if (RopePieceBTreeNode *RHS = getRoot(Root)->insert(Offset, R)) - Root = new RopePieceBTreeInterior(getRoot(Root), RHS); -} - -void RopePieceBTree::erase(unsigned Offset, unsigned NumBytes) { - // #1. Split at Offset. - if (RopePieceBTreeNode *RHS = getRoot(Root)->split(Offset)) - Root = new RopePieceBTreeInterior(getRoot(Root), RHS); - - // #2. Do the erasing. - getRoot(Root)->erase(Offset, NumBytes); -} - -//===----------------------------------------------------------------------===// -// RewriteRope Implementation -//===----------------------------------------------------------------------===// - -/// MakeRopeString - This copies the specified byte range into some instance of -/// RopeRefCountString, and return a RopePiece that represents it. This uses -/// the AllocBuffer object to aggregate requests for small strings into one -/// allocation instead of doing tons of tiny allocations. -RopePiece RewriteRope::MakeRopeString(const char *Start, const char *End) { - unsigned Len = End-Start; - assert(Len && "Zero length RopePiece is invalid!"); - - // If we have space for this string in the current alloc buffer, use it. - if (AllocOffs+Len <= AllocChunkSize) { - memcpy(AllocBuffer->Data+AllocOffs, Start, Len); - AllocOffs += Len; - return RopePiece(AllocBuffer, AllocOffs-Len, AllocOffs); - } - - // If we don't have enough room because this specific allocation is huge, - // just allocate a new rope piece for it alone. - if (Len > AllocChunkSize) { - unsigned Size = End-Start+sizeof(RopeRefCountString)-1; - RopeRefCountString *Res = - reinterpret_cast(new char[Size]); - Res->RefCount = 0; - memcpy(Res->Data, Start, End-Start); - return RopePiece(Res, 0, End-Start); - } - - // Otherwise, this was a small request but we just don't have space for it - // Make a new chunk and share it with later allocations. - - // If we had an old allocation, drop our reference to it. - if (AllocBuffer && --AllocBuffer->RefCount == 0) - delete [] (char*)AllocBuffer; - - unsigned AllocSize = offsetof(RopeRefCountString, Data) + AllocChunkSize; - AllocBuffer = reinterpret_cast(new char[AllocSize]); - AllocBuffer->RefCount = 0; - memcpy(AllocBuffer->Data, Start, Len); - AllocOffs = Len; - - // Start out the new allocation with a refcount of 1, since we have an - // internal reference to it. - AllocBuffer->addRef(); - return RopePiece(AllocBuffer, 0, Len); -} - - diff --git a/lib/Rewrite/RewriteTest.cpp b/lib/Rewrite/RewriteTest.cpp deleted file mode 100644 index 019e5e73120d1..0000000000000 --- a/lib/Rewrite/RewriteTest.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//===--- RewriteTest.cpp - Rewriter playground ----------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This is a testbed. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/Rewriters.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Rewrite/TokenRewriter.h" -#include "llvm/Support/raw_ostream.h" - -void clang::DoRewriteTest(Preprocessor &PP, raw_ostream* OS) { - SourceManager &SM = PP.getSourceManager(); - const LangOptions &LangOpts = PP.getLangOpts(); - - TokenRewriter Rewriter(SM.getMainFileID(), SM, LangOpts); - - // Throw tags around comments. - for (TokenRewriter::token_iterator I = Rewriter.token_begin(), - E = Rewriter.token_end(); I != E; ++I) { - if (I->isNot(tok::comment)) continue; - - Rewriter.AddTokenBefore(I, ""); - Rewriter.AddTokenAfter(I, ""); - } - - - // Print out the output. - for (TokenRewriter::token_iterator I = Rewriter.token_begin(), - E = Rewriter.token_end(); I != E; ++I) - *OS << PP.getSpelling(*I); -} diff --git a/lib/Rewrite/Rewriter.cpp b/lib/Rewrite/Rewriter.cpp deleted file mode 100644 index 7c27114f1cfda..0000000000000 --- a/lib/Rewrite/Rewriter.cpp +++ /dev/null @@ -1,486 +0,0 @@ -//===--- Rewriter.cpp - Code rewriting interface --------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the Rewriter class, which is used for code -// transformations. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/Rewriter.h" -#include "clang/AST/Stmt.h" -#include "clang/AST/Decl.h" -#include "clang/Basic/DiagnosticIDs.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Lex/Lexer.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/FileSystem.h" -using namespace clang; - -raw_ostream &RewriteBuffer::write(raw_ostream &os) const { - // FIXME: eliminate the copy by writing out each chunk at a time - os << std::string(begin(), end()); - return os; -} - -/// \brief Return true if this character is non-new-line whitespace: -/// ' ', '\\t', '\\f', '\\v', '\\r'. -static inline bool isWhitespace(unsigned char c) { - switch (c) { - case ' ': - case '\t': - case '\f': - case '\v': - case '\r': - return true; - default: - return false; - } -} - -void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, - bool removeLineIfEmpty) { - // Nothing to remove, exit early. - if (Size == 0) return; - - unsigned RealOffset = getMappedOffset(OrigOffset, true); - assert(RealOffset+Size < Buffer.size() && "Invalid location"); - - // Remove the dead characters. - Buffer.erase(RealOffset, Size); - - // Add a delta so that future changes are offset correctly. - AddReplaceDelta(OrigOffset, -Size); - - if (removeLineIfEmpty) { - // Find the line that the remove occurred and if it is completely empty - // remove the line as well. - - iterator curLineStart = begin(); - unsigned curLineStartOffs = 0; - iterator posI = begin(); - for (unsigned i = 0; i != RealOffset; ++i) { - if (*posI == '\n') { - curLineStart = posI; - ++curLineStart; - curLineStartOffs = i + 1; - } - ++posI; - } - - unsigned lineSize = 0; - posI = curLineStart; - while (posI != end() && isWhitespace(*posI)) { - ++posI; - ++lineSize; - } - if (posI != end() && *posI == '\n') { - Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/); - AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/)); - } - } -} - -void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, - bool InsertAfter) { - - // Nothing to insert, exit early. - if (Str.empty()) return; - - unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); - Buffer.insert(RealOffset, Str.begin(), Str.end()); - - // Add a delta so that future changes are offset correctly. - AddInsertDelta(OrigOffset, Str.size()); -} - -/// ReplaceText - This method replaces a range of characters in the input -/// buffer with a new string. This is effectively a combined "remove+insert" -/// operation. -void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, - StringRef NewStr) { - unsigned RealOffset = getMappedOffset(OrigOffset, true); - Buffer.erase(RealOffset, OrigLength); - Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); - if (OrigLength != NewStr.size()) - AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); -} - - -//===----------------------------------------------------------------------===// -// Rewriter class -//===----------------------------------------------------------------------===// - -/// getRangeSize - Return the size in bytes of the specified range if they -/// are in the same file. If not, this returns -1. -int Rewriter::getRangeSize(const CharSourceRange &Range, - RewriteOptions opts) const { - if (!isRewritable(Range.getBegin()) || - !isRewritable(Range.getEnd())) return -1; - - FileID StartFileID, EndFileID; - unsigned StartOff, EndOff; - - StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); - EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); - - if (StartFileID != EndFileID) - return -1; - - // If edits have been made to this buffer, the delta between the range may - // have changed. - std::map::const_iterator I = - RewriteBuffers.find(StartFileID); - if (I != RewriteBuffers.end()) { - const RewriteBuffer &RB = I->second; - EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange); - StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange); - } - - - // Adjust the end offset to the end of the last token, instead of being the - // start of the last token if this is a token range. - if (Range.isTokenRange()) - EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); - - return EndOff-StartOff; -} - -int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const { - return getRangeSize(CharSourceRange::getTokenRange(Range), opts); -} - - -/// getRewrittenText - Return the rewritten form of the text in the specified -/// range. If the start or end of the range was unrewritable or if they are -/// in different buffers, this returns an empty string. -/// -/// Note that this method is not particularly efficient. -/// -std::string Rewriter::getRewrittenText(SourceRange Range) const { - if (!isRewritable(Range.getBegin()) || - !isRewritable(Range.getEnd())) - return ""; - - FileID StartFileID, EndFileID; - unsigned StartOff, EndOff; - StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); - EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); - - if (StartFileID != EndFileID) - return ""; // Start and end in different buffers. - - // If edits have been made to this buffer, the delta between the range may - // have changed. - std::map::const_iterator I = - RewriteBuffers.find(StartFileID); - if (I == RewriteBuffers.end()) { - // If the buffer hasn't been rewritten, just return the text from the input. - const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); - - // Adjust the end offset to the end of the last token, instead of being the - // start of the last token. - EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); - return std::string(Ptr, Ptr+EndOff-StartOff); - } - - const RewriteBuffer &RB = I->second; - EndOff = RB.getMappedOffset(EndOff, true); - StartOff = RB.getMappedOffset(StartOff); - - // Adjust the end offset to the end of the last token, instead of being the - // start of the last token. - EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); - - // Advance the iterators to the right spot, yay for linear time algorithms. - RewriteBuffer::iterator Start = RB.begin(); - std::advance(Start, StartOff); - RewriteBuffer::iterator End = Start; - std::advance(End, EndOff-StartOff); - - return std::string(Start, End); -} - -unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, - FileID &FID) const { - assert(Loc.isValid() && "Invalid location"); - std::pair V = SourceMgr->getDecomposedLoc(Loc); - FID = V.first; - return V.second; -} - - -/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. -/// -RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { - std::map::iterator I = - RewriteBuffers.lower_bound(FID); - if (I != RewriteBuffers.end() && I->first == FID) - return I->second; - I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); - - StringRef MB = SourceMgr->getBufferData(FID); - I->second.Initialize(MB.begin(), MB.end()); - - return I->second; -} - -/// InsertText - Insert the specified string at the specified location in the -/// original buffer. -bool Rewriter::InsertText(SourceLocation Loc, StringRef Str, - bool InsertAfter, bool indentNewLines) { - if (!isRewritable(Loc)) return true; - FileID FID; - unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); - - SmallString<128> indentedStr; - if (indentNewLines && Str.find('\n') != StringRef::npos) { - StringRef MB = SourceMgr->getBufferData(FID); - - unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; - const SrcMgr::ContentCache * - Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); - unsigned lineOffs = Content->SourceLineCache[lineNo]; - - // Find the whitespace at the start of the line. - StringRef indentSpace; - { - unsigned i = lineOffs; - while (isWhitespace(MB[i])) - ++i; - indentSpace = MB.substr(lineOffs, i-lineOffs); - } - - SmallVector lines; - Str.split(lines, "\n"); - - for (unsigned i = 0, e = lines.size(); i != e; ++i) { - indentedStr += lines[i]; - if (i < e-1) { - indentedStr += '\n'; - indentedStr += indentSpace; - } - } - Str = indentedStr.str(); - } - - getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); - return false; -} - -bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) { - if (!isRewritable(Loc)) return true; - FileID FID; - unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); - RewriteOptions rangeOpts; - rangeOpts.IncludeInsertsAtBeginOfRange = false; - StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts); - getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true); - return false; -} - -/// RemoveText - Remove the specified text region. -bool Rewriter::RemoveText(SourceLocation Start, unsigned Length, - RewriteOptions opts) { - if (!isRewritable(Start)) return true; - FileID FID; - unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); - getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty); - return false; -} - -/// ReplaceText - This method replaces a range of characters in the input -/// buffer with a new string. This is effectively a combined "remove/insert" -/// operation. -bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, - StringRef NewStr) { - if (!isRewritable(Start)) return true; - FileID StartFileID; - unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); - - getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr); - return false; -} - -bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) { - if (!isRewritable(range.getBegin())) return true; - if (!isRewritable(range.getEnd())) return true; - if (replacementRange.isInvalid()) return true; - SourceLocation start = range.getBegin(); - unsigned origLength = getRangeSize(range); - unsigned newLength = getRangeSize(replacementRange); - FileID FID; - unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(), - FID); - StringRef MB = SourceMgr->getBufferData(FID); - return ReplaceText(start, origLength, MB.substr(newOffs, newLength)); -} - -/// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty -/// printer to generate the replacement code. This returns true if the input -/// could not be rewritten, or false if successful. -bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) { - // Measaure the old text. - int Size = getRangeSize(From->getSourceRange()); - if (Size == -1) - return true; - - // Get the new text. - std::string SStr; - llvm::raw_string_ostream S(SStr); - To->printPretty(S, 0, PrintingPolicy(*LangOpts)); - const std::string &Str = S.str(); - - ReplaceText(From->getLocStart(), Size, Str); - return false; -} - -std::string Rewriter::ConvertToString(Stmt *From) { - std::string SStr; - llvm::raw_string_ostream S(SStr); - From->printPretty(S, 0, PrintingPolicy(*LangOpts)); - return S.str(); -} - -bool Rewriter::IncreaseIndentation(CharSourceRange range, - SourceLocation parentIndent) { - if (range.isInvalid()) return true; - if (!isRewritable(range.getBegin())) return true; - if (!isRewritable(range.getEnd())) return true; - if (!isRewritable(parentIndent)) return true; - - FileID StartFileID, EndFileID, parentFileID; - unsigned StartOff, EndOff, parentOff; - - StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID); - EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID); - parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID); - - if (StartFileID != EndFileID || StartFileID != parentFileID) - return true; - if (StartOff > EndOff) - return true; - - FileID FID = StartFileID; - StringRef MB = SourceMgr->getBufferData(FID); - - unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1; - unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1; - unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1; - - const SrcMgr::ContentCache * - Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); - - // Find where the lines start. - unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; - unsigned startLineOffs = Content->SourceLineCache[startLineNo]; - - // Find the whitespace at the start of each line. - StringRef parentSpace, startSpace; - { - unsigned i = parentLineOffs; - while (isWhitespace(MB[i])) - ++i; - parentSpace = MB.substr(parentLineOffs, i-parentLineOffs); - - i = startLineOffs; - while (isWhitespace(MB[i])) - ++i; - startSpace = MB.substr(startLineOffs, i-startLineOffs); - } - if (parentSpace.size() >= startSpace.size()) - return true; - if (!startSpace.startswith(parentSpace)) - return true; - - StringRef indent = startSpace.substr(parentSpace.size()); - - // Indent the lines between start/end offsets. - RewriteBuffer &RB = getEditBuffer(FID); - for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { - unsigned offs = Content->SourceLineCache[lineNo]; - unsigned i = offs; - while (isWhitespace(MB[i])) - ++i; - StringRef origIndent = MB.substr(offs, i-offs); - if (origIndent.startswith(startSpace)) - RB.InsertText(offs, indent, /*InsertAfter=*/false); - } - - return false; -} - -// A wrapper for a file stream that atomically overwrites the target. -// -// Creates a file output stream for a temporary file in the constructor, -// which is later accessible via getStream() if ok() return true. -// Flushes the stream and moves the temporary file to the target location -// in the destructor. -class AtomicallyMovedFile { -public: - AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename, - bool &AllWritten) - : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) { - TempFilename = Filename; - TempFilename += "-%%%%%%%%"; - int FD; - if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename, - /*makeAbsolute=*/true, 0664)) { - AllWritten = false; - Diagnostics.Report(clang::diag::err_unable_to_make_temp) - << TempFilename; - } else { - FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true)); - } - } - - ~AtomicallyMovedFile() { - if (!ok()) return; - - FileStream->flush(); -#ifdef _WIN32 - // Win32 does not allow rename/removing opened files. - FileStream.reset(); -#endif - if (llvm::error_code ec = - llvm::sys::fs::rename(TempFilename.str(), Filename)) { - AllWritten = false; - Diagnostics.Report(clang::diag::err_unable_to_rename_temp) - << TempFilename << Filename << ec.message(); - bool existed; - // If the remove fails, there's not a lot we can do - this is already an - // error. - llvm::sys::fs::remove(TempFilename.str(), existed); - } - } - - bool ok() { return FileStream; } - llvm::raw_ostream &getStream() { return *FileStream; } - -private: - DiagnosticsEngine &Diagnostics; - StringRef Filename; - SmallString<128> TempFilename; - OwningPtr FileStream; - bool &AllWritten; -}; - -bool Rewriter::overwriteChangedFiles() { - bool AllWritten = true; - for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { - const FileEntry *Entry = - getSourceMgr().getFileEntryForID(I->first); - AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(), - AllWritten); - if (File.ok()) { - I->second.write(File.getStream()); - } - } - return !AllWritten; -} diff --git a/lib/Rewrite/TokenRewriter.cpp b/lib/Rewrite/TokenRewriter.cpp deleted file mode 100644 index 03ce63ea11746..0000000000000 --- a/lib/Rewrite/TokenRewriter.cpp +++ /dev/null @@ -1,99 +0,0 @@ -//===--- TokenRewriter.cpp - Token-based code rewriting interface ---------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the TokenRewriter class, which is used for code -// transformations. -// -//===----------------------------------------------------------------------===// - -#include "clang/Rewrite/TokenRewriter.h" -#include "clang/Lex/Lexer.h" -#include "clang/Lex/ScratchBuffer.h" -#include "clang/Basic/SourceManager.h" -using namespace clang; - -TokenRewriter::TokenRewriter(FileID FID, SourceManager &SM, - const LangOptions &LangOpts) { - ScratchBuf.reset(new ScratchBuffer(SM)); - - // Create a lexer to lex all the tokens of the main file in raw mode. - const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); - Lexer RawLex(FID, FromFile, SM, LangOpts); - - // Return all comments and whitespace as tokens. - RawLex.SetKeepWhitespaceMode(true); - - // Lex the file, populating our datastructures. - Token RawTok; - RawLex.LexFromRawLexer(RawTok); - while (RawTok.isNot(tok::eof)) { -#if 0 - if (Tok.is(tok::raw_identifier)) { - // Look up the identifier info for the token. This should use - // IdentifierTable directly instead of PP. - PP.LookUpIdentifierInfo(Tok); - } -#endif - - AddToken(RawTok, TokenList.end()); - RawLex.LexFromRawLexer(RawTok); - } -} - -TokenRewriter::~TokenRewriter() { -} - - -/// RemapIterator - Convert from token_iterator (a const iterator) to -/// TokenRefTy (a non-const iterator). -TokenRewriter::TokenRefTy TokenRewriter::RemapIterator(token_iterator I) { - if (I == token_end()) return TokenList.end(); - - // FIXME: This is horrible, we should use our own list or something to avoid - // this. - std::map::iterator MapIt = - TokenAtLoc.find(I->getLocation()); - assert(MapIt != TokenAtLoc.end() && "iterator not in rewriter?"); - return MapIt->second; -} - - -/// AddToken - Add the specified token into the Rewriter before the other -/// position. -TokenRewriter::TokenRefTy -TokenRewriter::AddToken(const Token &T, TokenRefTy Where) { - Where = TokenList.insert(Where, T); - - bool InsertSuccess = TokenAtLoc.insert(std::make_pair(T.getLocation(), - Where)).second; - assert(InsertSuccess && "Token location already in rewriter!"); - (void)InsertSuccess; - return Where; -} - - -TokenRewriter::token_iterator -TokenRewriter::AddTokenBefore(token_iterator I, const char *Val) { - unsigned Len = strlen(Val); - - // Plop the string into the scratch buffer, then create a token for this - // string. - Token Tok; - Tok.startToken(); - const char *Spelling; - Tok.setLocation(ScratchBuf->getToken(Val, Len, Spelling)); - Tok.setLength(Len); - - // TODO: Form a whole lexer around this and relex the token! For now, just - // set kind to tok::unknown. - Tok.setKind(tok::unknown); - - return AddToken(Tok, RemapIterator(I)); -} - -- cgit v1.3