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

http://ambienttalk.googlecode.com/ · Java · 246 lines · 122 code · 24 blank · 100 comment · 22 complexity · 6a87fd72bd19f51aee1d8b7e1ff6800f MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * NativeClosure.java created on 10-aug-2006 at 8:30:08
  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.eval.Evaluator;
  30. import edu.vub.at.exceptions.InterpreterException;
  31. import edu.vub.at.exceptions.XArityMismatch;
  32. import edu.vub.at.objects.ATContext;
  33. import edu.vub.at.objects.ATMethod;
  34. import edu.vub.at.objects.ATObject;
  35. import edu.vub.at.objects.ATTable;
  36. import edu.vub.at.objects.grammar.ATSymbol;
  37. import edu.vub.at.objects.natives.NATClosure;
  38. import edu.vub.at.objects.natives.NATContext;
  39. import edu.vub.at.objects.natives.NATNumber;
  40. import edu.vub.at.objects.natives.NATTable;
  41. import edu.vub.at.objects.natives.NATText;
  42. /**
  43. * A NativeClosure is a wrapper class for a piece of Java code. The Java code is
  44. * presented to the AmbientTalk system as a closure. A NativeClosure is represented
  45. * as follows:
  46. *
  47. * - Its encapsulating method is a 'dummy' NativeMethod ATMethod, with the following properties:
  48. * - name = "nativelambda" (to distinguish it from 'ordinary' lambdas)
  49. * - arguments = [ \@args ] (it takes an arbitrary number of arguments)
  50. * - body = "Native implementation in {class}" (a string telling an inspector that
  51. * this closure is natively implemented in the given Java class)
  52. * - applying a nativelambda directly (without going through this NativeClosure)
  53. * results in an error
  54. * - Its enclosed context consists of a triple (obj, obj, obj.super), where 'obj'
  55. * is the Java object (implementing ATObject) that created this NativeClosure.
  56. *
  57. * The method and context fields of a NativeClosure are lazily generated on demand
  58. * for efficiency reasons. Most of the time, a NativeClosure will not be introspected,
  59. * but only applied.
  60. *
  61. * A NativeClosure can be used in two ways by Java code:
  62. * 1) As a generator for anonymous classes to generate 'anonymous lambdas':
  63. * new NativeClosure(this) {
  64. * public ATObject meta_apply(ATTable args) throws NATException {
  65. * ...
  66. * }
  67. * }
  68. * 2) As a wrapper for an already existing NativeMethod:
  69. * new NativeClosure(this, aJavaMethod);
  70. *
  71. * @author tvc
  72. */
  73. public class NativeClosure extends NATClosure {
  74. protected final ATObject scope_;
  75. /**
  76. * Create a new NativeClosure where meta_apply will be overridden by anonymous subclasses.
  77. * @param scope the object creating this NativeClosure.
  78. */
  79. public NativeClosure(ATObject scope) {
  80. this(scope, null);
  81. }
  82. /**
  83. * Create a new NativeClosure where meta_apply will invoke the given Java Method.
  84. * @param scope the object that should be used as a receiver for the corresponding method.
  85. * @param meth a presumably native method
  86. */
  87. public NativeClosure(ATObject scope, NativeMethod meth) {
  88. super(meth, null);
  89. scope_ = scope;
  90. }
  91. /**
  92. * Overridden to allow for lazy instantiation of the method.
  93. *
  94. * If receiver is an anonymous NativeClosure, an 'anonymous' NativeMethod is returned.
  95. * @return a NativeMethod wrapped by this NativeClosure.
  96. */
  97. public ATMethod base_method() {
  98. if (method_ == null)
  99. method_ = new NativeAnonymousMethod(scope_.getClass());
  100. return method_;
  101. }
  102. /**
  103. * Overridden to allow for lazy instantiation of the context.
  104. *
  105. * A 'default' context is lazily constructed and returned.
  106. */
  107. public ATContext base_context() throws InterpreterException {
  108. if (context_ == null)
  109. context_ = new NATContext(scope_, scope_);
  110. return context_;
  111. }
  112. /**
  113. * Apply the NativeClosure, which either gives rise to executing a native piece of
  114. * code supplied by an anonymous subclass, or executes the wrapped NativeMethod.
  115. */
  116. public ATObject base_apply(ATTable arguments) throws InterpreterException {
  117. if (method_ == null) {
  118. // this method is supposed to be overridden by an anonymous subclass
  119. throw new RuntimeException("NativeClosure's base_apply not properly overridden by " + scope_.getClass());
  120. } else {
  121. return method_.base_apply(arguments, this.base_context());
  122. }
  123. }
  124. /**
  125. * A NativeClosure can also be directed to execute its wrapped NativeMethod in an
  126. * externally specified scope. Of course, for native calls or symbiotic calls, the
  127. * call will only succeed if the externally specified object is of the correct native type.
  128. */
  129. public ATObject base_applyInScope(ATTable args, ATObject scope) throws InterpreterException {
  130. if (method_ == null) {
  131. // this method is supposed to be overridden by an anonymous subclass
  132. throw new RuntimeException("NativeClosure's base_applyInScope not properly overridden by " + scope_.getClass());
  133. } else {
  134. return method_.base_applyInScope(args, new NATContext(scope, scope));
  135. }
  136. }
  137. public NATText meta_print() throws InterpreterException {
  138. return NATText.atValue("<native closure:"+base_method().base_name().base_text().asNativeText().javaValue+">");
  139. }
  140. /**
  141. * Auxiliary method to more easily extract arguments from an ATTable
  142. */
  143. public ATObject get(ATTable args, int n) throws InterpreterException {
  144. return args.base_at(NATNumber.atValue(n));
  145. }
  146. public int getNbr(ATTable args, int n) throws InterpreterException {
  147. return args.base_at(NATNumber.atValue(n)).asNativeNumber().javaValue;
  148. }
  149. public double getFrc(ATTable args, int n) throws InterpreterException {
  150. return args.base_at(NATNumber.atValue(n)).asNativeFraction().javaValue;
  151. }
  152. public String getTxt(ATTable args, int n) throws InterpreterException {
  153. return args.base_at(NATNumber.atValue(n)).asNativeText().javaValue;
  154. }
  155. public boolean getBln(ATTable args, int n) throws InterpreterException {
  156. return args.base_at(NATNumber.atValue(n)).asNativeBoolean().javaValue;
  157. }
  158. public Object[] getTab(ATTable args, int n) throws InterpreterException {
  159. return args.base_at(NATNumber.atValue(n)).asNativeTable().elements_;
  160. }
  161. public void checkArity(ATTable args, int required) throws InterpreterException {
  162. int provided = args.base_length().asNativeNumber().javaValue;
  163. if (provided != required) {
  164. throw new XArityMismatch(Evaluator._ANON_MTH_NAM_.toString(), required, provided);
  165. }
  166. }
  167. public static void checkNullaryArguments(ATSymbol selector, ATTable args) throws InterpreterException {
  168. if (args != NATTable.EMPTY)
  169. throw new XArityMismatch("access to non-closure field " + selector.toString(), 0, args.base_length().asNativeNumber().javaValue);
  170. }
  171. public static ATObject checkUnaryArguments(ATSymbol selector, ATTable args) throws InterpreterException {
  172. int len = args.base_length().asNativeNumber().javaValue;
  173. if (len != 1)
  174. throw new XArityMismatch("mutation of field " + selector.toString(), 1, len);
  175. return args.base_at(NATNumber.ONE);
  176. }
  177. /**
  178. * A zero-argument (0-ary) native closure representing an accessor.
  179. */
  180. public static abstract class Accessor extends NativeClosure {
  181. /** for debugging/error messages only */
  182. private final ATSymbol name_;
  183. public Accessor(ATSymbol name, ATObject scope) {
  184. super(scope);
  185. name_ = name;
  186. }
  187. public ATObject base_apply(ATTable args) throws InterpreterException {
  188. if (args != NATTable.EMPTY) {
  189. throw new XArityMismatch("accessor for " + name_, 0, args.base_length().asNativeNumber().javaValue);
  190. } else {
  191. return access();
  192. }
  193. }
  194. protected abstract ATObject access() throws InterpreterException;
  195. public NATText meta_print() throws InterpreterException {
  196. return NATText.atValue("<native closure:"+name_+">");
  197. }
  198. }
  199. /**
  200. * A one-argument (1-ary) native closure representing a mutator.
  201. */
  202. public static abstract class Mutator extends NativeClosure {
  203. /** for debugging/error messages only */
  204. private final ATSymbol name_;
  205. public Mutator(ATSymbol name, ATObject scope) {
  206. super(scope);
  207. name_ = name;
  208. }
  209. public ATObject base_apply(ATTable args) throws InterpreterException {
  210. int len = args.base_length().asNativeNumber().javaValue;
  211. if (len != 1) {
  212. throw new XArityMismatch("mutator for " + name_, 1, len);
  213. } else {
  214. return mutate(args.base_at(NATNumber.ONE));
  215. }
  216. }
  217. protected abstract ATObject mutate(ATObject val) throws InterpreterException;
  218. public NATText meta_print() throws InterpreterException {
  219. return NATText.atValue("<native closure:"+name_+">");
  220. }
  221. }
  222. }