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(clang::Preprocessor &PP,
144                 clang::Token &Tok,
145                 const int v) {
146    if (v != 1) {
147      PP.Diag(Tok,
148              PP.getDiagnostics().getCustomDiagID(
149                  clang::DiagnosticsEngine::Error,
150                  "pragma for version in source file must be set to 1"));
151      mContext->setVersion(1);
152      return;
153    }
154    std::stringstream ss;
155    ss << v;
156    mContext->addPragma(this->getName(), ss.str());
157    mContext->setVersion(v);
158  }
159
160 public:
161  RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
162      : RSPragmaHandler(Name, Context) { return; }
163
164  void HandlePragma(clang::Preprocessor &PP,
165                    clang::PragmaIntroducerKind Introducer,
166                    clang::Token &FirstToken) {
167    this->handleIntegerParamPragma(PP, FirstToken);
168  }
169};
170
171}  // namespace
172
173RSPragmaHandler *
174RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) {
175  return new RSExportTypePragmaHandler("export_type", Context);
176}
177
178RSPragmaHandler *
179RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) {
180  return new RSJavaPackageNamePragmaHandler("java_package_name", Context);
181}
182
183RSPragmaHandler *
184RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) {
185  return new RSReflectLicensePragmaHandler("set_reflect_license", Context);
186}
187
188RSPragmaHandler *
189RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) {
190  return new RSVersionPragmaHandler("version", Context);
191}
192
193void RSPragmaHandler::handleItemListPragma(clang::Preprocessor &PP,
194                                           clang::Token &FirstToken) {
195  clang::Token &PragmaToken = FirstToken;
196
197  // Skip first token, like "export_var"
198  PP.LexUnexpandedToken(PragmaToken);
199
200  // Now, the current token must be clang::tok::lpara
201  if (PragmaToken.isNot(clang::tok::l_paren))
202    return;
203
204  while (PragmaToken.isNot(clang::tok::eod)) {
205    // Lex variable name
206    PP.LexUnexpandedToken(PragmaToken);
207    if (PragmaToken.is(clang::tok::identifier))
208      this->handleItem(PP.getSpelling(PragmaToken));
209    else
210      break;
211
212    slangAssert(PragmaToken.isNot(clang::tok::eod));
213
214    PP.LexUnexpandedToken(PragmaToken);
215
216    if (PragmaToken.isNot(clang::tok::comma))
217      break;
218  }
219  return;
220}
221
222void RSPragmaHandler::handleNonParamPragma(clang::Preprocessor &PP,
223                                           clang::Token &FirstToken) {
224  clang::Token &PragmaToken = FirstToken;
225
226  // Skip first token, like "export_var_all"
227  PP.LexUnexpandedToken(PragmaToken);
228
229  // Should be end immediately
230  if (PragmaToken.isNot(clang::tok::eod))
231    if (PragmaToken.isNot(clang::tok::r_paren)) {
232      PP.Diag(PragmaToken,
233              PP.getDiagnostics().getCustomDiagID(
234                  clang::DiagnosticsEngine::Error,
235                  "expected a ')'"));
236      return;
237    }
238  return;
239}
240
241void RSPragmaHandler::handleOptionalStringLiteralParamPragma(
242    clang::Preprocessor &PP, clang::Token &FirstToken) {
243  clang::Token &PragmaToken = FirstToken;
244
245  // Skip first token, like "set_reflect_license"
246  PP.LexUnexpandedToken(PragmaToken);
247
248  // Now, the current token must be clang::tok::lpara
249  if (PragmaToken.isNot(clang::tok::l_paren))
250    return;
251
252  // If not ')', eat the following string literal as the license
253  PP.LexUnexpandedToken(PragmaToken);
254  if (PragmaToken.isNot(clang::tok::r_paren)) {
255    // Eat the whole string literal
256    clang::StringLiteralParser StringLiteral(&PragmaToken, 1, PP);
257    if (StringLiteral.hadError) {
258      // Diagnostics will be generated automatically
259      return;
260    } else {
261      this->handleItem(std::string(StringLiteral.GetString()));
262    }
263
264    // The current token should be clang::tok::r_para
265    PP.LexUnexpandedToken(PragmaToken);
266    if (PragmaToken.isNot(clang::tok::r_paren)) {
267      PP.Diag(PragmaToken,
268              PP.getDiagnostics().getCustomDiagID(
269                  clang::DiagnosticsEngine::Error,
270                  "expected a ')'"));
271      return;
272    }
273  } else {
274    // If no argument, remove the license
275    this->handleItem("");
276  }
277}
278
279void RSPragmaHandler::handleIntegerParamPragma(
280    clang::Preprocessor &PP, clang::Token &FirstToken) {
281  clang::Token &PragmaToken = FirstToken;
282
283  // Skip first token, like "version"
284  PP.LexUnexpandedToken(PragmaToken);
285
286  // Now, the current token must be clang::tok::lpara
287  if (PragmaToken.isNot(clang::tok::l_paren)) {
288    // If no argument, set the version to 0
289    this->handleInt(PP, PragmaToken, 0);
290    return;
291  }
292  PP.LexUnexpandedToken(PragmaToken);
293
294  if (PragmaToken.is(clang::tok::numeric_constant)) {
295    clang::NumericLiteralParser NumericLiteral(PragmaToken.getLiteralData(),
296        PragmaToken.getLiteralData() + PragmaToken.getLength(),
297        PragmaToken.getLocation(), PP);
298    if (NumericLiteral.hadError) {
299      // Diagnostics will be generated automatically
300      return;
301    } else {
302      llvm::APInt Val(32, 0);
303      NumericLiteral.GetIntegerValue(Val);
304      this->handleInt(PP, PragmaToken, static_cast<int>(Val.getSExtValue()));
305    }
306    PP.LexUnexpandedToken(PragmaToken);
307  } else {
308    // If no argument, set the version to 0
309    this->handleInt(PP, PragmaToken, 0);
310  }
311
312  if (PragmaToken.isNot(clang::tok::r_paren)) {
313    PP.Diag(PragmaToken,
314            PP.getDiagnostics().getCustomDiagID(
315                clang::DiagnosticsEngine::Error,
316                "expected a ')'"));
317    return;
318  }
319
320  do {
321    PP.LexUnexpandedToken(PragmaToken);
322  } while (PragmaToken.isNot(clang::tok::eod));
323}
324
325}  // namespace slang
326