slang_rs_pragma_handler.cpp revision d09df09ae1e354c4fa595ab421b365ce5cd58cf3
1/*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "slang_rs_pragma_handler.h"
18
19#include <sstream>
20#include <string>
21
22#include "clang/Basic/TokenKinds.h"
23
24#include "clang/Lex/LiteralSupport.h"
25#include "clang/Lex/Preprocessor.h"
26#include "clang/Lex/Token.h"
27
28#include "slang_rs_context.h"
29
30namespace slang {
31
32namespace {  // Anonymous namespace
33
34class RSExportTypePragmaHandler : public RSPragmaHandler {
35 private:
36  void handleItem(const std::string &Item) {
37    mContext->addPragma(this->getName(), Item);
38    mContext->addExportType(Item);
39  }
40
41 public:
42  RSExportTypePragmaHandler(llvm::StringRef Name, RSContext *Context)
43      : RSPragmaHandler(Name, Context) { return; }
44
45  void HandlePragma(clang::Preprocessor &PP, clang::Token &FirstToken) {
46    this->handleItemListPragma(PP, FirstToken);
47  }
48};
49
50class RSJavaPackageNamePragmaHandler : public RSPragmaHandler {
51 public:
52  RSJavaPackageNamePragmaHandler(llvm::StringRef Name, RSContext *Context)
53      : RSPragmaHandler(Name, Context) { return; }
54
55  void HandlePragma(clang::Preprocessor &PP, clang::Token &FirstToken) {
56    // FIXME: Need to validate the extracted package name from pragma.
57    // Currently "all chars" specified in pragma will be treated as package
58    // name.
59    //
60    // 18.1 The Grammar of the Java Programming Language
61    // (http://java.sun.com/docs/books/jls/third_edition/html/syntax.html#18.1)
62    //
63    // CompilationUnit:
64    //     [[Annotations] package QualifiedIdentifier   ;  ] {ImportDeclaration}
65    //     {TypeDeclaration}
66    //
67    // QualifiedIdentifier:
68    //     Identifier { . Identifier }
69    //
70    // Identifier:
71    //     IDENTIFIER
72    //
73    // 3.8 Identifiers
74    // (http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.8)
75    //
76    //
77
78    clang::Token &PragmaToken = FirstToken;
79    std::string PackageName;
80
81    // Skip first token, "java_package_name"
82    PP.LexUnexpandedToken(PragmaToken);
83
84    // Now, the current token must be clang::tok::lpara
85    if (PragmaToken.isNot(clang::tok::l_paren))
86      return;
87
88    while (PragmaToken.isNot(clang::tok::eom)) {
89      // Lex package name
90      PP.LexUnexpandedToken(PragmaToken);
91
92      bool Invalid;
93      std::string Spelling = PP.getSpelling(PragmaToken, &Invalid);
94      if (!Invalid)
95        PackageName.append(Spelling);
96
97      // Pre-mature end (syntax error will be triggered by preprocessor later)
98      if (PragmaToken.is(clang::tok::eom) || PragmaToken.is(clang::tok::eof)) {
99        break;
100      } else {
101        // Next token is ')' (end of pragma)
102        const clang::Token &NextTok = PP.LookAhead(0);
103        if (NextTok.is(clang::tok::r_paren)) {
104          mContext->addPragma(this->getName(), PackageName);
105          mContext->setReflectJavaPackageName(PackageName);
106          // Lex until meets clang::tok::eom
107          do {
108            PP.LexUnexpandedToken(PragmaToken);
109          } while (PragmaToken.isNot(clang::tok::eom));
110          break;
111        }
112      }
113    }
114    return;
115  }
116};
117
118class RSReflectLicensePragmaHandler : public RSPragmaHandler {
119 private:
120  void handleItem(const std::string &Item) {
121    mContext->addPragma(this->getName(), Item);
122    mContext->setLicenseNote(Item);
123  }
124
125 public:
126  RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context)
127      : RSPragmaHandler(Name, Context) { return; }
128
129  void HandlePragma(clang::Preprocessor &PP, clang::Token &FirstToken) {
130    this->handleOptionalStringLiteralParamPragma(PP, FirstToken);
131  }
132};
133
134class RSVersionPragmaHandler : public RSPragmaHandler {
135 private:
136  void handleInt(const int v) {
137    std::stringstream ss;
138    ss << v;
139    mContext->addPragma(this->getName(), ss.str());
140    mContext->setVersion(v);
141  }
142
143 public:
144  RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
145      : RSPragmaHandler(Name, Context) { return; }
146
147  void HandlePragma(clang::Preprocessor &PP, clang::Token &FirstToken) {
148    this->handleIntegerParamPragma(PP, FirstToken);
149  }
150};
151
152}  // namespace
153
154RSPragmaHandler *
155RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) {
156  return new RSExportTypePragmaHandler("export_type", Context);
157}
158
159RSPragmaHandler *
160RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) {
161  return new RSJavaPackageNamePragmaHandler("java_package_name", Context);
162}
163
164RSPragmaHandler *
165RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) {
166  return new RSReflectLicensePragmaHandler("set_reflect_license", Context);
167}
168
169RSPragmaHandler *
170RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) {
171  return new RSVersionPragmaHandler("version", Context);
172}
173
174void RSPragmaHandler::handleItemListPragma(clang::Preprocessor &PP,
175                                           clang::Token &FirstToken) {
176  clang::Token &PragmaToken = FirstToken;
177
178  // Skip first token, like "export_var"
179  PP.LexUnexpandedToken(PragmaToken);
180
181  // Now, the current token must be clang::tok::lpara
182  if (PragmaToken.isNot(clang::tok::l_paren))
183    return;
184
185  while (PragmaToken.isNot(clang::tok::eom)) {
186    // Lex variable name
187    PP.LexUnexpandedToken(PragmaToken);
188    if (PragmaToken.is(clang::tok::identifier))
189      this->handleItem(PP.getSpelling(PragmaToken));
190    else
191      break;
192
193    assert(PragmaToken.isNot(clang::tok::eom));
194
195    PP.LexUnexpandedToken(PragmaToken);
196
197    if (PragmaToken.isNot(clang::tok::comma))
198      break;
199  }
200  return;
201}
202
203void RSPragmaHandler::handleNonParamPragma(clang::Preprocessor &PP,
204                                           clang::Token &FirstToken) {
205  clang::Token &PragmaToken = FirstToken;
206
207  // Skip first token, like "export_var_all"
208  PP.LexUnexpandedToken(PragmaToken);
209
210  // Should be end immediately
211  if (PragmaToken.isNot(clang::tok::eom))
212    fprintf(stderr, "RSPragmaHandler::handleNonParamPragma: "
213                    "expected a clang::tok::eom\n");
214  return;
215}
216
217void RSPragmaHandler::handleOptionalStringLiteralParamPragma(
218    clang::Preprocessor &PP, clang::Token &FirstToken) {
219  clang::Token &PragmaToken = FirstToken;
220
221  // Skip first token, like "set_reflect_license"
222  PP.LexUnexpandedToken(PragmaToken);
223
224  // Now, the current token must be clang::tok::lpara
225  if (PragmaToken.isNot(clang::tok::l_paren))
226    return;
227
228  // If not ')', eat the following string literal as the license
229  PP.LexUnexpandedToken(PragmaToken);
230  if (PragmaToken.isNot(clang::tok::r_paren)) {
231    // Eat the whole string literal
232    clang::StringLiteralParser StringLiteral(&PragmaToken, 1, PP);
233    if (StringLiteral.hadError)
234      fprintf(stderr, "RSPragmaHandler::handleOptionalStringLiteralParamPragma"
235                      ": illegal string literal\n");
236    else
237      this->handleItem(std::string(StringLiteral.GetString()));
238
239    // The current token should be clang::tok::r_para
240    PP.LexUnexpandedToken(PragmaToken);
241    if (PragmaToken.isNot(clang::tok::r_paren))
242      fprintf(stderr, "RSPragmaHandler::handleOptionalStringLiteralParamPragma"
243                      ": expected a ')'\n");
244  } else {
245    // If no argument, remove the license
246    this->handleItem("");
247  }
248}
249
250void RSPragmaHandler::handleIntegerParamPragma(
251    clang::Preprocessor &PP, clang::Token &FirstToken) {
252  clang::Token &PragmaToken = FirstToken;
253
254  // Skip first token, like "version"
255  PP.LexUnexpandedToken(PragmaToken);
256
257  // Now, the current token must be clang::tok::lpara
258  if (PragmaToken.isNot(clang::tok::l_paren))
259    return;
260  PP.LexUnexpandedToken(PragmaToken);
261
262  if (PragmaToken.is(clang::tok::numeric_constant)) {
263    clang::NumericLiteralParser NumericLiteral(PragmaToken.getLiteralData(),
264        PragmaToken.getLiteralData() + PragmaToken.getLength(),
265        PragmaToken.getLocation(), PP);
266    if (NumericLiteral.hadError) {
267      fprintf(stderr, "RSPragmaHandler::handleIntegerParamPragma"
268                      ": illegal numeric literal\n");
269    } else {
270      llvm::APInt Val(32, 0);
271      NumericLiteral.GetIntegerValue(Val);
272      this->handleInt(static_cast<int>(Val.getSExtValue()));
273    }
274    PP.LexUnexpandedToken(PragmaToken);
275  } else {
276    // If no argument, set the version to 0
277    this->handleInt(0);
278  }
279
280  if (PragmaToken.isNot(clang::tok::r_paren)) {
281    fprintf(stderr, "RSPragmaHandler::handleIntegerParamPragma"
282                    ": expected a ')'\n");
283  }
284
285  do {
286    PP.LexUnexpandedToken(PragmaToken);
287  } while (PragmaToken.isNot(clang::tok::eom));
288}
289
290}  // namespace slang
291