//===--- RewriteBlocks.cpp ----------------------------------------------===// // // 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 closure rewriter. // //===----------------------------------------------------------------------===// #include "clang/Frontend/ASTConsumers.h" #include "clang/Rewrite/Rewriter.h" #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include using namespace clang; using llvm::utostr; namespace { class RewriteBlocks : public ASTConsumer { Rewriter Rewrite; Diagnostic &Diags; const LangOptions &LangOpts; unsigned RewriteFailedDiag; ASTContext *Context; SourceManager *SM; FileID MainFileID; const char *MainFileStart, *MainFileEnd; // Block expressions. llvm::SmallVector Blocks; llvm::SmallVector BlockDeclRefs; llvm::DenseMap BlockCallExprs; // Block related declarations. llvm::SmallPtrSet BlockByCopyDecls; llvm::SmallPtrSet BlockByRefDecls; llvm::SmallPtrSet ImportedBlockDecls; llvm::DenseMap RewrittenBlockExprs; // The function/method we are rewriting. FunctionDecl *CurFunctionDef; ObjCMethodDecl *CurMethodDef; bool IsHeader; std::string Preamble; public: RewriteBlocks(std::string inFile, Diagnostic &D, const LangOptions &LOpts); ~RewriteBlocks() { // Get the buffer corresponding to MainFileID. // If we haven't changed it, then we are done. if (const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(MainFileID)) { std::string S(RewriteBuf->begin(), RewriteBuf->end()); printf("%s\n", S.c_str()); } else { printf("No changes\n"); } } void Initialize(ASTContext &context); void InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen); void ReplaceText(SourceLocation Start, unsigned OrigLength, const char *NewStr, unsigned NewLength); // Top Level Driver code. virtual void HandleTopLevelDecl(DeclGroupRef D) { for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) HandleTopLevelSingleDecl(*I); } void HandleTopLevelSingleDecl(Decl *D); void HandleDeclInMainFile(Decl *D); // Top level Stmt *RewriteFunctionBody(Stmt *S); void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); // Block specific rewrite rules. std::string SynthesizeBlockInitExpr(BlockExpr *Exp, VarDecl *VD=0); void RewriteBlockCall(CallExpr *Exp); void RewriteBlockPointerDecl(NamedDecl *VD); void RewriteBlockDeclRefExpr(BlockDeclRefExpr *VD); void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, const char *funcName, std::string Tag); std::string SynthesizeBlockFunc(BlockExpr *CE, int i, const char *funcName, std::string Tag); std::string SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, bool hasCopyDisposeHelpers); std::string SynthesizeBlockCall(CallExpr *Exp); void SynthesizeBlockLiterals(SourceLocation FunLocStart, const char *FunName); void CollectBlockDeclRefInfo(BlockExpr *Exp); void GetBlockCallExprs(Stmt *S); void GetBlockDeclRefExprs(Stmt *S); // 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 isBlockPointerType(QualType T) { return isa(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->getAsPointerType()) { if (isa(PT->getPointeeType()) || isa(PT->getPointeeType())) return true; } return false; } // ObjC rewrite methods. void RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl); void RewriteCategoryDecl(ObjCCategoryDecl *CatDecl); void RewriteProtocolDecl(ObjCProtocolDecl *PDecl); void RewriteMethodDecl(ObjCMethodDecl *MDecl); void RewriteFunctionProtoType(QualType funcType, NamedDecl *D); void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); void RewriteCastExpr(CastExpr *CE); bool PointerTypeTakesAnyBlockArguments(QualType QT); void GetExtentOfArgList(const char *Name, const char *&LParen, const char *&RParen); }; } 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"; } RewriteBlocks::RewriteBlocks(std::string inFile, Diagnostic &D, const LangOptions &LOpts) : Diags(D), LangOpts(LOpts) { IsHeader = IsHeaderFile(inFile); CurFunctionDef = 0; CurMethodDef = 0; RewriteFailedDiag = Diags.getCustomDiagID(Diagnostic::Warning, "rewriting failed"); } ASTConsumer *clang::CreateBlockRewriter(const std::string& InFile, Diagnostic &Diags, const LangOptions &LangOpts) { return new RewriteBlocks(InFile, Diags, LangOpts); } void RewriteBlocks::Initialize(ASTContext &context) { Context = &context; SM = &Context->getSourceManager(); // 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(), LangOpts); if (IsHeader) Preamble = "#pragma once\n"; 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 Size;\n"; Preamble += " void *FuncPtr;\n"; Preamble += "};\n"; Preamble += "enum {\n"; Preamble += " BLOCK_HAS_COPY_DISPOSE = (1<<25),\n"; Preamble += " BLOCK_IS_GLOBAL = (1<<28)\n"; Preamble += "};\n"; if (LangOpts.Microsoft) Preamble += "#define __OBJC_RW_EXTERN extern \"C\" __declspec(dllimport)\n"; else Preamble += "#define __OBJC_RW_EXTERN extern\n"; Preamble += "// Runtime copy/destroy helper functions\n"; Preamble += "__OBJC_RW_EXTERN void _Block_copy_assign(void *, void *);\n"; Preamble += "__OBJC_RW_EXTERN void _Block_byref_assign_copy(void *, void *);\n"; Preamble += "__OBJC_RW_EXTERN void _Block_destroy(void *);\n"; Preamble += "__OBJC_RW_EXTERN void _Block_byref_release(void *);\n"; Preamble += "__OBJC_RW_EXTERN void *_NSConcreteGlobalBlock;\n"; Preamble += "__OBJC_RW_EXTERN void *_NSConcreteStackBlock;\n"; Preamble += "#endif\n"; InsertText(SM->getLocForStartOfFile(MainFileID), Preamble.c_str(), Preamble.size()); } void RewriteBlocks::InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen) { if (!Rewrite.InsertText(Loc, StrData, StrLen)) return; Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); } void RewriteBlocks::ReplaceText(SourceLocation Start, unsigned OrigLength, const char *NewStr, unsigned NewLength) { if (!Rewrite.ReplaceText(Start, OrigLength, NewStr, NewLength)) return; Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); } void RewriteBlocks::RewriteMethodDecl(ObjCMethodDecl *Method) { bool haveBlockPtrs = false; for (ObjCMethodDecl::param_iterator I = Method->param_begin(), E = Method->param_end(); I != E; ++I) if (isBlockPointerType((*I)->getType())) haveBlockPtrs = true; if (!haveBlockPtrs) return; // Do a fuzzy rewrite. // We have 1 or more arguments that have closure pointers. SourceLocation Loc = Method->getLocStart(); SourceLocation LocEnd = Method->getLocEnd(); const char *startBuf = SM->getCharacterData(Loc); const char *endBuf = SM->getCharacterData(LocEnd); const char *methodPtr = startBuf; std::string Tag = "struct __block_impl *"; while (*methodPtr++ && (methodPtr != endBuf)) { switch (*methodPtr) { case ':': methodPtr++; if (*methodPtr == '(') { const char *scanType = ++methodPtr; bool foundBlockPointer = false; unsigned parenCount = 1; while (parenCount) { switch (*scanType) { case '(': parenCount++; break; case ')': parenCount--; break; case '^': foundBlockPointer = true; break; } scanType++; } if (foundBlockPointer) { // advance the location to startArgList. Loc = Loc.getFileLocWithOffset(methodPtr-startBuf); assert((Loc.isValid()) && "Invalid Loc"); ReplaceText(Loc, scanType-methodPtr-1, Tag.c_str(), Tag.size()); // Advance startBuf. Since the underlying buffer has changed, // it's very important to advance startBuf (so we can correctly // compute a relative Loc the next time around). startBuf = methodPtr; } // Advance the method ptr to the end of the type. methodPtr = scanType; } break; } } return; } void RewriteBlocks::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { for (ObjCInterfaceDecl::instmeth_iterator I = ClassDecl->instmeth_begin(*Context), E = ClassDecl->instmeth_end(*Context); I != E; ++I) RewriteMethodDecl(*I); for (ObjCInterfaceDecl::classmeth_iterator I = ClassDecl->classmeth_begin(*Context), E = ClassDecl->classmeth_end(*Context); I != E; ++I) RewriteMethodDecl(*I); } void RewriteBlocks::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { for (ObjCCategoryDecl::instmeth_iterator I = CatDecl->instmeth_begin(*Context), E = CatDecl->instmeth_end(*Context); I != E; ++I) RewriteMethodDecl(*I); for (ObjCCategoryDecl::classmeth_iterator I = CatDecl->classmeth_begin(*Context), E = CatDecl->classmeth_end(*Context); I != E; ++I) RewriteMethodDecl(*I); } void RewriteBlocks::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { for (ObjCProtocolDecl::instmeth_iterator I = PDecl->instmeth_begin(*Context), E = PDecl->instmeth_end(*Context); I != E; ++I) RewriteMethodDecl(*I); for (ObjCProtocolDecl::classmeth_iterator I = PDecl->classmeth_begin(*Context), E = PDecl->classmeth_end(*Context); I != E; ++I) RewriteMethodDecl(*I); } //===----------------------------------------------------------------------===// // Top Level Driver Code //===----------------------------------------------------------------------===// void RewriteBlocks::HandleTopLevelSingleDecl(Decl *D) { // 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->getInstantiationLoc(Loc); // If this is for a builtin, ignore it. if (Loc.isInvalid()) return; if (ObjCInterfaceDecl *MD = dyn_cast(D)) RewriteInterfaceDecl(MD); else if (ObjCCategoryDecl *CD = dyn_cast(D)) RewriteCategoryDecl(CD); else if (ObjCProtocolDecl *PD = dyn_cast(D)) RewriteProtocolDecl(PD); // If we have a decl in the main file, see if we should rewrite it. if (SM->isFromMainFile(Loc)) HandleDeclInMainFile(D); return; } std::string RewriteBlocks::SynthesizeBlockFunc(BlockExpr *CE, int i, const char *funcName, std::string Tag) { const FunctionType *AFT = CE->getFunctionType(); QualType RT = AFT->getResultType(); std::string StructRef = "struct " + Tag; std::string S = "static " + RT.getAsString() + " __" + funcName + "_" + "block_func_" + utostr(i); BlockDecl *BD = CE->getBlockDecl(); if (isa(AFT)) { S += "()"; } 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(); (*AI)->getType().getAsStringInternal(ParamStr, Context->PrintingPolicy); 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 (llvm::SmallPtrSet::iterator I = BlockByRefDecls.begin(), E = BlockByRefDecls.end(); I != E; ++I) { S += " "; std::string Name = (*I)->getNameAsString(); Context->getPointerType((*I)->getType()).getAsStringInternal(Name, Context->PrintingPolicy); S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; } // Next, emit a declaration for all "by copy" declarations. for (llvm::SmallPtrSet::iterator I = BlockByCopyDecls.begin(), E = BlockByCopyDecls.end(); I != E; ++I) { S += " "; std::string Name = (*I)->getNameAsString(); // 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 (isBlockPointerType((*I)->getType())) S += "struct __block_impl *"; else (*I)->getType().getAsStringInternal(Name, Context->PrintingPolicy); 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 RewriteBlocks::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, const char *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) { S += "_Block_copy_assign(&dst->"; S += (*I)->getNameAsString(); S += ", src->"; S += (*I)->getNameAsString(); S += ");}"; } 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) { S += "_Block_destroy(src->"; S += (*I)->getNameAsString(); S += ");"; } S += "}\n"; return S; } std::string RewriteBlocks::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, bool hasCopyDisposeHelpers) { std::string S = "struct " + Tag; std::string Constructor = " " + Tag; S += " {\n struct __block_impl impl;\n"; if (hasCopyDisposeHelpers) S += " void *copy;\n void *dispose;\n"; Constructor += "(void *fp"; if (hasCopyDisposeHelpers) Constructor += ", void *copyHelp, void *disposeHelp"; if (BlockDeclRefs.size()) { // Output all "by copy" declarations. for (llvm::SmallPtrSet::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 (isBlockPointerType((*I)->getType())) { S += "struct __block_impl *"; Constructor += ", void *" + ArgName; } else { (*I)->getType().getAsStringInternal(FieldName, Context->PrintingPolicy); (*I)->getType().getAsStringInternal(ArgName, Context->PrintingPolicy); Constructor += ", " + ArgName; } S += FieldName + ";\n"; } // Output all "by ref" declarations. for (llvm::SmallPtrSet::iterator I = BlockByRefDecls.begin(), E = BlockByRefDecls.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 (isBlockPointerType((*I)->getType())) { S += "struct __block_impl *"; Constructor += ", void *" + ArgName; } else { Context->getPointerType((*I)->getType()).getAsStringInternal(FieldName, Context->PrintingPolicy); Context->getPointerType((*I)->getType()).getAsStringInternal(ArgName, Context->PrintingPolicy); Constructor += ", " + ArgName; } S += FieldName + "; // by ref\n"; } // Finish writing the constructor. // FIXME: handle NSConcreteGlobalBlock. Constructor += ", int flags=0) {\n"; Constructor += " impl.isa = 0/*&_NSConcreteStackBlock*/;\n impl.Size = sizeof("; Constructor += Tag + ");\n impl.Flags = flags;\n impl.FuncPtr = fp;\n"; if (hasCopyDisposeHelpers) Constructor += " copy = copyHelp;\n dispose = disposeHelp;\n"; // Initialize all "by copy" arguments. for (llvm::SmallPtrSet::iterator I = BlockByCopyDecls.begin(), E = BlockByCopyDecls.end(); I != E; ++I) { std::string Name = (*I)->getNameAsString(); Constructor += " "; if (isBlockPointerType((*I)->getType())) Constructor += Name + " = (struct __block_impl *)_"; else Constructor += Name + " = _"; Constructor += Name + ";\n"; } // Initialize all "by ref" arguments. for (llvm::SmallPtrSet::iterator I = BlockByRefDecls.begin(), E = BlockByRefDecls.end(); I != E; ++I) { std::string Name = (*I)->getNameAsString(); Constructor += " "; if (isBlockPointerType((*I)->getType())) Constructor += Name + " = (struct __block_impl *)_"; else Constructor += Name + " = _"; Constructor += Name + ";\n"; } } else { // Finish writing the constructor. // FIXME: handle NSConcreteGlobalBlock. Constructor += ", int flags=0) {\n"; Constructor += " impl.isa = 0/*&_NSConcreteStackBlock*/;\n impl.Size = sizeof("; Constructor += Tag + ");\n impl.Flags = flags;\n impl.FuncPtr = fp;\n"; if (hasCopyDisposeHelpers) Constructor += " copy = copyHelp;\n dispose = disposeHelp;\n"; } Constructor += " "; Constructor += "}\n"; S += Constructor; S += "};\n"; return S; } void RewriteBlocks::SynthesizeBlockLiterals(SourceLocation FunLocStart, const char *FunName) { // Insert closures that were part of the function. for (unsigned i = 0; i < Blocks.size(); i++) { CollectBlockDeclRefInfo(Blocks[i]); std::string Tag = "__" + std::string(FunName) + "_block_impl_" + utostr(i); std::string CI = SynthesizeBlockImpl(Blocks[i], Tag, ImportedBlockDecls.size() > 0); InsertText(FunLocStart, CI.c_str(), CI.size()); std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, Tag); InsertText(FunLocStart, CF.c_str(), CF.size()); if (ImportedBlockDecls.size()) { std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, Tag); InsertText(FunLocStart, HF.c_str(), HF.size()); } BlockDeclRefs.clear(); BlockByRefDecls.clear(); BlockByCopyDecls.clear(); BlockCallExprs.clear(); ImportedBlockDecls.clear(); } Blocks.clear(); RewrittenBlockExprs.clear(); } void RewriteBlocks::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); const char *FuncName = FD->getNameAsCString(); SynthesizeBlockLiterals(FunLocStart, FuncName); } void RewriteBlocks::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { SourceLocation FunLocStart = MD->getLocStart(); std::string FuncName = MD->getSelector().getAsString(); // Convert colons to underscores. std::string::size_type loc = 0; while ((loc = FuncName.find(":", loc)) != std::string::npos) FuncName.replace(loc, 1, "_"); SynthesizeBlockLiterals(FunLocStart, FuncName.c_str()); } void RewriteBlocks::GetBlockDeclRefExprs(Stmt *S) { for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); CI != E; ++CI) if (*CI) { if (BlockExpr *CBE = dyn_cast(*CI)) GetBlockDeclRefExprs(CBE->getBody()); else GetBlockDeclRefExprs(*CI); } // Handle specific things. if (BlockDeclRefExpr *CDRE = dyn_cast(S)) // FIXME: Handle enums. if (!isa(CDRE->getDecl())) BlockDeclRefs.push_back(CDRE); return; } void RewriteBlocks::GetBlockCallExprs(Stmt *S) { for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); CI != E; ++CI) if (*CI) { if (BlockExpr *CBE = dyn_cast(*CI)) GetBlockCallExprs(CBE->getBody()); else GetBlockCallExprs(*CI); } if (CallExpr *CE = dyn_cast(S)) { if (CE->getCallee()->getType()->isBlockPointerType()) { BlockCallExprs[dyn_cast(CE->getCallee())] = CE; } } return; } std::string RewriteBlocks::SynthesizeBlockCall(CallExpr *Exp) { // Navigate to relevant type information. const char *closureName = 0; const BlockPointerType *CPT = 0; if (const DeclRefExpr *DRE = dyn_cast(Exp->getCallee())) { closureName = DRE->getDecl()->getNameAsCString(); CPT = DRE->getType()->getAsBlockPointerType(); } else if (BlockDeclRefExpr *CDRE = dyn_cast(Exp->getCallee())) { closureName = CDRE->getDecl()->getNameAsCString(); CPT = CDRE->getType()->getAsBlockPointerType(); } else if (MemberExpr *MExpr = dyn_cast(Exp->getCallee())) { closureName = MExpr->getMemberDecl()->getNameAsCString(); CPT = MExpr->getType()->getAsBlockPointerType(); } else { assert(1 && "RewriteBlockClass: Bad type"); } assert(CPT && "RewriteBlockClass: Bad type"); const FunctionType *FT = CPT->getPointeeType()->getAsFunctionType(); assert(FT && "RewriteBlockClass: Bad type"); const FunctionProtoType *FTP = dyn_cast(FT); // FTP will be null for closures that don't take arguments. // Build a closure call - start with a paren expr to enforce precedence. std::string BlockCall = "("; // Synthesize the cast. BlockCall += "(" + Exp->getType().getAsString() + "(*)"; BlockCall += "(struct __block_impl *"; if (FTP) { for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), E = FTP->arg_type_end(); I && (I != E); ++I) BlockCall += ", " + (*I).getAsString(); } BlockCall += "))"; // close the argument list and paren expression. // Invoke the closure. We need to cast it since the declaration type is // bogus (it's a function pointer type) BlockCall += "((struct __block_impl *)"; std::string closureExprBufStr; llvm::raw_string_ostream closureExprBuf(closureExprBufStr); Exp->getCallee()->printPretty(closureExprBuf, *Context); BlockCall += closureExprBuf.str(); BlockCall += ")->FuncPtr)"; // Add the arguments. BlockCall += "((struct __block_impl *)"; BlockCall += closureExprBuf.str(); for (CallExpr::arg_iterator I = Exp->arg_begin(), E = Exp->arg_end(); I != E; ++I) { std::string syncExprBufS; llvm::raw_string_ostream Buf(syncExprBufS); (*I)->printPretty(Buf, *Context); BlockCall += ", " + Buf.str(); } return BlockCall; } void RewriteBlocks::RewriteBlockCall(CallExpr *Exp) { std::string BlockCall = SynthesizeBlockCall(Exp); const char *startBuf = SM->getCharacterData(Exp->getLocStart()); const char *endBuf = SM->getCharacterData(Exp->getLocEnd()); ReplaceText(Exp->getLocStart(), endBuf-startBuf, BlockCall.c_str(), BlockCall.size()); } void RewriteBlocks::RewriteBlockDeclRefExpr(BlockDeclRefExpr *BDRE) { // FIXME: Add more elaborate code generation required by the ABI. InsertText(BDRE->getLocStart(), "*", 1); } void RewriteBlocks::RewriteCastExpr(CastExpr *CE) { SourceLocation LocStart = CE->getLocStart(); SourceLocation LocEnd = CE->getLocEnd(); if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) return; const char *startBuf = SM->getCharacterData(LocStart); const char *endBuf = SM->getCharacterData(LocEnd); // advance the location to startArgList. const char *argPtr = startBuf; while (*argPtr++ && (argPtr < endBuf)) { switch (*argPtr) { case '^': // Replace the '^' with '*'. LocStart = LocStart.getFileLocWithOffset(argPtr-startBuf); ReplaceText(LocStart, 1, "*", 1); break; } } return; } void RewriteBlocks::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.getFileLocWithOffset(startArgList-startBuf); assert((DeclLoc.isValid()) && "Invalid DeclLoc"); const char *argPtr = startArgList; while (*argPtr++ && parenCount) { switch (*argPtr) { case '^': // Replace the '^' with '*'. DeclLoc = DeclLoc.getFileLocWithOffset(argPtr-startArgList); ReplaceText(DeclLoc, 1, "*", 1); break; case '(': parenCount++; break; case ')': parenCount--; break; } } return; } bool RewriteBlocks::PointerTypeTakesAnyBlockArguments(QualType QT) { const FunctionProtoType *FTP; const PointerType *PT = QT->getAsPointerType(); if (PT) { FTP = PT->getPointeeType()->getAsFunctionProtoType(); } else { const BlockPointerType *BPT = QT->getAsBlockPointerType(); assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); FTP = BPT->getPointeeType()->getAsFunctionProtoType(); } if (FTP) { for (FunctionProtoType::arg_type_iterator I = FTP->arg_type_begin(), E = FTP->arg_type_end(); I != E; ++I) if (isBlockPointerType(*I)) return true; } return false; } void RewriteBlocks::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 RewriteBlocks::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 (TypedefDecl *TDD = dyn_cast(ND)) DeclT = TDD->getUnderlyingType(); else if (FieldDecl *FD = dyn_cast(ND)) DeclT = FD->getType(); else assert(0 && "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--; // *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. DeclLoc = DeclLoc.getFileLocWithOffset(startBuf-endBuf); ReplaceText(DeclLoc, 1, "*", 1); } if (PointerTypeTakesAnyBlockArguments(DeclT)) { // Replace the '^' with '*' for arguments. DeclLoc = ND->getLocation(); startBuf = SM->getCharacterData(DeclLoc); const char *argListBegin, *argListEnd; GetExtentOfArgList(startBuf, argListBegin, argListEnd); while (argListBegin < argListEnd) { if (*argListBegin == '^') { SourceLocation CaretLoc = DeclLoc.getFileLocWithOffset(argListBegin-startBuf); ReplaceText(CaretLoc, 1, "*", 1); } argListBegin++; } } return; } void RewriteBlocks::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]->isByRef()) BlockByCopyDecls.insert(BlockDeclRefs[i]->getDecl()); // Unique all "by ref" declarations. for (unsigned i = 0; i < BlockDeclRefs.size(); i++) if (BlockDeclRefs[i]->isByRef()) { BlockByRefDecls.insert(BlockDeclRefs[i]->getDecl()); } // Find any imported blocks...they will need special attention. for (unsigned i = 0; i < BlockDeclRefs.size(); i++) if (isBlockPointerType(BlockDeclRefs[i]->getType())) { GetBlockCallExprs(Blocks[i]); ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); } } } std::string RewriteBlocks::SynthesizeBlockInitExpr(BlockExpr *Exp, VarDecl *VD) { Blocks.push_back(Exp); CollectBlockDeclRefInfo(Exp); std::string FuncName; if (CurFunctionDef) FuncName = std::string(CurFunctionDef->getNameAsString()); else if (CurMethodDef) { FuncName = CurMethodDef->getSelector().getAsString(); // Convert colons to underscores. std::string::size_type loc = 0; while ((loc = FuncName.find(":", loc)) != std::string::npos) FuncName.replace(loc, 1, "_"); } else if (VD) FuncName = std::string(VD->getNameAsString()); std::string BlockNumber = utostr(Blocks.size()-1); std::string Tag = "__" + FuncName + "_block_impl_" + BlockNumber; std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; std::string FunkTypeStr; // Get a pointer to the function type so we can cast appropriately. Context->getPointerType(QualType(Exp->getFunctionType(),0)) .getAsStringInternal(FunkTypeStr, Context->PrintingPolicy); // Rewrite the closure block with a compound literal. The first cast is // to prevent warnings from the C compiler. std::string Init = "(" + FunkTypeStr; Init += ")&" + Tag; // Initialize the block function. Init += "((void*)" + Func; if (ImportedBlockDecls.size()) { std::string Buf = "__" + FuncName + "_block_copy_" + BlockNumber; Init += ",(void*)" + Buf; Buf = "__" + FuncName + "_block_dispose_" + BlockNumber; Init += ",(void*)" + Buf; } // Add initializers for any closure decl refs. if (BlockDeclRefs.size()) { // Output all "by copy" declarations. for (llvm::SmallPtrSet::iterator I = BlockByCopyDecls.begin(), E = BlockByCopyDecls.end(); I != E; ++I) { Init += ","; if (isObjCType((*I)->getType())) { Init += "[["; Init += (*I)->getNameAsString(); Init += " retain] autorelease]"; } else if (isBlockPointerType((*I)->getType())) { Init += "(void *)"; Init += (*I)->getNameAsString(); } else { Init += (*I)->getNameAsString(); } } // Output all "by ref" declarations. for (llvm::SmallPtrSet::iterator I = BlockByRefDecls.begin(), E = BlockByRefDecls.end(); I != E; ++I) { Init += ",&"; Init += (*I)->getNameAsString(); } } Init += ")"; BlockDeclRefs.clear(); BlockByRefDecls.clear(); BlockByCopyDecls.clear(); ImportedBlockDecls.clear(); return Init; } //===----------------------------------------------------------------------===// // Function Body / Expression rewriting //===----------------------------------------------------------------------===// Stmt *RewriteBlocks::RewriteFunctionBody(Stmt *S) { // Start by rewriting all children. for (Stmt::child_iterator CI = S->child_begin(), E = S->child_end(); CI != E; ++CI) if (*CI) { if (BlockExpr *CBE = dyn_cast(*CI)) { Stmt *newStmt = RewriteFunctionBody(CBE->getBody()); if (newStmt) *CI = newStmt; // We've just rewritten the block body in place. // Now we snarf the rewritten text and stash it away for later use. std::string S = Rewrite.getRewritenText(CBE->getSourceRange()); RewrittenBlockExprs[CBE] = S; std::string Init = SynthesizeBlockInitExpr(CBE); // Do the rewrite, using S.size() which contains the rewritten size. ReplaceText(CBE->getLocStart(), S.size(), Init.c_str(), Init.size()); } else { Stmt *newStmt = RewriteFunctionBody(*CI); if (newStmt) *CI = newStmt; } } // Handle specific things. if (CallExpr *CE = dyn_cast(S)) { if (CE->getCallee()->getType()->isBlockPointerType()) RewriteBlockCall(CE); } if (CastExpr *CE = dyn_cast(S)) { RewriteCastExpr(CE); } if (DeclStmt *DS = dyn_cast(S)) { 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 (isBlockPointerType(ND->getType())) RewriteBlockPointerDecl(ND); else if (ND->getType()->isFunctionPointerType()) CheckFunctionPointerDecl(ND->getType(), ND); } if (TypedefDecl *TD = dyn_cast(SD)) { if (isBlockPointerType(TD->getUnderlyingType())) RewriteBlockPointerDecl(TD); else if (TD->getUnderlyingType()->isFunctionPointerType()) CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); } } } // Handle specific things. if (BlockDeclRefExpr *BDRE = dyn_cast(S)) { if (BDRE->isByRef()) RewriteBlockDeclRefExpr(BDRE); } // Return this stmt unmodified. return S; } void RewriteBlocks::RewriteFunctionProtoType(QualType funcType, NamedDecl *D) { if (FunctionProtoType *fproto = dyn_cast(funcType)) { for (FunctionProtoType::arg_type_iterator I = fproto->arg_type_begin(), E = fproto->arg_type_end(); I && (I != E); ++I) if (isBlockPointerType(*I)) { // All the args are checked/rewritten. Don't call twice! RewriteBlockPointerDecl(D); break; } } } void RewriteBlocks::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { const PointerType *PT = funcType->getAsPointerType(); if (PT && PointerTypeTakesAnyBlockArguments(funcType)) RewriteFunctionProtoType(PT->getPointeeType(), ND); } /// HandleDeclInMainFile - This is called for each top-level decl defined in the /// main file of the input. void RewriteBlocks::HandleDeclInMainFile(Decl *D) { if (FunctionDecl *FD = dyn_cast(D)) { // 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. RewriteFunctionProtoType(FD->getType(), FD); // FIXME: Handle CXXTryStmt if (CompoundStmt *Body = FD->getCompoundBody(*Context)) { CurFunctionDef = FD; FD->setBody(cast_or_null(RewriteFunctionBody(Body))); // This synthesizes and inserts the block "impl" struct, invoke function, // and any copy/dispose helper functions. InsertBlockLiteralsWithinFunction(FD); CurFunctionDef = 0; } return; } if (ObjCMethodDecl *MD = dyn_cast(D)) { RewriteMethodDecl(MD); if (Stmt *Body = MD->getBody(*Context)) { CurMethodDef = MD; RewriteFunctionBody(Body); InsertBlockLiteralsWithinMethod(MD); CurMethodDef = 0; } } if (VarDecl *VD = dyn_cast(D)) { if (isBlockPointerType(VD->getType())) { RewriteBlockPointerDecl(VD); if (VD->getInit()) { if (BlockExpr *CBE = dyn_cast(VD->getInit())) { RewriteFunctionBody(CBE->getBody(*Context)); // We've just rewritten the block body in place. // Now we snarf the rewritten text and stash it away for later use. std::string S = Rewrite.getRewritenText(CBE->getSourceRange()); RewrittenBlockExprs[CBE] = S; std::string Init = SynthesizeBlockInitExpr(CBE, VD); // Do the rewrite, using S.size() which contains the rewritten size. ReplaceText(CBE->getLocStart(), S.size(), Init.c_str(), Init.size()); SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getNameAsCString()); } else if (CastExpr *CE = dyn_cast(VD->getInit())) { RewriteCastExpr(CE); } } } else if (VD->getType()->isFunctionPointerType()) { CheckFunctionPointerDecl(VD->getType(), VD); if (VD->getInit()) { if (CastExpr *CE = dyn_cast(VD->getInit())) { RewriteCastExpr(CE); } } } return; } if (TypedefDecl *TD = dyn_cast(D)) { if (isBlockPointerType(TD->getUnderlyingType())) RewriteBlockPointerDecl(TD); else if (TD->getUnderlyingType()->isFunctionPointerType()) CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); return; } if (RecordDecl *RD = dyn_cast(D)) { if (RD->isDefinition()) { for (RecordDecl::field_iterator i = RD->field_begin(*Context), e = RD->field_end(*Context); i != e; ++i) { FieldDecl *FD = *i; if (isBlockPointerType(FD->getType())) RewriteBlockPointerDecl(FD); } } return; } }