Using Object.defineProperty to specify non-enumerable properties on Object
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 26 Jun 2014 07:42:54 +0200
changeset 1638a84f511654ae
parent 1631 e9cfb1d23abf
parent 1637 4156b1bd4b82
child 1639 4b09a4b689a4
child 1643 e6ff54a8dff6
Using Object.defineProperty to specify non-enumerable properties on Object
rt/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/AheadOfTime.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ClassTest.java	Thu Jun 26 07:42:54 2014 +0200
     1.3 @@ -0,0 +1,47 @@
     1.4 +/**
     1.5 + * Back 2 Browser Bytecode Translator
     1.6 + * Copyright (C) 2012 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 org.apidesign.bck2brwsr.vmtest.Compare;
    1.24 +import org.apidesign.bck2brwsr.vmtest.VMTest;
    1.25 +import org.testng.annotations.Factory;
    1.26 +
    1.27 +/**
    1.28 + *
    1.29 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.30 + */
    1.31 +public class ClassTest {
    1.32 +    @Compare
    1.33 +    public boolean isDoubleArrayAssignableToObject() throws Exception {
    1.34 +        double[] dbl = new double[0];
    1.35 +        Class<?> dblCls = dbl.getClass();
    1.36 +        return Object.class.isAssignableFrom(dblCls);
    1.37 +    }
    1.38 +
    1.39 +    @Compare
    1.40 +    public boolean isDoubleArrayAssignableToString() throws Exception {
    1.41 +        double[] dbl = new double[0];
    1.42 +        Class<?> dblCls = dbl.getClass();
    1.43 +        return String.class.isAssignableFrom(dblCls);
    1.44 +    }
    1.45 +    
    1.46 +    @Factory
    1.47 +    public static Object[] create() {
    1.48 +        return VMTest.create(ClassTest.class);
    1.49 +    }
    1.50 +}
     2.1 --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/HtmlAnnotations.java	Mon Jun 09 22:07:29 2014 +0200
     2.2 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/HtmlAnnotations.java	Thu Jun 26 07:42:54 2014 +0200
     2.3 @@ -53,6 +53,9 @@
     2.4  
     2.5      @JavaScriptBody(args = {  }, javacall = true, body = "return @org.apidesign.bck2brwsr.vmtest.impl.HtmlAnnotations::callback()();")
     2.6      public static native int staticCallback();
     2.7 +
     2.8 +    @JavaScriptBody(args = {  }, wait4js = false, body = "/* do nothing */")
     2.9 +    public static native void empty();
    2.10      
    2.11      
    2.12      protected long chooseLong(boolean takeFirst, boolean takeSecond, long first, long second) {
     3.1 --- a/rt/emul/mini/src/main/java/java/lang/Class.java	Mon Jun 09 22:07:29 2014 +0200
     3.2 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java	Thu Jun 26 07:42:54 2014 +0200
     3.3 @@ -406,6 +406,9 @@
     3.4          if (this == cls) {
     3.5              return true;
     3.6          }
     3.7 +        if (this == Object.class) {
     3.8 +            return true;
     3.9 +        }
    3.10          
    3.11          if (isArray()) {
    3.12              final Class<?> cmpType = cls.getComponentType();
    3.13 @@ -420,6 +423,9 @@
    3.14              if (cls.isPrimitive()) {
    3.15                  return false;
    3.16              }
    3.17 +            if (cls.isArray()) {
    3.18 +                return false;
    3.19 +            }
    3.20              String prop = "$instOf_" + getName().replace('.', '_');
    3.21              return hasCnstrProperty(cls, prop);
    3.22          }
     4.1 --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java	Mon Jun 09 22:07:29 2014 +0200
     4.2 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java	Thu Jun 26 07:42:54 2014 +0200
     4.3 @@ -49,17 +49,34 @@
     4.4      @JavaScriptBody(args = {"clazz", "prefix", "cnstr"},
     4.5          body = ""
     4.6          + "var c = clazz.cnstr;\n"
     4.7 -        + "if (!cnstr) c = c.prototype;"
     4.8 +        + "if (!cnstr) c = c.prototype;\n"
     4.9          + "var arr = new Array();\n"
    4.10 -        + "for (m in c) {\n"
    4.11 +        + "function check(m, verify) {\n"
    4.12          + "  if (m.indexOf(prefix) === 0) {\n"
    4.13 -        + "     if (!c[m].cls) continue;\n"
    4.14 +        + "     if (!c[m].cls) return;\n"
    4.15 +        + "     if (verify) {\n"
    4.16 +        + "       for (var i = 0; i < arr.length; i += 3) {\n"
    4.17 +        + "         if (arr[i] === m) return;\n"
    4.18 +        + "       }\n"
    4.19 +        + "     }\n"
    4.20          + "     arr.push(m);\n"
    4.21          + "     arr.push(c[m]);\n"
    4.22          + "     arr.push(c[m].cls.$class);\n"
    4.23 -        + "  }"
    4.24 +        + "  }\n"
    4.25          + "}\n"
    4.26 -        + "return arr;")
    4.27 +        + "for (m in c) {\n"
    4.28 +        + "  check(m)\n"
    4.29 +        + "}\n"
    4.30 +        + "check('wait__V', true);\n"
    4.31 +        + "check('wait__VJ', true);\n"
    4.32 +        + "check('wait__VJI', true);\n"
    4.33 +        + "check('equals__ZLjava_lang_Object_2', true);\n"
    4.34 +        + "check('toString__Ljava_lang_String_2', true);\n"
    4.35 +        + "check('hashCode__I', true);\n"
    4.36 +        + "check('getClass__Ljava_lang_Class_2', true);\n"
    4.37 +        + "check('notify__V', true);\n"
    4.38 +        + "check('notifyAll__V', true);\n"
    4.39 +        + "return arr;\n")
    4.40      private static native Object[] findMethodData(
    4.41          Class<?> clazz, String prefix, boolean cnstr);
    4.42  
     5.1 --- a/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js	Mon Jun 09 22:07:29 2014 +0200
     5.2 +++ b/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js	Thu Jun 26 07:42:54 2014 +0200
     5.3 @@ -2,7 +2,7 @@
     5.4  vm.java_lang_reflect_Array(false);
     5.5  vm.java_lang_String(false);
     5.6  
     5.7 -Array.at = function(arr, indx, value) {
     5.8 +Object.defineProperty(Array, "at", { configurable: true, writable: true, value : function(arr, indx, value) {
     5.9    var prev = arr[indx];
    5.10    if (typeof prev === 'undefined' && (indx < 0 || indx >= arr.length)) {
    5.11      var e = vm.java_lang_ArrayIndexOutOfBoundsException(true);
    5.12 @@ -14,11 +14,11 @@
    5.13    } else {
    5.14      return prev;
    5.15    }
    5.16 -};
    5.17 -Array.prototype.getClass__Ljava_lang_Class_2 = function() {
    5.18 +}});
    5.19 +Object.defineProperty(Array.prototype, "getClass__Ljava_lang_Class_2", { configurable: true, writable: true, value : function() {
    5.20    return vm.java_lang_Class(false).defineArray__Ljava_lang_Class_2Ljava_lang_String_2Ljava_lang_Object_2(this.jvmName, this.fnc);
    5.21 -};
    5.22 -Array.prototype.clone__Ljava_lang_Object_2 = function() {
    5.23 +}});
    5.24 +Object.defineProperty(Array.prototype, "clone__Ljava_lang_Object_2", { configurable: true, writable: true, value : function() {
    5.25    var s = this.length;
    5.26    var ret = new Array(s);
    5.27    for (var i = 0; i < s; i++) {
    5.28 @@ -27,4 +27,4 @@
    5.29    ret.jvmName = this.jvmName;
    5.30    ret.fnc = this.fnc;
    5.31    return ret;
    5.32 -};
    5.33 +}});
     6.1 --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Mon Jun 09 22:07:29 2014 +0200
     6.2 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Thu Jun 26 07:42:54 2014 +0200
     6.3 @@ -260,15 +260,18 @@
     6.4          append("\n    c.constructor = CLS;");
     6.5          append("\n    function fillInstOf(x) {");
     6.6          String instOfName = "$instOf_" + className;
     6.7 -        append("\n        x['").append(instOfName).append("'] = true;");
     6.8 +        append("\n        Object.defineProperty(x, '").append(instOfName).append("', { value : true });");
     6.9          for (String superInterface : jc.getSuperInterfaces()) {
    6.10              String intrfc = superInterface.replace('/', '_');
    6.11              append("\n      vm.").append(intrfc).append("(false)['fillInstOf'](x);");
    6.12              requireReference(superInterface);
    6.13          }
    6.14          append("\n    }");
    6.15 -        append("\n    c['fillInstOf'] = fillInstOf;");
    6.16 -        append("\n    fillInstOf(c);");
    6.17 +        append("\n    try {");
    6.18 +        append("\n      Object.defineProperty(c, 'fillInstOf', { value: fillInstOf });");
    6.19 +        append("\n      fillInstOf(c);");
    6.20 +        append("\n    } catch (ignore) {");
    6.21 +        append("\n    }");
    6.22  //        obfuscationDelegate.exportJSProperty(this, "c", instOfName);
    6.23          append("\n    CLS.$class = 'temp';");
    6.24          append("\n    CLS.$class = ");
    6.25 @@ -362,7 +365,14 @@
    6.26          final LocalsMapper lmapper =
    6.27                  new LocalsMapper(stackMapIterator.getArguments());
    6.28  
    6.29 -        append(destObject).append(".").append(name).append(" = function(");
    6.30 +        boolean obj = "java/lang/Object".equals(jc.getClassName());
    6.31 +        
    6.32 +        if (obj) {
    6.33 +            append("Object.defineProperty(").append(destObject).
    6.34 +            append(", '").append(name).append("', { configurable: true, writable: true, value: function(");
    6.35 +        } else {
    6.36 +            append(destObject).append(".").append(name).append(" = function(");
    6.37 +        }
    6.38          lmapper.outputArguments(this, m.isStatic());
    6.39          append(") {").append("\n");
    6.40  
    6.41 @@ -371,6 +381,9 @@
    6.42              append("  throw 'no code found for ")
    6.43                 .append(jc.getClassName()).append('.')
    6.44                 .append(m.getName()).append("';\n");
    6.45 +            if (obj) {
    6.46 +                append("}");
    6.47 +            }
    6.48              append("};");
    6.49              return;
    6.50          }
    6.51 @@ -1390,7 +1403,11 @@
    6.52          while (openBraces-- > 0) {
    6.53              append('}');
    6.54          }
    6.55 -        append("\n};");
    6.56 +        if (obj) {
    6.57 +            append("\n}});");
    6.58 +        } else {
    6.59 +            append("\n};");
    6.60 +        }
    6.61      }
    6.62  
    6.63      private int generateIf(StackMapper mapper, byte[] byteCodes, 
    6.64 @@ -1739,6 +1756,8 @@
    6.65                          args[cnt++] = value;
    6.66                      } else if ("javacall".equals(attr)) {
    6.67                          javacall = "1".equals(value);
    6.68 +                    } else if ("wait4js".equals(attr)) {
    6.69 +                        // ignore, we always invoke synchronously
    6.70                      } else {
    6.71                          throw new IllegalArgumentException(attr);
    6.72                      }
     7.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Mon Jun 09 22:07:29 2014 +0200
     7.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Thu Jun 26 07:42:54 2014 +0200
     7.3 @@ -124,6 +124,24 @@
     7.4              "java.lang.Object"
     7.5          );
     7.6      }
     7.7 +
     7.8 +    @Test public void jsListMethodsInObject() throws Exception {
     7.9 +        String methods = Classes.listObject(false, "");
    7.10 +        
    7.11 +        assertExec("Methods defined in Object", Classes.class, 
    7.12 +            "listObject__Ljava_lang_String_2ZLjava_lang_String_2", 
    7.13 +            methods, false, ""
    7.14 +        );
    7.15 +    }
    7.16 +
    7.17 +    @Test public void jsListMethodsInString() throws Exception {
    7.18 +        String methods = Classes.listObject(true, "toStr");
    7.19 +        
    7.20 +        assertExec("Methods defined in Object", Classes.class, 
    7.21 +            "listObject__Ljava_lang_String_2ZLjava_lang_String_2", 
    7.22 +            methods, true, "toStr"
    7.23 +        );
    7.24 +    }
    7.25      
    7.26      @Test public void jsInvokeParamMethod() throws Exception {
    7.27          assertExec("sums two numbers via reflection", Classes.class, 
     8.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Mon Jun 09 22:07:29 2014 +0200
     8.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Thu Jun 26 07:42:54 2014 +0200
     8.3 @@ -120,6 +120,18 @@
     8.4          }
     8.5          return sb.toString().toString();
     8.6      }
     8.7 +
     8.8 +    static String listObject(boolean string, String prefix) {
     8.9 +        final Class<?> c = string ? String.class : Object.class;
    8.10 +        StringBuilder sb = new StringBuilder();
    8.11 +        for (Method m : c.getMethods()) {
    8.12 +            final String n = m.getName();
    8.13 +            if (n.startsWith(prefix)) {
    8.14 +                sb.append(n).append("\n");
    8.15 +            }
    8.16 +        }
    8.17 +        return sb.toString().toString();
    8.18 +    }
    8.19      @Retention(RetentionPolicy.CLASS)
    8.20      @interface Ann {
    8.21      }
     9.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java	Mon Jun 09 22:07:29 2014 +0200
     9.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java	Thu Jun 26 07:42:54 2014 +0200
     9.3 @@ -135,6 +135,20 @@
     9.4          return jsgetbytes(new Instance());
     9.5      }
     9.6      
     9.7 +    public static int noInstOfExposed() {
     9.8 +        return countInstOf(10);
     9.9 +    }
    9.10 +    
    9.11 +    public static String props() {
    9.12 +        return list(new Object());
    9.13 +    }
    9.14 +    
    9.15 +    @JavaScriptBody(args = "o", body = "var s = ''; for (var p in {}) { s += p; }; return s;")
    9.16 +    private static native String list(Object o);
    9.17 +    
    9.18 +    @JavaScriptBody(args = "o", body = "var i = 0; for (p in o) { if (p.toString().indexOf('instOf') >= 0) i++; } return i;")
    9.19 +    private static native int countInstOf(Object o);
    9.20 +    
    9.21      @JavaScriptBody(args = { "instance" }, body = "return instance.getByte__B();")
    9.22      private static native int jsgetbytes(Instance instance);
    9.23  
    10.1 --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java	Mon Jun 09 22:07:29 2014 +0200
    10.2 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java	Thu Jun 26 07:42:54 2014 +0200
    10.3 @@ -17,9 +17,12 @@
    10.4   */
    10.5  package org.apidesign.vm4brwsr;
    10.6  
    10.7 +import javax.script.ScriptEngine;
    10.8 +import javax.script.ScriptEngineManager;
    10.9 +import static org.testng.Assert.fail;
   10.10  import org.testng.annotations.AfterClass;
   10.11 +import org.testng.annotations.BeforeClass;
   10.12  import org.testng.annotations.Test;
   10.13 -import org.testng.annotations.BeforeClass;
   10.14  
   10.15  /**
   10.16   *
   10.17 @@ -47,6 +50,21 @@
   10.18              Double.valueOf(31)
   10.19          );
   10.20      }
   10.21 +    @Test public void noInstOfExposed() throws Exception {
   10.22 +        assertExec(
   10.23 +            "No instOf properties found",
   10.24 +            Instance.class, "noInstOfExposed__I",
   10.25 +            Double.valueOf(0)
   10.26 +        );
   10.27 +    }
   10.28 +    @Test public void noIterablePropsOnObject() throws Exception {
   10.29 +        assertExec(
   10.30 +            "No instOf properties found",
   10.31 +            Instance.class, "props__Ljava_lang_String_2",
   10.32 +            ""
   10.33 +        );
   10.34 +    }
   10.35 +    
   10.36      @Test public void verifyMagicOne() throws Exception {
   10.37          assertExec(
   10.38              "Should be three and something",