/interpreter/tags/at2-build060407/src/edu/vub/at/objects/symbiosis/JavaClass.java

http://ambienttalk.googlecode.com/ · Java · 422 lines · 223 code · 39 blank · 160 comment · 23 complexity · 1757d14e18bdc441c4ca56c71e5b88f6 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * JavaClass.java created on 3-nov-2006 at 10:54:38
  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.XDuplicateSlot;
  32. import edu.vub.at.exceptions.XSelectorNotFound;
  33. import edu.vub.at.exceptions.XTypeMismatch;
  34. import edu.vub.at.exceptions.XUnassignableField;
  35. import edu.vub.at.exceptions.XUndefinedField;
  36. import edu.vub.at.objects.ATBoolean;
  37. import edu.vub.at.objects.ATContext;
  38. import edu.vub.at.objects.ATField;
  39. import edu.vub.at.objects.ATMethod;
  40. import edu.vub.at.objects.ATNil;
  41. import edu.vub.at.objects.ATObject;
  42. import edu.vub.at.objects.ATStripe;
  43. import edu.vub.at.objects.ATTable;
  44. import edu.vub.at.objects.coercion.NativeStripes;
  45. import edu.vub.at.objects.grammar.ATSymbol;
  46. import edu.vub.at.objects.mirrors.PrimitiveMethod;
  47. import edu.vub.at.objects.mirrors.Reflection;
  48. import edu.vub.at.objects.natives.NATBoolean;
  49. import edu.vub.at.objects.natives.NATNil;
  50. import edu.vub.at.objects.natives.NATNumber;
  51. import edu.vub.at.objects.natives.NATObject;
  52. import edu.vub.at.objects.natives.NATTable;
  53. import edu.vub.at.objects.natives.NATText;
  54. import edu.vub.at.objects.natives.grammar.AGSymbol;
  55. import edu.vub.at.util.logging.Logging;
  56. import edu.vub.util.IdentityHashMap;
  57. import java.lang.ref.SoftReference;
  58. /**
  59. * A JavaClass instance represents a Java Class under symbiosis.
  60. *
  61. * Java classes are treated as AmbientTalk 'singleton' objects:
  62. *
  63. * - cloning a Java class results in the same Java class instance
  64. * - sending 'new' to a Java class invokes the constructor and returns a new instance of the class under symbiosis
  65. * - all static fields and methods of the Java class are reflected under symbiosis as fields and methods of the AT object
  66. *
  67. * A Java Class object that represents an interface can furthermore be used
  68. * as an AmbientTalk stripe. The stripe's name corresponds to the interface's full name.
  69. *
  70. * JavaClass instances are pooled (on a per-actor basis): there should exist only one JavaClass instance
  71. * for each Java class loaded into the JVM. Because the JVM ensures that a Java class
  72. * can only be loaded once, we can use the Java class wrapped by the JavaClass instance
  73. * as a unique key to identify its corresponding JavaClass instance.
  74. *
  75. * @author tvcutsem
  76. */
  77. public final class JavaClass extends NATObject implements ATStripe {
  78. /**
  79. * A thread-local hashmap pooling all of the JavaClass wrappers for
  80. * the current actor, referring to them using SOFT references, such
  81. * that unused wrappers can be GC-ed when running low on memory.
  82. */
  83. private static final ThreadLocal _JAVACLASS_POOL_ = new ThreadLocal() {
  84. protected synchronized Object initialValue() {
  85. return new IdentityHashMap();
  86. }
  87. };
  88. public static final JavaClass wrapperFor(Class c) {
  89. IdentityHashMap map = (IdentityHashMap) _JAVACLASS_POOL_.get();
  90. if (map.containsKey(c)) {
  91. SoftReference ref = (SoftReference) map.get(c);
  92. JavaClass cls = (JavaClass) ref.get();
  93. if (cls != null) {
  94. return cls;
  95. } else {
  96. map.remove(c);
  97. cls = new JavaClass(c);
  98. map.put(c, new SoftReference(cls));
  99. return cls;
  100. }
  101. } else {
  102. JavaClass jc = new JavaClass(c);
  103. map.put(c, new SoftReference(jc));
  104. return jc;
  105. }
  106. }
  107. // primitive fields and method of a JavaClass wrapper
  108. private static final AGSymbol _PST_NAME_ = AGSymbol.jAlloc("parentStripes");
  109. private static final AGSymbol _SNM_NAME_ = AGSymbol.jAlloc("stripeName");
  110. /** def isSubstripeOf(stripe) { nil } */
  111. private static final PrimitiveMethod _PRIM_SST_ = new PrimitiveMethod(
  112. AGSymbol.jAlloc("isSubstripeOf"), NATTable.atValue(new ATObject[] { AGSymbol.jAlloc("stripe")})) {
  113. public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
  114. if (!arguments.base_getLength().equals(NATNumber.ONE)) {
  115. throw new XArityMismatch("isSubstripeOf", 1, arguments.base_getLength().asNativeNumber().javaValue);
  116. }
  117. return ctx.base_getLexicalScope().asJavaClassUnderSymbiosis().base_isSubstripeOf(arguments.base_at(NATNumber.ONE).asStripe());
  118. }
  119. };
  120. private final Class wrappedClass_;
  121. /**
  122. * A JavaClass wrapping a class c is an object that has the lexical scope as its lexical parent
  123. * and has NIL as its dynamic parent.
  124. *
  125. * If the JavaClass wraps a Java interface type, JavaClass instances are
  126. * also stripes.
  127. */
  128. private JavaClass(Class wrappedClass) {
  129. super(wrappedClass.isInterface() ?
  130. new ATStripe[] { NativeStripes._STRIPE_ } :
  131. NATObject._NO_STRIPES_);
  132. wrappedClass_ = wrappedClass;
  133. // add the two fields and one method needed for an ATStripe
  134. if (wrappedClass.isInterface()) {
  135. Class[] extendedInterfaces = wrappedClass_.getInterfaces();
  136. ATObject[] stripes = new ATObject[extendedInterfaces.length];
  137. for (int i = 0; i < extendedInterfaces.length; i++) {
  138. stripes[i] = JavaClass.wrapperFor(extendedInterfaces[i]);
  139. }
  140. try {
  141. super.meta_defineField(_PST_NAME_, NATTable.atValue(stripes));
  142. super.meta_defineField(_SNM_NAME_, AGSymbol.jAlloc(wrappedClass_.getName()));
  143. super.meta_addMethod(_PRIM_SST_);
  144. } catch (InterpreterException e) {
  145. Logging.Actor_LOG.fatal("Error while initializing Java Class as stripe: " + wrappedClass.getName(), e);
  146. }
  147. }
  148. }
  149. public Class getWrappedClass() { return wrappedClass_; }
  150. public JavaClass asJavaClassUnderSymbiosis() throws XTypeMismatch { return this; }
  151. public ATBoolean base__opeql__opeql_(ATObject comparand) throws InterpreterException {
  152. return NATBoolean.atValue(this.equals(comparand));
  153. }
  154. public boolean equals(Object other) {
  155. return ((other instanceof JavaClass) &&
  156. (wrappedClass_.equals(((JavaClass) other).wrappedClass_)));
  157. }
  158. /* ------------------------------------------------------
  159. * - Symbiotic implementation of the ATObject interface -
  160. * ------------------------------------------------------ */
  161. /**
  162. * When a method is invoked upon a symbiotic Java class object, the underlying static Java method
  163. * with the same name as the AmbientTalk selector is invoked. Its arguments are converted
  164. * into their Java equivalents. Conversely, the result of the method invocation is converted
  165. * into an AmbientTalk object.
  166. */
  167. public ATObject meta_invoke(ATObject receiver, ATSymbol atSelector, ATTable arguments) throws InterpreterException {
  168. try {
  169. String jSelector = Reflection.upSelector(atSelector);
  170. return Symbiosis.symbioticInvocation(
  171. this, null, wrappedClass_, jSelector, arguments.asNativeTable().elements_);
  172. } catch (XSelectorNotFound e) {
  173. e.catchOnlyIfSelectorEquals(atSelector);
  174. return super.meta_invoke(receiver, atSelector, arguments);
  175. }
  176. }
  177. /**
  178. * A symbiotic Java class object responds to all of the public static selectors of its Java class
  179. * plus all of the per-instance selectors added to its AmbientTalk symbiont.
  180. */
  181. public ATBoolean meta_respondsTo(ATSymbol atSelector) throws InterpreterException {
  182. String jSelector = Reflection.upSelector(atSelector);
  183. if (Symbiosis.hasMethod(wrappedClass_, jSelector, true) ||
  184. Symbiosis.hasField(wrappedClass_, jSelector, true)) {
  185. return NATBoolean._TRUE_;
  186. } else {
  187. return super.meta_respondsTo(atSelector);
  188. }
  189. }
  190. /**
  191. * When selecting a field from a symbiotic Java class object, if the object's class
  192. * has a static field with a matching selector, it is automatically read;
  193. * if it has methods corresponding to the selector, they are returned in a JavaMethod wrapper,
  194. * otherwise, the fields of its AT symbiont are checked.
  195. */
  196. public ATObject meta_select(ATObject receiver, ATSymbol selector) throws InterpreterException {
  197. String jSelector = Reflection.upSelector(selector);
  198. try {
  199. return Symbiosis.readField(null, wrappedClass_, jSelector);
  200. } catch(XUndefinedField e) {
  201. JavaMethod choices = Symbiosis.getMethods(wrappedClass_, jSelector, true);
  202. if (choices != null) {
  203. return new JavaClosure(this, choices);
  204. } else {
  205. return super.meta_select(receiver, selector);
  206. }
  207. }
  208. }
  209. /**
  210. * A variable lookup is resolved by first checking whether the Java object has an appropriate static
  211. * field with a matching name. If so, that field's contents are returned. If not, the AT symbiont's
  212. * fields are checked.
  213. */
  214. public ATObject meta_lookup(ATSymbol selector) throws InterpreterException {
  215. try {
  216. String jSelector = Reflection.upSelector(selector);
  217. return Symbiosis.readField(null, wrappedClass_, jSelector);
  218. } catch(XUndefinedField e) {
  219. return super.meta_lookup(selector);
  220. }
  221. }
  222. /**
  223. * Fields can be defined within a symbiotic Java class object. They are added
  224. * to its AmbientTalk symbiont, but only if they do not clash with already
  225. * existing field names.
  226. */
  227. public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
  228. if (Symbiosis.hasField(wrappedClass_, Reflection.upSelector(name), true)) {
  229. throw new XDuplicateSlot(XDuplicateSlot._FIELD_, name);
  230. } else {
  231. return super.meta_defineField(name, value);
  232. }
  233. }
  234. /**
  235. * Variables can be assigned within a symbiotic Java class object if that class object
  236. * has a mutable static field with a matching name. Variable assignment is first
  237. * resolved in the Java object and afterwards in the AT symbiont.
  238. */
  239. public ATNil meta_assignVariable(ATSymbol name, ATObject value) throws InterpreterException {
  240. try {
  241. String jSelector = Reflection.upSelector(name);
  242. Symbiosis.writeField(null, wrappedClass_, jSelector, value);
  243. return NATNil._INSTANCE_;
  244. } catch (XUnassignableField e) {
  245. return super.meta_assignVariable(name, value);
  246. }
  247. }
  248. /**
  249. * Fields can be assigned within a symbiotic Java class object if that class
  250. * has a mutable field with a matching name. Field assignment is first resolved
  251. * in the Java object and afterwards in the AT symbiont.
  252. */
  253. public ATNil meta_assignField(ATObject receiver, ATSymbol name, ATObject value) throws InterpreterException {
  254. try {
  255. String jSelector = Reflection.upSelector(name);
  256. Symbiosis.writeField(null, wrappedClass_, jSelector, value);
  257. return NATNil._INSTANCE_;
  258. } catch (XUnassignableField e) {
  259. return super.meta_assignField(receiver, name, value);
  260. }
  261. }
  262. /**
  263. * Symbiotic Java class objects are singletons.
  264. */
  265. public ATObject meta_clone() throws InterpreterException { return this; }
  266. /**
  267. * aJavaClass.new(@args) == invoke a Java constructor
  268. * AmbientTalk objects can add a custom new method to the class in order to intercept
  269. * instance creation. The original instance can then be performed by invoking the old new(@args).
  270. *
  271. * For example, imagine we want to extend the class java.lang.Point with a 3D coordinate, e.g. a 'z' field:
  272. * <tt>
  273. * def Point := jlobby.java.awt.Point;
  274. * def oldnew := Point.new;
  275. * def Point.new(x,y,z) { // 'override' the new method
  276. * def point := oldnew(x,y); // invokes the Java constructor
  277. * def point.z := z; // adds a field dynamically to the new JavaObject wrapper
  278. * point; // important! new should return the newly created instance
  279. * }
  280. * def mypoint := Point.new(1,2,3);
  281. * </tt>
  282. */
  283. public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
  284. return Symbiosis.symbioticInstanceCreation(wrappedClass_, initargs.asNativeTable().elements_);
  285. }
  286. /**
  287. * Methods can be added to a symbiotic Java class object provided they do not already
  288. * exist in the Java class.
  289. */
  290. public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
  291. ATSymbol name = method.base_getName();
  292. if (Symbiosis.hasMethod(wrappedClass_, Reflection.upSelector(name), true)) {
  293. throw new XDuplicateSlot(XDuplicateSlot._METHOD_, name);
  294. } else {
  295. return super.meta_addMethod(method);
  296. }
  297. }
  298. /**
  299. * Fields can be grabbed from a symbiotic Java class object. Fields that correspond
  300. * to static fields in the Java class are returned as JavaField instances.
  301. */
  302. public ATField meta_grabField(ATSymbol fieldName) throws InterpreterException {
  303. try {
  304. return new JavaField(null,
  305. Symbiosis.getField(wrappedClass_, Reflection.upSelector(fieldName), true));
  306. } catch(XUndefinedField e) {
  307. return super.meta_grabField(fieldName);
  308. }
  309. }
  310. /**
  311. * Methods can be grabbed from a symbiotic Java class object. Methods that correspond
  312. * to static methods in the Java class are returned as JavaMethod instances.
  313. */
  314. public ATMethod meta_grabMethod(ATSymbol methodName) throws InterpreterException {
  315. JavaMethod choices = Symbiosis.getMethods(wrappedClass_, Reflection.upSelector(methodName), true);
  316. if (choices != null) {
  317. return choices;
  318. } else {
  319. return super.meta_grabMethod(methodName);
  320. }
  321. }
  322. /**
  323. * Querying a symbiotic Java class object for its fields results in a table containing
  324. * both 'native' static Java fields and the fields of its AT symbiont
  325. */
  326. public ATTable meta_listFields() throws InterpreterException {
  327. // instance fields of the wrapped object's class
  328. JavaField[] jFields = Symbiosis.getAllFields(null, wrappedClass_);
  329. // fields of the AT symbiont
  330. ATObject[] symbiontFields = super.meta_listFields().asNativeTable().elements_;
  331. return NATTable.atValue(NATTable.collate(jFields, symbiontFields));
  332. }
  333. /**
  334. * Querying a symbiotic Java class object for its methods results in a table containing
  335. * both 'native' static Java methods and the methods of its AT symbiont
  336. */
  337. public ATTable meta_listMethods() throws InterpreterException {
  338. // instance methods of the wrapped object's class
  339. JavaMethod[] jMethods = Symbiosis.getAllMethods(wrappedClass_, true);
  340. // methods of the AT symbiont
  341. ATObject[] symbiontMethods = super.meta_listMethods().asNativeTable().elements_;
  342. return NATTable.atValue(NATTable.collate(jMethods, symbiontMethods));
  343. }
  344. public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
  345. return NATBoolean.atValue(this == original);
  346. }
  347. public NATText meta_print() throws InterpreterException {
  348. return NATText.atValue("<java:"+wrappedClass_.toString()+">");
  349. }
  350. /**
  351. * A Java Class object remains unique within an actor.
  352. */
  353. public ATObject meta_resolve() throws InterpreterException {
  354. return wrapperFor(wrappedClass_);
  355. }
  356. /* ========================
  357. * == ATStripe Interface ==
  358. * ======================== */
  359. /**
  360. * If this class represents an interface type, parentStripes
  361. * are wrappers for all interfaces extended by this Java interface type
  362. */
  363. public ATTable base_getParentStripes() throws InterpreterException {
  364. return super.meta_select(this, _PST_NAME_).asTable();
  365. }
  366. public ATSymbol base_getStripeName() throws InterpreterException {
  367. return super.meta_select(this, _SNM_NAME_).asSymbol();
  368. }
  369. /**
  370. * A Java interface type used as a stripe can only be a substripe of another
  371. * Java interface type used as a stripe, and only if this type is assignable
  372. * to the other type.
  373. */
  374. public ATBoolean base_isSubstripeOf(ATStripe other) throws InterpreterException {
  375. if (other instanceof JavaClass) {
  376. JavaClass otherClass = (JavaClass) other;
  377. // wrappedClass <: otherClass <=> otherClass >= wrappedClass
  378. return NATBoolean.atValue(otherClass.wrappedClass_.isAssignableFrom(wrappedClass_));
  379. } else {
  380. return NATBoolean._FALSE_;
  381. }
  382. }
  383. }