• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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