Home | History | Annotate | Download | only in slang
      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