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: 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) { } 45 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: 55 RSJavaPackageNamePragmaHandler(llvm::StringRef Name, RSContext *Context) 56 : RSPragmaHandler(Name, Context) { } 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 } 120 }; 121 122 class RSReflectLicensePragmaHandler : public RSPragmaHandler { 123 private: 124 void handleItem(const std::string &Item) { 125 mContext->addPragma(this->getName(), Item); 126 mContext->setLicenseNote(Item); 127 } 128 129 public: 130 RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context) 131 : RSPragmaHandler(Name, Context) { } 132 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: 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: 160 RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context) 161 : RSPragmaHandler(Name, Context) { } 162 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: 175 RSPrecisionPragmaHandler(llvm::StringRef Name, RSContext *Context) 176 : RSPragmaHandler(Name, Context) {} 177 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 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 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 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 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 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