/* * Copyright 2010, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "slang_rs_pragma_handler.h" #include <sstream> #include <string> #include "clang/Basic/TokenKinds.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/Token.h" #include "slang_assert.h" #include "slang_rs_context.h" namespace slang { namespace { // Anonymous namespace class RSExportTypePragmaHandler : public RSPragmaHandler { private: void handleItem(const std::string &Item) { mContext->addPragma(this->getName(), Item); mContext->addExportType(Item); } public: RSExportTypePragmaHandler(llvm::StringRef Name, RSContext *Context) : RSPragmaHandler(Name, Context) { } void HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducerKind Introducer, clang::Token &FirstToken) { this->handleItemListPragma(PP, FirstToken); } }; class RSJavaPackageNamePragmaHandler : public RSPragmaHandler { public: RSJavaPackageNamePragmaHandler(llvm::StringRef Name, RSContext *Context) : RSPragmaHandler(Name, Context) { } void HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducerKind Introducer, clang::Token &FirstToken) { // FIXME: Need to validate the extracted package name from pragma. // Currently "all chars" specified in pragma will be treated as package // name. // // 18.1 The Grammar of the Java Programming Language // (http://java.sun.com/docs/books/jls/third_edition/html/syntax.html#18.1) // // CompilationUnit: // [[Annotations] package QualifiedIdentifier ; ] {ImportDeclaration} // {TypeDeclaration} // // QualifiedIdentifier: // Identifier { . Identifier } // // Identifier: // IDENTIFIER // // 3.8 Identifiers // (http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.8) // // clang::Token &PragmaToken = FirstToken; std::string PackageName; // Skip first token, "java_package_name" PP.LexUnexpandedToken(PragmaToken); // Now, the current token must be clang::tok::lpara if (PragmaToken.isNot(clang::tok::l_paren)) return; while (PragmaToken.isNot(clang::tok::eod)) { // Lex package name PP.LexUnexpandedToken(PragmaToken); bool Invalid; std::string Spelling = PP.getSpelling(PragmaToken, &Invalid); if (!Invalid) PackageName.append(Spelling); // Pre-mature end (syntax error will be triggered by preprocessor later) if (PragmaToken.is(clang::tok::eod) || PragmaToken.is(clang::tok::eof)) { break; } else { // Next token is ')' (end of pragma) const clang::Token &NextTok = PP.LookAhead(0); if (NextTok.is(clang::tok::r_paren)) { mContext->addPragma(this->getName(), PackageName); mContext->setReflectJavaPackageName(PackageName); // Lex until meets clang::tok::eod do { PP.LexUnexpandedToken(PragmaToken); } while (PragmaToken.isNot(clang::tok::eod)); break; } } } } }; class RSReflectLicensePragmaHandler : public RSPragmaHandler { private: void handleItem(const std::string &Item) { mContext->addPragma(this->getName(), Item); mContext->setLicenseNote(Item); } public: RSReflectLicensePragmaHandler(llvm::StringRef Name, RSContext *Context) : RSPragmaHandler(Name, Context) { } void HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducerKind Introducer, clang::Token &FirstToken) { this->handleOptionalStringLiteralParamPragma(PP, FirstToken); } }; class RSVersionPragmaHandler : public RSPragmaHandler { private: void handleInt(clang::Preprocessor &PP, clang::Token &Tok, const int v) { if (v != 1) { PP.Diag(Tok, PP.getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "pragma for version in source file must be set to 1")); mContext->setVersion(1); return; } std::stringstream ss; ss << v; mContext->addPragma(this->getName(), ss.str()); mContext->setVersion(v); } public: RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context) : RSPragmaHandler(Name, Context) { } void HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducerKind Introducer, clang::Token &FirstToken) { this->handleIntegerParamPragma(PP, FirstToken); } }; // Handles the pragmas rs_fp_full, rs_fp_relaxed, and rs_fp_imprecise. // There's one instance of this handler for each of the above values. // Only getName() differs between the instances. class RSPrecisionPragmaHandler : public RSPragmaHandler { public: RSPrecisionPragmaHandler(llvm::StringRef Name, RSContext *Context) : RSPragmaHandler(Name, Context) {} void HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducerKind Introducer, clang::Token &Token) { std::string Precision = getName(); // We are deprecating rs_fp_imprecise. if (Precision == "rs_fp_imprecise") { PP.Diag(Token, PP.getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Warning, "rs_fp_imprecise is deprecated. Assuming " "rs_fp_relaxed instead.")); Precision = "rs_fp_relaxed"; } // Check if we have already encountered a precision pragma already. std::string PreviousPrecision = mContext->getPrecision(); if (!PreviousPrecision.empty()) { // If the previous specified a different value, it's an error. if (PreviousPrecision != Precision) { PP.Diag(Token, PP.getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "Multiple float precisions specified. Encountered " "%0 previously.")) << PreviousPrecision; } // Otherwise we ignore redundant entries. return; } mContext->addPragma(Precision, ""); mContext->setPrecision(Precision); } }; } // namespace void RSPragmaHandler::handleItemListPragma(clang::Preprocessor &PP, clang::Token &FirstToken) { clang::Token &PragmaToken = FirstToken; // Skip first token, like "export_var" PP.LexUnexpandedToken(PragmaToken); // Now, the current token must be clang::tok::lpara if (PragmaToken.isNot(clang::tok::l_paren)) return; while (PragmaToken.isNot(clang::tok::eod)) { // Lex variable name PP.LexUnexpandedToken(PragmaToken); if (PragmaToken.is(clang::tok::identifier)) this->handleItem(PP.getSpelling(PragmaToken)); else break; slangAssert(PragmaToken.isNot(clang::tok::eod)); PP.LexUnexpandedToken(PragmaToken); if (PragmaToken.isNot(clang::tok::comma)) break; } } void RSPragmaHandler::handleNonParamPragma(clang::Preprocessor &PP, clang::Token &FirstToken) { clang::Token &PragmaToken = FirstToken; // Skip first token, like "export_var_all" PP.LexUnexpandedToken(PragmaToken); // Should be end immediately if (PragmaToken.isNot(clang::tok::eod)) if (PragmaToken.isNot(clang::tok::r_paren)) { PP.Diag(PragmaToken, PP.getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "expected a ')'")); return; } } void RSPragmaHandler::handleOptionalStringLiteralParamPragma( clang::Preprocessor &PP, clang::Token &FirstToken) { clang::Token &PragmaToken = FirstToken; // Skip first token, like "set_reflect_license" PP.LexUnexpandedToken(PragmaToken); // Now, the current token must be clang::tok::lpara if (PragmaToken.isNot(clang::tok::l_paren)) return; // If not ')', eat the following string literal as the license PP.LexUnexpandedToken(PragmaToken); if (PragmaToken.isNot(clang::tok::r_paren)) { // Eat the whole string literal clang::StringLiteralParser StringLiteral(PragmaToken, PP); if (StringLiteral.hadError) { // Diagnostics will be generated automatically return; } else { this->handleItem(std::string(StringLiteral.GetString())); } // The current token should be clang::tok::r_para PP.LexUnexpandedToken(PragmaToken); if (PragmaToken.isNot(clang::tok::r_paren)) { PP.Diag(PragmaToken, PP.getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "expected a ')'")); return; } } else { // If no argument, remove the license this->handleItem(""); } } void RSPragmaHandler::handleIntegerParamPragma( clang::Preprocessor &PP, clang::Token &FirstToken) { clang::Token &PragmaToken = FirstToken; // Skip first token, like "version" PP.LexUnexpandedToken(PragmaToken); // Now, the current token must be clang::tok::lpara if (PragmaToken.isNot(clang::tok::l_paren)) { // If no argument, set the version to 0 this->handleInt(PP, PragmaToken, 0); return; } PP.LexUnexpandedToken(PragmaToken); if (PragmaToken.is(clang::tok::numeric_constant)) { llvm::SmallString<128> SpellingBuffer; SpellingBuffer.resize(PragmaToken.getLength() + 1); llvm::StringRef TokSpelling = PP.getSpelling(PragmaToken, SpellingBuffer); clang::NumericLiteralParser NumericLiteral(TokSpelling, PragmaToken.getLocation(), PP); if (NumericLiteral.hadError) { // Diagnostics will be generated automatically return; } else { llvm::APInt Val(32, 0); NumericLiteral.GetIntegerValue(Val); this->handleInt(PP, PragmaToken, static_cast<int>(Val.getSExtValue())); } PP.LexUnexpandedToken(PragmaToken); } else { // If no argument, set the version to 0 this->handleInt(PP, PragmaToken, 0); } if (PragmaToken.isNot(clang::tok::r_paren)) { PP.Diag(PragmaToken, PP.getDiagnostics().getCustomDiagID( clang::DiagnosticsEngine::Error, "expected a ')'")); return; } do { PP.LexUnexpandedToken(PragmaToken); } while (PragmaToken.isNot(clang::tok::eod)); } void AddPragmaHandlers(clang::Preprocessor &PP, RSContext *RsContext) { // For #pragma rs export_type PP.AddPragmaHandler("rs", new RSExportTypePragmaHandler("export_type", RsContext)); // For #pragma rs java_package_name PP.AddPragmaHandler( "rs", new RSJavaPackageNamePragmaHandler("java_package_name", RsContext)); // For #pragma rs set_reflect_license PP.AddPragmaHandler( "rs", new RSReflectLicensePragmaHandler("set_reflect_license", RsContext)); // For #pragma version PP.AddPragmaHandler(new RSVersionPragmaHandler("version", RsContext)); // For #pragma rs_fp* PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_full", RsContext)); PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_relaxed", RsContext)); PP.AddPragmaHandler(new RSPrecisionPragmaHandler("rs_fp_imprecise", RsContext)); } } // namespace slang