• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetFileNameStem(const char * fileName)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 
ComputePackagedPath(const char * prefixPath,const char * packageName)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 
InternalFileNameConvert(const char * rsFileName,bool toLower)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
JavaClassNameFromRSFileName(const char * rsFileName)98 RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) {
99   return InternalFileNameConvert(rsFileName, false);
100 }
101 
RootNameFromRSFileName(const std::string & rsFileName)102 std::string RootNameFromRSFileName(const std::string &rsFileName) {
103   return InternalFileNameConvert(rsFileName.c_str(), false);
104 }
105 
106 std::string
BCFileNameFromRSFileName(const char * rsFileName)107 RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) {
108   return InternalFileNameConvert(rsFileName, true);
109 }
110 
JavaBitcodeClassNameFromRSFileName(const char * rsFileName)111 std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName(
112     const char *rsFileName) {
113   std::string tmp(InternalFileNameConvert(rsFileName, false));
114   return tmp.append("BitCode");
115 }
116 
GenerateAccessorMethod(const RSSlangReflectUtils::BitCodeAccessorContext & context,int bitwidth,GeneratedFile & out)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.
GenerateSegmentMethod(const char * buff,int blen,int bitwidth,int seg_num,GeneratedFile & out)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 
GenerateJavaCodeAccessorMethodForBitwidth(const RSSlangReflectUtils::BitCodeAccessorContext & context,int bitwidth,GeneratedFile & out)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 
GenerateJavaCodeAccessorMethod(const RSSlangReflectUtils::BitCodeAccessorContext & context,GeneratedFile & out)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 
GenerateAccessorClass(const RSSlangReflectUtils::BitCodeAccessorContext & context,const char * clazz_name,GeneratedFile & out)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 
GenerateJavaBitCodeAccessor(const BitCodeAccessorContext & context)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 
JoinPath(const std::string & path1,const std::string & path2)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.
SanitizeString(std::string * s)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 
startFile(const string & outDirectory,const string & outFileName,const string & sourceFileName,const string * optionalLicense,bool isJava,bool verbose)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 
closeFile()388 void GeneratedFile::closeFile() { close(); }
389 
increaseIndent()390 void GeneratedFile::increaseIndent() { mIndent.append("    "); }
391 
decreaseIndent()392 void GeneratedFile::decreaseIndent() {
393   slangAssert(!mIndent.empty() && "No indent");
394   mIndent.erase(0, 4);
395 }
396 
comment(const std::string & s)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