diff --git a/app/src/androidTest/java/io/legado/app/AndroidJsTest.kt b/app/src/androidTest/java/io/legado/app/AndroidJsTest.kt index 84a868dea..83ae895a8 100644 --- a/app/src/androidTest/java/io/legado/app/AndroidJsTest.kt +++ b/app/src/androidTest/java/io/legado/app/AndroidJsTest.kt @@ -37,7 +37,7 @@ class AndroidJsTest { queryStringWithSign """.trimIndent() Rhino.use { - evaluateString(initStandardObjects(), js, "yy", 1, null) + evaluateString(it, js, "yy", 1, null) } @Language("js") val js1 = """ @@ -45,7 +45,7 @@ class AndroidJsTest { returnData.getErrorMsg() """.trimIndent() val result1 = Rhino.use { - evaluateString(initStandardObjects(), js1, "xx", 1, null) + evaluateString(it, js1, "xx", 1, null) } Assert.assertEquals(result1, "未知错误,请联系开发者!").let { @@ -59,17 +59,15 @@ class AndroidJsTest { @Language("js") val jsMap = "$=result;id=$.id;id" val result = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("result", map) - evaluateString(scope, jsMap, "xxx", 1, null) + it.putBinding("result", map) + evaluateString(it, jsMap, "xxx", 1, null) } Assert.assertEquals("3242532321", result) @Language("js") val jsMap1 = """result.get("id")""" val result1 = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("result", map) - evaluateString(scope, jsMap1, "xxx", 1, null) + it.putBinding("result", map) + evaluateString(it, jsMap1, "xxx", 1, null) } Assert.assertEquals("3242532321", result1) } diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index 38dd89cc9..b4667ffda 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -10,7 +10,6 @@ import android.os.Build import com.github.liuyueyi.quick.transfer.ChineseUtils import com.github.liuyueyi.quick.transfer.constants.TransType import com.jeremyliao.liveeventbus.LiveEventBus -import com.script.rhino.RhinoScriptEngine import io.legado.app.base.AppContextWrapper import io.legado.app.constant.AppConst.channelIdDownload import io.legado.app.constant.AppConst.channelIdReadAloud @@ -41,7 +40,6 @@ class App : Application() { override fun onCreate() { super.onCreate() - RhinoScriptEngine oldConfig = Configuration(resources.configuration) CrashHandler(this) //预下载Cronet so diff --git a/app/src/main/java/io/legado/app/data/entities/BaseSource.kt b/app/src/main/java/io/legado/app/data/entities/BaseSource.kt index 1f4c89f3a..ff631895e 100644 --- a/app/src/main/java/io/legado/app/data/entities/BaseSource.kt +++ b/app/src/main/java/io/legado/app/data/entities/BaseSource.kt @@ -236,15 +236,19 @@ interface BaseSource : JsExtensions { bindings["baseUrl"] = getKey() bindings["cookie"] = CookieStore bindings["cache"] = CacheManager -// return Rhino.use { -// val scope = initStandardObjects() +// return Rhino.use { scope -> // scope.putBindings(bindings) // getShareScope()?.let { // scope.prototype = it // } -// evaluate(scope, jsStr) +// eval(scope, jsStr) // } - return RhinoScriptEngine.eval(jsStr, SimpleBindings(bindings)) + val context = RhinoScriptEngine.getScriptContext(SimpleBindings(bindings)) + val scope = RhinoScriptEngine.getRuntimeScope(context) + getShareScope()?.let { + scope.prototype = it + } + return RhinoScriptEngine.eval(jsStr, scope) } fun getShareScope(): Scriptable? { diff --git a/app/src/main/java/io/legado/app/help/book/BookExtensions.kt b/app/src/main/java/io/legado/app/help/book/BookExtensions.kt index 55dbe3dde..fa5351be9 100644 --- a/app/src/main/java/io/legado/app/help/book/BookExtensions.kt +++ b/app/src/main/java/io/legado/app/help/book/BookExtensions.kt @@ -241,9 +241,8 @@ fun Book.getExportFileName(suffix: String): String { bindings["author"] = getRealAuthor() return kotlin.runCatching { Rhino.use { - val scope = initStandardObjects() - scope.putBindings(bindings) - evaluateString(scope, jsStr, "name&author", 1, null) + it.putBindings(bindings) + evaluateString(it, jsStr, "name&author", 1, null) }.toString() + "." + suffix }.onFailure { AppLog.put("导出书名规则错误,使用默认规则\n${it.localizedMessage}", it) diff --git a/app/src/main/java/io/legado/app/model/SharedJsScope.kt b/app/src/main/java/io/legado/app/model/SharedJsScope.kt index 10d320404..666a12f09 100644 --- a/app/src/main/java/io/legado/app/model/SharedJsScope.kt +++ b/app/src/main/java/io/legado/app/model/SharedJsScope.kt @@ -32,7 +32,7 @@ object SharedJsScope { var scope = scopeMap[key]?.get() if (scope == null) { Rhino.use { - scope = initStandardObjects() + scope = it if (jsLib.isJsonObject()) { val jsMap: Map = GSON.fromJson( jsLib, diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index c1530d614..41339c48d 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -258,6 +258,7 @@ class AnalyzeRule( } else { getAnalyzeByJSoup(it).getString(sourceRule.rule) } + else -> sourceRule.rule } } @@ -307,6 +308,7 @@ class AnalyzeRule( result.toString(), sourceRule.rule.splitNotBlank("&&") ) + Mode.Js -> evalJS(sourceRule.rule, it) Mode.Json -> getAnalyzeByJSonPath(it).getObject(sourceRule.rule) Mode.XPath -> getAnalyzeByXPath(it).getElements(sourceRule.rule) @@ -339,6 +341,7 @@ class AnalyzeRule( result.toString(), sourceRule.rule.splitNotBlank("&&") ) + Mode.Js -> evalJS(sourceRule.rule, result) Mode.Json -> getAnalyzeByJSonPath(it).getList(sourceRule.rule) Mode.XPath -> getAnalyzeByXPath(it).getElements(sourceRule.rule) @@ -389,7 +392,7 @@ class AnalyzeRule( if (rule.replaceRegex.isEmpty()) return result var vResult = result vResult = if (rule.replaceFirst) { - /* ##match##replace### 获取第一个匹配到的结果并进行替换 */ + /* ##match##replace### 获取第一个匹配到的结果并进行替换 */ kotlin.runCatching { val pattern = Pattern.compile(rule.replaceRegex) val matcher = pattern.matcher(vResult) @@ -402,7 +405,7 @@ class AnalyzeRule( rule.replacement } } else { - /* ##match##replace 替换*/ + /* ##match##replace 替换*/ kotlin.runCatching { vResult.replace(rule.replaceRegex.toRegex(), rule.replacement) }.getOrElse { @@ -476,26 +479,32 @@ class AnalyzeRule( mode = Mode.Default ruleStr } + ruleStr.startsWith("@@") -> { mode = Mode.Default ruleStr.substring(2) } + ruleStr.startsWith("@XPath:", true) -> { mode = Mode.XPath ruleStr.substring(7) } + ruleStr.startsWith("@Json:", true) -> { mode = Mode.Json ruleStr.substring(6) } + isJSON || ruleStr.startsWith("$.") || ruleStr.startsWith("$[") -> { mode = Mode.Json ruleStr } + ruleStr.startsWith("/") -> {//XPath特征很明显,无需配置单独的识别标头 mode = Mode.XPath ruleStr } + else -> ruleStr } //分离put @@ -523,10 +532,12 @@ class AnalyzeRule( ruleType.add(getRuleType) ruleParam.add(tmp.substring(6, tmp.lastIndex)) } + tmp.startsWith("{{") -> { ruleType.add(jsRuleType) ruleParam.add(tmp.substring(2, tmp.length - 2)) } + else -> { splitRegex(tmp) } @@ -592,6 +603,7 @@ class AnalyzeRule( } } ?: infoVal.insert(0, ruleParam[index]) } + regType == jsRuleType -> { if (isRule(ruleParam[index])) { getString(arrayListOf(SourceRule(ruleParam[index]))).let { @@ -606,13 +618,16 @@ class AnalyzeRule( 0, String.format("%.0f", jsEval) ) + else -> infoVal.insert(0, jsEval.toString()) } } } + regType == getRuleType -> { infoVal.insert(0, get(ruleParam[index])) } + else -> infoVal.insert(0, ruleParam[index]) } } @@ -667,6 +682,7 @@ class AnalyzeRule( "bookName" -> book?.let { return it.name } + "title" -> chapter?.let { return it.title } @@ -694,15 +710,19 @@ class AnalyzeRule( bindings["title"] = chapter?.title bindings["src"] = content bindings["nextChapterUrl"] = nextChapterUrl -// return Rhino.use { -// val scope = initStandardObjects() +// return Rhino.use { scope -> // scope.putBindings(bindings) // source?.getShareScope()?.let { // scope.prototype = it // } -// evaluate(scope, jsStr) +// eval(scope, jsStr) // } - return RhinoScriptEngine.eval(jsStr, SimpleBindings(bindings)) + val context = RhinoScriptEngine.getScriptContext(SimpleBindings(bindings)) + val scope = RhinoScriptEngine.getRuntimeScope(context) + source?.getShareScope()?.let { + scope.prototype = it + } + return RhinoScriptEngine.eval(jsStr, scope) } override fun getSource(): BaseSource? { diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 4e2836923..9d564a99b 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -215,6 +215,7 @@ class AnalyzeUrl( urlNoQuery = url.substring(0, pos) } } + RequestMethod.POST -> body?.let { if (!it.isJson() && !it.isXml() && headerMap["Content-Type"].isNullOrEmpty()) { analyzeFields(it) @@ -266,15 +267,19 @@ class AnalyzeUrl( bindings["book"] = ruleData as? Book bindings["source"] = source bindings["result"] = result -// return Rhino.use { -// val scope = initStandardObjects() +// return Rhino.use { scope -> // scope.putBindings(bindings) // source?.getShareScope()?.let { // scope.prototype = it // } -// evaluate(scope, jsStr) +// eval(scope, jsStr) // } - return RhinoScriptEngine.eval(jsStr, SimpleBindings(bindings)) + val context = RhinoScriptEngine.getScriptContext(SimpleBindings(bindings)) + val scope = RhinoScriptEngine.getRuntimeScope(context) + source?.getShareScope()?.let { + scope.prototype = it + } + return RhinoScriptEngine.eval(jsStr, scope) } fun put(key: String, value: String): String { @@ -288,6 +293,7 @@ class AnalyzeUrl( "bookName" -> (ruleData as? Book)?.let { return it.name } + "title" -> chapter?.let { return it.title } @@ -353,7 +359,10 @@ class AnalyzeUrl( } } if (waitTime > 0) { - throw ConcurrentException("根据并发率还需等待${waitTime}毫秒才可以访问", waitTime = waitTime) + throw ConcurrentException( + "根据并发率还需等待${waitTime}毫秒才可以访问", + waitTime = waitTime + ) } return fetchRecord } @@ -414,6 +423,7 @@ class AnalyzeUrl( headerMap = headerMap ).getStrResponse() } + else -> BackstageWebView( url = url, tag = source?.getKey(), @@ -439,6 +449,7 @@ class AnalyzeUrl( postJson(body) } } + else -> get(urlNoQuery, fieldMap, true) } }.let { @@ -500,6 +511,7 @@ class AnalyzeUrl( postJson(body) } } + else -> get(urlNoQuery, fieldMap, true) } } diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 1c3f5a52d..f543ad4e8 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -20,7 +20,7 @@ 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.eval import io.legado.app.rhino.putBinding import io.legado.app.utils.* import kotlinx.coroutines.runBlocking @@ -272,9 +272,8 @@ object LocalBook { AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})" //在脚本中定义如何分解文件名成书名、作者名 val jsonStr = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("src", tempFileName) - evaluate(scope, js) + it.putBinding("src", tempFileName) + eval(it, js) }.toString() val bookMess = GSON.fromJsonObject>(jsonStr) .getOrThrow() diff --git a/app/src/main/java/io/legado/app/rhino/Rhino.kt b/app/src/main/java/io/legado/app/rhino/Rhino.kt index c5d17184c..27ff42df1 100644 --- a/app/src/main/java/io/legado/app/rhino/Rhino.kt +++ b/app/src/main/java/io/legado/app/rhino/Rhino.kt @@ -1,15 +1,18 @@ package io.legado.app.rhino import org.mozilla.javascript.Context +import org.mozilla.javascript.ImporterTopLevel +import org.mozilla.javascript.Scriptable import org.mozilla.javascript.Undefined import org.mozilla.javascript.Wrapper object Rhino { - inline fun use(block: Context.() -> T): T { + inline fun use(block: Context.(Scriptable) -> T): T { return try { val cx = Context.enter() - block.invoke(cx) + val scope = cx.initStandardObjects(ImporterTopLevel(cx)) + block.invoke(cx, scope) } finally { Context.exit() } diff --git a/app/src/main/java/io/legado/app/rhino/RhinoExtensions.kt b/app/src/main/java/io/legado/app/rhino/RhinoExtensions.kt index c1589a125..56a3ebebb 100644 --- a/app/src/main/java/io/legado/app/rhino/RhinoExtensions.kt +++ b/app/src/main/java/io/legado/app/rhino/RhinoExtensions.kt @@ -7,7 +7,7 @@ import org.mozilla.javascript.Scriptable import org.mozilla.javascript.ScriptableObject import java.io.Reader -fun Context.evaluate( +fun Context.eval( scope: Scriptable, source: String, sourceName: String = "", @@ -19,21 +19,7 @@ fun Context.evaluate( ) } -fun Context.evaluate( - bindings: Bindings, - source: String, - sourceName: String = "", - 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( +fun Context.eval( scope: Scriptable, reader: Reader, sourceName: String = "", @@ -45,20 +31,6 @@ fun Context.evaluate( ) } -fun Context.evaluate( - bindings: Bindings, - reader: Reader, - sourceName: String = "", - 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?) { val wrappedOut = Context.javaToJS(value, this) ScriptableObject.putProperty(this, key, wrappedOut) diff --git a/app/src/main/java/io/legado/app/utils/JsUtils.kt b/app/src/main/java/io/legado/app/utils/JsUtils.kt index e23346fe9..5e79ddb52 100644 --- a/app/src/main/java/io/legado/app/utils/JsUtils.kt +++ b/app/src/main/java/io/legado/app/utils/JsUtils.kt @@ -3,7 +3,8 @@ package io.legado.app.utils import io.legado.app.constant.AppPattern.EXP_PATTERN import io.legado.app.rhino.Bindings import io.legado.app.rhino.Rhino -import io.legado.app.rhino.evaluate +import io.legado.app.rhino.eval +import io.legado.app.rhino.putBindings object JsUtils { @@ -14,9 +15,10 @@ object JsUtils { val sb = StringBuffer() val expMatcher = EXP_PATTERN.matcher(js) while (expMatcher.find()) { - val result = expMatcher.group(1)?.let { + val result = expMatcher.group(1)?.let { js1 -> Rhino.use { - evaluate(bindings, it) + it.putBindings(bindings) + eval(it, js1) } } ?: "" if (result is String) { @@ -31,7 +33,8 @@ object JsUtils { return sb.toString() } return Rhino.use { - evaluate(bindings, js) + it.putBindings(bindings) + eval(it, js) }.toString() } diff --git a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt index b931e36d8..8f018577a 100644 --- a/app/src/main/java/io/legado/app/utils/RegexExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/RegexExtensions.kt @@ -4,9 +4,9 @@ import androidx.core.os.postDelayed 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 io.legado.app.rhino.eval +import io.legado.app.rhino.putBinding import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine import splitties.init.appCtx @@ -31,10 +31,9 @@ fun CharSequence.replace(regex: Regex, replacement: String, timeout: Long): Stri val stringBuffer = StringBuffer() while (matcher.find()) { if (isJs) { - val bindings = Bindings() - bindings["result"] = matcher.group() val jsResult = Rhino.use { - evaluate(bindings, replacement1) + it.putBinding("result", matcher.group()) + eval(it, replacement1) }.toString() matcher.appendReplacement(stringBuffer, jsResult) } else { diff --git a/app/src/test/java/io/legado/app/JsTest.kt b/app/src/test/java/io/legado/app/JsTest.kt index 4c8cb7ec6..4a5bdfec2 100644 --- a/app/src/test/java/io/legado/app/JsTest.kt +++ b/app/src/test/java/io/legado/app/JsTest.kt @@ -3,7 +3,7 @@ package io.legado.app import com.script.SimpleBindings import io.legado.app.data.entities.BookChapter import io.legado.app.rhino.Rhino -import io.legado.app.rhino.evaluate +import io.legado.app.rhino.eval import io.legado.app.rhino.putBinding import org.intellij.lang.annotations.Language import org.junit.Assert @@ -42,9 +42,8 @@ class JsTest { @Language("js") val jsMap1 = """result.get("id")""" val result1 = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("result", map) - evaluateString(scope, jsMap1, "xxx", 1, null) + it.putBinding("result", map) + evaluateString(it, jsMap1, "xxx", 1, null) } Assert.assertEquals("3242532321", result1) } @@ -52,9 +51,8 @@ class JsTest { @Test fun testFor() { val scope = Rhino.use { - val scope = initStandardObjects() - evaluateString(scope, printJs, "print", 1, null) - scope + evaluateString(it, printJs, "print", 1, null) + it } @Language("js") @@ -77,7 +75,8 @@ class JsTest { result """.trimIndent() val result = Rhino.use { - evaluateString(scope, jsFor, "jsFor", 1, null) + it.prototype = scope + evaluateString(it, jsFor, "jsFor", 1, null) } Assert.assertEquals("12012", result) } @@ -85,7 +84,7 @@ class JsTest { @Test fun testReturnNull() { val result = Rhino.use { - evaluate(initStandardObjects(), "null") + eval(it, "null") } Assert.assertEquals(null, result) } @@ -103,9 +102,8 @@ class JsTest { .replace(/\:/g,":") """.trimIndent() val result = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("result", ",.!?…;:") - evaluate(scope, js) + it.putBinding("result", ",.!?…;:") + eval(it, js) } Assert.assertEquals(result, ",。!?……;:") } @@ -119,9 +117,8 @@ class JsTest { @Language("js") val js = "chapter.title" val result = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("chapter", chapter) - evaluate(scope, js) + it.putBinding("chapter", chapter) + eval(it, js) } Assert.assertEquals(result, "xxxyyy") } @@ -137,9 +134,8 @@ class JsTest { result """.trimIndent() val result = Rhino.use { - val scope = initStandardObjects() - scope.putBinding("list", list) - evaluate(scope, js) + it.putBinding("list", list) + eval(it, js) } Assert.assertEquals(result, 6.0) }