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