MemberInfo.java revision 775:b0bb00872963
1156321Sdamien/*
2156321Sdamien * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3156321Sdamien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4156321Sdamien *
5156321Sdamien * This code is free software; you can redistribute it and/or modify it
6156321Sdamien * under the terms of the GNU General Public License version 2 only, as
7156321Sdamien * published by the Free Software Foundation.  Oracle designates this
8156321Sdamien * particular file as subject to the "Classpath" exception as provided
9156321Sdamien * by Oracle in the LICENSE file that accompanied this code.
10156321Sdamien *
11156321Sdamien * This code is distributed in the hope that it will be useful, but WITHOUT
12156321Sdamien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13156321Sdamien * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14156321Sdamien * version 2 for more details (a copy is included in the LICENSE file that
15156321Sdamien * accompanied this code).
16156321Sdamien *
17156321Sdamien * You should have received a copy of the GNU General Public License version
18156321Sdamien * 2 along with this work; if not, write to the Free Software Foundation,
19156321Sdamien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20156321Sdamien *
21156321Sdamien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22156321Sdamien * or visit www.oracle.com if you need additional information or have any
23156321Sdamien * questions.
24156321Sdamien */
25156321Sdamienpackage jdk.nashorn.internal.tools.nasgen;
26156321Sdamien
27156321Sdamienimport static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC;
28156321Sdamienimport static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
29156321Sdamienimport static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC;
30156321Sdamienimport static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC;
31156321Sdamien
32156321Sdamienimport jdk.internal.org.objectweb.asm.Opcodes;
33156321Sdamienimport jdk.internal.org.objectweb.asm.Type;
34156321Sdamienimport jdk.nashorn.internal.objects.annotations.Where;
35156321Sdamienimport jdk.nashorn.internal.runtime.ScriptObject;
36164982Skevlo
37164982Skevlo/**
38156321Sdamien * Details about a Java method or field annotated with any of the field/method
39156321Sdamien * annotations from the jdk.nashorn.internal.objects.annotations package.
40156321Sdamien */
41156321Sdamienpublic final class MemberInfo implements Cloneable {
42156321Sdamien    // class loader of this class
43156321Sdamien    private static ClassLoader myLoader = MemberInfo.class.getClassLoader();
44156321Sdamien
45156321Sdamien    /**
46156321Sdamien     * The different kinds of available class annotations
47156321Sdamien     */
48156321Sdamien    public static enum Kind {
49156321Sdamien
50156321Sdamien        /**
51156321Sdamien         * This is a script class
52156321Sdamien         */
53156321Sdamien        SCRIPT_CLASS,
54156321Sdamien        /**
55156321Sdamien         * This is a constructor
56156321Sdamien         */
57156321Sdamien        CONSTRUCTOR,
58156321Sdamien        /**
59156321Sdamien         * This is a function
60156321Sdamien         */
61156321Sdamien        FUNCTION,
62156321Sdamien        /**
63156327Ssilby         * This is a getter
64156327Ssilby         */
65156327Ssilby        GETTER,
66156327Ssilby        /**
67156321Sdamien         * This is a setter
68156321Sdamien         */
69156321Sdamien        SETTER,
70156321Sdamien        /**
71156321Sdamien         * This is a property
72156321Sdamien         */
73156321Sdamien        PROPERTY,
74156321Sdamien        /**
75156321Sdamien         * This is a specialized version of a function
76156321Sdamien         */
77156321Sdamien        SPECIALIZED_FUNCTION,
78156321Sdamien        /**
79156321Sdamien         * This is a specialized version of a constructor
80156321Sdamien         */
81156321Sdamien        SPECIALIZED_CONSTRUCTOR
82156321Sdamien    }
83156321Sdamien
84156321Sdamien    // keep in sync with jdk.nashorn.internal.objects.annotations.Attribute
85156321Sdamien    static final int DEFAULT_ATTRIBUTES = 0x0;
86156321Sdamien
87156321Sdamien    static final int DEFAULT_ARITY = -2;
88156321Sdamien
89156321Sdamien    // the kind of the script annotation - one of the above constants
90156321Sdamien    private MemberInfo.Kind kind;
91156321Sdamien    // script property name
92156321Sdamien    private String name;
93156321Sdamien    // script property attributes
94156321Sdamien    private int attributes;
95156321Sdamien    // name of the java member
96156321Sdamien    private String javaName;
97156321Sdamien    // type descriptor of the java member
98156321Sdamien    private String javaDesc;
99156321Sdamien    // access bits of the Java field or method
100156321Sdamien    private int javaAccess;
101156321Sdamien    // initial value for static @Property fields
102156321Sdamien    private Object value;
103156321Sdamien    // class whose object is created to fill property value
104156321Sdamien    private String initClass;
105156321Sdamien    // arity of the Function or Constructor
106156321Sdamien    private int arity;
107156321Sdamien
108156321Sdamien    private Where where;
109156321Sdamien
110156321Sdamien    /**
111156321Sdamien     * @return the kind
112156321Sdamien     */
113156321Sdamien    public Kind getKind() {
114156321Sdamien        return kind;
115156321Sdamien    }
116156321Sdamien
117156321Sdamien    /**
118156321Sdamien     * @param kind the kind to set
119156321Sdamien     */
120156321Sdamien    public void setKind(final Kind kind) {
121156321Sdamien        this.kind = kind;
122156321Sdamien    }
123156321Sdamien
124156321Sdamien    /**
125156321Sdamien     * @return the name
126156321Sdamien     */
127156321Sdamien    public String getName() {
128156321Sdamien        return name;
129156321Sdamien    }
130156321Sdamien
131156321Sdamien    /**
132156321Sdamien     * @param name the name to set
133156321Sdamien     */
134156321Sdamien    public void setName(final String name) {
135156321Sdamien        this.name = name;
136156321Sdamien    }
137156321Sdamien
138156321Sdamien    /**
139156321Sdamien     * @return the attributes
140156321Sdamien     */
141156321Sdamien    public int getAttributes() {
142156321Sdamien        return attributes;
143156321Sdamien    }
144156321Sdamien
145156321Sdamien    /**
146156321Sdamien     * @param attributes the attributes to set
147156321Sdamien     */
148156321Sdamien    public void setAttributes(final int attributes) {
149156321Sdamien        this.attributes = attributes;
150156321Sdamien    }
151156321Sdamien
152156321Sdamien    /**
153156321Sdamien     * @return the javaName
154156321Sdamien     */
155156321Sdamien    public String getJavaName() {
156156321Sdamien        return javaName;
157156321Sdamien    }
158156321Sdamien
159156321Sdamien    /**
160156321Sdamien     * @param javaName the javaName to set
161156321Sdamien     */
162156321Sdamien    public void setJavaName(final String javaName) {
163156321Sdamien        this.javaName = javaName;
164156321Sdamien    }
165156321Sdamien
166156321Sdamien    /**
167156321Sdamien     * @return the javaDesc
168156321Sdamien     */
169156321Sdamien    public String getJavaDesc() {
170156321Sdamien        return javaDesc;
171156321Sdamien    }
172156321Sdamien
173156321Sdamien    void setJavaDesc(final String javaDesc) {
174156321Sdamien        this.javaDesc = javaDesc;
175156321Sdamien    }
176156321Sdamien
177156321Sdamien    int getJavaAccess() {
178156321Sdamien        return javaAccess;
179156321Sdamien    }
180156321Sdamien
181156321Sdamien    void setJavaAccess(final int access) {
182156321Sdamien        this.javaAccess = access;
183156321Sdamien    }
184156321Sdamien
185156321Sdamien    Object getValue() {
186156321Sdamien        return value;
187156321Sdamien    }
188156321Sdamien
189156321Sdamien    void setValue(final Object value) {
190156321Sdamien        this.value = value;
191156321Sdamien    }
192156321Sdamien
193156321Sdamien    Where getWhere() {
194156321Sdamien        return where;
195156321Sdamien    }
196156321Sdamien
197156321Sdamien    void setWhere(final Where where) {
198156321Sdamien        this.where = where;
199156321Sdamien    }
200156321Sdamien
201156321Sdamien    boolean isFinal() {
202156321Sdamien        return (javaAccess & Opcodes.ACC_FINAL) != 0;
203156321Sdamien    }
204156321Sdamien
205156321Sdamien    boolean isStatic() {
206156321Sdamien        return (javaAccess & Opcodes.ACC_STATIC) != 0;
207156321Sdamien    }
208156321Sdamien
209156321Sdamien    boolean isStaticFinal() {
210156321Sdamien        return isStatic() && isFinal();
211156321Sdamien    }
212156321Sdamien
213156321Sdamien    boolean isInstanceGetter() {
214156321Sdamien        return kind == Kind.GETTER && where == Where.INSTANCE;
215156321Sdamien    }
216156321Sdamien
217156321Sdamien    /**
218156321Sdamien     * Check whether this MemberInfo is a getter that resides in the instance
219156321Sdamien     *
220156321Sdamien     * @return true if instance setter
221156321Sdamien     */
222156321Sdamien    boolean isInstanceSetter() {
223156321Sdamien        return kind == Kind.SETTER && where == Where.INSTANCE;
224156321Sdamien    }
225156321Sdamien
226156321Sdamien    boolean isInstanceProperty() {
227156321Sdamien        return kind == Kind.PROPERTY && where == Where.INSTANCE;
228156321Sdamien    }
229156321Sdamien
230156321Sdamien    boolean isInstanceFunction() {
231156321Sdamien        return kind == Kind.FUNCTION && where == Where.INSTANCE;
232156321Sdamien    }
233156321Sdamien
234156321Sdamien    boolean isPrototypeGetter() {
235156321Sdamien        return kind == Kind.GETTER && where == Where.PROTOTYPE;
236156321Sdamien    }
237156321Sdamien
238156321Sdamien    boolean isPrototypeSetter() {
239156321Sdamien        return kind == Kind.SETTER && where == Where.PROTOTYPE;
240156321Sdamien    }
241156321Sdamien
242156321Sdamien    boolean isPrototypeProperty() {
243156321Sdamien        return kind == Kind.PROPERTY && where == Where.PROTOTYPE;
244156321Sdamien    }
245156321Sdamien
246156321Sdamien    boolean isPrototypeFunction() {
247156321Sdamien        return kind == Kind.FUNCTION && where == Where.PROTOTYPE;
248156321Sdamien    }
249156321Sdamien
250156321Sdamien    boolean isConstructorGetter() {
251156321Sdamien        return kind == Kind.GETTER && where == Where.CONSTRUCTOR;
252156321Sdamien    }
253156321Sdamien
254156321Sdamien    boolean isConstructorSetter() {
255156321Sdamien        return kind == Kind.SETTER && where == Where.CONSTRUCTOR;
256156321Sdamien    }
257156321Sdamien
258156321Sdamien    boolean isConstructorProperty() {
259156321Sdamien        return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR;
260156321Sdamien    }
261156321Sdamien
262156321Sdamien    boolean isConstructorFunction() {
263156321Sdamien        return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR;
264156321Sdamien    }
265156321Sdamien
266156321Sdamien    boolean isConstructor() {
267156321Sdamien        return kind == Kind.CONSTRUCTOR;
268156321Sdamien    }
269156321Sdamien
270156321Sdamien    void verify() {
271156321Sdamien        switch (kind) {
272156321Sdamien            case CONSTRUCTOR: {
273156321Sdamien                final Type returnType = Type.getReturnType(javaDesc);
274156321Sdamien                if (!isJSObjectType(returnType)) {
275156321Sdamien                    error("return value of a @Constructor method should be of Object type, found " + returnType);
276156321Sdamien                }
277156321Sdamien                final Type[] argTypes = Type.getArgumentTypes(javaDesc);
278156321Sdamien                if (argTypes.length < 2) {
279156321Sdamien                    error("@Constructor methods should have at least 2 args");
280156321Sdamien                }
281156321Sdamien                if (!argTypes[0].equals(Type.BOOLEAN_TYPE)) {
282156321Sdamien                    error("first argument of a @Constructor method should be of boolean type, found " + argTypes[0]);
283156321Sdamien                }
284156321Sdamien                if (!isJavaLangObject(argTypes[1])) {
285156321Sdamien                    error("second argument of a @Constructor method should be of Object type, found " + argTypes[0]);
286156321Sdamien                }
287156321Sdamien
288156321Sdamien                if (argTypes.length > 2) {
289156321Sdamien                    for (int i = 2; i < argTypes.length - 1; i++) {
290156321Sdamien                        if (!isJavaLangObject(argTypes[i])) {
291156321Sdamien                            error(i + "'th argument of a @Constructor method should be of Object type, found " + argTypes[i]);
292156321Sdamien                        }
293156321Sdamien                    }
294156321Sdamien
295156321Sdamien                    final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor();
296156321Sdamien                    final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC);
297156321Sdamien                    if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) {
298156321Sdamien                        error("last argument of a @Constructor method is neither Object nor Object[] type: " + lastArgTypeDesc);
299156321Sdamien                    }
300156321Sdamien
301156321Sdamien                    if (isVarArg && argTypes.length > 3) {
302156321Sdamien                        error("vararg of a @Constructor method has more than 3 arguments");
303156321Sdamien                    }
304156321Sdamien                }
305156321Sdamien            }
306156321Sdamien            break;
307156321Sdamien            case SPECIALIZED_CONSTRUCTOR: {
308156321Sdamien                final Type returnType = Type.getReturnType(javaDesc);
309156321Sdamien                if (!isJSObjectType(returnType)) {
310156321Sdamien                    error("return value of a @SpecializedConstructor method should be a valid JS type, found " + returnType);
311156321Sdamien                }
312156321Sdamien                final Type[] argTypes = Type.getArgumentTypes(javaDesc);
313156321Sdamien                for (int i = 0; i < argTypes.length; i++) {
314156407Sdamien                    if (!isValidJSType(argTypes[i])) {
315156321Sdamien                        error(i + "'th argument of a @SpecializedConstructor method is not valid JS type, found " + argTypes[i]);
316156407Sdamien                    }
317156321Sdamien                }
318156321Sdamien            }
319156321Sdamien            break;
320156321Sdamien            case FUNCTION: {
321156321Sdamien                final Type returnType = Type.getReturnType(javaDesc);
322156321Sdamien                if (!isValidJSType(returnType)) {
323156321Sdamien                    error("return value of a @Function method should be a valid JS type, found " + returnType);
324156321Sdamien                }
325156321Sdamien                final Type[] argTypes = Type.getArgumentTypes(javaDesc);
326156321Sdamien                if (argTypes.length < 1) {
327156321Sdamien                    error("@Function methods should have at least 1 arg");
328156321Sdamien                }
329156321Sdamien                if (!isJavaLangObject(argTypes[0])) {
330156321Sdamien                    error("first argument of a @Function method should be of Object type, found " + argTypes[0]);
331156321Sdamien                }
332156321Sdamien
333156321Sdamien                if (argTypes.length > 1) {
334156321Sdamien                    for (int i = 1; i < argTypes.length - 1; i++) {
335156321Sdamien                        if (!isJavaLangObject(argTypes[i])) {
336156321Sdamien                            error(i + "'th argument of a @Function method should be of Object type, found " + argTypes[i]);
337156321Sdamien                        }
338156321Sdamien                    }
339156321Sdamien
340156321Sdamien                    final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor();
341156321Sdamien                    final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC);
342156321Sdamien                    if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) {
343156321Sdamien                        error("last argument of a @Function method is neither Object nor Object[] type: " + lastArgTypeDesc);
344156321Sdamien                    }
345156321Sdamien
346156321Sdamien                    if (isVarArg && argTypes.length > 2) {
347156321Sdamien                        error("vararg @Function method has more than 2 arguments");
348156321Sdamien                    }
349156321Sdamien                }
350156321Sdamien            }
351156321Sdamien            break;
352156321Sdamien            case SPECIALIZED_FUNCTION: {
353156321Sdamien                final Type returnType = Type.getReturnType(javaDesc);
354156321Sdamien                if (!isValidJSType(returnType)) {
355156321Sdamien                    error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType);
356156321Sdamien                }
357156321Sdamien                final Type[] argTypes = Type.getArgumentTypes(javaDesc);
358156321Sdamien                for (int i = 0; i < argTypes.length; i++) {
359156321Sdamien                    if (!isValidJSType(argTypes[i])) {
360156321Sdamien                        error(i + "'th argument of a @SpecializedFunction method is not valid JS type, found " + argTypes[i]);
361156321Sdamien                    }
362156321Sdamien                }
363156321Sdamien            }
364156321Sdamien            break;
365156321Sdamien            case GETTER: {
366156321Sdamien                final Type[] argTypes = Type.getArgumentTypes(javaDesc);
367156321Sdamien                if (argTypes.length != 1) {
368156321Sdamien                    error("@Getter methods should have one argument");
369156321Sdamien                }
370156321Sdamien                if (!isJavaLangObject(argTypes[0])) {
371156321Sdamien                    error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]);
372156321Sdamien                }
373156321Sdamien
374156321Sdamien                final Type returnType = Type.getReturnType(javaDesc);
375156321Sdamien                if (!isJavaLangObject(returnType)) {
376156321Sdamien                    error("return type of a @Getter method should be Object, found: " + javaDesc);
377156321Sdamien                }
378156321Sdamien            }
379156321Sdamien            break;
380156321Sdamien            case SETTER: {
381156321Sdamien                final Type[] argTypes = Type.getArgumentTypes(javaDesc);
382156321Sdamien                if (argTypes.length != 2) {
383156321Sdamien                    error("@Setter methods should have two arguments");
384156321Sdamien                }
385156321Sdamien                if (!isJavaLangObject(argTypes[0])) {
386156321Sdamien                    error("first argument of a @Setter method should be of Object type, found: " + argTypes[0]);
387156321Sdamien                }
388156321Sdamien                if (!Type.getReturnType(javaDesc).toString().equals("V")) {
389156321Sdamien                    error("return type of of a @Setter method should be void, found: " + Type.getReturnType(javaDesc));
390156321Sdamien                }
391156321Sdamien            }
392156321Sdamien            break;
393156321Sdamien            case PROPERTY: {
394156321Sdamien                if (where == Where.CONSTRUCTOR) {
395156321Sdamien                    if (isStatic()) {
396156321Sdamien                        if (!isFinal()) {
397156321Sdamien                            error("static Where.CONSTRUCTOR @Property should be final");
398156321Sdamien                        }
399156321Sdamien
400156321Sdamien                        if (!isJSPrimitiveType(Type.getType(javaDesc))) {
401156321Sdamien                            error("static Where.CONSTRUCTOR @Property should be a JS primitive");
402156321Sdamien                        }
403156321Sdamien                    }
404156321Sdamien                } else if (where == Where.PROTOTYPE) {
405156321Sdamien                    if (isStatic()) {
406156321Sdamien                        if (!isFinal()) {
407156321Sdamien                            error("static Where.PROTOTYPE @Property should be final");
408156321Sdamien                        }
409156321Sdamien
410156321Sdamien                        if (!isJSPrimitiveType(Type.getType(javaDesc))) {
411156321Sdamien                            error("static Where.PROTOTYPE @Property should be a JS primitive");
412156321Sdamien                        }
413156321Sdamien                    }
414156321Sdamien                }
415156321Sdamien            }
416156321Sdamien        }
417156321Sdamien    }
418156321Sdamien
419156321Sdamien    private static boolean isValidJSType(final Type type) {
420156321Sdamien        return isJSPrimitiveType(type) || isJSObjectType(type);
421156321Sdamien    }
422156321Sdamien
423156321Sdamien    private static boolean isJSPrimitiveType(final Type type) {
424156321Sdamien        switch (type.getSort()) {
425156321Sdamien            case Type.BOOLEAN:
426156321Sdamien            case Type.INT:
427156321Sdamien            case Type.LONG:
428156321Sdamien            case Type.DOUBLE:
429156321Sdamien                return true;
430156321Sdamien            default:
431156321Sdamien                return false;
432156321Sdamien        }
433156321Sdamien    }
434156321Sdamien
435156321Sdamien    private static boolean isJSObjectType(final Type type) {
436156321Sdamien        return isJavaLangObject(type) || isJavaLangString(type) || isScriptObject(type);
437156321Sdamien    }
438156321Sdamien
439156321Sdamien    private static boolean isJavaLangObject(final Type type) {
440156321Sdamien        return type.getDescriptor().equals(OBJECT_DESC);
441156321Sdamien    }
442156321Sdamien
443156321Sdamien    private static boolean isJavaLangString(final Type type) {
444156321Sdamien        return type.getDescriptor().equals(STRING_DESC);
445156321Sdamien    }
446156321Sdamien
447156321Sdamien    private static boolean isScriptObject(final Type type) {
448156321Sdamien        if (type.getDescriptor().equals(SCRIPTOBJECT_DESC)) {
449156321Sdamien            return true;
450156321Sdamien        }
451156321Sdamien
452156321Sdamien        if (type.getSort() == Type.OBJECT) {
453156321Sdamien            try {
454156321Sdamien                final Class clazz = Class.forName(type.getClassName(), false, myLoader);
455156321Sdamien                return ScriptObject.class.isAssignableFrom(clazz);
456156321Sdamien            } catch (final ClassNotFoundException cnfe) {
457156321Sdamien                return false;
458156321Sdamien            }
459156321Sdamien        }
460156321Sdamien
461156321Sdamien        return false;
462156321Sdamien    }
463156321Sdamien
464156321Sdamien    private void error(final String msg) {
465156321Sdamien        throw new RuntimeException(javaName + " of type " + javaDesc + " : " + msg);
466156321Sdamien    }
467156321Sdamien
468156321Sdamien    /**
469156321Sdamien     * @return the initClass
470156321Sdamien     */
471156321Sdamien    String getInitClass() {
472156321Sdamien        return initClass;
473156321Sdamien    }
474156321Sdamien
475156321Sdamien    /**
476156321Sdamien     * @param initClass the initClass to set
477156321Sdamien     */
478156321Sdamien    void setInitClass(final String initClass) {
479156321Sdamien        this.initClass = initClass;
480156321Sdamien    }
481156321Sdamien
482156321Sdamien    @Override
483156321Sdamien    protected Object clone() {
484156321Sdamien        try {
485156321Sdamien            return super.clone();
486156321Sdamien        } catch (final CloneNotSupportedException e) {
487156321Sdamien            assert false : "clone not supported " + e;
488156321Sdamien            return null;
489156321Sdamien        }
490156321Sdamien    }
491156321Sdamien
492156321Sdamien    /**
493156321Sdamien     * @return the arity
494156321Sdamien     */
495156321Sdamien    int getArity() {
496156321Sdamien        return arity;
497156321Sdamien    }
498156321Sdamien
499156321Sdamien    /**
500156321Sdamien     * @param arity the arity to set
501156321Sdamien     */
502156321Sdamien    void setArity(final int arity) {
503156321Sdamien        this.arity = arity;
504156321Sdamien    }
505156321Sdamien}
506156321Sdamien