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