slang_rs_reflect_utils.cpp 13 KB

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