MemberInfo.java revision 1036:f0b5e3900a10
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 */
25package jdk.nashorn.internal.tools.nasgen;
26
27import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC;
28import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
29import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC;
30import static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC;
31import jdk.internal.org.objectweb.asm.Opcodes;
32import jdk.internal.org.objectweb.asm.Type;
33import jdk.nashorn.internal.objects.annotations.Where;
34import 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 */
40public 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     */
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 optimitic 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 thre 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, myLoader);
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}
548