MemberInfo.java revision 1626:d99fa86747ee
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; 31import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_SYMBOL; 32 33import jdk.internal.org.objectweb.asm.Opcodes; 34import jdk.internal.org.objectweb.asm.Type; 35import jdk.nashorn.internal.objects.annotations.Where; 36import jdk.nashorn.internal.runtime.ScriptObject; 37 38/** 39 * Details about a Java method or field annotated with any of the field/method 40 * annotations from the jdk.nashorn.internal.objects.annotations package. 41 */ 42public final class MemberInfo implements Cloneable { 43 // class loader of this class 44 private static final ClassLoader MY_LOADER = MemberInfo.class.getClassLoader(); 45 46 /** 47 * The different kinds of available class annotations 48 */ 49 public static enum Kind { 50 51 /** 52 * This is a script class 53 */ 54 SCRIPT_CLASS, 55 /** 56 * This is a constructor 57 */ 58 CONSTRUCTOR, 59 /** 60 * This is a function 61 */ 62 FUNCTION, 63 /** 64 * This is a getter 65 */ 66 GETTER, 67 /** 68 * This is a setter 69 */ 70 SETTER, 71 /** 72 * This is a property 73 */ 74 PROPERTY, 75 /** 76 * This is a specialized version of a function 77 */ 78 SPECIALIZED_FUNCTION, 79 } 80 81 // keep in sync with jdk.nashorn.internal.objects.annotations.Attribute 82 static final int DEFAULT_ATTRIBUTES = 0x0; 83 84 static final int DEFAULT_ARITY = -2; 85 86 // the kind of the script annotation - one of the above constants 87 private MemberInfo.Kind kind; 88 // script property name 89 private String name; 90 // script property attributes 91 private int attributes; 92 // name of the java member 93 private String javaName; 94 // type descriptor of the java member 95 private String javaDesc; 96 // access bits of the Java field or method 97 private int javaAccess; 98 // initial value for static @Property fields 99 private Object value; 100 // class whose object is created to fill property value 101 private String initClass; 102 // arity of the Function or Constructor 103 private int arity; 104 105 private Where where; 106 107 private Type linkLogicClass; 108 109 private boolean isSpecializedConstructor; 110 111 private boolean isOptimistic; 112 113 /** 114 * @return the kind 115 */ 116 public Kind getKind() { 117 return kind; 118 } 119 120 /** 121 * @param kind the kind to set 122 */ 123 public void setKind(final Kind kind) { 124 this.kind = kind; 125 } 126 127 /** 128 * @return the name 129 */ 130 public String getName() { 131 return name; 132 } 133 134 /** 135 * @param name the name to set 136 */ 137 public void setName(final String name) { 138 this.name = name; 139 } 140 141 /** 142 * Tag something as specialized constructor or not 143 * @param isSpecializedConstructor boolean, true if specialized constructor 144 */ 145 public void setIsSpecializedConstructor(final boolean isSpecializedConstructor) { 146 this.isSpecializedConstructor = isSpecializedConstructor; 147 } 148 149 /** 150 * Check if something is a specialized constructor 151 * @return true if specialized constructor 152 */ 153 public boolean isSpecializedConstructor() { 154 return isSpecializedConstructor; 155 } 156 157 /** 158 * Check if this is an optimistic builtin function 159 * @return true if optimistic builtin 160 */ 161 public boolean isOptimistic() { 162 return isOptimistic; 163 } 164 165 /** 166 * Tag something as optimistic builtin or not 167 * @param isOptimistic boolean, true if builtin constructor 168 */ 169 public void setIsOptimistic(final boolean isOptimistic) { 170 this.isOptimistic = isOptimistic; 171 } 172 173 /** 174 * Get the SpecializedFunction guard for specializations, i.e. optimistic 175 * builtins 176 * @return specialization, null if none 177 */ 178 public Type getLinkLogicClass() { 179 return linkLogicClass; 180 } 181 182 /** 183 * Set the SpecializedFunction link logic class for specializations, i.e. optimistic 184 * builtins 185 * @param linkLogicClass link logic class 186 */ 187 188 public void setLinkLogicClass(final Type linkLogicClass) { 189 this.linkLogicClass = linkLogicClass; 190 } 191 192 /** 193 * @return the attributes 194 */ 195 public int getAttributes() { 196 return attributes; 197 } 198 199 /** 200 * @param attributes the attributes to set 201 */ 202 public void setAttributes(final int attributes) { 203 this.attributes = attributes; 204 } 205 206 /** 207 * @return the javaName 208 */ 209 public String getJavaName() { 210 return javaName; 211 } 212 213 /** 214 * @param javaName the javaName to set 215 */ 216 public void setJavaName(final String javaName) { 217 this.javaName = javaName; 218 } 219 220 /** 221 * @return the javaDesc 222 */ 223 public String getJavaDesc() { 224 return javaDesc; 225 } 226 227 void setJavaDesc(final String javaDesc) { 228 this.javaDesc = javaDesc; 229 } 230 231 int getJavaAccess() { 232 return javaAccess; 233 } 234 235 void setJavaAccess(final int access) { 236 this.javaAccess = access; 237 } 238 239 Object getValue() { 240 return value; 241 } 242 243 void setValue(final Object value) { 244 this.value = value; 245 } 246 247 Where getWhere() { 248 return where; 249 } 250 251 void setWhere(final Where where) { 252 this.where = where; 253 } 254 255 boolean isFinal() { 256 return (javaAccess & Opcodes.ACC_FINAL) != 0; 257 } 258 259 boolean isStatic() { 260 return (javaAccess & Opcodes.ACC_STATIC) != 0; 261 } 262 263 boolean isStaticFinal() { 264 return isStatic() && isFinal(); 265 } 266 267 boolean isInstanceGetter() { 268 return kind == Kind.GETTER && where == Where.INSTANCE; 269 } 270 271 /** 272 * Check whether this MemberInfo is a getter that resides in the instance 273 * 274 * @return true if instance setter 275 */ 276 boolean isInstanceSetter() { 277 return kind == Kind.SETTER && where == Where.INSTANCE; 278 } 279 280 boolean isInstanceProperty() { 281 return kind == Kind.PROPERTY && where == Where.INSTANCE; 282 } 283 284 boolean isInstanceFunction() { 285 return kind == Kind.FUNCTION && where == Where.INSTANCE; 286 } 287 288 boolean isPrototypeGetter() { 289 return kind == Kind.GETTER && where == Where.PROTOTYPE; 290 } 291 292 boolean isPrototypeSetter() { 293 return kind == Kind.SETTER && where == Where.PROTOTYPE; 294 } 295 296 boolean isPrototypeProperty() { 297 return kind == Kind.PROPERTY && where == Where.PROTOTYPE; 298 } 299 300 boolean isPrototypeFunction() { 301 return kind == Kind.FUNCTION && where == Where.PROTOTYPE; 302 } 303 304 boolean isConstructorGetter() { 305 return kind == Kind.GETTER && where == Where.CONSTRUCTOR; 306 } 307 308 boolean isConstructorSetter() { 309 return kind == Kind.SETTER && where == Where.CONSTRUCTOR; 310 } 311 312 boolean isConstructorProperty() { 313 return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR; 314 } 315 316 boolean isConstructorFunction() { 317 return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR; 318 } 319 320 boolean isConstructor() { 321 return kind == Kind.CONSTRUCTOR; 322 } 323 324 void verify() { 325 switch (kind) { 326 case CONSTRUCTOR: { 327 final Type returnType = Type.getReturnType(javaDesc); 328 if (!isJSObjectType(returnType)) { 329 error("return value of a @Constructor method should be of Object type, found " + returnType); 330 } 331 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 332 if (argTypes.length < 2) { 333 error("@Constructor methods should have at least 2 args"); 334 } 335 if (!argTypes[0].equals(Type.BOOLEAN_TYPE)) { 336 error("first argument of a @Constructor method should be of boolean type, found " + argTypes[0]); 337 } 338 if (!isJavaLangObject(argTypes[1])) { 339 error("second argument of a @Constructor method should be of Object type, found " + argTypes[0]); 340 } 341 342 if (argTypes.length > 2) { 343 for (int i = 2; i < argTypes.length - 1; i++) { 344 if (!isJavaLangObject(argTypes[i])) { 345 error(i + "'th argument of a @Constructor method should be of Object type, found " + argTypes[i]); 346 } 347 } 348 349 final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); 350 final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); 351 if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { 352 error("last argument of a @Constructor method is neither Object nor Object[] type: " + lastArgTypeDesc); 353 } 354 355 if (isVarArg && argTypes.length > 3) { 356 error("vararg of a @Constructor method has more than 3 arguments"); 357 } 358 } 359 } 360 break; 361 case FUNCTION: { 362 final Type returnType = Type.getReturnType(javaDesc); 363 if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) { 364 error("return value of a @Function method should be a valid JS type, found " + returnType); 365 } 366 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 367 if (argTypes.length < 1) { 368 error("@Function methods should have at least 1 arg"); 369 } 370 if (!isJavaLangObject(argTypes[0])) { 371 error("first argument of a @Function method should be of Object type, found " + argTypes[0]); 372 } 373 374 if (argTypes.length > 1) { 375 for (int i = 1; i < argTypes.length - 1; i++) { 376 if (!isJavaLangObject(argTypes[i])) { 377 error(i + "'th argument of a @Function method should be of Object type, found " + argTypes[i]); 378 } 379 } 380 381 final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); 382 final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); 383 if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { 384 error("last argument of a @Function method is neither Object nor Object[] type: " + lastArgTypeDesc); 385 } 386 387 if (isVarArg && argTypes.length > 2) { 388 error("vararg @Function method has more than 2 arguments"); 389 } 390 } 391 } 392 break; 393 case SPECIALIZED_FUNCTION: { 394 final Type returnType = Type.getReturnType(javaDesc); 395 if (!(isValidJSType(returnType) || (isSpecializedConstructor() && Type.VOID_TYPE == returnType))) { 396 error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType); 397 } 398 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 399 for (int i = 0; i < argTypes.length; i++) { 400 if (!isValidJSType(argTypes[i])) { 401 error(i + "'th argument of a @SpecializedFunction method is not valid JS type, found " + argTypes[i]); 402 } 403 } 404 } 405 break; 406 case GETTER: { 407 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 408 if (argTypes.length != 1) { 409 error("@Getter methods should have one argument"); 410 } 411 if (!isJavaLangObject(argTypes[0])) { 412 error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]); 413 } 414 415 if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) { 416 error("return type of getter should not be void"); 417 } 418 } 419 break; 420 case SETTER: { 421 final Type[] argTypes = Type.getArgumentTypes(javaDesc); 422 if (argTypes.length != 2) { 423 error("@Setter methods should have two arguments"); 424 } 425 if (!isJavaLangObject(argTypes[0])) { 426 error("first argument of a @Setter method should be of Object type, found: " + argTypes[0]); 427 } 428 if (!Type.getReturnType(javaDesc).toString().equals("V")) { 429 error("return type of of a @Setter method should be void, found: " + Type.getReturnType(javaDesc)); 430 } 431 } 432 break; 433 case PROPERTY: { 434 if (where == Where.CONSTRUCTOR) { 435 if (isStatic()) { 436 if (!isFinal()) { 437 error("static Where.CONSTRUCTOR @Property should be final"); 438 } 439 440 if (!isJSPrimitiveType(Type.getType(javaDesc))) { 441 error("static Where.CONSTRUCTOR @Property should be a JS primitive"); 442 } 443 } 444 } else if (where == Where.PROTOTYPE) { 445 if (isStatic()) { 446 if (!isFinal()) { 447 error("static Where.PROTOTYPE @Property should be final"); 448 } 449 450 if (!isJSPrimitiveType(Type.getType(javaDesc))) { 451 error("static Where.PROTOTYPE @Property should be a JS primitive"); 452 } 453 } 454 } 455 } 456 break; 457 458 default: 459 break; 460 } 461 } 462 463 private static boolean isValidJSType(final Type type) { 464 return isJSPrimitiveType(type) || isJSObjectType(type); 465 } 466 467 private static boolean isJSPrimitiveType(final Type type) { 468 switch (type.getSort()) { 469 case Type.BOOLEAN: 470 case Type.INT: 471 case Type.DOUBLE: 472 return true; 473 default: 474 return type != TYPE_SYMBOL; 475 } 476 } 477 478 private static boolean isJSObjectType(final Type type) { 479 return isJavaLangObject(type) || isJavaLangString(type) || isScriptObject(type); 480 } 481 482 private static boolean isJavaLangObject(final Type type) { 483 return type.getDescriptor().equals(OBJECT_DESC); 484 } 485 486 private static boolean isJavaLangString(final Type type) { 487 return type.getDescriptor().equals(STRING_DESC); 488 } 489 490 private static boolean isScriptObject(final Type type) { 491 if (type.getDescriptor().equals(SCRIPTOBJECT_DESC)) { 492 return true; 493 } 494 495 if (type.getSort() == Type.OBJECT) { 496 try { 497 final Class<?> clazz = Class.forName(type.getClassName(), false, MY_LOADER); 498 return ScriptObject.class.isAssignableFrom(clazz); 499 } catch (final ClassNotFoundException cnfe) { 500 return false; 501 } 502 } 503 504 return false; 505 } 506 507 private void error(final String msg) { 508 throw new RuntimeException(javaName + " of type " + javaDesc + " : " + msg); 509 } 510 511 /** 512 * @return the initClass 513 */ 514 String getInitClass() { 515 return initClass; 516 } 517 518 /** 519 * @param initClass the initClass to set 520 */ 521 void setInitClass(final String initClass) { 522 this.initClass = initClass; 523 } 524 525 @Override 526 protected Object clone() { 527 try { 528 return super.clone(); 529 } catch (final CloneNotSupportedException e) { 530 assert false : "clone not supported " + e; 531 return null; 532 } 533 } 534 535 /** 536 * @return the arity 537 */ 538 int getArity() { 539 return arity; 540 } 541 542 /** 543 * @param arity the arity to set 544 */ 545 void setArity(final int arity) { 546 this.arity = arity; 547 } 548 549 String getDocumentationKey(final String objName) { 550 if (kind == Kind.FUNCTION) { 551 StringBuilder buf = new StringBuilder(objName); 552 switch (where) { 553 case CONSTRUCTOR: 554 break; 555 case PROTOTYPE: 556 buf.append(".prototype"); 557 break; 558 case INSTANCE: 559 buf.append(".this"); 560 break; 561 } 562 buf.append('.'); 563 buf.append(name); 564 return buf.toString(); 565 } 566 567 return null; 568 } 569} 570