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