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 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 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 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 98 RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) { 99 return InternalFileNameConvert(rsFileName, false); 100 } 101 102 std::string RootNameFromRSFileName(const std::string &rsFileName) { 103 return InternalFileNameConvert(rsFileName.c_str(), false); 104 } 105 106 std::string 107 RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) { 108 return InternalFileNameConvert(rsFileName, true); 109 } 110 111 std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName( 112 const char *rsFileName) { 113 std::string tmp(InternalFileNameConvert(rsFileName, false)); 114 return tmp.append("BitCode"); 115 } 116 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. 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 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 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 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 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 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. 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 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 388 void GeneratedFile::closeFile() { close(); } 389 390 void GeneratedFile::increaseIndent() { mIndent.append(" "); } 391 392 void GeneratedFile::decreaseIndent() { 393 slangAssert(!mIndent.empty() && "No indent"); 394 mIndent.erase(0, 4); 395 } 396 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