123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- /*
- * Copyright 2010-2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "slang_rs_reflect_utils.h"
- #include <cstdio>
- #include <cstring>
- #include <string>
- #include <iomanip>
- #include "llvm/ADT/StringRef.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/Path.h"
- #include "os_sep.h"
- #include "slang_assert.h"
- namespace slang {
- using std::string;
- string RSSlangReflectUtils::GetFileNameStem(const char *fileName) {
- const char *dot = fileName + strlen(fileName);
- const char *slash = dot - 1;
- while (slash >= fileName) {
- if (*slash == OS_PATH_SEPARATOR) {
- break;
- }
- if ((*slash == '.') && (*dot == 0)) {
- dot = slash;
- }
- --slash;
- }
- ++slash;
- return string(slash, dot - slash);
- }
- string RSSlangReflectUtils::ComputePackagedPath(const char *prefixPath,
- const char *packageName) {
- string packaged_path(prefixPath);
- if (!packaged_path.empty() &&
- (packaged_path[packaged_path.length() - 1] != OS_PATH_SEPARATOR)) {
- packaged_path += OS_PATH_SEPARATOR_STR;
- }
- size_t s = packaged_path.length();
- packaged_path += packageName;
- while (s < packaged_path.length()) {
- if (packaged_path[s] == '.') {
- packaged_path[s] = OS_PATH_SEPARATOR;
- }
- ++s;
- }
- return packaged_path;
- }
- static string InternalFileNameConvert(const char *rsFileName, bool toLower) {
- const char *dot = rsFileName + strlen(rsFileName);
- const char *slash = dot - 1;
- while (slash >= rsFileName) {
- if (*slash == OS_PATH_SEPARATOR) {
- break;
- }
- if ((*slash == '.') && (*dot == 0)) {
- dot = slash;
- }
- --slash;
- }
- ++slash;
- char ret[256];
- int i = 0;
- for (; (i < 255) && (slash < dot); ++slash) {
- if (isalnum(*slash) || *slash == '_') {
- if (toLower) {
- ret[i] = tolower(*slash);
- } else {
- ret[i] = *slash;
- }
- ++i;
- }
- }
- ret[i] = 0;
- return string(ret);
- }
- std::string
- RSSlangReflectUtils::JavaClassNameFromRSFileName(const char *rsFileName) {
- return InternalFileNameConvert(rsFileName, false);
- }
- std::string RootNameFromRSFileName(const std::string &rsFileName) {
- return InternalFileNameConvert(rsFileName.c_str(), false);
- }
- std::string
- RSSlangReflectUtils::BCFileNameFromRSFileName(const char *rsFileName) {
- return InternalFileNameConvert(rsFileName, true);
- }
- std::string RSSlangReflectUtils::JavaBitcodeClassNameFromRSFileName(
- const char *rsFileName) {
- std::string tmp(InternalFileNameConvert(rsFileName, false));
- return tmp.append("BitCode");
- }
- static bool GenerateAccessorMethod(
- const RSSlangReflectUtils::BitCodeAccessorContext &context,
- int bitwidth, GeneratedFile &out) {
- // the prototype of the accessor method
- out.indent() << "// return byte array representation of the " << bitwidth
- << "-bit bitcode.\n";
- out.indent() << "public static byte[] getBitCode" << bitwidth << "()";
- out.startBlock();
- out.indent() << "return getBitCode" << bitwidth << "Internal();\n";
- out.endBlock(true);
- return true;
- }
- // Java method size must not exceed 64k,
- // so we have to split the bitcode into multiple segments.
- static bool GenerateSegmentMethod(const char *buff, int blen, int bitwidth,
- int seg_num, GeneratedFile &out) {
- out.indent() << "private static byte[] getSegment" << bitwidth << "_"
- << seg_num << "()";
- out.startBlock();
- out.indent() << "byte[] data = {";
- out.increaseIndent();
- const int kEntriesPerLine = 16;
- int position = kEntriesPerLine; // We start with a new line and indent.
- for (int written = 0; written < blen; written++) {
- if (++position >= kEntriesPerLine) {
- out << "\n";
- out.indent();
- position = 0;
- } else {
- out << " ";
- }
- out << std::setw(4) << static_cast<int>(buff[written]) << ",";
- }
- out << "\n";
- out.decreaseIndent();
- out.indent() << "};\n";
- out.indent() << "return data;\n";
- out.endBlock();
- return true;
- }
- static bool GenerateJavaCodeAccessorMethodForBitwidth(
- const RSSlangReflectUtils::BitCodeAccessorContext &context,
- int bitwidth, GeneratedFile &out) {
- std::string filename(context.bc32FileName);
- if (bitwidth == 64) {
- filename = context.bc64FileName;
- }
- FILE *pfin = fopen(filename.c_str(), "rb");
- if (pfin == nullptr) {
- fprintf(stderr, "Error: could not read file %s\n", filename.c_str());
- return false;
- }
- // start the accessor method
- GenerateAccessorMethod(context, bitwidth, out);
- // output the data
- // make sure the generated function for a segment won't break the Javac
- // size limitation (64K).
- static const int SEG_SIZE = 0x2000;
- char *buff = new char[SEG_SIZE];
- int read_length;
- int seg_num = 0;
- int total_length = 0;
- while ((read_length = fread(buff, 1, SEG_SIZE, pfin)) > 0) {
- GenerateSegmentMethod(buff, read_length, bitwidth, seg_num, out);
- ++seg_num;
- total_length += read_length;
- }
- delete[] buff;
- fclose(pfin);
- // output the internal accessor method
- out.indent() << "private static int bitCode" << bitwidth << "Length = "
- << total_length << ";\n\n";
- out.indent() << "private static byte[] getBitCode" << bitwidth
- << "Internal()";
- out.startBlock();
- out.indent() << "byte[] bc = new byte[bitCode" << bitwidth << "Length];\n";
- out.indent() << "int offset = 0;\n";
- out.indent() << "byte[] seg;\n";
- for (int i = 0; i < seg_num; ++i) {
- out.indent() << "seg = getSegment" << bitwidth << "_" << i << "();\n";
- out.indent() << "System.arraycopy(seg, 0, bc, offset, seg.length);\n";
- out.indent() << "offset += seg.length;\n";
- }
- out.indent() << "return bc;\n";
- out.endBlock();
- return true;
- }
- static bool GenerateJavaCodeAccessorMethod(
- const RSSlangReflectUtils::BitCodeAccessorContext &context,
- GeneratedFile &out) {
- if (!GenerateJavaCodeAccessorMethodForBitwidth(context, 32, out)) {
- slangAssert(false && "Couldn't generate 32-bit embedded bitcode!");
- return false;
- }
- if (!GenerateJavaCodeAccessorMethodForBitwidth(context, 64, out)) {
- slangAssert(false && "Couldn't generate 64-bit embedded bitcode!");
- return false;
- }
- return true;
- }
- static bool GenerateAccessorClass(
- const RSSlangReflectUtils::BitCodeAccessorContext &context,
- const char *clazz_name, GeneratedFile &out) {
- // begin the class.
- out << "/**\n";
- out << " * @hide\n";
- out << " */\n";
- out << "public class " << clazz_name;
- out.startBlock();
- bool ret = true;
- switch (context.bcStorage) {
- case BCST_APK_RESOURCE:
- slangAssert(false &&
- "Invalid generation of bitcode accessor with resource");
- break;
- case BCST_JAVA_CODE:
- ret = GenerateJavaCodeAccessorMethod(context, out);
- break;
- default:
- ret = false;
- }
- // end the class.
- out.endBlock();
- return ret;
- }
- bool RSSlangReflectUtils::GenerateJavaBitCodeAccessor(
- const BitCodeAccessorContext &context) {
- string output_path =
- ComputePackagedPath(context.reflectPath, context.packageName);
- if (std::error_code EC = llvm::sys::fs::create_directories(
- llvm::sys::path::parent_path(output_path))) {
- fprintf(stderr, "Error: could not create dir %s: %s\n",
- output_path.c_str(), EC.message().c_str());
- return false;
- }
- string clazz_name(JavaBitcodeClassNameFromRSFileName(context.rsFileName));
- string filename(clazz_name);
- filename += ".java";
- GeneratedFile out;
- if (!out.startFile(output_path, filename, context.rsFileName,
- context.licenseNote, true, context.verbose)) {
- return false;
- }
- out << "package " << context.packageName << ";\n\n";
- bool ret = GenerateAccessorClass(context, clazz_name.c_str(), out);
- out.closeFile();
- return ret;
- }
- std::string JoinPath(const std::string &path1, const std::string &path2) {
- if (path1.empty()) {
- return path2;
- }
- if (path2.empty()) {
- return path1;
- }
- std::string fullPath = path1;
- if (fullPath[fullPath.length() - 1] != OS_PATH_SEPARATOR) {
- fullPath += OS_PATH_SEPARATOR;
- }
- if (path2[0] == OS_PATH_SEPARATOR) {
- fullPath += path2.substr(1, string::npos);
- } else {
- fullPath += path2;
- }
- return fullPath;
- }
- // Replace all instances of "\" with "\\" in a single string to prevent
- // formatting errors. In Java, this can happen even within comments, as
- // Java processes \u before the comments are stripped. E.g. if the generated
- // file in Windows contains the note:
- // /* Do not modify! Generated from \Users\MyName\MyDir\foo.cs */
- // Java will think that \U tells of a Unicode character.
- static void SanitizeString(std::string *s) {
- size_t p = 0;
- while ((p = s->find('\\', p)) != std::string::npos) {
- s->replace(p, 1, "\\\\");
- p += 2;
- }
- }
- static const char *const gApacheLicenseNote =
- "/*\n"
- " * Copyright (C) 2011-2014 The Android Open Source Project\n"
- " *\n"
- " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
- " * you may not use this file except in compliance with the License.\n"
- " * You may obtain a copy of the License at\n"
- " *\n"
- " * http://www.apache.org/licenses/LICENSE-2.0\n"
- " *\n"
- " * Unless required by applicable law or agreed to in writing, software\n"
- " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
- " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or "
- "implied.\n"
- " * See the License for the specific language governing permissions and\n"
- " * limitations under the License.\n"
- " */\n"
- "\n";
- bool GeneratedFile::startFile(const string &outDirectory,
- const string &outFileName,
- const string &sourceFileName,
- const string *optionalLicense, bool isJava,
- bool verbose) {
- if (verbose) {
- printf("Generating %s\n", outFileName.c_str());
- }
- // Create the parent directories.
- if (!outDirectory.empty()) {
- if (std::error_code EC = llvm::sys::fs::create_directories(
- llvm::sys::path::parent_path(outDirectory))) {
- fprintf(stderr, "Error: %s\n", EC.message().c_str());
- return false;
- }
- }
- std::string FilePath = JoinPath(outDirectory, outFileName);
- // Open the file.
- open(FilePath.c_str());
- if (!good()) {
- fprintf(stderr, "Error: could not write file %s\n", outFileName.c_str());
- return false;
- }
- // Write the license.
- if (optionalLicense != nullptr) {
- *this << *optionalLicense;
- } else {
- *this << gApacheLicenseNote;
- }
- // Write a notice that this is a generated file.
- std::string source(sourceFileName);
- if (isJava) {
- SanitizeString(&source);
- }
- *this << "/*\n"
- << " * This file is auto-generated. DO NOT MODIFY!\n"
- << " * The source Renderscript file: " << source << "\n"
- << " */\n\n";
- return true;
- }
- void GeneratedFile::closeFile() { close(); }
- void GeneratedFile::increaseIndent() { mIndent.append(" "); }
- void GeneratedFile::decreaseIndent() {
- slangAssert(!mIndent.empty() && "No indent");
- mIndent.erase(0, 4);
- }
- void GeneratedFile::comment(const std::string &s) {
- indent() << "/* ";
- // +3 for the " * " starting each line.
- std::size_t indentLength = mIndent.length() + 3;
- std::size_t lengthOfCommentOnLine = 0;
- const std::size_t maxPerLine = 80;
- for (std::size_t start = 0, length = s.length(), nextStart = 0;
- start < length; start = nextStart) {
- std::size_t p = s.find_first_of(" \n", start);
- std::size_t toCopy = 1;
- bool forceBreak = false;
- if (p == std::string::npos) {
- toCopy = length - start;
- nextStart = length;
- } else {
- toCopy = p - start;
- nextStart = p + 1;
- forceBreak = s[p] == '\n';
- }
- if (lengthOfCommentOnLine > 0) {
- if (indentLength + lengthOfCommentOnLine + toCopy >= maxPerLine) {
- *this << "\n";
- indent() << " * ";
- lengthOfCommentOnLine = 0;
- } else {
- *this << " ";
- }
- }
- *this << s.substr(start, toCopy);
- if (forceBreak) {
- lengthOfCommentOnLine = maxPerLine;
- } else {
- lengthOfCommentOnLine += toCopy;
- }
- }
- *this << "\n";
- indent() << " */\n";
- }
- } // namespace slang
|