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