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
28namespace slang {
29
30using std::string;
31
32string 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
48string 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
66static 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
95std::string RSSlangReflectUtils::JavaClassNameFromRSFileName(
96    const char *rsFileName) {
97    return InternalFileNameConvert(rsFileName, false);
98}
99
100
101std::string RSSlangReflectUtils::BCFileNameFromRSFileName(
102    const char *rsFileName) {
103    return InternalFileNameConvert(rsFileName, true);
104}
105
106static 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
119static 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.
129static 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
161static 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
209static 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
237bool 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}  // namespace slang
271