| /* |
| * 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) { return; } |
| |
| 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) { return; } |
| |
| 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; |
| } |
| } |
| } |
| return; |
| } |
| }; |
| |
| 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) { return; } |
| |
| 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) { return; } |
| |
| void HandlePragma(clang::Preprocessor &PP, |
| clang::PragmaIntroducerKind Introducer, |
| clang::Token &FirstToken) { |
| this->handleIntegerParamPragma(PP, FirstToken); |
| } |
| }; |
| |
| } // namespace |
| |
| RSPragmaHandler * |
| RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) { |
| return new RSExportTypePragmaHandler("export_type", Context); |
| } |
| |
| RSPragmaHandler * |
| RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) { |
| return new RSJavaPackageNamePragmaHandler("java_package_name", Context); |
| } |
| |
| RSPragmaHandler * |
| RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) { |
| return new RSReflectLicensePragmaHandler("set_reflect_license", Context); |
| } |
| |
| RSPragmaHandler * |
| RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) { |
| return new RSVersionPragmaHandler("version", Context); |
| } |
| |
| 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; |
| } |
| return; |
| } |
| |
| 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; |
| } |
| 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, 1, 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)) { |
| clang::NumericLiteralParser NumericLiteral(PragmaToken.getLiteralData(), |
| PragmaToken.getLiteralData() + PragmaToken.getLength(), |
| 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)); |
| } |
| |
| } // namespace slang |