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) { }
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) { }
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  }
120};
121
122class RSReflectLicensePragmaHandler : public RSPragmaHandler {
123 private:
124  void handleItem(const std::string &Item) {
125    mContext->addPragma(this->getName(), Item);
126    mContext->setLicenseNote(Item);
127  }
128
129 public:
130  RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context)
131      : RSPragmaHandler(Name, Context) { }
132
133  void HandlePragma(clang::Preprocessor &PP,
134                    clang::PragmaIntroducerKind Introducer,
135                    clang::Token &FirstToken) {
136    this->handleOptionalStringLiteralParamPragma(PP, FirstToken);
137  }
138};
139
140class RSVersionPragmaHandler : public RSPragmaHandler {
141 private:
142  void handleInt(clang::Preprocessor &PP,
143                 clang::Token &Tok,
144                 const int v) {
145    if (v != 1) {
146      PP.Diag(Tok,
147              PP.getDiagnostics().getCustomDiagID(
148                  clang::DiagnosticsEngine::Error,
149                  "pragma for version in source file must be set to 1"));
150      mContext->setVersion(1);
151      return;
152    }
153    std::stringstream ss;
154    ss << v;
155    mContext->addPragma(this->getName(), ss.str());
156    mContext->setVersion(v);
157  }
158
159 public:
160  RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
161      : RSPragmaHandler(Name, Context) { }
162
163  void HandlePragma(clang::Preprocessor &PP,
164                    clang::PragmaIntroducerKind Introducer,
165                    clang::Token &FirstToken) {
166    this->handleIntegerParamPragma(PP, FirstToken);
167  }
168};
169
170// Handles the pragmas rs_fp_full, rs_fp_relaxed, and rs_fp_imprecise.
171// There's one instance of this handler for each of the above values.
172// Only getName() differs between the instances.
173class RSPrecisionPragmaHandler : public RSPragmaHandler {
174public:
175  RSPrecisionPragmaHandler(llvm::StringRef Name, RSContext *Context)
176      : RSPragmaHandler(Name, Context) {}
177
178  void HandlePragma(clang::Preprocessor &PP,
179                    clang::PragmaIntroducerKind Introducer,
180                    clang::Token &Token) {
181    std::string Precision = getName();
182    // We are deprecating rs_fp_imprecise.
183    if (Precision == "rs_fp_imprecise") {
184      PP.Diag(Token, PP.getDiagnostics().getCustomDiagID(
185                         clang::DiagnosticsEngine::Warning,
186                         "rs_fp_imprecise is deprecated.  Assuming "
187                         "rs_fp_relaxed instead."));
188      Precision = "rs_fp_relaxed";
189    }
190    // Check if we have already encountered a precision pragma already.
191    std::string PreviousPrecision = mContext->getPrecision();
192    if (!PreviousPrecision.empty()) {
193      // If the previous specified a different value, it's an error.
194      if (PreviousPrecision != Precision) {
195        PP.Diag(Token, PP.getDiagnostics().getCustomDiagID(
196                           clang::DiagnosticsEngine::Error,
197                           "Multiple float precisions specified.  Encountered "
198                           "%0 previously."))
199            << PreviousPrecision;
200      }
201      // Otherwise we ignore redundant entries.
202      return;
203    }
204
205    mContext->addPragma(Precision, "");
206    mContext->setPrecision(Precision);
207  }
208};
209
210}  // namespace
211
212void RSPragmaHandler::handleItemListPragma(clang::Preprocessor &PP,
213                                           clang::Token &FirstToken) {
214  clang::Token &PragmaToken = FirstToken;
215
216  // Skip first token, like "export_var"
217  PP.LexUnexpandedToken(PragmaToken);
218
219  // Now, the current token must be clang::tok::lpara
220  if (PragmaToken.isNot(clang::tok::l_paren))
221    return;
222
223  while (PragmaToken.isNot(clang::tok::eod)) {
224    // Lex variable name
225    PP.LexUnexpandedToken(PragmaToken);
226    if (PragmaToken.is(clang::tok::identifier))
227      this->handleItem(PP.getSpelling(PragmaToken));
228    else
229      break;
230
231    slangAssert(PragmaToken.isNot(clang::tok::eod));
232
233    PP.LexUnexpandedToken(PragmaToken);
234
235    if (PragmaToken.isNot(clang::tok::comma))
236      break;
237  }
238}
239
240void RSPragmaHandler::handleNonParamPragma(clang::Preprocessor &PP,
241                                           clang::Token &FirstToken) {
242  clang::Token &PragmaToken = FirstToken;
243
244  // Skip first token, like "export_var_all"
245  PP.LexUnexpandedToken(PragmaToken);
246
247  // Should be end immediately
248  if (PragmaToken.isNot(clang::tok::eod))
249    if (PragmaToken.isNot(clang::tok::r_paren)) {
250      PP.Diag(PragmaToken,
251              PP.getDiagnostics().getCustomDiagID(
252                  clang::DiagnosticsEngine::Error,
253                  "expected a ')'"));
254      return;
255    }
256}
257
258void RSPragmaHandler::handleOptionalStringLiteralParamPragma(
259    clang::Preprocessor &PP, clang::Token &FirstToken) {
260  clang::Token &PragmaToken = FirstToken;
261
262  // Skip first token, like "set_reflect_license"
263  PP.LexUnexpandedToken(PragmaToken);
264
265  // Now, the current token must be clang::tok::lpara
266  if (PragmaToken.isNot(clang::tok::l_paren))
267    return;
268
269  // If not ')', eat the following string literal as the license
270  PP.LexUnexpandedToken(PragmaToken);
271  if (PragmaToken.isNot(clang::tok::r_paren)) {
272    // Eat the whole string literal
273    clang::StringLiteralParser StringLiteral(PragmaToken, PP);
274    if (StringLiteral.hadError) {
275      // Diagnostics will be generated automatically
276      return;
277    } else {
278      this->handleItem(std::string(StringLiteral.GetString()));
279    }
280
281    // The current token should be clang::tok::r_para
282    PP.LexUnexpandedToken(PragmaToken);
283    if (PragmaToken.isNot(clang::tok::r_paren)) {
284      PP.Diag(PragmaToken,
285              PP.getDiagnostics().getCustomDiagID(
286                  clang::DiagnosticsEngine::Error,
287                  "expected a ')'"));
288      return;
289    }
290  } else {
291    // If no argument, remove the license
292    this->handleItem("");
293  }
294}
295
296void RSPragmaHandler::handleIntegerParamPragma(
297    clang::Preprocessor &PP, clang::Token &FirstToken) {
298  clang::Token &PragmaToken = FirstToken;
299
300  // Skip first token, like "version"
301  PP.LexUnexpandedToken(PragmaToken);
302
303  // Now, the current token must be clang::tok::lpara
304  if (PragmaToken.isNot(clang::tok::l_paren)) {
305    // If no argument, set the version to 0
306    this->handleInt(PP, PragmaToken, 0);
307    return;
308  }
309  PP.LexUnexpandedToken(PragmaToken);
310
311  if (PragmaToken.is(clang::tok::numeric_constant)) {
312    llvm::SmallString<128> SpellingBuffer;
313    SpellingBuffer.resize(PragmaToken.getLength() + 1);
314    llvm::StringRef TokSpelling = PP.getSpelling(PragmaToken, SpellingBuffer);
315    clang::NumericLiteralParser NumericLiteral(TokSpelling,
316        PragmaToken.getLocation(), PP);
317    if (NumericLiteral.hadError) {
318      // Diagnostics will be generated automatically
319      return;
320    } else {
321      llvm::APInt Val(32, 0);
322      NumericLiteral.GetIntegerValue(Val);
323      this->handleInt(PP, PragmaToken, static_cast<int>(Val.getSExtValue()));
324    }
325    PP.LexUnexpandedToken(PragmaToken);
326  } else {
327    // If no argument, set the version to 0
328    this->handleInt(PP, PragmaToken, 0);
329  }
330
331  if (PragmaToken.isNot(clang::tok::r_paren)) {
332    PP.Diag(PragmaToken,
333            PP.getDiagnostics().getCustomDiagID(
334                clang::DiagnosticsEngine::Error,
335                "expected a ')'"));
336    return;
337  }
338
339  do {
340    PP.LexUnexpandedToken(PragmaToken);
341  } while (PragmaToken.isNot(clang::tok::eod));
342}
343
344void AddPragmaHandlers(clang::Preprocessor &PP, RSContext *RsContext) {
345  // For #pragma rs export_type
346  PP.AddPragmaHandler("rs",
347                      new RSExportTypePragmaHandler("export_type", RsContext));
348
349  // For #pragma rs java_package_name
350  PP.AddPragmaHandler(
351      "rs", new RSJavaPackageNamePragmaHandler("java_package_name", RsContext));
352
353  // For #pragma rs set_reflect_license
354  PP.AddPragmaHandler(
355      "rs", new RSReflectLicensePragmaHandler("set_reflect_license", RsContext));
356
357  // For #pragma version
358  PP.AddPragmaHandler(new RSVersionPragmaHandler("version", RsContext));
359
360  // For #pragma rs_fp*
361  PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_full", RsContext));
362  PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_relaxed", RsContext));
363  PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_imprecise", RsContext));
364}
365
366
367}  // namespace slang
368