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 
31 namespace slang {
32 
33 namespace {  // Anonymous namespace
34 
35 class RSExportTypePragmaHandler : public RSPragmaHandler {
36  private:
handleItem(const std::string & Item)37   void handleItem(const std::string &Item) {
38     mContext->addPragma(this->getName(), Item);
39     mContext->addExportType(Item);
40   }
41 
42  public:
RSExportTypePragmaHandler(llvm::StringRef Name,RSContext * Context)43   RSExportTypePragmaHandler(llvm::StringRef Name, RSContext *Context)
44       : RSPragmaHandler(Name, Context) { }
45 
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)46   void HandlePragma(clang::Preprocessor &PP,
47                     clang::PragmaIntroducerKind Introducer,
48                     clang::Token &FirstToken) {
49     this->handleItemListPragma(PP, FirstToken);
50   }
51 };
52 
53 class RSJavaPackageNamePragmaHandler : public RSPragmaHandler {
54  public:
RSJavaPackageNamePragmaHandler(llvm::StringRef Name,RSContext * Context)55   RSJavaPackageNamePragmaHandler(llvm::StringRef Name, RSContext *Context)
56       : RSPragmaHandler(Name, Context) { }
57 
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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 
122 class RSReflectLicensePragmaHandler : public RSPragmaHandler {
123  private:
handleItem(const std::string & Item)124   void handleItem(const std::string &Item) {
125     mContext->addPragma(this->getName(), Item);
126     mContext->setLicenseNote(Item);
127   }
128 
129  public:
RSReflectLicensePragmaHandler(llvm::StringRef Name,RSContext * Context)130   RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context)
131       : RSPragmaHandler(Name, Context) { }
132 
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)133   void HandlePragma(clang::Preprocessor &PP,
134                     clang::PragmaIntroducerKind Introducer,
135                     clang::Token &FirstToken) {
136     this->handleOptionalStringLiteralParamPragma(PP, FirstToken);
137   }
138 };
139 
140 class RSVersionPragmaHandler : public RSPragmaHandler {
141  private:
handleInt(clang::Preprocessor & PP,clang::Token & Tok,const int v)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:
RSVersionPragmaHandler(llvm::StringRef Name,RSContext * Context)160   RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
161       : RSPragmaHandler(Name, Context) { }
162 
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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.
173 class RSPrecisionPragmaHandler : public RSPragmaHandler {
174 public:
RSPrecisionPragmaHandler(llvm::StringRef Name,RSContext * Context)175   RSPrecisionPragmaHandler(llvm::StringRef Name, RSContext *Context)
176       : RSPragmaHandler(Name, Context) {}
177 
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & Token)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 
handleItemListPragma(clang::Preprocessor & PP,clang::Token & FirstToken)212 void 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 
handleNonParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)240 void 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 
handleOptionalStringLiteralParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)258 void 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 
handleIntegerParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)296 void 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 
AddPragmaHandlers(clang::Preprocessor & PP,RSContext * RsContext)344 void 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