You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
440 lines
12 KiB
440 lines
12 KiB
/*
|
|
* 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
|