JSAdapter.kt 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * This code is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 only, as
  7. * published by the Free Software Foundation. Oracle designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Oracle in the LICENSE file that accompanied this code.
  10. *
  11. * This code is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * version 2 for more details (a copy is included in the LICENSE file that
  15. * accompanied this code).
  16. *
  17. * You should have received a copy of the GNU General Public License version
  18. * 2 along with this work; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22. * or visit www.oracle.com if you need additional information or have any
  23. * questions.
  24. */
  25. package com.script.rhino
  26. import org.mozilla.javascript.*
  27. import org.mozilla.javascript.Function
  28. /**
  29. * JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter
  30. * calls specially named JavaScript methods on an adaptee object when property
  31. * access is attempted on it.
  32. *
  33. * Example:
  34. *
  35. * var y = {
  36. * __get__ : function (name) { ... }
  37. * __has__ : function (name) { ... }
  38. * __put__ : function (name, value) {...}
  39. * __delete__ : function (name) { ... }
  40. * __getIds__ : function () { ... }
  41. * };
  42. *
  43. * var x = new JSAdapter(y);
  44. *
  45. * x.i; // calls y.__get__
  46. * i in x; // calls y.__has__
  47. * x.p = 10; // calls y.__put__
  48. * delete x.p; // calls y.__delete__
  49. * for (i in x) { print(i); } // calls y.__getIds__
  50. *
  51. * If a special JavaScript method is not found in the adaptee, then JSAdapter
  52. * forwards the property access to the adaptee itself.
  53. *
  54. * JavaScript caller of adapter object is isolated from the fact that
  55. * the property access/mutation/deletion are really calls to
  56. * JavaScript methods on adaptee. Use cases include 'smart'
  57. * properties, property access tracing/debugging, encaptulation with
  58. * easy client access - in short JavaScript becomes more "Self" like.
  59. *
  60. * Note that Rhino already supports special properties like __proto__
  61. * (to set, get prototype), __parent__ (to set, get parent scope). We
  62. * follow the same double underscore nameing convention here. Similarly
  63. * the name JSAdapter is derived from JavaAdapter -- which is a facility
  64. * to extend, implement Java classes/interfaces by JavaScript.
  65. *
  66. * @author A. Sundararajan
  67. * @since 1.6
  68. */
  69. @Suppress("unused", "UNUSED_PARAMETER")
  70. class JSAdapter private constructor(var adaptee: Scriptable) : Scriptable, Function {
  71. private var prototype: Scriptable? = null
  72. private var parent: Scriptable? = null
  73. private var isPrototype = false
  74. override fun getClassName(): String {
  75. return "JSAdapter"
  76. }
  77. override fun get(name: String, start: Scriptable): Any {
  78. val func = getAdapteeFunction(GET_PROP)
  79. return if (func != null) {
  80. this.call(func, arrayOf(name))
  81. } else {
  82. adaptee[name, adaptee]
  83. }
  84. }
  85. override fun get(index: Int, start: Scriptable): Any {
  86. val func = getAdapteeFunction(GET_PROP)
  87. return if (func != null) {
  88. this.call(func, arrayOf(index))
  89. } else {
  90. adaptee[index, adaptee]
  91. }
  92. }
  93. override fun has(name: String, start: Scriptable): Boolean {
  94. val func = getAdapteeFunction(HAS_PROP)
  95. return if (func != null) {
  96. val res = this.call(func, arrayOf(name))
  97. Context.toBoolean(res)
  98. } else {
  99. adaptee.has(name, adaptee)
  100. }
  101. }
  102. override fun has(index: Int, start: Scriptable): Boolean {
  103. val func = getAdapteeFunction(HAS_PROP)
  104. return if (func != null) {
  105. val res = this.call(func, arrayOf(index))
  106. Context.toBoolean(res)
  107. } else {
  108. adaptee.has(index, adaptee)
  109. }
  110. }
  111. override fun put(name: String, start: Scriptable, value: Any) {
  112. if (start === this) {
  113. val func = getAdapteeFunction(PUT_PROP)
  114. if (func != null) {
  115. this.call(func, arrayOf(name, value))
  116. } else {
  117. adaptee.put(name, adaptee, value)
  118. }
  119. } else {
  120. start.put(name, start, value)
  121. }
  122. }
  123. override fun put(index: Int, start: Scriptable, value: Any) {
  124. if (start === this) {
  125. val func = getAdapteeFunction(PUT_PROP)
  126. if (func != null) {
  127. this.call(func, arrayOf(index, value))
  128. } else {
  129. adaptee.put(index, adaptee, value)
  130. }
  131. } else {
  132. start.put(index, start, value)
  133. }
  134. }
  135. override fun delete(name: String) {
  136. val func = getAdapteeFunction(DEL_PROP)
  137. if (func != null) {
  138. this.call(func, arrayOf(name))
  139. } else {
  140. adaptee.delete(name)
  141. }
  142. }
  143. override fun delete(index: Int) {
  144. val func = getAdapteeFunction(DEL_PROP)
  145. if (func != null) {
  146. this.call(func, arrayOf(index))
  147. } else {
  148. adaptee.delete(index)
  149. }
  150. }
  151. override fun getPrototype(): Scriptable? {
  152. return prototype
  153. }
  154. override fun setPrototype(prototype: Scriptable?) {
  155. this.prototype = prototype
  156. }
  157. override fun getParentScope(): Scriptable? {
  158. return parent
  159. }
  160. override fun setParentScope(parent: Scriptable?) {
  161. this.parent = parent
  162. }
  163. override fun getIds(): Array<Any?> {
  164. val func = getAdapteeFunction(GET_PROPIDS)
  165. return if (func == null) {
  166. adaptee.ids
  167. } else {
  168. val val1 = this.call(func, arrayOfNulls(0))
  169. val res: Array<Any?>
  170. when (val1) {
  171. is NativeArray -> {
  172. res = arrayOfNulls(val1.length.toInt())
  173. for (index in res.indices) {
  174. res[index] = mapToId(val1[index, val1])
  175. }
  176. res
  177. }
  178. !is NativeJavaArray -> {
  179. Context.emptyArgs
  180. }
  181. else -> {
  182. val tmp = val1.unwrap()
  183. if (tmp.javaClass == Array<Any>::class.java) {
  184. val array = tmp as Array<*>
  185. res = arrayOfNulls(array.size)
  186. for (index in array.indices) {
  187. res[index] = mapToId(array[index])
  188. }
  189. } else {
  190. res = Context.emptyArgs
  191. }
  192. res
  193. }
  194. }
  195. }
  196. }
  197. override fun hasInstance(scriptable: Scriptable): Boolean {
  198. return if (scriptable is JSAdapter) {
  199. true
  200. } else {
  201. var proto = scriptable.prototype
  202. while (proto != null) {
  203. if (proto == this) {
  204. return true
  205. }
  206. proto = proto.prototype
  207. }
  208. false
  209. }
  210. }
  211. override fun getDefaultValue(hint: Class<*>?): Any {
  212. return adaptee.getDefaultValue(hint)
  213. }
  214. @Throws(RhinoException::class)
  215. override fun call(cx: Context, scope: Scriptable, thisObj: Scriptable, args: Array<Any>): Any {
  216. return if (isPrototype) {
  217. construct(cx, scope, args)
  218. } else {
  219. val tmp = adaptee
  220. if (tmp is Function) {
  221. tmp.call(cx, scope, tmp, args)
  222. } else {
  223. throw Context.reportRuntimeError("TypeError: not a function")
  224. }
  225. }
  226. }
  227. @Throws(RhinoException::class)
  228. override fun construct(cx: Context, scope: Scriptable, args: Array<Any>): Scriptable {
  229. val tmp: Scriptable?
  230. return if (isPrototype) {
  231. tmp = ScriptableObject.getTopLevelScope(scope)
  232. if (args.isNotEmpty()) {
  233. JSAdapter(Context.toObject(args[0], tmp))
  234. } else {
  235. throw Context.reportRuntimeError("JSAdapter requires adaptee")
  236. }
  237. } else {
  238. tmp = adaptee
  239. if (tmp is Function) {
  240. tmp.construct(cx, scope, args)
  241. } else {
  242. throw Context.reportRuntimeError("TypeError: not a constructor")
  243. }
  244. }
  245. }
  246. private fun mapToId(tmp: Any?): Any {
  247. return if (tmp is Double) tmp.toInt() else Context.toString(tmp)
  248. }
  249. private fun getAdapteeFunction(name: String): Function? {
  250. val o = ScriptableObject.getProperty(adaptee, name)
  251. return o as? Function
  252. }
  253. private fun call(func: Function, args: Array<Any?>): Any {
  254. val cx = Context.getCurrentContext()
  255. val thisObj = adaptee
  256. val scope = func.parentScope
  257. return try {
  258. func.call(cx, scope, thisObj, args)
  259. } catch (re: RhinoException) {
  260. throw Context.reportRuntimeError(re.message)
  261. }
  262. }
  263. companion object {
  264. private const val GET_PROP = "__get__"
  265. private const val HAS_PROP = "__has__"
  266. private const val PUT_PROP = "__put__"
  267. private const val DEL_PROP = "__delete__"
  268. private const val GET_PROPIDS = "__getIds__"
  269. @Throws(RhinoException::class)
  270. fun init(cx: Context, scope: Scriptable, sealed: Boolean) {
  271. val obj = JSAdapter(cx.newObject(scope))
  272. obj.parentScope = scope
  273. obj.setPrototype(getFunctionPrototype(scope))
  274. obj.isPrototype = true
  275. ScriptableObject.defineProperty(scope, "JSAdapter", obj, 2)
  276. }
  277. private fun getFunctionPrototype(scope: Scriptable): Scriptable {
  278. return ScriptableObject.getFunctionPrototype(scope)
  279. }
  280. }
  281. }