/interpreter/tags/at2dist030708/src/edu/vub/at/objects/mirrors/Reflection.java

http://ambienttalk.googlecode.com/ · Java · 478 lines · 201 code · 35 blank · 242 comment · 50 complexity · 2dc536a6aa09fdec1683de0e5916d71c MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * Reflection.java created on 10-aug-2006 at 16:19:17
  4. * (c) Programming Technology Lab, 2006 - 2007
  5. * Authors: Tom Van Cutsem & Stijn Mostinckx
  6. *
  7. * Permission is hereby granted, free of charge, to any person
  8. * obtaining a copy of this software and associated documentation
  9. * files (the "Software"), to deal in the Software without
  10. * restriction, including without limitation the rights to use,
  11. * copy, modify, merge, publish, distribute, sublicense, and/or
  12. * sell copies of the Software, and to permit persons to whom the
  13. * Software is furnished to do so, subject to the following
  14. * conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be
  17. * included in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  20. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  21. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  22. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  23. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  24. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  25. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  26. * OTHER DEALINGS IN THE SOFTWARE.
  27. */
  28. package edu.vub.at.objects.mirrors;
  29. import edu.vub.at.exceptions.InterpreterException;
  30. import edu.vub.at.exceptions.XIllegalArgument;
  31. import edu.vub.at.objects.ATMethod;
  32. import edu.vub.at.objects.ATObject;
  33. import edu.vub.at.objects.ATTable;
  34. import edu.vub.at.objects.grammar.ATSymbol;
  35. import edu.vub.at.objects.natives.NATTable;
  36. import edu.vub.at.objects.natives.grammar.AGSymbol;
  37. import edu.vub.at.objects.symbiosis.JavaConstructor;
  38. import edu.vub.at.objects.symbiosis.Symbiosis;
  39. import edu.vub.at.util.logging.Logging;
  40. import edu.vub.util.Regexp;
  41. import java.lang.reflect.Method;
  42. import java.util.Vector;
  43. import org.apache.regexp.RE;
  44. import org.apache.regexp.REProgram;
  45. /**
  46. * Reflection is an auxiliary class meant to serve as a repository for methods
  47. * related to 'up' and 'down' Java values properly into and out of the AmbientTalk base level.
  48. *
  49. * Keep the following mental picture in mind:
  50. *
  51. * ^ Java = implementation-level |
  52. * to deify | Java AT objects = meta-level | to reify
  53. * (= to up) | ------------------------------------------ | (= to down)
  54. * (= to absorb) | AmbientTalk = base-level v (= to reflect)
  55. *
  56. * Although deification and reification are more accurate terms, we will use 'up' and 'down'
  57. * because they are the clearest terminology, and clarity matters.
  58. *
  59. * In this class, the following conventions hold:
  60. * - methods start with either 'up' or 'down', denoting whether they deify or reify something
  61. * - arguments start with either 'j' or 'at', denoting whether they represent Java or AmbientTalk values
  62. * With 'java values' is meant 'java objects representing mirrors'
  63. *
  64. * @author tvc
  65. */
  66. public final class Reflection {
  67. private static final String _BASE_PREFIX_ = "base_";
  68. private static final String _META_PREFIX_ = "meta_";
  69. /**
  70. * A selector passed from the Java to the AmbientTalk level undergoes the following transformations:
  71. *
  72. * - any pattern of the form _op{code}_ is transformed to a symbol corresponding to the operator code
  73. * Operator codes are:
  74. * pls -> +
  75. * mns -> -
  76. * tms -> *
  77. * div -> /
  78. * bsl -> \
  79. * not -> !
  80. * gtx -> >
  81. * ltx -> <
  82. * eql -> =
  83. * til -> ~
  84. * que -> ?
  85. * rem -> %
  86. * - any underscores (_) are replaced by colons (:)
  87. */
  88. public static final ATSymbol downSelector(String jSelector) {
  89. return AGSymbol.jAlloc(javaToAmbientTalkSelector(jSelector));
  90. }
  91. /**
  92. * Transforms a Java selector prefixed with base_ into an AmbientTalk selector without the prefix.
  93. */
  94. public static final ATSymbol downBaseLevelSelector(String jSelector) throws InterpreterException {
  95. if (jSelector.startsWith(Reflection._BASE_PREFIX_)) {
  96. return downSelector(stripPrefix(jSelector, Reflection._BASE_PREFIX_));
  97. } else if (jSelector.startsWith(Reflection._META_PREFIX_)) {
  98. return downSelector(stripPrefix(jSelector, Reflection._META_PREFIX_));
  99. } else {
  100. throw new XIllegalArgument("Illegal base level selector to down: " + jSelector);
  101. }
  102. }
  103. /**
  104. * Transforms a Java selector prefixed with meta_ into an AmbientTalk selector without the prefix.
  105. */
  106. public static final ATSymbol downMetaLevelSelector(String jSelector) throws InterpreterException {
  107. if (jSelector.startsWith(Reflection._META_PREFIX_)) {
  108. return downSelector(stripPrefix(jSelector, Reflection._META_PREFIX_));
  109. } else {
  110. throw new XIllegalArgument("Illegal meta level selector to down: " + jSelector);
  111. }
  112. }
  113. /**
  114. * A selector passed from the AmbientTalk to the Java level undergoes the following transformations:
  115. *
  116. * - any colons (:) are replaced by underscores (_)
  117. * - any operator symbol is replaced by _op{code}_ where code is generated as follows:
  118. * Operator codes are:
  119. * + -> pls
  120. * - -> mns
  121. * * -> tms
  122. * / -> div
  123. * \ -> bsl
  124. * ! -> not
  125. * > -> gtx
  126. * < -> ltx
  127. * = -> eql
  128. * ~ -> til
  129. * ? -> que
  130. * % -> rem
  131. */
  132. public static final String upSelector(ATSymbol atSelector) throws InterpreterException {
  133. // : -> _
  134. String nam = new RE(colon).subst(atSelector.base_text().asNativeText().javaValue, "_");
  135. // operator symbol -> _op{code}_
  136. // find every occurence of a non-word character and convert it into a symbol
  137. return Regexp.replaceAll(new RE(symbol), nam, new Regexp.StringCallable() {
  138. public String call(String match) {
  139. String oprCode = symbol2oprCode(match);
  140. // only add the _op prefix and _ postfix if the code has been found...
  141. return (oprCode.length() == 3) ? "_op" + oprCode + "_" : oprCode;
  142. }
  143. });
  144. }
  145. /**
  146. * Transforms an AmbientTalk selector into a Java-level selector prefixed with base_.
  147. */
  148. public static final String upBaseLevelSelector(ATSymbol atSelector) throws InterpreterException {
  149. return Reflection._BASE_PREFIX_ + upSelector(atSelector);
  150. }
  151. /**
  152. * Transforms an AmbientTalk selector into a Java-level selector prefixed with meta_.
  153. */
  154. public static final String upMetaLevelSelector(ATSymbol atSelector) throws InterpreterException {
  155. return Reflection._META_PREFIX_ + upSelector(atSelector);
  156. }
  157. /**
  158. * Constructs an AmbientTalk ATMethod from a Java method.
  159. * Given an object obj and a String sel, it is checked whether obj has a method
  160. * named sel. If so, the corresponding Java method is wrapped in a NativeMethod.
  161. * If not, the downing fails.
  162. *
  163. * @param natObject the native AmbientTalk object in whose class the method should be found
  164. * @param jSelector a selector which should yield a method in natObject
  165. * @param origName the original AmbientTalk name of the method
  166. * @return a reified method wrapping the Java method
  167. *
  168. * Example:
  169. * eval "(reflect: tbl).getMethod('at')" where tbl is a NATTable
  170. * => downMethod(aNATTable, "base_at")
  171. * => NATTable must have a method named base_at
  172. *
  173. * Callers should use the more specialised 'downBaseLevelMethod' and 'downMetaLevelMethod'
  174. * methods to specify the prefix of the method to be found
  175. */
  176. public static final ATMethod downMethod(ATObject natObject, String jSelector, ATSymbol origName) throws InterpreterException {
  177. return new NativeMethod(JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, jSelector, origName),
  178. origName,
  179. natObject);
  180. }
  181. public static final ATMethod downBaseLevelMethod(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
  182. return downMethod(natObject, upBaseLevelSelector(atSelector), atSelector);
  183. }
  184. public static final ATMethod downMetaLevelMethod(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
  185. return downMethod(natObject, upMetaLevelSelector(atSelector), atSelector);
  186. }
  187. /**
  188. * downInvocation takes an implicit Java invocation and turns it into an explicit
  189. * AmbientTalk invocation process. This happens when Java code sends normal
  190. * Java messages to AmbientTalk objects (wrapped by a mirage).
  191. *
  192. * @param atRcvr the AmbientTalk object having received the Java method invocation
  193. * @param jSelector the Java selector, to be converted to an AmbientTalk selector
  194. * @param jArgs the arguments to the Java method invocation (normally all args are ATObjects)
  195. * jArgs may be null, indicating that there are no arguments
  196. * @return the return value of the AmbientTalk method invoked via the java invocation.
  197. *
  198. * Example:
  199. * in Java: "tbl.base_at(1)" where tbl is an ATTable coercer wrapping aNATObject
  200. * => downInvocation(aNATObject, "base_at", ATObject[] { ATNumber(1) })
  201. * => aNATObject must implement a method named "at"
  202. *
  203. * Depending on the prefix of the invoked Java method selector, the following translation should occur:
  204. * - obj.base_selector(args) => obj.meta_invoke(obj, selector, args)
  205. * - obj.base_selector() => obj.meta_invokeField(obj, selector)
  206. * - obj.meta_selector(args) => obj.meta_selector(args)
  207. * - obj.selector(args) => either obj.selector(args) if selector is understood natively
  208. * or obj.meta_invoke(obj, selector, args) otherwise
  209. * - obj.selector() => obj.meta_invokeField(obj, selector)
  210. */
  211. public static final ATObject downInvocation(ATObject atRcvr, Method jMethod, ATObject[] jArgs) throws InterpreterException {
  212. String jSelector = jMethod.getName();
  213. if (jArgs == null) { jArgs = NATTable.EMPTY.elements_; }
  214. if (jSelector.startsWith(Reflection._BASE_PREFIX_)) {
  215. if (jArgs.length == 0) {
  216. // obj.base_selector() => obj.meta_invokeField(obj, selector)
  217. return atRcvr.meta_invokeField(atRcvr, downBaseLevelSelector(jSelector));
  218. } else {
  219. // obj.base_selector(args) => obj.meta_invoke(obj, selector, args)
  220. return atRcvr.impl_invoke(atRcvr, downBaseLevelSelector(jSelector), NATTable.atValue(jArgs));
  221. }
  222. } else if (jSelector.startsWith(Reflection._META_PREFIX_)) {
  223. // obj.meta_selector(args) => obj.meta_selector(args)
  224. return JavaInterfaceAdaptor.invokeNativeATMethod(jMethod, atRcvr, jArgs);
  225. } else {
  226. // atRcvr can respond to the given method natively
  227. if (jMethod.getDeclaringClass().isInstance(atRcvr)) {
  228. return JavaInterfaceAdaptor.invokeNativeATMethod(jMethod, atRcvr, jArgs);
  229. } else {
  230. if (jArgs.length == 0) {
  231. // obj.selector() => obj.meta_invokeField(obj, selector)
  232. return atRcvr.meta_invokeField(atRcvr, downSelector(jSelector));
  233. } else {
  234. // obj.selector(args) => obj.meta_invoke(obj, selector, args)
  235. return atRcvr.impl_invoke(atRcvr, downSelector(jSelector), NATTable.atValue(jArgs));
  236. }
  237. }
  238. }
  239. }
  240. /**
  241. * upInvocation takes an explicit AmbientTalk method invocation and turns it into an
  242. * implicitly performed Java invocation.
  243. *
  244. * Depending on whether the AmbientTalk invocation happens at the base-level or the meta-level
  245. * (i.e. the receiver denotes a base-level object or a mirror), the jSelector parameter will have
  246. * a different prefix.
  247. *
  248. * @param atOrigRcvr the original AmbientTalk object that received the invocation
  249. * @param jSelector the selector of the message to be invoked, converted to a Java selector
  250. * @param atArgs the arguments to the AmbientTalk method invocation
  251. * @return the return value of the Java method invoked via the java invocation.
  252. *
  253. * Example:
  254. * eval "tbl.at(1)" where tbl is a NATTable
  255. * => upInvocation(aNATTable, "base_at", ATObject[] { ATNumber(1) })
  256. * => NATTable must have a method named base_at
  257. *
  258. * Example:
  259. * eval "(reflect: tbl).invoke(tbl, "at", [1])" where tbl is a NATTable
  260. * => upInvocation(aNATTable, "meta_invoke", ATObject[] { aNATTable, ATSymbol('at'), ATTable([ATNumber(1)]) })
  261. * => NATTable must have a method named meta_invoke
  262. */
  263. public static final ATObject upInvocation(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector, ATTable atArgs) throws InterpreterException {
  264. return JavaInterfaceAdaptor.invokeNativeATMethod(
  265. atOrigRcvr.getClass(),
  266. atOrigRcvr,
  267. jSelector,
  268. atSelector, atArgs.asNativeTable().elements_);
  269. }
  270. /**
  271. * upRespondsTo transforms an explicit AmbientTalk respondsTo meta-level request
  272. * into an implicit check whether the given jRcvr java object has a method
  273. * corresponding to the given selector, prefixed with base_
  274. *
  275. * @param jRcvr the Java object being queried for a certain selector
  276. * @param jSelector the selector of the message to be invoked, converted to a Java selector
  277. * @return a boolean indicating whether the jRcvr implements a method corresponding to base_ + atSelector
  278. *
  279. * Example:
  280. * eval "(reflect: [1,2,3]).respondsTo("at")" where the receiver of repondsTo is a NATTable
  281. * => upRespondsTo(aNATTable, "at")
  282. * => NATTable must have a method named base_at
  283. */
  284. public static final boolean upRespondsTo(ATObject jRcvr,String jSelector) throws InterpreterException {
  285. return JavaInterfaceAdaptor.hasApplicableJavaMethod(
  286. jRcvr.getClass(),
  287. jSelector);
  288. }
  289. /**
  290. * upMethodSelection takes an explicit AmbientTalk field selection and checks whether
  291. * a Java method exists that matches the selector. If so, this method is wrapped in a
  292. * NativeClosure and returned.
  293. *
  294. * @param atOrigRcvr the original AmbientTalk object that received the selection
  295. * @param jSelector the selector of the message to be invoked, converted to a Java selector
  296. * @return a closure wrapping the method selected via the AmbientTalk selection.
  297. *
  298. * Example:
  299. * eval "[1,2,3].at"
  300. * => upSelection(aNATTable, "at")
  301. * => either NATTable must have a method base_at, which is then wrapped
  302. */
  303. public static final NativeMethod upMethodSelection(ATObject atOrigRcvr, String jSelector, ATSymbol origSelector) throws InterpreterException {
  304. Method m = JavaInterfaceAdaptor.getNativeATMethod(atOrigRcvr.getClass(), atOrigRcvr, jSelector, origSelector);
  305. return new NativeMethod(m, origSelector, atOrigRcvr);
  306. }
  307. /**
  308. * upInstanceCreation takes an explicit AmbientTalk 'new' invocation and turns it into an
  309. * implicit Java instance creation by calling a constructor. The initargs are upped as well
  310. * and are passed as arguments to the constructor.
  311. *
  312. * @param jRcvr the Java object having received the call to new
  313. * @param atInitargs the arguments to the constructor
  314. * @return a new instance of a Java class
  315. * @throws InterpreterException
  316. */
  317. public static final ATObject upInstanceCreation(ATObject jRcvr, ATTable atInitargs) throws InterpreterException {
  318. ATObject[] args = atInitargs.asNativeTable().elements_;
  319. return JavaInterfaceAdaptor.createNativeATObject(jRcvr.getClass(), args);
  320. }
  321. public static final ATObject upExceptionCreation(InterpreterException jRcvr, ATTable atInitargs) throws InterpreterException {
  322. ATObject[] args = atInitargs.asNativeTable().elements_;
  323. return Symbiosis.symbioticInstanceCreation(new JavaConstructor(jRcvr.getClass()), args);
  324. }
  325. /**
  326. * Pass an AmbientTalk meta-level object into the base-level
  327. */
  328. public static final ATObject downObject(ATObject metaObject) throws InterpreterException {
  329. return metaObject;
  330. /*if (metaObject.meta_isTaggedAs(NativeTypeTags._MIRROR_).asNativeBoolean().javaValue) {
  331. return metaObject.meta_select(metaObject, OBJMirrorRoot._BASE_NAME_);
  332. } else {
  333. return metaObject; // most native objects represent both the object at the base and at the meta-level
  334. }*/
  335. }
  336. /**
  337. * Pass an AmbientTalk base-level object to the meta-level
  338. */
  339. public static final ATObject upObject(ATObject baseObject) {
  340. if (baseObject instanceof NATMirage) {
  341. return ((NATMirage) baseObject).getMirror();
  342. } else {
  343. return baseObject;
  344. }
  345. }
  346. /**
  347. * Returns, for a given AmbientTalk object atObj, an array of NativeMethod objects corresponding
  348. * to all non-static methods of that object's class, where each method's name is prefixed with 'base_'
  349. */
  350. public static final ATMethod[] downBaseLevelMethods(ATObject atObj) throws InterpreterException {
  351. Method[] allBaseMethods =
  352. JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._BASE_PREFIX_, false);
  353. Vector allATBaseMethods = new Vector();
  354. for (int i = 0; i < allBaseMethods.length; i++) {
  355. Method m = allBaseMethods[i];
  356. String nam = m.getName();
  357. allATBaseMethods.add(new NativeMethod(m, downBaseLevelSelector(nam), atObj));
  358. }
  359. return (ATMethod[]) allATBaseMethods.toArray(new ATMethod[allATBaseMethods.size()]);
  360. }
  361. /**
  362. * Returns, for a given AmbientTalk object natObj, an array of NativeMethod objects corresponding
  363. * to all non-static methods of that object's class, where each method's name is prefixed with 'meta_'
  364. */
  365. public static final ATMethod[] downMetaLevelMethods(ATObject natObj) throws InterpreterException {
  366. Method[] allMetaMethods =
  367. JavaInterfaceAdaptor.allMethodsPrefixed(natObj.getClass(), Reflection._META_PREFIX_, false);
  368. Vector allATMetaMethods = new Vector();
  369. for (int i = 0; i < allMetaMethods.length; i++) {
  370. Method m = allMetaMethods[i];
  371. String nam = m.getName();
  372. allATMetaMethods.add(new NativeMethod(m, downMetaLevelSelector(nam), natObj));
  373. }
  374. return (ATMethod[]) allATMetaMethods.toArray(new ATMethod[allATMetaMethods.size()]);
  375. }
  376. private static final REProgram oprCode = Regexp.compile("_op(\\w\\w\\w)_"); //'_op', 3 chars, '_'
  377. private static final REProgram symbol = Regexp.compile("\\W"); //any non-word character
  378. private static final REProgram underScore = Regexp.compile("_");
  379. private static final REProgram colon = Regexp.compile(":");
  380. private static String stripPrefix(String input, String prefix) {
  381. // ^ matches start of input
  382. // Backport from JDK 1.4 to 1.3
  383. // return input.replaceFirst("\\A"+prefix, "");
  384. return new RE(Regexp.compile("^"+prefix)).subst(input, "", RE.REPLACE_FIRSTONLY);
  385. // return Pattern.compile("\\A"+prefix).matcher(new StringBuffer(input)).replaceFirst("");
  386. }
  387. private static final String oprCode2Symbol(String code) {
  388. switch (code.charAt(0)) {
  389. case 'p': if (code.equals("pls")) { return "+"; } else break;
  390. case 'm': if (code.equals("mns")) { return "-"; } else break;
  391. case 't': if (code.equals("tms")) { return "*"; } else
  392. if (code.equals("til")) { return "~"; } else break;
  393. case 'd': if (code.equals("div")) { return "/"; } else break;
  394. case 'b': if (code.equals("bsl")) { return "\\"; } else break;
  395. case 'n': if (code.equals("not")) { return "!"; } else break;
  396. case 'g': if (code.equals("gtx")) { return ">"; } else break;
  397. case 'l': if (code.equals("ltx")) { return "<"; } else break;
  398. case 'e': if (code.equals("eql")) { return "="; } else break;
  399. case 'q': if (code.equals("que")) { return "?"; } else break;
  400. case 'r': if (code.equals("rem")) { return "%"; } else break;
  401. }
  402. return "_op" + code + "_"; // no match, return original input
  403. }
  404. private static final String symbol2oprCode(String symbol) {
  405. switch (symbol.charAt(0)) {
  406. case '+': return "pls";
  407. case '-': return "mns";
  408. case '*': return "tms";
  409. case '/': return "div";
  410. case '\\': return "bsl";
  411. case '!': return "not";
  412. case '>': return "gtx";
  413. case '<': return "ltx";
  414. case '=': return "eql";
  415. case '~': return "til";
  416. case '?': return "que";
  417. case '%': return "rem";
  418. default: return symbol; // no match, return original input
  419. }
  420. }
  421. private static final String javaToAmbientTalkSelector(String jSelector) {
  422. // find every occurence of _op\w\w\w_ and convert it into a symbol
  423. try {
  424. final RE codePattern = new RE(oprCode);
  425. String oprcodesReplaced = Regexp.replaceAll(codePattern, jSelector, new Regexp.StringCallable() {
  426. public String call(String match) {
  427. // match == _op{code}_
  428. // CAREFUL: we are mutating the same RE object used by the replaceAll function
  429. // itself! This is normally not a problem if we do not depend on the codePattern's
  430. // stateful methods
  431. codePattern.match(match);
  432. // _op{code}_ -> operator symbol
  433. return oprCode2Symbol(codePattern.getParen(1));
  434. }
  435. });
  436. // finally, replace all "_" by ":"
  437. return new RE(underScore).subst(oprcodesReplaced,":");
  438. } catch (InterpreterException e) { // all this to make compiler happy
  439. Logging.VirtualMachine_LOG.fatal("unexpected exception: " + e.getMessage(), e);
  440. throw new RuntimeException("Unexpected exception: " + e.getMessage());
  441. }
  442. }
  443. }