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     // 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      */
 114     public Kind getKind() {
 115         return kind;
 116     }
 117 
 118     /**
 119      * @param kind the kind to set
 120      */
 121     public void setKind(final Kind kind) {
 122         this.kind = kind;
 123     }
 124 
 125     /**
 126      * @return the name
 127      */
 128     public String getName() {
 129         return name;
 130     }
 131 
 132     /**
 133      * @param name the name to set
 134      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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 
 186     public void setLinkLogicClass(final Type linkLogicClass) {
 187         this.linkLogicClass = linkLogicClass;
 188     }
 189 
 190     /**
 191      * @return the attributes
 192      */
 193     public int getAttributes() {
 194         return attributes;
 195     }
 196 
 197     /**
 198      * @param attributes the attributes to set
 199      */
 200     public void setAttributes(final int attributes) {
 201         this.attributes = attributes;
 202     }
 203 
 204     /**
 205      * @return the javaName
 206      */
 207     public String getJavaName() {
 208         return javaName;
 209     }
 210 
 211     /**
 212      * @param javaName the javaName to set
 213      */
 214     public void setJavaName(final String javaName) {
 215         this.javaName = javaName;
 216     }
 217 
 218     /**
 219      * @return the javaDesc
 220      */
 221     public String getJavaDesc() {
 222         return javaDesc;
 223     }
 224 
 225     void setJavaDesc(final String javaDesc) {
 226         this.javaDesc = javaDesc;
 227     }
 228 
 229     int getJavaAccess() {
 230         return javaAccess;
 231     }
 232 
 233     void setJavaAccess(final int access) {
 234         this.javaAccess = access;
 235     }
 236 
 237     Object getValue() {
 238         return value;
 239     }
 240 
 241     void setValue(final Object value) {
 242         this.value = value;
 243     }
 244 
 245     Where getWhere() {
 246         return where;
 247     }
 248 
 249     void setWhere(final Where where) {
 250         this.where = where;
 251     }
 252 
 253     boolean isFinal() {
 254         return (javaAccess & Opcodes.ACC_FINAL) != 0;
 255     }
 256 
 257     boolean isStatic() {
 258         return (javaAccess & Opcodes.ACC_STATIC) != 0;
 259     }
 260 
 261     boolean isStaticFinal() {
 262         return isStatic() && isFinal();
 263     }
 264 
 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      */
 274     boolean isInstanceSetter() {
 275         return kind == Kind.SETTER && where == Where.INSTANCE;
 276     }
 277 
 278     boolean isInstanceProperty() {
 279         return kind == Kind.PROPERTY && where == Where.INSTANCE;
 280     }
 281 
 282     boolean isInstanceFunction() {
 283         return kind == Kind.FUNCTION && where == Where.INSTANCE;
 284     }
 285 
 286     boolean isPrototypeGetter() {
 287         return kind == Kind.GETTER && where == Where.PROTOTYPE;
 288     }
 289 
 290     boolean isPrototypeSetter() {
 291         return kind == Kind.SETTER && where == Where.PROTOTYPE;
 292     }
 293 
 294     boolean isPrototypeProperty() {
 295         return kind == Kind.PROPERTY && where == Where.PROTOTYPE;
 296     }
 297 
 298     boolean isPrototypeFunction() {
 299         return kind == Kind.FUNCTION && where == Where.PROTOTYPE;
 300     }
 301 
 302     boolean isConstructorGetter() {
 303         return kind == Kind.GETTER && where == Where.CONSTRUCTOR;
 304     }
 305 
 306     boolean isConstructorSetter() {
 307         return kind == Kind.SETTER && where == Where.CONSTRUCTOR;
 308     }
 309 
 310     boolean isConstructorProperty() {
 311         return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR;
 312     }
 313 
 314     boolean isConstructorFunction() {
 315         return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR;
 316     }
 317 
 318     boolean isConstructor() {
 319         return kind == Kind.CONSTRUCTOR;
 320     }
 321 
 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 
 461     private static boolean isValidJSType(final Type type) {
 462         return isJSPrimitiveType(type) || isJSObjectType(type);
 463     }
 464 
 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 
 477     private static boolean isJSObjectType(final Type type) {
 478         return isJavaLangObject(type) || isJavaLangString(type) || isScriptObject(type);
 479     }
 480 
 481     private static boolean isJavaLangObject(final Type type) {
 482         return type.getDescriptor().equals(OBJECT_DESC);
 483     }
 484 
 485     private static boolean isJavaLangString(final Type type) {
 486         return type.getDescriptor().equals(STRING_DESC);
 487     }
 488 
 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, MY_LOADER);
 497                 return ScriptObject.class.isAssignableFrom(clazz);
 498             } catch (final ClassNotFoundException cnfe) {
 499                 return false;
 500             }
 501         }
 502 
 503         return false;
 504     }
 505 
 506     private void error(final String msg) {
 507         throw new RuntimeException(javaName + " of type " + javaDesc + " : " + msg);
 508     }
 509 
 510     /**
 511      * @return the initClass
 512      */
 513     String getInitClass() {
 514         return initClass;
 515     }
 516 
 517     /**
 518      * @param initClass the initClass to set
 519      */
 520     void setInitClass(final String initClass) {
 521         this.initClass = initClass;
 522     }
 523 
 524     @Override
 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      */
 537     int getArity() {
 538         return arity;
 539     }
 540 
 541     /**
 542      * @param arity the arity to set
 543      */
 544     void setArity(final int arity) {
 545         this.arity = arity;
 546     }
 547 }
--- EOF ---