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 
  26 package jdk.nashorn.internal.tools.nasgen;
  27 
  28 import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC;
  29 import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
  30 
  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 
  35 /**
  36  * Details about a Java method or field annotated with any of the field/method
  37  * annotations from the jdk.nashorn.internal.objects.annotations package.
  38  */
  39 public final class MemberInfo implements Cloneable {
  40     /**
  41      * The different kinds of available class annotations
  42      */
  43     public static enum Kind {
  44         /** This is a script class */
  45         SCRIPT_CLASS,
  46         /** This is a constructor */
  47         CONSTRUCTOR,
  48         /** This is a function */
  49         FUNCTION,
  50         /** This is a getter */
  51         GETTER,
  52         /** This is a setter */
  53         SETTER,
  54         /** This is a property */
  55         PROPERTY,
  56         /** This is a specialized version of a function */
  57         SPECIALIZED_FUNCTION,
  58         /** This is a specialized version of a constructor */
  59         SPECIALIZED_CONSTRUCTOR
  60     }
  61 
  62     // keep in sync with jdk.nashorn.internal.objects.annotations.Attribute
  63     static final int DEFAULT_ATTRIBUTES = 0x0;
  64 
  65     static final int DEFAULT_ARITY = -2;
  66 
  67     // the kind of the script annotation - one of the above constants
  68     private MemberInfo.Kind kind;
  69     // script property name
  70     private String name;
  71     // script property attributes
  72     private int attributes;
  73     // name of the java member
  74     private String javaName;
  75     // type descriptor of the java member
  76     private String javaDesc;
  77     // access bits of the Java field or method
  78     private int javaAccess;
  79     // initial value for static @Property fields
  80     private Object value;
  81     // class whose object is created to fill property value
  82     private String initClass;
  83     // arity of the Function or Constructor
  84     private int arity;
  85 
  86     private Where where;
  87 
  88     /**
  89      * @return the kind
  90      */
  91     public Kind getKind() {
  92         return kind;
  93     }
  94 
  95     /**
  96      * @param kind the kind to set
  97      */
  98     public void setKind(final Kind kind) {
  99         this.kind = kind;
 100     }
 101 
 102     /**
 103      * @return the name
 104      */
 105     public String getName() {
 106         return name;
 107     }
 108 
 109     /**
 110      * @param name the name to set
 111      */
 112     public void setName(final String name) {
 113         this.name = name;
 114     }
 115 
 116     /**
 117      * @return the attributes
 118      */
 119     public int getAttributes() {
 120         return attributes;
 121     }
 122 
 123     /**
 124      * @param attributes the attributes to set
 125      */
 126     public void setAttributes(final int attributes) {
 127         this.attributes = attributes;
 128     }
 129 
 130     /**
 131      * @return the javaName
 132      */
 133     public String getJavaName() {
 134         return javaName;
 135     }
 136 
 137     /**
 138      * @param javaName the javaName to set
 139      */
 140     public void setJavaName(final String javaName) {
 141         this.javaName = javaName;
 142     }
 143 
 144     /**
 145      * @return the javaDesc
 146      */
 147     public String getJavaDesc() {
 148         return javaDesc;
 149     }
 150 
 151     void setJavaDesc(final String javaDesc) {
 152         this.javaDesc = javaDesc;
 153     }
 154 
 155     int getJavaAccess() {
 156         return javaAccess;
 157     }
 158 
 159     void setJavaAccess(final int access) {
 160         this.javaAccess = access;
 161     }
 162 
 163     Object getValue() {
 164         return value;
 165     }
 166 
 167     void setValue(final Object value) {
 168         this.value = value;
 169     }
 170 
 171     Where getWhere() {
 172         return where;
 173     }
 174 
 175     void setWhere(final Where where) {
 176         this.where = where;
 177     }
 178 
 179     boolean isFinal() {
 180         return (javaAccess & Opcodes.ACC_FINAL) != 0;
 181     }
 182 
 183     boolean isStatic() {
 184         return (javaAccess & Opcodes.ACC_STATIC) != 0;
 185     }
 186 
 187     boolean isStaticFinal() {
 188         return isStatic() && isFinal();
 189     }
 190 
 191     boolean isInstanceGetter() {
 192         return kind == Kind.GETTER && where == Where.INSTANCE;
 193     }
 194 
 195     /**
 196      * Check whether this MemberInfo is a getter that resides in the instance
 197      * @return true if instance setter
 198      */
 199     boolean isInstanceSetter() {
 200         return kind == Kind.SETTER && where == Where.INSTANCE;
 201     }
 202 
 203     boolean isInstanceProperty() {
 204         return kind == Kind.PROPERTY && where == Where.INSTANCE;
 205     }
 206 
 207     boolean isInstanceFunction() {
 208         return kind == Kind.FUNCTION && where == Where.INSTANCE;
 209     }
 210 
 211     boolean isPrototypeGetter() {
 212         return kind == Kind.GETTER && where == Where.PROTOTYPE;
 213     }
 214 
 215     boolean isPrototypeSetter() {
 216         return kind == Kind.SETTER && where == Where.PROTOTYPE;
 217     }
 218 
 219     boolean isPrototypeProperty() {
 220         return kind == Kind.PROPERTY && where == Where.PROTOTYPE;
 221     }
 222 
 223     boolean isPrototypeFunction() {
 224         return kind == Kind.FUNCTION && where == Where.PROTOTYPE;
 225     }
 226 
 227     boolean isConstructorGetter() {
 228         return kind == Kind.GETTER && where == Where.CONSTRUCTOR;
 229     }
 230 
 231     boolean isConstructorSetter() {
 232         return kind == Kind.SETTER && where == Where.CONSTRUCTOR;
 233     }
 234 
 235     boolean isConstructorProperty() {
 236         return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR;
 237     }
 238 
 239     boolean isConstructorFunction() {
 240         return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR;
 241     }
 242 
 243     boolean isConstructor() {
 244         return kind == Kind.CONSTRUCTOR;
 245     }
 246 
 247     void verify() {
 248         if (kind == Kind.CONSTRUCTOR) {
 249             final Type returnType = Type.getReturnType(javaDesc);
 250             if (! returnType.toString().equals(OBJECT_DESC)) {
 251                 error("return value should be of Object type, found" + returnType);
 252             }
 253             final Type[] argTypes = Type.getArgumentTypes(javaDesc);
 254             if (argTypes.length < 2) {
 255                 error("constructor methods should have at least 2 args");
 256             }
 257             if (! argTypes[0].equals(Type.BOOLEAN_TYPE)) {
 258                 error("first argument should be of boolean type, found" + argTypes[0]);
 259             }
 260             if (! argTypes[1].toString().equals(OBJECT_DESC)) {
 261                 error("second argument should be of Object type, found" + argTypes[0]);
 262             }
 263 
 264             if (argTypes.length > 2) {
 265                 for (int i = 2; i < argTypes.length - 1; i++) {
 266                     if (! argTypes[i].toString().equals(OBJECT_DESC)) {
 267                         error(i + "'th argument should be of Object type, found " + argTypes[i]);
 268                     }
 269                 }
 270 
 271                 final String lastArgType = argTypes[argTypes.length - 1].toString();
 272                 final boolean isVarArg = lastArgType.equals(OBJECT_ARRAY_DESC);
 273                 if (!lastArgType.equals(OBJECT_DESC) && !isVarArg) {
 274                     error("last argument is neither Object nor Object[] type: " + lastArgType);
 275                 }
 276 
 277                 if (isVarArg && argTypes.length > 3) {
 278                     error("vararg constructor has more than 3 arguments");
 279                 }
 280             }
 281         } else if (kind == Kind.FUNCTION) {
 282             final Type[] argTypes = Type.getArgumentTypes(javaDesc);
 283             if (argTypes.length < 1) {
 284                 error("function methods should have at least 1 arg");
 285             }
 286             if (! argTypes[0].toString().equals(OBJECT_DESC)) {
 287                 error("first argument should be of Object type, found" + argTypes[0]);
 288             }
 289 
 290             if (argTypes.length > 1) {
 291                 for (int i = 1; i < argTypes.length - 1; i++) {
 292                     if (! argTypes[i].toString().equals(OBJECT_DESC)) {
 293                         error(i + "'th argument should be of Object type, found " + argTypes[i]);
 294                     }
 295                 }
 296 
 297                 final String lastArgType = argTypes[argTypes.length - 1].toString();
 298                 final boolean isVarArg = lastArgType.equals(OBJECT_ARRAY_DESC);
 299                 if (!lastArgType.equals(OBJECT_DESC) && !isVarArg) {
 300                     error("last argument is neither Object nor Object[] type: " + lastArgType);
 301                 }
 302 
 303                 if (isVarArg && argTypes.length > 2) {
 304                     error("vararg function has more than 2 arguments");
 305                 }
 306             }
 307         } else if (kind == Kind.GETTER) {
 308             final Type[] argTypes = Type.getArgumentTypes(javaDesc);
 309             if (argTypes.length != 1) {
 310                 error("getter methods should have one argument");
 311             }
 312             if (! argTypes[0].toString().equals(OBJECT_DESC)) {
 313                 error("first argument of getter should be of Object type, found: " + argTypes[0]);
 314             }
 315             if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) {
 316                 error("return type of getter should not be void");
 317             }
 318         } else if (kind == Kind.SETTER) {
 319             final Type[] argTypes = Type.getArgumentTypes(javaDesc);
 320             if (argTypes.length != 2) {
 321                 error("setter methods should have two arguments");
 322             }
 323             if (! argTypes[0].toString().equals(OBJECT_DESC)) {
 324                 error("first argument of setter should be of Object type, found: " + argTypes[0]);
 325             }
 326             if (!Type.getReturnType(javaDesc).toString().equals("V")) {
 327                 error("return type of setter should be void, found: " + Type.getReturnType(javaDesc));
 328             }
 329         }
 330     }
 331 
 332     private void error(final String msg) {
 333         throw new RuntimeException(javaName + javaDesc + " : " + msg);
 334     }
 335 
 336     /**
 337      * @return the initClass
 338      */
 339     String getInitClass() {
 340         return initClass;
 341     }
 342 
 343     /**
 344      * @param initClass the initClass to set
 345      */
 346     void setInitClass(final String initClass) {
 347         this.initClass = initClass;
 348     }
 349 
 350     @Override
 351     protected Object clone() {
 352         try {
 353             return super.clone();
 354         } catch (final CloneNotSupportedException e) {
 355             assert false : "clone not supported " + e;
 356             return null;
 357         }
 358     }
 359 
 360     /**
 361      * @return the arity
 362      */
 363     int getArity() {
 364         return arity;
 365     }
 366 
 367     /**
 368      * @param arity the arity to set
 369      */
 370     void setArity(final int arity) {
 371         this.arity = arity;
 372     }
 373 }