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