# HG changeset patch # User Jaroslav Tulach # Date 1403761374 -7200 # Node ID a84f511654ae5ab03b28cdb6c7d32af16f703bc2 # Parent e9cfb1d23abfbc05ed1ea780dbf5ee7cb8b9f04f# Parent 4156b1bd4b825cdf832694bace099630a774d251 Using Object.defineProperty to specify non-enumerable properties on Object diff -r e9cfb1d23abf -r a84f511654ae rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ClassTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/tck/ClassTest.java Thu Jun 26 07:42:54 2014 +0200 @@ -0,0 +1,47 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.tck; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class ClassTest { + @Compare + public boolean isDoubleArrayAssignableToObject() throws Exception { + double[] dbl = new double[0]; + Class dblCls = dbl.getClass(); + return Object.class.isAssignableFrom(dblCls); + } + + @Compare + public boolean isDoubleArrayAssignableToString() throws Exception { + double[] dbl = new double[0]; + Class dblCls = dbl.getClass(); + return String.class.isAssignableFrom(dblCls); + } + + @Factory + public static Object[] create() { + return VMTest.create(ClassTest.class); + } +} diff -r e9cfb1d23abf -r a84f511654ae rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/HtmlAnnotations.java --- a/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/HtmlAnnotations.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/emul/compact/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/HtmlAnnotations.java Thu Jun 26 07:42:54 2014 +0200 @@ -53,6 +53,9 @@ @JavaScriptBody(args = { }, javacall = true, body = "return @org.apidesign.bck2brwsr.vmtest.impl.HtmlAnnotations::callback()();") public static native int staticCallback(); + + @JavaScriptBody(args = { }, wait4js = false, body = "/* do nothing */") + public static native void empty(); protected long chooseLong(boolean takeFirst, boolean takeSecond, long first, long second) { diff -r e9cfb1d23abf -r a84f511654ae rt/emul/mini/src/main/java/java/lang/Class.java --- a/rt/emul/mini/src/main/java/java/lang/Class.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Thu Jun 26 07:42:54 2014 +0200 @@ -406,6 +406,9 @@ if (this == cls) { return true; } + if (this == Object.class) { + return true; + } if (isArray()) { final Class cmpType = cls.getComponentType(); @@ -420,6 +423,9 @@ if (cls.isPrimitive()) { return false; } + if (cls.isArray()) { + return false; + } String prop = "$instOf_" + getName().replace('.', '_'); return hasCnstrProperty(cls, prop); } diff -r e9cfb1d23abf -r a84f511654ae rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Thu Jun 26 07:42:54 2014 +0200 @@ -49,17 +49,34 @@ @JavaScriptBody(args = {"clazz", "prefix", "cnstr"}, body = "" + "var c = clazz.cnstr;\n" - + "if (!cnstr) c = c.prototype;" + + "if (!cnstr) c = c.prototype;\n" + "var arr = new Array();\n" - + "for (m in c) {\n" + + "function check(m, verify) {\n" + " if (m.indexOf(prefix) === 0) {\n" - + " if (!c[m].cls) continue;\n" + + " if (!c[m].cls) return;\n" + + " if (verify) {\n" + + " for (var i = 0; i < arr.length; i += 3) {\n" + + " if (arr[i] === m) return;\n" + + " }\n" + + " }\n" + " arr.push(m);\n" + " arr.push(c[m]);\n" + " arr.push(c[m].cls.$class);\n" - + " }" + + " }\n" + "}\n" - + "return arr;") + + "for (m in c) {\n" + + " check(m)\n" + + "}\n" + + "check('wait__V', true);\n" + + "check('wait__VJ', true);\n" + + "check('wait__VJI', true);\n" + + "check('equals__ZLjava_lang_Object_2', true);\n" + + "check('toString__Ljava_lang_String_2', true);\n" + + "check('hashCode__I', true);\n" + + "check('getClass__Ljava_lang_Class_2', true);\n" + + "check('notify__V', true);\n" + + "check('notifyAll__V', true);\n" + + "return arr;\n") private static native Object[] findMethodData( Class clazz, String prefix, boolean cnstr); diff -r e9cfb1d23abf -r a84f511654ae rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js --- a/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/emul/mini/src/main/resources/org/apidesign/vm4brwsr/emul/lang/java_lang_String.js Thu Jun 26 07:42:54 2014 +0200 @@ -2,7 +2,7 @@ vm.java_lang_reflect_Array(false); vm.java_lang_String(false); -Array.at = function(arr, indx, value) { +Object.defineProperty(Array, "at", { configurable: true, writable: true, value : function(arr, indx, value) { var prev = arr[indx]; if (typeof prev === 'undefined' && (indx < 0 || indx >= arr.length)) { var e = vm.java_lang_ArrayIndexOutOfBoundsException(true); @@ -14,11 +14,11 @@ } else { return prev; } -}; -Array.prototype.getClass__Ljava_lang_Class_2 = function() { +}}); +Object.defineProperty(Array.prototype, "getClass__Ljava_lang_Class_2", { configurable: true, writable: true, value : function() { return vm.java_lang_Class(false).defineArray__Ljava_lang_Class_2Ljava_lang_String_2Ljava_lang_Object_2(this.jvmName, this.fnc); -}; -Array.prototype.clone__Ljava_lang_Object_2 = function() { +}}); +Object.defineProperty(Array.prototype, "clone__Ljava_lang_Object_2", { configurable: true, writable: true, value : function() { var s = this.length; var ret = new Array(s); for (var i = 0; i < s; i++) { @@ -27,4 +27,4 @@ ret.jvmName = this.jvmName; ret.fnc = this.fnc; return ret; -}; +}}); diff -r e9cfb1d23abf -r a84f511654ae rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Thu Jun 26 07:42:54 2014 +0200 @@ -260,15 +260,18 @@ append("\n c.constructor = CLS;"); append("\n function fillInstOf(x) {"); String instOfName = "$instOf_" + className; - append("\n x['").append(instOfName).append("'] = true;"); + append("\n Object.defineProperty(x, '").append(instOfName).append("', { value : true });"); for (String superInterface : jc.getSuperInterfaces()) { String intrfc = superInterface.replace('/', '_'); append("\n vm.").append(intrfc).append("(false)['fillInstOf'](x);"); requireReference(superInterface); } append("\n }"); - append("\n c['fillInstOf'] = fillInstOf;"); - append("\n fillInstOf(c);"); + append("\n try {"); + append("\n Object.defineProperty(c, 'fillInstOf', { value: fillInstOf });"); + append("\n fillInstOf(c);"); + append("\n } catch (ignore) {"); + append("\n }"); // obfuscationDelegate.exportJSProperty(this, "c", instOfName); append("\n CLS.$class = 'temp';"); append("\n CLS.$class = "); @@ -362,7 +365,14 @@ final LocalsMapper lmapper = new LocalsMapper(stackMapIterator.getArguments()); - append(destObject).append(".").append(name).append(" = function("); + boolean obj = "java/lang/Object".equals(jc.getClassName()); + + if (obj) { + append("Object.defineProperty(").append(destObject). + append(", '").append(name).append("', { configurable: true, writable: true, value: function("); + } else { + append(destObject).append(".").append(name).append(" = function("); + } lmapper.outputArguments(this, m.isStatic()); append(") {").append("\n"); @@ -371,6 +381,9 @@ append(" throw 'no code found for ") .append(jc.getClassName()).append('.') .append(m.getName()).append("';\n"); + if (obj) { + append("}"); + } append("};"); return; } @@ -1390,7 +1403,11 @@ while (openBraces-- > 0) { append('}'); } - append("\n};"); + if (obj) { + append("\n}});"); + } else { + append("\n};"); + } } private int generateIf(StackMapper mapper, byte[] byteCodes, @@ -1739,6 +1756,8 @@ args[cnt++] = value; } else if ("javacall".equals(attr)) { javacall = "1".equals(value); + } else if ("wait4js".equals(attr)) { + // ignore, we always invoke synchronously } else { throw new IllegalArgumentException(attr); } diff -r e9cfb1d23abf -r a84f511654ae rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Thu Jun 26 07:42:54 2014 +0200 @@ -124,6 +124,24 @@ "java.lang.Object" ); } + + @Test public void jsListMethodsInObject() throws Exception { + String methods = Classes.listObject(false, ""); + + assertExec("Methods defined in Object", Classes.class, + "listObject__Ljava_lang_String_2ZLjava_lang_String_2", + methods, false, "" + ); + } + + @Test public void jsListMethodsInString() throws Exception { + String methods = Classes.listObject(true, "toStr"); + + assertExec("Methods defined in Object", Classes.class, + "listObject__Ljava_lang_String_2ZLjava_lang_String_2", + methods, true, "toStr" + ); + } @Test public void jsInvokeParamMethod() throws Exception { assertExec("sums two numbers via reflection", Classes.class, diff -r e9cfb1d23abf -r a84f511654ae rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Thu Jun 26 07:42:54 2014 +0200 @@ -120,6 +120,18 @@ } return sb.toString().toString(); } + + static String listObject(boolean string, String prefix) { + final Class c = string ? String.class : Object.class; + StringBuilder sb = new StringBuilder(); + for (Method m : c.getMethods()) { + final String n = m.getName(); + if (n.startsWith(prefix)) { + sb.append(n).append("\n"); + } + } + return sb.toString().toString(); + } @Retention(RetentionPolicy.CLASS) @interface Ann { } diff -r e9cfb1d23abf -r a84f511654ae rt/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/Instance.java Thu Jun 26 07:42:54 2014 +0200 @@ -135,6 +135,20 @@ return jsgetbytes(new Instance()); } + public static int noInstOfExposed() { + return countInstOf(10); + } + + public static String props() { + return list(new Object()); + } + + @JavaScriptBody(args = "o", body = "var s = ''; for (var p in {}) { s += p; }; return s;") + private static native String list(Object o); + + @JavaScriptBody(args = "o", body = "var i = 0; for (p in o) { if (p.toString().indexOf('instOf') >= 0) i++; } return i;") + private static native int countInstOf(Object o); + @JavaScriptBody(args = { "instance" }, body = "return instance.getByte__B();") private static native int jsgetbytes(Instance instance); diff -r e9cfb1d23abf -r a84f511654ae rt/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Mon Jun 09 22:07:29 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/InstanceTest.java Thu Jun 26 07:42:54 2014 +0200 @@ -17,9 +17,12 @@ */ package org.apidesign.vm4brwsr; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import static org.testng.Assert.fail; import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import org.testng.annotations.BeforeClass; /** * @@ -47,6 +50,21 @@ Double.valueOf(31) ); } + @Test public void noInstOfExposed() throws Exception { + assertExec( + "No instOf properties found", + Instance.class, "noInstOfExposed__I", + Double.valueOf(0) + ); + } + @Test public void noIterablePropsOnObject() throws Exception { + assertExec( + "No instOf properties found", + Instance.class, "props__Ljava_lang_String_2", + "" + ); + } + @Test public void verifyMagicOne() throws Exception { assertExec( "Should be three and something",