blob: 66facabd2c99b1ebd492f5052b7d9fbe437f5212 [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"
25
Raphael8d5a2f62011-02-08 00:15:05 -080026#include "os_sep.h"
Stephen Hines44d495d2014-05-22 19:42:55 -070027#include "slang_assert.h"
Zonr Chang8c6d9b22010-10-07 18:01:19 +080028#include "slang_utils.h"
Ying Wang3f8b44d2010-09-04 01:17:01 -070029
30namespace slang {
31
32using std::string;
33
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070034string 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;
Ying Wangdba31112010-10-07 17:30:38 -070040 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070041 if ((*slash == '.') && (*dot == 0)) {
42 dot = slash;
43 }
44 --slash;
45 }
46 ++slash;
47 return string(slash, dot - slash);
Ying Wangdba31112010-10-07 17:30:38 -070048}
49
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070050string 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;
Ying Wang3f8b44d2010-09-04 01:17:01 -070062 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070063 ++s;
64 }
65 return packaged_path;
Ying Wang3f8b44d2010-09-04 01:17:01 -070066}
67
zonr6315f762010-10-05 15:35:14 +080068static string InternalFileNameConvert(const char *rsFileName, bool toLower) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070069 const char *dot = rsFileName + strlen(rsFileName);
70 const char *slash = dot - 1;
71 while (slash >= rsFileName) {
72 if (*slash == OS_PATH_SEPARATOR) {
73 break;
Ying Wang3f8b44d2010-09-04 01:17:01 -070074 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070075 if ((*slash == '.') && (*dot == 0)) {
76 dot = slash;
Ying Wang3f8b44d2010-09-04 01:17:01 -070077 }
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070078 --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);
Ying Wang3f8b44d2010-09-04 01:17:01 -070095}
96
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -070097std::string
98RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) {
99 return InternalFileNameConvert(rsFileName, false);
Ying Wang3f8b44d2010-09-04 01:17:01 -0700100}
101
Jean-Luc Brouilletefcff102014-06-03 16:13:51 -0700102std::string RootNameFromRSFileName(const std::string &rsFileName) {
103 return InternalFileNameConvert(rsFileName.c_str(), false);
104}
105
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700106std::string
107RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) {
108 return InternalFileNameConvert(rsFileName, true);
Ying Wang3f8b44d2010-09-04 01:17:01 -0700109}
110
Stephen Hines44d495d2014-05-22 19:42:55 -0700111std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName(
112 const char *rsFileName) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700113 std::string tmp(InternalFileNameConvert(rsFileName, false));
114 return tmp.append("BitCode");
Stephen Hines44d495d2014-05-22 19:42:55 -0700115}
Ying Wang3f8b44d2010-09-04 01:17:01 -0700116
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700117static bool GenerateAccessorMethod(
118 const RSSlangReflectUtils::BitCodeAccessorContext &context,
Stephen Hines9ae18b22014-06-10 23:53:00 -0700119 int bitwidth, GeneratedFile &out) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700120 // the prototype of the accessor method
Stephen Hines9ae18b22014-06-10 23:53:00 -0700121 out.indent() << "// return byte array representation of the " << bitwidth
122 << "-bit bitcode.\n";
123 out.indent() << "public static byte[] getBitCode" << bitwidth << "()";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700124 out.startBlock();
Stephen Hines9ae18b22014-06-10 23:53:00 -0700125 out.indent() << "return getBitCode" << bitwidth << "Internal();\n";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700126 out.endBlock(true);
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700127 return true;
Ying Wang0877f052010-09-09 17:19:33 -0700128}
129
130// Java method size must not exceed 64k,
131// so we have to split the bitcode into multiple segments.
Stephen Hines9ae18b22014-06-10 23:53:00 -0700132static 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 << "()";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700136 out.startBlock();
137 out.indent() << "byte[] data = {";
138 out.increaseIndent();
Ying Wang0877f052010-09-09 17:19:33 -0700139
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700140 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 << " ";
Ying Wang0877f052010-09-09 17:19:33 -0700149 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700150 out << std::setw(4) << static_cast<int>(buff[written]) << ",";
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700151 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700152 out << "\n";
Ying Wang0877f052010-09-09 17:19:33 -0700153
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700154 out.decreaseIndent();
155 out.indent() << "};\n";
156 out.indent() << "return data;\n";
157 out.endBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700158
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700159 return true;
Ying Wang0877f052010-09-09 17:19:33 -0700160}
161
Stephen Hines9ae18b22014-06-10 23:53:00 -0700162static bool GenerateJavaCodeAccessorMethodForBitwidth(
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700163 const RSSlangReflectUtils::BitCodeAccessorContext &context,
Stephen Hines9ae18b22014-06-10 23:53:00 -0700164 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");
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700172 if (pfin == NULL) {
Stephen Hines9ae18b22014-06-10 23:53:00 -0700173 fprintf(stderr, "Error: could not read file %s\n", filename.c_str());
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700174 return false;
175 }
Ying Wang3f8b44d2010-09-04 01:17:01 -0700176
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700177 // start the accessor method
Stephen Hines9ae18b22014-06-10 23:53:00 -0700178 GenerateAccessorMethod(context, bitwidth, out);
Ying Wang0877f052010-09-09 17:19:33 -0700179
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700180 // 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) {
Stephen Hines9ae18b22014-06-10 23:53:00 -0700189 GenerateSegmentMethod(buff, read_length, bitwidth, seg_num, out);
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700190 ++seg_num;
191 total_length += read_length;
192 }
193 delete[] buff;
194 fclose(pfin);
Ying Wang0877f052010-09-09 17:19:33 -0700195
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700196 // output the internal accessor method
Stephen Hines9ae18b22014-06-10 23:53:00 -0700197 out.indent() << "private static int bitCode" << bitwidth << "Length = "
198 << total_length << ";\n\n";
199 out.indent() << "private static byte[] getBitCode" << bitwidth
200 << "Internal()";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700201 out.startBlock();
Stephen Hines9ae18b22014-06-10 23:53:00 -0700202 out.indent() << "byte[] bc = new byte[bitCode" << bitwidth << "Length];\n";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700203 out.indent() << "int offset = 0;\n";
204 out.indent() << "byte[] seg;\n";
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700205 for (int i = 0; i < seg_num; ++i) {
Stephen Hines9ae18b22014-06-10 23:53:00 -0700206 out.indent() << "seg = getSegment" << bitwidth << "_" << i << "();\n";
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700207 out.indent() << "System.arraycopy(seg, 0, bc, offset, seg.length);\n";
208 out.indent() << "offset += seg.length;\n";
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700209 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700210 out.indent() << "return bc;\n";
211 out.endBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700212
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700213 return true;
Ying Wang0877f052010-09-09 17:19:33 -0700214}
215
Stephen Hines9ae18b22014-06-10 23:53:00 -0700216static 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
Ying Wang0877f052010-09-09 17:19:33 -0700231static bool GenerateAccessorClass(
zonr6315f762010-10-05 15:35:14 +0800232 const RSSlangReflectUtils::BitCodeAccessorContext &context,
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700233 const char *clazz_name, GeneratedFile &out) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700234 // begin the class.
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700235 out << "/**\n";
236 out << " * @hide\n";
237 out << " */\n";
238 out << "public class " << clazz_name;
239 out.startBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700240
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700241 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:
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700248 ret = GenerateJavaCodeAccessorMethod(context, out);
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700249 break;
250 default:
251 ret = false;
252 }
Ying Wang3f8b44d2010-09-04 01:17:01 -0700253
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700254 // end the class.
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700255 out.endBlock();
Ying Wang0877f052010-09-09 17:19:33 -0700256
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700257 return ret;
Ying Wang0877f052010-09-09 17:19:33 -0700258}
259
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700260bool RSSlangReflectUtils::GenerateJavaBitCodeAccessor(
zonr6315f762010-10-05 15:35:14 +0800261 const BitCodeAccessorContext &context) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700262 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 }
Ying Wang0877f052010-09-09 17:19:33 -0700269
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700270 string clazz_name(JavaBitcodeClassNameFromRSFileName(context.rsFileName));
271 string filename(clazz_name);
272 filename += ".java";
Ying Wang3f8b44d2010-09-04 01:17:01 -0700273
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700274 GeneratedFile out;
275 if (!out.startFile(output_path, filename, context.rsFileName,
Stephen Hinesfc4f78b2014-06-10 18:07:10 -0700276 context.licenseNote, true, context.verbose)) {
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700277 return false;
278 }
Ying Wang3f8b44d2010-09-04 01:17:01 -0700279
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700280 out << "package " << context.packageName << ";\n\n";
Ying Wang3f8b44d2010-09-04 01:17:01 -0700281
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700282 bool ret = GenerateAccessorClass(context, clazz_name.c_str(), out);
283
284 out.closeFile();
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700285 return ret;
Ying Wang3f8b44d2010-09-04 01:17:01 -0700286}
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700287
288std::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
Jean-Luc Brouilletf33e1562014-06-03 17:55:57 -0700307// 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.
313static 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
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700321static 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
340bool GeneratedFile::startFile(const string &outDirectory,
341 const string &outFileName,
342 const string &sourceFileName,
Stephen Hinesfc4f78b2014-06-10 18:07:10 -0700343 const string *optionalLicense, bool isJava,
344 bool verbose) {
345 if (verbose) {
346 printf("Generating %s\n", outFileName.c_str());
347 }
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700348
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.
Jean-Luc Brouilletf33e1562014-06-03 17:55:57 -0700375 std::string source(sourceFileName);
376 if (isJava) {
377 SanitizeString(&source);
378 }
379
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700380 *this << "/*\n"
381 << " * This file is auto-generated. DO NOT MODIFY!\n"
Jean-Luc Brouilletf33e1562014-06-03 17:55:57 -0700382 << " * The source Renderscript file: " << source << "\n"
Jean-Luc Brouillet129fd822014-05-28 17:46:10 -0700383 << " */\n\n";
384
385 return true;
386}
387
388void GeneratedFile::closeFile() { close(); }
389
390void GeneratedFile::increaseIndent() { mIndent.append(" "); }
391
392void GeneratedFile::decreaseIndent() {
393 slangAssert(!mIndent.empty() && "No indent");
394 mIndent.erase(0, 4);
395}
396
Jean-Luc Brouilletf5ab7e72014-07-25 15:50:32 -0700397void 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
Jean-Luc Brouillet2ce118e2014-05-27 17:41:22 -0700437} // namespace slang