From: <rb...@us...> - 2018-01-22 20:09:19
|
Revision: 15090 http://sourceforge.net/p/htmlunit/code/15090 Author: rbri Date: 2018-01-22 20:09:16 +0000 (Mon, 22 Jan 2018) Log Message: ----------- WeakSet and WeakMap constructor now supports user defined iterators also Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Map.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMap.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMapTest.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2018-01-22 18:44:13 UTC (rev 15089) +++ trunk/htmlunit/src/changes/changes.xml 2018-01-22 20:09:16 UTC (rev 15090) @@ -8,6 +8,9 @@ <body> <release version="2.30" date="xx, 2018" description="Bugfixes, URLSearchParams implemented, start adding support of user defined iterators"> + <action type="add" dev="rbri"> + JavaScript: WeakSet and WeakMap constructor now supports user defined iterators also. + </action> <action type="fix" dev="rbri"> Doing Ctrl+Click on an anchor now opens a new window also if the href is javascript based. </action> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java 2018-01-22 18:44:13 UTC (rev 15089) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/BrowserVersionFeatures.java 2018-01-22 20:09:16 UTC (rev 15090) @@ -1338,9 +1338,9 @@ @BrowserFeature({CHROME, FF}) JS_TYPE_ACCEPTS_ARBITRARY_VALUES, - /** WeakMap supports the argument constructor. */ - @BrowserFeature({CHROME, FF}) - JS_WEAKMAP_CONSTRUCTOR_ARGUMENT, + /** WeakMap ignores the constructor argument. */ + @BrowserFeature(IE) + JS_WEAKMAP_CONSTRUCTOR_IGNORE_ARGUMENT, /** Allow inheriting parent constants * in {@link com.gargoylesoftware.htmlunit.javascript.host.event.WebGLContextEvent}. */ Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Map.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Map.java 2018-01-22 18:44:13 UTC (rev 15089) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Map.java 2018-01-22 20:09:16 UTC (rev 15090) @@ -99,43 +99,22 @@ if (arrayLike instanceof Scriptable) { final Scriptable scriptable = (Scriptable) arrayLike; - final Object length = scriptable.get("length", scriptable); - if (length != Scriptable.NOT_FOUND) { - final int size = (int) Context.toNumber(length); - for (int i = 0; i < size; i++) { - final Object entryObject = scriptable.get(i, scriptable); - if (entryObject instanceof NativeArray) { - final Object[] entry = toArray((NativeArray) entryObject); + if (Iterator.iterate(Context.getCurrentContext(), this, scriptable, + value -> { + if (Undefined.instance != value && value instanceof NativeArray) { + final Object[] entry = toArray((NativeArray) value); if (entry.length > 0) { final Object key = entry[0]; - final Object value = entry.length > 1 ? entry[1] : null; - set(key, value); + if (Undefined.instance != key) { + final Object entryValue = entry.length > 1 ? entry[1] : null; + set(key, entryValue); + } } } else { throw Context.reportRuntimeError("TypeError: object is not iterable (" - + entryObject.getClass().getName() + ")"); + + value.getClass().getName() + ")"); } - } - return; - } - - if (Iterator.iterate(context, this, scriptable, - value -> { - if (value != Undefined.instance) { - if (value instanceof NativeArray) { - final Object[] entry = toArray((NativeArray) value); - if (entry.length > 0) { - final Object entryKey = entry[0]; - final Object entryValue = entry.length > 1 ? entry[1] : null; - set(entryKey, entryValue); - } - } - else { - throw Context.reportRuntimeError("TypeError: object is not iterable (" - + value.getClass().getName() + ")"); - } - } })) { return; } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMap.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMap.java 2018-01-22 18:44:13 UTC (rev 15089) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMap.java 2018-01-22 20:09:16 UTC (rev 15090) @@ -14,7 +14,7 @@ */ package com.gargoylesoftware.htmlunit.javascript.host; -import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_WEAKMAP_CONSTRUCTOR_ARGUMENT; +import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_WEAKMAP_CONSTRUCTOR_IGNORE_ARGUMENT; import java.util.WeakHashMap; @@ -27,6 +27,7 @@ import net.sourceforge.htmlunit.corejs.javascript.Delegator; import net.sourceforge.htmlunit.corejs.javascript.NativeArray; import net.sourceforge.htmlunit.corejs.javascript.ScriptRuntime; +import net.sourceforge.htmlunit.corejs.javascript.Scriptable; import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; import net.sourceforge.htmlunit.corejs.javascript.Undefined; @@ -48,40 +49,86 @@ /** * Creates an instance. - * @param iterable an Array or other iterable object + * @param arrayLike an Array or other iterable object */ @JsxConstructor - public WeakMap(final Object iterable) { - if (iterable != Undefined.instance) { - final Window window = (Window) ScriptRuntime.getTopCallScope(Context.getCurrentContext()); - if (window.getBrowserVersion().hasFeature(JS_WEAKMAP_CONSTRUCTOR_ARGUMENT)) { - if (iterable instanceof NativeArray) { - final NativeArray array = (NativeArray) iterable; - for (int i = 0; i < array.getLength(); i++) { - final Object entryObject = array.get(i); - if (entryObject instanceof NativeArray) { - final Object[] entry = ((NativeArray) entryObject).toArray(); - if (entry.length > 0) { - final Object key = entry[0]; - final Object value = entry.length > 1 ? entry[1] : null; - set(key, value); - } + public WeakMap(final Object arrayLike) { + if (arrayLike == Undefined.instance) { + return; + } + + final Context context = Context.getCurrentContext(); + final Window window = (Window) ScriptRuntime.getTopCallScope(context); + if (window.getBrowserVersion().hasFeature(JS_WEAKMAP_CONSTRUCTOR_IGNORE_ARGUMENT)) { + return; + } + + if (arrayLike instanceof NativeArray) { + final NativeArray array = (NativeArray) arrayLike; + for (int i = 0; i < array.getLength(); i++) { + final Object entryObject = array.get(i); + if (entryObject instanceof NativeArray) { + final Object[] entry = toArray((NativeArray) entryObject); + if (entry.length > 0) { + final Object key = entry[0]; + if (Undefined.instance != key && key instanceof ScriptableObject) { + final Object value = entry.length > 1 ? entry[1] : null; + set(key, value); } - else { - throw Context.reportRuntimeError("TypeError: object is not iterable (" - + entryObject.getClass().getName() + ")"); - } } } else { throw Context.reportRuntimeError("TypeError: object is not iterable (" - + iterable.getClass().getName() + ")"); + + entryObject.getClass().getName() + ")"); } } + return; } + + if (arrayLike instanceof Scriptable) { + final Scriptable scriptable = (Scriptable) arrayLike; + if (Iterator.iterate(Context.getCurrentContext(), this, scriptable, + value -> { + if (Undefined.instance != value && value instanceof NativeArray) { + final Object[] entry = toArray((NativeArray) value); + if (entry.length > 0) { + final Object key = entry[0]; + if (Undefined.instance != key && key instanceof ScriptableObject) { + final Object entryValue = entry.length > 1 ? entry[1] : null; + set(key, entryValue); + } + } + } + else { + throw Context.reportRuntimeError("TypeError: object is not iterable (" + + value.getClass().getName() + ")"); + } + })) { + return; + } + } + + throw Context.reportRuntimeError("TypeError: object is not iterable (" + arrayLike.getClass().getName() + ")"); } /** + * Replacement of {@link NativeArray#toArray()}. + */ + private static Object[] toArray(final NativeArray narray) { + final long longLen = narray.getLength(); + if (longLen > Integer.MAX_VALUE) { + throw new IllegalStateException(); + } + + final int len = (int) longLen; + final Object[] arr = new Object[len]; + for (int i = 0; i < len; i++) { + arr[i] = ScriptableObject.getProperty(narray, i); + } + return arr; + } + + /** * Returns the value of the given key. * @param key the key * @return the value Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMapTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMapTest.java 2018-01-22 18:44:13 UTC (rev 15089) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/host/WeakMapTest.java 2018-01-22 20:09:16 UTC (rev 15090) @@ -25,6 +25,7 @@ * Tests for {@link WeakMap}. * * @author Ahmed Ashour + * @author Ronald Brill */ @RunWith(BrowserRunner.class) public class WeakMapTest extends WebDriverTestCase { @@ -33,6 +34,118 @@ * @throws Exception if the test fails */ @Test + @Alerts(DEFAULT = {"true", "one"}, + IE = {"false", "undefined"}) + public void constructorArray() throws Exception { + final String html + = "<html><head><title>foo</title><script>\n" + + "function test() {\n" + + " var obj = {};\n" + + " var foo = {};\n" + + " var myMap = new WeakMap([[ obj, 'one' ],[ foo, 'two' ]]);\n" + + " alert(myMap.has(foo));\n" + + " alert(myMap.get(obj));\n" + + "}\n" + + "</script></head><body onload='test()'>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts(DEFAULT = "exception", + IE = {"false"}) + public void constructorSetParam() throws Exception { + final String html + = "<html><head><title>foo</title><script>\n" + + "function test() {\n" + + " try {\n" + + " var myMap = new WeakMap(new Set('test'));\n" + + " alert(myMap.has('test'));\n" + + " } catch(e) {\n" + + " alert('exception');\n" + + " }\n" + + "}\n" + + "</script></head><body onload='test()'>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts(DEFAULT = "true", + IE = "false") + public void constructorMapParam() throws Exception { + final String html + = "<html><head><title>foo</title><script>\n" + + "function test() {\n" + + " var obj = {};\n" + + " var foo = {};\n" + + " var testMap = new Map([[ obj, 'one' ],[ foo, 'two' ]]);\n" + + " try {\n" + + " var myMap = new WeakMap(testMap);\n" + + " alert(myMap.has(foo));\n" + + " } catch(e) {\n" + + " alert('exception');\n" + + " }\n" + + "}\n" + + "</script></head><body onload='test()'>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts(DEFAULT = "true", + IE = {}) + public void constructorIteratorParam() throws Exception { + final String html + = "<html><head><title>foo</title><script>\n" + + "function test() {\n" + + " if (window.WeakSet) {\n" + + " var obj = {};\n" + + " var foo = {};\n" + + " var myIterable = {};\n" + + " myIterable[Symbol.iterator] = function() {\n" + + " return {\n" + + " next: function() {\n" + + " if (this._first) {;\n" + + " this._first = false;\n" + + " return { value: [ foo, 'one' ], done: false };\n" + + " }\n" + + " return { done: true };\n" + + " },\n" + + " _first: true\n" + + " };\n" + + " };\n" + + " try {\n" + + " var myMap = new WeakMap(myIterable);\n" + + " alert(myMap.has(foo));\n" + + " } catch(e) {\n" + + " alert('exception');\n" + + " }\n" + + " }" + + "}\n" + + "</script></head>\n" + + "<body onload='test()'>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test @Alerts(DEFAULT = {"undefined", "value2"}, IE = {"undefined", "undefined"}) public void get() throws Exception { |