capacitor-dex-editor 0.0.54 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/aetherlink/dexeditor/DexManager.java +139 -119
- package/android/src/main/java/com/android/tools/smali/smali2/Smali.java +102 -0
- package/android/src/main/java/com/android/tools/smali/smali2/SmaliCatchErrFlexLexer.java +54 -0
- package/android/src/main/java/com/android/tools/smali/smali2/SmaliCatchErrParser.java +49 -0
- package/android/src/main/java/com/android/tools/smali/smali2/SmaliCatchErrTreeWalker.java +49 -0
- package/android/src/main/java/com/android/tools/smali/smali2/SyntaxError.java +42 -0
- package/package.json +1 -1
|
@@ -98,6 +98,30 @@ public class DexManager {
|
|
|
98
98
|
|
|
99
99
|
// 存储多 DEX 会话(MCP 工作流)
|
|
100
100
|
private final Map<String, MultiDexSession> multiDexSessions = new HashMap<>();
|
|
101
|
+
|
|
102
|
+
// APK DEX 缓存 - 用于加速编译(key: apkPath + ":" + dexPath)
|
|
103
|
+
private final Map<String, ApkDexCache> apkDexCaches = new HashMap<>();
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* APK DEX 缓存 - 缓存从 APK 读取的 DEX 数据和 ClassDef
|
|
107
|
+
*/
|
|
108
|
+
private static class ApkDexCache {
|
|
109
|
+
String apkPath;
|
|
110
|
+
String dexPath;
|
|
111
|
+
long lastModified;
|
|
112
|
+
Map<String, ClassDef> classDefMap; // type -> ClassDef
|
|
113
|
+
int dexVersion;
|
|
114
|
+
|
|
115
|
+
ApkDexCache(String apkPath, String dexPath) {
|
|
116
|
+
this.apkPath = apkPath;
|
|
117
|
+
this.dexPath = dexPath;
|
|
118
|
+
this.classDefMap = new HashMap<>();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
String getCacheKey() {
|
|
122
|
+
return apkPath + ":" + dexPath;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
101
125
|
|
|
102
126
|
/**
|
|
103
127
|
* DEX 会话 - 存储加载的 DEX 文件及其修改状态
|
|
@@ -2119,27 +2143,28 @@ public class DexManager {
|
|
|
2119
2143
|
}
|
|
2120
2144
|
|
|
2121
2145
|
/**
|
|
2122
|
-
*
|
|
2146
|
+
* 获取或创建 APK DEX 缓存
|
|
2123
2147
|
*/
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2148
|
+
private ApkDexCache getOrCreateDexCache(String apkPath, String dexPath) throws Exception {
|
|
2149
|
+
String cacheKey = apkPath + ":" + dexPath;
|
|
2150
|
+
java.io.File apkFile = new java.io.File(apkPath);
|
|
2151
|
+
long currentModified = apkFile.lastModified();
|
|
2127
2152
|
|
|
2128
|
-
|
|
2153
|
+
ApkDexCache cache = apkDexCaches.get(cacheKey);
|
|
2129
2154
|
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2155
|
+
// 检查缓存是否有效
|
|
2156
|
+
if (cache != null && cache.lastModified == currentModified && !cache.classDefMap.isEmpty()) {
|
|
2157
|
+
Log.d(TAG, "Using DEX cache for: " + cacheKey + " (" + cache.classDefMap.size() + " classes)");
|
|
2158
|
+
return cache;
|
|
2133
2159
|
}
|
|
2134
2160
|
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2161
|
+
// 需要重新加载
|
|
2162
|
+
Log.d(TAG, "Loading DEX into cache: " + cacheKey);
|
|
2163
|
+
cache = new ApkDexCache(apkPath, dexPath);
|
|
2164
|
+
cache.lastModified = currentModified;
|
|
2139
2165
|
|
|
2166
|
+
java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2140
2167
|
try {
|
|
2141
|
-
zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2142
|
-
|
|
2143
2168
|
// 尝试多种可能的 dexPath 格式
|
|
2144
2169
|
java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
|
|
2145
2170
|
if (dexEntry == null) {
|
|
@@ -2154,33 +2179,70 @@ public class DexManager {
|
|
|
2154
2179
|
}
|
|
2155
2180
|
|
|
2156
2181
|
if (dexEntry == null) {
|
|
2157
|
-
|
|
2158
|
-
return result;
|
|
2182
|
+
throw new Exception("DEX 文件未找到: " + dexPath);
|
|
2159
2183
|
}
|
|
2160
2184
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
// 读取 DEX 文件到内存
|
|
2185
|
+
// 读取 DEX 文件
|
|
2186
|
+
java.io.InputStream dexInputStream = zipFile.getInputStream(dexEntry);
|
|
2164
2187
|
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
2165
|
-
byte[] buffer = new byte[
|
|
2188
|
+
byte[] buffer = new byte[16384];
|
|
2166
2189
|
int len;
|
|
2167
2190
|
while ((len = dexInputStream.read(buffer)) != -1) {
|
|
2168
2191
|
baos.write(buffer, 0, len);
|
|
2169
2192
|
}
|
|
2193
|
+
dexInputStream.close();
|
|
2170
2194
|
byte[] dexBytes = baos.toByteArray();
|
|
2171
2195
|
|
|
2172
|
-
// 解析 DEX
|
|
2196
|
+
// 解析 DEX 文件并缓存所有 ClassDef
|
|
2173
2197
|
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.getDefault(), dexBytes);
|
|
2198
|
+
cache.dexVersion = 35; // 默认 DEX 版本
|
|
2174
2199
|
|
|
2175
|
-
// 查找目标类
|
|
2176
|
-
ClassDef targetClass = null;
|
|
2177
2200
|
for (ClassDef classDef : dexFile.getClasses()) {
|
|
2178
|
-
|
|
2179
|
-
targetClass = classDef;
|
|
2180
|
-
break;
|
|
2181
|
-
}
|
|
2201
|
+
cache.classDefMap.put(classDef.getType(), classDef);
|
|
2182
2202
|
}
|
|
2183
2203
|
|
|
2204
|
+
Log.d(TAG, "Cached " + cache.classDefMap.size() + " classes from " + dexPath);
|
|
2205
|
+
} finally {
|
|
2206
|
+
zipFile.close();
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
apkDexCaches.put(cacheKey, cache);
|
|
2210
|
+
return cache;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
/**
|
|
2214
|
+
* 清除指定 APK 的 DEX 缓存
|
|
2215
|
+
*/
|
|
2216
|
+
public void clearDexCache(String apkPath) {
|
|
2217
|
+
java.util.Iterator<String> it = apkDexCaches.keySet().iterator();
|
|
2218
|
+
while (it.hasNext()) {
|
|
2219
|
+
if (it.next().startsWith(apkPath + ":")) {
|
|
2220
|
+
it.remove();
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
Log.d(TAG, "Cleared DEX cache for: " + apkPath);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/**
|
|
2227
|
+
* 从 APK 中的 DEX 文件获取类的 Smali 代码(使用缓存)
|
|
2228
|
+
*/
|
|
2229
|
+
public JSObject getClassSmaliFromApk(String apkPath, String dexPath, String className) throws Exception {
|
|
2230
|
+
JSObject result = new JSObject();
|
|
2231
|
+
|
|
2232
|
+
Log.d(TAG, "getClassSmaliFromApk: apkPath=" + apkPath + ", dexPath=" + dexPath + ", className=" + className);
|
|
2233
|
+
|
|
2234
|
+
if (className == null || className.isEmpty()) {
|
|
2235
|
+
result.put("smali", "# 未指定类名");
|
|
2236
|
+
return result;
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
String targetType = convertClassNameToType(className);
|
|
2240
|
+
|
|
2241
|
+
try {
|
|
2242
|
+
// 使用缓存获取 ClassDef
|
|
2243
|
+
ApkDexCache cache = getOrCreateDexCache(apkPath, dexPath);
|
|
2244
|
+
ClassDef targetClass = cache.classDefMap.get(targetType);
|
|
2245
|
+
|
|
2184
2246
|
if (targetClass == null) {
|
|
2185
2247
|
result.put("smali", "# 类未找到: " + className + "\n# 目标类型: " + targetType);
|
|
2186
2248
|
return result;
|
|
@@ -2197,13 +2259,8 @@ public class DexManager {
|
|
|
2197
2259
|
|
|
2198
2260
|
result.put("smali", stringWriter.toString());
|
|
2199
2261
|
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
try { dexInputStream.close(); } catch (Exception ignored) {}
|
|
2203
|
-
}
|
|
2204
|
-
if (zipFile != null) {
|
|
2205
|
-
try { zipFile.close(); } catch (Exception ignored) {}
|
|
2206
|
-
}
|
|
2262
|
+
} catch (Exception e) {
|
|
2263
|
+
result.put("smali", "# 加载失败: " + e.getMessage());
|
|
2207
2264
|
}
|
|
2208
2265
|
|
|
2209
2266
|
return result;
|
|
@@ -2232,112 +2289,74 @@ public class DexManager {
|
|
|
2232
2289
|
}
|
|
2233
2290
|
|
|
2234
2291
|
try {
|
|
2235
|
-
//
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
result.put("error", "无法创建临时目录");
|
|
2240
|
-
return result;
|
|
2241
|
-
}
|
|
2242
|
-
|
|
2243
|
-
// 将类名转换为文件路径
|
|
2244
|
-
String classPath = className.replace(".", "/") + ".smali";
|
|
2245
|
-
java.io.File smaliFile = new java.io.File(tempDir, classPath);
|
|
2246
|
-
smaliFile.getParentFile().mkdirs();
|
|
2247
|
-
|
|
2248
|
-
// 写入 smali 文件
|
|
2249
|
-
try (java.io.FileWriter writer = new java.io.FileWriter(smaliFile)) {
|
|
2250
|
-
writer.write(smaliContent);
|
|
2251
|
-
}
|
|
2292
|
+
// 使用优化的 smali2.Smali 直接编译成 ClassDef(无需临时文件)
|
|
2293
|
+
reportTitle("编译 Smali");
|
|
2294
|
+
reportMessage("正在编译 " + className + "...");
|
|
2295
|
+
reportProgress(10, 100);
|
|
2252
2296
|
|
|
2253
|
-
Log.d(TAG, "Smali file written to: " + smaliFile.getAbsolutePath());
|
|
2254
|
-
|
|
2255
|
-
// 使用 smali 库编译 smali 文件为 dex
|
|
2256
|
-
// 注意:这需要 smali 库的支持
|
|
2257
|
-
java.io.File outputDex = new java.io.File(tempDir, "classes_modified.dex");
|
|
2258
|
-
|
|
2259
|
-
// 使用 SmaliOptions 编译
|
|
2260
2297
|
com.android.tools.smali.smali.SmaliOptions options = new com.android.tools.smali.smali.SmaliOptions();
|
|
2261
|
-
options.outputDexFile = outputDex.getAbsolutePath();
|
|
2262
2298
|
options.apiLevel = 30;
|
|
2263
2299
|
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
if (!success) {
|
|
2270
|
-
result.put("success", false);
|
|
2271
|
-
result.put("error", "Smali 编译失败");
|
|
2272
|
-
cleanupTempDir(tempDir);
|
|
2273
|
-
return result;
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
Log.d(TAG, "Smali compiled successfully to: " + outputDex.getAbsolutePath());
|
|
2277
|
-
|
|
2278
|
-
// 将编译后的类合并到原 DEX 文件中
|
|
2279
|
-
// 1. 读取原 APK 中的 DEX 文件
|
|
2280
|
-
java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2281
|
-
java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
|
|
2282
|
-
|
|
2283
|
-
if (dexEntry == null) {
|
|
2300
|
+
// 直接编译 Smali 代码为 ClassDef
|
|
2301
|
+
ClassDef newClassDef;
|
|
2302
|
+
try {
|
|
2303
|
+
newClassDef = com.android.tools.smali.smali2.Smali.assemble(smaliContent, options, 35);
|
|
2304
|
+
} catch (Exception e) {
|
|
2284
2305
|
result.put("success", false);
|
|
2285
|
-
result.put("error", "
|
|
2286
|
-
zipFile.close();
|
|
2287
|
-
cleanupTempDir(tempDir);
|
|
2306
|
+
result.put("error", "Smali 编译失败: " + e.getMessage());
|
|
2288
2307
|
return result;
|
|
2289
2308
|
}
|
|
2290
2309
|
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
2294
|
-
byte[] buffer = new byte[8192];
|
|
2295
|
-
int len;
|
|
2296
|
-
while ((len = dexInputStream.read(buffer)) != -1) {
|
|
2297
|
-
baos.write(buffer, 0, len);
|
|
2298
|
-
}
|
|
2299
|
-
byte[] originalDexBytes = baos.toByteArray();
|
|
2300
|
-
dexInputStream.close();
|
|
2310
|
+
reportProgress(20, 100);
|
|
2311
|
+
Log.d(TAG, "Smali compiled successfully to ClassDef");
|
|
2301
2312
|
|
|
2302
|
-
//
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
new java.io.BufferedInputStream(new java.io.FileInputStream(outputDex)));
|
|
2313
|
+
// 使用缓存获取 ClassDef(核心优化)
|
|
2314
|
+
reportTitle("合并 DEX");
|
|
2315
|
+
reportMessage("获取缓存...");
|
|
2306
2316
|
|
|
2307
|
-
// 合并 DEX:用新类替换原类
|
|
2308
2317
|
String targetType = "L" + className.replace(".", "/") + ";";
|
|
2309
|
-
|
|
2318
|
+
ApkDexCache cache = getOrCreateDexCache(apkPath, dexPath);
|
|
2310
2319
|
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
if (!classDef.getType().equals(targetType)) {
|
|
2314
|
-
mergedClasses.add(classDef);
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2320
|
+
reportProgress(30, 100);
|
|
2321
|
+
reportMessage("更新缓存...");
|
|
2317
2322
|
|
|
2318
|
-
//
|
|
2319
|
-
|
|
2320
|
-
mergedClasses.add(classDef);
|
|
2321
|
-
}
|
|
2323
|
+
// 更新缓存中的 ClassDef
|
|
2324
|
+
cache.classDefMap.put(targetType, newClassDef);
|
|
2322
2325
|
|
|
2323
|
-
Log.d(TAG, "
|
|
2326
|
+
Log.d(TAG, "Using cached " + cache.classDefMap.size() + " classes");
|
|
2327
|
+
|
|
2328
|
+
// 创建新的 DEX 文件(使用缓存的 ClassDef)
|
|
2329
|
+
reportMessage("写入 DEX (" + cache.classDefMap.size() + " 个类)...");
|
|
2330
|
+
reportProgress(40, 100);
|
|
2324
2331
|
|
|
2325
|
-
// 创建新的 DEX 文件
|
|
2326
|
-
java.io.File mergedDexFile = new java.io.File(tempDir, "classes_merged.dex");
|
|
2327
2332
|
DexPool dexPool = new DexPool(Opcodes.getDefault());
|
|
2328
|
-
|
|
2333
|
+
int totalClasses = cache.classDefMap.size();
|
|
2334
|
+
int currentClass = 0;
|
|
2335
|
+
for (ClassDef classDef : cache.classDefMap.values()) {
|
|
2329
2336
|
dexPool.internClass(classDef);
|
|
2337
|
+
currentClass++;
|
|
2338
|
+
if (currentClass % 200 == 0 || currentClass == totalClasses) {
|
|
2339
|
+
reportProgress(40 + (currentClass * 40 / totalClasses), 100);
|
|
2340
|
+
}
|
|
2330
2341
|
}
|
|
2331
|
-
dexPool.writeTo(new FileDataStore(mergedDexFile));
|
|
2332
2342
|
|
|
2333
|
-
|
|
2343
|
+
// 使用内存写入 DEX(避免临时文件)
|
|
2344
|
+
reportProgress(80, 100);
|
|
2345
|
+
reportMessage("生成 DEX...");
|
|
2346
|
+
com.android.tools.smali.dexlib2.writer.io.MemoryDataStore memoryStore =
|
|
2347
|
+
new com.android.tools.smali.dexlib2.writer.io.MemoryDataStore();
|
|
2348
|
+
dexPool.writeTo(memoryStore);
|
|
2349
|
+
byte[] newDexBytes = java.util.Arrays.copyOf(memoryStore.getBuffer(), memoryStore.getSize());
|
|
2334
2350
|
|
|
2335
|
-
|
|
2351
|
+
Log.d(TAG, "Merged DEX size: " + newDexBytes.length + " bytes");
|
|
2336
2352
|
|
|
2337
2353
|
// MT 风格:直接替换 APK 内的 DEX
|
|
2354
|
+
reportTitle("更新 APK");
|
|
2355
|
+
reportMessage("正在替换 DEX...");
|
|
2356
|
+
reportProgress(85, 100);
|
|
2357
|
+
|
|
2338
2358
|
java.io.File apkFile = new java.io.File(apkPath);
|
|
2339
2359
|
java.io.File tempApkFile = new java.io.File(apkPath + ".tmp");
|
|
2340
|
-
byte[] newDexBytes = readFileBytes(mergedDexFile);
|
|
2341
2360
|
|
|
2342
2361
|
Log.d(TAG, "Replacing DEX in APK (MT style)...");
|
|
2343
2362
|
|
|
@@ -2393,15 +2412,16 @@ public class DexManager {
|
|
|
2393
2412
|
}
|
|
2394
2413
|
|
|
2395
2414
|
Log.d(TAG, "APK updated successfully: " + apkPath);
|
|
2415
|
+
reportProgress(100, 100);
|
|
2416
|
+
|
|
2417
|
+
// 更新缓存的 lastModified 以匹配新的 APK
|
|
2418
|
+
cache.lastModified = new java.io.File(apkPath).lastModified();
|
|
2396
2419
|
|
|
2397
2420
|
result.put("success", true);
|
|
2398
2421
|
result.put("message", "Smali 编译成功!APK 已更新");
|
|
2399
2422
|
result.put("apkPath", apkPath);
|
|
2400
2423
|
result.put("needSign", true);
|
|
2401
2424
|
|
|
2402
|
-
// 清理临时文件
|
|
2403
|
-
cleanupTempDir(tempDir);
|
|
2404
|
-
|
|
2405
2425
|
} catch (Exception e) {
|
|
2406
2426
|
Log.e(TAG, "Error saving smali: " + e.getMessage(), e);
|
|
2407
2427
|
result.put("success", false);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Based on Dex-Editor-Android by developer-krushna
|
|
3
|
+
* Original Author @MikeAndrson
|
|
4
|
+
*
|
|
5
|
+
* 直接将 Smali 代码编译成 ClassDef,无需临时文件
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
package com.android.tools.smali.smali2;
|
|
9
|
+
|
|
10
|
+
import com.android.tools.smali.dexlib2.Opcodes;
|
|
11
|
+
import com.android.tools.smali.dexlib2.iface.ClassDef;
|
|
12
|
+
import com.android.tools.smali.dexlib2.writer.builder.DexBuilder;
|
|
13
|
+
import com.android.tools.smali.smali.SmaliOptions;
|
|
14
|
+
|
|
15
|
+
import java.io.StringReader;
|
|
16
|
+
import org.antlr.runtime.CommonTokenStream;
|
|
17
|
+
import org.antlr.runtime.tree.CommonTree;
|
|
18
|
+
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
|
19
|
+
|
|
20
|
+
public class Smali {
|
|
21
|
+
|
|
22
|
+
public static ClassDef assemble(String smaliCode, SmaliOptions options, int dexVer)
|
|
23
|
+
throws Exception {
|
|
24
|
+
DexBuilder dexBuilder = new DexBuilder(Opcodes.forDexVersion(dexVer));
|
|
25
|
+
|
|
26
|
+
SmaliCatchErrFlexLexer lexer = new SmaliCatchErrFlexLexer(new StringReader(smaliCode), options.apiLevel);
|
|
27
|
+
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
|
28
|
+
|
|
29
|
+
SmaliCatchErrParser parser = new SmaliCatchErrParser(tokens);
|
|
30
|
+
parser.setVerboseErrors(options.verboseErrors);
|
|
31
|
+
parser.setAllowOdex(options.allowOdexOpcodes);
|
|
32
|
+
parser.setApiLevel(dexVer);
|
|
33
|
+
|
|
34
|
+
SmaliCatchErrParser.smali_file_return result = parser.smali_file();
|
|
35
|
+
|
|
36
|
+
if (lexer.getNumberOfSyntaxErrors() > 0) {
|
|
37
|
+
throw new Exception(lexer.getErrorsString());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (parser.getNumberOfSyntaxErrors() > 0) {
|
|
41
|
+
throw new Exception(parser.getErrorsString());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
CommonTree t = result.getTree();
|
|
45
|
+
|
|
46
|
+
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
|
47
|
+
treeStream.setTokenStream(tokens);
|
|
48
|
+
|
|
49
|
+
SmaliCatchErrTreeWalker treeWalker = new SmaliCatchErrTreeWalker(treeStream);
|
|
50
|
+
treeWalker.setApiLevel(dexVer);
|
|
51
|
+
treeWalker.setVerboseErrors(options.verboseErrors);
|
|
52
|
+
treeWalker.setDexBuilder(dexBuilder);
|
|
53
|
+
|
|
54
|
+
ClassDef classDef = treeWalker.smali_file();
|
|
55
|
+
|
|
56
|
+
if (treeWalker.getNumberOfSyntaxErrors() > 0) {
|
|
57
|
+
throw new Exception(treeWalker.getErrorsString());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return classDef;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public static ClassDef assemble(String smaliCode, SmaliOptions options) throws Exception {
|
|
64
|
+
DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel));
|
|
65
|
+
|
|
66
|
+
SmaliCatchErrFlexLexer lexer = new SmaliCatchErrFlexLexer(new StringReader(smaliCode), options.apiLevel);
|
|
67
|
+
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
|
68
|
+
|
|
69
|
+
SmaliCatchErrParser parser = new SmaliCatchErrParser(tokens);
|
|
70
|
+
parser.setVerboseErrors(options.verboseErrors);
|
|
71
|
+
parser.setAllowOdex(options.allowOdexOpcodes);
|
|
72
|
+
parser.setApiLevel(options.apiLevel);
|
|
73
|
+
|
|
74
|
+
SmaliCatchErrParser.smali_file_return result = parser.smali_file();
|
|
75
|
+
|
|
76
|
+
if (lexer.getNumberOfSyntaxErrors() > 0) {
|
|
77
|
+
throw new Exception(lexer.getErrorsString());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (parser.getNumberOfSyntaxErrors() > 0) {
|
|
81
|
+
throw new Exception(parser.getErrorsString());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
CommonTree t = result.getTree();
|
|
85
|
+
|
|
86
|
+
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
|
|
87
|
+
treeStream.setTokenStream(tokens);
|
|
88
|
+
|
|
89
|
+
SmaliCatchErrTreeWalker treeWalker = new SmaliCatchErrTreeWalker(treeStream);
|
|
90
|
+
treeWalker.setApiLevel(options.apiLevel);
|
|
91
|
+
treeWalker.setVerboseErrors(options.verboseErrors);
|
|
92
|
+
treeWalker.setDexBuilder(dexBuilder);
|
|
93
|
+
|
|
94
|
+
ClassDef classDef = treeWalker.smali_file();
|
|
95
|
+
|
|
96
|
+
if (treeWalker.getNumberOfSyntaxErrors() > 0) {
|
|
97
|
+
throw new Exception(treeWalker.getErrorsString());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return classDef;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Based on Dex-Editor-Android by developer-krushna
|
|
3
|
+
* Original Author @MikeAndrson
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
package com.android.tools.smali.smali2;
|
|
7
|
+
|
|
8
|
+
import com.android.tools.smali.smali.InvalidToken;
|
|
9
|
+
import com.android.tools.smali.smali.smaliFlexLexer;
|
|
10
|
+
|
|
11
|
+
import java.io.Reader;
|
|
12
|
+
import java.util.ArrayList;
|
|
13
|
+
import java.util.List;
|
|
14
|
+
|
|
15
|
+
public class SmaliCatchErrFlexLexer extends smaliFlexLexer {
|
|
16
|
+
|
|
17
|
+
private StringBuilder errors = new StringBuilder();
|
|
18
|
+
private List<SyntaxError> syntaxErrors = new ArrayList<>();
|
|
19
|
+
|
|
20
|
+
public SmaliCatchErrFlexLexer(Reader reader, int apiLevel) {
|
|
21
|
+
super(reader, apiLevel);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public String getErrorHeader(InvalidToken invalidToken) {
|
|
26
|
+
errors.append("[")
|
|
27
|
+
.append(invalidToken.getLine())
|
|
28
|
+
.append(",")
|
|
29
|
+
.append(invalidToken.getCharPositionInLine())
|
|
30
|
+
.append("] Error for input '")
|
|
31
|
+
.append(invalidToken.getText())
|
|
32
|
+
.append("': ")
|
|
33
|
+
.append(invalidToken.getMessage())
|
|
34
|
+
.append("\n");
|
|
35
|
+
|
|
36
|
+
syntaxErrors.add(new SyntaxError(
|
|
37
|
+
invalidToken.getLine(),
|
|
38
|
+
invalidToken.getCharPositionInLine(),
|
|
39
|
+
invalidToken.getLine(),
|
|
40
|
+
invalidToken.getCharPositionInLine() + invalidToken.getText().length(),
|
|
41
|
+
invalidToken.getMessage()
|
|
42
|
+
));
|
|
43
|
+
|
|
44
|
+
return super.getErrorHeader(invalidToken);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public String getErrorsString() {
|
|
48
|
+
return errors.toString();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public List<SyntaxError> getErrors() {
|
|
52
|
+
return syntaxErrors;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Based on Dex-Editor-Android by developer-krushna
|
|
3
|
+
* Original Author @MikeAndrson
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
package com.android.tools.smali.smali2;
|
|
7
|
+
|
|
8
|
+
import org.antlr.runtime.CommonTokenStream;
|
|
9
|
+
import org.antlr.runtime.RecognitionException;
|
|
10
|
+
import com.android.tools.smali.smali.smaliParser;
|
|
11
|
+
|
|
12
|
+
import java.util.ArrayList;
|
|
13
|
+
import java.util.List;
|
|
14
|
+
|
|
15
|
+
public class SmaliCatchErrParser extends smaliParser {
|
|
16
|
+
|
|
17
|
+
private StringBuilder errors = new StringBuilder();
|
|
18
|
+
private List<SyntaxError> syntaxErrors = new ArrayList<>();
|
|
19
|
+
|
|
20
|
+
public SmaliCatchErrParser(CommonTokenStream tokens) {
|
|
21
|
+
super(tokens);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public void emitErrorMessage(String msg) {
|
|
26
|
+
errors.append(msg).append("\n");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public String getErrorsString() {
|
|
30
|
+
return errors.toString();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Override
|
|
34
|
+
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
|
|
35
|
+
syntaxErrors.add(new SyntaxError(
|
|
36
|
+
e.line,
|
|
37
|
+
e.charPositionInLine,
|
|
38
|
+
e.line,
|
|
39
|
+
e.charPositionInLine + (e.token != null ? e.token.getText().length() : 1),
|
|
40
|
+
getErrorMessage(e, tokenNames)
|
|
41
|
+
));
|
|
42
|
+
|
|
43
|
+
super.displayRecognitionError(tokenNames, e);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public List<SyntaxError> getErrors() {
|
|
47
|
+
return syntaxErrors;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Based on Dex-Editor-Android by developer-krushna
|
|
3
|
+
* Original Author @MikeAndrson
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
package com.android.tools.smali.smali2;
|
|
7
|
+
|
|
8
|
+
import org.antlr.runtime.RecognitionException;
|
|
9
|
+
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
|
10
|
+
import com.android.tools.smali.smali.smaliTreeWalker;
|
|
11
|
+
|
|
12
|
+
import java.util.ArrayList;
|
|
13
|
+
import java.util.List;
|
|
14
|
+
|
|
15
|
+
public class SmaliCatchErrTreeWalker extends smaliTreeWalker {
|
|
16
|
+
|
|
17
|
+
private StringBuilder errors = new StringBuilder();
|
|
18
|
+
private List<SyntaxError> syntaxErrors = new ArrayList<>();
|
|
19
|
+
|
|
20
|
+
public SmaliCatchErrTreeWalker(CommonTreeNodeStream treeStream) {
|
|
21
|
+
super(treeStream);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public void emitErrorMessage(String msg) {
|
|
26
|
+
errors.append(msg);
|
|
27
|
+
errors.append("\n");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public String getErrorsString() {
|
|
31
|
+
return errors.toString();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Override
|
|
35
|
+
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
|
|
36
|
+
syntaxErrors.add(new SyntaxError(
|
|
37
|
+
e.line,
|
|
38
|
+
e.charPositionInLine,
|
|
39
|
+
e.line,
|
|
40
|
+
e.charPositionInLine + (e.token != null ? e.token.getText().length() : 1),
|
|
41
|
+
getErrorMessage(e, tokenNames)
|
|
42
|
+
));
|
|
43
|
+
super.displayRecognitionError(tokenNames, e);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public List<SyntaxError> getErrors() {
|
|
47
|
+
return syntaxErrors;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Based on Dex-Editor-Android by developer-krushna
|
|
3
|
+
* Original Author @MikeAndrson
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
package com.android.tools.smali.smali2;
|
|
7
|
+
|
|
8
|
+
public class SyntaxError {
|
|
9
|
+
private final int startLine;
|
|
10
|
+
private final int startColumn;
|
|
11
|
+
private final int endLine;
|
|
12
|
+
private final int endColumn;
|
|
13
|
+
private final String message;
|
|
14
|
+
|
|
15
|
+
public SyntaxError(int startLine, int startColumn, int endLine, int endColumn, String message) {
|
|
16
|
+
this.startLine = startLine;
|
|
17
|
+
this.startColumn = startColumn;
|
|
18
|
+
this.endLine = endLine;
|
|
19
|
+
this.endColumn = endColumn;
|
|
20
|
+
this.message = message;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public int getStartLine() {
|
|
24
|
+
return startLine;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public int getStartColumn() {
|
|
28
|
+
return startColumn;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public int getEndLine() {
|
|
32
|
+
return endLine;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public int getEndColumn() {
|
|
36
|
+
return endColumn;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public String getMessage() {
|
|
40
|
+
return message;
|
|
41
|
+
}
|
|
42
|
+
}
|