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