305 lines
10 KiB
Kotlin
305 lines
10 KiB
Kotlin
/*
|
|
* 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(
|
|
context: ScriptContext?,
|
|
indexedProps: MutableMap<Any, Any?> = HashMap()
|
|
) : Scriptable {
|
|
val context: ScriptContext
|
|
private val indexedProps: MutableMap<Any, Any?>
|
|
private var prototype: Scriptable? = null
|
|
private var parent: Scriptable? = null
|
|
|
|
init {
|
|
if (context == null) {
|
|
throw NullPointerException("context is null")
|
|
} else {
|
|
this.context = context
|
|
this.indexedProps = indexedProps
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
} |