/interpreter/tags/at2dist220411/src/edu/vub/at/objects/symbiosis/JavaConstructor.java
Java | 285 lines | 174 code | 43 blank | 68 comment | 25 complexity | 8652577c78295f5c18882fa819ea75e3 MD5 | raw file
1/** 2 * AmbientTalk/2 Project 3 * JavaConstructor.java created on May 20, 2008 at 4:56:32 PM 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.symbiosis; 29 30import edu.vub.at.eval.Evaluator; 31import edu.vub.at.exceptions.InterpreterException; 32import edu.vub.at.exceptions.XIllegalOperation; 33import edu.vub.at.exceptions.XNotInstantiatable; 34import edu.vub.at.exceptions.XTypeMismatch; 35import edu.vub.at.objects.ATBoolean; 36import edu.vub.at.objects.ATClosure; 37import edu.vub.at.objects.ATContext; 38import edu.vub.at.objects.ATMethod; 39import edu.vub.at.objects.ATObject; 40import edu.vub.at.objects.ATTable; 41import edu.vub.at.objects.coercion.NativeTypeTags; 42import edu.vub.at.objects.grammar.ATBegin; 43import edu.vub.at.objects.grammar.ATSymbol; 44import edu.vub.at.objects.mirrors.NativeClosure; 45import edu.vub.at.objects.natives.NATBoolean; 46import edu.vub.at.objects.natives.NATByRef; 47import edu.vub.at.objects.natives.NATContext; 48import edu.vub.at.objects.natives.NATNil; 49import edu.vub.at.objects.natives.NATNumber; 50import edu.vub.at.objects.natives.NATTable; 51import edu.vub.at.objects.natives.NATText; 52import edu.vub.at.objects.natives.grammar.AGBegin; 53import edu.vub.at.parser.SourceLocation; 54import edu.vub.util.TempFieldGenerator; 55 56import java.lang.reflect.Array; 57import java.lang.reflect.Constructor; 58import java.util.Vector; 59 60/** 61 * JavaConstructor is a wrapper class encapsulating one or more java.lang.reflect.Constructor objects. 62 * All methods in the choices array are overloaded constructors for the same class. The choices array 63 * should never be empty! 64 * 65 * Since constructors do not need an receiver to become a full closure, this class implements both the 66 * ATMethod and the ATJavaClosure interface. The latter ensures that constructors can be cast properly. 67 * 68 * @author smostinc 69 */ 70public class JavaConstructor extends NATByRef implements ATMethod, ATJavaClosure { 71 72 73 protected final Class class_; 74 protected final Constructor[] choices_; 75 76 private ATContext context_ = null; // lazily initialized 77 78 public JavaConstructor(Class clazz) { 79 this(clazz, clazz.getConstructors()); 80 } 81 82 public JavaConstructor(Class clazz, Constructor[] choices) { 83 // assertion 84 class_ = clazz; 85 choices_ = choices; 86 } 87 88 // ATObject Interface implementation 89 90 public ATBoolean base__opeql__opeql_(ATObject comparand) throws InterpreterException { 91 return NATBoolean.atValue(this.equals(comparand)); 92 } 93 94 /** 95 * Two JavaConstructor instances are equal if they both represent a set of constructors 96 * from the same declaring class. 97 */ 98 public boolean equals(Object other) { 99 if (other instanceof JavaConstructor) { 100 JavaConstructor mth = (JavaConstructor) other; 101 return (mth.class_ == this.class_); 102 } else { 103 return false; 104 } 105 } 106 107 public ATTable meta_typeTags() throws InterpreterException { 108 return NATTable.of(NativeTypeTags._METHOD_, NativeTypeTags._CLOSURE_); 109 } 110 111 112 // ATMethod Interface implementation 113 114 public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException { 115 return base_apply(arguments); 116 } 117 118 public ATObject base_applyInScope(ATTable arguments, ATContext ctx) throws InterpreterException { 119 return base_apply(arguments); 120 } 121 122 public ATBegin base_bodyExpression() throws InterpreterException { 123 // list all of the method signatures of the (possibly overloaded) Java method 124 StringBuffer buff = new StringBuffer("Java implementation of: "); 125 for (int i = 0; i < choices_.length; i++) { 126 buff.append("\n"); 127 buff.append(choices_[i].toString()); 128 } 129 buff.append("\n"); 130 return new AGBegin(NATTable.atValue(new ATObject[] { NATText.atValue(buff.toString()) })); 131 } 132 133 public ATSymbol base_name() throws InterpreterException { 134 return NATNil._NEW_NAME_; 135 } 136 137 public ATTable base_parameters() throws InterpreterException { 138 return Evaluator._ANON_MTH_ARGS_; 139 } 140 141 public ATTable base_annotations() throws InterpreterException { 142 return NATTable.EMPTY; 143 } 144 145 public NATText meta_print() throws InterpreterException { 146 return NATText.atValue("<java constructor:"+class_+">"); 147 } 148 149 150 151 public NATText impl_asCode(TempFieldGenerator objectMap) throws InterpreterException { 152 String simpleClassName = class_.getSimpleName(); 153 char[] simpleClassNameChars = simpleClassName.toCharArray(); 154 if (Character.isLowerCase(simpleClassNameChars[0])) { 155 String packageName = class_.getPackage().getName(); 156 return NATText.atValue("jlobby." + packageName + ".class(`" + simpleClassName + ").&new"); 157 } else { 158 return NATText.atValue("jlobby." + class_.getCanonicalName() + ".&new"); 159 } 160 } 161 162 public ATMethod asMethod() throws XTypeMismatch { 163 return this; 164 } 165 166 /** 167 * When selected from a JavaClass object, the constructor needs to be wrapped in a dedicated 168 * closure object. For constructors the JavaConstructor instance itself can be used. 169 */ 170 public ATClosure base_wrap(ATObject lexicalScope, ATObject dynamicReceiver) { 171 return this; 172 } 173 174 // ATJavaClosure Interface implementation 175 176 public ATClosure asClosure() throws InterpreterException { 177 return this; 178 } 179 180 public ATObject base_apply(ATTable arguments) throws InterpreterException { 181 return Symbiosis.symbioticInstanceCreation(this, arguments.asNativeTable().elements_); 182 } 183 184 public ATObject base_applyInScope(ATTable arguments, ATObject scope) throws InterpreterException { 185 return base_apply(arguments); 186 } 187 188 public ATContext base_context() throws InterpreterException { 189 if(context_ == null) { 190 JavaClass atClass = JavaClass.wrapperFor(class_); 191 context_ = new NATContext(atClass, atClass); 192 } 193 194 return context_; 195 } 196 197 public ATObject base_escape() throws InterpreterException { 198 throw new XIllegalOperation("Cannot call escape() in a Java Constructor"); 199 } 200 201 public ATMethod base_method() throws InterpreterException { 202 return this; 203 } 204 205 public ATObject base_whileTrue_(final ATClosure body) throws InterpreterException { 206 final ATClosure test = this; 207 ATBoolean cond; 208 209 while (true) { 210 // cond = self.apply() 211 cond = this.base_apply(NATTable.EMPTY).asBoolean(); 212 if(cond.isNativeBoolean()) { 213 // cond is a native boolean, perform the conditional ifTrue: test natively 214 if (cond.asNativeBoolean().javaValue) { 215 // execute body and continue while loop 216 body.base_apply(NATTable.EMPTY); 217 continue; 218 } else { 219 // return nil 220 return Evaluator.getNil(); 221 } 222 } else { 223 // cond is a user-defined boolean, do a recursive send 224 return cond.base_ifTrue_(new NativeClosure(this) { 225 public ATObject base_apply(ATTable args) throws InterpreterException { 226 // if user-defined bool is true, execute body and recurse 227 body.base_apply(NATTable.EMPTY); 228 return test.base_whileTrue_(body); 229 } 230 }); 231 } 232 } } 233 234 /** 235 * For each Method in the wrapped JavaMethod's choices_, check whether it is compatible with 236 * the given types. If so, add it to the choices_ array of the new JavaMethod. 237 */ 238 public ATClosure base_cast(ATObject[] types) throws InterpreterException { 239 // unwrap the JavaClass wrappers 240 Class[] actualTypes = new Class[types.length]; 241 for (int i = 0; i < actualTypes.length; i++) { 242 // Array types may be represented as one-arg tables of a type: [Type] 243 // TODO: properly refactor the instanceof test 244 // problem: cannot do base_isTable because JavaObject/JavaClass objects will say yes! 245 if (types[i] instanceof NATTable) { 246 // Array.newInstance([Type][1],0).getClass() 247 actualTypes[i] = Array.newInstance(types[i].asTable(). 248 base_at(NATNumber.ONE).asJavaClassUnderSymbiosis().getWrappedClass(), 0).getClass(); 249 } else { 250 actualTypes[i] = types[i].asJavaClassUnderSymbiosis().getWrappedClass(); 251 } 252 } 253 Vector matchingMethods = new Vector(); 254 255 for (int i = 0; i < choices_.length; i++) { 256 if(matches(choices_[i].getParameterTypes(), actualTypes)) { 257 matchingMethods.add(choices_[i]); 258 } 259 } 260 261 Constructor[] matches = (Constructor[]) matchingMethods.toArray(new Constructor[matchingMethods.size()]); 262 263 if (matches.length > 0) { 264 return new JavaConstructor(class_, matches); 265 } else { 266 throw new XNotInstantiatable(class_); 267 } 268 } 269 270 /** 271 * Compares two Class arrays and returns true iff both arrays have equal size and all members are the same. 272 */ 273 private static final boolean matches(Class[] formals, Class[] actuals) { 274 if (formals.length != actuals.length) 275 return false; 276 277 for (int i = 0; i < formals.length; i++) { 278 if (!(formals[i] == actuals[i])) { 279 return false; 280 } 281 } 282 283 return true; 284 } 285}