feat(project): 初始化项目结构和配置
This commit is contained in:
Executable
+47
@@ -0,0 +1,47 @@
|
||||
plugins {
|
||||
//d 'com.android.library'
|
||||
//id 'org.jetbrains.kotlin.android'
|
||||
alias libs.plugins.android.library
|
||||
alias libs.plugins.kotlin.android
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = compile_sdk_version
|
||||
namespace = 'com.script'
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
defaultConfig {
|
||||
minSdk 21
|
||||
targetSdk 36
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
lint {
|
||||
checkDependencies = true
|
||||
}
|
||||
tasks.withType(JavaCompile).tap {
|
||||
configureEach {
|
||||
options.compilerArgs << "-Xlint:deprecation"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// api(fileTree(dir: 'lib', include: ['rhino-1.7.14.jar']))
|
||||
api libs.mozilla.rhino
|
||||
|
||||
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")
|
||||
}
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
## Rhino
|
||||
-keep class
|
||||
!org.mozilla.javascript.ast.**,
|
||||
!org.mozilla.javascript.xml.**,
|
||||
!org.mozilla.javascript.commonjs.**,
|
||||
!org.mozilla.javascript.optimizer.**,
|
||||
!org.mozilla.javascript.serialize.**,
|
||||
org.mozilla.javascript.** { *; }
|
||||
|
||||
-dontwarn org.mozilla.javascript.engine.RhinoScriptEngineFactory
|
||||
### 以下内容是更新rhino1.7.14.jar后IDE提示添加的
|
||||
-dontwarn java.beans.**
|
||||
-dontwarn javax.script.**
|
||||
### 以下内容是更新rhino1.8.0.jar后IDE提示添加的
|
||||
-dontwarn jdk.dynalink.CallSiteDescriptor
|
||||
-dontwarn jdk.dynalink.DynamicLinker
|
||||
-dontwarn jdk.dynalink.DynamicLinkerFactory
|
||||
-dontwarn jdk.dynalink.NamedOperation
|
||||
-dontwarn jdk.dynalink.Namespace
|
||||
-dontwarn jdk.dynalink.NamespaceOperation
|
||||
-dontwarn jdk.dynalink.Operation
|
||||
-dontwarn jdk.dynalink.RelinkableCallSite
|
||||
-dontwarn jdk.dynalink.StandardNamespace
|
||||
-dontwarn jdk.dynalink.StandardOperation
|
||||
-dontwarn jdk.dynalink.linker.GuardedInvocation
|
||||
-dontwarn jdk.dynalink.linker.GuardingDynamicLinker
|
||||
-dontwarn jdk.dynalink.linker.LinkRequest
|
||||
-dontwarn jdk.dynalink.linker.LinkerServices
|
||||
-dontwarn jdk.dynalink.linker.TypeBasedGuardingDynamicLinker
|
||||
-dontwarn jdk.dynalink.linker.support.CompositeTypeBasedGuardingDynamicLinker
|
||||
-dontwarn jdk.dynalink.linker.support.Guards
|
||||
-dontwarn jdk.dynalink.support.ChainedCallSite
|
||||
Executable
BIN
Binary file not shown.
Executable
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
import com.script.ScriptContext.Companion.ENGINE_SCOPE
|
||||
import com.script.ScriptContext.Companion.GLOBAL_SCOPE
|
||||
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 {
|
||||
|
||||
override var context: ScriptContext = SimpleScriptContext()
|
||||
|
||||
init {
|
||||
bindings?.let {
|
||||
context.setBindings(bindings, ENGINE_SCOPE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBindings(scope: Int): Bindings? {
|
||||
if (scope == GLOBAL_SCOPE) {
|
||||
return context.getBindings(GLOBAL_SCOPE)
|
||||
}
|
||||
if (scope == ENGINE_SCOPE) {
|
||||
return context.getBindings(ENGINE_SCOPE)
|
||||
}
|
||||
throw IllegalArgumentException("Invalid scope value.")
|
||||
}
|
||||
|
||||
override fun setBindings(bindings: Bindings?, scope: Int) {
|
||||
when (scope) {
|
||||
GLOBAL_SCOPE -> {
|
||||
context.setBindings(bindings, GLOBAL_SCOPE)
|
||||
}
|
||||
|
||||
ENGINE_SCOPE -> {
|
||||
context.setBindings(bindings, ENGINE_SCOPE)
|
||||
}
|
||||
|
||||
else -> {
|
||||
throw IllegalArgumentException("Invalid scope value.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun put(key: String, value: Any?) {
|
||||
getBindings(ENGINE_SCOPE)?.put(key, value)
|
||||
}
|
||||
|
||||
override fun get(key: String): Any? {
|
||||
return getBindings(ENGINE_SCOPE)?.get(key)
|
||||
}
|
||||
|
||||
override fun eval(reader: Reader, scope: Scriptable): Any? {
|
||||
return eval(reader, scope, null)
|
||||
}
|
||||
|
||||
override suspend fun evalSuspend(script: String, scope: Scriptable): Any? {
|
||||
return this.evalSuspend(StringReader(script), scope)
|
||||
}
|
||||
|
||||
override fun eval(script: String, scope: Scriptable): Any? {
|
||||
return this.eval(StringReader(script), scope)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
override fun eval(reader: Reader, context: ScriptContext): Any? {
|
||||
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))
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
override fun eval(script: String, bindings: Bindings): Any? {
|
||||
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)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
override fun eval(script: String): Any? {
|
||||
return this.eval(script, context)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
override fun eval(script: String, context: ScriptContext): Any? {
|
||||
return this.eval(StringReader(script), context)
|
||||
}
|
||||
|
||||
override fun getScriptContext(bindings: Bindings): ScriptContext {
|
||||
val ctx = SimpleScriptContext(bindings, context.errorWriter, context.reader, context.writer)
|
||||
val gs = getBindings(GLOBAL_SCOPE)
|
||||
if (gs != null) {
|
||||
ctx.setBindings(gs, GLOBAL_SCOPE)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
interface Bindings : MutableMap<String, Any?> {
|
||||
|
||||
override fun containsKey(key: String): Boolean
|
||||
|
||||
override operator fun get(key: String): Any?
|
||||
|
||||
override fun put(key: String, value: Any?): Any?
|
||||
|
||||
override fun putAll(from: Map<out String, *>)
|
||||
|
||||
override fun remove(key: String): Any?
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
import java.io.Reader
|
||||
|
||||
interface Compilable {
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun compile(script: Reader): CompiledScript
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun compile(script: String): CompiledScript
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
import com.script.ScriptContext.Companion.ENGINE_SCOPE
|
||||
import com.script.ScriptContext.Companion.GLOBAL_SCOPE
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
abstract class CompiledScript {
|
||||
|
||||
abstract fun getEngine(): ScriptEngine
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(context: ScriptContext): Any? {
|
||||
return eval(getEngine().getRuntimeScope(context))
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(scope: Scriptable): Any? {
|
||||
return eval(scope, null)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
abstract fun eval(scope: Scriptable, coroutineContext: CoroutineContext?): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
abstract suspend fun evalSuspend(scope: Scriptable): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(bindings: Bindings?): Any? {
|
||||
var ctxt = getEngine().context
|
||||
if (bindings != null) {
|
||||
val tempContext = SimpleScriptContext()
|
||||
tempContext.setBindings(bindings, ENGINE_SCOPE)
|
||||
tempContext.setBindings(ctxt.getBindings(GLOBAL_SCOPE), GLOBAL_SCOPE)
|
||||
tempContext.writer = ctxt.writer
|
||||
tempContext.reader = ctxt.reader
|
||||
tempContext.errorWriter = ctxt.errorWriter
|
||||
ctxt = tempContext
|
||||
}
|
||||
return this.eval(ctxt)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(): Any? {
|
||||
return this.eval(getEngine().context)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
interface Invocable {
|
||||
fun <T> getInterface(clazz: Class<T>): T?
|
||||
|
||||
fun <T> getInterface(obj: Any?, paramClass: Class<T>): T?
|
||||
|
||||
@Throws(ScriptException::class, NoSuchMethodException::class)
|
||||
fun invokeFunction(name: String, vararg args: Any): Any?
|
||||
|
||||
@Throws(ScriptException::class, NoSuchMethodException::class)
|
||||
fun invokeMethod(obj: Any?, name: String, vararg args: Any): Any?
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.script
|
||||
|
||||
import org.mozilla.javascript.ContextFactory
|
||||
|
||||
open class RhinoContextFactory : ContextFactory() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
fun put(key: String, value: Any?) {
|
||||
set(key, value)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.script
|
||||
|
||||
import org.mozilla.javascript.Context
|
||||
|
||||
inline fun buildScriptBindings(block: (bindings: ScriptBindings) -> Unit): ScriptBindings {
|
||||
val bindings = ScriptBindings()
|
||||
Context.enter()
|
||||
try {
|
||||
block(bindings)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
return bindings
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
import java.io.Reader
|
||||
import java.io.Writer
|
||||
|
||||
interface ScriptContext {
|
||||
|
||||
var errorWriter: Writer
|
||||
|
||||
var reader: Reader
|
||||
|
||||
val scopes: List<Int>
|
||||
|
||||
var writer: Writer
|
||||
|
||||
fun getAttribute(name: String): Any?
|
||||
|
||||
fun getAttribute(name: String, scope: Int): Any?
|
||||
|
||||
fun getAttributesScope(name: String): Int
|
||||
|
||||
fun getBindings(scope: Int): Bindings?
|
||||
|
||||
fun removeAttribute(name: String, scope: Int): Any?
|
||||
|
||||
fun setAttribute(name: String, value: Any?, scope: Int)
|
||||
|
||||
fun setBindings(bindings: Bindings?, scope: Int)
|
||||
|
||||
companion object {
|
||||
const val ENGINE_SCOPE = 100
|
||||
const val GLOBAL_SCOPE = 200
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import java.io.Reader
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
interface ScriptEngine {
|
||||
var context: ScriptContext
|
||||
|
||||
fun createBindings(): Bindings?
|
||||
|
||||
@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, coroutineContext: CoroutineContext?): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(reader: Reader): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(reader: Reader, bindings: Bindings): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(reader: Reader, context: ScriptContext): Any?
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
fun eval(script: String): Any?
|
||||
|
||||
@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
|
||||
|
||||
operator fun get(key: String): Any?
|
||||
|
||||
fun getBindings(scope: Int): Bindings?
|
||||
|
||||
fun put(key: String, value: Any?)
|
||||
|
||||
fun setBindings(bindings: Bindings?, scope: Int)
|
||||
|
||||
companion object {
|
||||
const val FILENAME = "javax.script.filename"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
class ScriptException : Exception {
|
||||
var columnNumber: Int
|
||||
private set
|
||||
var fileName: String?
|
||||
private set
|
||||
var lineNumber: Int
|
||||
private set
|
||||
|
||||
constructor(s: String?) : super(s) {
|
||||
fileName = null
|
||||
lineNumber = -1
|
||||
columnNumber = -1
|
||||
}
|
||||
|
||||
constructor(e: Exception?) : super(e) {
|
||||
fileName = null
|
||||
lineNumber = -1
|
||||
columnNumber = -1
|
||||
}
|
||||
|
||||
constructor(message: String?, fileName2: String?, lineNumber2: Int) : super(message) {
|
||||
fileName = fileName2
|
||||
lineNumber = lineNumber2
|
||||
columnNumber = -1
|
||||
}
|
||||
|
||||
constructor(message: String?, fileName2: String?, lineNumber2: Int, columnNumber2: Int) : super(
|
||||
message
|
||||
) {
|
||||
fileName = fileName2
|
||||
lineNumber = lineNumber2
|
||||
columnNumber = columnNumber2
|
||||
}
|
||||
|
||||
override val message: String
|
||||
get() {
|
||||
val ret = super.message
|
||||
if (fileName == null) {
|
||||
return ret!!
|
||||
}
|
||||
var ret2 = "$ret in $fileName"
|
||||
if (lineNumber != -1) {
|
||||
ret2 = "$ret2 at line number $lineNumber"
|
||||
}
|
||||
return if (columnNumber != -1) {
|
||||
"$ret2 at column number $columnNumber"
|
||||
} else ret2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
class SimpleBindings @JvmOverloads constructor(
|
||||
private val map: MutableMap<String, Any?> = HashMap()
|
||||
) : Bindings {
|
||||
|
||||
override fun put(key: String, value: Any?): Any? {
|
||||
return map.put(key, value)
|
||||
}
|
||||
|
||||
override fun putAll(from: Map<out String, Any?>) {
|
||||
map.putAll(from)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
map.clear()
|
||||
}
|
||||
|
||||
override fun containsKey(key: String): Boolean {
|
||||
return map.containsKey(key)
|
||||
}
|
||||
|
||||
override fun containsValue(value: Any?): Boolean {
|
||||
return map.containsValue(value)
|
||||
}
|
||||
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<String, Any?>>
|
||||
get() = map.entries
|
||||
|
||||
override operator fun get(key: String): Any? {
|
||||
return map[key]
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return map.isEmpty()
|
||||
}
|
||||
|
||||
override val keys: MutableSet<String>
|
||||
get() = map.keys
|
||||
|
||||
override fun remove(key: String): Any? {
|
||||
return map.remove(key)
|
||||
}
|
||||
|
||||
override val size: Int
|
||||
get() = map.size
|
||||
|
||||
override val values: MutableCollection<Any?>
|
||||
get() = map.values
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Decompiled with CFR 0.152.
|
||||
*/
|
||||
package com.script
|
||||
|
||||
import com.script.ScriptContext.Companion.ENGINE_SCOPE
|
||||
import com.script.ScriptContext.Companion.GLOBAL_SCOPE
|
||||
import java.io.InputStreamReader
|
||||
import java.io.PrintWriter
|
||||
import java.io.Reader
|
||||
import java.io.Writer
|
||||
|
||||
open class SimpleScriptContext(
|
||||
private var engineScope: Bindings = SimpleBindings(),
|
||||
override var errorWriter: Writer = PrintWriter(System.err, true),
|
||||
override var reader: Reader = InputStreamReader(System.`in`),
|
||||
override var writer: Writer = PrintWriter(System.out, true)
|
||||
) : ScriptContext {
|
||||
private var globalScope: Bindings? = null
|
||||
|
||||
override fun setBindings(bindings: Bindings?, scope: Int) {
|
||||
when (scope) {
|
||||
ENGINE_SCOPE -> {
|
||||
if (bindings == null) {
|
||||
throw NullPointerException("Engine scope cannot be null.")
|
||||
}
|
||||
engineScope = bindings
|
||||
return
|
||||
}
|
||||
|
||||
GLOBAL_SCOPE -> {
|
||||
globalScope = bindings
|
||||
return
|
||||
}
|
||||
}
|
||||
throw IllegalArgumentException("Invalid scope value.")
|
||||
}
|
||||
|
||||
override fun getAttribute(name: String): Any? {
|
||||
return if (engineScope.containsKey(name)) {
|
||||
this.getAttribute(name, ENGINE_SCOPE)
|
||||
} else if (globalScope?.containsKey(name) == true) {
|
||||
this.getAttribute(name, GLOBAL_SCOPE)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttribute(name: String, scope: Int): Any? {
|
||||
when (scope) {
|
||||
ENGINE_SCOPE -> {
|
||||
return engineScope[name]
|
||||
}
|
||||
|
||||
GLOBAL_SCOPE -> {
|
||||
return globalScope?.get(name)
|
||||
}
|
||||
}
|
||||
throw IllegalArgumentException("Illegal scope value.")
|
||||
}
|
||||
|
||||
override fun removeAttribute(name: String, scope: Int): Any? {
|
||||
when (scope) {
|
||||
ENGINE_SCOPE -> {
|
||||
return getBindings(ENGINE_SCOPE)?.remove(name)
|
||||
}
|
||||
|
||||
GLOBAL_SCOPE -> {
|
||||
return getBindings(GLOBAL_SCOPE)?.remove(name)
|
||||
}
|
||||
}
|
||||
throw IllegalArgumentException("Illegal scope value.")
|
||||
}
|
||||
|
||||
override fun setAttribute(name: String, value: Any?, scope: Int) {
|
||||
when (scope) {
|
||||
ENGINE_SCOPE -> engineScope[name] = value
|
||||
GLOBAL_SCOPE -> globalScope?.put(name, value)
|
||||
else -> throw IllegalArgumentException("Illegal scope value.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttributesScope(name: String): Int {
|
||||
return if (engineScope.containsKey(name)) {
|
||||
ENGINE_SCOPE
|
||||
} else if (globalScope?.containsKey(name) == true) {
|
||||
GLOBAL_SCOPE
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBindings(scope: Int): Bindings? {
|
||||
if (scope == ENGINE_SCOPE) {
|
||||
return engineScope
|
||||
}
|
||||
if (scope == GLOBAL_SCOPE) {
|
||||
return globalScope
|
||||
}
|
||||
throw IllegalArgumentException("Illegal scope value.")
|
||||
}
|
||||
|
||||
override val scopes: List<Int>
|
||||
get() = Companion.scopes
|
||||
|
||||
companion object {
|
||||
private val scopes = listOf(ENGINE_SCOPE, GLOBAL_SCOPE)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.ScriptContext
|
||||
import org.mozilla.javascript.*
|
||||
import org.mozilla.javascript.Function
|
||||
|
||||
/**
|
||||
* ExternalScriptable is an implementation of Scriptable
|
||||
* backed by a JSR 223 ScriptContext instance.
|
||||
*
|
||||
* @author Mike Grogan
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
internal class ExternalScriptable @JvmOverloads constructor(
|
||||
val context: ScriptContext,
|
||||
private val indexedProps: MutableMap<Any, Any?> = HashMap()
|
||||
) : Scriptable {
|
||||
private var prototype: Scriptable? = null
|
||||
private var parent: Scriptable? = null
|
||||
|
||||
private fun isEmpty(name: String): Boolean {
|
||||
return name == ""
|
||||
}
|
||||
|
||||
override fun getClassName(): String {
|
||||
return "Global"
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun get(name: String, start: Scriptable): Any? {
|
||||
return if (this.isEmpty(name)) {
|
||||
indexedProps.getOrElse(name) { Scriptable.NOT_FOUND }
|
||||
} else {
|
||||
synchronized(context) {
|
||||
val scope = context.getAttributesScope(name)
|
||||
return if (scope != -1) {
|
||||
val value = context.getAttribute(name, scope)
|
||||
Context.javaToJS(value, this)
|
||||
} else {
|
||||
Scriptable.NOT_FOUND
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun get(index: Int, start: Scriptable): Any? {
|
||||
return indexedProps.getOrElse(index) { Scriptable.NOT_FOUND }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun has(name: String, start: Scriptable): Boolean {
|
||||
return if (this.isEmpty(name)) {
|
||||
indexedProps.containsKey(name)
|
||||
} else {
|
||||
synchronized(context) { return context.getAttributesScope(name) != -1 }
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun has(index: Int, start: Scriptable): Boolean {
|
||||
return indexedProps.containsKey(index)
|
||||
}
|
||||
|
||||
override fun put(name: String, start: Scriptable, value: Any?) {
|
||||
if (start === this) {
|
||||
synchronized(this) {
|
||||
if (this.isEmpty(name)) {
|
||||
indexedProps.put(name, value)
|
||||
} else {
|
||||
synchronized(context) {
|
||||
var scope = context.getAttributesScope(name)
|
||||
if (scope == -1) {
|
||||
scope = 100
|
||||
}
|
||||
context.setAttribute(name, jsToJava(value), scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
start.put(name, start, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun put(index: Int, start: Scriptable, value: Any?) {
|
||||
if (start === this) {
|
||||
synchronized(this) { indexedProps.put(index, value) }
|
||||
} else {
|
||||
start.put(index, start, value)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun delete(name: String) {
|
||||
if (this.isEmpty(name)) {
|
||||
indexedProps.remove(name)
|
||||
} else {
|
||||
synchronized(context) {
|
||||
val scope = context.getAttributesScope(name)
|
||||
if (scope != -1) {
|
||||
context.removeAttribute(name, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(index: Int) {
|
||||
indexedProps.remove(index)
|
||||
}
|
||||
|
||||
override fun getPrototype(): Scriptable? {
|
||||
return prototype
|
||||
}
|
||||
|
||||
override fun setPrototype(prototype: Scriptable?) {
|
||||
this.prototype = prototype
|
||||
}
|
||||
|
||||
override fun getParentScope(): Scriptable? {
|
||||
return parent
|
||||
}
|
||||
|
||||
override fun setParentScope(parent: Scriptable?) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun getIds(): Array<Any> {
|
||||
val keys = allKeys
|
||||
val size = keys.size + indexedProps.size
|
||||
val res = arrayOfNulls<Any>(size)
|
||||
System.arraycopy(keys, 0, res, 0, keys.size)
|
||||
var i = keys.size
|
||||
var index: Any
|
||||
val var5: Iterator<*> = indexedProps.keys.iterator()
|
||||
while (var5.hasNext()) {
|
||||
index = var5.next()!!
|
||||
res[i++] = index
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return res as Array<Any>
|
||||
}
|
||||
|
||||
override fun getDefaultValue(typeHint: Class<*>?): Any {
|
||||
for (i in 0..1) {
|
||||
val tryToString: Boolean =
|
||||
if (typeHint == ScriptRuntime.StringClass) {
|
||||
i == 0
|
||||
} else {
|
||||
i == 1
|
||||
}
|
||||
var methodName: String
|
||||
var args: Array<Any?>
|
||||
if (tryToString) {
|
||||
methodName = "toString"
|
||||
args = ScriptRuntime.emptyArgs
|
||||
} else {
|
||||
methodName = "valueOf"
|
||||
args = arrayOfNulls(1)
|
||||
val hint: String = when {
|
||||
typeHint == null -> {
|
||||
"undefined"
|
||||
}
|
||||
typeHint == ScriptRuntime.StringClass -> {
|
||||
"string"
|
||||
}
|
||||
typeHint == ScriptRuntime.ScriptableClass -> {
|
||||
"object"
|
||||
}
|
||||
typeHint == ScriptRuntime.FunctionClass -> {
|
||||
"function"
|
||||
}
|
||||
typeHint != ScriptRuntime.BooleanClass && typeHint != java.lang.Boolean.TYPE -> {
|
||||
if (typeHint != ScriptRuntime.NumberClass
|
||||
&& typeHint != ScriptRuntime.ByteClass
|
||||
&& typeHint != java.lang.Byte.TYPE
|
||||
&& typeHint != ScriptRuntime.ShortClass
|
||||
&& typeHint != java.lang.Short.TYPE
|
||||
&& typeHint != ScriptRuntime.IntegerClass
|
||||
&& typeHint != Integer.TYPE
|
||||
&& typeHint != ScriptRuntime.FloatClass
|
||||
&& typeHint != java.lang.Float.TYPE
|
||||
&& typeHint != ScriptRuntime.DoubleClass
|
||||
&& typeHint != java.lang.Double.TYPE
|
||||
) {
|
||||
throw Context.reportRuntimeError("Invalid JavaScript value of type $typeHint")
|
||||
}
|
||||
"number"
|
||||
}
|
||||
else -> {
|
||||
"boolean"
|
||||
}
|
||||
}
|
||||
args[0] = hint
|
||||
}
|
||||
var v = ScriptableObject.getProperty(this, methodName)
|
||||
if (v is Function) {
|
||||
val function = v
|
||||
val cx = Context.enter()
|
||||
v = try {
|
||||
function.call(cx, function.parentScope, this, args)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
if (v != null) {
|
||||
if (v !is Scriptable) {
|
||||
return v
|
||||
}
|
||||
if (typeHint == ScriptRuntime.ScriptableClass || typeHint == ScriptRuntime.FunctionClass) {
|
||||
return v
|
||||
}
|
||||
if (tryToString && v is Wrapper) {
|
||||
val u = (v as Wrapper).unwrap()
|
||||
if (u is String) {
|
||||
return u
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val arg = if (typeHint == null) "undefined" else typeHint.name
|
||||
throw Context.reportRuntimeError("找不到对象的默认值 $arg")
|
||||
}
|
||||
|
||||
override fun hasInstance(instance: Scriptable): Boolean {
|
||||
var proto = instance.prototype
|
||||
while (proto != null) {
|
||||
if (proto == this) {
|
||||
return true
|
||||
}
|
||||
proto = proto.prototype
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private val allKeys: Array<String>
|
||||
get() {
|
||||
val list = ArrayList<String>()
|
||||
synchronized(context) {
|
||||
val iterator: Iterator<*> = context.scopes.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val scope = iterator.next() as Int
|
||||
val bindings = context.getBindings(scope)
|
||||
if (bindings != null) {
|
||||
list.ensureCapacity(bindings.size)
|
||||
val iterator1: Iterator<*> = bindings.keys.iterator()
|
||||
while (iterator1.hasNext()) {
|
||||
val key = iterator1.next() as String
|
||||
list.add(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list.toTypedArray()
|
||||
}
|
||||
|
||||
private fun jsToJava(jsObj: Any?): Any? {
|
||||
return if (jsObj is Wrapper) {
|
||||
if (jsObj is NativeJavaClass) {
|
||||
jsObj
|
||||
} else {
|
||||
val obj = jsObj.unwrap()
|
||||
if (obj !is Number && obj !is String && obj !is Boolean && obj !is Char) obj else jsObj
|
||||
}
|
||||
} else {
|
||||
jsObj
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.Invocable
|
||||
import com.script.ScriptException
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
import java.security.AccessControlContext
|
||||
import java.security.AccessController
|
||||
import java.security.PrivilegedExceptionAction
|
||||
|
||||
/**
|
||||
* java.lang.reflect.Proxy based interface implementor. This is meant
|
||||
* to be used to implement Invocable.getInterface.
|
||||
*
|
||||
* @author Mike Grogan
|
||||
* @since 1.6
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
open class InterfaceImplementor(private val engine: Invocable) {
|
||||
@Throws(ScriptException::class)
|
||||
fun <T> getInterface(obj: Any?, clazz: Class<T>?): T? {
|
||||
return if (clazz != null && clazz.isInterface) {
|
||||
if (!isImplemented(obj, clazz)) {
|
||||
null
|
||||
} else {
|
||||
val accContext = AccessController.getContext()
|
||||
clazz.cast(
|
||||
Proxy.newProxyInstance(
|
||||
clazz.classLoader,
|
||||
arrayOf<Class<*>>(clazz),
|
||||
InterfaceImplementorInvocationHandler(obj, accContext)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw IllegalArgumentException("interface Class expected")
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun isImplemented(obj: Any?, clazz: Class<*>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
protected open fun convertResult(method: Method?, res: Any?): Any? {
|
||||
return res
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
protected fun convertArguments(method: Method?, args: Array<Any>): Array<Any> {
|
||||
return args
|
||||
}
|
||||
|
||||
private inner class InterfaceImplementorInvocationHandler(
|
||||
private val obj: Any?,
|
||||
private val accContext: AccessControlContext
|
||||
) : InvocationHandler {
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun invoke(proxy: Any, method: Method, args: Array<Any>): Any? {
|
||||
val finalArgs = convertArguments(method, args)
|
||||
val result = AccessController.doPrivileged(PrivilegedExceptionAction {
|
||||
if (obj == null) engine.invokeFunction(method.name, *finalArgs)
|
||||
else engine.invokeMethod(obj, method.name, *finalArgs)
|
||||
}, accContext)
|
||||
return convertResult(method, result)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.*
|
||||
import org.mozilla.javascript.Function
|
||||
|
||||
/**
|
||||
* JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter
|
||||
* calls specially named JavaScript methods on an adaptee object when property
|
||||
* access is attempted on it.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var y = {
|
||||
* __get__ : function (name) { ... }
|
||||
* __has__ : function (name) { ... }
|
||||
* __put__ : function (name, value) {...}
|
||||
* __delete__ : function (name) { ... }
|
||||
* __getIds__ : function () { ... }
|
||||
* };
|
||||
*
|
||||
* var x = new JSAdapter(y);
|
||||
*
|
||||
* x.i; // calls y.__get__
|
||||
* i in x; // calls y.__has__
|
||||
* x.p = 10; // calls y.__put__
|
||||
* delete x.p; // calls y.__delete__
|
||||
* for (i in x) { print(i); } // calls y.__getIds__
|
||||
*
|
||||
* If a special JavaScript method is not found in the adaptee, then JSAdapter
|
||||
* forwards the property access to the adaptee itself.
|
||||
*
|
||||
* JavaScript caller of adapter object is isolated from the fact that
|
||||
* the property access/mutation/deletion are really calls to
|
||||
* JavaScript methods on adaptee. Use cases include 'smart'
|
||||
* properties, property access tracing/debugging, encaptulation with
|
||||
* easy client access - in short JavaScript becomes more "Self" like.
|
||||
*
|
||||
* Note that Rhino already supports special properties like __proto__
|
||||
* (to set, get prototype), __parent__ (to set, get parent scope). We
|
||||
* follow the same double underscore nameing convention here. Similarly
|
||||
* the name JSAdapter is derived from JavaAdapter -- which is a facility
|
||||
* to extend, implement Java classes/interfaces by JavaScript.
|
||||
*
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
class JSAdapter private constructor(var adaptee: Scriptable) : Scriptable, Function {
|
||||
private var prototype: Scriptable? = null
|
||||
private var parent: Scriptable? = null
|
||||
private var isPrototype = false
|
||||
|
||||
override fun getClassName(): String {
|
||||
return "JSAdapter"
|
||||
}
|
||||
|
||||
override fun get(name: String, start: Scriptable): Any {
|
||||
val func = getAdapteeFunction(GET_PROP)
|
||||
return if (func != null) {
|
||||
this.call(func, arrayOf(name))
|
||||
} else {
|
||||
adaptee[name, adaptee]
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(index: Int, start: Scriptable): Any {
|
||||
val func = getAdapteeFunction(GET_PROP)
|
||||
return if (func != null) {
|
||||
this.call(func, arrayOf(index))
|
||||
} else {
|
||||
adaptee[index, adaptee]
|
||||
}
|
||||
}
|
||||
|
||||
override fun has(name: String, start: Scriptable): Boolean {
|
||||
val func = getAdapteeFunction(HAS_PROP)
|
||||
return if (func != null) {
|
||||
val res = this.call(func, arrayOf(name))
|
||||
Context.toBoolean(res)
|
||||
} else {
|
||||
adaptee.has(name, adaptee)
|
||||
}
|
||||
}
|
||||
|
||||
override fun has(index: Int, start: Scriptable): Boolean {
|
||||
val func = getAdapteeFunction(HAS_PROP)
|
||||
return if (func != null) {
|
||||
val res = this.call(func, arrayOf(index))
|
||||
Context.toBoolean(res)
|
||||
} else {
|
||||
adaptee.has(index, adaptee)
|
||||
}
|
||||
}
|
||||
|
||||
override fun put(name: String, start: Scriptable, value: Any) {
|
||||
if (start === this) {
|
||||
val func = getAdapteeFunction(PUT_PROP)
|
||||
if (func != null) {
|
||||
this.call(func, arrayOf(name, value))
|
||||
} else {
|
||||
adaptee.put(name, adaptee, value)
|
||||
}
|
||||
} else {
|
||||
start.put(name, start, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun put(index: Int, start: Scriptable, value: Any) {
|
||||
if (start === this) {
|
||||
val func = getAdapteeFunction(PUT_PROP)
|
||||
if (func != null) {
|
||||
this.call(func, arrayOf(index, value))
|
||||
} else {
|
||||
adaptee.put(index, adaptee, value)
|
||||
}
|
||||
} else {
|
||||
start.put(index, start, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(name: String) {
|
||||
val func = getAdapteeFunction(DEL_PROP)
|
||||
if (func != null) {
|
||||
this.call(func, arrayOf(name))
|
||||
} else {
|
||||
adaptee.delete(name)
|
||||
}
|
||||
}
|
||||
|
||||
override fun delete(index: Int) {
|
||||
val func = getAdapteeFunction(DEL_PROP)
|
||||
if (func != null) {
|
||||
this.call(func, arrayOf(index))
|
||||
} else {
|
||||
adaptee.delete(index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPrototype(): Scriptable? {
|
||||
return prototype
|
||||
}
|
||||
|
||||
override fun setPrototype(prototype: Scriptable?) {
|
||||
this.prototype = prototype
|
||||
}
|
||||
|
||||
override fun getParentScope(): Scriptable? {
|
||||
return parent
|
||||
}
|
||||
|
||||
override fun setParentScope(parent: Scriptable?) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun getIds(): Array<Any?> {
|
||||
val func = getAdapteeFunction(GET_PROPIDS)
|
||||
return if (func == null) {
|
||||
adaptee.ids
|
||||
} else {
|
||||
val val1 = this.call(func, arrayOfNulls(0))
|
||||
val res: Array<Any?>
|
||||
when (val1) {
|
||||
is NativeArray -> {
|
||||
res = arrayOfNulls(val1.length.toInt())
|
||||
for (index in res.indices) {
|
||||
res[index] = mapToId(val1[index, val1])
|
||||
}
|
||||
res
|
||||
}
|
||||
!is NativeJavaArray -> {
|
||||
Context.emptyArgs
|
||||
}
|
||||
else -> {
|
||||
val tmp = val1.unwrap()
|
||||
if (tmp.javaClass == Array<Any>::class.java) {
|
||||
val array = tmp as Array<*>
|
||||
res = arrayOfNulls(array.size)
|
||||
for (index in array.indices) {
|
||||
res[index] = mapToId(array[index])
|
||||
}
|
||||
} else {
|
||||
res = Context.emptyArgs
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasInstance(scriptable: Scriptable): Boolean {
|
||||
return if (scriptable is JSAdapter) {
|
||||
true
|
||||
} else {
|
||||
var proto = scriptable.prototype
|
||||
while (proto != null) {
|
||||
if (proto == this) {
|
||||
return true
|
||||
}
|
||||
proto = proto.prototype
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDefaultValue(hint: Class<*>?): Any {
|
||||
return adaptee.getDefaultValue(hint)
|
||||
}
|
||||
|
||||
@Throws(RhinoException::class)
|
||||
override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable, args: Array<Any>): Any {
|
||||
return if (isPrototype) {
|
||||
construct(cx, scope, args)
|
||||
} else {
|
||||
val tmp = adaptee
|
||||
if (tmp is Function) {
|
||||
tmp.call(cx, scope, tmp, args)
|
||||
} else {
|
||||
throw Context.reportRuntimeError("TypeError: not a function")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(RhinoException::class)
|
||||
override fun construct(cx: Context, scope: Scriptable, args: Array<Any>): Scriptable {
|
||||
val tmp: Scriptable?
|
||||
return if (isPrototype) {
|
||||
tmp = ScriptableObject.getTopLevelScope(scope)
|
||||
if (args.isNotEmpty()) {
|
||||
JSAdapter(Context.toObject(args[0], tmp))
|
||||
} else {
|
||||
throw Context.reportRuntimeError("JSAdapter requires adaptee")
|
||||
}
|
||||
} else {
|
||||
tmp = adaptee
|
||||
if (tmp is Function) {
|
||||
tmp.construct(cx, scope, args)
|
||||
} else {
|
||||
throw Context.reportRuntimeError("TypeError: not a constructor")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapToId(tmp: Any?): Any {
|
||||
return if (tmp is Double) tmp.toInt() else Context.toString(tmp)
|
||||
}
|
||||
|
||||
private fun getAdapteeFunction(name: String): Function? {
|
||||
val o = ScriptableObject.getProperty(adaptee, name)
|
||||
return o as? Function
|
||||
}
|
||||
|
||||
private fun call(func: Function, args: Array<Any?>): Any {
|
||||
val cx = Context.getCurrentContext()
|
||||
val thisObj = adaptee
|
||||
val scope = func.parentScope
|
||||
return try {
|
||||
func.call(cx, scope, thisObj, args)
|
||||
} catch (re: RhinoException) {
|
||||
throw Context.reportRuntimeError(re.message)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val GET_PROP = "__get__"
|
||||
private const val HAS_PROP = "__has__"
|
||||
private const val PUT_PROP = "__put__"
|
||||
private const val DEL_PROP = "__delete__"
|
||||
private const val GET_PROPIDS = "__getIds__"
|
||||
|
||||
@Throws(RhinoException::class)
|
||||
fun init(cx: Context, scope: Scriptable, sealed: Boolean) {
|
||||
val obj = JSAdapter(cx.newObject(scope))
|
||||
obj.parentScope = scope
|
||||
obj.setPrototype(getFunctionPrototype(scope))
|
||||
obj.isPrototype = true
|
||||
ScriptableObject.defineProperty(scope, "JSAdapter", obj, 2)
|
||||
}
|
||||
|
||||
private fun getFunctionPrototype(scope: Scriptable): Scriptable {
|
||||
return ScriptableObject.getFunctionPrototype(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.Invocable
|
||||
import org.mozilla.javascript.*
|
||||
import org.mozilla.javascript.Function
|
||||
|
||||
/**
|
||||
* This class implements Rhino-like JavaAdapter to help implement a Java
|
||||
* interface in JavaScript. We support this using Invocable.getInterface.
|
||||
* Using this JavaAdapter, script author could write:
|
||||
*
|
||||
*
|
||||
* var r = new java.lang.Runnable() {
|
||||
* run: function() { script... }
|
||||
* };
|
||||
*
|
||||
*
|
||||
* r.run();
|
||||
* new java.lang.Thread(r).start();
|
||||
*
|
||||
*
|
||||
* Note that Rhino's JavaAdapter support allows extending a Java class and/or
|
||||
* implementing one or more interfaces. This JavaAdapter implementation does
|
||||
* not support these.
|
||||
*
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal class JavaAdapter private constructor(private val engine: Invocable) : ScriptableObject(),
|
||||
Function {
|
||||
override fun getClassName(): String {
|
||||
return "JavaAdapter"
|
||||
}
|
||||
|
||||
@Throws(RhinoException::class)
|
||||
override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable, args: Array<Any>): Any {
|
||||
return construct(cx, scope, args)
|
||||
}
|
||||
|
||||
@Throws(RhinoException::class)
|
||||
override fun construct(cx: Context, scope: Scriptable, args: Array<Any>): Scriptable {
|
||||
return if (args.size == 2) {
|
||||
var clazz: Class<*>? = null
|
||||
val obj = args[0]
|
||||
if (obj is Wrapper) {
|
||||
val o = obj.unwrap()
|
||||
if (o is Class<*> && o.isInterface) {
|
||||
clazz = o
|
||||
}
|
||||
} else if (obj is Class<*> && obj.isInterface) {
|
||||
clazz = obj
|
||||
}
|
||||
if (clazz == null) {
|
||||
throw Context.reportRuntimeError("JavaAdapter: first arg should be interface Class")
|
||||
} else {
|
||||
val topLevel = getTopLevelScope(scope)
|
||||
Context.toObject(
|
||||
engine.getInterface(args[1], clazz),
|
||||
topLevel
|
||||
)
|
||||
}
|
||||
} else {
|
||||
throw Context.reportRuntimeError("JavaAdapter requires two arguments")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Throws(RhinoException::class)
|
||||
fun init(cx: Context, scope: Scriptable, sealed: Boolean) {
|
||||
val topLevel = scope as RhinoTopLevel
|
||||
val engine: Invocable = topLevel.scriptEngine
|
||||
val obj = JavaAdapter(engine)
|
||||
obj.parentScope = scope
|
||||
obj.prototype = getFunctionPrototype(scope)
|
||||
putProperty(topLevel, "JavaAdapter", obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.Scriptable
|
||||
|
||||
fun interface JavaObjectWrapFactory {
|
||||
|
||||
fun wrap(scope: Scriptable?, javaObject: Any, staticType: Class<*>?): Scriptable
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.NativeJavaClass
|
||||
import org.mozilla.javascript.Scriptable
|
||||
|
||||
class ProtectedNativeJavaClass(
|
||||
scope: Scriptable,
|
||||
javaClass: Class<*>,
|
||||
private val protectedName: Set<String> = emptySet()
|
||||
) : NativeJavaClass(scope, javaClass) {
|
||||
|
||||
override fun has(
|
||||
name: String,
|
||||
start: Scriptable?
|
||||
): Boolean {
|
||||
if (protectedName.contains(name)) {
|
||||
return false
|
||||
}
|
||||
return super.has(name, start)
|
||||
}
|
||||
|
||||
override fun get(name: String, start: Scriptable?): Any? {
|
||||
if (protectedName.contains(name)) {
|
||||
return NOT_FOUND
|
||||
}
|
||||
return super.get(name, start)
|
||||
}
|
||||
|
||||
override fun put(
|
||||
name: String,
|
||||
start: Scriptable?,
|
||||
value: Any?
|
||||
) {
|
||||
if (protectedName.contains(name)) {
|
||||
return
|
||||
}
|
||||
super.put(name, start, value)
|
||||
}
|
||||
|
||||
override fun unwrap(): Any? {
|
||||
return javaObject.toString()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.NativeJavaObject
|
||||
import org.mozilla.javascript.Scriptable
|
||||
|
||||
class ReadOnlyJavaObject(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) :
|
||||
NativeJavaObject(scope, javaObject, staticType) {
|
||||
|
||||
override fun has(name: String, start: Scriptable): Boolean {
|
||||
if (name.length > 3 && name.startsWith("set")) {
|
||||
val name = name.substring(3).replaceFirstChar { it.lowercase() }
|
||||
if (super.has(name, start)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return super.has(name, start)
|
||||
}
|
||||
|
||||
override fun get(name: String, start: Scriptable): Any? {
|
||||
if (name.length > 3 && name.startsWith("set")) {
|
||||
val name = name.substring(3).replaceFirstChar { it.lowercase() }
|
||||
if (super.has(name, start)) {
|
||||
return NOT_FOUND
|
||||
}
|
||||
}
|
||||
return super.get(name, start)
|
||||
}
|
||||
|
||||
override fun put(
|
||||
name: String?,
|
||||
start: Scriptable?,
|
||||
value: Any?
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
companion object {
|
||||
val factory = JavaObjectWrapFactory { scope, javaObject, staticType ->
|
||||
ReadOnlyJavaObject(scope, javaObject, staticType)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import android.os.Build
|
||||
import org.mozilla.javascript.ClassShutter
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.lang.reflect.Member
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Path
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
* This class prevents script access to certain sensitive classes.
|
||||
* Note that this class checks over and above SecurityManager. i.e., although
|
||||
* a SecurityManager would pass, class shutter may still prevent access.
|
||||
*
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
object RhinoClassShutter : ClassShutter {
|
||||
|
||||
private val protectedClassNamesMatcher by lazy {
|
||||
listOf(
|
||||
"java.lang.Class",
|
||||
"java.lang.ClassLoader",
|
||||
"java.net.URLClassLoader",
|
||||
"java.lang.Runtime",
|
||||
"java.lang.ProcessBuilder",
|
||||
"java.lang.ProcessImpl",
|
||||
"java.lang.UNIXProcess",
|
||||
"java.io.File",
|
||||
"java.io.FileDescriptor",
|
||||
"java.io.FileInputStream",
|
||||
"java.io.FileOutputStream",
|
||||
"java.io.PrintStream",
|
||||
"java.io.FileReader",
|
||||
"java.io.FileWriter",
|
||||
"java.io.PrintWriter",
|
||||
"java.io.UnixFileSystem",
|
||||
"java.io.RandomAccessFile",
|
||||
"java.io.ObjectInputStream",
|
||||
"java.io.ObjectOutputStream",
|
||||
"java.security.AccessController",
|
||||
"java.nio.file.Paths",
|
||||
"java.nio.file.Files",
|
||||
"java.nio.file.FileSystems",
|
||||
"java.util.Formatter",
|
||||
"sun.misc.Unsafe",
|
||||
"android.content.Intent",
|
||||
"android.provider.Settings",
|
||||
"android.app.ActivityThread",
|
||||
"android.app.AppGlobals",
|
||||
"android.os.Looper",
|
||||
"android.os.Process",
|
||||
"android.os.FileUtils",
|
||||
|
||||
"cn.hutool.core.lang.JarClassLoader",
|
||||
"cn.hutool.core.lang.Singleton",
|
||||
"cn.hutool.core.util.RuntimeUtil",
|
||||
"cn.hutool.core.util.ClassLoaderUtil",
|
||||
"cn.hutool.core.util.ReflectUtil",
|
||||
"cn.hutool.core.util.SerializeUtil",
|
||||
"cn.hutool.core.util.ClassUtil",
|
||||
"org.mozilla.javascript.DefiningClassLoader",
|
||||
"io.legado.app.data.AppDatabase",
|
||||
"io.legado.app.data.AppDatabase_Impl",
|
||||
"io.legado.app.data.AppDatabaseKt",
|
||||
"io.legado.app.utils.ContextExtensionsKt",
|
||||
"androidx.core.content.FileProvider",
|
||||
"splitties.init.AppCtxKt",
|
||||
"okio.JvmSystemFileSystem",
|
||||
"okio.JvmFileHandle",
|
||||
"okio.NioSystemFileSystem",
|
||||
"okio.NioFileSystemFileHandle",
|
||||
"okio.Path",
|
||||
|
||||
"android.system",
|
||||
"android.database",
|
||||
"androidx.sqlite.db",
|
||||
"androidx.room",
|
||||
"cn.hutool.core.io",
|
||||
"cn.hutool.core.bean",
|
||||
"cn.hutool.core.lang.reflect",
|
||||
"dalvik.system",
|
||||
"java.nio.file",
|
||||
"java.lang.reflect",
|
||||
"java.lang.invoke",
|
||||
"io.legado.app.data.dao",
|
||||
"com.script",
|
||||
"org.mozilla",
|
||||
"sun",
|
||||
"libcore",
|
||||
).let { ClassNameMatcher(it) }
|
||||
}
|
||||
|
||||
private val systemClassProtectedName by lazy {
|
||||
Collections.unmodifiableSet(hashSetOf("load", "loadLibrary", "exit"))
|
||||
}
|
||||
|
||||
private val protectedClasses by lazy {
|
||||
arrayOf(
|
||||
ClassLoader::class.java,
|
||||
Class::class.java,
|
||||
Member::class.java,
|
||||
Context::class.java,
|
||||
ObjectInputStream::class.java,
|
||||
ObjectOutputStream::class.java,
|
||||
okio.FileSystem::class.java,
|
||||
okio.FileHandle::class.java,
|
||||
okio.Path::class.java,
|
||||
android.content.Context::class.java,
|
||||
) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
arrayOf(FileSystem::class.java, Path::class.java)
|
||||
} else {
|
||||
emptyArray()
|
||||
}
|
||||
}
|
||||
|
||||
fun visibleToScripts(obj: Any): Boolean {
|
||||
when (obj) {
|
||||
is ClassLoader,
|
||||
is Class<*>,
|
||||
is Member,
|
||||
is Context,
|
||||
is ObjectInputStream,
|
||||
is ObjectOutputStream,
|
||||
is okio.FileSystem,
|
||||
is okio.FileHandle,
|
||||
is okio.Path,
|
||||
is android.content.Context -> return false
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
when (obj) {
|
||||
is FileSystem,
|
||||
is Path -> return false
|
||||
}
|
||||
}
|
||||
return visibleToScripts(obj.javaClass.name)
|
||||
}
|
||||
|
||||
fun visibleToScripts(clazz: Class<*>): Boolean {
|
||||
protectedClasses.forEach {
|
||||
if (it.isAssignableFrom(clazz)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun wrapJavaClass(scope: Scriptable, javaClass: Class<*>): Scriptable {
|
||||
return when (javaClass) {
|
||||
System::class.java -> {
|
||||
ProtectedNativeJavaClass(scope, javaClass, systemClassProtectedName)
|
||||
}
|
||||
|
||||
else -> ProtectedNativeJavaClass(scope, javaClass)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visibleToScripts(fullClassName: String): Boolean {
|
||||
return !protectedClassNamesMatcher.match(fullClassName)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.CompiledScript
|
||||
import com.script.ScriptEngine
|
||||
import com.script.ScriptException
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.asContextElement
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.ContinuationPending
|
||||
import org.mozilla.javascript.JavaScriptException
|
||||
import org.mozilla.javascript.RhinoException
|
||||
import org.mozilla.javascript.Script
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import java.io.IOException
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
/**
|
||||
* Represents compiled JavaScript code.
|
||||
*
|
||||
* @author Mike Grogan
|
||||
* @since 1.6
|
||||
*/
|
||||
internal class RhinoCompiledScript(
|
||||
private val engine: RhinoScriptEngine,
|
||||
private val script: Script
|
||||
) : CompiledScript() {
|
||||
|
||||
override fun getEngine(): ScriptEngine {
|
||||
return engine
|
||||
}
|
||||
|
||||
override fun eval(scope: Scriptable, coroutineContext: CoroutineContext?): Any? {
|
||||
val cx = Context.enter() as RhinoContext
|
||||
val previousCoroutineContext = cx.coroutineContext
|
||||
if (coroutineContext != null && coroutineContext[Job] != null) {
|
||||
cx.coroutineContext = coroutineContext
|
||||
}
|
||||
cx.allowScriptRun = true
|
||||
cx.recursiveCount++
|
||||
val result: Any?
|
||||
try {
|
||||
cx.checkRecursive()
|
||||
val ret = script.exec(cx, scope)
|
||||
result = engine.unwrapReturnValue(ret)
|
||||
} 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
|
||||
} finally {
|
||||
cx.coroutineContext = previousCoroutineContext
|
||||
cx.allowScriptRun = false
|
||||
cx.recursiveCount--
|
||||
Context.exit()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override suspend fun evalSuspend(scope: Scriptable): Any? {
|
||||
val cx = Context.enter() as RhinoContext
|
||||
var ret: Any?
|
||||
withContext(VMBridgeReflect.contextLocal.asContextElement()) {
|
||||
cx.allowScriptRun = true
|
||||
cx.recursiveCount++
|
||||
try {
|
||||
cx.checkRecursive()
|
||||
try {
|
||||
ret = cx.executeScriptWithContinuations(script, scope)
|
||||
} catch (e: ContinuationPending) {
|
||||
var pending = e
|
||||
while (true) {
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val suspendFunction = pending.applicationState as suspend () -> Any?
|
||||
val functionResult = suspendFunction()
|
||||
val continuation = pending.continuation
|
||||
ret = cx.resumeContinuation(continuation, scope, functionResult)
|
||||
break
|
||||
} catch (e: ContinuationPending) {
|
||||
pending = e
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
cx.allowScriptRun = false
|
||||
cx.recursiveCount--
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
return engine.unwrapReturnValue(ret)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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
|
||||
var allowScriptRun = false
|
||||
var recursiveCount = 0
|
||||
|
||||
@Throws(RhinoInterruptError::class)
|
||||
fun ensureActive() {
|
||||
try {
|
||||
coroutineContext?.ensureActive()
|
||||
} catch (e: CancellationException) {
|
||||
throw RhinoInterruptError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(RhinoRecursionError::class)
|
||||
fun checkRecursive() {
|
||||
if (recursiveCount >= 10) {
|
||||
throw RhinoRecursionError()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.script.rhino
|
||||
|
||||
class RhinoInterruptError(override val cause: Throwable) : Error()
|
||||
|
||||
class RhinoRecursionError(): Error("Maximum recursion depth exceeded.")
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.script.rhino
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import org.mozilla.javascript.Context
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.ContinuationInterceptor
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
val rhinoContext: RhinoContext
|
||||
get() = Context.getCurrentContext() as RhinoContext
|
||||
|
||||
val rhinoContextOrNull: RhinoContext?
|
||||
get() = Context.getCurrentContext() as? RhinoContext
|
||||
|
||||
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun <T> suspendContinuation(crossinline block: suspend CoroutineScope.() -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
val cx = Context.enter()
|
||||
try {
|
||||
val pending = cx.captureContinuation()
|
||||
pending.applicationState = suspend {
|
||||
supervisorScope {
|
||||
block()
|
||||
}
|
||||
}
|
||||
throw pending
|
||||
} catch (e: IllegalStateException) {
|
||||
return runBlocking { block() }
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> runScriptWithContext(context: CoroutineContext, block: () -> T): T {
|
||||
RhinoScriptEngine
|
||||
val rhinoContext = Context.enter() as RhinoContext
|
||||
val previousCoroutineContext = rhinoContext.coroutineContext
|
||||
rhinoContext.coroutineContext = context.minusKey(ContinuationInterceptor)
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
rhinoContext.coroutineContext = previousCoroutineContext
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <T> runScriptWithContext(block: () -> T): T {
|
||||
val rhinoContext = Context.enter() as RhinoContext
|
||||
val previousCoroutineContext = rhinoContext.coroutineContext
|
||||
rhinoContext.coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
rhinoContext.coroutineContext = previousCoroutineContext
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.AbstractScriptEngine
|
||||
import com.script.Bindings
|
||||
import com.script.Compilable
|
||||
import com.script.CompiledScript
|
||||
import com.script.Invocable
|
||||
import com.script.ScriptBindings
|
||||
import com.script.ScriptContext
|
||||
import com.script.ScriptException
|
||||
import com.script.SimpleBindings
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.asContextElement
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mozilla.javascript.Callable
|
||||
import org.mozilla.javascript.ConsString
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.ContextFactory
|
||||
import org.mozilla.javascript.ContinuationPending
|
||||
import org.mozilla.javascript.Function
|
||||
import org.mozilla.javascript.JavaScriptException
|
||||
import org.mozilla.javascript.RhinoException
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
import org.mozilla.javascript.Undefined
|
||||
import org.mozilla.javascript.Wrapper
|
||||
import java.io.IOException
|
||||
import java.io.Reader
|
||||
import java.io.StringReader
|
||||
import java.lang.reflect.Method
|
||||
import java.security.AccessControlContext
|
||||
import java.security.AccessControlException
|
||||
import java.security.AccessController
|
||||
import java.security.AllPermission
|
||||
import java.security.PrivilegedAction
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
/**
|
||||
* Implementation of `ScriptEngine` using the Mozilla Rhino
|
||||
* interpreter.
|
||||
*
|
||||
* @author Mike Grogan
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
object RhinoScriptEngine : AbstractScriptEngine(), Invocable, Compilable {
|
||||
var accessContext: AccessControlContext? = null
|
||||
private var topLevel: RhinoTopLevel? = null
|
||||
private val indexedProps: MutableMap<Any, Any?>
|
||||
private val implementor: InterfaceImplementor
|
||||
|
||||
fun eval(js: String, bindingsConfig: ScriptBindings.() -> Unit = {}): Any? {
|
||||
val bindings = ScriptBindings()
|
||||
Context.enter()
|
||||
try {
|
||||
bindings.apply(bindingsConfig)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
return eval(js, bindings)
|
||||
}
|
||||
|
||||
override fun eval(
|
||||
reader: Reader,
|
||||
scope: Scriptable,
|
||||
coroutineContext: CoroutineContext?
|
||||
): Any? {
|
||||
val cx = Context.enter() as RhinoContext
|
||||
val previousCoroutineContext = cx.coroutineContext
|
||||
if (coroutineContext != null && coroutineContext[Job] != null) {
|
||||
cx.coroutineContext = coroutineContext
|
||||
}
|
||||
cx.allowScriptRun = true
|
||||
cx.recursiveCount++
|
||||
val ret: Any?
|
||||
try {
|
||||
cx.checkRecursive()
|
||||
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 {
|
||||
cx.coroutineContext = previousCoroutineContext
|
||||
cx.allowScriptRun = false
|
||||
cx.recursiveCount--
|
||||
Context.exit()
|
||||
}
|
||||
return unwrapReturnValue(ret)
|
||||
}
|
||||
|
||||
@Throws(ContinuationPending::class)
|
||||
override suspend fun evalSuspend(reader: Reader, scope: Scriptable): Any? {
|
||||
val cx = Context.enter() as RhinoContext
|
||||
var ret: Any?
|
||||
withContext(VMBridgeReflect.contextLocal.asContextElement()) {
|
||||
cx.allowScriptRun = true
|
||||
cx.recursiveCount++
|
||||
try {
|
||||
cx.checkRecursive()
|
||||
var filename = this@RhinoScriptEngine["javax.script.filename"] as? String
|
||||
filename = filename ?: "<Unknown source>"
|
||||
val script = cx.compileReader(reader, filename, 1, null)
|
||||
try {
|
||||
ret = cx.executeScriptWithContinuations(script, scope)
|
||||
} catch (e: ContinuationPending) {
|
||||
var pending = e
|
||||
while (true) {
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val suspendFunction = pending.applicationState as suspend () -> Any?
|
||||
val functionResult = suspendFunction()
|
||||
val continuation = pending.continuation
|
||||
ret = cx.resumeContinuation(continuation, scope, functionResult)
|
||||
break
|
||||
} catch (e: ContinuationPending) {
|
||||
pending = e
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 {
|
||||
cx.allowScriptRun = false
|
||||
cx.recursiveCount--
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
return unwrapReturnValue(ret)
|
||||
}
|
||||
|
||||
override fun createBindings(): Bindings {
|
||||
return SimpleBindings()
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class, NoSuchMethodException::class)
|
||||
override fun invokeFunction(name: String, vararg args: Any): Any? {
|
||||
return this.invoke(null, name, *args)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class, NoSuchMethodException::class)
|
||||
override fun invokeMethod(obj: Any?, name: String, vararg args: Any): Any? {
|
||||
return if (obj == null) {
|
||||
throw IllegalArgumentException("脚本对象不能为空")
|
||||
} else {
|
||||
this.invoke(obj, name, *args)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Throws(ScriptException::class, NoSuchMethodException::class)
|
||||
private operator fun invoke(thiz: Any?, name: String?, vararg args: Any?): Any? {
|
||||
var thiz1 = thiz
|
||||
val cx = Context.enter()
|
||||
val var11: Any?
|
||||
try {
|
||||
if (name == null) {
|
||||
throw NullPointerException("方法名为空")
|
||||
}
|
||||
if (thiz1 != null && thiz1 !is Scriptable) {
|
||||
thiz1 = Context.toObject(thiz1, topLevel)
|
||||
}
|
||||
val engineScope = getRuntimeScope(context)
|
||||
val localScope = thiz1 ?: engineScope
|
||||
val obj = ScriptableObject.getProperty(localScope, name) as? Function
|
||||
?: throw NoSuchMethodException("no such method: $name")
|
||||
var scope = obj.parentScope
|
||||
if (scope == null) {
|
||||
scope = engineScope
|
||||
}
|
||||
val result = obj.call(cx, scope, localScope, wrapArguments(args as? Array<Any?>))
|
||||
var11 = unwrapReturnValue(result)
|
||||
} catch (re: RhinoException) {
|
||||
val line = if (re.lineNumber() == 0) -1 else re.lineNumber()
|
||||
val se = ScriptException(re.toString(), re.sourceName(), line)
|
||||
se.initCause(re)
|
||||
throw se
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
return var11
|
||||
}
|
||||
|
||||
override fun <T> getInterface(clazz: Class<T>): T? {
|
||||
return try {
|
||||
implementor.getInterface(null, clazz)
|
||||
} catch (_: ScriptException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T> getInterface(obj: Any?, paramClass: Class<T>): T? {
|
||||
return if (obj == null) {
|
||||
throw IllegalArgumentException("脚本对象不能为空")
|
||||
} else {
|
||||
try {
|
||||
implementor.getInterface(obj, paramClass)
|
||||
} catch (_: ScriptException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRuntimeScope(context: ScriptContext): Scriptable {
|
||||
val newScope: Scriptable = ExternalScriptable(context, indexedProps)
|
||||
val cx = Context.enter()
|
||||
try {
|
||||
newScope.prototype = RhinoTopLevel(cx, this)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
//newScope.put("context", newScope, context)
|
||||
return newScope
|
||||
}
|
||||
|
||||
override fun getRuntimeScope(bindings: ScriptBindings): Scriptable {
|
||||
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)
|
||||
}
|
||||
|
||||
@Throws(ScriptException::class)
|
||||
override fun compile(script: Reader): CompiledScript {
|
||||
val cx = Context.enter()
|
||||
val ret: RhinoCompiledScript
|
||||
try {
|
||||
var fileName = this["javax.script.filename"] as? String
|
||||
if (fileName == null) {
|
||||
fileName = "<Unknown Source>"
|
||||
}
|
||||
val scr = cx.compileReader(script, fileName, 1, null)
|
||||
ret = RhinoCompiledScript(this, scr)
|
||||
} catch (var9: Exception) {
|
||||
throw ScriptException(var9)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
fun wrapArguments(args: Array<Any?>?): Array<Any?> {
|
||||
return if (args == null) {
|
||||
Context.emptyArgs
|
||||
} else {
|
||||
val res = arrayOfNulls<Any>(args.size)
|
||||
for (i in res.indices) {
|
||||
res[i] = Context.javaToJS(args[i], topLevel)
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fun unwrapReturnValue(result: Any?): Any? {
|
||||
var result1 = result
|
||||
if (result1 is Wrapper) {
|
||||
result1 = result1.unwrap()
|
||||
}
|
||||
if (result1 is ConsString) {
|
||||
result1 = result1.toString()
|
||||
}
|
||||
return if (result1 is Undefined) null else result1
|
||||
}
|
||||
|
||||
init {
|
||||
ContextFactory.initGlobal(object : ContextFactory() {
|
||||
|
||||
override fun makeContext(): Context {
|
||||
val cx = RhinoContext(this)
|
||||
cx.languageVersion = Context.VERSION_ES6
|
||||
cx.setInterpretedMode(true)
|
||||
cx.setClassShutter(RhinoClassShutter)
|
||||
cx.wrapFactory = RhinoWrapFactory
|
||||
cx.instructionObserverThreshold = 10000
|
||||
cx.maximumInterpreterStackDepth = 1000
|
||||
return cx
|
||||
}
|
||||
|
||||
override fun hasFeature(cx: Context, featureIndex: Int): Boolean {
|
||||
@Suppress("UNUSED_EXPRESSION")
|
||||
return when (featureIndex) {
|
||||
Context.FEATURE_ENABLE_JAVA_MAP_ACCESS -> true
|
||||
else -> super.hasFeature(cx, featureIndex)
|
||||
}
|
||||
}
|
||||
|
||||
override fun observeInstructionCount(cx: Context, instructionCount: Int) {
|
||||
if (cx is RhinoContext) {
|
||||
cx.ensureActive()
|
||||
}
|
||||
}
|
||||
|
||||
override fun doTopCall(
|
||||
callable: Callable,
|
||||
cx: Context,
|
||||
scope: Scriptable,
|
||||
thisObj: Scriptable?,
|
||||
args: Array<Any>
|
||||
): Any? {
|
||||
var accContext: AccessControlContext? = null
|
||||
val global = ScriptableObject.getTopLevelScope(scope)
|
||||
val globalProto = global.prototype
|
||||
if (globalProto is RhinoTopLevel) {
|
||||
accContext = globalProto.accessContext
|
||||
}
|
||||
return if (accContext != null) AccessController.doPrivileged(
|
||||
PrivilegedAction {
|
||||
superDoTopCall(callable, cx, scope, thisObj, args)
|
||||
}, accContext
|
||||
) else superDoTopCall(
|
||||
callable,
|
||||
cx,
|
||||
scope,
|
||||
thisObj,
|
||||
args
|
||||
)
|
||||
}
|
||||
|
||||
private fun superDoTopCall(
|
||||
callable: Callable,
|
||||
cx: Context,
|
||||
scope: Scriptable,
|
||||
thisObj: Scriptable?,
|
||||
args: Array<Any>
|
||||
): Any? {
|
||||
try {
|
||||
if (cx is RhinoContext) {
|
||||
if (!cx.allowScriptRun) {
|
||||
error("Not allow run script in unauthorized way.")
|
||||
}
|
||||
cx.ensureActive()
|
||||
}
|
||||
return super.doTopCall(callable, cx, scope, thisObj, args)
|
||||
} catch (e: RhinoInterruptError) {
|
||||
throw e.cause
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (System.getSecurityManager() != null) {
|
||||
try {
|
||||
AccessController.checkPermission(AllPermission())
|
||||
} catch (_: AccessControlException) {
|
||||
accessContext = AccessController.getContext()
|
||||
}
|
||||
}
|
||||
val cx = Context.enter()
|
||||
try {
|
||||
topLevel = RhinoTopLevel(cx, this)
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
indexedProps = HashMap()
|
||||
implementor = object : InterfaceImplementor(this) {
|
||||
|
||||
override fun isImplemented(obj: Any?, clazz: Class<*>): Boolean {
|
||||
var obj1 = obj
|
||||
return try {
|
||||
if (obj1 != null && obj1 !is Scriptable) {
|
||||
obj1 = Context.toObject(obj1, topLevel)
|
||||
}
|
||||
val engineScope = getRuntimeScope(context)
|
||||
val localScope = obj1 ?: engineScope
|
||||
val methods = clazz.methods
|
||||
val methodsSize = methods.size
|
||||
for (index in 0 until methodsSize) {
|
||||
val method = methods[index]
|
||||
if (method.declaringClass != Any::class.java) {
|
||||
if (ScriptableObject.getProperty(
|
||||
localScope,
|
||||
method.name
|
||||
) !is Function
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
} finally {
|
||||
Context.exit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertResult(method: Method?, res: Any?): Any? {
|
||||
method ?: return null
|
||||
val desiredType = method.returnType
|
||||
if (desiredType == Void.TYPE) return null
|
||||
return Context.jsToJava(res, desiredType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import com.script.Bindings
|
||||
import com.script.ScriptContext
|
||||
import com.script.SimpleScriptContext
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.Function
|
||||
import org.mozilla.javascript.ImporterTopLevel
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import org.mozilla.javascript.Synchronizer
|
||||
import org.mozilla.javascript.Wrapper
|
||||
import java.security.AccessControlContext
|
||||
|
||||
/**
|
||||
* This class serves as top level scope for Rhino. This class adds
|
||||
* 3 top level functions (bindings, scope, sync) and two constructors
|
||||
* (JSAdapter, JavaAdapter).
|
||||
*
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
@Suppress("UNUSED_PARAMETER", "unused")
|
||||
class RhinoTopLevel(cx: Context, val scriptEngine: RhinoScriptEngine) :
|
||||
ImporterTopLevel(cx, System.getSecurityManager() != null) {
|
||||
|
||||
init {
|
||||
// LazilyLoadedCtor(this, "JSAdapter", "com.script.rhino.JSAdapter", false)
|
||||
// JavaAdapter.init(cx, this, false)
|
||||
// val names = arrayOf("bindings", "scope", "sync")
|
||||
// defineFunctionProperties(names, RhinoTopLevel::class.java, 2)
|
||||
}
|
||||
|
||||
val accessContext: AccessControlContext?
|
||||
get() = scriptEngine.accessContext
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun bindings(
|
||||
cx: Context,
|
||||
thisObj: Scriptable?,
|
||||
args: Array<Any?>,
|
||||
funObj: Function?
|
||||
): Any {
|
||||
if (args.size == 1) {
|
||||
var arg = args[0]
|
||||
if (arg is Wrapper) {
|
||||
arg = arg.unwrap()
|
||||
}
|
||||
if (arg is ExternalScriptable) {
|
||||
val ctx = arg.context
|
||||
val bind = ctx.getBindings(100)
|
||||
return Context.javaToJS(bind, getTopLevelScope(thisObj))
|
||||
}
|
||||
}
|
||||
return Context.getUndefinedValue()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun scope(cx: Context, thisObj: Scriptable?, args: Array<Any?>, funObj: Function?): Any {
|
||||
if (args.size == 1) {
|
||||
var arg = args[0]
|
||||
if (arg is Wrapper) {
|
||||
arg = arg.unwrap()
|
||||
}
|
||||
if (arg is Bindings) {
|
||||
val ctx: ScriptContext = SimpleScriptContext()
|
||||
ctx.setBindings(arg as Bindings?, 100)
|
||||
val res: Scriptable = ExternalScriptable(ctx)
|
||||
res.prototype = getObjectPrototype(thisObj)
|
||||
res.parentScope = getTopLevelScope(thisObj)
|
||||
return res
|
||||
}
|
||||
}
|
||||
return Context.getUndefinedValue()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sync(cx: Context, thisObj: Scriptable?, args: Array<Any?>, funObj: Function?): Any {
|
||||
return if (args.size == 1 && args[0] is Function) {
|
||||
Synchronizer(args[0] as Function?)
|
||||
} else {
|
||||
throw Context.reportRuntimeError("wrong argument(s) for sync")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.NativeJavaPackage
|
||||
import org.mozilla.javascript.ScriptRuntime
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import org.mozilla.javascript.WrapFactory
|
||||
|
||||
/**
|
||||
* This wrap factory is used for security reasons. JSR 223 script
|
||||
* engine interface and JavaScript engine classes are run as bootstrap
|
||||
* classes. For example, java.lang.Class.forName method (when called without
|
||||
* class loader) uses caller's class loader. This may be exploited by script
|
||||
* authors to access classes otherwise not accessible. For example,
|
||||
* classes in sun.* namespace are normally not accessible to untrusted
|
||||
* code and hence should not be accessible to JavaScript run from
|
||||
* untrusted code.
|
||||
*
|
||||
* @author A. Sundararajan
|
||||
* @since 1.6
|
||||
*/
|
||||
object RhinoWrapFactory : WrapFactory() {
|
||||
|
||||
private val factories = hashMapOf<Class<*>, JavaObjectWrapFactory>()
|
||||
|
||||
override fun wrapAsJavaObject(
|
||||
cx: Context,
|
||||
scope: Scriptable?,
|
||||
javaObject: Any,
|
||||
staticType: Class<*>?
|
||||
): Scriptable? {
|
||||
if (!RhinoClassShutter.visibleToScripts(javaObject)) {
|
||||
return null
|
||||
}
|
||||
return wrapOrNull(scope, javaObject, staticType)
|
||||
?: super.wrapAsJavaObject(cx, scope, javaObject, staticType)
|
||||
}
|
||||
|
||||
override fun wrapJavaClass(
|
||||
cx: Context,
|
||||
scope: Scriptable,
|
||||
javaClass: Class<*>
|
||||
): Scriptable {
|
||||
if (!RhinoClassShutter.visibleToScripts(javaClass)) {
|
||||
@Suppress("DEPRECATION")
|
||||
val pkg = NativeJavaPackage(javaClass.name, null)
|
||||
ScriptRuntime.setObjectProtoAndParent(pkg, scope)
|
||||
return pkg
|
||||
}
|
||||
return RhinoClassShutter.wrapJavaClass(scope, javaClass)
|
||||
}
|
||||
|
||||
private fun wrapOrNull(
|
||||
scope: Scriptable?,
|
||||
javaObject: Any,
|
||||
staticType: Class<*>?
|
||||
): Scriptable? {
|
||||
return factories[javaObject.javaClass]?.wrap(scope, javaObject, staticType)
|
||||
}
|
||||
|
||||
fun register(clazz: Class<*>, factory: JavaObjectWrapFactory) {
|
||||
if (!factories.contains(clazz)) {
|
||||
factories.put(clazz, factory)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.VMBridge
|
||||
|
||||
object VMBridgeReflect {
|
||||
|
||||
val instance: VMBridge by lazy {
|
||||
VMBridge::class.java.getDeclaredField("instance").apply {
|
||||
isAccessible = true
|
||||
}.get(null) as VMBridge
|
||||
}
|
||||
|
||||
val contextLocal: ThreadLocal<Any> by lazy {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
instance::class.java.getDeclaredField("contextLocal").apply {
|
||||
isAccessible = true
|
||||
}.get(null) as ThreadLocal<Any>
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user