mirror of https://github.com/FongMi/TV.git
parent
8093518ea0
commit
f0d63fb2ab
@ -0,0 +1,49 @@ |
||||
package com.fongmi.quickjs.method; |
||||
|
||||
import com.whl.quickjs.wrapper.JSCallFunction; |
||||
import com.whl.quickjs.wrapper.JSFunction; |
||||
import com.whl.quickjs.wrapper.JSObject; |
||||
|
||||
import java9.util.concurrent.CompletableFuture; |
||||
|
||||
public class Async { |
||||
|
||||
private final CompletableFuture<Object> future; |
||||
|
||||
public static CompletableFuture<Object> run(JSObject object, String name, Object[] args) { |
||||
return new Async().call(object, name, args); |
||||
} |
||||
|
||||
private Async() { |
||||
this.future = new CompletableFuture<>(); |
||||
} |
||||
|
||||
private CompletableFuture<Object> call(JSObject object, String name, Object[] args) { |
||||
JSFunction function = object.getJSFunction(name); |
||||
if (function == null) return empty(); |
||||
Object result = function.call(args); |
||||
if (result instanceof JSObject) return then(result); |
||||
future.complete(result); |
||||
return future; |
||||
} |
||||
|
||||
private CompletableFuture<Object> empty() { |
||||
future.complete(null); |
||||
return future; |
||||
} |
||||
|
||||
private CompletableFuture<Object> then(Object result) { |
||||
JSObject promise = (JSObject) result; |
||||
JSFunction then = promise.getJSFunction("then"); |
||||
if (then != null) then.call(func); |
||||
return future; |
||||
} |
||||
|
||||
private final JSCallFunction func = new JSCallFunction() { |
||||
@Override |
||||
public Object call(Object... args) { |
||||
future.complete(args[0]); |
||||
return null; |
||||
} |
||||
}; |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
package com.whl.quickjs.android; |
||||
|
||||
/** |
||||
* Created by Harlon Wang on 2022/8/12. |
||||
*/ |
||||
public final class QuickJSLoader { |
||||
|
||||
public static void init() { |
||||
System.loadLibrary("quickjs-android-wrapper"); |
||||
} |
||||
|
||||
/** |
||||
* Start threads to show stdout and stderr in logcat. |
||||
* |
||||
* @param tag Android Tag |
||||
*/ |
||||
public native static void startRedirectingStdoutStderr(String tag); |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
public class JSArray extends JSObject { |
||||
|
||||
public JSArray(QuickJSContext context, long pointer) { |
||||
super(context, pointer); |
||||
} |
||||
|
||||
public int length() { |
||||
checkReleased(); |
||||
return getContext().length(this); |
||||
} |
||||
|
||||
public Object get(int index) { |
||||
checkReleased(); |
||||
return getContext().get(this, index); |
||||
} |
||||
|
||||
public void set(Object value, int index) { |
||||
checkReleased(); |
||||
getContext().set(this, value, index); |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
public interface JSCallFunction { |
||||
|
||||
Object call(Object... args); |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
public class JSFunction extends JSObject { |
||||
|
||||
private final long objPointer; |
||||
|
||||
public JSFunction(QuickJSContext context, long objPointer, long pointer) { |
||||
super(context, pointer); |
||||
this.objPointer = objPointer; |
||||
} |
||||
|
||||
public Object call(Object... args) { |
||||
return getContext().call(this, objPointer, args); |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
@Retention(value = RetentionPolicy.RUNTIME) |
||||
@Target(value = {ElementType.METHOD}) |
||||
public @interface JSMethod { |
||||
} |
||||
@ -0,0 +1,203 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
import androidx.annotation.NonNull; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
import java.util.Arrays; |
||||
|
||||
public class JSObject { |
||||
|
||||
private final QuickJSContext context; |
||||
private final long pointer; |
||||
|
||||
private boolean isReleased; |
||||
|
||||
public JSObject(QuickJSContext context, long pointer) { |
||||
this.context = context; |
||||
this.pointer = pointer; |
||||
} |
||||
|
||||
public long getPointer() { |
||||
return pointer; |
||||
} |
||||
|
||||
public QuickJSContext getContext() { |
||||
return context; |
||||
} |
||||
|
||||
public Object getProperty(String name) { |
||||
checkReleased(); |
||||
return context.getProperty(this, name); |
||||
} |
||||
|
||||
public void setProperty(String name, String value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
public void setProperty(String name, int value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
public void setProperty(String name, long value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
public void setProperty(String name, JSObject value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
public void setProperty(String name, boolean value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
public void setProperty(String name, double value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
public void setProperty(String name, JSCallFunction value) { |
||||
context.setProperty(this, name, value); |
||||
} |
||||
|
||||
/** |
||||
* Class 添加 {@link JSMethod} 的方法会被注入到 JSContext 中 |
||||
* 注意:该方法暂不支持匿名内部类的注册,因为匿名内部类构造参数不是无参的,newInstance 时会报错 |
||||
* |
||||
* @param name |
||||
* @param clazz |
||||
*/ |
||||
public void setProperty(String name, Class<?> clazz) { |
||||
Object javaObj = null; |
||||
try { |
||||
javaObj = clazz.newInstance(); |
||||
} catch (IllegalAccessException 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(); |
||||
Method[] methods = clazz.getMethods(); |
||||
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; |
||||
}); |
||||
} |
||||
} |
||||
|
||||
setProperty(name, jsObj); |
||||
} |
||||
|
||||
public String getString(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof String ? (String) value : null; |
||||
} |
||||
|
||||
public Integer getInteger(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof Integer ? (Integer) value : null; |
||||
} |
||||
|
||||
public Boolean getBoolean(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof Boolean ? (Boolean) value : null; |
||||
} |
||||
|
||||
public Double getDouble(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof Double ? (Double) value : null; |
||||
} |
||||
|
||||
public Long getLong(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof Long ? (Long) value : null; |
||||
} |
||||
|
||||
public JSObject getJSObject(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof JSObject ? (JSObject) value : null; |
||||
} |
||||
|
||||
public JSFunction getJSFunction(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof JSFunction ? (JSFunction) value : null; |
||||
} |
||||
|
||||
public JSArray getJSArray(String name) { |
||||
Object value = getProperty(name); |
||||
return value instanceof JSArray ? (JSArray) value : null; |
||||
} |
||||
|
||||
public JSArray getNames() { |
||||
JSFunction getOwnPropertyNames = (JSFunction) context.evaluate("Object.getOwnPropertyNames"); |
||||
return (JSArray) getOwnPropertyNames.call(this); |
||||
} |
||||
|
||||
/** |
||||
* JSObject 确定不再使用后,调用该方法可主动释放对 JS 对象的引用。 |
||||
* 注意:该方法不能调用多次以及释放后不能再被使用对应的 JS 对象。 |
||||
*/ |
||||
public void release() { |
||||
checkReleased(); |
||||
context.freeValue(this); |
||||
isReleased = true; |
||||
} |
||||
|
||||
public void hold() { |
||||
context.hold(this); |
||||
} |
||||
|
||||
/** |
||||
* 这里与 JavaScript 的 toString 方法保持一致 |
||||
* 返回结果参考:https://262.ecma-international.org/14.0/#sec-tostring
|
||||
* |
||||
* @return toString in JavaScript. |
||||
*/ |
||||
@NonNull |
||||
@Override |
||||
public String toString() { |
||||
checkReleased(); |
||||
JSFunction toString = getJSFunction("toString"); |
||||
return (String) toString.call(); |
||||
} |
||||
|
||||
public String stringify() { |
||||
return context.stringify(this); |
||||
} |
||||
|
||||
final void checkReleased() { |
||||
if (isReleased) { |
||||
throw new NullPointerException("This JSObject was Released, Can not call this!"); |
||||
} |
||||
} |
||||
|
||||
public boolean isAlive() { |
||||
return context.isLiveObject(this); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) return true; |
||||
if (o == null || getClass() != o.getClass()) return false; |
||||
JSObject jsObject = (JSObject) o; |
||||
return pointer == jsObject.pointer; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Arrays.hashCode(new long[]{pointer}); |
||||
} |
||||
} |
||||
@ -0,0 +1,46 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
/** |
||||
* Created by Harlon Wang on 2023/8/26. |
||||
* 该类仅提供给 Native 层调用 |
||||
*/ |
||||
public abstract class ModuleLoader { |
||||
/** |
||||
* 模块加载模式: |
||||
* True 会调用 {@link #getModuleBytecode(String)} |
||||
* False 会调用 {@link #getModuleStringCode(String)} |
||||
* |
||||
* @return 是否字节码模式 |
||||
*/ |
||||
public abstract boolean isBytecodeMode(); |
||||
|
||||
/** |
||||
* 获取字节码代码内容 |
||||
* |
||||
* @param moduleName 模块路径名,例如 "xxx.js" |
||||
* @return 代码内容 |
||||
*/ |
||||
public abstract byte[] getModuleBytecode(String moduleName); |
||||
|
||||
/** |
||||
* 获取字符串代码内容 |
||||
* |
||||
* @param moduleName 模块路径名,例如 "xxx.js" |
||||
* @return 代码内容 |
||||
*/ |
||||
public abstract String getModuleStringCode(String moduleName); |
||||
|
||||
|
||||
/** |
||||
* 该方法返回结果会作为 moduleName 参数给到 {@link #getModuleBytecode(String)} |
||||
* 或者 {@link #getModuleStringCode(String)} 中使用,默认返回 moduleName。 |
||||
* 一般可以在这里对模块名称进行转换处理。 |
||||
* |
||||
* @param baseModuleName 使用 Import 的所在模块名称 |
||||
* @param moduleName 需要加载的模块名称 |
||||
* @return 模块名称 |
||||
*/ |
||||
public String moduleNormalizeName(String baseModuleName, String moduleName) { |
||||
return moduleName; |
||||
} |
||||
} |
||||
@ -0,0 +1,76 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
import java.lang.ref.PhantomReference; |
||||
import java.lang.ref.ReferenceQueue; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* https://youtu.be/7_caITSjk1k
|
||||
*/ |
||||
abstract class NativeCleaner<T> { |
||||
|
||||
private Set<NativeReference<T>> phantomReferences = new HashSet<>(); |
||||
private ReferenceQueue<T> referenceQueue = new ReferenceQueue<>(); |
||||
|
||||
/** |
||||
* Returns the size of not removed objects. |
||||
*/ |
||||
public int size() { |
||||
return phantomReferences.size(); |
||||
} |
||||
|
||||
/** |
||||
* Registers the object and the native pointer to this cleaner. |
||||
* |
||||
* @param referent the object |
||||
* @param pointer the native pointer |
||||
*/ |
||||
public void register(T referent, long pointer) { |
||||
phantomReferences.add(new NativeReference<>(referent, pointer, referenceQueue)); |
||||
} |
||||
|
||||
/** |
||||
* Releases the native resources associated with the native pointer. |
||||
* It's called in {@link #clean()} on objects recycled by GC, |
||||
* or in {@link #forceClean()} on all objects. |
||||
* It's only called once on each object. |
||||
* |
||||
* @param pointer the native pointer |
||||
*/ |
||||
public abstract void onRemove(long pointer); |
||||
|
||||
/** |
||||
* Calls {@link #onRemove(long)} on objects recycled by GC. |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public void clean() { |
||||
NativeReference<T> ref; |
||||
while ((ref = (NativeReference<T>) referenceQueue.poll()) != null) { |
||||
if (phantomReferences.contains(ref)) { |
||||
onRemove(ref.pointer); |
||||
phantomReferences.remove(ref); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Calls {@link #onRemove(long)} on all objects. |
||||
*/ |
||||
public void forceClean() { |
||||
for (NativeReference<T> ref : phantomReferences) { |
||||
onRemove(ref.pointer); |
||||
} |
||||
phantomReferences.clear(); |
||||
} |
||||
|
||||
private static class NativeReference<T> extends PhantomReference<T> { |
||||
|
||||
private long pointer; |
||||
|
||||
private NativeReference(T referent, long pointer, ReferenceQueue<? super T> q) { |
||||
super(referent, q); |
||||
this.pointer = pointer; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,365 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
import java.io.File; |
||||
import java.util.HashMap; |
||||
|
||||
public class QuickJSContext { |
||||
|
||||
public static abstract class DefaultModuleLoader extends ModuleLoader { |
||||
|
||||
@Override |
||||
public boolean isBytecodeMode() { |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public byte[] getModuleBytecode(String moduleName) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public static abstract class BytecodeModuleLoader extends ModuleLoader { |
||||
|
||||
@Override |
||||
public boolean isBytecodeMode() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public String getModuleStringCode(String moduleName) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
private static final String UNKNOWN_FILE = "unknown.js"; |
||||
|
||||
public static QuickJSContext create() { |
||||
return new QuickJSContext(); |
||||
} |
||||
|
||||
public boolean isLiveObject(JSObject jsObj) { |
||||
return isLiveObject(runtime, jsObj.getPointer()); |
||||
} |
||||
|
||||
public void setMaxStackSize(int maxStackSize) { |
||||
setMaxStackSize(runtime, maxStackSize); |
||||
} |
||||
|
||||
public void runGC() { |
||||
runGC(runtime); |
||||
} |
||||
|
||||
public void setMemoryLimit(int memoryLimitSize) { |
||||
setMemoryLimit(runtime, memoryLimitSize); |
||||
} |
||||
|
||||
public void dumpMemoryUsage(File target) { |
||||
if (target == null || !target.exists()) return; |
||||
dumpMemoryUsage(runtime, target.getAbsolutePath()); |
||||
} |
||||
|
||||
// will use stdout to print.
|
||||
public void dumpMemoryUsage() { |
||||
dumpMemoryUsage(runtime, null); |
||||
} |
||||
|
||||
public void dumpObjects(File target) { |
||||
if (target == null || !target.exists()) return; |
||||
dumpObjects(runtime, target.getAbsolutePath()); |
||||
} |
||||
|
||||
// will use stdout to print.
|
||||
public void dumpObjects() { |
||||
dumpObjects(runtime, null); |
||||
} |
||||
|
||||
private final long runtime; |
||||
private final long context; |
||||
private final NativeCleaner<JSObject> nativeCleaner = new NativeCleaner<JSObject>() { |
||||
@Override |
||||
public void onRemove(long pointer) { |
||||
freeDupValue(context, pointer); |
||||
} |
||||
}; |
||||
private final long currentThreadId; |
||||
private boolean destroyed = false; |
||||
private final HashMap<Integer, JSCallFunction> callFunctionMap = new HashMap<>(); |
||||
|
||||
private ModuleLoader moduleLoader; |
||||
|
||||
private QuickJSContext() { |
||||
try { |
||||
runtime = createRuntime(); |
||||
context = createContext(runtime); |
||||
} catch (UnsatisfiedLinkError e) { |
||||
throw new QuickJSException("The so library must be initialized before createContext! QuickJSLoader.init should be called on the Android platform. In the JVM, you need to manually call System.loadLibrary"); |
||||
} |
||||
currentThreadId = Thread.currentThread().getId(); |
||||
} |
||||
|
||||
private void checkSameThread() { |
||||
boolean isSameThread = currentThreadId == Thread.currentThread().getId(); |
||||
if (!isSameThread) { |
||||
throw new QuickJSException("Must be call same thread in QuickJSContext.create!"); |
||||
} |
||||
} |
||||
|
||||
public long getCurrentThreadId() { |
||||
return currentThreadId; |
||||
} |
||||
|
||||
public void setModuleLoader(ModuleLoader moduleLoader) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
this.moduleLoader = moduleLoader; |
||||
} |
||||
|
||||
public ModuleLoader getModuleLoader() { |
||||
return moduleLoader; |
||||
} |
||||
|
||||
private void checkDestroyed() { |
||||
if (destroyed) { |
||||
throw new QuickJSException("Can not called this after QuickJSContext was destroyed!"); |
||||
} |
||||
} |
||||
|
||||
public JSObject getGlobalObject() { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return getGlobalObject(context); |
||||
} |
||||
|
||||
public void destroy() { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
nativeCleaner.forceClean(); |
||||
callFunctionMap.clear(); |
||||
destroyContext(context); |
||||
destroyed = true; |
||||
} |
||||
|
||||
public String stringify(JSObject jsObj) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return stringify(context, jsObj.getPointer()); |
||||
} |
||||
|
||||
public Object getProperty(JSObject jsObj, String name) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return getProperty(context, jsObj.getPointer(), name); |
||||
} |
||||
|
||||
public void setProperty(JSObject jsObj, String name, Object value) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
if (value instanceof JSCallFunction) putCallFunction((JSCallFunction) value); |
||||
setProperty(context, jsObj.getPointer(), name, value); |
||||
} |
||||
|
||||
private void putCallFunction(JSCallFunction callFunction) { |
||||
int callFunctionId = callFunction.hashCode(); |
||||
callFunctionMap.put(callFunctionId, (JSCallFunction) callFunction); |
||||
} |
||||
|
||||
/** |
||||
* 该方法只提供给 Native 层回调. |
||||
* |
||||
* @param callFunctionId JSCallFunction 对象标识 |
||||
*/ |
||||
public void removeCallFunction(int callFunctionId) { |
||||
callFunctionMap.remove(callFunctionId); |
||||
} |
||||
|
||||
/** |
||||
* 该方法只提供给 Native 层回调. |
||||
* |
||||
* @param callFunctionId JSCallFunction 对象标识 |
||||
* @param args JS 到 Java 的参数映射 |
||||
*/ |
||||
public Object callFunctionBack(int callFunctionId, Object... args) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
JSCallFunction callFunction = callFunctionMap.get(callFunctionId); |
||||
Object ret = callFunction.call(args); |
||||
if (ret instanceof JSCallFunction) putCallFunction((JSCallFunction) ret); |
||||
return ret; |
||||
} |
||||
|
||||
public void freeValue(JSObject jsObj) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
freeValue(context, jsObj.getPointer()); |
||||
} |
||||
|
||||
/** |
||||
* @VisibleForTesting 该方法仅供单元测试使用 |
||||
*/ |
||||
int getCallFunctionMapSize() { |
||||
return callFunctionMap.size(); |
||||
} |
||||
|
||||
/** |
||||
* Native 层注册的 JS 方法里的对象需要在其他地方使用, |
||||
* 调用该方法进行计数加一增加引用,不然 JS 方法执行完会被回收掉。 |
||||
* 注意:不再使用的时候,调用对应的 {@link #freeDupValue(JSObject)} 方法进行计数减一。 |
||||
*/ |
||||
private void dupValue(JSObject jsObj) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
dupValue(context, jsObj.getPointer()); |
||||
} |
||||
|
||||
/** |
||||
* 引用计数减一,对应 {@link #dupValue(JSObject)} |
||||
*/ |
||||
private void freeDupValue(JSObject jsObj) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
freeDupValue(context, jsObj.getPointer()); |
||||
} |
||||
|
||||
public int length(JSArray jsArray) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return length(context, jsArray.getPointer()); |
||||
} |
||||
|
||||
public Object get(JSArray jsArray, int index) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return get(context, jsArray.getPointer(), index); |
||||
} |
||||
|
||||
public void set(JSArray jsArray, Object value, int index) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
set(context, jsArray.getPointer(), value, index); |
||||
} |
||||
|
||||
Object call(JSObject func, long objPointer, Object... args) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
for (Object arg : args) if (arg instanceof JSCallFunction) putCallFunction((JSCallFunction) arg); |
||||
return call(context, func.getPointer(), objPointer, args); |
||||
} |
||||
|
||||
/** |
||||
* Automatically manage the release of objects, |
||||
* the hold method is equivalent to call the |
||||
* dupValue and freeDupValue methods with NativeCleaner. |
||||
*/ |
||||
public void hold(JSObject jsObj) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
dupValue(jsObj); |
||||
nativeCleaner.register(jsObj, jsObj.getPointer()); |
||||
} |
||||
|
||||
public JSObject createNewJSObject() { |
||||
return (JSObject) parse("{}"); |
||||
} |
||||
|
||||
public JSArray createNewJSArray() { |
||||
return (JSArray) parse("[]"); |
||||
} |
||||
|
||||
public Object parse(String json) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return parseJSON(context, json); |
||||
} |
||||
|
||||
public byte[] compile(String source) { |
||||
return compile(source, UNKNOWN_FILE); |
||||
} |
||||
|
||||
public byte[] compile(String source, String fileName) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return compile(context, source, fileName, false); |
||||
} |
||||
|
||||
public byte[] compileModule(String source) { |
||||
return compileModule(source, UNKNOWN_FILE); |
||||
} |
||||
|
||||
public byte[] compileModule(String source, String fileName) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return compile(context, source, fileName, true); |
||||
} |
||||
|
||||
public Object execute(byte[] code) { |
||||
return execute(code, UNKNOWN_FILE); |
||||
} |
||||
|
||||
public Object execute(byte[] code, String fileName) { |
||||
return execute(code, fileName, "default"); |
||||
} |
||||
|
||||
public Object execute(byte[] code, String fileName, String extName) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return execute(context, code, fileName, extName); |
||||
} |
||||
|
||||
public Object evaluate(String script) { |
||||
return evaluate(script, UNKNOWN_FILE); |
||||
} |
||||
|
||||
public Object evaluate(String script, String fileName) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return evaluate(context, script, fileName, "default", false); |
||||
} |
||||
|
||||
public Object evaluateModule(String script) { |
||||
return evaluateModule(script, UNKNOWN_FILE); |
||||
} |
||||
|
||||
public Object evaluateModule(String script, String moduleName) { |
||||
return evaluateModule(script, moduleName, "default"); |
||||
} |
||||
|
||||
public Object evaluateModule(String script, String moduleName, String extName) { |
||||
checkSameThread(); |
||||
checkDestroyed(); |
||||
return evaluate(context, script, moduleName, extName, true); |
||||
} |
||||
|
||||
public void throwJSException(String error) { |
||||
// throw $error;
|
||||
String errorScript = "throw " + "\"" + error + "\"" + ";"; |
||||
evaluate(errorScript); |
||||
} |
||||
|
||||
// runtime
|
||||
private native long createRuntime(); |
||||
private native void setMaxStackSize(long runtime, int size); // The default is 1024 * 256, and 0 means unlimited.
|
||||
private native boolean isLiveObject(long runtime, long objValue); |
||||
private native void runGC(long runtime); |
||||
private native void setMemoryLimit(long runtime, int size); |
||||
private native void dumpMemoryUsage(long runtime, String fileName); |
||||
private native void dumpObjects(long runtime, String fileName); |
||||
// context
|
||||
private native long createContext(long runtime); |
||||
private native Object evaluate(long context, String script, String fileName, String extName, boolean isModule); |
||||
private native JSObject getGlobalObject(long context); |
||||
private native Object call(long context, long func, long thisObj, Object[] args); |
||||
private native Object getProperty(long context, long objValue, String name); |
||||
private native void setProperty(long context, long objValue, String name, Object value); |
||||
private native String stringify(long context, long objValue); |
||||
private native int length(long context, long objValue); |
||||
private native Object get(long context, long objValue, int index); |
||||
private native void set(long context, long objValue, Object value, int index); |
||||
private native void freeValue(long context, long objValue); |
||||
private native void dupValue(long context, long objValue); |
||||
private native void freeDupValue(long context, long objValue); |
||||
private native Object parseJSON(long context, String json); |
||||
private native byte[] compile(long context, String sourceCode, String fileName, boolean isModule); // Bytecode compile
|
||||
private native Object execute(long context, byte[] bytecode, String fileName, String extName); |
||||
// destroy context and runtime
|
||||
private native void destroyContext(long context); |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
package com.whl.quickjs.wrapper; |
||||
|
||||
/** |
||||
* Created by Harlon Wang on 2022/2/8. |
||||
*/ |
||||
public class QuickJSException extends RuntimeException { |
||||
|
||||
private final boolean jsError; |
||||
|
||||
public QuickJSException(String message) { |
||||
this(message, false); |
||||
} |
||||
|
||||
public QuickJSException(String message, boolean jsError) { |
||||
super(message); |
||||
this.jsError = jsError; |
||||
} |
||||
|
||||
public boolean isJSError() { |
||||
return jsError; |
||||
} |
||||
} |
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue