1 /* 2 * Copyright the original author or authors. 3 * 4 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.mozilla.org/MPL/MPL-1.1.html 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import org.as2lib.core.BasicClass; 18 import org.as2lib.util.Stringifier; 19 import org.as2lib.env.reflect.TypeInfo; 20 import org.as2lib.env.reflect.TypeMemberInfo; 21 import org.as2lib.env.reflect.stringifier.MethodInfoStringifier; 22 23 /** 24 * {@code MethodInfo} represents a method. 25 * 26 * <p>{@code MethodInfo} instances for specific methods can be obtained using the 27 * {@link ClassInfo#getMethods} or {@link ClassInfo#getMethod} methods. That means 28 * you first have to get a class info for the class that declares or inherits the 29 * method. You can therefor use the {@link ClassInfo#forObject}, {@link ClassInfo#forClass}, 30 * {@link ClassInfo#forInstance} and {@link ClassInfo#forName} methods. 31 * 32 * <p>When you have obtained the method info you can use it to get information about 33 * the method. 34 * 35 * <code> 36 * trace("Method name: " + methodInfo.getName()); 37 * trace("Declaring type: " + methodInfo.getDeclaringType().getFullName()); 38 * trace("Is Static?: " + methodInfo.isStatic()); 39 * </code> 40 * 41 * @author Simon Wacker 42 */ 43 class org.as2lib.env.reflect.MethodInfo extends BasicClass implements TypeMemberInfo { 44 45 /** The method info stringifier. */ 46 private static var stringifier:Stringifier; 47 48 /** 49 * The invoker method used to invoke this method. This invoker is invoked on 50 * different scopes, never on this scope. 51 * 52 * <p>This invoker removes itself, before executing the method, from the object it 53 * was assigned to. It expects itself to have the name {@code "__as2lib__invoker"}. 54 * 55 * @param object the object that holds this invoker method 56 * @param method the method to invoke on the {@code super} object 57 * @param args the arguments to use for the invocation 58 * @return the result of the invocation of {@code method} with {@code args} on the 59 * {@code super} scope 60 */ 61 private var INVOKER:Function = function(object, method:Function, args:Array) { 62 // deletes the variable '__as2lib__invoker'; deletes the reference to this function 63 delete object.__as2lib__invoker; 64 // ('super' is not accessible from this scope, at least that's the compiler error) <-- this was at the time INVOKER was static 65 // eval("su" + "per") is not supported by MTASC. INVOKER must thus be an instance variable because normal 66 // 'super' usage is not allowed for per class / static functions 67 return method.apply(super, args); 68 }; 69 70 /** 71 * Returns the stringifier used to stringify method infos. 72 * 73 * <p>If no custom stringifier has been set via the {@link #setStringifier} method, 74 * a instance of the default {@code MethodInfoStringifier} class is returned. 75 * 76 * @return the stringifier that stringifies method infos 77 */ 78 public static function getStringifier(Void):Stringifier { 79 if (!stringifier) stringifier = new MethodInfoStringifier(); 80 return stringifier; 81 } 82 83 /** 84 * Sets the stringifier used to stringify method infos. 85 * 86 * <p>If {@code methodInfoStringifier} is {@code null} or {@code undefined} 87 * {@link #getStringifier} will return the default stringifier. 88 * 89 * @param methodInfoStringifier the stringifier that stringifies method infos 90 */ 91 public static function setStringifier(methodInfoStringifier:MethodInfoStringifier):Void { 92 stringifier = methodInfoStringifier; 93 } 94 95 /** The name of this method. */ 96 private var name:String; 97 98 /** The concrete method. */ 99 private var method:Function; 100 101 /** The type that declares this method. */ 102 private var declaringType:TypeInfo; 103 104 /** A flag representing whether this method is static or not. */ 105 private var staticFlag:Boolean; 106 107 /** 108 * Constructs a new {@code MethodInfo} instance. 109 * 110 * <p>All arguments are allowed to be {@code null}. But keep in mind that not all 111 * methods will function properly if one is. 112 * 113 * <p>If {@code method} is not specified, it will be resolved at run-time everytime 114 * it is needed. This means that the concrete method will always be up-to-date even 115 * if you have overwritten it. 116 * 117 * @param name the name of the method 118 * @param declaringType the declaring type of the method 119 * @param staticFlag determines whether the method is static 120 * @param method (optional) the concrete method 121 */ 122 public function MethodInfo(name:String, 123 declaringType:TypeInfo, 124 staticFlag:Boolean, 125 method:Function) { 126 this.name = name; 127 this.declaringType = declaringType; 128 this.staticFlag = staticFlag; 129 this.method = method; 130 } 131 132 /** 133 * Returns the name of this method. 134 * 135 * @return the name of this method 136 */ 137 public function getName(Void):String { 138 return name; 139 } 140 141 /** 142 * Returns the full name of this method. 143 * 144 * <p>The full name is the fully qualified name of the declaring type plus the name 145 * of this method. 146 * 147 * @return the full name of this method 148 */ 149 public function getFullName(Void):String { 150 if (declaringType.getFullName()) { 151 return declaringType.getFullName() + "." + name; 152 } 153 return name; 154 } 155 156 /** 157 * Returns the concrete method this instance represents. 158 * 159 * <p>If the concrete method was not specified on construction it will be resolved 160 * on run-time by this method everytime asked for. The returned method is thus 161 * always the current method on the declaring type. 162 * 163 * @return the concrete method 164 */ 165 public function getMethod(Void):Function { 166 if (method !== undefined) { 167 return method; 168 } 169 var t:Function = declaringType.getType(); 170 if (staticFlag) { 171 if (t[name] != t.__proto__[name]) { 172 return t[name]; 173 } 174 } 175 var p:Object = t.prototype; 176 if (p[name] != p.__proto__[name]) { 177 return p[name]; 178 } 179 return null; 180 } 181 182 /** 183 * Returns the type that declares this method. 184 * 185 * @return the type that declares this method 186 */ 187 public function getDeclaringType(Void):TypeInfo { 188 return declaringType; 189 } 190 191 /** 192 * Invokes this method on the passed-in {@code scope} passing the given {@code args}. 193 * 194 * <p>The object referenced by {@code this} in this method is the object this method 195 * is invoked on, its / the passed-in {@code scope}. 196 * 197 * @param scope the {@code this}-scope for the method invocation 198 * @param args the arguments to pass-to the method on invocation 199 * @return the return value of the method invocation 200 */ 201 public function invoke(scope, args:Array) { 202 // there is no super bug with apply and static methods because 'super' is not allowed in static methods 203 if (!staticFlag) { 204 var t:Function = declaringType.getType(); 205 // super bug can only be fixed if scope is an instance of the declaring type 206 // otherwise everything is messed up anyway 207 if (scope instanceof t) { 208 var p:Object = t.prototype; 209 // if scope is a direct instance of the declaring type super works as expected 210 if (scope.__proto__ != p) { 211 var s = scope.__proto__; 212 while (s.__proto__ != p) { 213 s = s.__proto__; 214 } 215 s.__as2lib__invoker = INVOKER; 216 return scope.__as2lib__invoker(s, getMethod(), args); 217 } 218 } 219 } 220 return getMethod().apply(scope, args); 221 } 222 223 /** 224 * Returns whether this method is static or not. 225 * 226 * <p>Static methods are methods per type. 227 * 228 * <p>Non-Static methods are methods per instance. 229 * 230 * @return {@code true} if this method is static else {@code false} 231 */ 232 public function isStatic(Void):Boolean { 233 return staticFlag; 234 } 235 236 /** 237 * Returns a method info that reflects the current state of this method info. 238 * 239 * @return a snapshot of this method info 240 */ 241 public function snapshot(Void):MethodInfo { 242 return new MethodInfo(name, declaringType, staticFlag, getMethod()); 243 } 244 245 /** 246 * Returns the string representation of this method. 247 * 248 * <p>The string representation is obtained via the stringifier returned by the 249 * static {@link #getStringifier} method. 250 * 251 * @return the string representation of this method 252 */ 253 public function toString():String { 254 return getStringifier().execute(this); 255 } 256 257 }