-
Notifications
You must be signed in to change notification settings - Fork 47
optimize: JSMethod #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
optimize: JSMethod #108
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,7 @@ | ||
| -keepclasseswithmembers class * { | ||
| native <methods>; | ||
| } | ||
| -keep class com.whl.quickjs.**{*;} | ||
| -keep class com.whl.quickjs.**{*;} | ||
| -keepclassmembers,allowobfuscation class * { | ||
| @com.whl.quickjs.wrapper.JSMethod <methods>; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,10 @@ | |
|
|
||
| import java.lang.reflect.InvocationTargetException; | ||
| import java.lang.reflect.Method; | ||
| import java.lang.reflect.Modifier; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.HashMap; | ||
| import java.util.HashSet; | ||
| import java.util.Map; | ||
|
|
||
| /** | ||
|
|
@@ -104,33 +104,37 @@ private void setPropertyObject(String name, Object o) { | |
| public void setProperty(String name, Class<?> clazz) { | ||
| Object javaObj = null; | ||
| try { | ||
| javaObj = clazz.newInstance(); | ||
| } catch (IllegalAccessException e) { | ||
| javaObj = clazz.getConstructor().newInstance(); | ||
| } catch (IllegalAccessException | ||
| | InstantiationException | ||
| | InvocationTargetException | ||
| | NoSuchMethodException e) { | ||
| e.printStackTrace(); | ||
| } catch (InstantiationException e) { | ||
| e.printStackTrace(); | ||
| } | ||
|
|
||
| if (javaObj == null) { | ||
| throw new NullPointerException("The JavaObj cannot be null. An error occurred in newInstance!"); | ||
| } | ||
|
|
||
| JSObject jsObj = context.createNewJSObject(); | ||
|
Comment on lines
106
to
115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (bug_risk): 构造函数失败时,只打印堆栈而没有进一步处理,会导致实例方法被静默忽略。 在改为使用 Original comment in Englishissue (bug_risk): Constructor failure leads to silent dropping of instance methods with only a stack trace as signal. With the new use of |
||
| Method[] methods = clazz.getMethods(); | ||
| Object finalJavaObj = javaObj; | ||
| for (Method method : methods) { | ||
| if (method.isAnnotationPresent(JSMethod.class)) { | ||
| Object finalJavaObj = javaObj; | ||
| jsObj.setProperty(method.getName(), args -> { | ||
| try { | ||
| return method.invoke(finalJavaObj, args); | ||
| } catch (IllegalAccessException e) { | ||
| e.printStackTrace(); | ||
| } catch (InvocationTargetException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| return null; | ||
| }); | ||
| if (finalJavaObj == null && !Modifier.isStatic(method.getModifiers())) { | ||
| continue; | ||
| } | ||
| JSMethod jsMethod = method.getAnnotation(JSMethod.class); | ||
| if (jsMethod == null) { | ||
| continue; | ||
| } | ||
| String jsMethodName = jsMethod.value(); | ||
| if (jsMethodName.isBlank()) { | ||
| jsMethodName = method.getName(); | ||
| } | ||
| jsObj.setProperty(jsMethodName, args -> { | ||
| try { | ||
| return method.invoke(finalJavaObj, args); | ||
| } catch (IllegalAccessException | InvocationTargetException e) { | ||
| e.printStackTrace(); | ||
| } | ||
| return null; | ||
| }); | ||
| } | ||
|
|
||
| setProperty(name, jsObj); | ||
|
|
@@ -288,6 +292,7 @@ public ArrayList<Object> toArray(MapFilter filter) { | |
| return toArray(filter, null, null); | ||
| } | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| protected void convertToMap(Object target, Object map, HashMap<Long, Object> circulars, MapFilter filter, Object extra, MapCreator mapCreator) { | ||
| circulars.put(((JSObject) target).getPointer(), map); | ||
|
|
||
|
|
@@ -314,7 +319,7 @@ protected void convertToMap(Object target, Object map, HashMap<Long, Object> cir | |
| Object refValue = circulars.get(pointer); | ||
| if (map instanceof Map) { | ||
| ((Map<String, Object>) map).put(key, refValue); | ||
| } else if (map instanceof ArrayList){ | ||
| } else if (map instanceof ArrayList) { | ||
| ((ArrayList<Object>) map).add(refValue); | ||
| } | ||
| continue; | ||
|
|
@@ -332,7 +337,7 @@ protected void convertToMap(Object target, Object map, HashMap<Long, Object> cir | |
| convertToMap(value, list, circulars, filter, extra, mapCreator); | ||
| if (map instanceof Map) { | ||
| ((Map<String, Object>) map).put(key, list); | ||
| } else if (map instanceof ArrayList){ | ||
| } else if (map instanceof ArrayList) { | ||
| ((ArrayList<Object>) map).add(list); | ||
| } | ||
| ((JSArray) value).release(); | ||
|
|
@@ -344,7 +349,7 @@ protected void convertToMap(Object target, Object map, HashMap<Long, Object> cir | |
| convertToMap(value, valueMap, circulars, filter, extra, mapCreator); | ||
| if (map instanceof Map) { | ||
| ((Map<String, Object>) map).put(key, valueMap); | ||
| } else if (map instanceof ArrayList){ | ||
| } else if (map instanceof ArrayList) { | ||
| ((ArrayList<Object>) map).add(valueMap); | ||
| } | ||
| ((JSObject) value).release(); | ||
|
|
@@ -354,7 +359,7 @@ protected void convertToMap(Object target, Object map, HashMap<Long, Object> cir | |
| // Primitive types. | ||
| if (map instanceof Map) { | ||
| ((Map<String, Object>) map).put(key, value); | ||
| } else if (map instanceof ArrayList){ | ||
| } else if (map instanceof ArrayList) { | ||
| ((ArrayList<Object>) map).add(value); | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): 允许对带有 @JSmethod 的方法进行混淆,可能会破坏依赖默认方法名的 JS API。
当前绑定逻辑会使用注解中的 value,如果 value 为空则回退到 method.getName()。如果这些方法被混淆,那么任何依赖默认(空)注解值的 JS API 在发布构建中就会暴露为混淆后的方法名。仅当所有暴露给 JS 的方法都显式指定了 @JSmethod("...") 的值时,这才是安全的。如果无法保证这一点,建议对这些方法去掉 allowobfuscation,或者拆分规则,只对注解值为空的 @JSmethod 方法保持不混淆。
Original comment in English
issue (bug_risk): Allowing obfuscation of @JSmethod methods can break JS APIs that rely on default method names.
Because the binding uses the annotation value or falls back to method.getName() when the value is blank, obfuscating these methods means any JS API that relies on the default (blank) annotation will now be exposed under an obfuscated name in release builds. This is only safe if all JS-exposed methods use an explicit @JSmethod("...") value. If that isn’t guaranteed, consider removing allowobfuscation for these methods or splitting the rule so that only methods with blank annotation values remain unobfuscated.