From: <rb...@us...> - 2018-01-15 20:16:05
|
Revision: 15079 http://sourceforge.net/p/htmlunit/code/15079 Author: rbri Date: 2018-01-15 20:16:02 +0000 (Mon, 15 Jan 2018) Log Message: ----------- Array.from() now supports user defined iterators also Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2018-01-13 16:57:54 UTC (rev 15078) +++ trunk/htmlunit/src/changes/changes.xml 2018-01-15 20:16:02 UTC (rev 15079) @@ -9,6 +9,9 @@ <body> <release version="2.30" date="xx, 2018" description="Bugfixes, URLSearchParams implemented"> <action type="change" dev="rbri"> + JavaScript: Array.from() now supports user defined iterators also. + </action> + <action type="change" dev="rbri"> Samples on page Get Started updated. </action> <action type="fix" dev="rbri" issue="1946"> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java 2018-01-13 16:57:54 UTC (rev 15078) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java 2018-01-15 20:16:02 UTC (rev 15079) @@ -21,8 +21,11 @@ import net.sourceforge.htmlunit.corejs.javascript.BaseFunction; import net.sourceforge.htmlunit.corejs.javascript.Context; +import net.sourceforge.htmlunit.corejs.javascript.ES6Iterator; import net.sourceforge.htmlunit.corejs.javascript.Function; +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,7 +51,7 @@ final Object arrayLike = args[0]; Object[] array = null; if (arrayLike instanceof Scriptable) { - final Scriptable scriptable = (Scriptable) arrayLike; + Scriptable scriptable = (Scriptable) arrayLike; final Object length = scriptable.get("length", scriptable); if (length != Scriptable.NOT_FOUND) { final int size = (int) Context.toNumber(length); @@ -60,21 +63,52 @@ final Object iterator = scriptable.get(Symbol.ITERATOR_STRING, scriptable); if (iterator != Scriptable.NOT_FOUND) { - final List<Object> list = new ArrayList<>(); - final Iterator it = (Iterator) ((BaseFunction) iterator) + final Object obj = ((BaseFunction) iterator) .call(context, thisObj.getParentScope(), scriptable, new Object[0]); - SimpleScriptable next = it.next(); - boolean done = (boolean) next.get("done"); - Object value = next.get("value"); - while (!done) { - if (value != Undefined.instance) { - list.add(value); + + if (obj instanceof Iterator) { + final Iterator it = (Iterator) obj; + SimpleScriptable next = it.next(); + boolean done = (boolean) next.get(ES6Iterator.DONE_PROPERTY); + Object value = next.get(ES6Iterator.VALUE_PROPERTY); + + final List<Object> list = new ArrayList<>(); + while (!done) { + if (value != Undefined.instance) { + list.add(value); + } + next = it.next(); + done = (boolean) next.get(ES6Iterator.DONE_PROPERTY); + value = next.get(ES6Iterator.VALUE_PROPERTY); } - next = it.next(); - done = (boolean) next.get("done"); - value = next.get("value"); + array = list.toArray(); } - array = list.toArray(); + else if (obj instanceof Scriptable) { + scriptable = (Scriptable) obj; + // handle user defined iterator + final Object nextFunct = scriptable.get(ES6Iterator.NEXT_METHOD, (Scriptable) obj); + if (!(nextFunct instanceof BaseFunction)) { + throw ScriptRuntime.typeError("undefined is not a function"); + } + final Object nextObj = ((BaseFunction) nextFunct) + .call(context, thisObj.getParentScope(), scriptable, new Object[0]); + + ScriptableObject next = (ScriptableObject) nextObj; + boolean done = (boolean) next.get(ES6Iterator.DONE_PROPERTY); + Object value = next.get(ES6Iterator.VALUE_PROPERTY); + + final List<Object> list = new ArrayList<>(); + while (!done) { + if (value != Undefined.instance) { + list.add(value); + } + next = (ScriptableObject) ((BaseFunction) nextFunct) + .call(context, thisObj.getParentScope(), scriptable, new Object[0]); + done = (boolean) next.get(ES6Iterator.DONE_PROPERTY); + value = next.get(ES6Iterator.VALUE_PROPERTY); + } + array = list.toArray(); + } } } else if (arrayLike instanceof String) { Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java 2018-01-13 16:57:54 UTC (rev 15078) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java 2018-01-15 20:16:02 UTC (rev 15079) @@ -14,6 +14,8 @@ */ package com.gargoylesoftware.htmlunit.javascript; +import static com.gargoylesoftware.htmlunit.BrowserRunner.TestedBrowser.CHROME; +import static com.gargoylesoftware.htmlunit.BrowserRunner.TestedBrowser.FF; import static com.gargoylesoftware.htmlunit.BrowserRunner.TestedBrowser.FF45; import static com.gargoylesoftware.htmlunit.BrowserRunner.TestedBrowser.IE; @@ -27,8 +29,8 @@ import com.gargoylesoftware.htmlunit.BrowserRunner; import com.gargoylesoftware.htmlunit.BrowserRunner.Alerts; import com.gargoylesoftware.htmlunit.BrowserRunner.NotYetImplemented; +import com.gargoylesoftware.htmlunit.WebDriverTestCase; import com.gargoylesoftware.htmlunit.annotations.BuildServerDiscrepancy; -import com.gargoylesoftware.htmlunit.WebDriverTestCase; /** * Array is a native JavaScript object and therefore provided by Rhino but behavior should be @@ -307,6 +309,36 @@ * @throws Exception if the test fails */ @Test + @Alerts(DEFAULT = {"3", "a", "b", "c"}, + IE = "not supported") + @NotYetImplemented({CHROME, FF}) + public void fromArrayIterator() throws Exception { + final String html + = "<html>\n" + + "<head>\n" + + "<script>\n" + + " if (Array.from) {\n" + + " var input = ['a', 'b', 'c'];\n" + + " var arr = Array.from(input[Symbol.iterator]());\n" + + " alert(arr.length);\n" + + " for (var i = 0; i < arr.length; i++) {\n" + + " alert(arr[i]);\n" + + " }\n" + + " } else {\n" + + " alert('not supported');\n" + + " }\n" + + "</script>\n" + + "</head>\n" + + "<body>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test @Alerts(DEFAULT = {"2", "abc", "[object Window]"}, IE = "not supported") public void fromSet() throws Exception { @@ -365,9 +397,8 @@ * @throws Exception if the test fails */ @Test - @Alerts(DEFAULT = {"3", "1", "two", "3"}, + @Alerts(DEFAULT = {"1", "by"}, IE = "not supported") - @NotYetImplemented public void fromUserDefinedIterable() throws Exception { final String html = "<html>\n" @@ -375,11 +406,19 @@ + "<script>\n" + " if (Array.from) {\n" + " var myIterable = {};\n" - + " myIterable[Symbol.iterator] = function*() {\n" - + " yield 1;\n" - + " yield 'two';\n" - + " yield 3;\n" + + " myIterable[Symbol.iterator] = function() {\n" + + " return {\n" + + " next: function(){\n" + + " if (this._first) {;\n" + + " this._first = false;\n" + + " return { value: 'by', done: false };\n" + + " }\n" + + " return { done: true };\n" + + " },\n" + + " _first: true\n" + " };\n" + + " };\n" + + " var arr = Array.from(myIterable);\n" + " alert(arr.length);\n" + " for (var i = 0; i < arr.length; i++) {\n" @@ -400,6 +439,37 @@ * @throws Exception if the test fails */ @Test + @Alerts(DEFAULT = {"TypeError"}, + IE = "not supported") + public void fromObject() throws Exception { + final String html + = "<html>\n" + + "<head>\n" + + "<script>\n" + + " if (Array.from) {\n" + + " var myIterable = {};\n" + + " myIterable[Symbol.iterator] = function() {\n" + + " return { done: true };\n" + + " };\n" + + + " try {\n" + + " Array.from(myIterable);\n" + + " } catch(e) { alert('TypeError'); }\n" + + " } else {\n" + + " alert('not supported');\n" + + " }\n" + + "</script>\n" + + "</head>\n" + + "<body>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test @Alerts(DEFAULT = "0", IE = "not supported") public void fromNativeObject() throws Exception { @@ -428,6 +498,36 @@ * @throws Exception if the test fails */ @Test + @Alerts(DEFAULT = {"1", "abc"}, + IE = "not supported") + public void fromArguments() throws Exception { + final String html + = "<html>\n" + + "<head>\n" + + "<script>\n" + + " function test(a) { return Array.from(arguments); }\n" + + + " if (Array.from) {\n" + + " var arr = test('abc') \n" + + " alert(arr.length);\n" + + " for (var i = 0; i < arr.length; i++) {\n" + + " alert(arr[i]);\n" + + " }\n" + + " } else {\n" + + " alert('not supported');\n" + + " }\n" + + "</script>\n" + + "</head>\n" + + "<body>\n" + + "</body></html>"; + + loadPageWithAlerts2(html); + } + + /** + * @throws Exception if the test fails + */ + @Test @Alerts({"true", "true", "true", "true", "false", "false", "false", "false", "false", "false", "false", "false", "false"}) public void isArray() throws Exception { @@ -460,7 +560,9 @@ * @throws Exception if the test fails */ @Test - @Alerts({"function", "20"}) + @Alerts(DEFAULT = {"function", "20"}, + IE = "undefined") + @NotYetImplemented(IE) public void find() throws Exception { final String html = "<html>\n" @@ -470,7 +572,9 @@ + "\n" + " var arr = [1, 20, 7, 17];\n" + " alert(typeof arr.find);\n" - + " alert(arr.find(isBig));\n" + + " if (typeof arr.find == 'function') {\n" + + " alert(arr.find(isBig));\n" + + " }\n" + "</script>\n" + "</head>\n" + "<body>\n" @@ -483,7 +587,9 @@ * @throws Exception if the test fails */ @Test - @Alerts({"function", "20"}) + @Alerts(DEFAULT = {"function", "20"}, + IE = "undefined") + @NotYetImplemented(IE) public void findPrototype() throws Exception { final String html = "<html>\n" @@ -494,7 +600,9 @@ + " var arr = [1, 20, 7, 17];\n" + " var find = Array.prototype.find;\n" + " alert(typeof find);\n" - + " alert(find.call(arr, isBig));\n" + + " if (typeof find == 'function') {\n" + + " alert(find.call(arr, isBig));\n" + + " }\n" + "</script>\n" + "</head>\n" + "<body>\n" @@ -532,7 +640,9 @@ * @throws Exception if the test fails */ @Test - @Alerts({"function", "1"}) + @Alerts(DEFAULT = {"function", "1"}, + IE = "undefined") + @NotYetImplemented(IE) public void findIndex() throws Exception { final String html = "<html>\n" @@ -542,7 +652,9 @@ + "\n" + " var arr = [1, 20, 7, 17];\n" + " alert(typeof arr.findIndex);\n" - + " alert(arr.findIndex(isBig));\n" + + " if (typeof arr.findIndex == 'function') {\n" + + " alert(arr.findIndex(isBig));\n" + + " }\n" + "</script>\n" + "</head>\n" + "<body>\n" @@ -555,7 +667,9 @@ * @throws Exception if the test fails */ @Test - @Alerts({"function", "1"}) + @Alerts(DEFAULT = {"function", "1"}, + IE = "undefined") + @NotYetImplemented(IE) public void findIndexPrototype() throws Exception { final String html = "<html>\n" @@ -566,7 +680,9 @@ + " var arr = [1, 20, 7, 17];\n" + " var findIndex = Array.prototype.findIndex;\n" + " alert(typeof findIndex);\n" - + " alert(findIndex.call(arr, isBig));\n" + + " if (typeof findIndex == 'function') {\n" + + " alert(findIndex.call(arr, isBig));\n" + + " }\n" + "</script>\n" + "</head>\n" + "<body>\n" |