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#include "llvm/Support/FileSystem.h" 26#include "llvm/Support/Path.h" 27 28#include "os_sep.h" 29#include "slang_assert.h" 30 31namespace slang { 32 33using std::string; 34 35string 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; 41 } 42 if ((*slash == '.') && (*dot == 0)) { 43 dot = slash; 44 } 45 --slash; 46 } 47 ++slash; 48 return string(slash, dot - slash); 49} 50 51string 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; 63 } 64 ++s; 65 } 66 return packaged_path; 67} 68 69static string InternalFileNameConvert(const char *rsFileName, bool toLower) { 70 const char *dot = rsFileName + strlen(rsFileName); 71 const char *slash = dot - 1; 72 while (slash >= rsFileName) { 73 if (*slash == OS_PATH_SEPARATOR) { 74 break; 75 } 76 if ((*slash == '.') && (*dot == 0)) { 77 dot = slash; 78 } 79 --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); 96} 97 98std::string 99RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) { 100 return InternalFileNameConvert(rsFileName, false); 101} 102 103std::string RootNameFromRSFileName(const std::string &rsFileName) { 104 return InternalFileNameConvert(rsFileName.c_str(), false); 105} 106 107std::string 108RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) { 109 return InternalFileNameConvert(rsFileName, true); 110} 111 112std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName( 113 const char *rsFileName) { 114 std::string tmp(InternalFileNameConvert(rsFileName, false)); 115 return tmp.append("BitCode"); 116} 117 118static bool GenerateAccessorMethod( 119 const RSSlangReflectUtils::BitCodeAccessorContext &context, 120 int bitwidth, GeneratedFile &out) { 121 // the prototype of the accessor method 122 out.indent() << "// return byte array representation of the " << bitwidth 123 << "-bit bitcode.\n"; 124 out.indent() << "public static byte[] getBitCode" << bitwidth << "()"; 125 out.startBlock(); 126 out.indent() << "return getBitCode" << bitwidth << "Internal();\n"; 127 out.endBlock(true); 128 return true; 129} 130 131// Java method size must not exceed 64k, 132// so we have to split the bitcode into multiple segments. 133static 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 << "()"; 137 out.startBlock(); 138 out.indent() << "byte[] data = {"; 139 out.increaseIndent(); 140 141 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 << " "; 150 } 151 out << std::setw(4) << static_cast<int>(buff[written]) << ","; 152 } 153 out << "\n"; 154 155 out.decreaseIndent(); 156 out.indent() << "};\n"; 157 out.indent() << "return data;\n"; 158 out.endBlock(); 159 160 return true; 161} 162 163static bool GenerateJavaCodeAccessorMethodForBitwidth( 164 const RSSlangReflectUtils::BitCodeAccessorContext &context, 165 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"); 173 if (pfin == nullptr) { 174 fprintf(stderr, "Error: could not read file %s\n", filename.c_str()); 175 return false; 176 } 177 178 // start the accessor method 179 GenerateAccessorMethod(context, bitwidth, out); 180 181 // 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) { 190 GenerateSegmentMethod(buff, read_length, bitwidth, seg_num, out); 191 ++seg_num; 192 total_length += read_length; 193 } 194 delete[] buff; 195 fclose(pfin); 196 197 // output the internal accessor method 198 out.indent() << "private static int bitCode" << bitwidth << "Length = " 199 << total_length << ";\n\n"; 200 out.indent() << "private static byte[] getBitCode" << bitwidth 201 << "Internal()"; 202 out.startBlock(); 203 out.indent() << "byte[] bc = new byte[bitCode" << bitwidth << "Length];\n"; 204 out.indent() << "int offset = 0;\n"; 205 out.indent() << "byte[] seg;\n"; 206 for (int i = 0; i < seg_num; ++i) { 207 out.indent() << "seg = getSegment" << bitwidth << "_" << i << "();\n"; 208 out.indent() << "System.arraycopy(seg, 0, bc, offset, seg.length);\n"; 209 out.indent() << "offset += seg.length;\n"; 210 } 211 out.indent() << "return bc;\n"; 212 out.endBlock(); 213 214 return true; 215} 216 217static 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 232static bool GenerateAccessorClass( 233 const RSSlangReflectUtils::BitCodeAccessorContext &context, 234 const char *clazz_name, GeneratedFile &out) { 235 // begin the class. 236 out << "/**\n"; 237 out << " * @hide\n"; 238 out << " */\n"; 239 out << "public class " << clazz_name; 240 out.startBlock(); 241 242 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: 249 ret = GenerateJavaCodeAccessorMethod(context, out); 250 break; 251 default: 252 ret = false; 253 } 254 255 // end the class. 256 out.endBlock(); 257 258 return ret; 259} 260 261bool RSSlangReflectUtils::GenerateJavaBitCodeAccessor( 262 const BitCodeAccessorContext &context) { 263 string output_path = 264 ComputePackagedPath(context.reflectPath, context.packageName); 265 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()); 269 return false; 270 } 271 272 string clazz_name(JavaBitcodeClassNameFromRSFileName(context.rsFileName)); 273 string filename(clazz_name); 274 filename += ".java"; 275 276 GeneratedFile out; 277 if (!out.startFile(output_path, filename, context.rsFileName, 278 context.licenseNote, true, context.verbose)) { 279 return false; 280 } 281 282 out << "package " << context.packageName << ";\n\n"; 283 284 bool ret = GenerateAccessorClass(context, clazz_name.c_str(), out); 285 286 out.closeFile(); 287 return ret; 288} 289 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 309// 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 323static 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, 345 const string *optionalLicense, bool isJava, 346 bool verbose) { 347 if (verbose) { 348 printf("Generating %s\n", outFileName.c_str()); 349 } 350 351 // Create the parent directories. 352 if (!outDirectory.empty()) { 353 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()); 356 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. 370 if (optionalLicense != nullptr) { 371 *this << *optionalLicense; 372 } else { 373 *this << gApacheLicenseNote; 374 } 375 376 // Write a notice that this is a generated file. 377 std::string source(sourceFileName); 378 if (isJava) { 379 SanitizeString(&source); 380 } 381 382 *this << "/*\n" 383 << " * This file is auto-generated. DO NOT MODIFY!\n" 384 << " * The source Renderscript file: " << source << "\n" 385 << " */\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 399void 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 439} // namespace slang 440