Open quickjs source

pull/149/head
FongMi 2 years ago
parent 8093518ea0
commit f0d63fb2ab
  1. 2
      quickjs/build.gradle
  2. 37
      quickjs/src/main/java/com/fongmi/quickjs/crawler/Spider.java
  3. 49
      quickjs/src/main/java/com/fongmi/quickjs/method/Async.java
  4. 4
      quickjs/src/main/java/com/fongmi/quickjs/utils/Crypto.java
  5. 18
      quickjs/src/main/java/com/whl/quickjs/android/QuickJSLoader.java
  6. 23
      quickjs/src/main/java/com/whl/quickjs/wrapper/JSArray.java
  7. 6
      quickjs/src/main/java/com/whl/quickjs/wrapper/JSCallFunction.java
  8. 15
      quickjs/src/main/java/com/whl/quickjs/wrapper/JSFunction.java
  9. 11
      quickjs/src/main/java/com/whl/quickjs/wrapper/JSMethod.java
  10. 203
      quickjs/src/main/java/com/whl/quickjs/wrapper/JSObject.java
  11. 46
      quickjs/src/main/java/com/whl/quickjs/wrapper/ModuleLoader.java
  12. 76
      quickjs/src/main/java/com/whl/quickjs/wrapper/NativeCleaner.java
  13. 365
      quickjs/src/main/java/com/whl/quickjs/wrapper/QuickJSContext.java
  14. 22
      quickjs/src/main/java/com/whl/quickjs/wrapper/QuickJSException.java
  15. BIN
      quickjs/src/main/jniLibs/arm64-v8a/libquickjs-android-wrapper.so
  16. BIN
      quickjs/src/main/jniLibs/armeabi-v7a/libquickjs-android-wrapper.so

@ -13,5 +13,5 @@ android {
dependencies {
implementation project(':catvod')
implementation 'wang.harlon.quickjs:wrapper-android:1.0.0-beta'
implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.4'
}

@ -5,8 +5,8 @@ import android.content.Context;
import androidx.media3.common.util.UriUtil;
import com.fongmi.quickjs.bean.Res;
import com.fongmi.quickjs.method.Async;
import com.fongmi.quickjs.method.Console;
import com.fongmi.quickjs.method.Function;
import com.fongmi.quickjs.method.Global;
import com.fongmi.quickjs.method.Local;
import com.fongmi.quickjs.utils.JSUtil;
@ -32,6 +32,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import dalvik.system.DexClassLoader;
import java9.util.concurrent.CompletableFuture;
public class Spider extends com.github.catvod.crawler.Spider {
@ -60,12 +61,13 @@ public class Spider extends com.github.catvod.crawler.Spider {
}
private Object call(String func, Object... args) throws Exception {
return executor.submit((Function.call(jsObject, func, args))).get();
return CompletableFuture.supplyAsync(() -> Async.run(jsObject, func, args), executor).join().get();
}
@Override
public void init(Context context, String extend) throws Exception {
call("init", Json.valid(extend) ? ctx.parse(extend) : extend);
if (cat) call("init", submit(() -> cfg(extend)).get());
else call("init", Json.valid(extend) ? ctx.parse(extend) : extend);
}
@Override
@ -152,8 +154,8 @@ public class Spider extends com.github.catvod.crawler.Spider {
@Override
public byte[] getModuleBytecode(String moduleName) {
String code = Module.get().fetch(moduleName);
return code.startsWith("//bb") ? Module.get().bb(code) : ctx.compileModule(code, moduleName);
String content = Module.get().fetch(moduleName);
return content.startsWith("//bb") ? Module.get().bb(content) : ctx.compileModule(content, moduleName);
}
});
}
@ -202,19 +204,26 @@ public class Spider extends com.github.catvod.crawler.Spider {
}
private void createObj() {
String jsEval = "__jsEvalReturn";
String spider = "__JS_SPIDER__";
String global = "globalThis." + spider;
String content = Module.get().fetch(api);
String catOnly = "\n" + global + " = __jsEvalReturn();";
if (content.contains("__jsEvalReturn")) {
cat = true;
ctx.evaluateModule(content.concat(catOnly), api);
} else if (content.contains(spider)) {
ctx.evaluateModule(content.replace(spider, global), api);
} else {
ctx.evaluateModule(content.replaceAll("export default.*?[{]", global + " = {"), api);
}
if (content.startsWith("//bb") || content.contains(jsEval)) cat = true;
if (content.startsWith("//bb")) ctx.execute(Module.get().bb(content), spider, jsEval);
else if (content.contains(jsEval)) ctx.evaluateModule(content, api, jsEval);
else if (content.contains(spider)) ctx.evaluateModule(content.replace(spider, global), api);
else ctx.evaluateModule(content.replaceAll("export default.*?[{]", global + " = {"), api);
jsObject = (JSObject) ctx.getProperty(ctx.getGlobalObject(), spider);
if (cat) ctx.evaluate("req = http");
}
private JSObject cfg(String ext) {
JSObject cfg = ctx.createNewJSObject();
cfg.setProperty("stype", 3);
cfg.setProperty("skey", key);
if (Json.invalid(ext)) cfg.setProperty("ext", ext);
else cfg.setProperty("ext", (JSObject) ctx.parse(ext));
return cfg;
}
private Object[] proxy1(Map<String, String> params) throws Exception {

@ -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;
}
};
}

@ -28,7 +28,7 @@ public class Crypto {
if (iv == null) cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, keySpec);
else cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivBuf));
byte[] inBuf = inBase64 ? Base64.decode(input.replaceAll("_", "/").replaceAll("-", "+"), Base64.DEFAULT) : input.getBytes("UTF-8");
return outBase64 ? Base64.encodeToString(cipher.doFinal(inBuf), Base64.DEFAULT) : new String(cipher.doFinal(inBuf), "UTF-8");
return outBase64 ? Base64.encodeToString(cipher.doFinal(inBuf), Base64.NO_WRAP) : new String(cipher.doFinal(inBuf), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return "";
@ -53,7 +53,7 @@ public class Crypto {
bufIdx = bufEndIdx;
outBytes = concatArrays(outBytes, tmpBytes);
}
return outBase64 ? Base64.encodeToString(outBytes, Base64.DEFAULT) : new String(outBytes, "UTF-8");
return outBase64 ? Base64.encodeToString(outBytes, Base64.NO_WRAP) : new String(outBytes, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return "";

@ -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;
}
}
Loading…
Cancel
Save