kunfei 3 лет назад
Родитель
Сommit
f704db3384

+ 0 - 7
app/src/main/java/io/legado/app/constant/ScriptEngine.kt

@@ -1,7 +0,0 @@
-package io.legado.app.constant
-
-import com.script.rhino.RhinoScriptEngine
-
-val SCRIPT_ENGINE: RhinoScriptEngine by lazy {
-    RhinoScriptEngine()
-}

+ 13 - 8
app/src/main/java/io/legado/app/data/entities/BaseSource.kt

@@ -1,16 +1,18 @@
 package io.legado.app.data.entities
 
 import cn.hutool.crypto.symmetric.AES
-import com.script.SimpleBindings
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppLog
-import io.legado.app.constant.SCRIPT_ENGINE
 import io.legado.app.data.entities.rule.RowUi
 import io.legado.app.help.CacheManager
 import io.legado.app.help.JsExtensions
 import io.legado.app.help.config.AppConfig
 import io.legado.app.help.http.CookieStore
 import io.legado.app.model.SharedJsScope
+import io.legado.app.rhino.Bindings
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
+import io.legado.app.rhino.putBindings
 import io.legado.app.utils.*
 import org.intellij.lang.annotations.Language
 import org.mozilla.javascript.Scriptable
@@ -227,19 +229,22 @@ interface BaseSource : JsExtensions {
      * 执行JS
      */
     @Throws(Exception::class)
-    fun evalJS(jsStr: String, bindingsConfig: SimpleBindings.() -> Unit = {}): Any? {
-        val bindings = SimpleBindings()
+    fun evalJS(jsStr: String, bindingsConfig: Bindings.() -> Unit = {}): Any? {
+        val bindings = Bindings()
         bindings.apply(bindingsConfig)
         bindings["java"] = this
         bindings["source"] = this
         bindings["baseUrl"] = getKey()
         bindings["cookie"] = CookieStore
         bindings["cache"] = CacheManager
-        val scope = SCRIPT_ENGINE.getRuntimeScope(SCRIPT_ENGINE.getScriptContext(bindings))
-        getShareScope()?.let {
-            scope.prototype = it
+        return Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBindings(bindings)
+            getShareScope()?.let {
+                scope.prototype = it
+            }
+            evaluate(scope, jsStr)
         }
-        return SCRIPT_ENGINE.eval(jsStr, scope)
     }
 
     fun getShareScope(): Scriptable? {

+ 9 - 3
app/src/main/java/io/legado/app/help/book/BookExtensions.kt

@@ -3,7 +3,6 @@
 package io.legado.app.help.book
 
 import android.net.Uri
-import com.script.SimpleBindings
 import io.legado.app.constant.*
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.BaseBook
@@ -11,6 +10,9 @@ import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookSource
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.config.AppConfig
+import io.legado.app.rhino.Bindings
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.putBindings
 import io.legado.app.utils.*
 import splitties.init.appCtx
 import java.io.File
@@ -234,11 +236,15 @@ fun Book.getExportFileName(suffix: String): String {
     if (jsStr.isNullOrBlank()) {
         return "$name 作者:${getRealAuthor()}.$suffix"
     }
-    val bindings = SimpleBindings()
+    val bindings = Bindings()
     bindings["name"] = name
     bindings["author"] = getRealAuthor()
     return kotlin.runCatching {
-        SCRIPT_ENGINE.eval(jsStr, bindings).toString() + "." + suffix
+        Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBindings(bindings)
+            evaluateString(scope, jsStr, "name&author", 1, null)
+        }.toString() + "." + suffix
     }.onFailure {
         AppLog.put("导出书名规则错误,使用默认规则\n${it.localizedMessage}", it)
     }.getOrDefault("${name} 作者:${getRealAuthor()}.$suffix")

+ 1 - 4
app/src/main/java/io/legado/app/model/SharedJsScope.kt

@@ -1,8 +1,6 @@
 package io.legado.app.model
 
 import com.google.gson.reflect.TypeToken
-import com.script.SimpleBindings
-import io.legado.app.constant.SCRIPT_ENGINE
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.http.newCallStrResponse
 import io.legado.app.help.http.okHttpClient
@@ -33,9 +31,8 @@ object SharedJsScope {
         val key = MD5Utils.md5Encode(jsLib)
         var scope = scopeMap[key]?.get()
         if (scope == null) {
-            val context = SCRIPT_ENGINE.getScriptContext(SimpleBindings())
-            scope = SCRIPT_ENGINE.getRuntimeScope(context)
             Rhino.use {
+                scope = initStandardObjects()
                 if (jsLib.isJsonObject()) {
                     val jsMap: Map<String, String> = GSON.fromJson(
                         jsLib,

+ 12 - 8
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -2,14 +2,16 @@ package io.legado.app.model.analyzeRule
 
 import android.text.TextUtils
 import androidx.annotation.Keep
-import com.script.SimpleBindings
 import io.legado.app.constant.AppPattern.JS_PATTERN
-import io.legado.app.constant.SCRIPT_ENGINE
 import io.legado.app.data.entities.*
 import io.legado.app.help.CacheManager
 import io.legado.app.help.JsExtensions
 import io.legado.app.help.http.CookieStore
 import io.legado.app.model.webBook.WebBook
+import io.legado.app.rhino.Bindings
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
+import io.legado.app.rhino.putBindings
 import io.legado.app.utils.*
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeout
@@ -681,7 +683,7 @@ class AnalyzeRule(
      * 执行JS
      */
     fun evalJS(jsStr: String, result: Any? = null): Any? {
-        val bindings = SimpleBindings()
+        val bindings = Bindings()
         bindings["java"] = this
         bindings["cookie"] = CookieStore
         bindings["cache"] = CacheManager
@@ -693,12 +695,14 @@ class AnalyzeRule(
         bindings["title"] = chapter?.title
         bindings["src"] = content
         bindings["nextChapterUrl"] = nextChapterUrl
-        val context = SCRIPT_ENGINE.getScriptContext(bindings)
-        val scope = SCRIPT_ENGINE.getRuntimeScope(context)
-        source?.getShareScope()?.let {
-            scope.prototype = it
+        return Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBindings(bindings)
+            source?.getShareScope()?.let {
+                scope.prototype = it
+            }
+            evaluate(scope, jsStr)
         }
-        return SCRIPT_ENGINE.eval(jsStr, scope)
     }
 
     override fun getSource(): BaseSource? {

+ 12 - 8
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@@ -5,12 +5,10 @@ import android.util.Base64
 import androidx.annotation.Keep
 import cn.hutool.core.util.HexUtil
 import com.bumptech.glide.load.model.GlideUrl
-import com.script.SimpleBindings
 import io.legado.app.constant.AppConst.UA_NAME
 import io.legado.app.constant.AppPattern
 import io.legado.app.constant.AppPattern.JS_PATTERN
 import io.legado.app.constant.AppPattern.dataUriRegex
-import io.legado.app.constant.SCRIPT_ENGINE
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
@@ -20,6 +18,10 @@ import io.legado.app.help.JsExtensions
 import io.legado.app.help.config.AppConfig
 import io.legado.app.help.glide.GlideHeaders
 import io.legado.app.help.http.*
+import io.legado.app.rhino.Bindings
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
+import io.legado.app.rhino.putBindings
 import io.legado.app.utils.*
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
@@ -253,7 +255,7 @@ class AnalyzeUrl(
      * 执行JS
      */
     fun evalJS(jsStr: String, result: Any? = null): Any? {
-        val bindings = SimpleBindings()
+        val bindings = Bindings()
         bindings["java"] = this
         bindings["baseUrl"] = baseUrl
         bindings["cookie"] = CookieStore
@@ -265,12 +267,14 @@ class AnalyzeUrl(
         bindings["book"] = ruleData as? Book
         bindings["source"] = source
         bindings["result"] = result
-        val context = SCRIPT_ENGINE.getScriptContext(bindings)
-        val scope = SCRIPT_ENGINE.getRuntimeScope(context)
-        source?.getShareScope()?.let {
-            scope.prototype = it
+        return Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBindings(bindings)
+            source?.getShareScope()?.let {
+                scope.prototype = it
+            }
+            evaluate(scope, jsStr)
         }
-        return SCRIPT_ENGINE.eval(jsStr, scope)
     }
 
     fun put(key: String, value: String): String {

+ 11 - 7
app/src/main/java/io/legado/app/model/localBook/LocalBook.kt

@@ -3,7 +3,6 @@ package io.legado.app.model.localBook
 import android.net.Uri
 import android.util.Base64
 import androidx.documentfile.provider.DocumentFile
-import com.script.SimpleBindings
 import io.legado.app.R
 import io.legado.app.constant.*
 import io.legado.app.data.appDb
@@ -20,6 +19,9 @@ import io.legado.app.help.config.AppConfig
 import io.legado.app.lib.webdav.WebDav
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.model.analyzeRule.AnalyzeUrl
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
+import io.legado.app.rhino.putBinding
 import io.legado.app.utils.*
 import kotlinx.coroutines.runBlocking
 import org.jsoup.nodes.Entities
@@ -265,13 +267,15 @@ object LocalBook {
         var author = ""
         if (!AppConfig.bookImportFileName.isNullOrBlank()) {
             try {
+                //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获
+                val js =
+                    AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})"
                 //在脚本中定义如何分解文件名成书名、作者名
-                val jsonStr = SCRIPT_ENGINE.eval(
-                    //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获
-                    AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})",
-                    //将文件名注入到脚本的src变量中
-                    SimpleBindings().also { it["src"] = tempFileName }
-                ).toString()
+                val jsonStr = Rhino.use {
+                    val scope = initStandardObjects()
+                    scope.putBinding("src", tempFileName)
+                    evaluate(scope, js)
+                }.toString()
                 val bookMess = GSON.fromJsonObject<HashMap<String, String>>(jsonStr)
                     .getOrThrow()
                 name = bookMess["name"] ?: ""

+ 54 - 0
app/src/main/java/io/legado/app/rhino/Bindings.kt

@@ -0,0 +1,54 @@
+/*
+ * Decompiled with CFR 0.152.
+ */
+package io.legado.app.rhino
+
+class Bindings @JvmOverloads constructor(
+    private val map: MutableMap<String, Any?> = HashMap()
+) : MutableMap<String, Any?> {
+
+    override fun put(key: String, value: Any?): Any? {
+        return map.put(key, value)
+    }
+
+    override fun putAll(from: Map<out String, Any?>) {
+        map.putAll(from)
+    }
+
+    override fun clear() {
+        map.clear()
+    }
+
+    override fun containsKey(key: String): Boolean {
+        return map.containsKey(key)
+    }
+
+    override fun containsValue(value: Any?): Boolean {
+        return map.containsValue(value)
+    }
+
+    override val entries: MutableSet<MutableMap.MutableEntry<String, Any?>>
+        get() = map.entries
+
+    override operator fun get(key: String): Any? {
+        return map[key]
+    }
+
+    override fun isEmpty(): Boolean {
+        return map.isEmpty()
+    }
+
+    override val keys: MutableSet<String>
+        get() = map.keys
+
+    override fun remove(key: String): Any? {
+        return map.remove(key)
+    }
+
+    override val size: Int
+        get() = map.size
+
+    override val values: MutableCollection<Any?>
+        get() = map.values
+
+}

+ 2 - 3
app/src/main/java/io/legado/app/rhino/Rhino.kt

@@ -8,11 +8,10 @@ import org.mozilla.javascript.Wrapper
 
 object Rhino {
 
-    inline fun use(block: Context.() -> Any?): Any? {
+    inline fun <T> use(block: Context.() -> T): T {
         return try {
             val cx = Context.enter()
-            val result = block.invoke(cx)
-            unwrapReturnValue(result)
+            block.invoke(cx)
         } finally {
             Context.exit()
         }

+ 49 - 5
app/src/main/java/io/legado/app/rhino/RhinoExtensions.kt

@@ -7,12 +7,56 @@ import org.mozilla.javascript.Scriptable
 import org.mozilla.javascript.ScriptableObject
 import java.io.Reader
 
-fun Context.evaluateString(scope: Scriptable, source: String, sourceName: String) {
-    evaluateString(scope, source, sourceName, 1, null)
+fun Context.evaluate(
+    scope: Scriptable,
+    source: String,
+    sourceName: String = "<Unknown source>",
+    lineno: Int = 1,
+    securityDomain: Any? = null
+): Any? {
+    return Rhino.unwrapReturnValue(
+        evaluateString(scope, source, sourceName, lineno, securityDomain)
+    )
 }
 
-fun Context.evaluateReader(scope: Scriptable, reader: Reader, sourceName: String) {
-    evaluateReader(scope, reader, sourceName, 1, null)
+fun Context.evaluate(
+    bindings: Bindings,
+    source: String,
+    sourceName: String = "<Unknown source>",
+    lineno: Int = 1,
+    securityDomain: Any? = null
+): Any? {
+    val scope = initStandardObjects()
+    scope.putBindings(bindings)
+    return Rhino.unwrapReturnValue(
+        evaluateString(scope, source, sourceName, lineno, securityDomain)
+    )
+}
+
+fun Context.evaluate(
+    scope: Scriptable,
+    reader: Reader,
+    sourceName: String = "<Unknown source>",
+    lineno: Int = 1,
+    securityDomain: Any? = null
+): Any? {
+    return Rhino.unwrapReturnValue(
+        evaluateReader(scope, reader, sourceName, lineno, securityDomain)
+    )
+}
+
+fun Context.evaluate(
+    bindings: Bindings,
+    reader: Reader,
+    sourceName: String = "<Unknown source>",
+    lineno: Int = 1,
+    securityDomain: Any? = null
+): Any? {
+    val scope = initStandardObjects()
+    scope.putBindings(bindings)
+    return Rhino.unwrapReturnValue(
+        evaluateReader(scope, reader, sourceName, lineno, securityDomain)
+    )
 }
 
 fun Scriptable.putBinding(key: String, value: Any?) {
@@ -20,7 +64,7 @@ fun Scriptable.putBinding(key: String, value: Any?) {
     ScriptableObject.putProperty(this, key, wrappedOut)
 }
 
-fun Scriptable.putBindings(bindings: Map<String, Any?>) {
+fun Scriptable.putBindings(bindings: Bindings) {
     bindings.forEach { (t, u) ->
         putBinding(t, u)
     }

+ 11 - 6
app/src/main/java/io/legado/app/utils/JsUtils.kt

@@ -1,20 +1,23 @@
 package io.legado.app.utils
 
-import com.script.SimpleBindings
 import io.legado.app.constant.AppPattern.EXP_PATTERN
-import io.legado.app.constant.SCRIPT_ENGINE
+import io.legado.app.rhino.Bindings
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
 
 object JsUtils {
 
-    fun evalJs(js: String, bindingsFun: ((SimpleBindings) -> Unit)? = null): String {
-        val bindings = SimpleBindings()
+    fun evalJs(js: String, bindingsFun: ((Bindings) -> Unit)? = null): String {
+        val bindings = Bindings()
         bindingsFun?.invoke(bindings)
         if (js.contains("{{") && js.contains("}}")) {
             val sb = StringBuffer()
             val expMatcher = EXP_PATTERN.matcher(js)
             while (expMatcher.find()) {
                 val result = expMatcher.group(1)?.let {
-                    SCRIPT_ENGINE.eval(it, bindings)
+                    Rhino.use {
+                        evaluate(bindings, it)
+                    }
                 } ?: ""
                 if (result is String) {
                     expMatcher.appendReplacement(sb, result)
@@ -27,7 +30,9 @@ object JsUtils {
             expMatcher.appendTail(sb)
             return sb.toString()
         }
-        return SCRIPT_ENGINE.eval(js, bindings).toString()
+        return Rhino.use {
+            evaluate(bindings, js)
+        }.toString()
     }
 
 

+ 7 - 5
app/src/main/java/io/legado/app/utils/RegexExtensions.kt

@@ -1,11 +1,12 @@
 package io.legado.app.utils
 
 import androidx.core.os.postDelayed
-import com.script.SimpleBindings
-import io.legado.app.constant.SCRIPT_ENGINE
 import io.legado.app.exception.RegexTimeoutException
 import io.legado.app.help.CrashHandler
 import io.legado.app.help.coroutine.Coroutine
+import io.legado.app.rhino.Bindings
+import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.suspendCancellableCoroutine
 import splitties.init.appCtx
@@ -30,10 +31,11 @@ fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): Stri
                     val stringBuffer = StringBuffer()
                     while (matcher.find()) {
                         if (isJs) {
-                            val bindings = SimpleBindings()
+                            val bindings = Bindings()
                             bindings["result"] = matcher.group()
-                            val jsResult =
-                                SCRIPT_ENGINE.eval(replacement1, bindings).toString()
+                            val jsResult = Rhino.use {
+                                evaluate(bindings, replacement1)
+                            }.toString()
                             matcher.appendReplacement(stringBuffer, jsResult)
                         } else {
                             matcher.appendReplacement(stringBuffer, replacement1)

+ 21 - 11
app/src/test/java/io/legado/app/JsTest.kt

@@ -1,14 +1,13 @@
 package io.legado.app
 
 import com.script.SimpleBindings
-import io.legado.app.constant.SCRIPT_ENGINE
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.rhino.Rhino
+import io.legado.app.rhino.evaluate
 import io.legado.app.rhino.putBinding
 import org.intellij.lang.annotations.Language
 import org.junit.Assert
 import org.junit.Test
-import org.mozilla.javascript.Scriptable
 
 class JsTest {
 
@@ -56,7 +55,7 @@ class JsTest {
             val scope = initStandardObjects()
             evaluateString(scope, printJs, "print", 1, null)
             scope
-        } as Scriptable
+        }
 
         @Language("js")
         val jsFor = """
@@ -85,7 +84,9 @@ class JsTest {
 
     @Test
     fun testReturnNull() {
-        val result = SCRIPT_ENGINE.eval("null")
+        val result = Rhino.use {
+            evaluate(initStandardObjects(), "null")
+        }
         Assert.assertEquals(null, result)
     }
 
@@ -101,9 +102,11 @@ class JsTest {
             .replace(/\;/g,";")
             .replace(/\:/g,":")
         """.trimIndent()
-        val bindings = SimpleBindings()
-        bindings["result"] = ",.!?…;:"
-        val result = SCRIPT_ENGINE.eval(js, bindings).toString()
+        val result = Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBinding("result", ",.!?…;:")
+            evaluate(scope, js)
+        }
         Assert.assertEquals(result, ",。!?……;:")
     }
 
@@ -115,22 +118,29 @@ class JsTest {
         bindings["chapter"] = chapter
         @Language("js")
         val js = "chapter.title"
-        val result = SCRIPT_ENGINE.eval(js, bindings)
+        val result = Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBinding("chapter", chapter)
+            evaluate(scope, js)
+        }
         Assert.assertEquals(result, "xxxyyy")
     }
 
     @Test
     fun javaListForEach() {
         val list = arrayListOf(1, 2, 3)
-        val bindings = SimpleBindings()
-        bindings["list"] = list
+
         @Language("js")
         val js = """
             var result = 0
             list.forEach(item => {result = result + item})
             result
         """.trimIndent()
-        val result = SCRIPT_ENGINE.eval(js, bindings)
+        val result = Rhino.use {
+            val scope = initStandardObjects()
+            scope.putBinding("list", list)
+            evaluate(scope, js)
+        }
         Assert.assertEquals(result, 6.0)
     }