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