Home | History | Annotate | Download | only in slang
      1 /*
      2  * Copyright 2010-2014, 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_reflect_utils.h"
     18 
     19 #include <cstdio>
     20 #include <cstring>
     21 #include <string>
     22 #include <iomanip>
     23 
     24 #include "llvm/ADT/StringRef.h"
     25 #include "llvm/Support/FileSystem.h"
     26 #include "llvm/Support/Path.h"
     27 
     28 #include "os_sep.h"
     29 #include "slang_assert.h"
     30 
     31 namespace slang {
     32 
     33 using std::string;
     34 
     35 string RSSlangReflectUtils::GetFileNameStem(const char *fileName) {
     36   const char *dot = fileName + strlen(fileName);
     37   const char *slash = dot - 1;
     38   while (slash >= fileName) {
     39     if (*slash == OS_PATH_SEPARATOR) {
     40       break;
     41     }
     42     if ((*slash == '.') && (*dot == 0)) {
     43       dot = slash;
     44     }
     45     --slash;
     46   }
     47   ++slash;
     48   return string(slash, dot - slash);
     49 }
     50 
     51 string RSSlangReflectUtils::ComputePackagedPath(const char *prefixPath,
     52                                                 const char *packageName) {
     53   string packaged_path(prefixPath);
     54   if (!packaged_path.empty() &&
     55       (packaged_path[packaged_path.length() - 1] != OS_PATH_SEPARATOR)) {
     56     packaged_path += OS_PATH_SEPARATOR_STR;
     57   }
     58   size_t s = packaged_path.length();
     59   packaged_path += packageName;
     60   while (s < packaged_path.length()) {
     61     if (packaged_path[s] == '.') {
     62       packaged_path[s] = OS_PATH_SEPARATOR;
     63     }
     64     ++s;
     65   }
     66   return packaged_path;
     67 }
     68 
     69 static string InternalFileNameConvert(const char *rsFileName, bool toLower) {
     70   const char *dot = rsFileName + strlen(rsFileName);
     71   const char *slash = dot - 1;
     72   while (slash >= rsFileName) {
     73     if (*slash == OS_PATH_SEPARATOR) {
     74       break;
     75     }
     76     if ((*slash == '.') && (*dot == 0)) {
     77       dot = slash;
     78     }
     79     --slash;
     80   }
     81   ++slash;
     82   char ret[256];
     83   int i = 0;
     84   for (; (i < 255) && (slash < dot); ++slash) {
     85     if (isalnum(*slash) || *slash == '_') {
     86       if (toLower) {
     87         ret[i] = tolower(*slash);
     88       } else {
     89         ret[i] = *slash;
     90       }
     91       ++i;
     92     }
     93   }
     94   ret[i] = 0;
     95   return string(ret);
     96 }
     97 
     98 std::string
     99 RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) {
    100   return InternalFileNameConvert(rsFileName, false);
    101 }
    102 
    103 std::string RootNameFromRSFileName(const std::string &rsFileName) {
    104   return InternalFileNameConvert(rsFileName.c_str(), false);
    105 }
    106 
    107 std::string
    108 RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) {
    109   return InternalFileNameConvert(rsFileName, true);
    110 }
    111 
    112 std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName(
    113     const char *rsFileName) {
    114   std::string tmp(InternalFileNameConvert(rsFileName, false));
    115   return tmp.append("BitCode");
    116 }
    117 
    118 static bool GenerateAccessorMethod(
    119     const RSSlangReflectUtils::BitCodeAccessorContext &context,
    120     int bitwidth, GeneratedFile &out) {
    121   // the prototype of the accessor method
    122   out.indent() << "// return byte array representation of the " << bitwidth
    123                << "-bit bitcode.\n";
    124   out.indent() << "public static byte[] getBitCode" << bitwidth << "()";
    125   out.startBlock();
    126   out.indent() << "return getBitCode" << bitwidth << "Internal();\n";
    127   out.endBlock(true);
    128   return true;
    129 }
    130 
    131 // Java method size must not exceed 64k,
    132 // so we have to split the bitcode into multiple segments.
    133 static bool GenerateSegmentMethod(const char *buff, int blen, int bitwidth,
    134                                   int seg_num, GeneratedFile &out) {
    135   out.indent() << "private static byte[] getSegment" << bitwidth << "_"
    136                << seg_num << "()";
    137   out.startBlock();
    138   out.indent() << "byte[] data = {";
    139   out.increaseIndent();
    140 
    141   const int kEntriesPerLine = 16;
    142   int position = kEntriesPerLine;  // We start with a new line and indent.
    143   for (int written = 0; written < blen; written++) {
    144     if (++position >= kEntriesPerLine) {
    145       out << "\n";
    146       out.indent();
    147       position = 0;
    148     } else {
    149       out << " ";
    150     }
    151     out << std::setw(4) << static_cast<int>(buff[written]) << ",";
    152   }
    153   out << "\n";
    154 
    155   out.decreaseIndent();
    156   out.indent() << "};\n";
    157   out.indent() << "return data;\n";
    158   out.endBlock();
    159 
    160   return true;
    161 }
    162 
    163 static bool GenerateJavaCodeAccessorMethodForBitwidth(
    164     const RSSlangReflectUtils::BitCodeAccessorContext &context,
    165     int bitwidth, GeneratedFile &out) {
    166 
    167   std::string filename(context.bc32FileName);
    168   if (bitwidth == 64) {
    169     filename = context.bc64FileName;
    170   }
    171 
    172   FILE *pfin = fopen(filename.c_str(), "rb");
    173   if (pfin == nullptr) {
    174     fprintf(stderr, "Error: could not read file %s\n", filename.c_str());
    175     return false;
    176   }
    177 
    178   // start the accessor method
    179   GenerateAccessorMethod(context, bitwidth, out);
    180 
    181   // output the data
    182   // make sure the generated function for a segment won't break the Javac
    183   // size limitation (64K).
    184   static const int SEG_SIZE = 0x2000;
    185   char *buff = new char[SEG_SIZE];
    186   int read_length;
    187   int seg_num = 0;
    188   int total_length = 0;
    189   while ((read_length = fread(buff, 1, SEG_SIZE, pfin)) > 0) {
    190     GenerateSegmentMethod(buff, read_length, bitwidth, seg_num, out);
    191     ++seg_num;
    192     total_length += read_length;
    193   }
    194   delete[] buff;
    195   fclose(pfin);
    196 
    197   // output the internal accessor method
    198   out.indent() << "private static int bitCode" << bitwidth << "Length = "
    199                << total_length << ";\n\n";
    200   out.indent() << "private static byte[] getBitCode" << bitwidth
    201                << "Internal()";
    202   out.startBlock();
    203   out.indent() << "byte[] bc = new byte[bitCode" << bitwidth << "Length];\n";
    204   out.indent() << "int offset = 0;\n";
    205   out.indent() << "byte[] seg;\n";
    206   for (int i = 0; i < seg_num; ++i) {
    207     out.indent() << "seg = getSegment" << bitwidth << "_" << i << "();\n";
    208     out.indent() << "System.arraycopy(seg, 0, bc, offset, seg.length);\n";
    209     out.indent() << "offset += seg.length;\n";
    210   }
    211   out.indent() << "return bc;\n";
    212   out.endBlock();
    213 
    214   return true;
    215 }
    216 
    217 static bool GenerateJavaCodeAccessorMethod(
    218     const RSSlangReflectUtils::BitCodeAccessorContext &context,
    219     GeneratedFile &out) {
    220   if (!GenerateJavaCodeAccessorMethodForBitwidth(context, 32, out)) {
    221     slangAssert(false && "Couldn't generate 32-bit embedded bitcode!");
    222     return false;
    223   }
    224   if (!GenerateJavaCodeAccessorMethodForBitwidth(context, 64, out)) {
    225     slangAssert(false && "Couldn't generate 64-bit embedded bitcode!");
    226     return false;
    227   }
    228 
    229   return true;
    230 }
    231 
    232 static bool GenerateAccessorClass(
    233     const RSSlangReflectUtils::BitCodeAccessorContext &context,
    234     const char *clazz_name, GeneratedFile &out) {
    235   // begin the class.
    236   out << "/**\n";
    237   out << " * @hide\n";
    238   out << " */\n";
    239   out << "public class " << clazz_name;
    240   out.startBlock();
    241 
    242   bool ret = true;
    243   switch (context.bcStorage) {
    244   case BCST_APK_RESOURCE:
    245     slangAssert(false &&
    246                 "Invalid generation of bitcode accessor with resource");
    247     break;
    248   case BCST_JAVA_CODE:
    249     ret = GenerateJavaCodeAccessorMethod(context, out);
    250     break;
    251   default:
    252     ret = false;
    253   }
    254 
    255   // end the class.
    256   out.endBlock();
    257 
    258   return ret;
    259 }
    260 
    261 bool RSSlangReflectUtils::GenerateJavaBitCodeAccessor(
    262     const BitCodeAccessorContext &context) {
    263   string output_path =
    264       ComputePackagedPath(context.reflectPath, context.packageName);
    265   if (std::error_code EC = llvm::sys::fs::create_directories(
    266           llvm::sys::path::parent_path(output_path))) {
    267     fprintf(stderr, "Error: could not create dir %s: %s\n",
    268             output_path.c_str(), EC.message().c_str());
    269     return false;
    270   }
    271 
    272   string clazz_name(JavaBitcodeClassNameFromRSFileName(context.rsFileName));
    273   string filename(clazz_name);
    274   filename += ".java";
    275 
    276   GeneratedFile out;
    277   if (!out.startFile(output_path, filename, context.rsFileName,
    278                      context.licenseNote, true, context.verbose)) {
    279     return false;
    280   }
    281 
    282   out << "package " << context.packageName << ";\n\n";
    283 
    284   bool ret = GenerateAccessorClass(context, clazz_name.c_str(), out);
    285 
    286   out.closeFile();
    287   return ret;
    288 }
    289 
    290 std::string JoinPath(const std::string &path1, const std::string &path2) {
    291   if (path1.empty()) {
    292     return path2;
    293   }
    294   if (path2.empty()) {
    295     return path1;
    296   }
    297   std::string fullPath = path1;
    298   if (fullPath[fullPath.length() - 1] != OS_PATH_SEPARATOR) {
    299     fullPath += OS_PATH_SEPARATOR;
    300   }
    301   if (path2[0] == OS_PATH_SEPARATOR) {
    302     fullPath += path2.substr(1, string::npos);
    303   } else {
    304     fullPath += path2;
    305   }
    306   return fullPath;
    307 }
    308 
    309 // Replace all instances of "\" with "\\" in a single string to prevent
    310 // formatting errors.  In Java, this can happen even within comments, as
    311 // Java processes \u before the comments are stripped.  E.g. if the generated
    312 // file in Windows contains the note:
    313 //     /* Do not modify!  Generated from \Users\MyName\MyDir\foo.cs */
    314 // Java will think that \U tells of a Unicode character.
    315 static void SanitizeString(std::string *s) {
    316   size_t p = 0;
    317   while ((p = s->find('\\', p)) != std::string::npos) {
    318     s->replace(p, 1, "\\\\");
    319     p += 2;
    320   }
    321 }
    322 
    323 static const char *const gApacheLicenseNote =
    324     "/*\n"
    325     " * Copyright (C) 2011-2014 The Android Open Source Project\n"
    326     " *\n"
    327     " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
    328     " * you may not use this file except in compliance with the License.\n"
    329     " * You may obtain a copy of the License at\n"
    330     " *\n"
    331     " *      http://www.apache.org/licenses/LICENSE-2.0\n"
    332     " *\n"
    333     " * Unless required by applicable law or agreed to in writing, software\n"
    334     " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
    335     " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or "
    336     "implied.\n"
    337     " * See the License for the specific language governing permissions and\n"
    338     " * limitations under the License.\n"
    339     " */\n"
    340     "\n";
    341 
    342 bool GeneratedFile::startFile(const string &outDirectory,
    343                               const string &outFileName,
    344                               const string &sourceFileName,
    345                               const string *optionalLicense, bool isJava,
    346                               bool verbose) {
    347   if (verbose) {
    348     printf("Generating %s\n", outFileName.c_str());
    349   }
    350 
    351   // Create the parent directories.
    352   if (!outDirectory.empty()) {
    353     if (std::error_code EC = llvm::sys::fs::create_directories(
    354             llvm::sys::path::parent_path(outDirectory))) {
    355       fprintf(stderr, "Error: %s\n", EC.message().c_str());
    356       return false;
    357     }
    358   }
    359 
    360   std::string FilePath = JoinPath(outDirectory, outFileName);
    361 
    362   // Open the file.
    363   open(FilePath.c_str());
    364   if (!good()) {
    365     fprintf(stderr, "Error: could not write file %s\n", outFileName.c_str());
    366     return false;
    367   }
    368 
    369   // Write the license.
    370   if (optionalLicense != nullptr) {
    371     *this << *optionalLicense;
    372   } else {
    373     *this << gApacheLicenseNote;
    374   }
    375 
    376   // Write a notice that this is a generated file.
    377   std::string source(sourceFileName);
    378   if (isJava) {
    379     SanitizeString(&source);
    380   }
    381 
    382   *this << "/*\n"
    383         << " * This file is auto-generated. DO NOT MODIFY!\n"
    384         << " * The source Renderscript file: " << source << "\n"
    385         << " */\n\n";
    386 
    387   return true;
    388 }
    389 
    390 void GeneratedFile::closeFile() { close(); }
    391 
    392 void GeneratedFile::increaseIndent() { mIndent.append("    "); }
    393 
    394 void GeneratedFile::decreaseIndent() {
    395   slangAssert(!mIndent.empty() && "No indent");
    396   mIndent.erase(0, 4);
    397 }
    398 
    399 void GeneratedFile::comment(const std::string &s) {
    400   indent() << "/* ";
    401   // +3 for the " * " starting each line.
    402   std::size_t indentLength = mIndent.length() + 3;
    403   std::size_t lengthOfCommentOnLine = 0;
    404   const std::size_t maxPerLine = 80;
    405   for (std::size_t start = 0, length = s.length(), nextStart = 0;
    406        start < length; start = nextStart) {
    407     std::size_t p = s.find_first_of(" \n", start);
    408     std::size_t toCopy = 1;
    409     bool forceBreak = false;
    410     if (p == std::string::npos) {
    411       toCopy = length - start;
    412       nextStart = length;
    413     } else {
    414       toCopy = p - start;
    415       nextStart = p + 1;
    416       forceBreak = s[p] == '\n';
    417     }
    418     if (lengthOfCommentOnLine > 0) {
    419       if (indentLength + lengthOfCommentOnLine + toCopy >= maxPerLine) {
    420         *this << "\n";
    421         indent() << " * ";
    422         lengthOfCommentOnLine = 0;
    423       } else {
    424         *this << " ";
    425       }
    426     }
    427 
    428     *this << s.substr(start, toCopy);
    429     if (forceBreak) {
    430       lengthOfCommentOnLine = maxPerLine;
    431     } else {
    432       lengthOfCommentOnLine += toCopy;
    433     }
    434   }
    435   *this << "\n";
    436   indent() << " */\n";
    437 }
    438 
    439 } // namespace slang
    440