/interpreter/tags/at_build150307/src/edu/vub/at/objects/symbiosis/Symbiosis.java

http://ambienttalk.googlecode.com/ · Java · 642 lines · 383 code · 27 blank · 232 comment · 131 complexity · 9475039d1b3f56308f98a7bd477ae56b MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * Symbiosis.java created on 5-nov-2006 at 19:22:26
  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.symbiosis;
  29. import edu.vub.at.exceptions.InterpreterException;
  30. import edu.vub.at.exceptions.XArityMismatch;
  31. import edu.vub.at.exceptions.XIllegalArgument;
  32. import edu.vub.at.exceptions.XNotInstantiatable;
  33. import edu.vub.at.exceptions.XReflectionFailure;
  34. import edu.vub.at.exceptions.XSelectorNotFound;
  35. import edu.vub.at.exceptions.XSymbiosisFailure;
  36. import edu.vub.at.exceptions.XTypeMismatch;
  37. import edu.vub.at.exceptions.XUnassignableField;
  38. import edu.vub.at.exceptions.XUndefinedField;
  39. import edu.vub.at.exceptions.signals.Signal;
  40. import edu.vub.at.objects.ATObject;
  41. import edu.vub.at.objects.coercion.Coercer;
  42. import edu.vub.at.objects.mirrors.JavaInterfaceAdaptor;
  43. import edu.vub.at.objects.mirrors.Reflection;
  44. import edu.vub.at.objects.natives.NATException;
  45. import edu.vub.at.objects.natives.NATNil;
  46. import edu.vub.at.objects.natives.NATTable;
  47. import edu.vub.at.objects.natives.NATText;
  48. import java.lang.reflect.Array;
  49. import java.lang.reflect.Constructor;
  50. import java.lang.reflect.Field;
  51. import java.lang.reflect.InvocationTargetException;
  52. import java.lang.reflect.Method;
  53. import java.lang.reflect.Modifier;
  54. import java.util.HashSet;
  55. import java.util.Hashtable;
  56. import java.util.Iterator;
  57. import java.util.LinkedList;
  58. import java.util.Vector;
  59. /**
  60. * The Symbiosis class is a container for auxiliary methods pertaining to making symbiotic
  61. * reflective Java invocations.
  62. *
  63. * @author tvcutsem
  64. */
  65. public final class Symbiosis {
  66. public static ATObject symbioticInvocation(ATObject wrapper, Object symbiont, Class ofClass, String selector, ATObject[] atArgs) throws InterpreterException {
  67. return symbioticInvocation(wrapper, symbiont, selector, getMethods(ofClass, selector, (symbiont==null)), atArgs);
  68. }
  69. /**
  70. * The Java method invocation algorithm is as follows:
  71. *
  72. * case of # of methods matching selector:
  73. * 0 => XSelectorNotFound
  74. * 1 => invoke the method OR XIllegalArgument, XArityMismatch, XReflectionFailure
  75. * * => (case of # of methods with matching arity OR taking varargs:
  76. * 0 => XSymbiosisFailure
  77. * 1 => invoke the method OR XIllegalArgument, XReflectionFailure
  78. * * => (case of # of methods matching 'default type' of the actual arguments:
  79. * 0 => XSymbiosisFailure
  80. * 1 => invoke OR XReflectionFailure
  81. * * => XSymbiosisFailure))
  82. *
  83. * A Java method takes a variable number of AT arguments <=> it has one formal parameter of type ATObject[]
  84. *
  85. * @param wrapper the ATObject wrapper for the symbiont
  86. * @param symbiont the Java object being accessed from within AmbientTalk
  87. * @param selector the Java selector corresponding to the method invocation
  88. * @param jMethod a JavaMethod encapsulating all applicable Java methods that correspond to the selector
  89. * @param atArgs the AT args to the symbiotic invocation
  90. * @return the wrapped result of the Java method invocation
  91. *
  92. * @throws XArityMismatch if the wrong number of arguments were supplied
  93. * @throws XSelectorNotFound if no methods correspond to the given selector (i.e. jMethod is null)
  94. * @throws XTypeMismatch if one of the arguments cannot be converted into the static type expected by the Java method
  95. * @throws XSymbiosisFailure if the method is overloaded and cannot be unambiguously resolved given the actual arguments
  96. * @throws XReflectionFailure if the invoked method is not accessible from within AmbientTalk
  97. * @throws XJavaException if the invoked Java method throws a Java exception
  98. */
  99. public static ATObject symbioticInvocation(ATObject wrapper, Object symbiont, String selector, JavaMethod jMethod, ATObject[] atArgs)
  100. throws InterpreterException {
  101. if (jMethod == null) {
  102. // no methods found? selector does not exist...
  103. throw new XSelectorNotFound(Reflection.downSelector(selector), wrapper);
  104. } else {
  105. Method[] methods = jMethod.choices_;
  106. if (methods.length == 1) {
  107. // just one method found, no need to resolve overloaded methods
  108. // if the Java method takes an ATObject array as its sole parameter, it is interpreted as taking
  109. // a variable number of ambienttalk arguments
  110. Class[] params = methods[0].getParameterTypes();
  111. Object[] args;
  112. if ((params.length == 1) && params[0].equals(ATObject[].class)) {
  113. args = new Object[] { atArgs };
  114. } else {
  115. if (params.length != atArgs.length) {
  116. throw new XArityMismatch("Java method "+Reflection.downSelector(methods[0].getName()), params.length, atArgs.length);
  117. }
  118. // make sure to properly 'coerce' each argument into the proper AT interface type
  119. args = atArgsToJavaArgs(atArgs, params);
  120. }
  121. return invokeUniqueSymbioticMethod(symbiont, methods[0], args);
  122. } else {
  123. // overloading: filter out all methods that do not match arity or whose
  124. // argument types do not match
  125. Object[] actuals = null;
  126. Class[] params;
  127. LinkedList matchingMethods = new LinkedList();
  128. for (int i = 0; i < methods.length; i++) {
  129. params = methods[i].getParameterTypes();
  130. // is the method a varargs method?
  131. if ((params.length == 1) && params[0].equals(ATObject[].class)) {
  132. actuals = new Object[] { atArgs };
  133. matchingMethods.addFirst(methods[i]);
  134. // does the arity match?
  135. } else if (params.length == atArgs.length) {
  136. // can it be invoked with the given actuals?
  137. try {
  138. actuals = atArgsToJavaArgs(atArgs, params);
  139. matchingMethods.addFirst(methods[i]);
  140. } catch(XTypeMismatch e) {
  141. // types don't match
  142. }
  143. } else {
  144. // arity does not match
  145. }
  146. }
  147. switch (matchingMethods.size()) {
  148. case 0: {
  149. // no methods left: overloading resolution failed
  150. throw new XSymbiosisFailure(symbiont, selector, atArgs);
  151. }
  152. case 1: {
  153. // just one method left, invoke it
  154. return invokeUniqueSymbioticMethod(symbiont, (Method) matchingMethods.getFirst(), actuals);
  155. }
  156. default: {
  157. // more than one method left: overloading resolution failed
  158. throw new XSymbiosisFailure(symbiont, selector, matchingMethods, atArgs);
  159. }
  160. }
  161. }
  162. }
  163. }
  164. /**
  165. * Creates a new instance of a Java class.
  166. *
  167. * @param ofClass the Java class of which to create an instance
  168. * @param atArgs the AmbientTalk arguments to the constructor, to be converted to Java arguments
  169. * @return an unitialized JavaObject wrapper around a newly created instance of the class
  170. *
  171. * @throws XArityMismatch if the wrong number of arguments were supplied
  172. * @throws XNotInstantiatable if no public constructors are available or if the class is abstract
  173. * @throws XTypeMismatch if one of the arguments cannot be converted into the static type expected by the constructor
  174. * @throws XSymbiosisFailure if the constructor is overloaded and cannot be unambiguously resolved given the actual arguments
  175. * @throws XReflectionFailure if the invoked constructor is not accessible from within AmbientTalk
  176. * @throws XJavaException if the invoked Java constructor throws a Java exception
  177. */
  178. public static ATObject symbioticInstanceCreation(Class ofClass, ATObject[] atArgs) throws InterpreterException {
  179. Constructor[] ctors = ofClass.getConstructors();
  180. switch (ctors.length) {
  181. // no constructors found? class is not instantiatable...
  182. case 0:
  183. throw new XNotInstantiatable(ofClass);
  184. // just one constructor found, no need to resolve overloaded methods
  185. case 1: {
  186. // if the constructor takes an ATObject array as its sole parameter, it is interpreted as taking
  187. // a variable number of ambienttalk arguments
  188. Class[] params = ctors[0].getParameterTypes();
  189. Object[] args;
  190. if ((params.length == 1) && params[0].equals(ATObject[].class)) {
  191. args = new Object[] { atArgs };
  192. } else {
  193. if (params.length != atArgs.length) {
  194. throw new XArityMismatch("Java constructor "+Reflection.downSelector(ctors[0].getName()), params.length, atArgs.length);
  195. }
  196. // make sure to properly convert actual arguments into Java objects
  197. args = atArgsToJavaArgs(atArgs, params);
  198. }
  199. return invokeUniqueSymbioticConstructor(ctors[0], args);
  200. }
  201. }
  202. // overloading: filter out all constructors that do not match arity or whose argument types do not match
  203. int matchingCtors = 0;
  204. Constructor matchingCtor = null;
  205. Object[] actuals = null;
  206. Class[] params;
  207. for (int i = 0; i < ctors.length; i++) {
  208. params = ctors[i].getParameterTypes();
  209. // is the constructor a varargs constructor?
  210. if ((params.length == 1) && params[0].equals(ATObject[].class)) {
  211. actuals = new Object[] { atArgs };
  212. matchingCtor = ctors[i];
  213. matchingCtors++;
  214. // does the arity match?
  215. } else if (params.length == atArgs.length) {
  216. // can it be invoked with the given actuals?
  217. try {
  218. actuals = atArgsToJavaArgs(atArgs, params);
  219. matchingCtor = ctors[i];
  220. matchingCtors++;
  221. } catch(XTypeMismatch e) {
  222. // types don't match
  223. ctors[i] = null; // TODO: don't assign to null, array may be cached or used later on (or by wrapper method)
  224. }
  225. } else {
  226. // arity does not match
  227. ctors[i] = null;
  228. }
  229. }
  230. if (matchingCtors != 1) {
  231. // no constructors left or more than one constructor left? overloading resolution failed
  232. throw new XSymbiosisFailure(ofClass, ctors, atArgs, matchingCtors);
  233. } else {
  234. // just one constructor left, invoke it
  235. return invokeUniqueSymbioticConstructor(matchingCtor, actuals);
  236. }
  237. }
  238. /**
  239. * Read a field from the given Java object reflectively.
  240. * @return the contents of the Java field, converted into its AmbientTalk equivalent
  241. */
  242. public static ATObject readField(Object fromObject, Class ofClass, String fieldName)
  243. throws InterpreterException {
  244. Field f = getField(ofClass, fieldName, (fromObject == null));
  245. return readField(fromObject, f);
  246. }
  247. /**
  248. * Read a field from the given Java object reflectively.
  249. * @return the contents of the Java field, converted into its AmbientTalk equivalent
  250. */
  251. public static ATObject readField(Object fromObject, Field f) throws InterpreterException {
  252. try {
  253. return Symbiosis.javaToAmbientTalk(f.get(fromObject));
  254. } catch (IllegalArgumentException e) {
  255. // the given object is of the wrong class, should not happen!
  256. throw new XReflectionFailure("Illegal class for field access of "+f.getName() + ": " + e.getMessage());
  257. } catch (IllegalAccessException e) {
  258. // the read field is not publicly accessible
  259. throw new XReflectionFailure("field access of " + f.getName() + " not accessible.");
  260. }
  261. }
  262. /**
  263. * Write a field in the given Java object reflectively.
  264. * @param toObject if null, the field is assumed to be static
  265. * @param value the AmbientTalk value which will be converted into its Java equivalent to be written int he field
  266. */
  267. public static void writeField(Object toObject, Class ofClass, String fieldName, ATObject value)
  268. throws InterpreterException {
  269. Field f = getField(ofClass, fieldName, (toObject == null));
  270. writeField(toObject, f, value);
  271. }
  272. /**
  273. * Write a field in the given Java object reflectively.
  274. * @param value the AmbientTalk value which will be converted into its Java equivalent to be written int he field
  275. */
  276. public static void writeField(Object toObject, Field f, ATObject value) throws InterpreterException {
  277. try {
  278. f.set(toObject, Symbiosis.ambientTalkToJava(value, f.getType()));
  279. } catch (IllegalArgumentException e) {
  280. // the given value is of the wrong type
  281. throw new XIllegalArgument("Illegal value for field "+f.getName() + ": " + e.getMessage());
  282. } catch (IllegalAccessException e) {
  283. // the read field is not publicly accessible or final
  284. throw new XUnassignableField(Reflection.downSelector(f.getName()).toString());
  285. }
  286. }
  287. /**
  288. * Query whether the given Java Class contains a (non-)static method with the given selector
  289. */
  290. public static boolean hasMethod(Class c, String selector, boolean isStatic) {
  291. Method[] methods = c.getMethods();
  292. for (int i = 0; i < methods.length; i++) {
  293. if (Modifier.isStatic(methods[i].getModifiers()) == isStatic) {
  294. if (methods[i].getName().equals(selector)) {
  295. return true;
  296. }
  297. }
  298. }
  299. return false;
  300. }
  301. /**
  302. * Query whether the given Java Class contains a (non-)static field with the given selector
  303. */
  304. public static boolean hasField(Class c, String selector, boolean isStatic) {
  305. try {
  306. Field f = c.getField(selector);
  307. return (Modifier.isStatic(f.getModifiers()) == isStatic);
  308. } catch (NoSuchFieldException e) {
  309. return false;
  310. }
  311. }
  312. /**
  313. * Retrieve a field from a Java object.
  314. * @throws XUndefinedField if the field does not exist or its static property does not match
  315. */
  316. public static Field getField(Class fromClass, String fieldName, boolean isStatic) throws XUndefinedField {
  317. try {
  318. Field f = fromClass.getField(fieldName);
  319. if ((Modifier.isStatic(f.getModifiers())) == isStatic) {
  320. return f;
  321. } else {
  322. throw new XUndefinedField("field access ", fieldName + " not accessible.");
  323. }
  324. } catch (NoSuchFieldException e) {
  325. // the field does not exist
  326. throw new XUndefinedField("field access ", fieldName + " not accessible.");
  327. }
  328. }
  329. /**
  330. * Retrieve all methods of a given name from a Java object. These are bundled together
  331. * in a first-class JavaMethod object, which is cached for later reference.
  332. *
  333. * A null return value indicates no matches.
  334. */
  335. public static JavaMethod getMethods(Class fromClass, String selector, boolean isStatic) {
  336. // first, check the method cache
  337. JavaMethod cachedEntry = JMethodCache._INSTANCE_.get(fromClass, selector, isStatic);
  338. if (cachedEntry != null) {
  339. // cache hit
  340. return cachedEntry;
  341. } else {
  342. // cache miss: assemble a new JavaMethod entry
  343. Method[] methods = fromClass.getMethods();
  344. Method m;
  345. Vector properMethods = new Vector(methods.length);
  346. for (int i = 0; i < methods.length; i++) {
  347. m = methods[i];
  348. if ((Modifier.isStatic(m.getModifiers())) == isStatic && m.getName().equals(selector)) {
  349. properMethods.add(methods[i]);
  350. }
  351. }
  352. Method[] choices = (Method[]) properMethods.toArray(new Method[properMethods.size()]);
  353. if (choices.length == 0) {
  354. // no matches
  355. return null;
  356. } else {
  357. // add entry to cache and return it
  358. JavaMethod jMethod = new JavaMethod(choices);
  359. JMethodCache._INSTANCE_.put(fromClass, selector, isStatic, jMethod);
  360. return jMethod;
  361. }
  362. }
  363. }
  364. /**
  365. * Retrieve all public static or non-static methods from a given Java class
  366. * (this includes methods defined in superclasses). All methods are properly wrapped in a
  367. * JavaMethod wrapper, taking care to wrap a set of overloaded methods using the same wrapper.
  368. *
  369. * @param isStatic if true, all static methods of fromClass are returned, otherwise the instance methods are returned
  370. */
  371. public static JavaMethod[] getAllMethods(Class fromClass, boolean isStatic) {
  372. // assemble a set of all unique selectors of all (non-)static methods of the class
  373. HashSet uniqueNames = new HashSet();
  374. Method[] methods = fromClass.getMethods();
  375. for (int i = 0; i < methods.length; i++) {
  376. Method m = methods[i];
  377. if ((Modifier.isStatic(m.getModifiers())) == isStatic) {
  378. uniqueNames.add(m.getName());
  379. }
  380. }
  381. // create a JavaMethod[] array large enough to contain all 'unique methods'
  382. JavaMethod[] jmethods = new JavaMethod[uniqueNames.size()];
  383. // loop over all entries and group the methods into a single wrapper
  384. int i = 0;
  385. for (Iterator iter = uniqueNames.iterator(); iter.hasNext();) {
  386. String methodName = (String) iter.next();
  387. jmethods[i++] = getMethods(fromClass, methodName, isStatic);
  388. }
  389. return jmethods;
  390. }
  391. /**
  392. * Retrieve all public static or non-static fields from a given Java class
  393. * (this includes fields defined in superclasses, but excludes shadowed superclass fields). All fields are properly wrapped in a
  394. * JavaField wrapper.
  395. *
  396. * @param ofObject if null, all static fields of fromClass are returned, otherwise the instance fields are returned
  397. */
  398. public static JavaField[] getAllFields(Object ofObject, Class fromClass) {
  399. boolean isStatic = (ofObject == null);
  400. Field[] fields = fromClass.getFields();
  401. // we do not consider shadowed superclass fields, therefore we store all encountered fields
  402. // in a table and only keep the field with the most specific class
  403. Hashtable recordedFields = new Hashtable();
  404. for (int i = 0; i < fields.length; i++) {
  405. Field f = fields[i];
  406. if ((Modifier.isStatic(f.getModifiers())) == isStatic) {
  407. // did we already encounter this field?
  408. if (recordedFields.contains(f.getName())) {
  409. // yes, then compare encountered field with previous field and only store most specific one
  410. Field prev = (Field) recordedFields.get(f.getName());
  411. // is f's Class a subclass of prev's Class?
  412. if (prev.getDeclaringClass().isAssignableFrom(f.getDeclaringClass())) {
  413. // yes, so f is more specific, store it instead of prev
  414. recordedFields.remove(prev.getName());
  415. recordedFields.put(f.getName(), f);
  416. } // if not, keep previous field
  417. } else {
  418. // field not encountered yet, store it
  419. recordedFields.put(f.getName(), f);
  420. }
  421. }
  422. }
  423. // create a JavaField[] array large enough to contain all entries in the table
  424. JavaField[] jfields = new JavaField[recordedFields.size()];
  425. // loop over all entries in the table and wrap each field
  426. int i = 0;
  427. for (Iterator iter = recordedFields.values().iterator(); iter.hasNext(); i++) {
  428. jfields[i] = new JavaField(ofObject, (Field) iter.next());
  429. }
  430. return jfields;
  431. }
  432. /**
  433. * Convert a Java object into an AmbientTalk object.
  434. *
  435. * @param jObj the Java object representing a mirror or a native type
  436. * @return the same object if it implements the ATObject interface
  437. */
  438. public static final ATObject javaToAmbientTalk(Object jObj) throws InterpreterException {
  439. // -- IMPLEMENTATION-level OBJECTS --
  440. if (jObj instanceof ATObject) {
  441. // the object is already an AmbientTalk object
  442. return (ATObject) jObj;
  443. }
  444. // -- NULL => NIL --
  445. if (jObj == null) {
  446. return NATNil._INSTANCE_;
  447. // -- AmbientTalk implementation-level objects --
  448. } else if(jObj instanceof ATObject) {
  449. return (ATObject) jObj;
  450. // -- PRIMITIVE TYPE => NUMERIC, TXT --
  451. } else if (JavaInterfaceAdaptor.isPrimitiveType(jObj.getClass())) {
  452. return JavaInterfaceAdaptor.primitiveJavaToATObject(jObj);
  453. // -- STRING => TEXT --
  454. } else if (jObj instanceof String) {
  455. return NATText.atValue((String) jObj);
  456. // -- ARRAY => TABLE --
  457. } else if (jObj.getClass().isArray()) {
  458. int length = Array.getLength(jObj);
  459. ATObject[] atTable = new ATObject[length];
  460. for (int i = 0; i < length; i++) {
  461. atTable[i] = javaToAmbientTalk(Array.get(jObj, i));
  462. }
  463. return NATTable.atValue(atTable);
  464. // -- EXCEPTION => NATEXCEPTION --
  465. } else if(jObj instanceof InterpreterException) {
  466. return ((InterpreterException)jObj).getAmbientTalkRepresentation();
  467. } else if (jObj instanceof Exception) {
  468. return new NATException(new XJavaException((Exception) jObj));
  469. // -- Symbiotic AmbientTalk object => AmbientTalk object --
  470. } else if (jObj instanceof SymbioticATObjectMarker) {
  471. return ((SymbioticATObjectMarker) jObj)._returnNativeAmbientTalkObject();
  472. // -- java.lang.Class => Symbiotic Class --
  473. } else if (jObj instanceof Class) {
  474. return JavaClass.wrapperFor((Class) jObj);
  475. // -- Object => Symbiotic AT Object --
  476. } else {
  477. return JavaObject.wrapperFor(jObj);
  478. }
  479. }
  480. /**
  481. * Convert an AmbientTalk object into an equivalent Java object.
  482. * @param atObj the AmbientTalk object to convert to a Java value
  483. * @param targetType the known static type of the Java object that should be attained
  484. * @return a Java object o where (o instanceof targetType) should yield true
  485. *
  486. * @throws XTypeMismatch if the object cannot be converted into the correct Java targetType
  487. */
  488. public static final Object ambientTalkToJava(ATObject atObj, Class targetType) throws InterpreterException {
  489. // -- WRAPPED JAVA OBJECTS --
  490. if (atObj.isJavaObjectUnderSymbiosis()) {
  491. Object jObj = atObj.asJavaObjectUnderSymbiosis().getWrappedObject();
  492. Class jCls = jObj.getClass();
  493. // dynamic subtype test: is jCls a subclass of targetType?
  494. if (targetType.isAssignableFrom(jCls)) {
  495. return jObj;
  496. } else {
  497. throw new XTypeMismatch(targetType, atObj);
  498. }
  499. // -- IMPLEMENTATION-LEVEL OBJECTS --
  500. } else if (targetType.isInstance(atObj)) {
  501. // target type is a subtype of ATObject, return the implementation-level object itself
  502. return atObj;
  503. // -- PRIMITIVE TYPES --
  504. } else if (JavaInterfaceAdaptor.isPrimitiveType(targetType)) {
  505. return JavaInterfaceAdaptor.atObjectToPrimitiveJava(atObj, targetType);
  506. // -- STRINGS --
  507. } else if (targetType == String.class) {
  508. return atObj.asNativeText().javaValue;
  509. // -- ARRAYS --
  510. } else if (targetType.isArray()) {
  511. ATObject[] atArray = atObj.asNativeTable().elements_;
  512. Object jArray = Array.newInstance(targetType.getComponentType(), atArray.length);
  513. for (int i = 0; i < Array.getLength(jArray); i++) {
  514. Array.set(jArray, i, ambientTalkToJava(atArray[i], targetType.getComponentType()));
  515. }
  516. return jArray;
  517. // -- EXCEPTIONS --
  518. } else if (targetType == Exception.class) {
  519. return atObj.asNativeException().getWrappedException();
  520. // -- CLASS OBJECTS --
  521. } else if (targetType == Class.class) {
  522. return atObj.asJavaClassUnderSymbiosis().getWrappedClass();
  523. // -- nil => NULL --
  524. } else if (atObj == NATNil._INSTANCE_) {
  525. return null;
  526. // -- INTERFACE TYPES AND NAT CLASSES --
  527. } else {
  528. return Coercer.coerce(atObj, targetType);
  529. }
  530. }
  531. private static ATObject invokeUniqueSymbioticMethod(Object symbiont, Method javaMethod, Object[] jArgs) throws InterpreterException {
  532. try {
  533. return Symbiosis.javaToAmbientTalk(javaMethod.invoke(symbiont, jArgs));
  534. } catch (IllegalAccessException e) {
  535. // the invoked method is not publicly accessible
  536. // sometimes this may happen when accessing inner classes, try again with an interface method:
  537. Method interfaceMethod = toInterfaceMethod(javaMethod);
  538. if (interfaceMethod == null) { // no success
  539. throw new XReflectionFailure("Java method "+Reflection.downSelector(javaMethod.getName()) + " is not accessible.", e);
  540. } else {
  541. return invokeUniqueSymbioticMethod(symbiont, interfaceMethod, jArgs);
  542. }
  543. } catch (IllegalArgumentException e) {
  544. // illegal argument types were supplied, should not happen because the conversion should have already failed earlier (in atArgsToJavaArgs)
  545. throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java method "+javaMethod.getName(), e);
  546. } catch (InvocationTargetException e) {
  547. // the invoked method threw an exception
  548. if (e.getCause() instanceof InterpreterException)
  549. throw (InterpreterException) e.getCause();
  550. else if (e.getCause() instanceof Signal) {
  551. throw (Signal) e.getCause();
  552. } else {
  553. throw new XJavaException(symbiont, javaMethod, e.getCause());
  554. }
  555. }
  556. }
  557. private static ATObject invokeUniqueSymbioticConstructor(Constructor ctor, Object[] jArgs) throws InterpreterException {
  558. try {
  559. return JavaObject.wrapperFor(ctor.newInstance(jArgs));
  560. } catch (IllegalAccessException e) {
  561. // the invoked method is not publicly accessible
  562. throw new XReflectionFailure("Java constructor "+Reflection.downSelector(ctor.getName()) + " is not accessible.", e);
  563. } catch (IllegalArgumentException e) {
  564. // illegal argument types were supplied, should not happen because the conversion should have already failed earlier (in atArgsToJavaArgs)
  565. throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java constructor "+ctor.getName(), e);
  566. } catch (InstantiationException e) {
  567. // the given class is abstract
  568. throw new XNotInstantiatable(ctor.getDeclaringClass(), e);
  569. } catch (InvocationTargetException e) {
  570. // the invoked method threw an exception
  571. if (e.getCause() instanceof InterpreterException)
  572. throw (InterpreterException) e.getCause();
  573. else if (e.getCause() instanceof Signal) {
  574. throw (Signal) e.getCause();
  575. } else {
  576. throw new XJavaException(null, ctor, e.getCause());
  577. }
  578. }
  579. }
  580. private static Object[] atArgsToJavaArgs(ATObject[] args, Class[] types) throws InterpreterException {
  581. Object[] jArgs = new Object[args.length];
  582. for (int i = 0; i < args.length; i++) {
  583. jArgs[i] = Symbiosis.ambientTalkToJava(args[i], types[i]);
  584. }
  585. return jArgs;
  586. }
  587. /**
  588. * Extremely vague and dirty feature of Java reflection: it can sometimes happen that
  589. * a method is invoked on a private inner class via a publicly accessible interface method.
  590. * In those cases, invoking that method results in an IllegalAccessException.
  591. * One example is invoking aVector.iterator().hasNext()
  592. *
  593. * The problem is that aVector.iterator() returns an instance of java.util.AbstractList$Itr
  594. * which is probably private. Selecting that class's hasNext method and invoking it results in
  595. * an IllegalAccessException. This can be circumvented by invoking the hasNext method through
  596. * the java.util.Iterator interface class.
  597. */
  598. private static Method toInterfaceMethod(Method m) {
  599. Class[] interfaces = m.getDeclaringClass().getInterfaces();
  600. if (interfaces == null) {
  601. return null;
  602. } else {
  603. // find the method in one of the interface declarations
  604. for (int i = 0; i < interfaces.length; i++) {
  605. try {
  606. return interfaces[i].getMethod(m.getName(), m.getParameterTypes());
  607. } catch(NoSuchMethodException e) {
  608. // continue searching
  609. }
  610. }
  611. // no declared method found
  612. return null;
  613. }
  614. }
  615. }