blob: c67a1a864c0f4b4ca46baef5202c3e4043c46b5a [file] [log] [blame]
Zonr Changc383a502010-10-12 01:52:08 +08001/*
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -07002 * Copyright 2010-2014, The Android Open Source Project
Zonr Changc383a502010-10-12 01:52:08 +08003 *
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
zonr6315f762010-10-05 15:35:14 +080017#include "slang_rs_reflect_utils.h"
Ying Wang3f8b44d2010-09-04 01:17:01 -070018
Ying Wang3f8b44d2010-09-04 01:17:01 -070019#include <cstdio>
Mike Lockwoodb1980a22010-09-08 10:20:40 -040020#include <cstring>
Stephen Hinese639eb52010-11-08 19:27:20 -080021#include <string>
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -070022#include <iomanip>
Ying Wang3f8b44d2010-09-04 01:17:01 -070023
Zonr Chang8c6d9b22010-10-07 18:01:19 +080024#include "llvm/ADT/StringRef.h"
Stephen Hines3eb819a2014-11-24 18:16:52 -080025#include "llvm/Support/FileSystem.h"
26#include "llvm/Support/Path.h"
Zonr Chang8c6d9b22010-10-07 18:01:19 +080027
Raphael8d5a2f62011-02-08 00:15:05 -080028#include "os_sep.h"
Stephen Hines44d495d2014-05-22 19:42:55 -070029#include "slang_assert.h"
Ying Wang3f8b44d2010-09-04 01:17:01 -070030
31namespace slang {
32
33using std::string;
34
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070035string 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;
Ying Wangdba31112010-10-07 17:30:38 -070041 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070042 if ((*slash == '.') && (*dot == 0)) {
43 dot = slash;
44 }
45 --slash;
46 }
47 ++slash;
48 return string(slash, dot - slash);
Ying Wangdba31112010-10-07 17:30:38 -070049}
50
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070051string 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;
Ying Wang3f8b44d2010-09-04 01:17:01 -070063 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070064 ++s;
65 }
66 return packaged_path;
Ying Wang3f8b44d2010-09-04 01:17:01 -070067}
68
zonr6315f762010-10-05 15:35:14 +080069static string InternalFileNameConvert(const char *rsFileName, bool toLower) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070070 const char *dot = rsFileName + strlen(rsFileName);
71 const char *slash = dot - 1;
72 while (slash >= rsFileName) {
73 if (*slash == OS_PATH_SEPARATOR) {
74 break;
Ying Wang3f8b44d2010-09-04 01:17:01 -070075 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070076 if ((*slash == '.') && (*dot == 0)) {
77 dot = slash;
Ying Wang3f8b44d2010-09-04 01:17:01 -070078 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070079 --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);
Ying Wang3f8b44d2010-09-04 01:17:01 -070096}
97
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070098std::string
99RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) {
100 return InternalFileNameConvert(rsFileName, false);
Ying Wang3f8b44d2010-09-04 01:17:01 -0700101}
102
Jean-Luc Brouilletefcff102014-06-03 16:13:51 -0700103std::string RootNameFromRSFileName(const std::string &rsFileName) {
104 return InternalFileNameConvert(rsFileName.c_str(), false);
105}
106
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700107std::string
108RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) {
109 return InternalFileNameConvert(rsFileName, true);
Ying Wang3f8b44d2010-09-04 01:17:01 -0700110}
111
Stephen Hines44d495d2014-05-22 19:42:55 -0700112std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName(
113 const char *rsFileName) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700114 std::string tmp(InternalFileNameConvert(rsFileName, false));
115 return tmp.append("BitCode");
Stephen Hines44d495d2014-05-22 19:42:55 -0700116}
Ying Wang3f8b44d2010-09-04 01:17:01 -0700117
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700118static bool GenerateAccessorMethod(
119 const RSSlangReflectUtils::BitCodeAccessorContext &context,
Stephen Hines9ae18b22014-06-10 23:53:00 -0700120 int bitwidth, GeneratedFile &out) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700121 // the prototype of the accessor method
Stephen Hines9ae18b22014-06-10 23:53:00 -0700122 out.indent() << "// return byte array representation of the " << bitwidth
123 << "-bit bitcode.\n";
124 out.indent() << "public static byte[] getBitCode" << bitwidth << "()";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700125 out.startBlock();
Stephen Hines9ae18b22014-06-10 23:53:00 -0700126 out.indent() << "return getBitCode" << bitwidth << "Internal();\n";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700127 out.endBlock(true);
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700128 return true;
Ying Wang0877f052010-09-09 17:19:33 -0700129}
130
131// Java method size must not exceed 64k,
132// so we have to split the bitcode into multiple segments.
Stephen Hines9ae18b22014-06-10 23:53:00 -0700133static 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 << "()";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700137 out.startBlock();
138 out.indent() << "byte[] data = {";
139 out.increaseIndent();
Ying Wang0877f052010-09-09 17:19:33 -0700140
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700141 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 << " ";
Ying Wang0877f052010-09-09 17:19:33 -0700150 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700151 out << std::setw(4) << static_cast<int>(buff[written]) << ",";
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700152 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700153 out << "\n";
Ying Wang0877f052010-09-09 17:19:33 -0700154
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700155 out.decreaseIndent();
156 out.indent() << "};\n";
157 out.indent() << "return data;\n";
158 out.endBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700159
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700160 return true;
Ying Wang0877f052010-09-09 17:19:33 -0700161}
162
Stephen Hines9ae18b22014-06-10 23:53:00 -0700163static bool GenerateJavaCodeAccessorMethodForBitwidth(
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700164 const RSSlangReflectUtils::BitCodeAccessorContext &context,
Stephen Hines9ae18b22014-06-10 23:53:00 -0700165 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");
Chris Wailes5abbe0e2014-08-12 15:58:29 -0700173 if (pfin == nullptr) {
Stephen Hines9ae18b22014-06-10 23:53:00 -0700174 fprintf(stderr, "Error: could not read file %s\n", filename.c_str());
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700175 return false;
176 }
Ying Wang3f8b44d2010-09-04 01:17:01 -0700177
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700178 // start the accessor method
Stephen Hines9ae18b22014-06-10 23:53:00 -0700179 GenerateAccessorMethod(context, bitwidth, out);
Ying Wang0877f052010-09-09 17:19:33 -0700180
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700181 // 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) {
Stephen Hines9ae18b22014-06-10 23:53:00 -0700190 GenerateSegmentMethod(buff, read_length, bitwidth, seg_num, out);
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700191 ++seg_num;
192 total_length += read_length;
193 }
194 delete[] buff;
195 fclose(pfin);
Ying Wang0877f052010-09-09 17:19:33 -0700196
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700197 // output the internal accessor method
Stephen Hines9ae18b22014-06-10 23:53:00 -0700198 out.indent() << "private static int bitCode" << bitwidth << "Length = "
199 << total_length << ";\n\n";
200 out.indent() << "private static byte[] getBitCode" << bitwidth
201 << "Internal()";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700202 out.startBlock();
Stephen Hines9ae18b22014-06-10 23:53:00 -0700203 out.indent() << "byte[] bc = new byte[bitCode" << bitwidth << "Length];\n";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700204 out.indent() << "int offset = 0;\n";
205 out.indent() << "byte[] seg;\n";
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700206 for (int i = 0; i < seg_num; ++i) {
Stephen Hines9ae18b22014-06-10 23:53:00 -0700207 out.indent() << "seg = getSegment" << bitwidth << "_" << i << "();\n";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700208 out.indent() << "System.arraycopy(seg, 0, bc, offset, seg.length);\n";
209 out.indent() << "offset += seg.length;\n";
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700210 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700211 out.indent() << "return bc;\n";
212 out.endBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700213
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700214 return true;
Ying Wang0877f052010-09-09 17:19:33 -0700215}
216
Stephen Hines9ae18b22014-06-10 23:53:00 -0700217static 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
Ying Wang0877f052010-09-09 17:19:33 -0700232static bool GenerateAccessorClass(
zonr6315f762010-10-05 15:35:14 +0800233 const RSSlangReflectUtils::BitCodeAccessorContext &context,
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700234 const char *clazz_name, GeneratedFile &out) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700235 // begin the class.
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700236 out << "/**\n";
237 out << " * @hide\n";
238 out << " */\n";
239 out << "public class " << clazz_name;
240 out.startBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700241
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700242 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:
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700249 ret = GenerateJavaCodeAccessorMethod(context, out);
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700250 break;
251 default:
252 ret = false;
253 }
Ying Wang3f8b44d2010-09-04 01:17:01 -0700254
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700255 // end the class.
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700256 out.endBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700257
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700258 return ret;
Ying Wang0877f052010-09-09 17:19:33 -0700259}
260
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700261bool RSSlangReflectUtils::GenerateJavaBitCodeAccessor(
zonr6315f762010-10-05 15:35:14 +0800262 const BitCodeAccessorContext &context) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700263 string output_path =
264 ComputePackagedPath(context.reflectPath, context.packageName);
Stephen Hines3eb819a2014-11-24 18:16:52 -0800265 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());
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700269 return false;
270 }
Ying Wang0877f052010-09-09 17:19:33 -0700271
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700272 string clazz_name(JavaBitcodeClassNameFromRSFileName(context.rsFileName));
273 string filename(clazz_name);
274 filename += ".java";
Ying Wang3f8b44d2010-09-04 01:17:01 -0700275
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700276 GeneratedFile out;
277 if (!out.startFile(output_path, filename, context.rsFileName,
Stephen Hinesfc4f78b2014-06-10 18:07:10 -0700278 context.licenseNote, true, context.verbose)) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700279 return false;
280 }
Ying Wang3f8b44d2010-09-04 01:17:01 -0700281
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700282 out << "package " << context.packageName << ";\n\n";
Ying Wang3f8b44d2010-09-04 01:17:01 -0700283
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700284 bool ret = GenerateAccessorClass(context, clazz_name.c_str(), out);
285
286 out.closeFile();
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700287 return ret;
Ying Wang3f8b44d2010-09-04 01:17:01 -0700288}
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700289
290std::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
Jean-Luc Brouilletf33e1562014-06-03 17:55:57 -0700309// 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.
315static 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
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700323static 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
342bool GeneratedFile::startFile(const string &outDirectory,
343 const string &outFileName,
344 const string &sourceFileName,
Stephen Hinesfc4f78b2014-06-10 18:07:10 -0700345 const string *optionalLicense, bool isJava,
346 bool verbose) {
347 if (verbose) {
348 printf("Generating %s\n", outFileName.c_str());
349 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700350
351 // Create the parent directories.
352 if (!outDirectory.empty()) {
Stephen Hines3eb819a2014-11-24 18:16:52 -0800353 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());
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700356 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.
Chris Wailes5abbe0e2014-08-12 15:58:29 -0700370 if (optionalLicense != nullptr) {
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700371 *this << *optionalLicense;
372 } else {
373 *this << gApacheLicenseNote;
374 }
375
376 // Write a notice that this is a generated file.
Jean-Luc Brouilletf33e1562014-06-03 17:55:57 -0700377 std::string source(sourceFileName);
378 if (isJava) {
379 SanitizeString(&source);
380 }
381
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700382 *this << "/*\n"
383 << " * This file is auto-generated. DO NOT MODIFY!\n"
Jean-Luc Brouilletf33e1562014-06-03 17:55:57 -0700384 << " * The source Renderscript file: " << source << "\n"
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700385 << " */\n\n";
386
387 return true;
388}
389
390void GeneratedFile::closeFile() { close(); }
391
392void GeneratedFile::increaseIndent() { mIndent.append(" "); }
393
394void GeneratedFile::decreaseIndent() {
395 slangAssert(!mIndent.empty() && "No indent");
396 mIndent.erase(0, 4);
397}
398
Jean-Luc Brouilletc83b7902014-07-25 15:50:32 -0700399void 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
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700439} // namespace slang