Skip to content

Commit a77b126

Browse files
committed
Remove deprecated DexFile api
1 parent abf71d7 commit a77b126

File tree

13 files changed

+463
-121
lines changed

13 files changed

+463
-121
lines changed

test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,6 @@ describe("Tests for runtime binding generator", function () {
4949
expect(isInstanceOf).toEqual(true);
5050
});
5151

52-
it("When_implementing_interface_and_its_implemented_interfaces", function() {
53-
var impl = new com.tns.tests.MyDerivedPublicInterface({
54-
methodOfMyDerivedPublicInterface: function(input) {
55-
return "Le java " + input;
56-
},
57-
methodOfMyPublicInterface: function(input) {
58-
return "Le java " + input;
59-
}
60-
});
61-
62-
try {
63-
var C = java.lang.Class.forName("com.tns.gen.com.tns.tests.MyDerivedPublicInterface");
64-
expect(C).not.toBe(null);
65-
66-
var expected = "Le java test derived method";
67-
var actual = impl.methodOfMyPublicInterface("test derived method");
68-
expect(actual).toBe(expected);
69-
} catch (e) {
70-
//fail("class was not found");
71-
expect(true).toBe(false);
72-
}
73-
});
74-
7552
it("When_generating_a_class_that_implements_interfaces_javascript", function() {
7653

7754
__log("TEST: When_generating_a_class_that_implements_interfaces_javascript");

test-app/runtime/src/main/java/com/tns/ClassResolver.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package com.tns;
22

3+
import com.tns.system.classes.loading.ClassStorageService;
4+
35
import java.io.IOException;
46

57
class ClassResolver {
6-
private final Runtime runtime;
8+
private final ClassStorageService classStorageService;
79

8-
public ClassResolver(Runtime runtime) {
9-
this.runtime = runtime;
10+
ClassResolver(ClassStorageService classStorageService) {
11+
this.classStorageService = classStorageService;
1012
}
1113

12-
public Class<?> resolveClass(String baseClassName, String fullClassName, DexFactory dexFactory, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException {
14+
Class<?> resolveClass(String baseClassName, String fullClassName, DexFactory dexFactory, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException {
1315
String canonicalClassName = fullClassName.replace('/', '.');
1416
String canonicalBaseClassName = baseClassName.replace('/', '.');
1517
String name = "";
@@ -24,7 +26,7 @@ public Class<?> resolveClass(String baseClassName, String fullClassName, DexFact
2426
}
2527

2628
if (clazz == null) {
27-
clazz = Runtime.getClassForName(className);
29+
clazz = classStorageService.retrieveClass(className);
2830
}
2931

3032
return clazz;

test-app/runtime/src/main/java/com/tns/DexFactory.java

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
import android.util.Log;
44

5+
import com.tns.bindings.AnnotationDescriptor;
6+
import com.tns.bindings.ProxyGenerator;
7+
import com.tns.bindings.desc.ClassDescriptor;
8+
import com.tns.bindings.desc.reflection.ClassInfo;
9+
import com.tns.system.classes.loading.ClassStorageService;
10+
511
import java.io.BufferedReader;
612
import java.io.BufferedWriter;
713
import java.io.File;
@@ -12,18 +18,11 @@
1218
import java.io.InputStreamReader;
1319
import java.io.InvalidClassException;
1420
import java.io.OutputStreamWriter;
15-
import java.util.ArrayList;
1621
import java.util.HashMap;
1722
import java.util.HashSet;
18-
import java.util.List;
1923
import java.util.zip.ZipEntry;
2024
import java.util.zip.ZipOutputStream;
2125

22-
import com.tns.bindings.AnnotationDescriptor;
23-
import com.tns.bindings.ProxyGenerator;
24-
import com.tns.bindings.desc.ClassDescriptor;
25-
import com.tns.bindings.desc.reflection.ClassInfo;
26-
2726
import dalvik.system.DexClassLoader;
2827

2928
public class DexFactory {
@@ -34,11 +33,12 @@ public class DexFactory {
3433
private final File odexDir;
3534
private final String dexThumb;
3635
private final ClassLoader classLoader;
36+
private final ClassStorageService classStorageService;
3737

3838
private ProxyGenerator proxyGenerator;
3939
private HashMap<String, Class<?>> injectedDexClasses = new HashMap<String, Class<?>>();
4040

41-
public DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, String dexThumb) {
41+
DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, String dexThumb, ClassStorageService classStorageService) {
4242
this.logger = logger;
4343
this.classLoader = classLoader;
4444
this.dexDir = dexBaseDir;
@@ -58,6 +58,7 @@ public DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, Strin
5858

5959
this.updateDexThumbAndPurgeCache();
6060
this.proxyGenerator.setProxyThumb(this.dexThumb);
61+
this.classStorageService = classStorageService;
6162
}
6263

6364
static long totalGenTime = 0;
@@ -154,36 +155,18 @@ public Class<?> resolveClass(String baseClassName, String name, String className
154155
out.closeEntry();
155156
out.close();
156157
}
157-
//
158158

159-
Class<?> result = null;
160-
try {
161-
// use DexFile instead of DexClassLoader to allow class loading
162-
// within the default class loader
163-
// Note: According to the official documentation, DexFile should not
164-
// be directly used.
165-
// However, this is the only viable way to get our dynamic classes
166-
// loaded within the system class loader
159+
Class<?> result;
167160

168-
if (isInterface) {
169-
@SuppressWarnings("deprecation")
170-
dalvik.system.DexFile df = dalvik.system.DexFile.loadDex(jarFilePath, new File(this.odexDir, fullClassName).getAbsolutePath(), 0);
171-
result = df.loadClass(fullClassName, classLoader);
172-
} else {
173-
@SuppressWarnings("deprecation")
174-
dalvik.system.DexFile df = dalvik.system.DexFile.loadDex(jarFilePath, new File(this.odexDir, desiredDexClassName).getAbsolutePath(), 0);
175-
result = df.loadClass(desiredDexClassName, classLoader);
176-
}
177-
} catch (IOException e) {
178-
Log.w("JS", String.format("Error resolving class %s: %s. Fall back to DexClassLoader."));
179-
if (com.tns.Runtime.isDebuggable()) {
180-
e.printStackTrace();
181-
}
182-
// fall back to DexClassLoader
161+
if (isInterface) {
183162
DexClassLoader dexClassLoader = new DexClassLoader(jarFilePath, this.odexDir.getAbsolutePath(), null, classLoader);
184163
result = dexClassLoader.loadClass(fullClassName);
164+
} else {
165+
DexClassLoader dexClassLoader = new DexClassLoader(jarFilePath, this.odexDir.getAbsolutePath(), null, classLoader);
166+
result = dexClassLoader.loadClass(desiredDexClassName);
185167
}
186168

169+
classStorageService.storeClass(result.getName(), result);
187170
this.injectedDexClasses.put(originalFullClassName, result);
188171

189172
return result;

test-app/runtime/src/main/java/com/tns/Runtime.java

Lines changed: 34 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
11
package com.tns;
22

3+
import android.os.Handler;
4+
import android.os.HandlerThread;
5+
import android.os.Looper;
6+
import android.os.Message;
7+
import android.os.Process;
8+
9+
import com.tns.bindings.ProxyGenerator;
10+
import com.tns.system.classes.caching.impl.ClassCacheImpl;
11+
import com.tns.system.classes.loading.ClassStorageService;
12+
import com.tns.system.classes.loading.impl.ClassStorageServiceImpl;
13+
import com.tns.system.classloaders.impl.ClassLoadersCollectionImpl;
14+
315
import java.io.File;
416
import java.io.IOException;
5-
import java.nio.ByteBuffer;
617
import java.lang.ref.WeakReference;
718
import java.lang.reflect.Array;
819
import java.lang.reflect.Constructor;
920
import java.lang.reflect.Field;
1021
import java.lang.reflect.Method;
1122
import java.lang.reflect.Modifier;
23+
import java.nio.ByteBuffer;
1224
import java.util.ArrayList;
1325
import java.util.Arrays;
1426
import java.util.Comparator;
1527
import java.util.Date;
1628
import java.util.HashMap;
17-
import java.util.HashSet;
1829
import java.util.Map;
1930
import java.util.Queue;
2031
import java.util.concurrent.ConcurrentHashMap;
2132
import java.util.concurrent.ConcurrentLinkedQueue;
2233
import java.util.concurrent.atomic.AtomicInteger;
2334

24-
import android.os.Handler;
25-
import android.os.HandlerThread;
26-
import android.os.Looper;
27-
import android.os.Message;
28-
import android.os.Process;
29-
import android.util.Log;
30-
import android.util.SparseArray;
31-
32-
import com.tns.bindings.ProxyGenerator;
33-
3435
public class Runtime {
3536
private native void initNativeScript(int runtimeId, String filesPath, String nativeLibDir, boolean verboseLoggingEnabled, boolean isDebuggable, String packageName,
3637
Object[] v8Options, String callingDir, int maxLogcatObjectSize, boolean forceLog);
@@ -94,10 +95,6 @@ public static void passSuppressedExceptionToJs(Throwable ex, String methodName)
9495

9596
private boolean initialized;
9697

97-
private final static HashMap<String, Class<?>> classCache = new HashMap<String, Class<?>>();
98-
99-
private final static HashSet<ClassLoader> classLoaderCache = new HashSet<ClassLoader>();
100-
10198
private final static String FAILED_CTOR_RESOLUTION_MSG = "Check the number and type of arguments.\n" +
10299
"Primitive types need to be manually wrapped in their respective Object wrappers.\n" +
103100
"If you are creating an instance of an inner class, make sure to always provide reference to the outer `this` as the first argument.";
@@ -181,6 +178,8 @@ public int compare(Method lhs, Method rhs) {
181178
*/
182179
private Map<Integer, Handler> workerIdToHandler = new HashMap<>();
183180

181+
private static final ClassStorageService classStorageService = new ClassStorageServiceImpl(ClassCacheImpl.INSTANCE, ClassLoadersCollectionImpl.INSTANCE);
182+
184183
public Runtime(ClassResolver classResolver, GcListener gcListener, StaticConfiguration config, DynamicConfiguration dynamicConfig, int runtimeId, int workerId, HashMap<Integer, Object> strongInstances, HashMap<Integer, WeakReference<Object>> weakInstances, NativeScriptHashMap<Object, Integer> strongJavaObjectToId, NativeScriptWeakHashMap<Object, Integer> weakJavaObjectToId) {
185184
this.classResolver = classResolver;
186185
this.gcListener = gcListener;
@@ -212,7 +211,7 @@ public Runtime(StaticConfiguration config, DynamicConfiguration dynamicConfigura
212211
this.mainThreadHandler = dynamicConfiguration.mainThreadScheduler.getHandler();
213212
}
214213

215-
classResolver = new ClassResolver(this);
214+
classResolver = new ClassResolver(classStorageService);
216215
currentRuntime.set(this);
217216

218217
runtimeCache.put(this.runtimeId, this);
@@ -236,7 +235,7 @@ public static Runtime getCurrentRuntime() {
236235

237236
public static boolean isDebuggable() {
238237
Runtime runtime = com.tns.Runtime.getCurrentRuntime();
239-
if(runtime != null) {
238+
if (runtime != null) {
240239
return runtime.config.isDebuggable;
241240
} else {
242241
return false;
@@ -265,7 +264,7 @@ private static String getStackTraceOnly(String content) {
265264
lines = Arrays.copyOfRange(lines, 1, lines.length);
266265
}
267266
String result = "";
268-
for (String line: lines) {
267+
for (String line : lines) {
269268
result += line + "\n";
270269
}
271270
return result;
@@ -284,7 +283,7 @@ public static String getStackTraceErrorMessage(Throwable ex) {
284283
content = baos.toString("US-ASCII");
285284
if (ex instanceof NativeScriptException) {
286285
content = getStackTraceOnly(content);
287-
content = ((NativeScriptException)ex).getIncomingStackTrace() + content;
286+
content = ((NativeScriptException) ex).getIncomingStackTrace() + content;
288287
}
289288
} catch (java.io.UnsupportedEncodingException e) {
290289
content = e.getMessage();
@@ -599,7 +598,7 @@ private void init(Logger logger, String appName, String nativeLibDir, File rootD
599598
try {
600599
this.logger = logger;
601600

602-
this.dexFactory = new DexFactory(logger, classLoader, dexDir, dexThumb);
601+
this.dexFactory = new DexFactory(logger, classLoader, dexDir, dexThumb, classStorageService);
603602

604603
if (logger.isEnabled()) {
605604
logger.write("Initializing NativeScript JAVA");
@@ -784,25 +783,7 @@ private void createJSInstance(Object instance) {
784783

785784
@RuntimeCallable
786785
private static String[] getTypeMetadata(String className, int index) throws ClassNotFoundException {
787-
Class<?> clazz = classCache.get(className);
788-
789-
if (clazz == null) {
790-
for (ClassLoader classLoader : classLoaderCache) {
791-
try {
792-
clazz = classLoader.loadClass(className);
793-
if (clazz != null) {
794-
classCache.put(className, clazz);
795-
break;
796-
}
797-
} catch (ClassNotFoundException e1) {
798-
Log.w("JS", "Dynamically loading class " + className + " was unsuccessful. Will attempt to load class from alternative ClassLoader.");
799-
}
800-
}
801-
if (clazz == null) {
802-
clazz = Class.forName(className);
803-
classCache.put(className, clazz);
804-
}
805-
}
786+
Class<?> clazz = classStorageService.retrieveClass(className);
806787

807788
String[] result = getTypeMetadata(clazz, index);
808789

@@ -924,14 +905,7 @@ private void makeInstanceStrong(Object instance, int objectId) {
924905
strongJavaObjectToID.put(instance, key);
925906

926907
Class<?> clazz = instance.getClass();
927-
String className = clazz.getName();
928-
if (!classCache.containsKey(className)) {
929-
classCache.put(className, clazz);
930-
ClassLoader clazzloader = clazz.getClassLoader();
931-
if (!classLoaderCache.contains(clazzloader)) {
932-
classLoaderCache.add(clazzloader);
933-
}
934-
}
908+
classStorageService.storeClass(clazz.getName(), clazz);
935909

936910
if (logger != null && logger.isEnabled()) {
937911
logger.write("MakeInstanceStrong (" + key + ", " + instance.getClass().toString() + ")");
@@ -1156,16 +1130,8 @@ private Object[] packageArgs(Object... args) {
11561130
return packagedArgs;
11571131
}
11581132

1159-
static Class<?> getClassForName(String className) throws ClassNotFoundException {
1160-
Class<?> clazz = classCache.get(className);
1161-
if (clazz == null) {
1162-
clazz = Class.forName(className);
1163-
if (clazz != null) {
1164-
classCache.put(className, clazz);
1165-
}
1166-
}
1167-
1168-
return clazz;
1133+
static Class<?> getClassForName(String className) {
1134+
return classStorageService.retrieveClass(className);
11691135
}
11701136

11711137
@RuntimeCallable
@@ -1191,7 +1157,8 @@ private String resolveMethodOverload(String className, String methodName, Object
11911157
logger.write("resolveMethodOverload: Resolving method " + methodName + " on class " + className);
11921158
}
11931159

1194-
Class<?> clazz = getClassForName(className);
1160+
Class<?> clazz = classStorageService.retrieveClass(className);
1161+
11951162

11961163
String res = MethodResolver.resolveMethodOverload(clazz, methodName, args);
11971164
if (logger.isEnabled()) {
@@ -1306,8 +1273,14 @@ public void run() {
13061273

13071274
@RuntimeCallable
13081275
private static Class<?> getCachedClass(String className) {
1309-
Class<?> clazz = classCache.get(className);
1310-
return clazz;
1276+
Class<?> clazz;
1277+
1278+
try {
1279+
clazz = classStorageService.retrieveClass(className);
1280+
return clazz;
1281+
} catch (RuntimeException e){
1282+
return null;
1283+
}
13111284
}
13121285

13131286
@RuntimeCallable
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.tns.system.classes.caching;
2+
3+
public interface ClassCache {
4+
5+
/**
6+
* Get cached class by given key
7+
* @param lookupKey key used for getting the class
8+
* @return cached class
9+
*/
10+
Class<?> getCachedClass(String lookupKey);
11+
12+
/**
13+
* Adds a class to the cache
14+
*
15+
* @param lookupKey key used for getting the class at later point; must not be null or empty string; at most times it's probably best to be the class' name
16+
* @param clazz class to store in the cache
17+
*/
18+
void addClass(String lookupKey, Class<?> clazz);
19+
}

0 commit comments

Comments
 (0)