Horis vor 2 Jahren
Ursprung
Commit
bc7b9383f5

+ 2 - 2
app/src/androidTest/java/io/legado/app/AndroidJsTest.kt

@@ -1,7 +1,7 @@
 package io.legado.app
 
 import cn.hutool.core.lang.JarClassLoader
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import dalvik.system.DexClassLoader
 import org.intellij.lang.annotations.Language
@@ -69,7 +69,7 @@ class AndroidJsTest {
     @Test
     fun testMap() {
         val map = hashMapOf("id" to "3242532321")
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["result"] = map
         @Language("js")
         val jsMap = "$=result;id=$.id;id"

+ 2 - 2
app/src/androidTest/java/io/legado/app/MigrationTest.kt

@@ -21,7 +21,7 @@ class MigrationTest {
 
     )
 
-    @Rule
+    @get:Rule
     val helper: MigrationTestHelper = MigrationTestHelper(
         InstrumentationRegistry.getInstrumentation(),
         AppDatabase::class.java.canonicalName,
@@ -32,7 +32,7 @@ class MigrationTest {
     @Throws(IOException::class)
     fun migrateAll() {
         // Create earliest version of the database.
-        helper.createDatabase(TEST_DB, 30).apply {
+        helper.createDatabase(TEST_DB, 50).apply {
             close()
         }
 

+ 4 - 5
app/src/main/java/io/legado/app/data/entities/BaseSource.kt

@@ -1,7 +1,7 @@
 package io.legado.app.data.entities
 
 import cn.hutool.crypto.symmetric.AES
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppLog
@@ -231,16 +231,15 @@ interface BaseSource : JsExtensions {
      * 执行JS
      */
     @Throws(Exception::class)
-    fun evalJS(jsStr: String, bindingsConfig: SimpleBindings.() -> Unit = {}): Any? {
-        val bindings = SimpleBindings()
+    fun evalJS(jsStr: String, bindingsConfig: ScriptBindings.() -> Unit = {}): Any? {
+        val bindings = ScriptBindings()
         bindings.apply(bindingsConfig)
         bindings["java"] = this
         bindings["source"] = this
         bindings["baseUrl"] = getKey()
         bindings["cookie"] = CookieStore
         bindings["cache"] = CacheManager
-        val context = RhinoScriptEngine.getScriptContext(bindings)
-        val scope = RhinoScriptEngine.getRuntimeScope(context)
+        val scope = RhinoScriptEngine.getRuntimeScope(bindings)
         getShareScope()?.let {
             scope.prototype = it
         }

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

@@ -3,7 +3,7 @@
 package io.legado.app.help.book
 
 import android.net.Uri
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.BookSourceType
@@ -273,7 +273,7 @@ fun Book.getExportFileName(suffix: String): String {
     if (jsStr.isNullOrBlank()) {
         return "$name 作者:${getRealAuthor()}.$suffix"
     }
-    val bindings = SimpleBindings()
+    val bindings = ScriptBindings()
     bindings["epubIndex"] = ""// 兼容老版本,修复可能存在的错误
     bindings["name"] = name
     bindings["author"] = getRealAuthor()
@@ -297,7 +297,7 @@ fun Book.getExportFileName(
     if (jsStr.isNullOrBlank()) {
         return default
     }
-    val bindings = SimpleBindings()
+    val bindings = ScriptBindings()
     bindings["name"] = name
     bindings["author"] = getRealAuthor()
     bindings["epubIndex"] = epubIndex
@@ -327,7 +327,7 @@ fun Book.readSimulating(): Boolean {
 }
 
 fun tryParesExportFileName(jsStr: String): Boolean {
-    val bindings = SimpleBindings()
+    val bindings = ScriptBindings()
     bindings["name"] = "name"
     bindings["author"] = "author"
     bindings["epubIndex"] = "epubIndex"

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

@@ -1,7 +1,7 @@
 package io.legado.app.model
 
 import com.google.gson.reflect.TypeToken
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.http.newCallStrResponse
@@ -33,7 +33,7 @@ object SharedJsScope {
         var scope = scopeMap[key]?.get()
         if (scope == null) {
             scope = RhinoScriptEngine.run {
-                getRuntimeScope(getScriptContext(SimpleBindings()))
+                getRuntimeScope(ScriptBindings())
             }
             if (jsLib.isJsonObject()) {
                 val jsMap: Map<String, String> = GSON.fromJson(

+ 3 - 5
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -2,7 +2,7 @@ package io.legado.app.model.analyzeRule
 
 import android.text.TextUtils
 import androidx.annotation.Keep
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.constant.AppPattern.JS_PATTERN
 import io.legado.app.data.entities.BaseBook
@@ -21,7 +21,6 @@ import io.legado.app.utils.isJson
 import io.legado.app.utils.printOnDebug
 import io.legado.app.utils.splitNotBlank
 import io.legado.app.utils.stackTraceStr
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeout
 import org.apache.commons.text.StringEscapeUtils
@@ -746,7 +745,7 @@ class AnalyzeRule(
      * 执行JS
      */
     fun evalJS(jsStr: String, result: Any? = null): Any? {
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["java"] = this
         bindings["cookie"] = CookieStore
         bindings["cache"] = CacheManager
@@ -758,8 +757,7 @@ class AnalyzeRule(
         bindings["title"] = chapter?.title
         bindings["src"] = content
         bindings["nextChapterUrl"] = nextChapterUrl
-        val context = RhinoScriptEngine.getScriptContext(bindings)
-        val scope = RhinoScriptEngine.getRuntimeScope(context)
+        val scope = RhinoScriptEngine.getRuntimeScope(bindings)
         source?.getShareScope()?.let {
             scope.prototype = it
         }

+ 3 - 4
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@@ -6,7 +6,7 @@ import androidx.annotation.Keep
 import androidx.media3.common.MediaItem
 import cn.hutool.core.util.HexUtil
 import com.bumptech.glide.load.model.GlideUrl
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.constant.AppConst.UA_NAME
 import io.legado.app.constant.AppPattern
@@ -266,7 +266,7 @@ class AnalyzeUrl(
      * 执行JS
      */
     fun evalJS(jsStr: String, result: Any? = null): Any? {
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["java"] = this
         bindings["baseUrl"] = baseUrl
         bindings["cookie"] = CookieStore
@@ -278,8 +278,7 @@ class AnalyzeUrl(
         bindings["book"] = ruleData as? Book
         bindings["source"] = source
         bindings["result"] = result
-        val context = RhinoScriptEngine.getScriptContext(bindings)
-        val scope = RhinoScriptEngine.getRuntimeScope(context)
+        val scope = RhinoScriptEngine.getRuntimeScope(bindings)
         source?.getShareScope()?.let {
             scope.prototype = it
         }

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

@@ -3,7 +3,7 @@ package io.legado.app.model.localBook
 import android.net.Uri
 import android.util.Base64
 import androidx.documentfile.provider.DocumentFile
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.R
 import io.legado.app.constant.*
@@ -291,7 +291,7 @@ object LocalBook {
                     AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})"
                 //在脚本中定义如何分解文件名成书名、作者名
                 val jsonStr = RhinoScriptEngine.run {
-                    val bindings = SimpleBindings()
+                    val bindings = ScriptBindings()
                     bindings["src"] = tempFileName
                     eval(js, bindings)
                 }.toString()

+ 2 - 2
app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt

@@ -1,7 +1,7 @@
 package io.legado.app.model.webBook
 
 import android.text.TextUtils
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.R
 import io.legado.app.data.entities.Book
@@ -127,7 +127,7 @@ object BookChapterList {
         Debug.log(book.origin, "◇目录总数:${list.size}")
         coroutineContext.ensureActive()
         val formatJs = tocRule.formatJs
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["gInt"] = 0
         list.forEachIndexed { index, bookChapter ->
             bookChapter.index = index

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

@@ -1,7 +1,7 @@
 package io.legado.app.utils
 
 import androidx.core.os.postDelayed
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.exception.RegexTimeoutException
 import io.legado.app.help.CrashHandler
@@ -32,7 +32,7 @@ fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): Stri
                     while (matcher.find()) {
                         if (isJs) {
                             val jsResult = RhinoScriptEngine.run {
-                                val bindings = SimpleBindings()
+                                val bindings = ScriptBindings()
                                 bindings["result"] = matcher.group()
                                 eval(replacement1, bindings)
                             }.toString()

+ 18 - 6
app/src/test/java/io/legado/app/JsTest.kt

@@ -1,6 +1,6 @@
 package io.legado.app
 
-import com.script.SimpleBindings
+import com.script.ScriptBindings
 import com.script.rhino.RhinoScriptEngine
 import io.legado.app.data.entities.BookChapter
 import org.intellij.lang.annotations.Language
@@ -28,7 +28,7 @@ class JsTest {
     @Test
     fun testMap() {
         val map = hashMapOf("id" to "3242532321")
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["result"] = map
         @Language("js")
         val jsMap = "$=result;id=$.id;id"
@@ -43,7 +43,7 @@ class JsTest {
     @Test
     fun testFor() {
         val scope = RhinoScriptEngine.run {
-            val scope = getRuntimeScope(getScriptContext(SimpleBindings()))
+            val scope = getRuntimeScope(ScriptBindings())
             eval(printJs, scope)
             scope
         }
@@ -86,7 +86,7 @@ class JsTest {
             s[2].substr(0,n);
         """.trimIndent()
         val x = RhinoScriptEngine.run {
-            val bindings = SimpleBindings()
+            val bindings = ScriptBindings()
             bindings["result"] = "筳彩涫第七百一十四章 人头树鮺舦綸"
             eval(js, bindings)
         }
@@ -97,7 +97,7 @@ class JsTest {
     @Test
     fun chapterText() {
         val chapter = BookChapter(title = "xxxyyy")
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["chapter"] = chapter
         @Language("js")
         val js = "chapter.title"
@@ -108,7 +108,7 @@ class JsTest {
     @Test
     fun javaListForEach() {
         val list = arrayListOf(1, 2, 3)
-        val bindings = SimpleBindings()
+        val bindings = ScriptBindings()
         bindings["list"] = list
         @Language("js")
         val js = """
@@ -120,4 +120,16 @@ class JsTest {
         Assert.assertEquals(result, 6.0)
     }
 
+    @Test
+    fun typeofString() {
+        val bindings = ScriptBindings()
+        @Language("js")
+        val js = """
+            s = "" + String()
+            typeof s
+        """.trimIndent()
+        val result = RhinoScriptEngine.eval(js, bindings)
+        Assert.assertEquals(result, "string")
+    }
+
 }

+ 2 - 1
gradle.properties

@@ -45,4 +45,5 @@ android.nonTransitiveRClass=true
 CronetVersion=123.0.6312.80
 CronetMainVersion=123.0.0.0
 android.injected.testOnly=false
-android.nonFinalResIds=true
+android.nonFinalResIds=true
+android.injected.androidTest.leaveApksInstalledAfterRun=true

+ 5 - 0
gradle/libs.versions.toml

@@ -37,6 +37,7 @@ protobufJavalite = "4.26.1"
 quickChineseTransfer = "0.2.13"
 room = "2.6.1"
 splitties = "3.0.0"
+rhino = "1.7.15"
 
 activity = "1.8.2"
 kotlinxSerialization = "1.6.3"
@@ -104,6 +105,7 @@ protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.
 
 quick-chinese-transfer-core = { module = "com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core", version.ref = "quickChineseTransfer" }
 
+
 room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
 room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
 room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
@@ -148,6 +150,9 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa
 kotlinx-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinxSerialization" }
 liveeventbus = { module = "com.github.michaellee123:LiveEventBus", version.ref = "liveeventbus" }
 material = { module = "com.google.android.material:material", version.ref = "material" }
+
+mozilla-rhino = { module = "org.mozilla:rhino", version.ref = "rhino" }
+
 renderscript-intrinsics-replacement-toolkit = { module = "com.github.android:renderscript-intrinsics-replacement-toolkit", version = "b6363490c3" }
 zxing-lite = { module = "com.github.jenly1314:zxing-lite", version.ref = "zxingLite" }
 

+ 2 - 1
modules/rhino/build.gradle

@@ -35,7 +35,8 @@ android {
 }
 
 dependencies {
-    api(fileTree(dir: 'lib', include: ['rhino-1.7.14.jar']))
+//    api(fileTree(dir: 'lib', include: ['rhino-1.7.14.jar']))
+    api libs.mozilla.rhino
 
     implementation(libs.kotlinx.coroutines.core)
 

+ 5 - 0
modules/rhino/src/main/java/com/script/AbstractScriptEngine.kt

@@ -81,6 +81,11 @@ abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngi
         return this.eval(script, getScriptContext(bindings))
     }
 
+    @Throws(ScriptException::class)
+    override fun eval(script: String, bindings: ScriptBindings): Any? {
+        return this.eval(script, getRuntimeScope(bindings))
+    }
+
     @Throws(ScriptException::class)
     override fun eval(reader: Reader): Any? {
         return this.eval(reader, context)

+ 42 - 0
modules/rhino/src/main/java/com/script/ScriptBindings.kt

@@ -0,0 +1,42 @@
+package com.script
+
+import org.mozilla.javascript.Context
+import org.mozilla.javascript.NativeObject
+import org.mozilla.javascript.ScriptableObject
+
+class ScriptBindings : NativeObject() {
+
+    companion object {
+        private val topLevelScope: ScriptableObject by lazy {
+            val cx = Context.enter()
+            try {
+                cx.initStandardObjects()
+            } finally {
+                Context.exit()
+            }
+        }
+    }
+
+    init {
+        prototype = topLevelScope
+    }
+
+    operator fun set(key: String, value: Any?) {
+        Context.enter()
+        try {
+            put(key, this, Context.javaToJS(value, this))
+        } finally {
+            Context.exit()
+        }
+    }
+
+    operator fun set(index: Int, value: Any?) {
+        Context.enter()
+        try {
+            put(index, this, Context.javaToJS(value, this))
+        } finally {
+            Context.exit()
+        }
+    }
+
+}

+ 5 - 0
modules/rhino/src/main/java/com/script/ScriptEngine.kt

@@ -45,9 +45,14 @@ interface ScriptEngine {
     @Throws(ScriptException::class)
     fun eval(script: String, bindings: Bindings): Any?
 
+    @Throws(ScriptException::class)
+    fun eval(script: String, bindings: ScriptBindings): Any?
+
     @Throws(ScriptException::class)
     fun eval(script: String, context: ScriptContext): Any?
 
+    fun getRuntimeScope(bindings: ScriptBindings): Scriptable
+
     fun getRuntimeScope(context: ScriptContext): Scriptable
 
     fun getScriptContext(bindings: Bindings): ScriptContext

+ 12 - 2
modules/rhino/src/main/java/com/script/rhino/RhinoScriptEngine.kt

@@ -53,8 +53,8 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
     private val indexedProps: MutableMap<Any, Any?>
     private val implementor: InterfaceImplementor
 
-    fun eval(js: String, bindingsConfig: SimpleBindings.() -> Unit = {}): Any? {
-        val bindings = SimpleBindings()
+    fun eval(js: String, bindingsConfig: ScriptBindings.() -> Unit = {}): Any? {
+        val bindings = ScriptBindings()
         bindings.apply(bindingsConfig)
         return eval(js, bindings)
     }
@@ -249,6 +249,16 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
         return newScope
     }
 
+    override fun getRuntimeScope(bindings: ScriptBindings): ScriptBindings {
+        val cx = Context.enter()
+        try {
+            bindings.prototype = cx.initStandardObjects()
+        } finally {
+            Context.exit()
+        }
+        return bindings
+    }
+
     @Throws(ScriptException::class)
     override fun compile(script: String): CompiledScript {
         return this.compile(StringReader(script) as Reader)