Support new Class operations: isMemberClass, isAnonymousClass and isLocalClass
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 20 Mar 2016 08:12:55 +0100
changeset 1898cf6d5d357696
parent 1897 cb637833bfb3
child 1899 d729cfa77fa7
Support new Class operations: isMemberClass, isAnonymousClass and isLocalClass
rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ClassTest.java
rt/emul/mini/src/main/java/java/lang/Class.java
rt/emul/mini/src/main/java/java/lang/ClassLoader.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
     1.1 --- a/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ClassTest.java	Sun Mar 20 07:01:40 2016 +0100
     1.2 +++ b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ClassTest.java	Sun Mar 20 08:12:55 2016 +0100
     1.3 @@ -39,6 +39,45 @@
     1.4          Class<?> dblCls = dbl.getClass();
     1.5          return String.class.isAssignableFrom(dblCls);
     1.6      }
     1.7 +
     1.8 +    private String toClassInfo(Class<?> c) {
     1.9 +        StringBuilder sb = new StringBuilder();
    1.10 +        sb.append("\n");
    1.11 +        sb.append("name: ").append(c.getName()).append("\n");
    1.12 +        sb.append("local: ").append(c.isLocalClass()).append("\n");
    1.13 +        sb.append("member: ").append(c.isMemberClass()).append("\n");
    1.14 +        sb.append("annonymous: ").append(c.isAnonymousClass()).append("\n");
    1.15 +        return sb.toString();
    1.16 +    }
    1.17 +
    1.18 +
    1.19 +    @Compare
    1.20 +    public String globalClass() throws Exception {
    1.21 +        return toClassInfo(ClassTest.class);
    1.22 +    }
    1.23 +
    1.24 +    @Compare
    1.25 +    public String localClass() throws Exception {
    1.26 +        class Local {
    1.27 +        }
    1.28 +        return toClassInfo(Local.class);
    1.29 +    }
    1.30 +
    1.31 +    class Member {
    1.32 +    }
    1.33 +
    1.34 +    @Compare
    1.35 +    public String memberClass() throws Exception {
    1.36 +        return toClassInfo(Member.class);
    1.37 +    }
    1.38 +
    1.39 +    static class NonMember {
    1.40 +    }
    1.41 +
    1.42 +    @Compare
    1.43 +    public String nonMemberClass() throws Exception {
    1.44 +        return toClassInfo(NonMember.class);
    1.45 +    }
    1.46      
    1.47      @Factory
    1.48      public static Object[] create() {
     2.1 --- a/rt/emul/mini/src/main/java/java/lang/Class.java	Sun Mar 20 07:01:40 2016 +0100
     2.2 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java	Sun Mar 20 08:12:55 2016 +0100
     2.3 @@ -721,15 +721,12 @@
     2.4       * class.
     2.5       */
     2.6      private String getSimpleBinaryName() {
     2.7 -        Class<?> enclosingClass = null; // XXX getEnclosingClass();
     2.8 -        if (enclosingClass == null) // top level class
     2.9 +        final String name = getName();
    2.10 +        int dolar = name.lastIndexOf('$');
    2.11 +        if (dolar == -1) {
    2.12              return null;
    2.13 -        // Otherwise, strip the enclosing class' name
    2.14 -        try {
    2.15 -            return getName().substring(enclosingClass.getName().length());
    2.16 -        } catch (IndexOutOfBoundsException ex) {
    2.17 -            throw new IllegalStateException("Malformed class name");
    2.18          }
    2.19 +        return name.substring(dolar);
    2.20      }
    2.21  
    2.22      /**
    2.23 @@ -1350,8 +1347,8 @@
    2.24              else
    2.25                  return null;
    2.26          }
    2.27 -//        if (isLocalOrAnonymousClass())
    2.28 -//            return null;
    2.29 +        if (isLocalOrAnonymousClass())
    2.30 +            return null;
    2.31  //        Class<?> enclosingClass = getEnclosingClass();
    2.32          Class<?> enclosingClass = null;
    2.33          if (enclosingClass == null) { // top level class
    2.34 @@ -1365,6 +1362,47 @@
    2.35      }
    2.36  
    2.37      /**
    2.38 +     * Returns {@code true} if and only if the underlying class is an anonymous
    2.39 +     * class.
    2.40 +     *
    2.41 +     * @return {@code true} if and only if this class is an anonymous class.
    2.42 +     * @since 1.5
    2.43 +     */
    2.44 +    public boolean isAnonymousClass() {
    2.45 +        return "".equals(getSimpleName());
    2.46 +    }
    2.47 +
    2.48 +    /**
    2.49 +     * Returns {@code true} if and only if the underlying class is a local
    2.50 +     * class.
    2.51 +     *
    2.52 +     * @return {@code true} if and only if this class is a local class.
    2.53 +     * @since 1.5
    2.54 +     */
    2.55 +    public boolean isLocalClass() {
    2.56 +        return isLocalOrAnonymousClass() && !isAnonymousClass();
    2.57 +    }
    2.58 +
    2.59 +    /**
    2.60 +     * Returns {@code true} if and only if the underlying class is a member
    2.61 +     * class.
    2.62 +     *
    2.63 +     * @return {@code true} if and only if this class is a member class.
    2.64 +     * @since 1.5
    2.65 +     */
    2.66 +    public boolean isMemberClass() {
    2.67 +        return getSimpleBinaryName() != null && !isLocalOrAnonymousClass();
    2.68 +    }
    2.69 +
    2.70 +    /**
    2.71 +     * Returns {@code true} if this is a local class or an anonymous class.
    2.72 +     * Returns {@code false} otherwise.
    2.73 +     */
    2.74 +    private boolean isLocalOrAnonymousClass() {
    2.75 +        return (getAccess() & 0x10000) != 0;
    2.76 +    }
    2.77 +
    2.78 +    /**
    2.79       * Finds a resource with a given name.  The rules for searching resources
    2.80       * associated with a given class are implemented by the defining
    2.81       * {@linkplain ClassLoader class loader} of the class.  This method
     3.1 --- a/rt/emul/mini/src/main/java/java/lang/ClassLoader.java	Sun Mar 20 07:01:40 2016 +0100
     3.2 +++ b/rt/emul/mini/src/main/java/java/lang/ClassLoader.java	Sun Mar 20 08:12:55 2016 +0100
     3.3 @@ -24,7 +24,6 @@
     3.4   */
     3.5  package java.lang;
     3.6  
     3.7 -import java.io.ByteArrayInputStream;
     3.8  import java.io.InputStream;
     3.9  import java.io.IOException;
    3.10  import java.net.URL;
     4.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java	Sun Mar 20 07:01:40 2016 +0100
     4.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java	Sun Mar 20 08:12:55 2016 +0100
     4.3 @@ -570,6 +570,7 @@
     4.4          private Hashtable indexHashAscii = new Hashtable();
     4.5          private String pkgPrefix = "";
     4.6          private int pkgPrefixLen = 0;
     4.7 +        private boolean hasEnclosingMethod;
     4.8  
     4.9          /**
    4.10           * Read classfile to disassemble.
    4.11 @@ -620,42 +621,45 @@
    4.12              attrs = new AttrData[attributes_count];
    4.13              for (int k = 0; k < attributes_count; k++) {
    4.14                  int name_cpx = in.readUnsignedShort();
    4.15 -                if (getTag(name_cpx) == CONSTANT_UTF8
    4.16 -                    && getString(name_cpx).equals("SourceFile")) {
    4.17 -                    if (in.readInt() != 2) {
    4.18 -                        throw new ClassFormatError("invalid attr length");
    4.19 +                if (getTag(name_cpx) == CONSTANT_UTF8) {
    4.20 +                    final String attrName = getString(name_cpx);
    4.21 +                    if (attrName.equals("SourceFile")) {
    4.22 +                        if (in.readInt() != 2) {
    4.23 +                            throw new ClassFormatError("invalid attr length");
    4.24 +                        }
    4.25 +                        source_cpx = in.readUnsignedShort();
    4.26 +                        AttrData attr = new AttrData(this);
    4.27 +                        attr.read(name_cpx);
    4.28 +                        attrs[k] = attr;
    4.29 +
    4.30 +                    } else if (attrName.equals("InnerClasses")) {
    4.31 +                        int length = in.readInt();
    4.32 +                        int num = in.readUnsignedShort();
    4.33 +                        if (2 + num * 8 != length) {
    4.34 +                            throw new ClassFormatError("invalid attr length");
    4.35 +                        }
    4.36 +                        innerClasses = new InnerClassData[num];
    4.37 +                        for (int j = 0; j < num; j++) {
    4.38 +                            InnerClassData innerClass = new InnerClassData(this);
    4.39 +                            innerClass.read(in);
    4.40 +                            innerClasses[j] = innerClass;
    4.41 +                        }
    4.42 +                        AttrData attr = new AttrData(this);
    4.43 +                        attr.read(name_cpx);
    4.44 +                        attrs[k] = attr;
    4.45 +                    } else if (attrName.equals("BootstrapMethods")) {
    4.46 +                        AttrData attr = new AttrData(this);
    4.47 +                        bootMethods = readBootstrapMethods(in);
    4.48 +                        attr.read(name_cpx);
    4.49 +                        attrs[k] = attr;
    4.50 +                    } else {
    4.51 +                        if (attrName.equals("EnclosingMethod")) {
    4.52 +                            hasEnclosingMethod = true;
    4.53 +                        }
    4.54 +                        AttrData attr = new AttrData(this);
    4.55 +                        attr.read(name_cpx, in);
    4.56 +                        attrs[k] = attr;
    4.57                      }
    4.58 -                    source_cpx = in.readUnsignedShort();
    4.59 -                    AttrData attr = new AttrData(this);
    4.60 -                    attr.read(name_cpx);
    4.61 -                    attrs[k] = attr;
    4.62 -
    4.63 -                } else if (getTag(name_cpx) == CONSTANT_UTF8
    4.64 -                    && getString(name_cpx).equals("InnerClasses")) {
    4.65 -                    int length = in.readInt();
    4.66 -                    int num = in.readUnsignedShort();
    4.67 -                    if (2 + num * 8 != length) {
    4.68 -                        throw new ClassFormatError("invalid attr length");
    4.69 -                    }
    4.70 -                    innerClasses = new InnerClassData[num];
    4.71 -                    for (int j = 0; j < num; j++) {
    4.72 -                        InnerClassData innerClass = new InnerClassData(this);
    4.73 -                        innerClass.read(in);
    4.74 -                        innerClasses[j] = innerClass;
    4.75 -                    }
    4.76 -                    AttrData attr = new AttrData(this);
    4.77 -                    attr.read(name_cpx);
    4.78 -                    attrs[k] = attr;
    4.79 -                } else if (getTag(name_cpx) == CONSTANT_UTF8
    4.80 -                    && getString(name_cpx).equals("BootstrapMethods")) {
    4.81 -                    AttrData attr = new AttrData(this);
    4.82 -                    bootMethods = readBootstrapMethods(in);
    4.83 -                    attr.read(name_cpx);
    4.84 -                    attrs[k] = attr;
    4.85 -                } else {
    4.86 -                    AttrData attr = new AttrData(this);
    4.87 -                    attr.read(name_cpx, in);
    4.88 -                    attrs[k] = attr;
    4.89                  }
    4.90              }
    4.91              in.close();
    4.92 @@ -876,6 +880,10 @@
    4.93              return access;
    4.94          }
    4.95  
    4.96 +        public boolean hasEnclosingMethod() {
    4.97 +            return hasEnclosingMethod;
    4.98 +        }
    4.99 +
   4.100          /**
   4.101           * Returns true if it is a class
   4.102           */
     5.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Sun Mar 20 07:01:40 2016 +0100
     5.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Sun Mar 20 08:12:55 2016 +0100
     5.3 @@ -339,7 +339,11 @@
     5.4              }
     5.5          }
     5.6          append("\n    ]; };");
     5.7 -        append("\n    CLS.$class.access = ").append(jc.getAccessFlags()+";");
     5.8 +        int flags = jc.getAccessFlags();
     5.9 +        if (jc.hasEnclosingMethod()) {
    5.10 +            flags |= 0x10000;
    5.11 +        }
    5.12 +        append("\n    CLS.$class.access = ").append(flags+";");
    5.13          append("\n    CLS.$class.cnstr = CLS;");
    5.14          byte[] classAnno = jc.findAnnotationData(false);
    5.15          if (classAnno != null) {