Support for default attributes of annotations
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 19 Mar 2016 10:31:13 +0100
changeset 1889e1953d8b8338
parent 1888 d13786d6c185
child 1891 c8af252ea9eb
Support for default attributes of annotations
rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionAnnotationTest.java
rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java
rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java
rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java
rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/rt/emul/compacttest/src/test/java/org/apidesign/bck2brwsr/tck/ReflectionAnnotationTest.java	Sat Mar 19 10:31:13 2016 +0100
     1.3 @@ -0,0 +1,117 @@
     1.4 +/**
     1.5 + * Back 2 Browser Bytecode Translator
     1.6 + * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     1.7 + *
     1.8 + * This program is free software: you can redistribute it and/or modify
     1.9 + * it under the terms of the GNU General Public License as published by
    1.10 + * the Free Software Foundation, version 2 of the License.
    1.11 + *
    1.12 + * This program is distributed in the hope that it will be useful,
    1.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.15 + * GNU General Public License for more details.
    1.16 + *
    1.17 + * You should have received a copy of the GNU General Public License
    1.18 + * along with this program. Look for COPYING file in the top folder.
    1.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    1.20 + */
    1.21 +package org.apidesign.bck2brwsr.tck;
    1.22 +
    1.23 +import java.lang.annotation.Retention;
    1.24 +import java.lang.annotation.RetentionPolicy;
    1.25 +import org.apidesign.bck2brwsr.vmtest.Compare;
    1.26 +import org.apidesign.bck2brwsr.vmtest.VMTest;
    1.27 +import org.testng.annotations.Factory;
    1.28 +
    1.29 +/**
    1.30 + *
    1.31 + * @author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    1.32 + */
    1.33 +public class ReflectionAnnotationTest {
    1.34 +    @Retention(RetentionPolicy.RUNTIME)
    1.35 +    @interface Ann {
    1.36 +        int integer() default 33;
    1.37 +        double dbl() default 33.3;
    1.38 +        String string() default "Ahoj";
    1.39 +        E enums() default E.B;
    1.40 +        Class<?> type() default String.class;
    1.41 +    }
    1.42 +
    1.43 +    @interface AnnAnn {
    1.44 +        Ann ann() default @Ann(
    1.45 +            dbl = 44.4,
    1.46 +            enums = E.A,
    1.47 +            string = "Hi",
    1.48 +            integer = 44
    1.49 +        );
    1.50 +    }
    1.51 +
    1.52 +    @Ann
    1.53 +    @AnnAnn
    1.54 +    class D {
    1.55 +    }
    1.56 +
    1.57 +    @Ann(type = String.class)
    1.58 +    @AnnAnn(ann = @Ann(string = "Ciao"))
    1.59 +    enum E {
    1.60 +        A, B
    1.61 +    };
    1.62 +
    1.63 +    
    1.64 +    @Compare public String annoClass() throws Exception {
    1.65 +        Retention r = Ann.class.getAnnotation(Retention.class);
    1.66 +        assert r != null : "Annotation is present";
    1.67 +        assert r.value() == RetentionPolicy.RUNTIME : "Policy value is OK: " + r.value();
    1.68 +        return r.annotationType().getName();
    1.69 +    }
    1.70 +
    1.71 +    @Compare public boolean isAnnotation() {
    1.72 +        return Ann.class.isAnnotation();
    1.73 +    }
    1.74 +    @Compare public boolean isNotAnnotation() {
    1.75 +        return String.class.isAnnotation();
    1.76 +    }
    1.77 +    @Compare public boolean isNotAnnotationEnum() {
    1.78 +        return E.class.isAnnotation();
    1.79 +    }
    1.80 +
    1.81 +    @Compare public int intDefaultAttrIsRead() {
    1.82 +        return D.class.getAnnotation(Ann.class).integer();
    1.83 +    }
    1.84 +
    1.85 +    @Compare public double doubleDefaultAttrIsRead() {
    1.86 +        return D.class.getAnnotation(Ann.class).dbl();
    1.87 +    }
    1.88 +
    1.89 +    @Compare public String stringDefaultAttrIsRead() {
    1.90 +        return D.class.getAnnotation(Ann.class).string();
    1.91 +    }
    1.92 +
    1.93 +    @Compare public String enumDefaultAttrIsRead() {
    1.94 +        return D.class.getAnnotation(Ann.class).enums().toString();
    1.95 +    }
    1.96 +
    1.97 +    @Compare public String classDefaultAttrIsRead() {
    1.98 +        return D.class.getAnnotation(Ann.class).type().getName();
    1.99 +    }
   1.100 +
   1.101 +    @Compare public String classAttrIsRead() {
   1.102 +        return E.class.getAnnotation(Ann.class).type().getName();
   1.103 +    }
   1.104 +
   1.105 +//    @Compare public String defaultAnnotationAttrIsRead() {
   1.106 +//        final Ann ann = D.class.getAnnotation(AnnAnn.class).ann();
   1.107 +//        return ann.string() + ann.dbl() + ann.enums() + ann.integer() + ann.type();
   1.108 +//    }
   1.109 +//
   1.110 +//    @Compare public String annotationAttrIsRead() {
   1.111 +//        final Ann ann = E.class.getAnnotation(AnnAnn.class).ann();
   1.112 +//        return ann.string();
   1.113 +//    }
   1.114 +
   1.115 +    @Factory
   1.116 +    public static Object[] create() {
   1.117 +        return VMTest.create(ReflectionAnnotationTest.class);
   1.118 +    }
   1.119 +    
   1.120 +}
     2.1 --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java	Sat Mar 19 10:28:03 2016 +0100
     2.2 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java	Sat Mar 19 10:31:13 2016 +0100
     2.3 @@ -38,11 +38,20 @@
     2.4      }
     2.5  
     2.6      @JavaScriptBody(args = { "a", "n", "arr", "values" }, body = ""
     2.7 -        + "function f(val, prop, clazz) {\n"
     2.8 +        + "function r(anno, val, prop, m) {\n"
     2.9 +        + "  var v = val[prop];\n"
    2.10 +        + "  if (typeof v === 'undefined') {\n"
    2.11 +        + "    var cls = anno.fld_org_apidesign_bck2brwsr_emul_reflect_AnnotationImpl_type.cnstr;\n"
    2.12 +        + "    try { throw 'x'; } catch (errr) {};\n"
    2.13 +        + "    v = cls.prototype[m]();\n"
    2.14 +        + "  }\n"
    2.15 +        + "  return v;\n"
    2.16 +        + "}\n"
    2.17 +        + "function f(val, prop, clazz, m) {\n"
    2.18          + "  return function() {\n"
    2.19 -        + "    if (clazz == null) return val[prop];\n"
    2.20 +        + "    if (clazz == null) return r(this, val, prop, m);\n"
    2.21          + "    if (clazz.isArray__Z()) {\n"
    2.22 -        + "      var valarr = val[prop];\n"
    2.23 +        + "      var valarr = r(this, val, prop, m);\n"
    2.24          + "      var cmp = clazz.getComponentType__Ljava_lang_Class_2();\n"
    2.25          + "      var retarr = vm.java_lang_reflect_Array(false).newInstance__Ljava_lang_Object_2Ljava_lang_Class_2I(cmp, valarr.length);\n"
    2.26          + "      for (var i = 0; i < valarr.length; i++) {\n"
    2.27 @@ -50,14 +59,14 @@
    2.28          + "      }\n"
    2.29          + "      return retarr;\n"
    2.30          + "    }\n"
    2.31 -        + "    return CLS.prototype.c__Ljava_lang_Object_2Ljava_lang_Class_2Ljava_lang_Object_2(clazz, val[prop]);\n"
    2.32 +        + "    return CLS.prototype.c__Ljava_lang_Object_2Ljava_lang_Class_2Ljava_lang_Object_2(clazz, r(this, val, prop, m));\n"
    2.33          + "  };\n"
    2.34          + "}\n"
    2.35          + "for (var i = 0; i < arr.length; i += 3) {\n"
    2.36          + "  var m = arr[i];\n"
    2.37          + "  var p = arr[i + 1];\n"
    2.38          + "  var c = arr[i + 2];\n"
    2.39 -        + "  a[m] = f(values, p, c);\n"
    2.40 +        + "  a[m] = f(values, p, c, m);\n"
    2.41          + "}\n"
    2.42          + "a['$instOf_' + n] = true;\n"
    2.43          + "return a;"
     3.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java	Sat Mar 19 10:28:03 2016 +0100
     3.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java	Sat Mar 19 10:31:13 2016 +0100
     3.3 @@ -78,6 +78,7 @@
     3.4      public static final int ACC_STRICT                   = 0x00000800;
     3.5      public static final int ACC_EXPLICIT                 = 0x00001000;
     3.6      public static final int ACC_SYNTHETIC                = 0x00010000; // actually, this is an attribute
     3.7 +    private static final int ACC_ANNOTATION              = 0x00020000;
     3.8  
     3.9      /* Type codes for StackMap attribute */
    3.10      public static final int ITEM_Bogus      =0; // an unknown or uninitialized value
    3.11 @@ -356,14 +357,22 @@
    3.12          }
    3.13  
    3.14          protected void visitAttr(
    3.15 -            String annoType, String attr, String attrType, String value) throws IOException {
    3.16 +            String annoType, String attr, String attrType, String value
    3.17 +        ) throws IOException {
    3.18          }
    3.19  
    3.20          protected void visitEnumAttr(
    3.21 -            String annoType, String attr, String attrType, String value) throws IOException {
    3.22 +            String annoType, String attr, String attrType, String value
    3.23 +        ) throws IOException {
    3.24              visitAttr(annoType, attr, attrType, value);
    3.25          }
    3.26  
    3.27 +        protected void visitClassAttr(
    3.28 +            String annoType, String attr, String className
    3.29 +        ) throws IOException {
    3.30 +            visitAttr(annoType, attr, className, className);
    3.31 +        }
    3.32 +
    3.33          /**
    3.34           * Initialize the parsing with constant pool from
    3.35           * <code>cd</code>.
    3.36 @@ -404,6 +413,16 @@
    3.37              }
    3.38          }
    3.39  
    3.40 +        public void parseDefault(byte[] defaultAttribute, ClassData cd) throws IOException {
    3.41 +            ByteArrayInputStream is = new ByteArrayInputStream(defaultAttribute);
    3.42 +            DataInputStream dis = new DataInputStream(is);
    3.43 +            try {
    3.44 +                readValue(dis, cd, null, null);
    3.45 +            } finally {
    3.46 +                is.close();
    3.47 +            }
    3.48 +        }
    3.49 +
    3.50          private void readValue(
    3.51              DataInputStream dis, ClassData cd, String typeName, String attrName) throws IOException {
    3.52              char type = (char) dis.readByte();
    3.53 @@ -425,6 +444,8 @@
    3.54                  visitAttr(typeName, attrName, attrType, val);
    3.55              } else if (type == 'c') {
    3.56                  int cls = dis.readUnsignedShort();
    3.57 +                String attrType = cd.stringValue(cls, textual);
    3.58 +                visitClassAttr(typeName, attrName, attrType);
    3.59              } else if (type == '[') {
    3.60                  int cnt = dis.readUnsignedShort();
    3.61                  for (int i = 0; i < cnt; i++) {
    3.62 @@ -875,6 +896,10 @@
    3.63              return false;
    3.64          }
    3.65  
    3.66 +        public boolean isAnnotation() {
    3.67 +            return (access & ACC_ANNOTATION) != 0;
    3.68 +        }
    3.69 +
    3.70          /**
    3.71           * Returns true if this member is public, false otherwise.
    3.72           */
    3.73 @@ -1687,6 +1712,7 @@
    3.74          int max_stack, max_locals;
    3.75          boolean isSynthetic = false;
    3.76          boolean isDeprecated = false;
    3.77 +        private AttrData annotationDefault;
    3.78  
    3.79          public MethodData(ClassData cls) {
    3.80              this.cls = cls;
    3.81 @@ -1737,6 +1763,12 @@
    3.82                              attr.read(attr_name_index);
    3.83                              attrs.addElement(attr);
    3.84                              break readAttr;
    3.85 +                        } else if (attr_name.equals("AnnotationDefault")) {
    3.86 +                            AttrData attr = new AttrData(cls);
    3.87 +                            attr.read(attr_name_index, in);
    3.88 +                            attrs.addElement(attr);
    3.89 +                            annotationDefault = attr;
    3.90 +                            break readAttr;
    3.91                          }
    3.92                      }
    3.93                      AttrData attr = new AttrData(cls);
    3.94 @@ -1994,6 +2026,10 @@
    3.95              return code_attrs;
    3.96          }
    3.97  
    3.98 +        byte[] getDefaultAttribute() {
    3.99 +            return annotationDefault == null ? null : annotationDefault.getData();
   3.100 +        }
   3.101 +
   3.102          /**
   3.103           * Return true if method id synthetic.
   3.104           */
     4.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Sat Mar 19 10:28:03 2016 +0100
     4.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Sat Mar 19 10:31:13 2016 +0100
     4.3 @@ -288,7 +288,8 @@
     4.4              byte[] runAnno = m.findAnnotationData(false);
     4.5              if (runAnno != null) {
     4.6                  append("\n    m.anno = {");
     4.7 -                generateAnno(jc, runAnno);
     4.8 +                AnnotationParser ap = new GenerateAnno(true, false);
     4.9 +                ap.parse(runAnno, jc);
    4.10                  append("\n    };");
    4.11              }
    4.12              append("\n    m.access = " + m.getAccess()).append(";");
    4.13 @@ -343,7 +344,8 @@
    4.14          byte[] classAnno = jc.findAnnotationData(false);
    4.15          if (classAnno != null) {
    4.16              append("\n    CLS.$class.anno = {");
    4.17 -            generateAnno(jc, classAnno);
    4.18 +            AnnotationParser ap = new GenerateAnno(true, false);
    4.19 +            ap.parse(classAnno, jc);
    4.20              append("\n    };");
    4.21          }
    4.22          for (String init : toInitilize.toArray()) {
    4.23 @@ -453,10 +455,18 @@
    4.24  
    4.25          final byte[] byteCodes = m.getCode();
    4.26          if (byteCodes == null) {
    4.27 -            if (debug("  throw 'no code found for ")) {
    4.28 -               this
    4.29 -               .append(jc.getClassName()).append('.')
    4.30 -               .append(m.getName()).append("';\n");
    4.31 +            byte[] defaultAttr = m.getDefaultAttribute();
    4.32 +            if (defaultAttr != null) {
    4.33 +                append("  return ");
    4.34 +                AnnotationParser ap = new GenerateAnno(true, false);
    4.35 +                ap.parseDefault(defaultAttr, jc);
    4.36 +                append(";\n");
    4.37 +            } else {
    4.38 +                if (debug("  throw 'no code found for ")) {
    4.39 +                   this
    4.40 +                   .append(jc.getClassName()).append('.')
    4.41 +                   .append(m.getName()).append("';\n");
    4.42 +                }
    4.43              }
    4.44              if (defineProp) {
    4.45                  append("}});");
    4.46 @@ -2156,81 +2166,6 @@
    4.47          return " = null;";
    4.48      }
    4.49  
    4.50 -    private void generateAnno(ClassData cd, byte[] data) throws IOException {
    4.51 -        AnnotationParser ap = new AnnotationParser(true, false) {
    4.52 -            int[] cnt = new int[32];
    4.53 -            int depth;
    4.54 -            
    4.55 -            @Override
    4.56 -            protected void visitAnnotationStart(String attrType, boolean top) throws IOException {
    4.57 -                final String slashType = attrType.substring(1, attrType.length() - 1);
    4.58 -                requireReference(slashType);
    4.59 -                
    4.60 -                if (cnt[depth]++ > 0) {
    4.61 -                    append(",");
    4.62 -                }
    4.63 -                if (top) {
    4.64 -                    append('"').append(attrType).append("\" : ");
    4.65 -                }
    4.66 -                append("{\n");
    4.67 -                cnt[++depth] = 0;
    4.68 -            }
    4.69 -
    4.70 -            @Override
    4.71 -            protected void visitAnnotationEnd(String type, boolean top) throws IOException {
    4.72 -                append("\n}\n");
    4.73 -                depth--;
    4.74 -            }
    4.75 -
    4.76 -            @Override
    4.77 -            protected void visitValueStart(String attrName, char type) throws IOException {
    4.78 -                if (cnt[depth]++ > 0) {
    4.79 -                    append(",\n");
    4.80 -                }
    4.81 -                cnt[++depth] = 0;
    4.82 -                if (attrName != null) {
    4.83 -                    append('"').append(attrName).append("\" : ");
    4.84 -                }
    4.85 -                if (type == '[') {
    4.86 -                    append("[");
    4.87 -                }
    4.88 -            }
    4.89 -
    4.90 -            @Override
    4.91 -            protected void visitValueEnd(String attrName, char type) throws IOException {
    4.92 -                if (type == '[') {
    4.93 -                    append("]");
    4.94 -                }
    4.95 -                depth--;
    4.96 -            }
    4.97 -            
    4.98 -            @Override
    4.99 -            protected void visitAttr(String type, String attr, String attrType, String value) 
   4.100 -            throws IOException {
   4.101 -                if (attr == null && value == null) {
   4.102 -                    return;
   4.103 -                }
   4.104 -                append(value);
   4.105 -            }
   4.106 -
   4.107 -            @Override
   4.108 -            protected void visitEnumAttr(String type, String attr, String attrType, String value) 
   4.109 -            throws IOException {
   4.110 -                final String slashType = attrType.substring(1, attrType.length() - 1);
   4.111 -                requireReference(slashType);
   4.112 -                
   4.113 -                final String cn = mangleClassName(slashType);
   4.114 -                append(accessClassFalse(cn))
   4.115 -                   .append("['valueOf__L").
   4.116 -                    append(cn).
   4.117 -                    append("_2Ljava_lang_String_2']('").
   4.118 -                    append(value).
   4.119 -                    append("')");
   4.120 -            }
   4.121 -        };
   4.122 -        ap.parse(data, cd);
   4.123 -    }
   4.124 -
   4.125      private static String outputArg(Appendable out, String[] args, int indx) throws IOException {
   4.126          final String name = args[indx];
   4.127          if (name == null) {
   4.128 @@ -2505,4 +2440,88 @@
   4.129      private static void println(String msg) {
   4.130          System.err.println(msg);
   4.131      }
   4.132 +
   4.133 +    private class GenerateAnno extends AnnotationParser {
   4.134 +        public GenerateAnno(boolean textual, boolean iterateArray) {
   4.135 +            super(textual, iterateArray);
   4.136 +        }
   4.137 +        int[] cnt = new int[32];
   4.138 +        int depth;
   4.139 +
   4.140 +        @Override
   4.141 +        protected void visitAnnotationStart(String attrType, boolean top) throws IOException {
   4.142 +            final String slashType = attrType.substring(1, attrType.length() - 1);
   4.143 +            requireReference(slashType);
   4.144 +
   4.145 +            if (cnt[depth]++ > 0) {
   4.146 +                append(",");
   4.147 +            }
   4.148 +            if (top) {
   4.149 +                append('"').append(attrType).append("\" : ");
   4.150 +            }
   4.151 +            append("{\n");
   4.152 +            cnt[++depth] = 0;
   4.153 +        }
   4.154 +
   4.155 +        @Override
   4.156 +        protected void visitAnnotationEnd(String type, boolean top) throws IOException {
   4.157 +            append("\n}\n");
   4.158 +            depth--;
   4.159 +        }
   4.160 +
   4.161 +        @Override
   4.162 +        protected void visitValueStart(String attrName, char type) throws IOException {
   4.163 +            if (cnt[depth]++ > 0) {
   4.164 +                append(",\n");
   4.165 +            }
   4.166 +            cnt[++depth] = 0;
   4.167 +            if (attrName != null) {
   4.168 +                append('"').append(attrName).append("\" : ");
   4.169 +            }
   4.170 +            if (type == '[') {
   4.171 +                append("[");
   4.172 +            }
   4.173 +        }
   4.174 +
   4.175 +        @Override
   4.176 +        protected void visitValueEnd(String attrName, char type) throws IOException {
   4.177 +            if (type == '[') {
   4.178 +                append("]");
   4.179 +            }
   4.180 +            depth--;
   4.181 +        }
   4.182 +
   4.183 +        @Override
   4.184 +        protected void visitAttr(String type, String attr, String attrType, String value)
   4.185 +            throws IOException {
   4.186 +            if (attr == null && value == null) {
   4.187 +                return;
   4.188 +            }
   4.189 +            append(value);
   4.190 +        }
   4.191 +
   4.192 +        @Override
   4.193 +        protected void visitEnumAttr(String type, String attr, String attrType, String value)
   4.194 +            throws IOException {
   4.195 +            final String slashType = attrType.substring(1, attrType.length() - 1);
   4.196 +            requireReference(slashType);
   4.197 +
   4.198 +            final String cn = mangleClassName(slashType);
   4.199 +            append(accessClassFalse(cn))
   4.200 +                .append("['valueOf__L").
   4.201 +                append(cn).
   4.202 +                append("_2Ljava_lang_String_2']('").
   4.203 +                append(value).
   4.204 +                append("')");
   4.205 +        }
   4.206 +
   4.207 +        @Override
   4.208 +        protected void visitClassAttr(String annoType, String attr, String className) throws IOException {
   4.209 +            final String slashType = className.substring(1, className.length() - 1);
   4.210 +            requireReference(slashType);
   4.211 +
   4.212 +            final String cn = mangleClassName(slashType);
   4.213 +            append(accessClassFalse(cn)).append(".constructor.$class");
   4.214 +        }
   4.215 +    }
   4.216  }
     5.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Sat Mar 19 10:28:03 2016 +0100
     5.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Sat Mar 19 10:31:13 2016 +0100
     5.3 @@ -87,12 +87,18 @@
     5.4      @Test public void jsAnnotation() throws Exception {
     5.5          assertExec("Check class annotation", Classes.class, "getMarker__I", Double.valueOf(10));
     5.6      }
     5.7 +    @Test public void jsAnnotationDefaultValue() throws Exception {
     5.8 +        assertExec("Check class annotation", Classes.class, "getMarkerDefault__I", Double.valueOf(42));
     5.9 +    }
    5.10      @Test public void jsArrayAnnotation() throws Exception {
    5.11          assertExec("Check array annotation", Classes.class, "getMarkerNicknames__Ljava_lang_String_2", Classes.getMarkerNicknames());
    5.12      }
    5.13      @Test public void jsEnumAnnotation() throws Exception {
    5.14          assertExec("Check enum annotation", Classes.class, "getMarkerE__Ljava_lang_String_2", Classes.getMarkerE());
    5.15      }
    5.16 +    @Test public void jsEnumAnnotationDefault() throws Exception {
    5.17 +        assertExec("Check enum annotation", Classes.class, "getMarkerED__Ljava_lang_String_2", Classes.getMarkerED());
    5.18 +    }
    5.19      @Test public void jsRetentionAnnotation() throws Exception {
    5.20          assertExec("Check enum annotation", Classes.class, "getRetention__Ljava_lang_String_2", Classes.getRetention());
    5.21      }
    5.22 @@ -108,6 +114,12 @@
    5.23      @Test public void jsInnerAnnotationFromArray() throws Exception {
    5.24          assertExec("Check inner annotation", Classes.class, "getInnerNamers__I", Double.valueOf(Classes.getInnerNamers()));
    5.25      }
    5.26 +    @Test public void jsAnnotationClassAttr() throws Exception {
    5.27 +        assertExec("Check annotation with class attribute", Classes.class, "self__I", 1);
    5.28 +    }
    5.29 +    @Test public void jsAnnotationDefaultClassAttr() throws Exception {
    5.30 +        assertExec("Check annotation with class attribute", Classes.class, "defaultSelf__I", 1);
    5.31 +    }
    5.32      @Test public void javaInvokeMethod() throws Exception {
    5.33          assertEquals(Classes.reflectiveMethodCall(true, "name"), "java.io.IOException", "Calls the name() method via reflection");
    5.34      }
     6.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Sat Mar 19 10:28:03 2016 +0100
     6.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Sat Mar 19 10:31:13 2016 +0100
     6.3 @@ -67,6 +67,33 @@
     6.4      public static String name() {
     6.5          return IOException.class.getName().toString();
     6.6      }
     6.7 +
     6.8 +    @ClassesMarker(self = Self.class, number = 42, nicknames = {})
     6.9 +    public static class Self {
    6.10 +    }
    6.11 +
    6.12 +    @ClassesMarker(number = 42, nicknames = {})
    6.13 +    public static class DefaultSelf {
    6.14 +    }
    6.15 +
    6.16 +    public static int self() {
    6.17 +        ClassesMarker cm = Self.class.getAnnotation(ClassesMarker.class);
    6.18 +        if (cm.self() == Self.class) {
    6.19 +            return 1;
    6.20 +        } else {
    6.21 +            return 0;
    6.22 +        }
    6.23 +    }
    6.24 +
    6.25 +    public static int defaultSelf() {
    6.26 +        ClassesMarker cm = DefaultSelf.class.getAnnotation(ClassesMarker.class);
    6.27 +        if (cm.self() == Object.class) {
    6.28 +            return 1;
    6.29 +        } else {
    6.30 +            throw new IllegalStateException("" + cm.self());
    6.31 +        }
    6.32 +    }
    6.33 +
    6.34      public static String simpleName() {
    6.35          return IOException.class.getSimpleName();
    6.36      }
    6.37 @@ -103,6 +130,11 @@
    6.38          assert !((Object)cm instanceof Class) : "Is not Class " + cm;
    6.39          return cm == null ? -1 : cm.number();
    6.40      }
    6.41 +    public static int getMarkerDefault() {
    6.42 +        try { throw new IllegalStateException(); } catch (Exception e) {}
    6.43 +        ClassesMarker cm = CD.class.getAnnotation(ClassesMarker.class);
    6.44 +        return cm == null ? -1 : cm.number();
    6.45 +    }
    6.46      public static String getMarkerNicknames() {
    6.47          ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class);
    6.48          if (cm == null) {
    6.49 @@ -149,6 +181,17 @@
    6.50          }
    6.51          return cm.count().name();
    6.52      }
    6.53 +
    6.54 +    @ClassesMarker(nicknames = {})
    6.55 +    class CD {
    6.56 +    }
    6.57 +    public static String getMarkerED() {
    6.58 +        ClassesMarker cm = CD.class.getAnnotation(ClassesMarker.class);
    6.59 +        if (cm == null) {
    6.60 +            return null;
    6.61 +        }
    6.62 +        return cm.count().name();
    6.63 +    }
    6.64      public static String getNamer(boolean direct) {
    6.65          if (direct) {
    6.66              ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class);
     7.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java	Sat Mar 19 10:28:03 2016 +0100
     7.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java	Sat Mar 19 10:31:13 2016 +0100
     7.3 @@ -26,9 +26,10 @@
     7.4   */
     7.5  @Retention(RetentionPolicy.RUNTIME)
     7.6  public @interface ClassesMarker {
     7.7 -    int number();
     7.8 +    int number() default 42;
     7.9      String[] nicknames();
    7.10      E count() default E.ONE;
    7.11 +    Class<?> self() default Object.class;
    7.12      
    7.13      enum E {
    7.14          ONE, TWO;