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) { return; }
     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) { 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 
    123 class 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 
    141 class 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 
    173 RSPragmaHandler *
    174 RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) {
    175   return new RSExportTypePragmaHandler("export_type", Context);
    176 }
    177 
    178 RSPragmaHandler *
    179 RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) {
    180   return new RSJavaPackageNamePragmaHandler("java_package_name", Context);
    181 }
    182 
    183 RSPragmaHandler *
    184 RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) {
    185   return new RSReflectLicensePragmaHandler("set_reflect_license", Context);
    186 }
    187 
    188 RSPragmaHandler *
    189 RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) {
    190   return new RSVersionPragmaHandler("version", Context);
    191 }
    192 
    193 void 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 
    222 void 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 
    241 void 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 
    279 void 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