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