| /* |
| * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package jdk.nashorn.internal.tools.nasgen; |
| |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC; |
| import static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC; |
| import jdk.internal.org.objectweb.asm.Opcodes; |
| import jdk.internal.org.objectweb.asm.Type; |
| import jdk.nashorn.internal.objects.annotations.Where; |
| import jdk.nashorn.internal.runtime.ScriptObject; |
| |
| /** |
| * Details about a Java method or field annotated with any of the field/method |
| * annotations from the jdk.nashorn.internal.objects.annotations package. |
| */ |
| public final class MemberInfo implements Cloneable { |
| // class loader of this class |
| private static ClassLoader myLoader = MemberInfo.class.getClassLoader(); |
| |
| /** |
| * The different kinds of available class annotations |
| */ |
| public static enum Kind { |
| |
| /** |
| * This is a script class |
| */ |
| SCRIPT_CLASS, |
| /** |
| * This is a constructor |
| */ |
| CONSTRUCTOR, |
| /** |
| * This is a function |
| */ |
| FUNCTION, |
| /** |
| * This is a getter |
| */ |
| GETTER, |
| /** |
| * This is a setter |
| */ |
| SETTER, |
| /** |
| * This is a property |
| */ |
| PROPERTY, |
| /** |
| * This is a specialized version of a function |
| */ |
| SPECIALIZED_FUNCTION, |
| } |
| |
| // keep in sync with jdk.nashorn.internal.objects.annotations.Attribute |
| static final int DEFAULT_ATTRIBUTES = 0x0; |
| |
| static final int DEFAULT_ARITY = -2; |
| |
| // the kind of the script annotation - one of the above constants |
| private MemberInfo.Kind kind; |
| // script property name |
| private String name; |
| // script property attributes |
| private int attributes; |
| // name of the java member |
| private String javaName; |
| // type descriptor of the java member |
| private String javaDesc; |
| // access bits of the Java field or method |
| private int javaAccess; |
| // initial value for static @Property fields |
| private Object value; |
| // class whose object is created to fill property value |
| private String initClass; |
| // arity of the Function or Constructor |
| private int arity; |
| |
| private Where where; |
| |
| private Type linkLogicClass; |
| |
| private boolean isSpecializedConstructor; |
| |
| private boolean isOptimistic; |
| |
| /** |
| * @return the kind |
| */ |
| public Kind getKind() { |
| return kind; |
| } |
| |
| /** |
| * @param kind the kind to set |
| */ |
| public void setKind(final Kind kind) { |
| this.kind = kind; |
| } |
| |
| /** |
| * @return the name |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * @param name the name to set |
| */ |
| public void setName(final String name) { |
| this.name = name; |
| } |
| |
| /** |
| * Tag something as specialized constructor or not |
| * @param isSpecializedConstructor boolean, true if specialized constructor |
| */ |
| public void setIsSpecializedConstructor(final boolean isSpecializedConstructor) { |
| this.isSpecializedConstructor = isSpecializedConstructor; |
| } |
| |
| /** |
| * Check if something is a specialized constructor |
| * @return true if specialized constructor |
| */ |
| public boolean isSpecializedConstructor() { |
| return isSpecializedConstructor; |
| } |
| |
| /** |
| * Check if this is an optimistic builtin function |
| * @return true if optimistic builtin |
| */ |
| public boolean isOptimistic() { |
| return isOptimistic; |
| } |
| |
| /** |
| * Tag something as optimistic builtin or not |
| * @param isOptimistic boolean, true if builtin constructor |
| */ |
| public void setIsOptimistic(final boolean isOptimistic) { |
| this.isOptimistic = isOptimistic; |
| } |
| |
| /** |
| * Get the SpecializedFunction guard for specializations, i.e. optimistic |
| * builtins |
| * @return specialization, null if none |
| */ |
| public Type getLinkLogicClass() { |
| return linkLogicClass; |
| } |
| |
| /** |
| * Set the SpecializedFunction link logic class for specializations, i.e. optimistic |
| * builtins |
| * @param linkLogicClass link logic class |
| */ |
| |
| public void setLinkLogicClass(final Type linkLogicClass) { |
| this.linkLogicClass = linkLogicClass; |
| } |
| |
| /** |
| * @return the attributes |
| */ |
| public int getAttributes() { |
| return attributes; |
| } |
| |
| /** |
| * @param attributes the attributes to set |
| */ |
| public void setAttributes(final int attributes) { |
| this.attributes = attributes; |
| } |
| |
| /** |
| * @return the javaName |
| */ |
| public String getJavaName() { |
| return javaName; |
| } |
| |
| /** |
| * @param javaName the javaName to set |
| */ |
| public void setJavaName(final String javaName) { |
| this.javaName = javaName; |
| } |
| |
| /** |
| * @return the javaDesc |
| */ |
| public String getJavaDesc() { |
| return javaDesc; |
| } |
| |
| void setJavaDesc(final String javaDesc) { |
| this.javaDesc = javaDesc; |
| } |
| |
| int getJavaAccess() { |
| return javaAccess; |
| } |
| |
| void setJavaAccess(final int access) { |
| this.javaAccess = access; |
| } |
| |
| Object getValue() { |
| return value; |
| } |
| |
| void setValue(final Object value) { |
| this.value = value; |
| } |
| |
| Where getWhere() { |
| return where; |
| } |
| |
| void setWhere(final Where where) { |
| this.where = where; |
| } |
| |
| boolean isFinal() { |
| return (javaAccess & Opcodes.ACC_FINAL) != 0; |
| } |
| |
| boolean isStatic() { |
| return (javaAccess & Opcodes.ACC_STATIC) != 0; |
| } |
| |
| boolean isStaticFinal() { |
| return isStatic() && isFinal(); |
| } |
| |
| boolean isInstanceGetter() { |
| return kind == Kind.GETTER && where == Where.INSTANCE; |
| } |
| |
| /** |
| * Check whether this MemberInfo is a getter that resides in the instance |
| * |
| * @return true if instance setter |
| */ |
| boolean isInstanceSetter() { |
| return kind == Kind.SETTER && where == Where.INSTANCE; |
| } |
| |
| boolean isInstanceProperty() { |
| return kind == Kind.PROPERTY && where == Where.INSTANCE; |
| } |
| |
| boolean isInstanceFunction() { |
| return kind == Kind.FUNCTION && where == Where.INSTANCE; |
| } |
| |
| boolean isPrototypeGetter() { |
| return kind == Kind.GETTER && where == Where.PROTOTYPE; |
| } |
| |
| boolean isPrototypeSetter() { |
| return kind == Kind.SETTER && where == Where.PROTOTYPE; |
| } |
| |
| boolean isPrototypeProperty() { |
| return kind == Kind.PROPERTY && where == Where.PROTOTYPE; |
| } |
| |
| boolean isPrototypeFunction() { |
| return kind == Kind.FUNCTION && where == Where.PROTOTYPE; |
| } |
| |
| boolean isConstructorGetter() { |
| return kind == Kind.GETTER && where == Where.CONSTRUCTOR; |
| } |
| |
| boolean isConstructorSetter() { |
| return kind == Kind.SETTER && where == Where.CONSTRUCTOR; |
| } |
| |
| boolean isConstructorProperty() { |
| return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR; |
| } |
| |
| boolean isConstructorFunction() { |
| return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR; |
| } |
| |
| boolean isConstructor() { |
| return kind == Kind.CONSTRUCTOR; |
| } |
| |
| void verify() { |
| switch (kind) { |
| case CONSTRUCTOR: { |
| final Type returnType = Type.getReturnType(javaDesc); |
| if (!isJSObjectType(returnType)) { |
| error("return value of a @Constructor method should be of Object type, found " + returnType); |
| } |
| final Type[] argTypes = Type.getArgumentTypes(javaDesc); |
| if (argTypes.length < 2) { |
| error("@Constructor methods should have at least 2 args"); |
| } |
| if (!argTypes[0].equals(Type.BOOLEAN_TYPE)) { |
| error("first argument of a @Constructor method should be of boolean type, found " + argTypes[0]); |
| } |
| if (!isJavaLangObject(argTypes[1])) { |
| error("second argument of a @Constructor method should be of Object type, found " + argTypes[0]); |
| } |
| |
| if (argTypes.length > 2) { |
| for (int i = 2; i < argTypes.length - 1; i++) { |
| if (!isJavaLangObject(argTypes[i])) { |
| error(i + "'th argument of a @Constructor method should be of Object type, found " + argTypes[i]); |
| } |
| } |
| |
| final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); |
| final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); |
| if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { |
| error("last argument of a @Constructor method is neither Object nor Object[] type: " + lastArgTypeDesc); |
| } |
| |
| if (isVarArg && argTypes.length > 3) { |
| error("vararg of a @Constructor method has more than 3 arguments"); |
| } |
| } |
| } |
| break; |
| case FUNCTION: { |
| final Type returnType = Type.getReturnType(javaDesc); |
| if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) { |
| error("return value of a @Function method should be a valid JS type, found " + returnType); |
| } |
| final Type[] argTypes = Type.getArgumentTypes(javaDesc); |
| if (argTypes.length < 1) { |
| error("@Function methods should have at least 1 arg"); |
| } |
| if (!isJavaLangObject(argTypes[0])) { |
| error("first argument of a @Function method should be of Object type, found " + argTypes[0]); |
| } |
| |
| if (argTypes.length > 1) { |
| for (int i = 1; i < argTypes.length - 1; i++) { |
| if (!isJavaLangObject(argTypes[i])) { |
| error(i + "'th argument of a @Function method should be of Object type, found " + argTypes[i]); |
| } |
| } |
| |
| final String lastArgTypeDesc = argTypes[argTypes.length - 1].getDescriptor(); |
| final boolean isVarArg = lastArgTypeDesc.equals(OBJECT_ARRAY_DESC); |
| if (!lastArgTypeDesc.equals(OBJECT_DESC) && !isVarArg) { |
| error("last argument of a @Function method is neither Object nor Object[] type: " + lastArgTypeDesc); |
| } |
| |
| if (isVarArg && argTypes.length > 2) { |
| error("vararg @Function method has more than 2 arguments"); |
| } |
| } |
| } |
| break; |
| case SPECIALIZED_FUNCTION: { |
| final Type returnType = Type.getReturnType(javaDesc); |
| if (!(isValidJSType(returnType) || (isSpecializedConstructor() && Type.VOID_TYPE == returnType))) { |
| error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType); |
| } |
| final Type[] argTypes = Type.getArgumentTypes(javaDesc); |
| for (int i = 0; i < argTypes.length; i++) { |
| if (!isValidJSType(argTypes[i])) { |
| error(i + "'th argument of a @SpecializedFunction method is not valid JS type, found " + argTypes[i]); |
| } |
| } |
| } |
| break; |
| case GETTER: { |
| final Type[] argTypes = Type.getArgumentTypes(javaDesc); |
| if (argTypes.length != 1) { |
| error("@Getter methods should have one argument"); |
| } |
| if (!isJavaLangObject(argTypes[0])) { |
| error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]); |
| } |
| |
| if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) { |
| error("return type of getter should not be void"); |
| } |
| } |
| break; |
| case SETTER: { |
| final Type[] argTypes = Type.getArgumentTypes(javaDesc); |
| if (argTypes.length != 2) { |
| error("@Setter methods should have two arguments"); |
| } |
| if (!isJavaLangObject(argTypes[0])) { |
| error("first argument of a @Setter method should be of Object type, found: " + argTypes[0]); |
| } |
| if (!Type.getReturnType(javaDesc).toString().equals("V")) { |
| error("return type of of a @Setter method should be void, found: " + Type.getReturnType(javaDesc)); |
| } |
| } |
| break; |
| case PROPERTY: { |
| if (where == Where.CONSTRUCTOR) { |
| if (isStatic()) { |
| if (!isFinal()) { |
| error("static Where.CONSTRUCTOR @Property should be final"); |
| } |
| |
| if (!isJSPrimitiveType(Type.getType(javaDesc))) { |
| error("static Where.CONSTRUCTOR @Property should be a JS primitive"); |
| } |
| } |
| } else if (where == Where.PROTOTYPE) { |
| if (isStatic()) { |
| if (!isFinal()) { |
| error("static Where.PROTOTYPE @Property should be final"); |
| } |
| |
| if (!isJSPrimitiveType(Type.getType(javaDesc))) { |
| error("static Where.PROTOTYPE @Property should be a JS primitive"); |
| } |
| } |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| private static boolean isValidJSType(final Type type) { |
| return isJSPrimitiveType(type) || isJSObjectType(type); |
| } |
| |
| private static boolean isJSPrimitiveType(final Type type) { |
| switch (type.getSort()) { |
| case Type.BOOLEAN: |
| case Type.INT: |
| case Type.LONG: |
| case Type.DOUBLE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| private static boolean isJSObjectType(final Type type) { |
| return isJavaLangObject(type) || isJavaLangString(type) || isScriptObject(type); |
| } |
| |
| private static boolean isJavaLangObject(final Type type) { |
| return type.getDescriptor().equals(OBJECT_DESC); |
| } |
| |
| private static boolean isJavaLangString(final Type type) { |
| return type.getDescriptor().equals(STRING_DESC); |
| } |
| |
| private static boolean isScriptObject(final Type type) { |
| if (type.getDescriptor().equals(SCRIPTOBJECT_DESC)) { |
| return true; |
| } |
| |
| if (type.getSort() == Type.OBJECT) { |
| try { |
| final Class<?> clazz = Class.forName(type.getClassName(), false, myLoader); |
| return ScriptObject.class.isAssignableFrom(clazz); |
| } catch (final ClassNotFoundException cnfe) { |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| private void error(final String msg) { |
| throw new RuntimeException(javaName + " of type " + javaDesc + " : " + msg); |
| } |
| |
| /** |
| * @return the initClass |
| */ |
| String getInitClass() { |
| return initClass; |
| } |
| |
| /** |
| * @param initClass the initClass to set |
| */ |
| void setInitClass(final String initClass) { |
| this.initClass = initClass; |
| } |
| |
| @Override |
| protected Object clone() { |
| try { |
| return super.clone(); |
| } catch (final CloneNotSupportedException e) { |
| assert false : "clone not supported " + e; |
| return null; |
| } |
| } |
| |
| /** |
| * @return the arity |
| */ |
| int getArity() { |
| return arity; |
| } |
| |
| /** |
| * @param arity the arity to set |
| */ |
| void setArity(final int arity) { |
| this.arity = arity; |
| } |
| } |