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