MemberInfo.java revision 868:f44ec6545b9a
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package jdk.nashorn.internal.tools.nasgen; 26 27import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC; 28import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; 29import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC; 30import static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC; 31 32import jdk.internal.org.objectweb.asm.Opcodes; 33import jdk.internal.org.objectweb.asm.Type; 34import jdk.nashorn.internal.objects.annotations.Where; 35import jdk.nashorn.internal.runtime.ScriptObject; 36 37/** 38 * Details about a Java method or field annotated with any of the field/method 39 * annotations from the jdk.nashorn.internal.objects.annotations package. 40 */ 41public final class MemberInfo implements Cloneable { 42 // class loader of this class 43 private static ClassLoader myLoader = MemberInfo.class.getClassLoader(); 44 45 /** 46 * The different kinds of available class annotations 47 */ 48 public static enum Kind { 49 50 /** 51 * This is a script class 52 */ 53 SCRIPT_CLASS, 54 /** 55 * This is a constructor 56 */ 57 CONSTRUCTOR, 58 /** 59 * This is a function 60 */ 61 FUNCTION, 62 /** 63 * This is a getter 64 */ 65 GETTER, 66 /** 67 * This is a setter 68 */ 69 SETTER, 70 /** 71 * This is a property 72 */ 73 PROPERTY, 74 /** 75 * This is a specialized version of a function 76 */ 77 SPECIALIZED_FUNCTION, 78 /** 79 * This is a specialized version of a constructor 80 */ 81 SPECIALIZED_CONSTRUCTOR 82 } 83 84 // keep in sync with jdk.nashorn.internal.objects.annotations.Attribute 85 static final int DEFAULT_ATTRIBUTES = 0x0; 86 87 static final int DEFAULT_ARITY = -2; 88 89 // the kind of the script annotation - one of the above constants 90 private MemberInfo.Kind kind; 91 // script property name 92 private String name; 93 // script property attributes 94 private int attributes; 95 // name of the java member 96 private String javaName; 97 // type descriptor of the java member 98 private String javaDesc; 99 // access bits of the Java field or method 100 private int javaAccess; 101 // initial value for static @Property fields 102 private Object value; 103 // class whose object is created to fill property value 104 private String initClass; 105 // arity of the Function or Constructor 106 private int arity; 107 108 private Where where; 109 110 /** 111 * @return the kind 112 */ 113 public Kind getKind() { 114 return kind; 115 } 116 117 /** 118 * @param kind the kind to set 119 */ 120 public void setKind(final Kind kind) { 121 this.kind = kind; 122 } 123 124 /** 125 * @return the name 126 */ 127 public String getName() { 128 return name; 129 } 130 131 /** 132 * @param name the name to set 133 */ 134 public void setName(final String name) { 135 this.name = name; 136 } 137 138 /** 139 * @return the attributes 140 */ 141 public int getAttributes() { 142 return attributes; 143 } 144 145 /** 146 * @param attributes the attributes to set 147 */ 148 public void setAttributes(final int attributes) { 149 this.attributes = attributes; 150 } 151 152 /** 153 * @return the javaName 154 */ 155 public String getJavaName() { 156 return javaName; 157 } 158 159 /** 160 * @param javaName the javaName to set 161 */ 162 public void setJavaName(final String javaName) { 163 this.javaName = javaName; 164 } 165 166 /** 167 * @return the javaDesc 168 */ 169 public String getJavaDesc() { 170 return javaDesc; 171 } 172 173 void setJavaDesc(final String javaDesc) { 174 this.javaDesc = javaDesc; 175 } 176 177 int getJavaAccess() { 178 return javaAccess; 179 } 180 181 void setJavaAccess(final int access) { 182 this.javaAccess = access; 183 } 184 185 Object getValue() { 186 return value; 187 } 188 189 void setValue(final Object value) { 190 this.value = value; 191 } 192 193 Where getWhere() { 194 return where; 195 } 196 197 void setWhere(final Where where) { 198 this.where = where; 199 } 200 201 boolean isFinal() { 202 return (javaAccess & Opcodes.ACC_FINAL) != 0; 203 } 204 205 boolean isStatic() { 206 return (javaAccess & Opcodes.ACC_STATIC) != 0; 207 } 208 209 boolean isStaticFinal() { 210 return isStatic() && isFinal(); 211 } 212 213 boolean isInstanceGetter() { 214 return kind == Kind.GETTER && where == Where.INSTANCE; 215 } 216 217 /** 218 * Check whether this MemberInfo is a getter that resides in the instance 219 * 220 * @return true if instance setter 221 */ 222 boolean isInstanceSetter() { 223 return kind == Kind.SETTER && where == Where.INSTANCE; 224 } 225 226 boolean isInstanceProperty() { 227 return kind == Kind.PROPERTY && where == Where.INSTANCE; 228 } 229 230 boolean isInstanceFunction() { 231 return kind == Kind.FUNCTION && where == Where.INSTANCE; 232 } 233 234 boolean isPrototypeGetter() { 235 return kind == Kind.GETTER && where == Where.PROTOTYPE; 236 } 237 238 boolean isPrototypeSetter() { 239 return kind == Kind.SETTER && where == Where.PROTOTYPE; 240 } 241 242 boolean isPrototypeProperty() { 243 return kind == Kind.PROPERTY && where == Where.PROTOTYPE; 244 } 245 246 boolean isPrototypeFunction() { 247 return kind == Kind.FUNCTION && where == Where.PROTOTYPE; 248 } 249 250 boolean isConstructorGetter() { 251 return kind == Kind.GETTER && where == Where.CONSTRUCTOR; 252 } 253 254 boolean isConstructorSetter() { 255 return kind == Kind.SETTER && where == Where.CONSTRUCTOR; 256 } 257 258 boolean isConstructorProperty() { 259 return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR; 260 } 261 262 boolean isConstructorFunction() { 263 return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR; 264 } 265 266 boolean isConstructor() { 267 return kind == Kind.CONSTRUCTOR; 268 } 269 270 void verify() { 271 switch (kind) { 272 case CONSTRUCTOR: { 273 final Type returnType = Type.getReturnType(javaDesc); 274 if (!isJSObjectType(returnType)) { 275 error("return value of a @Constructor method should be of Object type, found " + returnType); 276 } 277 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 278 if (argTypes.length < 2) { 279 error("@Constructor methods should have at least 2 args"); 280 } 281 if (!argTypes[0].equals(Type.BOOLEAN_TYPE)) { 282 error("first argument of a @Constructor method should be of boolean type, found " + argTypes[0]); 283 } 284 if (!isJavaLangObject(argTypes[1])) { 285 error("second argument of a @Constructor method should be of Object type, found " + argTypes[0]); 286 } 287 288 if (argTypes.length > 2) { 289 for (int i = 2; i < argTypes.length - 1; i++) { 290 if (!isJavaLangObject(argTypes[i])) { 291 error(i + "'th argument of a @Constructor method should be of Object type, found " + argTypes[i]); 292 } 293 } 294 295 final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); 296 final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); 297 if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { 298 error("last argument of a @Constructor method is neither Object nor Object[] type: " + lastArgTypeDesc); 299 } 300 301 if (isVarArg && argTypes.length > 3) { 302 error("vararg of a @Constructor method has more than 3 arguments"); 303 } 304 } 305 } 306 break; 307 case SPECIALIZED_CONSTRUCTOR: { 308 final Type returnType = Type.getReturnType(javaDesc); 309 if (!isJSObjectType(returnType)) { 310 error("return value of a @SpecializedConstructor method should be a valid JS type, found " + returnType); 311 } 312 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 313 for (int i = 0; i < argTypes.length; i++) { 314 if (!isValidJSType(argTypes[i])) { 315 error(i + "'th argument of a @SpecializedConstructor method is not valid JS type, found " + argTypes[i]); 316 } 317 } 318 } 319 break; 320 case FUNCTION: { 321 final Type returnType = Type.getReturnType(javaDesc); 322 if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) { 323 error("return value of a @Function method should be a valid JS type, found " + returnType); 324 } 325 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 326 if (argTypes.length < 1) { 327 error("@Function methods should have at least 1 arg"); 328 } 329 if (!isJavaLangObject(argTypes[0])) { 330 error("first argument of a @Function method should be of Object type, found " + argTypes[0]); 331 } 332 333 if (argTypes.length > 1) { 334 for (int i = 1; i < argTypes.length - 1; i++) { 335 if (!isJavaLangObject(argTypes[i])) { 336 error(i + "'th argument of a @Function method should be of Object type, found " + argTypes[i]); 337 } 338 } 339 340 final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); 341 final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); 342 if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { 343 error("last argument of a @Function method is neither Object nor Object[] type: " + lastArgTypeDesc); 344 } 345 346 if (isVarArg && argTypes.length > 2) { 347 error("vararg @Function method has more than 2 arguments"); 348 } 349 } 350 } 351 break; 352 case SPECIALIZED_FUNCTION: { 353 final Type returnType = Type.getReturnType(javaDesc); 354 if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) { 355 error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType); 356 } 357 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 358 for (int i = 0; i < argTypes.length; i++) { 359 if (!isValidJSType(argTypes[i])) { 360 error(i + "'th argument of a @SpecializedFunction method is not valid JS type, found " + argTypes[i]); 361 } 362 } 363 } 364 break; 365 case GETTER: { 366 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 367 if (argTypes.length != 1) { 368 error("@Getter methods should have one argument"); 369 } 370 if (!isJavaLangObject(argTypes[0])) { 371 error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]); 372 } 373 374 if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) { 375 error("return type of getter should not be void"); 376 } 377 } 378 break; 379 case SETTER: { 380 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 381 if (argTypes.length != 2) { 382 error("@Setter methods should have two arguments"); 383 } 384 if (!isJavaLangObject(argTypes[0])) { 385 error("first argument of a @Setter method should be of Object type, found: " + argTypes[0]); 386 } 387 if (!Type.getReturnType(javaDesc).toString().equals("V")) { 388 error("return type of of a @Setter method should be void, found: " + Type.getReturnType(javaDesc)); 389 } 390 } 391 break; 392 case PROPERTY: { 393 if (where == Where.CONSTRUCTOR) { 394 if (isStatic()) { 395 if (!isFinal()) { 396 error("static Where.CONSTRUCTOR @Property should be final"); 397 } 398 399 if (!isJSPrimitiveType(Type.getType(javaDesc))) { 400 error("static Where.CONSTRUCTOR @Property should be a JS primitive"); 401 } 402 } 403 } else if (where == Where.PROTOTYPE) { 404 if (isStatic()) { 405 if (!isFinal()) { 406 error("static Where.PROTOTYPE @Property should be final"); 407 } 408 409 if (!isJSPrimitiveType(Type.getType(javaDesc))) { 410 error("static Where.PROTOTYPE @Property should be a JS primitive"); 411 } 412 } 413 } 414 } 415 } 416 } 417 418 private static boolean isValidJSType(final Type type) { 419 return isJSPrimitiveType(type) || isJSObjectType(type); 420 } 421 422 private static boolean isJSPrimitiveType(final Type type) { 423 switch (type.getSort()) { 424 case Type.BOOLEAN: 425 case Type.INT: 426 case Type.LONG: 427 case Type.DOUBLE: 428 return true; 429 default: 430 return false; 431 } 432 } 433 434 private static boolean isJSObjectType(final Type type) { 435 return isJavaLangObject(type) || isJavaLangString(type) || isScriptObject(type); 436 } 437 438 private static boolean isJavaLangObject(final Type type) { 439 return type.getDescriptor().equals(OBJECT_DESC); 440 } 441 442 private static boolean isJavaLangString(final Type type) { 443 return type.getDescriptor().equals(STRING_DESC); 444 } 445 446 private static boolean isScriptObject(final Type type) { 447 if (type.getDescriptor().equals(SCRIPTOBJECT_DESC)) { 448 return true; 449 } 450 451 if (type.getSort() == Type.OBJECT) { 452 try { 453 final Class clazz = Class.forName(type.getClassName(), false, myLoader); 454 return ScriptObject.class.isAssignableFrom(clazz); 455 } catch (final ClassNotFoundException cnfe) { 456 return false; 457 } 458 } 459 460 return false; 461 } 462 463 private void error(final String msg) { 464 throw new RuntimeException(javaName + " of type " + javaDesc + " : " + msg); 465 } 466 467 /** 468 * @return the initClass 469 */ 470 String getInitClass() { 471 return initClass; 472 } 473 474 /** 475 * @param initClass the initClass to set 476 */ 477 void setInitClass(final String initClass) { 478 this.initClass = initClass; 479 } 480 481 @Override 482 protected Object clone() { 483 try { 484 return super.clone(); 485 } catch (final CloneNotSupportedException e) { 486 assert false : "clone not supported " + e; 487 return null; 488 } 489 } 490 491 /** 492 * @return the arity 493 */ 494 int getArity() { 495 return arity; 496 } 497 498 /** 499 * @param arity the arity to set 500 */ 501 void setArity(final int arity) { 502 this.arity = arity; 503 } 504} 505