Horis 1 год назад
Родитель
Сommit
6daff489a1

+ 6 - 1
app/src/main/java/io/legado/app/data/entities/BaseSource.kt

@@ -14,7 +14,9 @@ import io.legado.app.help.crypto.SymmetricCryptoAndroid
 import io.legado.app.help.http.CookieStore
 import io.legado.app.help.source.copy
 import io.legado.app.help.source.getShareScope
+import io.legado.app.model.Debug
 import io.legado.app.utils.GSON
+import io.legado.app.utils.GSONStrict
 import io.legado.app.utils.fromJsonArray
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.has
@@ -112,7 +114,10 @@ interface BaseSource : JsExtensions {
 
                     else -> it
                 }
-                GSON.fromJsonObject<Map<String, String>>(json).getOrNull()?.let { map ->
+                GSONStrict.fromJsonObject<Map<String, String>>(json).getOrNull()?.let { map ->
+                    putAll(map)
+                } ?: GSON.fromJsonObject<Map<String, String>>(json).getOrNull()?.let { map ->
+                    Debug.log("≡请求头规则 JSON 格式不规范,请改为规范格式")
                     putAll(map)
                 }
             } catch (e: Exception) {

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

@@ -520,6 +520,12 @@ class AnalyzeRule(
         return ruleList
     }
 
+    private fun getOrCreateSingleSourceRule(rule: String): List<SourceRule> {
+        return stringRuleCache.getOrPutLimit(rule, 16) {
+            listOf(SourceRule(rule))
+        }
+    }
+
     /**
      * 规则类
      */
@@ -672,7 +678,8 @@ class AnalyzeRule(
 
                         regType == jsRuleType -> {
                             if (isRule(ruleParam[index])) {
-                                getString(arrayListOf(SourceRule(ruleParam[index]))).let {
+                                val ruleList = getOrCreateSingleSourceRule(ruleParam[index])
+                                getString(ruleList).let {
                                     infoVal.insert(0, it)
                                 }
                             } else {

+ 3 - 0
gradle/libs.versions.toml

@@ -48,6 +48,8 @@ recyclerview = "1.2.0"
 #noinspection GradleDependency
 viewpager2 = "1.0.0"
 webkit = "1.13.0"
+collection = "1.5.0"
+
 zxingLite = "3.2.0"
 
 
@@ -60,6 +62,7 @@ activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activ
 androidx-annotation = { group = "androidx.annotation", name = "annotation", version = "1.9.1" }
 #androidx-annotation-experimental = { group = "androidx.annotation", name = "annotation-experimental", version = "1.3.1" }
 
+androidx-collection = { module = "androidx.collection:collection", version.ref = "collection" }
 appcompat-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
 androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
 

+ 1 - 0
modules/rhino/build.gradle

@@ -40,6 +40,7 @@ dependencies {
 
     implementation(libs.kotlinx.coroutines.core)
     implementation(libs.okhttp)
+    implementation(libs.androidx.collection)
 
 //    def coroutines_version = '1.7.3'
 //    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")

+ 41 - 0
modules/rhino/src/main/java/com/script/rhino/ClassNameMatcher.kt

@@ -0,0 +1,41 @@
+package com.script.rhino
+
+import androidx.collection.LruCache
+import kotlin.math.min
+
+class ClassNameMatcher(classNames: List<String>) {
+
+    private val sortedClassNames = classNames.sorted()
+    private val matchCache = LruCache<String, Boolean>(64)
+
+    fun match(className: String): Boolean {
+        matchCache[className]?.let {
+            return it
+        }
+        val match = matchInternal(className)
+        matchCache.put(className, match)
+        return match
+    }
+
+    private fun matchInternal(className: String): Boolean {
+        val index = sortedClassNames.fastBinarySearch { prefix ->
+            comparePrefix(className, prefix)
+        }
+        if (index >= 0) {
+            return true
+        }
+        val prefix = sortedClassNames.getOrNull(-index - 2) ?: return false
+        return className.getOrNull(prefix.length) == '.' && className.startsWith(prefix)
+    }
+
+    private fun comparePrefix(className: String, prefix: String): Int {
+        val len = min(className.length, prefix.length)
+        for (i in 0 ..< len) {
+            val c1 = className[i]
+            val c2 = prefix[i]
+            if (c1 != c2) return c2 - c1
+        }
+        return prefix.length - className.length
+    }
+
+}

+ 22 - 0
modules/rhino/src/main/java/com/script/rhino/CollectionExtensions.kt

@@ -0,0 +1,22 @@
+package com.script.rhino
+
+inline fun <T> List<T>.fastBinarySearch(
+    comparison: (T) -> Int
+): Int {
+    var low = 0
+    var high = lastIndex
+
+    while (low <= high) {
+        val mid = (low + high).ushr(1) // safe from overflows
+        val midVal = get(mid)
+        val cmp = comparison(midVal)
+
+        if (cmp < 0)
+            low = mid + 1
+        else if (cmp > 0)
+            high = mid - 1
+        else
+            return mid // key found
+    }
+    return -(low + 1)  // key not found
+}

+ 4 - 11
modules/rhino/src/main/java/com/script/rhino/RhinoClassShutter.kt

@@ -45,8 +45,8 @@ import java.util.Collections
  */
 object RhinoClassShutter : ClassShutter {
 
-    private val protectedClassNames by lazy {
-        hashSetOf(
+    private val protectedClassNamesMatcher by lazy {
+        listOf(
             "java.lang.Class",
             "java.lang.ClassLoader",
             "java.net.URLClassLoader",
@@ -113,7 +113,7 @@ object RhinoClassShutter : ClassShutter {
             "com.script",
             "org.mozilla",
             "sun",
-        ).let { Collections.unmodifiableSet(it) }
+        ).let { ClassNameMatcher(it) }
     }
 
     private val systemClassProtectedName by lazy {
@@ -181,14 +181,7 @@ object RhinoClassShutter : ClassShutter {
     }
 
     override fun visibleToScripts(fullClassName: String): Boolean {
-        var className = fullClassName
-        while (className.isNotEmpty()) {
-            if (protectedClassNames.contains(className)) {
-                return false
-            }
-            className = className.substringBeforeLast(".", "")
-        }
-        return true
+        return !protectedClassNamesMatcher.match(fullClassName)
     }
 
 }