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) { return; } 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) { return; } 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 return; 120 } 121}; 122 123class RSReflectLicensePragmaHandler : public RSPragmaHandler { 124 private: 125 void handleItem(const std::string &Item) { 126 mContext->addPragma(this->getName(), Item); 127 mContext->setLicenseNote(Item); 128 } 129 130 public: 131 RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context) 132 : RSPragmaHandler(Name, Context) { return; } 133 134 void HandlePragma(clang::Preprocessor &PP, 135 clang::PragmaIntroducerKind Introducer, 136 clang::Token &FirstToken) { 137 this->handleOptionalStringLiteralParamPragma(PP, FirstToken); 138 } 139}; 140 141class RSVersionPragmaHandler : public RSPragmaHandler { 142 private: 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: 161 RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context) 162 : RSPragmaHandler(Name, Context) { return; } 163 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 173RSPragmaHandler * 174RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) { 175 return new RSExportTypePragmaHandler("export_type", Context); 176} 177 178RSPragmaHandler * 179RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) { 180 return new RSJavaPackageNamePragmaHandler("java_package_name", Context); 181} 182 183RSPragmaHandler * 184RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) { 185 return new RSReflectLicensePragmaHandler("set_reflect_license", Context); 186} 187 188RSPragmaHandler * 189RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) { 190 return new RSVersionPragmaHandler("version", Context); 191} 192 193void 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 222void 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 241void 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 279void 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 llvm::SmallString<128> SpellingBuffer; 296 SpellingBuffer.resize(PragmaToken.getLength() + 1); 297 llvm::StringRef TokSpelling = PP.getSpelling(PragmaToken, SpellingBuffer); 298 clang::NumericLiteralParser NumericLiteral(TokSpelling, 299 PragmaToken.getLocation(), PP); 300 if (NumericLiteral.hadError) { 301 // Diagnostics will be generated automatically 302 return; 303 } else { 304 llvm::APInt Val(32, 0); 305 NumericLiteral.GetIntegerValue(Val); 306 this->handleInt(PP, PragmaToken, static_cast<int>(Val.getSExtValue())); 307 } 308 PP.LexUnexpandedToken(PragmaToken); 309 } else { 310 // If no argument, set the version to 0 311 this->handleInt(PP, PragmaToken, 0); 312 } 313 314 if (PragmaToken.isNot(clang::tok::r_paren)) { 315 PP.Diag(PragmaToken, 316 PP.getDiagnostics().getCustomDiagID( 317 clang::DiagnosticsEngine::Error, 318 "expected a ')'")); 319 return; 320 } 321 322 do { 323 PP.LexUnexpandedToken(PragmaToken); 324 } while (PragmaToken.isNot(clang::tok::eod)); 325} 326 327} // namespace slang 328