From: <asa...@us...> - 2017-06-03 09:29:52
|
Revision: 14538 http://sourceforge.net/p/htmlunit/code/14538 Author: asashour Date: 2017-06-03 09:29:49 +0000 (Sat, 03 Jun 2017) Log Message: ----------- JavaScript: implement Array.from() Modified Paths: -------------- trunk/htmlunit/src/changes/changes.xml trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/JavaScriptEngine.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Iterator.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Map.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Set.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java Added Paths: ----------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java Modified: trunk/htmlunit/src/changes/changes.xml =================================================================== --- trunk/htmlunit/src/changes/changes.xml 2017-06-03 06:31:32 UTC (rev 14537) +++ trunk/htmlunit/src/changes/changes.xml 2017-06-03 09:29:49 UTC (rev 14538) @@ -7,7 +7,10 @@ </properties> <body> - <release version="2.27" date="???" description="GAE broken, FF52, Bugfixes"> + <release version="2.27" date="???" description="FF52, Bugfixes"> + <action type="add" dev="asashour" issue="44336828" system="stackoverflow"> + JavaScript: implement Array.from(). + </action> <action type="update" dev="rbri"> CSS: cssparser updated to 0.9.23. </action> Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/JavaScriptEngine.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/JavaScriptEngine.java 2017-06-03 06:31:32 UTC (rev 14537) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/JavaScriptEngine.java 2017-06-03 09:29:49 UTC (rev 14538) @@ -57,6 +57,7 @@ import com.gargoylesoftware.htmlunit.javascript.configuration.ClassConfiguration.PropertyInfo; import com.gargoylesoftware.htmlunit.javascript.configuration.JavaScriptConfiguration; import com.gargoylesoftware.htmlunit.javascript.host.ActiveXObject; +import com.gargoylesoftware.htmlunit.javascript.host.ArrayCustom; import com.gargoylesoftware.htmlunit.javascript.host.DateCustom; import com.gargoylesoftware.htmlunit.javascript.host.ObjectCustom; import com.gargoylesoftware.htmlunit.javascript.host.Reflect; @@ -493,6 +494,10 @@ if (browserVersion.hasFeature(STRING_INCLUDES)) { ((ScriptableObject) objectPrototype).defineFunctionProperties(new String[] {"getOwnPropertySymbols"}, ObjectCustom.class, ScriptableObject.DONTENUM); + + final ScriptableObject arrayPrototype = (ScriptableObject) ScriptRuntime.name(context, window, "Array"); + ((ScriptableObject) arrayPrototype).defineFunctionProperties(new String[] {"from"}, + ArrayCustom.class, ScriptableObject.DONTENUM); } window.setPrototypes(prototypes, prototypesPerJSName); Added: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java (rev 0) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java 2017-06-03 09:29:49 UTC (rev 14538) @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2002-2017 Gargoyle Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gargoylesoftware.htmlunit.javascript.host; + +import java.util.ArrayList; +import java.util.List; + +import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable; + +import net.sourceforge.htmlunit.corejs.javascript.Context; +import net.sourceforge.htmlunit.corejs.javascript.Function; +import net.sourceforge.htmlunit.corejs.javascript.FunctionObject; +import net.sourceforge.htmlunit.corejs.javascript.Scriptable; +import net.sourceforge.htmlunit.corejs.javascript.Undefined; + +/** + * Contains some missing features of Rhino NativeArray. + * + * @author Ahmed Ashour + */ +public final class ArrayCustom { + + private ArrayCustom() { } + + /** + * Creates a new Array instance from an array-like or iterable object. + * @param context the JavaScript context + * @param thisObj the scriptable + * @param args the arguments passed into the method + * @param function the function + * @return converted string + */ + public static Scriptable from( + final Context context, final Scriptable thisObj, final Object[] args, final Function function) { + final Object arrayLike = args[0]; + Object[] array = null; + if (arrayLike instanceof Scriptable) { + final Scriptable scriptable = (Scriptable) arrayLike; + final Object length = scriptable.get("length", scriptable); + if (length != Scriptable.NOT_FOUND) { + int size = (int) length; + array = new Object[(int) size]; + for (int i = 0; i < size; i++) { + array[i] = scriptable.get(i, scriptable); + } + } + final Object iterator = scriptable.get(Symbol.ITERATOR_STRING, scriptable); + if (iterator != Scriptable.NOT_FOUND) { + final List<Object> list = new ArrayList<>(); + final Iterator it = (Iterator) ((FunctionObject) 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); + } + next = it.next(); + done = (boolean) next.get("done"); + value = next.get("value"); + } + array = list.toArray(); + } + } + else if (arrayLike instanceof String) { + final String string = (String) arrayLike; + array = new Object[string.length()]; + for (int i = 0; i < array.length; i++) { + array[i] = string.charAt(i); + } + } + if (array == null) { + array = new Object[0]; + } + return context.newArray(thisObj, array); + } +} Property changes on: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/ArrayCustom.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Iterator.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Iterator.java 2017-06-03 06:31:32 UTC (rev 14537) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Iterator.java 2017-06-03 09:29:49 UTC (rev 14538) @@ -63,7 +63,7 @@ * Returns the next object. * @return the next object */ - public Object next() { + public SimpleScriptable next() { final SimpleScriptable object = new SimpleScriptable(); object.setParentScope(getParentScope()); final Object value; 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 2017-06-03 06:31:32 UTC (rev 14537) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Map.java 2017-06-03 09:29:49 UTC (rev 14538) @@ -187,7 +187,7 @@ public Object get(final String name, final Scriptable start) { // A hack to handle Rhino not supporting "get(Object object, Scriptable start)" if (name.equals(Symbol.ITERATOR_STRING)) { - return super.get("entries", start); + return ScriptableObject.getProperty(start, "entries"); } return super.get(name, start); } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Set.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Set.java 2017-06-03 06:31:32 UTC (rev 14537) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/Set.java 2017-06-03 09:29:49 UTC (rev 14538) @@ -166,7 +166,7 @@ public Object get(final String name, final Scriptable start) { // A hack to handle Rhino not supporting "get(Object object, Scriptable start)" if (name.equals(Symbol.ITERATOR_STRING)) { - return super.get("values", start); + return ScriptableObject.getProperty(start, "values"); } return super.get(name, start); } Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java 2017-06-03 06:31:32 UTC (rev 14537) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/javascript/NativeArrayTest.java 2017-06-03 09:29:49 UTC (rev 14538) @@ -35,6 +35,7 @@ * * @author Marc Guillemot * @author Frank Danek + * @author Ahmed Ashour */ @RunWith(BrowserRunner.class) public class NativeArrayTest extends WebDriverTestCase { @@ -266,4 +267,90 @@ loadPageWithAlerts2(html); } + + /** + * @throws Exception if the test fails + */ + @Test + @Alerts(DEFAULT = {"3", "a", "b", "c"}, + IE = "not supported") + public void from() throws Exception { + final String html + = "<html>\n" + + "<head>\n" + + "<script>\n" + + " if (Array.from) {\n" + + " var arr = Array.from('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(DEFAULT = {"2", "abc", "[object Window]"}, + IE = "not supported") + public void fromSet() throws Exception { + final String html + = "<html>\n" + + "<head>\n" + + "<script>\n" + + " if (Array.from) {\n" + + " var s = new Set(['abc', window]);\n" + + " var arr = Array.from(s);\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 = {"3", "1,2", "3,4", "5,6"}, + IE = "not supported") + public void fromMap() throws Exception { + final String html + = "<html>\n" + + "<head>\n" + + "<script>\n" + + " if (Array.from) {\n" + + " var m = new Map([[1, 2], [3, 4], [5, 6]]);\n" + + " var arr = Array.from(m);\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); + } } |