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(const int v) {
    144     std::stringstream ss;
    145     ss << v;
    146     mContext->addPragma(this->getName(), ss.str());
    147     mContext->setVersion(v);
    148   }
    149 
    150  public:
    151   RSVersionPragmaHandler(llvm::StringRef Name, RSContext *Context)
    152       : RSPragmaHandler(Name, Context) { return; }
    153 
    154   void HandlePragma(clang::Preprocessor &PP,
    155                     clang::PragmaIntroducerKind Introducer,
    156                     clang::Token &FirstToken) {
    157     this->handleIntegerParamPragma(PP, FirstToken);
    158   }
    159 };
    160 
    161 }  // namespace
    162 
    163 RSPragmaHandler *
    164 RSPragmaHandler::CreatePragmaExportTypeHandler(RSContext *Context) {
    165   return new RSExportTypePragmaHandler("export_type", Context);
    166 }
    167 
    168 RSPragmaHandler *
    169 RSPragmaHandler::CreatePragmaJavaPackageNameHandler(RSContext *Context) {
    170   return new RSJavaPackageNamePragmaHandler("java_package_name", Context);
    171 }
    172 
    173 RSPragmaHandler *
    174 RSPragmaHandler::CreatePragmaReflectLicenseHandler(RSContext *Context) {
    175   return new RSReflectLicensePragmaHandler("set_reflect_license", Context);
    176 }
    177 
    178 RSPragmaHandler *
    179 RSPragmaHandler::CreatePragmaVersionHandler(RSContext *Context) {
    180   return new RSVersionPragmaHandler("version", Context);
    181 }
    182 
    183 void RSPragmaHandler::handleItemListPragma(clang::Preprocessor &PP,
    184                                            clang::Token &FirstToken) {
    185   clang::Token &PragmaToken = FirstToken;
    186 
    187   // Skip first token, like "export_var"
    188   PP.LexUnexpandedToken(PragmaToken);
    189 
    190   // Now, the current token must be clang::tok::lpara
    191   if (PragmaToken.isNot(clang::tok::l_paren))
    192     return;
    193 
    194   while (PragmaToken.isNot(clang::tok::eod)) {
    195     // Lex variable name
    196     PP.LexUnexpandedToken(PragmaToken);
    197     if (PragmaToken.is(clang::tok::identifier))
    198       this->handleItem(PP.getSpelling(PragmaToken));
    199     else
    200       break;
    201 
    202     slangAssert(PragmaToken.isNot(clang::tok::eod));
    203 
    204     PP.LexUnexpandedToken(PragmaToken);
    205 
    206     if (PragmaToken.isNot(clang::tok::comma))
    207       break;
    208   }
    209   return;
    210 }
    211 
    212 void RSPragmaHandler::handleNonParamPragma(clang::Preprocessor &PP,
    213                                            clang::Token &FirstToken) {
    214   clang::Token &PragmaToken = FirstToken;
    215 
    216   // Skip first token, like "export_var_all"
    217   PP.LexUnexpandedToken(PragmaToken);
    218 
    219   // Should be end immediately
    220   if (PragmaToken.isNot(clang::tok::eod))
    221     fprintf(stderr, "RSPragmaHandler::handleNonParamPragma: "
    222                     "expected a clang::tok::eod\n");
    223   return;
    224 }
    225 
    226 void RSPragmaHandler::handleOptionalStringLiteralParamPragma(
    227     clang::Preprocessor &PP, clang::Token &FirstToken) {
    228   clang::Token &PragmaToken = FirstToken;
    229 
    230   // Skip first token, like "set_reflect_license"
    231   PP.LexUnexpandedToken(PragmaToken);
    232 
    233   // Now, the current token must be clang::tok::lpara
    234   if (PragmaToken.isNot(clang::tok::l_paren))
    235     return;
    236 
    237   // If not ')', eat the following string literal as the license
    238   PP.LexUnexpandedToken(PragmaToken);
    239   if (PragmaToken.isNot(clang::tok::r_paren)) {
    240     // Eat the whole string literal
    241     clang::StringLiteralParser StringLiteral(&PragmaToken, 1, PP);
    242     if (StringLiteral.hadError)
    243       fprintf(stderr, "RSPragmaHandler::handleOptionalStringLiteralParamPragma"
    244                       ": illegal string literal\n");
    245     else
    246       this->handleItem(std::string(StringLiteral.GetString()));
    247 
    248     // The current token should be clang::tok::r_para
    249     PP.LexUnexpandedToken(PragmaToken);
    250     if (PragmaToken.isNot(clang::tok::r_paren))
    251       fprintf(stderr, "RSPragmaHandler::handleOptionalStringLiteralParamPragma"
    252                       ": expected a ')'\n");
    253   } else {
    254     // If no argument, remove the license
    255     this->handleItem("");
    256   }
    257 }
    258 
    259 void RSPragmaHandler::handleIntegerParamPragma(
    260     clang::Preprocessor &PP, clang::Token &FirstToken) {
    261   clang::Token &PragmaToken = FirstToken;
    262 
    263   // Skip first token, like "version"
    264   PP.LexUnexpandedToken(PragmaToken);
    265 
    266   // Now, the current token must be clang::tok::lpara
    267   if (PragmaToken.isNot(clang::tok::l_paren))
    268     return;
    269   PP.LexUnexpandedToken(PragmaToken);
    270 
    271   if (PragmaToken.is(clang::tok::numeric_constant)) {
    272     clang::NumericLiteralParser NumericLiteral(PragmaToken.getLiteralData(),
    273         PragmaToken.getLiteralData() + PragmaToken.getLength(),
    274         PragmaToken.getLocation(), PP);
    275     if (NumericLiteral.hadError) {
    276       fprintf(stderr, "RSPragmaHandler::handleIntegerParamPragma"
    277                       ": illegal numeric literal\n");
    278     } else {
    279       llvm::APInt Val(32, 0);
    280       NumericLiteral.GetIntegerValue(Val);
    281       this->handleInt(static_cast<int>(Val.getSExtValue()));
    282     }
    283     PP.LexUnexpandedToken(PragmaToken);
    284   } else {
    285     // If no argument, set the version to 0
    286     this->handleInt(0);
    287   }
    288 
    289   if (PragmaToken.isNot(clang::tok::r_paren)) {
    290     fprintf(stderr, "RSPragmaHandler::handleIntegerParamPragma"
    291                     ": expected a ')'\n");
    292   }
    293 
    294   do {
    295     PP.LexUnexpandedToken(PragmaToken);
    296   } while (PragmaToken.isNot(clang::tok::eod));
    297 }
    298 
    299 }  // namespace slang
    300