mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
优化
This commit is contained in:
parent
1dbe4b4ec0
commit
a4a4de8ef1
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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? {
|
||||
|
@ -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)
|
||||
|
@ -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<String, String> = GSON.fromJson(
|
||||
jsLib,
|
||||
|
@ -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? {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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<HashMap<String, String>>(jsonStr)
|
||||
.getOrThrow()
|
||||
|
@ -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 <T> use(block: Context.() -> T): T {
|
||||
inline fun <T> 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()
|
||||
}
|
||||
|
@ -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 = "<Unknown source>",
|
||||
@ -19,21 +19,7 @@ fun Context.evaluate(
|
||||
)
|
||||
}
|
||||
|
||||
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(
|
||||
fun Context.eval(
|
||||
scope: Scriptable,
|
||||
reader: Reader,
|
||||
sourceName: String = "<Unknown source>",
|
||||
@ -45,20 +31,6 @@ fun Context.evaluate(
|
||||
)
|
||||
}
|
||||
|
||||
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?) {
|
||||
val wrappedOut = Context.javaToJS(value, this)
|
||||
ScriptableObject.putProperty(this, key, wrappedOut)
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user