Clang Project

clang_source_code/lib/AST/OSLog.cpp
1// TODO: header template
2
3#include "clang/AST/OSLog.h"
4#include "clang/AST/Attr.h"
5#include "clang/AST/Decl.h"
6#include "clang/AST/DeclCXX.h"
7#include "clang/AST/ExprObjC.h"
8#include "clang/AST/FormatString.h"
9#include "clang/Basic/Builtins.h"
10#include "llvm/ADT/SmallBitVector.h"
11
12using namespace clang;
13
14using clang::analyze_os_log::OSLogBufferItem;
15using clang::analyze_os_log::OSLogBufferLayout;
16
17namespace {
18class OSLogFormatStringHandler
19    : public analyze_format_string::FormatStringHandler {
20private:
21  struct ArgData {
22    const Expr *E = nullptr;
23    Optional<OSLogBufferItem::KindKind;
24    Optional<unsignedSize;
25    Optional<const Expr *> Count;
26    Optional<const Expr *> Precision;
27    Optional<const Expr *> FieldWidth;
28    unsigned char Flags = 0;
29    StringRef MaskType;
30  };
31  SmallVector<ArgData4ArgsData;
32  ArrayRef<const Expr *> Args;
33
34  OSLogBufferItem::Kind
35  getKind(analyze_format_string::ConversionSpecifier::Kind K) {
36    switch (K) {
37    case clang::analyze_format_string::ConversionSpecifier::sArg// "%s"
38      return OSLogBufferItem::StringKind;
39    case clang::analyze_format_string::ConversionSpecifier::SArg// "%S"
40      return OSLogBufferItem::WideStringKind;
41    case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
42      return OSLogBufferItem::PointerKind;
43    case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg// "%@"
44      return OSLogBufferItem::ObjCObjKind;
45    case clang::analyze_format_string::ConversionSpecifier::PrintErrno// "%m"
46      return OSLogBufferItem::ErrnoKind;
47    default:
48      return OSLogBufferItem::ScalarKind;
49    }
50    }
51  }
52
53public:
54  OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
55    ArgsData.reserve(Args.size());
56  }
57
58  virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
59                                     const char *StartSpecifier,
60                                     unsigned SpecifierLen) {
61    if (!FS.consumesDataArgument() &&
62        FS.getConversionSpecifier().getKind() !=
63            clang::analyze_format_string::ConversionSpecifier::PrintErrno)
64      return true;
65
66    ArgsData.emplace_back();
67    unsigned ArgIndex = FS.getArgIndex();
68    if (ArgIndex < Args.size())
69      ArgsData.back().E = Args[ArgIndex];
70
71    // First get the Kind
72    ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
73    if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
74        !ArgsData.back().E) {
75      // missing argument
76      ArgsData.pop_back();
77      return false;
78    }
79
80    switch (FS.getConversionSpecifier().getKind()) {
81    case clang::analyze_format_string::ConversionSpecifier::sArg:   // "%s"
82    case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
83      auto &precision = FS.getPrecision();
84      switch (precision.getHowSpecified()) {
85      case clang::analyze_format_string::OptionalAmount::NotSpecified// "%s"
86        break;
87      case clang::analyze_format_string::OptionalAmount::Constant// "%.16s"
88        ArgsData.back().Size = precision.getConstantAmount();
89        break;
90      case clang::analyze_format_string::OptionalAmount::Arg// "%.*s"
91        ArgsData.back().Count = Args[precision.getArgIndex()];
92        break;
93      case clang::analyze_format_string::OptionalAmount::Invalid:
94        return false;
95      }
96      break;
97    }
98    case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
99      auto &precision = FS.getPrecision();
100      switch (precision.getHowSpecified()) {
101      case clang::analyze_format_string::OptionalAmount::NotSpecified// "%P"
102        return false// length must be supplied with pointer format specifier
103      case clang::analyze_format_string::OptionalAmount::Constant// "%.16P"
104        ArgsData.back().Size = precision.getConstantAmount();
105        break;
106      case clang::analyze_format_string::OptionalAmount::Arg// "%.*P"
107        ArgsData.back().Count = Args[precision.getArgIndex()];
108        break;
109      case clang::analyze_format_string::OptionalAmount::Invalid:
110        return false;
111      }
112      break;
113    }
114    default:
115      if (FS.getPrecision().hasDataArgument()) {
116        ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()];
117      }
118      break;
119    }
120    if (FS.getFieldWidth().hasDataArgument()) {
121      ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()];
122    }
123
124    if (FS.isSensitive())
125      ArgsData.back().Flags |= OSLogBufferItem::IsSensitive;
126    else if (FS.isPrivate())
127      ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
128    else if (FS.isPublic())
129      ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
130
131    ArgsData.back().MaskType = FS.getMaskType();
132    return true;
133  }
134
135  void computeLayout(ASTContext &CtxOSLogBufferLayout &Layoutconst {
136    Layout.Items.clear();
137    for (auto &Data : ArgsData) {
138      if (!Data.MaskType.empty()) {
139        CharUnits Size = CharUnits::fromQuantity(8);
140        Layout.Items.emplace_back(OSLogBufferItem::MaskKind, nullptr,
141                                  Size, 0, Data.MaskType);
142      }
143
144      if (Data.FieldWidth) {
145        CharUnits Size = Ctx.getTypeSizeInChars((*Data.FieldWidth)->getType());
146        Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.FieldWidth,
147                                  Size, 0);
148      }
149      if (Data.Precision) {
150        CharUnits Size = Ctx.getTypeSizeInChars((*Data.Precision)->getType());
151        Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.Precision,
152                                  Size, 0);
153      }
154      if (Data.Count) {
155        // "%.*P" has an extra "count" that we insert before the argument.
156        CharUnits Size = Ctx.getTypeSizeInChars((*Data.Count)->getType());
157        Layout.Items.emplace_back(OSLogBufferItem::CountKind, *Data.Count, Size,
158                                  0);
159      }
160      if (Data.Size)
161        Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size),
162                                  Data.Flags);
163      if (Data.Kind) {
164        CharUnits Size;
165        if (*Data.Kind == OSLogBufferItem::ErrnoKind)
166          Size = CharUnits::Zero();
167        else
168          Size = Ctx.getTypeSizeInChars(Data.E->getType());
169        Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags);
170      } else {
171        auto Size = Ctx.getTypeSizeInChars(Data.E->getType());
172        Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size,
173                                  Data.Flags);
174      }
175    }
176  }
177};
178// end anonymous namespace
179
180bool clang::analyze_os_log::computeOSLogBufferLayout(
181    ASTContext &Ctxconst CallExpr *EOSLogBufferLayout &Layout) {
182  ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs());
183
184  const Expr *StringArg;
185  ArrayRef<const Expr *> VarArgs;
186  switch (E->getBuiltinCallee()) {
187  case Builtin::BI__builtin_os_log_format_buffer_size:
188     (0) . __assert_fail ("E->getNumArgs() >= 1 && \"__builtin_os_log_format_buffer_size takes at least 1 argument\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 189, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(E->getNumArgs() >= 1 &&
189 (0) . __assert_fail ("E->getNumArgs() >= 1 && \"__builtin_os_log_format_buffer_size takes at least 1 argument\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 189, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">           "__builtin_os_log_format_buffer_size takes at least 1 argument");
190    StringArg = E->getArg(0);
191    VarArgs = Args.slice(1);
192    break;
193  case Builtin::BI__builtin_os_log_format:
194     (0) . __assert_fail ("E->getNumArgs() >= 2 && \"__builtin_os_log_format takes at least 2 arguments\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 195, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(E->getNumArgs() >= 2 &&
195 (0) . __assert_fail ("E->getNumArgs() >= 2 && \"__builtin_os_log_format takes at least 2 arguments\"", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 195, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">           "__builtin_os_log_format takes at least 2 arguments");
196    StringArg = E->getArg(1);
197    VarArgs = Args.slice(2);
198    break;
199  default:
200    llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
201  }
202
203  const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts());
204  isAscii() || Lit->isUTF8())", "/home/seafit/code_projects/clang_source/clang/lib/AST/OSLog.cpp", 204, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(Lit && (Lit->isAscii() || Lit->isUTF8()));
205  StringRef Data = Lit->getString();
206  OSLogFormatStringHandler H(VarArgs);
207  ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(),
208                    Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
209
210  H.computeLayout(CtxLayout);
211  return true;
212}
213