Horis 2 лет назад
Родитель
Сommit
09a0c7eda9

+ 10 - 1
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -21,6 +21,7 @@ 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
@@ -31,6 +32,7 @@ import java.util.regex.Pattern
 import kotlin.collections.component1
 import kotlin.collections.component2
 import kotlin.collections.set
+import kotlin.coroutines.CoroutineContext
 
 /**
  * 解析规则获取结果
@@ -65,6 +67,8 @@ class AnalyzeRule(
 
     private val stringRuleCache = hashMapOf<String, List<SourceRule>>()
 
+    private var coroutineContext: CoroutineContext? = null
+
     @JvmOverloads
     fun setContent(content: Any?, baseUrl: String? = null): AnalyzeRule {
         if (content == null) throw AssertionError("内容不可空(Content cannot be null)")
@@ -80,6 +84,11 @@ class AnalyzeRule(
         return this
     }
 
+    fun setCoroutineContext(context: CoroutineContext?): AnalyzeRule {
+        coroutineContext = context
+        return this
+    }
+
     fun setBaseUrl(baseUrl: String?): AnalyzeRule {
         baseUrl?.let {
             this.baseUrl = baseUrl
@@ -752,7 +761,7 @@ class AnalyzeRule(
         source?.getShareScope()?.let {
             scope.prototype = it
         }
-        return RhinoScriptEngine.eval(jsStr, scope)
+        return RhinoScriptEngine.eval(jsStr, scope, coroutineContext)
     }
 
     override fun getSource(): BaseSource? {

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

@@ -168,6 +168,7 @@ object BookChapterList {
         val analyzeRule = AnalyzeRule(book, bookSource)
         analyzeRule.setContent(body).setBaseUrl(baseUrl)
         analyzeRule.setRedirectUrl(redirectUrl)
+        analyzeRule.setCoroutineContext(coroutineContext)
         //获取目录列表
         val chapterList = arrayListOf<BookChapter>()
         Debug.log(bookSource.bookSourceUrl, "┌获取目录列表", log)

+ 3 - 1
app/src/main/java/io/legado/app/model/webBook/BookContent.kt

@@ -57,6 +57,7 @@ object BookContent {
         val analyzeRule = AnalyzeRule(book, bookSource)
         analyzeRule.setContent(body, baseUrl)
         analyzeRule.setRedirectUrl(redirectUrl)
+        analyzeRule.setCoroutineContext(coroutineContext)
         analyzeRule.chapter = bookChapter
         analyzeRule.nextChapterUrl = mNextChapterUrl
         coroutineContext.ensureActive()
@@ -150,7 +151,7 @@ object BookContent {
     }
 
     @Throws(Exception::class)
-    private fun analyzeContent(
+    private suspend fun analyzeContent(
         book: Book,
         baseUrl: String,
         redirectUrl: String,
@@ -164,6 +165,7 @@ object BookContent {
     ): Pair<String, List<String>> {
         val analyzeRule = AnalyzeRule(book, bookSource)
         analyzeRule.setContent(body, baseUrl)
+        analyzeRule.setCoroutineContext(coroutineContext)
         val rUrl = analyzeRule.setRedirectUrl(redirectUrl)
         analyzeRule.nextChapterUrl = nextChapterUrl
         val nextUrlList = arrayListOf<String>()

+ 1 - 0
app/src/main/java/io/legado/app/model/webBook/BookInfo.kt

@@ -40,6 +40,7 @@ object BookInfo {
         val analyzeRule = AnalyzeRule(book, bookSource)
         analyzeRule.setContent(body).setBaseUrl(baseUrl)
         analyzeRule.setRedirectUrl(redirectUrl)
+        analyzeRule.setCoroutineContext(coroutineContext)
         analyzeBookInfo(book, body, analyzeRule, bookSource, baseUrl, redirectUrl, canReName)
     }
 

+ 1 - 0
app/src/main/java/io/legado/app/model/webBook/BookList.kt

@@ -45,6 +45,7 @@ object BookList {
         val analyzeRule = AnalyzeRule(ruleData, bookSource)
         analyzeRule.setContent(body).setBaseUrl(baseUrl)
         analyzeRule.setRedirectUrl(baseUrl)
+        analyzeRule.setCoroutineContext(coroutineContext)
         if (isSearch) bookSource.bookUrlPattern?.let {
             coroutineContext.ensureActive()
             if (baseUrl.matches(it.toRegex())) {

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

@@ -18,6 +18,7 @@ import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.isActive
 import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.coroutineContext
 
 @Suppress("MemberVisibilityCanBePrivate")
 object WebBook {
@@ -196,12 +197,14 @@ object WebBook {
         }
     }
 
-    fun runPreUpdateJs(bookSource: BookSource, book: Book): Result<Boolean> {
+    suspend fun runPreUpdateJs(bookSource: BookSource, book: Book): Result<Boolean> {
         return kotlin.runCatching {
             val preUpdateJs = bookSource.ruleToc?.preUpdateJs
             if (!preUpdateJs.isNullOrBlank()) {
                 kotlin.runCatching {
-                    AnalyzeRule(book, bookSource).evalJS(preUpdateJs)
+                    AnalyzeRule(book, bookSource)
+                        .setCoroutineContext(coroutineContext)
+                        .evalJS(preUpdateJs)
                 }.onFailure {
                     AppLog.put("执行preUpdateJs规则失败 书源:${bookSource.bookSourceName}", it)
                     throw it

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

@@ -6,6 +6,7 @@ package com.script
 import org.mozilla.javascript.Scriptable
 import java.io.Reader
 import java.io.StringReader
+import kotlin.coroutines.CoroutineContext
 
 abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngine {
 
@@ -64,6 +65,10 @@ abstract class AbstractScriptEngine(val bindings: Bindings? = null) : ScriptEngi
         return this.eval(reader, getRuntimeScope(context))
     }
 
+    override fun eval(script: String, scope: Scriptable, coroutineContext: CoroutineContext?): Any? {
+        return this.eval(StringReader(script), scope, coroutineContext)
+    }
+
     @Throws(ScriptException::class)
     override fun eval(reader: Reader, bindings: Bindings): Any? {
         return this.eval(reader, getScriptContext(bindings))

+ 8 - 1
modules/rhino1.7.3/src/main/java/com/script/ScriptEngine.kt

@@ -5,6 +5,7 @@ package com.script
 
 import org.mozilla.javascript.Scriptable
 import java.io.Reader
+import kotlin.coroutines.CoroutineContext
 
 interface ScriptEngine {
     var context: ScriptContext
@@ -14,14 +15,20 @@ interface ScriptEngine {
     @Throws(ScriptException::class)
     fun eval(reader: Reader, scope: Scriptable): Any?
 
+    @Throws(ScriptException::class)
+    fun eval(reader: Reader, scope: Scriptable, coroutineContext: CoroutineContext?): Any?
+
     @Throws(ScriptException::class)
     suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any?
 
+    @Throws(ScriptException::class)
+    fun eval(script: String, scope: Scriptable): Any?
+
     @Throws(ScriptException::class)
     suspend fun evalSuspend(script: String, scope: Scriptable): Any?
 
     @Throws(ScriptException::class)
-    fun eval(script: String, scope: Scriptable): Any?
+    fun eval(script: String, scope: Scriptable, coroutineContext: CoroutineContext?): Any?
 
     @Throws(ScriptException::class)
     fun eval(reader: Reader): Any?

+ 22 - 0
modules/rhino1.7.3/src/main/java/com/script/rhino/RhinoContext.kt

@@ -0,0 +1,22 @@
+package com.script.rhino
+
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.ensureActive
+import org.mozilla.javascript.Context
+import org.mozilla.javascript.ContextFactory
+import kotlin.coroutines.CoroutineContext
+
+class RhinoContext(factory: ContextFactory) : Context(factory) {
+
+    var coroutineContext: CoroutineContext? = null
+
+    @Throws(RhinoInterruptError::class)
+    fun ensureActive() {
+        try {
+            coroutineContext?.ensureActive()
+        } catch (e: CancellationException) {
+            throw RhinoInterruptError(e)
+        }
+    }
+
+}

+ 3 - 0
modules/rhino1.7.3/src/main/java/com/script/rhino/RhinoInterruptedError.kt

@@ -0,0 +1,3 @@
+package com.script.rhino
+
+class RhinoInterruptError(override val cause: Throwable) : Error()

+ 46 - 2
modules/rhino1.7.3/src/main/java/com/script/rhino/RhinoScriptEngine.kt

@@ -35,6 +35,7 @@ import java.io.StringReader
 import java.lang.reflect.Method
 import java.security.*
 import kotlin.coroutines.Continuation
+import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
 
 /**
@@ -84,6 +85,38 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
         return unwrapReturnValue(ret)
     }
 
+    override fun eval(
+        reader: Reader,
+        scope: Scriptable,
+        coroutineContext: CoroutineContext?
+    ): Any? {
+        val cx = Context.enter()
+        if (cx is RhinoContext) {
+            cx.coroutineContext = coroutineContext
+        }
+        val ret: Any?
+        try {
+            var filename = this["javax.script.filename"] as? String
+            filename = filename ?: "<Unknown source>"
+            ret = cx.evaluateReader(scope, reader, filename, 1, null)
+        } catch (re: RhinoException) {
+            val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
+            val msg: String = if (re is JavaScriptException) {
+                re.value.toString()
+            } else {
+                re.toString()
+            }
+            val se = ScriptException(msg, re.sourceName(), line)
+            se.initCause(re)
+            throw se
+        } catch (var14: IOException) {
+            throw ScriptException(var14)
+        } finally {
+            Context.exit()
+        }
+        return unwrapReturnValue(ret)
+    }
+
     @Throws(ContinuationPending::class)
     override suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any? {
         val cx = Context.enter()
@@ -259,11 +292,12 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
         ContextFactory.initGlobal(object : ContextFactory() {
 
             override fun makeContext(): Context {
-                val cx = super.makeContext()
+                val cx = RhinoContext(this)
                 cx.languageVersion = 200
                 cx.optimizationLevel = -1
                 cx.setClassShutter(RhinoClassShutter)
                 cx.wrapFactory = RhinoWrapFactory
+                cx.instructionObserverThreshold = 10000
                 return cx
             }
 
@@ -275,6 +309,12 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
                 }
             }
 
+            override fun observeInstructionCount(cx: Context, instructionCount: Int) {
+                if (cx is RhinoContext) {
+                    cx.ensureActive()
+                }
+            }
+
             override fun doTopCall(
                 callable: Callable,
                 cx: Context,
@@ -308,7 +348,11 @@ object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
                 thisObj: Scriptable?,
                 args: Array<Any>
             ): Any? {
-                return super.doTopCall(callable, cx, scope, thisObj, args)
+                try {
+                    return super.doTopCall(callable, cx, scope, thisObj, args)
+                } catch (e: RhinoInterruptError) {
+                    throw e.cause
+                }
             }
         })