1 /* 2 * Copyright 2010, 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 23 #include "llvm/ADT/StringRef.h" 24 25 #include "os_sep.h" 26 #include "slang_utils.h" 27 28 namespace slang { 29 30 using std::string; 31 32 string RSSlangReflectUtils::GetFileNameStem(const char* fileName) { 33 const char *dot = fileName + strlen(fileName); 34 const char *slash = dot - 1; 35 while (slash >= fileName) { 36 if (*slash == OS_PATH_SEPARATOR) { 37 break; 38 } 39 if ((*slash == '.') && (*dot == 0)) { 40 dot = slash; 41 } 42 --slash; 43 } 44 ++slash; 45 return string(slash, dot - slash); 46 } 47 48 string RSSlangReflectUtils::ComputePackagedPath( 49 const char *prefixPath, const char *packageName) { 50 string packaged_path(prefixPath); 51 if (!packaged_path.empty() && 52 (packaged_path[packaged_path.length() - 1] != OS_PATH_SEPARATOR)) { 53 packaged_path += OS_PATH_SEPARATOR_STR; 54 } 55 size_t s = packaged_path.length(); 56 packaged_path += packageName; 57 while (s < packaged_path.length()) { 58 if (packaged_path[s] == '.') { 59 packaged_path[s] = OS_PATH_SEPARATOR; 60 } 61 ++s; 62 } 63 return packaged_path; 64 } 65 66 static string InternalFileNameConvert(const char *rsFileName, bool toLower) { 67 const char *dot = rsFileName + strlen(rsFileName); 68 const char *slash = dot - 1; 69 while (slash >= rsFileName) { 70 if (*slash == OS_PATH_SEPARATOR) { 71 break; 72 } 73 if ((*slash == '.') && (*dot == 0)) { 74 dot = slash; 75 } 76 --slash; 77 } 78 ++slash; 79 char ret[256]; 80 int i = 0; 81 for (; (i < 255) && (slash < dot); ++slash) { 82 if (isalnum(*slash) || *slash == '_') { 83 if (toLower) { 84 ret[i] = tolower(*slash); 85 } else { 86 ret[i] = *slash; 87 } 88 ++i; 89 } 90 } 91 ret[i] = 0; 92 return string(ret); 93 } 94 95 std::string RSSlangReflectUtils::JavaClassNameFromRSFileName( 96 const char *rsFileName) { 97 return InternalFileNameConvert(rsFileName, false); 98 } 99 100 101 std::string RSSlangReflectUtils::BCFileNameFromRSFileName( 102 const char *rsFileName) { 103 return InternalFileNameConvert(rsFileName, true); 104 } 105 106 static bool GenerateAccessorHeader( 107 const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) { 108 fprintf(pfout, "/*\n"); 109 fprintf(pfout, " * This file is auto-generated. DO NOT MODIFY!\n"); 110 fprintf(pfout, " * The source Renderscript file: %s\n", context.rsFileName); 111 fprintf(pfout, " */\n\n"); 112 fprintf(pfout, "package %s;\n\n", context.packageName); 113 114 // add imports here. 115 116 return true; 117 } 118 119 static bool GenerateAccessorMethodSignature( 120 const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) { 121 // the prototype of the accessor method 122 fprintf(pfout, " // return byte array representation of the bitcode.\n"); 123 fprintf(pfout, " public static byte[] getBitCode() {\n"); 124 return true; 125 } 126 127 // Java method size must not exceed 64k, 128 // so we have to split the bitcode into multiple segments. 129 static bool GenerateSegmentMethod( 130 const char *buff, int blen, int seg_num, FILE *pfout) { 131 132 fprintf(pfout, " private static byte[] getSegment_%d() {\n", seg_num); 133 fprintf(pfout, " byte[] data = {\n"); 134 135 static const int LINE_BYTE_NUM = 16; 136 char out_line[LINE_BYTE_NUM*6 + 10]; 137 const char *out_line_end = out_line + sizeof(out_line); 138 char *p = out_line; 139 140 int write_length = 0; 141 while (write_length < blen) { 142 p += snprintf(p, out_line_end - p, 143 " %4d,", static_cast<int>(buff[write_length])); 144 ++write_length; 145 if (((write_length % LINE_BYTE_NUM) == 0) 146 || (write_length == blen)) { 147 fprintf(pfout, " "); 148 fprintf(pfout, "%s", out_line); 149 fprintf(pfout, "\n"); 150 p = out_line; 151 } 152 } 153 154 fprintf(pfout, " };\n"); 155 fprintf(pfout, " return data;\n"); 156 fprintf(pfout, " }\n\n"); 157 158 return true; 159 } 160 161 static bool GenerateJavaCodeAccessorMethod( 162 const RSSlangReflectUtils::BitCodeAccessorContext &context, FILE *pfout) { 163 FILE *pfin = fopen(context.bcFileName, "rb"); 164 if (pfin == NULL) { 165 fprintf(stderr, "Error: could not read file %s\n", context.bcFileName); 166 return false; 167 } 168 169 // start the accessor method 170 GenerateAccessorMethodSignature(context, pfout); 171 fprintf(pfout, " return getBitCodeInternal();\n"); 172 // end the accessor method 173 fprintf(pfout, " };\n\n"); 174 175 // output the data 176 // make sure the generated function for a segment won't break the Javac 177 // size limitation (64K). 178 static const int SEG_SIZE = 0x2000; 179 char *buff = new char[SEG_SIZE]; 180 int read_length; 181 int seg_num = 0; 182 int total_length = 0; 183 while ((read_length = fread(buff, 1, SEG_SIZE, pfin)) > 0) { 184 GenerateSegmentMethod(buff, read_length, seg_num, pfout); 185 ++seg_num; 186 total_length += read_length; 187 } 188 delete []buff; 189 fclose(pfin); 190 191 // output the internal accessor method 192 fprintf(pfout, " private static int bitCodeLength = %d;\n\n", 193 total_length); 194 fprintf(pfout, " private static byte[] getBitCodeInternal() {\n"); 195 fprintf(pfout, " byte[] bc = new byte[bitCodeLength];\n"); 196 fprintf(pfout, " int offset = 0;\n"); 197 fprintf(pfout, " byte[] seg;\n"); 198 for (int i = 0; i < seg_num; ++i) { 199 fprintf(pfout, " seg = getSegment_%d();\n", i); 200 fprintf(pfout, " System.arraycopy(seg, 0, bc, offset, seg.length);\n"); 201 fprintf(pfout, " offset += seg.length;\n"); 202 } 203 fprintf(pfout, " return bc;\n"); 204 fprintf(pfout, " }\n\n"); 205 206 return true; 207 } 208 209 static bool GenerateAccessorClass( 210 const RSSlangReflectUtils::BitCodeAccessorContext &context, 211 const char *clazz_name, FILE *pfout) { 212 // begin the class. 213 fprintf(pfout, "/**\n"); 214 fprintf(pfout, " * @hide\n"); 215 fprintf(pfout, " */\n"); 216 fprintf(pfout, "public class %s {\n", clazz_name); 217 fprintf(pfout, "\n"); 218 219 bool ret = true; 220 switch (context.bcStorage) { 221 case BCST_APK_RESOURCE: 222 break; 223 case BCST_JAVA_CODE: 224 ret = GenerateJavaCodeAccessorMethod(context, pfout); 225 break; 226 default: 227 ret = false; 228 } 229 230 // end the class. 231 fprintf(pfout, "}\n"); 232 233 return ret; 234 } 235 236 237 bool RSSlangReflectUtils::GenerateBitCodeAccessor( 238 const BitCodeAccessorContext &context) { 239 string output_path = ComputePackagedPath(context.reflectPath, 240 context.packageName); 241 if (!SlangUtils::CreateDirectoryWithParents(llvm::StringRef(output_path), 242 NULL)) { 243 fprintf(stderr, "Error: could not create dir %s\n", 244 output_path.c_str()); 245 return false; 246 } 247 248 string clazz_name(JavaClassNameFromRSFileName(context.rsFileName)); 249 clazz_name += "BitCode"; 250 string filename(clazz_name); 251 filename += ".java"; 252 253 string output_filename(output_path); 254 output_filename += OS_PATH_SEPARATOR_STR; 255 output_filename += filename; 256 printf("Generating %s ...\n", filename.c_str()); 257 FILE *pfout = fopen(output_filename.c_str(), "w"); 258 if (pfout == NULL) { 259 fprintf(stderr, "Error: could not write to file %s\n", 260 output_filename.c_str()); 261 return false; 262 } 263 264 bool ret = GenerateAccessorHeader(context, pfout) && 265 GenerateAccessorClass(context, clazz_name.c_str(), pfout); 266 267 fclose(pfout); 268 return ret; 269 } 270 } 271