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) { return; }
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) { return; }
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 return;
120 }
121 };
122
123 class RSReflectLicensePragmaHandler : public RSPragmaHandler {
124 private:
handleItem(const std::string & Item)125 void handleItem(const std::string &Item) {
126 mContext->addPragma(this->getName(), Item);
127 mContext->setLicenseNote(Item);
128 }
129
130 public:
RSReflectLicensePragmaHandler(llvm::StringRef Name,RSContext * Context)131 RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context)
132 : RSPragmaHandler(Name, Context) { return; }
133
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)134 void HandlePragma(clang::Preprocessor &PP,
135 clang::PragmaIntroducerKind Introducer,
136 clang::Token &FirstToken) {
137 this->handleOptionalStringLiteralParamPragma(PP, FirstToken);
138 }
139 };
140
141 class RSVersionPragmaHandler : public RSPragmaHandler {
142 private:
handleInt(clang::Preprocessor & PP,clang::Token & Tok,const int v)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:
RSVersionPragmaHandler(llvm::StringRef Name,RSContext * Context)161 RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
162 : RSPragmaHandler(Name, Context) { return; }
163
HandlePragma(clang::Preprocessor & PP,clang::PragmaIntroducerKind Introducer,clang::Token & FirstToken)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
173 RSPragmaHandler *
CreatePragmaExportTypeHandler(RSContext * Context)174 RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) {
175 return new RSExportTypePragmaHandler("export_type", Context);
176 }
177
178 RSPragmaHandler *
CreatePragmaJavaPackageNameHandler(RSContext * Context)179 RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) {
180 return new RSJavaPackageNamePragmaHandler("java_package_name", Context);
181 }
182
183 RSPragmaHandler *
CreatePragmaReflectLicenseHandler(RSContext * Context)184 RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) {
185 return new RSReflectLicensePragmaHandler("set_reflect_license", Context);
186 }
187
188 RSPragmaHandler *
CreatePragmaVersionHandler(RSContext * Context)189 RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) {
190 return new RSVersionPragmaHandler("version", Context);
191 }
192
handleItemListPragma(clang::Preprocessor & PP,clang::Token & FirstToken)193 void 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
handleNonParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)222 void 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
handleOptionalStringLiteralParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)241 void 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
handleIntegerParamPragma(clang::Preprocessor & PP,clang::Token & FirstToken)279 void 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