This commit is contained in:
Cam Walter 2024-08-23 00:51:10 -06:00 коммит произвёл Greg Brail
Родитель c0936cae9b
Коммит 4e6523b61b
4 изменённых файлов: 143 добавлений и 28 удалений

Просмотреть файл

@ -1,5 +1,10 @@
package org.mozilla.javascript;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Abstract Object Operations as defined by EcmaScript
*
@ -24,6 +29,11 @@ public class AbstractEcmaObjectOperations {
SEALED
}
enum KEY_COERCION {
PROPERTY,
COLLECTION,
}
/**
* Implementation of Abstract Object operation HasOwnProperty as defined by EcmaScript
*
@ -232,4 +242,65 @@ public class AbstractEcmaObjectOperations {
base.put(p, o, v);
}
}
/**
* Implement the ECMAScript abstract operation "GroupBy" defined in section 7.3.35 of ECMA262.
*
* @param cx
* @param scope
* @param items
* @param callback
* @param keyCoercion
* @see <a href="https://tc39.es/ecma262/#sec-groupby"></a>
*/
static Map<Object, List<Object>> groupBy(
Context cx,
Scriptable scope,
IdFunctionObject f,
Object items,
Object callback,
KEY_COERCION keyCoercion) {
if (cx.getLanguageVersion() >= Context.VERSION_ES6) {
ScriptRuntimeES6.requireObjectCoercible(cx, items, f);
}
if (!(callback instanceof Callable)) {
throw ScriptRuntime.typeErrorById(
"msg.isnt.function", callback, ScriptRuntime.typeof(callback));
}
// LinkedHashMap used to preserve key creation order
Map<Object, List<Object>> groups = new LinkedHashMap<>();
final Object iterator = ScriptRuntime.callIterator(items, cx, scope);
try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, iterator)) {
double i = 0;
for (Object o : it) {
if (i > NativeNumber.MAX_SAFE_INTEGER) {
it.close();
throw ScriptRuntime.typeError("Too many values to iterate");
}
Object[] args = {o, i};
Object key =
((Callable) callback).call(cx, scope, Undefined.SCRIPTABLE_UNDEFINED, args);
if (keyCoercion == KEY_COERCION.PROPERTY) {
if (!ScriptRuntime.isSymbol(key)) {
key = ScriptRuntime.toString(key);
}
} else {
assert keyCoercion == KEY_COERCION.COLLECTION;
if ((key instanceof Number)
&& ((Number) key).doubleValue() == ScriptRuntime.negativeZero) {
key = ScriptRuntime.zeroObj;
}
}
List<Object> group = groups.computeIfAbsent(key, (k) -> new ArrayList<>());
group.add(o);
i++;
}
}
return groups;
}
}

Просмотреть файл

@ -6,6 +6,9 @@
package org.mozilla.javascript;
import java.util.List;
import java.util.Map;
public class NativeMap extends IdScriptableObject {
private static final long serialVersionUID = 1171922614280016891L;
private static final Object MAP_TAG = "Map";
@ -37,6 +40,12 @@ public class NativeMap extends IdScriptableObject {
return "Map";
}
@Override
public void fillConstructorProperties(IdFunctionObject ctor) {
addIdFunctionProperty(ctor, MAP_TAG, ConstructorId_groupBy, "groupBy", 2);
super.fillConstructorProperties(ctor);
}
@Override
public Object execIdCall(
IdFunctionObject f, Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
@ -82,6 +91,30 @@ public class NativeMap extends IdScriptableObject {
args.length > 1 ? args[1] : Undefined.instance);
case SymbolId_getSize:
return realThis(thisObj, f).js_getSize();
case ConstructorId_groupBy:
{
Object items = args.length < 1 ? Undefined.instance : args[0];
Object callback = args.length < 2 ? Undefined.instance : args[1];
Map<Object, List<Object>> groups =
AbstractEcmaObjectOperations.groupBy(
cx,
scope,
f,
items,
callback,
AbstractEcmaObjectOperations.KEY_COERCION.COLLECTION);
NativeMap map = (NativeMap) cx.newObject(scope, "Map");
for (Map.Entry<Object, List<Object>> entry : groups.entrySet()) {
Scriptable elements = cx.newArray(scope, entry.getValue().toArray());
map.entries.put(entry.getKey(), elements);
}
return map;
}
}
throw new IllegalArgumentException("Map.prototype has no method: " + f.getFunctionName());
}
@ -315,7 +348,8 @@ public class NativeMap extends IdScriptableObject {
// Note that "SymbolId_iterator" is not present here. That's because the spec
// requires that it be the same value as the "entries" prototype property.
private static final int Id_constructor = 1,
private static final int ConstructorId_groupBy = -1,
Id_constructor = 1,
Id_set = 2,
Id_get = 3,
Id_delete = 4,

Просмотреть файл

@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
@ -85,6 +86,7 @@ public class NativeObject extends IdScriptableObject implements Map {
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_freeze, "freeze", 1);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_assign, "assign", 2);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_is, "is", 2);
addIdFunctionProperty(ctor, OBJECT_TAG, ConstructorId_groupBy, "groupBy", 2);
super.fillConstructorProperties(ctor);
}
@ -687,6 +689,37 @@ public class NativeObject extends IdScriptableObject implements Map {
return ScriptRuntime.wrapBoolean(ScriptRuntime.same(a1, a2));
}
case ConstructorId_groupBy:
{
Object items = args.length < 1 ? Undefined.instance : args[0];
Object callback = args.length < 2 ? Undefined.instance : args[1];
Map<Object, List<Object>> groups =
AbstractEcmaObjectOperations.groupBy(
cx,
scope,
f,
items,
callback,
AbstractEcmaObjectOperations.KEY_COERCION.PROPERTY);
NativeObject obj = (NativeObject) cx.newObject(scope);
obj.setPrototype(null);
for (Map.Entry<Object, List<Object>> entry : groups.entrySet()) {
Scriptable elements = cx.newArray(scope, entry.getValue().toArray());
ScriptableObject desc = (ScriptableObject) cx.newObject(scope);
desc.put("enumerable", desc, Boolean.TRUE);
desc.put("configurable", desc, Boolean.TRUE);
desc.put("value", desc, elements);
obj.defineOwnProperty(cx, entry.getKey(), desc);
}
return obj;
}
default:
throw new IllegalArgumentException(String.valueOf(id));
}
@ -1021,6 +1054,7 @@ public class NativeObject extends IdScriptableObject implements Map {
ConstructorId_fromEntries = -20,
ConstructorId_values = -21,
ConstructorId_hasOwn = -22,
ConstructorId_groupBy = -23,
Id_constructor = 1,
Id_toString = 2,
Id_toLocaleString = 3,

Просмотреть файл

@ -991,19 +991,7 @@ built-ins/JSON 37/144 (25.69%)
stringify/value-object-proxy-revoked.js {unsupported: [Proxy]}
stringify/value-string-escape-unicode.js
built-ins/Map 25/171 (14.62%)
groupBy/callback-arg.js
groupBy/callback-throws.js
groupBy/emptyList.js
groupBy/evenOdd.js
groupBy/groupLength.js
groupBy/iterator-next-throws.js
groupBy/length.js
groupBy/map-instance.js
groupBy/name.js
groupBy/negativeZero.js
groupBy/string.js
groupBy/toPropertyKey.js
built-ins/Map 13/171 (7.6%)
prototype/clear/not-a-constructor.js {unsupported: [Reflect.construct]}
prototype/delete/not-a-constructor.js {unsupported: [Reflect.construct]}
prototype/entries/not-a-constructor.js {unsupported: [Reflect.construct]}
@ -1110,7 +1098,7 @@ built-ins/Number 24/335 (7.16%)
S9.3.1_A3_T1_U180E.js {unsupported: [u180e]}
S9.3.1_A3_T2_U180E.js {unsupported: [u180e]}
built-ins/Object 230/3403 (6.76%)
built-ins/Object 218/3403 (6.41%)
assign/assignment-to-readonly-property-of-target-must-throw-a-typeerror-exception.js
assign/not-a-constructor.js {unsupported: [Reflect.construct]}
assign/source-own-prop-desc-missing.js {unsupported: [Proxy]}
@ -1200,18 +1188,6 @@ built-ins/Object 230/3403 (6.76%)
getOwnPropertySymbols/proxy-invariant-not-extensible-absent-string-key.js {unsupported: [Proxy]}
getOwnPropertySymbols/proxy-invariant-not-extensible-extra-string-key.js {unsupported: [Proxy]}
getPrototypeOf/not-a-constructor.js {unsupported: [Reflect.construct]}
groupBy/callback-arg.js
groupBy/callback-throws.js
groupBy/emptyList.js
groupBy/evenOdd.js
groupBy/groupLength.js
groupBy/invalid-property-key.js
groupBy/iterator-next-throws.js
groupBy/length.js
groupBy/name.js
groupBy/null-prototype.js
groupBy/string.js
groupBy/toPropertyKey.js
hasOwn/length.js
hasOwn/not-a-constructor.js {unsupported: [Reflect.construct]}
hasOwn/symbol_property_toPrimitive.js
@ -5306,7 +5282,7 @@ language/expressions/new 41/59 (69.49%)
~language/expressions/new.target
language/expressions/object 867/1169 (74.17%)
language/expressions/object 866/1169 (74.08%)
dstr/async-gen-meth-ary-init-iter-close.js {unsupported: [async-iteration, async]}
dstr/async-gen-meth-ary-init-iter-get-err.js {unsupported: [async-iteration]}
dstr/async-gen-meth-ary-init-iter-get-err-array-prototype.js {unsupported: [async-iteration]}