增加 可选特性continuation
增加 协程示例
This commit is contained in:
@@ -16,16 +16,7 @@ runtime.init();
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
//初始化不依赖环境的模块
|
||||
global.JSON = require('__json2__.js');
|
||||
global.util = require('__util__.js');
|
||||
global.device = runtime.device;
|
||||
|
||||
//设置JavaScriptBridges用于与Java层的交互和数据转换
|
||||
runtime.bridges.setBridges(require('__bridges__.js'));
|
||||
|
||||
//一些内部函数
|
||||
//内部函数
|
||||
global.__asGlobal__ = function (obj, functions) {
|
||||
var len = functions.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
@@ -50,7 +41,6 @@ runtime.init();
|
||||
exit(err);
|
||||
} else if (err instanceof Error) {
|
||||
exit(new org.mozilla.javascript.EvaluatorException(err.name + ": " + err.message, err.fileName, err.lineNumber));
|
||||
//new java.lang.RuntimeException(err.name + ": " + err.message + "\n" + err.stack));
|
||||
} else {
|
||||
exit();
|
||||
}
|
||||
@@ -58,12 +48,24 @@ runtime.init();
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化基础模块
|
||||
global.timers = require('__timers__.js')(runtime, global);
|
||||
|
||||
//初始化不依赖环境的模块
|
||||
global.JSON = require('__json2__.js');
|
||||
global.util = require('__util__.js');
|
||||
global.device = runtime.device;
|
||||
global.Promise = require('promise.js');
|
||||
|
||||
//设置JavaScriptBridges用于与Java层的交互和数据转换
|
||||
runtime.bridges.setBridges(require('__bridges__.js'));
|
||||
|
||||
//初始化全局函数
|
||||
require("__globals__")(runtime, global);
|
||||
//初始化一般模块
|
||||
(function (scope) {
|
||||
var modules = ['app', 'automator', 'console', 'dialogs', 'io', 'selector', 'shell', 'web', 'ui',
|
||||
"images", "timers", "threads", "events", "engines", "RootAutomator", "http", "storages", "floaty",
|
||||
"images", "threads", "events", "engines", "RootAutomator", "http", "storages", "floaty",
|
||||
"sensors", "media", "plugins", "continuation"];
|
||||
var len = modules.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
@@ -72,11 +74,6 @@ runtime.init();
|
||||
}
|
||||
})(global);
|
||||
|
||||
global.Promise = require('promise.js');
|
||||
global.Promise.prototype.await = function () {
|
||||
return continuation.await(this);
|
||||
}
|
||||
|
||||
importClass(android.view.KeyEvent);
|
||||
importClass(com.stardust.autojs.core.util.Shell);
|
||||
importClass(android.graphics.Paint);
|
||||
|
||||
@@ -44,5 +44,21 @@ module.exports = function (runtime, global) {
|
||||
throw new TypeError('cannot await ' + any);
|
||||
}
|
||||
|
||||
continuation.delay = function (millis) {
|
||||
var cont = continuation.create();
|
||||
setTimeout(()=>{
|
||||
cont.resume();
|
||||
}, millis);
|
||||
cont.await();
|
||||
}
|
||||
|
||||
continuation.__defineGetter__('enabled', function () {
|
||||
return engines.myEngine().hasFeature("continuation");
|
||||
});
|
||||
|
||||
global.Promise.prototype.await = function () {
|
||||
return continuation.await(this);
|
||||
}
|
||||
|
||||
return continuation;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ module.exports = function (runtime, scope) {
|
||||
|
||||
http.request = function (url, options, callback) {
|
||||
var cont = null;
|
||||
if (!callback && ui.isUiThread()) {
|
||||
if (!callback && ui.isUiThread() && continuation.enabled) {
|
||||
cont = continuation.create();
|
||||
}
|
||||
var call = http.client().newCall(http.buildRequest(url, options));
|
||||
@@ -59,7 +59,7 @@ module.exports = function (runtime, scope) {
|
||||
callback && callback(null, ex);
|
||||
}
|
||||
}));
|
||||
if(cont) {
|
||||
if (cont) {
|
||||
return cont.await();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ module.exports = function (runtime, scope) {
|
||||
if(landscape === false){
|
||||
orientation = ScreenCapturer.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
return ResultAdapter.promise(javaImages.requestScreenCapture(orientation)).await();
|
||||
return ResultAdapter.wait(javaImages.requestScreenCapture(orientation));
|
||||
}
|
||||
|
||||
images.save = function (img, path, format, quality) {
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
|
||||
module.exports = function(__runtime__, scope){
|
||||
var threads = Object.create(__runtime__.threads);
|
||||
module.exports = function (__runtime__, scope) {
|
||||
var threads = Object.create(__runtime__.threads);
|
||||
|
||||
|
||||
scope.sync = function(func, lock){
|
||||
scope.sync = function (func, lock) {
|
||||
lock = lock || null;
|
||||
return new org.mozilla.javascript.Synchronizer(func, lock);
|
||||
}
|
||||
}
|
||||
|
||||
return threads;
|
||||
global.Promise.prototype.wait = function () {
|
||||
var disposable = threads.disposable();
|
||||
promise.then(result => {
|
||||
disposable.setAndNotify({ result: result });
|
||||
}).catch(error => {
|
||||
cont.resumeError({ error: error });
|
||||
});
|
||||
var r = cont.blockedGet();
|
||||
if (r.error) {
|
||||
throw r.error;
|
||||
}
|
||||
return r.result;
|
||||
}
|
||||
|
||||
return threads;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ ResultAdapter.prototype.setError = function (error) {
|
||||
ResultAdapter.prototype.callback = function () {
|
||||
var that = this;
|
||||
return function (result, error) {
|
||||
if(that.result !== undefined){
|
||||
if (that.result !== undefined) {
|
||||
that.result = {
|
||||
result: result,
|
||||
error: error
|
||||
@@ -63,21 +63,33 @@ ResultAdapter.prototype.callback = function () {
|
||||
}
|
||||
|
||||
ResultAdapter.prototype.get = function () {
|
||||
if(this.result){
|
||||
if (this.result) {
|
||||
return getOrThrow(this.result);
|
||||
}
|
||||
this.result = null;
|
||||
return this.impl.get();
|
||||
}
|
||||
|
||||
ResultAdapter.promise = function(promiseAdapter) {
|
||||
return new Promise(function(resolve, reject){
|
||||
promiseAdapter.onResolve(function(result) {
|
||||
ResultAdapter.promise = function (promiseAdapter) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
promiseAdapter.onResolve(function (result) {
|
||||
resolve(result);
|
||||
}).onReject(function(error){
|
||||
}).onReject(function (error) {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
ResultAdapter.wait = function (promise) {
|
||||
var proto = Object.getPrototypeOf(promise);
|
||||
if (!proto || proto.constructor !== Promise) {
|
||||
promise = ResultAdapter.promise(promise);
|
||||
}
|
||||
if (continuation.enabled) {
|
||||
return promise.await();
|
||||
} else {
|
||||
return promise.wait();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResultAdapter;
|
||||
@@ -63,7 +63,6 @@ public class BlockedMaterialDialog extends MaterialDialog {
|
||||
private VolatileDispose<Object> mResultBox;
|
||||
private UiHandler mUiHandler;
|
||||
private Object mCallback;
|
||||
private Continuation mContinuation;
|
||||
private ScriptBridges mScriptBridges;
|
||||
private boolean mNotified = false;
|
||||
|
||||
@@ -75,10 +74,6 @@ public class BlockedMaterialDialog extends MaterialDialog {
|
||||
mCallback = callback;
|
||||
if (Looper.getMainLooper() != Looper.myLooper()) {
|
||||
mResultBox = new VolatileDispose<>();
|
||||
} else {
|
||||
if (mCallback == null) {
|
||||
mContinuation = runtime.createContinuation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,9 +91,6 @@ public class BlockedMaterialDialog extends MaterialDialog {
|
||||
if (mCallback != null) {
|
||||
mScriptBridges.callFunction(mCallback, null, new Object[]{r});
|
||||
}
|
||||
if (mContinuation != null) {
|
||||
mContinuation.resumeWith(Continuation.Result.Companion.success(r));
|
||||
}
|
||||
if (mResultBox != null) {
|
||||
mResultBox.setAndNotify(r);
|
||||
}
|
||||
@@ -179,9 +171,6 @@ public class BlockedMaterialDialog extends MaterialDialog {
|
||||
} else {
|
||||
mUiHandler.post(Builder.super::show);
|
||||
}
|
||||
if (mContinuation != null) {
|
||||
mContinuation.suspend();
|
||||
}
|
||||
if (mResultBox != null) {
|
||||
return mResultBox.blockedGetOrThrow(ScriptInterruptedException.class);
|
||||
} else {
|
||||
|
||||
@@ -46,8 +46,7 @@ public class LoopBasedJavaScriptEngine extends RhinoJavaScriptEngine {
|
||||
Object o = LoopBasedJavaScriptEngine.super.execute((JavaScriptSource) source);
|
||||
if (callback != null)
|
||||
callback.onResult(o);
|
||||
} catch (ContinuationPending pending) {
|
||||
pending.printStackTrace();
|
||||
} catch (ContinuationPending ignored) {
|
||||
} catch (Exception e) {
|
||||
if (callback == null) {
|
||||
throw e;
|
||||
@@ -61,8 +60,17 @@ public class LoopBasedJavaScriptEngine extends RhinoJavaScriptEngine {
|
||||
mHandler.post(r);
|
||||
if (!mLooping && Looper.myLooper() != Looper.getMainLooper()) {
|
||||
mLooping = true;
|
||||
Looper.loop();
|
||||
mLooping = false;
|
||||
while (true) {
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (ContinuationPending ignored) {
|
||||
continue;
|
||||
} catch (Throwable t) {
|
||||
mLooping = false;
|
||||
throw t;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +87,7 @@ public class LoopBasedJavaScriptEngine extends RhinoJavaScriptEngine {
|
||||
@Override
|
||||
public synchronized void destroy() {
|
||||
Thread thread = getThread();
|
||||
if (thread != null)
|
||||
LooperHelper.quitForThread(thread);
|
||||
LooperHelper.quitForThread(thread);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
package com.stardust.autojs.engine;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.stardust.autojs.BuildConfig;
|
||||
import com.stardust.autojs.core.ui.ViewExtras;
|
||||
import com.stardust.autojs.engine.module.AssetAndUrlModuleSourceProvider;
|
||||
import com.stardust.autojs.rhino.RhinoAndroidHelper;
|
||||
import com.stardust.autojs.rhino.TopLevelScope;
|
||||
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||
import com.stardust.autojs.script.JavaScriptSource;
|
||||
import com.stardust.automator.UiObjectCollection;
|
||||
import com.stardust.pio.UncheckedIOException;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Script;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import org.mozilla.javascript.commonjs.module.RequireBuilder;
|
||||
import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
*/
|
||||
|
||||
public class RhinoJavaScriptEngine extends JavaScriptEngine {
|
||||
|
||||
public static final String SOURCE_NAME_INIT = "<init>";
|
||||
|
||||
private static final String LOG_TAG = "RhinoJavaScriptEngine";
|
||||
|
||||
private static final String MODULES_PATH = "modules";
|
||||
private static Script sInitScript;
|
||||
private static final ConcurrentHashMap<Context, RhinoJavaScriptEngine> sContextEngineMap = new ConcurrentHashMap<>();
|
||||
|
||||
private Context mContext;
|
||||
private TopLevelScope mScriptable;
|
||||
private Thread mThread;
|
||||
private android.content.Context mAndroidContext;
|
||||
|
||||
public RhinoJavaScriptEngine(android.content.Context context) {
|
||||
mAndroidContext = context;
|
||||
mContext = enterContext();
|
||||
mScriptable = createScope(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, Object value) {
|
||||
ScriptableObject.putProperty(mScriptable, name, Context.javaToJS(value, mScriptable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRuntime(ScriptRuntime runtime) {
|
||||
super.setRuntime(runtime);
|
||||
runtime.setTopLevelScope(mScriptable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object doExecution(JavaScriptSource source) {
|
||||
Reader reader = source.getNonNullScriptReader();
|
||||
try {
|
||||
reader = preprocess(reader);
|
||||
Script script = mContext.compileReader(reader, source.toString(), 1, null);
|
||||
return mContext.executeScriptWithContinuations(script, mScriptable);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Reader preprocess(Reader script) throws IOException {
|
||||
return script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceStop() {
|
||||
Log.d(LOG_TAG, "forceStop: interrupt Thread: " + mThread);
|
||||
mThread.interrupt();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void destroy() {
|
||||
super.destroy();
|
||||
Log.d(LOG_TAG, "on destroy");
|
||||
sContextEngineMap.remove(getContext());
|
||||
Context.exit();
|
||||
}
|
||||
|
||||
public Thread getThread() {
|
||||
return mThread;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void init() {
|
||||
mThread = Thread.currentThread();
|
||||
ScriptableObject.putProperty(mScriptable, "__engine__", this);
|
||||
initRequireBuilder(mContext, mScriptable);
|
||||
mContext.executeScriptWithContinuations(getInitScript(), mScriptable);
|
||||
}
|
||||
|
||||
private Script getInitScript() {
|
||||
if (sInitScript == null || BuildConfig.DEBUG) {
|
||||
try {
|
||||
Reader reader = new InputStreamReader(mAndroidContext.getAssets().open("init.js"));
|
||||
sInitScript = mContext.compileReader(reader, SOURCE_NAME_INIT, 1, null);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
return sInitScript;
|
||||
}
|
||||
|
||||
void initRequireBuilder(Context context, Scriptable scope) {
|
||||
AssetAndUrlModuleSourceProvider provider = new AssetAndUrlModuleSourceProvider(mAndroidContext, MODULES_PATH,
|
||||
Collections.singletonList(new File("/").toURI()));
|
||||
new RequireBuilder()
|
||||
.setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider))
|
||||
.setSandboxed(true)
|
||||
.createRequire(context, scope)
|
||||
.install(scope);
|
||||
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public Scriptable getScriptable() {
|
||||
return mScriptable;
|
||||
}
|
||||
|
||||
protected TopLevelScope createScope(Context context) {
|
||||
TopLevelScope topLevelScope = new TopLevelScope();
|
||||
topLevelScope.initStandardObjects(context, false);
|
||||
return topLevelScope;
|
||||
}
|
||||
|
||||
public Context enterContext() {
|
||||
Context context = new RhinoAndroidHelper(mAndroidContext).enterContext();
|
||||
setupContext(context);
|
||||
sContextEngineMap.put(context, this);
|
||||
return context;
|
||||
}
|
||||
|
||||
protected void setupContext(Context context) {
|
||||
context.setOptimizationLevel(-1);
|
||||
context.setLanguageVersion(Context.VERSION_ES6);
|
||||
context.setLocale(Locale.getDefault());
|
||||
context.setWrapFactory(new WrapFactory());
|
||||
}
|
||||
|
||||
|
||||
public static RhinoJavaScriptEngine getEngineOfContext(Context context) {
|
||||
return sContextEngineMap.get(context);
|
||||
}
|
||||
|
||||
private class WrapFactory extends org.mozilla.javascript.WrapFactory {
|
||||
|
||||
@Override
|
||||
public Object wrap(Context cx, Scriptable scope, Object obj, Class<?> staticType) {
|
||||
Object result;
|
||||
if (obj instanceof String) {
|
||||
result = getRuntime().bridges.toString(obj.toString());
|
||||
} else if (staticType == UiObjectCollection.class) {
|
||||
result = getRuntime().bridges.asArray(obj);
|
||||
} else {
|
||||
result = super.wrap(cx, scope, obj, staticType);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class<?> staticType) {
|
||||
Scriptable result;
|
||||
if (javaObject instanceof View) {
|
||||
result = ViewExtras.getNativeView(scope, (View) javaObject, staticType, getRuntime());
|
||||
} else {
|
||||
result = super.wrapAsJavaObject(cx, scope, javaObject, staticType);
|
||||
}
|
||||
//Log.d(LOG_TAG, "wrapAsJavaObject: java = " + javaObject + ", result = " + result + ", scope = " + scope);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.stardust.autojs.engine
|
||||
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
|
||||
import com.stardust.autojs.BuildConfig
|
||||
import com.stardust.autojs.core.ui.ViewExtras
|
||||
import com.stardust.autojs.engine.module.AssetAndUrlModuleSourceProvider
|
||||
import com.stardust.autojs.execution.ExecutionConfig
|
||||
import com.stardust.autojs.project.ScriptConfig
|
||||
import com.stardust.autojs.rhino.RhinoAndroidHelper
|
||||
import com.stardust.autojs.rhino.TopLevelScope
|
||||
import com.stardust.autojs.runtime.ScriptRuntime
|
||||
import com.stardust.autojs.script.JavaScriptSource
|
||||
import com.stardust.automator.UiObjectCollection
|
||||
import com.stardust.pio.UncheckedIOException
|
||||
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.Script
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
import org.mozilla.javascript.commonjs.module.RequireBuilder
|
||||
import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.io.Reader
|
||||
import java.net.URI
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/2.
|
||||
*/
|
||||
|
||||
open class RhinoJavaScriptEngine(private val mAndroidContext: android.content.Context) : JavaScriptEngine() {
|
||||
|
||||
val context: Context
|
||||
private val mScriptable: TopLevelScope
|
||||
lateinit var thread: Thread
|
||||
private set
|
||||
|
||||
private val initScript: Script
|
||||
get() {
|
||||
return sInitScript ?: {
|
||||
try {
|
||||
val reader = InputStreamReader(mAndroidContext.assets.open("init.js"))
|
||||
val script = context.compileReader(reader, SOURCE_NAME_INIT, 1, null)
|
||||
sInitScript = script
|
||||
script
|
||||
} catch (e: IOException) {
|
||||
throw UncheckedIOException(e)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
val scriptable: Scriptable
|
||||
get() = mScriptable
|
||||
|
||||
init {
|
||||
this.context = enterContext()
|
||||
mScriptable = createScope(this.context)
|
||||
}
|
||||
|
||||
override fun put(name: String, value: Any?) {
|
||||
ScriptableObject.putProperty(mScriptable, name, Context.javaToJS(value, mScriptable))
|
||||
}
|
||||
|
||||
override fun setRuntime(runtime: ScriptRuntime) {
|
||||
super.setRuntime(runtime)
|
||||
runtime.topLevelScope = mScriptable
|
||||
}
|
||||
|
||||
public override fun doExecution(source: JavaScriptSource): Any {
|
||||
var reader = source.nonNullScriptReader
|
||||
try {
|
||||
reader = preprocess(reader)
|
||||
val script = context.compileReader(reader, source.toString(), 1, null)
|
||||
return if (hasFeature(ScriptConfig.FEATURE_CONTINUATION)) {
|
||||
context.executeScriptWithContinuations(script, mScriptable)
|
||||
} else {
|
||||
script.exec(context, mScriptable)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw UncheckedIOException(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun hasFeature(feature: String): Boolean {
|
||||
val config = getTag(ExecutionConfig.tag) as ExecutionConfig?
|
||||
return config != null && config.scriptConfig.hasFeature(feature)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
protected fun preprocess(script: Reader): Reader {
|
||||
return script
|
||||
}
|
||||
|
||||
override fun forceStop() {
|
||||
Log.d(LOG_TAG, "forceStop: interrupt Thread: $thread")
|
||||
thread.interrupt()
|
||||
}
|
||||
|
||||
|
||||
@Synchronized
|
||||
override fun destroy() {
|
||||
super.destroy()
|
||||
Log.d(LOG_TAG, "on destroy")
|
||||
sContextEngineMap.remove(context)
|
||||
Context.exit()
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
thread = Thread.currentThread()
|
||||
ScriptableObject.putProperty(mScriptable, "__engine__", this)
|
||||
initRequireBuilder(context, mScriptable)
|
||||
context.executeScriptWithContinuations(initScript, mScriptable)
|
||||
}
|
||||
|
||||
internal fun initRequireBuilder(context: Context, scope: Scriptable) {
|
||||
val provider = AssetAndUrlModuleSourceProvider(mAndroidContext, MODULES_PATH,
|
||||
listOf<URI>(File("/").toURI()))
|
||||
RequireBuilder()
|
||||
.setModuleScriptProvider(SoftCachingModuleScriptProvider(provider))
|
||||
.setSandboxed(true)
|
||||
.createRequire(context, scope)
|
||||
.install(scope)
|
||||
|
||||
}
|
||||
|
||||
protected fun createScope(context: Context): TopLevelScope {
|
||||
val topLevelScope = TopLevelScope()
|
||||
topLevelScope.initStandardObjects(context, false)
|
||||
return topLevelScope
|
||||
}
|
||||
|
||||
fun enterContext(): Context {
|
||||
val context = RhinoAndroidHelper(mAndroidContext).enterContext()
|
||||
setupContext(context)
|
||||
sContextEngineMap[context] = this
|
||||
return context
|
||||
}
|
||||
|
||||
protected fun setupContext(context: Context) {
|
||||
context.optimizationLevel = -1
|
||||
context.languageVersion = Context.VERSION_ES6
|
||||
context.locale = Locale.getDefault()
|
||||
context.wrapFactory = WrapFactory()
|
||||
}
|
||||
|
||||
private inner class WrapFactory : org.mozilla.javascript.WrapFactory() {
|
||||
|
||||
override fun wrap(cx: Context, scope: Scriptable, obj: Any?, staticType: Class<*>?): Any? {
|
||||
return when {
|
||||
obj is String -> runtime.bridges.toString(obj.toString())
|
||||
staticType == UiObjectCollection::class.java -> runtime.bridges.asArray(obj)
|
||||
else -> super.wrap(cx, scope, obj, staticType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun wrapAsJavaObject(cx: Context?, scope: Scriptable, javaObject: Any?, staticType: Class<*>?): Scriptable? {
|
||||
//Log.d(LOG_TAG, "wrapAsJavaObject: java = " + javaObject + ", result = " + result + ", scope = " + scope);
|
||||
return if (javaObject is View) {
|
||||
ViewExtras.getNativeView(scope, javaObject, staticType, runtime)
|
||||
} else {
|
||||
super.wrapAsJavaObject(cx, scope, javaObject, staticType)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val SOURCE_NAME_INIT = "<init>"
|
||||
|
||||
private val LOG_TAG = "RhinoJavaScriptEngine"
|
||||
|
||||
private val MODULES_PATH = "modules"
|
||||
private var sInitScript: Script? = null
|
||||
private val sContextEngineMap = ConcurrentHashMap<Context, RhinoJavaScriptEngine>()
|
||||
|
||||
|
||||
fun getEngineOfContext(context: Context): RhinoJavaScriptEngine? {
|
||||
return sContextEngineMap[context]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.stardust.autojs.execution
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.stardust.autojs.project.ScriptConfig
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -13,8 +14,7 @@ data class ExecutionConfig(var workingDirectory: String = "",
|
||||
var delay: Long = 0,
|
||||
var interval: Long = 0,
|
||||
var loopTimes: Int = 1,
|
||||
var uiMode: Boolean = false,
|
||||
var features: Int = 0) : Parcelable {
|
||||
var scriptConfig: ScriptConfig = ScriptConfig()) : Parcelable {
|
||||
|
||||
|
||||
private val mArguments = HashMap<String, Any>()
|
||||
@@ -88,9 +88,6 @@ data class ExecutionConfig(var workingDirectory: String = "",
|
||||
val default: ExecutionConfig
|
||||
get() = ExecutionConfig()
|
||||
|
||||
@JvmStatic
|
||||
val featureContinuation = 1
|
||||
|
||||
override fun createFromParcel(parcel: Parcel): ExecutionConfig {
|
||||
return ExecutionConfig(parcel)
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||
import com.stardust.autojs.script.ScriptSource;
|
||||
import com.stardust.lang.ThreadCompat;
|
||||
|
||||
import org.mozilla.javascript.ContinuationPending;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/5/1.
|
||||
*/
|
||||
|
||||
public class RunnableScriptExecution extends ScriptExecution.AbstractScriptExecution implements Runnable {
|
||||
|
||||
|
||||
private static final String TAG = "RunnableJSExecution";
|
||||
private ScriptEngine mScriptEngine;
|
||||
private ScriptEngineManager mScriptEngineManager;
|
||||
|
||||
@@ -18,4 +18,5 @@ public class LaunchConfig {
|
||||
public void setHideLogs(boolean hideLogs) {
|
||||
mHideLogs = hideLogs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.stardust.autojs.project;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
@@ -12,7 +11,9 @@ import com.stardust.pio.PFiles;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2018/1/24.
|
||||
@@ -51,31 +52,38 @@ public class ProjectConfig {
|
||||
@SerializedName("icon")
|
||||
private String mIcon;
|
||||
|
||||
@SerializedName("scripts")
|
||||
private Map<String, ScriptConfig> mScriptConfigs = new HashMap<>();
|
||||
|
||||
@SerializedName("useFeatures")
|
||||
private List<String> mFeatures = new ArrayList<>();
|
||||
|
||||
|
||||
public static ProjectConfig fromJson(String json) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
ProjectConfig config = GSON.fromJson(json, ProjectConfig.class);
|
||||
if(!isValid(config)){
|
||||
if (!isValid(config)) {
|
||||
return null;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private static boolean isValid(ProjectConfig config) {
|
||||
if(TextUtils.isEmpty(config.getName())){
|
||||
if (TextUtils.isEmpty(config.getName())) {
|
||||
return false;
|
||||
}
|
||||
if(TextUtils.isEmpty(config.getPackageName())){
|
||||
if (TextUtils.isEmpty(config.getPackageName())) {
|
||||
return false;
|
||||
}
|
||||
if(TextUtils.isEmpty(config.getVersionName())){
|
||||
if (TextUtils.isEmpty(config.getVersionName())) {
|
||||
return false;
|
||||
}
|
||||
if(TextUtils.isEmpty(config.getMainScriptFile())){
|
||||
if (TextUtils.isEmpty(config.getMainScriptFile())) {
|
||||
return false;
|
||||
}
|
||||
if(config.getVersionCode() == -1){
|
||||
if (config.getVersionCode() == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -160,6 +168,10 @@ public class ProjectConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, ScriptConfig> getScriptConfigs() {
|
||||
return mScriptConfigs;
|
||||
}
|
||||
|
||||
public List<String> getAssets() {
|
||||
if (mAssets == null) {
|
||||
mAssets = Collections.emptyList();
|
||||
@@ -210,4 +222,30 @@ public class ProjectConfig {
|
||||
public String getBuildDir() {
|
||||
return "build";
|
||||
}
|
||||
|
||||
public List<String> getFeatures() {
|
||||
return mFeatures;
|
||||
}
|
||||
|
||||
public void setFeatures(List<String> features) {
|
||||
mFeatures = features;
|
||||
}
|
||||
|
||||
public ScriptConfig getScriptConfig(String path) {
|
||||
ScriptConfig config = mScriptConfigs.get(path);
|
||||
if (config == null) {
|
||||
config = new ScriptConfig();
|
||||
}
|
||||
if(mFeatures.isEmpty()){
|
||||
return config;
|
||||
}
|
||||
ArrayList<String> features = new ArrayList<>(config.getFeatures());
|
||||
for (String feature : mFeatures) {
|
||||
if (!features.contains(feature)) {
|
||||
features.add(feature);
|
||||
}
|
||||
}
|
||||
config.setFeatures(features);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ public class ProjectLauncher {
|
||||
mMainScriptFile = new File(mProjectDir, mProjectConfig.getMainScriptFile());
|
||||
}
|
||||
|
||||
public void launch(ScriptEngineService service){
|
||||
public void launch(ScriptEngineService service) {
|
||||
ExecutionConfig config = new ExecutionConfig();
|
||||
config.setWorkingDirectory(mProjectDir);
|
||||
config.getScriptConfig().setFeatures(mProjectConfig.getFeatures());
|
||||
service.execute(new JavaScriptFileSource(mMainScriptFile), config);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.stardust.autojs.project
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ScriptConfig(
|
||||
@SerializedName("useFeatures") var features: List<String>,
|
||||
@SerializedName("uiMode") var uiMode: Boolean
|
||||
) {
|
||||
constructor() : this(emptyList(), false)
|
||||
|
||||
fun hasFeature(feature: String): Boolean {
|
||||
return features.contains(feature)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FEATURE_CONTINUATION = "continuation"
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class Dialogs {
|
||||
builder.content(content);
|
||||
}
|
||||
return ((BlockedMaterialDialog.Builder) builder).showAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private Context getContext() {
|
||||
if (mThemeWrapper != null)
|
||||
|
||||
@@ -23,7 +23,7 @@ public class VMBridge_custom extends VMBridge_jdk15 {
|
||||
protected Object newInterfaceProxy(Object proxyHelper, ContextFactory cf, InterfaceAdapter adapter, Object target, Scriptable topScope) {
|
||||
Context context = Context.getCurrentContext();
|
||||
InterfaceAdapterWrapper adapterWrapper = new InterfaceAdapterWrapper(adapter, context);
|
||||
RhinoJavaScriptEngine engine = RhinoJavaScriptEngine.getEngineOfContext(context);
|
||||
RhinoJavaScriptEngine engine = RhinoJavaScriptEngine.Companion.getEngineOfContext(context);
|
||||
// --- The following code is copied from super class --
|
||||
Constructor<?> c = (Constructor) proxyHelper;
|
||||
InvocationHandler handler = (proxy, method, args) -> {
|
||||
@@ -50,6 +50,8 @@ public class VMBridge_custom extends VMBridge_jdk15 {
|
||||
try {
|
||||
Object result = adapterWrapper.invoke(cf, target, topScope, proxy, method, args);
|
||||
return castReturnValue(method, result);
|
||||
} catch (ContinuationPending pending) {
|
||||
return defaultValue(method.getReturnType());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
// notify the script thread to exit
|
||||
@@ -143,6 +145,7 @@ public class VMBridge_custom extends VMBridge_jdk15 {
|
||||
|
||||
|
||||
public Object invokeImpl(Context cx, Object target, Scriptable topScope, Object thisObject, Method method, Object[] args) {
|
||||
cx.isContinuationsTopCall = true;
|
||||
return mInterfaceAdapter.invokeImpl(cx, target, topScope, thisObject, method, args);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user