Skip to content

Commit ec427e5

Browse files
committed
[GR-27661] Proposal relative-indexing-method (to 21.0 branch).
PullRequest: js/1794
2 parents b8b6205 + 9014a7b commit ec427e5

File tree

7 files changed

+177
-17
lines changed

7 files changed

+177
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The main focus is on user-observable behavior of the engine.
99
* Adopted new interop exception handling and made JS exceptions extend `AbstractTruffleException`.
1010
* Implemented interop identity messages.
1111
* Expose `Graal.versionECMAScript` instead of `Graal.versionJS`.
12+
* Implemented the [relative indexing method](https://tc39.es/proposal-relative-indexing-method/) proposal. It is available in ECMAScript 2022 mode (`--js.ecmascript-version=2022`).
1213

1314
## Version 20.3.0
1415
* Updated Node.js to version 12.18.4.

graal-js/src/com.oracle.truffle.js.test.external/src/com/oracle/truffle/js/test/external/test262/Test262Runnable.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public class Test262Runnable extends TestRunnable {
8989

9090
private static final Set<String> SUPPORTED_FEATURES = new HashSet<>(Arrays.asList(new String[]{
9191
"AggregateError",
92+
"Array.prototype.at",
9293
"Array.prototype.flat",
9394
"Array.prototype.flatMap",
9495
"Array.prototype.values",
@@ -133,6 +134,7 @@ public class Test262Runnable extends TestRunnable {
133134
"Set",
134135
"SharedArrayBuffer",
135136
"String.fromCodePoint",
137+
"String.prototype.at",
136138
"String.prototype.endsWith",
137139
"String.prototype.includes",
138140
"String.prototype.matchAll",
@@ -155,6 +157,7 @@ public class Test262Runnable extends TestRunnable {
155157
"Symbol.toStringTag",
156158
"Symbol.unscopables",
157159
"TypedArray",
160+
"TypedArray.prototype.at",
158161
"Uint16Array",
159162
"Uint32Array",
160163
"Uint8Array",
@@ -220,19 +223,19 @@ public class Test262Runnable extends TestRunnable {
220223
"top-level-await",
221224
}));
222225
private static final Set<String> UNSUPPORTED_FEATURES = new HashSet<>(Arrays.asList(new String[]{
223-
"Array.prototype.at",
224226
"Atomics.waitAsync",
225227
"Intl.DateTimeFormat-dayPeriod",
226228
"Intl.DateTimeFormat-formatRange",
227229
"Intl.DateTimeFormat-fractionalSecondDigits",
228230
"IsHTMLDDA",
229-
"String.prototype.at",
230-
"TypedArray.prototype.at",
231231
"align-detached-buffer-semantics-with-web-reality",
232232
"arbitrary-module-namespace-names",
233233
"tail-call-optimization",
234234
}));
235235
private static final Set<String> ES2022_FEATURES = new HashSet<>(Arrays.asList(new String[]{
236+
"Array.prototype.at",
237+
"String.prototype.at",
238+
"TypedArray.prototype.at",
236239
"class-fields-private",
237240
"class-fields-public",
238241
"class-methods-private",
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
6+
*/
7+
/**
8+
* Test for the relative-indexing-method proposal.
9+
* https://tc39.es/proposal-relative-indexing-method/
10+
*
11+
* @option ecmascript-version=2022
12+
*/
13+
14+
load('../assert.js');
15+
16+
function arrayPrototypeTest() {
17+
var arr = [0,1,2,3,4,5];
18+
19+
//in bound
20+
assertSame(0, arr.at(0));
21+
assertSame(1, arr.at(1));
22+
assertSame(5, arr.at(5));
23+
24+
//out of bound
25+
assertSame(undefined, arr.at(6));
26+
assertSame(5, arr.at(-1));
27+
assertSame(4, arr.at(-2));
28+
29+
//general
30+
assertSame(1, Array.prototype.at.length);
31+
}
32+
33+
function stringPrototypeTest() {
34+
var str = "Graal.js";
35+
36+
//in bound
37+
assertSame("G", str.at(0));
38+
assertSame("r", str.at(1));
39+
assertSame("s", str.at(7));
40+
41+
//out of bound
42+
assertSame(undefined, str.at(100));
43+
assertSame("s", str.at(-1));
44+
assertSame("j", str.at(-2));
45+
46+
//general
47+
assertSame(1, String.prototype.at.length);
48+
}
49+
50+
function typedArrayPrototypeTestIntl(constr) {
51+
var arr = new constr([0,1,2,3,4,5]);
52+
53+
//in bound
54+
assertSame(0, arr.at(0));
55+
assertSame(1, arr.at(1));
56+
assertSame(5, arr.at(5));
57+
58+
//out of bound
59+
assertSame(undefined, arr.at(100));
60+
assertSame(5, arr.at(-1));
61+
assertSame(4, arr.at(-2));
62+
63+
//general
64+
assertSame(1, constr.prototype.at.length);
65+
}
66+
67+
function typedArrayPrototypeTest() {
68+
var types = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array,
69+
Uint32Array, Float32Array, Float64Array];
70+
types.forEach( constr => {
71+
typedArrayPrototypeTestIntl(constr);
72+
});
73+
}
74+
75+
arrayPrototypeTest();
76+
stringPrototypeTest();
77+
typedArrayPrototypeTest();
78+
79+
true;

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/builtins/ArrayPrototypeBuiltins.java

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import com.oracle.truffle.api.profiles.ValueProfile;
7878
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.DeleteAndSetLengthNodeGen;
7979
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.FlattenIntoArrayNodeGen;
80+
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayAtNodeGen;
8081
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayConcatNodeGen;
8182
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayCopyWithinNodeGen;
8283
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayEveryNodeGen;
@@ -209,8 +210,6 @@ public enum ArrayPrototype implements BuiltinEnum<ArrayPrototype> {
209210
splice(2),
210211
every(1),
211212
filter(1),
212-
flat(0),
213-
flatMap(1),
214213
forEach(1),
215214
some(1),
216215
map(1),
@@ -219,7 +218,7 @@ public enum ArrayPrototype implements BuiltinEnum<ArrayPrototype> {
219218
reduceRight(1),
220219
reverse(0),
221220

222-
// ES6
221+
// ES6 / ES2015
223222
find(1),
224223
findIndex(1),
225224
fill(1),
@@ -228,8 +227,15 @@ public enum ArrayPrototype implements BuiltinEnum<ArrayPrototype> {
228227
values(0),
229228
entries(0),
230229

231-
// ES7
232-
includes(1);
230+
// ES2016
231+
includes(1),
232+
233+
// ES2019
234+
flat(0),
235+
flatMap(1),
236+
237+
// ES2022
238+
at(1);
233239

234240
private final int length;
235241

@@ -245,11 +251,13 @@ public int getLength() {
245251
@Override
246252
public int getECMAScriptVersion() {
247253
if (EnumSet.of(find, findIndex, fill, copyWithin, keys, values, entries).contains(this)) {
248-
return 6;
254+
return JSConfig.ECMAScript2015;
249255
} else if (this == includes) {
250-
return 7;
256+
return JSConfig.ECMAScript2016;
251257
} else if (EnumSet.of(flat, flatMap).contains(this)) {
252-
return 10;
258+
return JSConfig.ECMAScript2019;
259+
} else if (this == at) {
260+
return JSConfig.ECMAScript2022;
253261
}
254262
return BuiltinEnum.super.getECMAScriptVersion();
255263
}
@@ -323,6 +331,9 @@ protected Object createNode(JSContext context, JSBuiltin builtin, boolean constr
323331
return JSArrayFlatMapNodeGen.create(context, builtin, args().withThis().fixedArgs(2).createArgumentNodes(context));
324332
case flat:
325333
return JSArrayFlatNodeGen.create(context, builtin, args().withThis().fixedArgs(3).createArgumentNodes(context));
334+
335+
case at:
336+
return JSArrayAtNodeGen.create(context, builtin, false, args().withThis().fixedArgs(1).createArgumentNodes(context));
326337
}
327338
return null;
328339
}
@@ -3197,4 +3208,29 @@ protected DynamicObject doNotJSObject(VirtualFrame frame, Object thisObj,
31973208
}
31983209
}
31993210

3211+
public abstract static class JSArrayAtNode extends JSArrayOperationWithToInt {
3212+
public JSArrayAtNode(JSContext context, JSBuiltin builtin, boolean isTypedArrayImplementation) {
3213+
super(context, builtin, isTypedArrayImplementation);
3214+
}
3215+
3216+
@Specialization
3217+
protected Object at(Object thisObj, Object index) {
3218+
final Object o = toObject(thisObj);
3219+
if (isTypedArrayImplementation) {
3220+
validateTypedArray(o);
3221+
}
3222+
final long length = getLength(o);
3223+
long relativeIndex = toIntegerAsLong(index);
3224+
long k;
3225+
if (relativeIndex >= 0) {
3226+
k = relativeIndex;
3227+
} else {
3228+
k = length + relativeIndex;
3229+
}
3230+
if (k < 0 || k >= length) {
3231+
return Undefined.instance;
3232+
}
3233+
return read(o, k);
3234+
}
3235+
}
32003236
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/builtins/StringPrototypeBuiltins.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import com.oracle.truffle.js.builtins.RegExpPrototypeBuiltinsFactory.JSRegExpExecES5NodeGen;
6767
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.CreateHTMLNodeGen;
6868
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.CreateStringIteratorNodeGen;
69+
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringAtNodeGen;
6970
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringCharAtNodeGen;
7071
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringCharCodeAtNodeGen;
7172
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringCodePointAtNodeGen;
@@ -193,7 +194,7 @@ public enum StringPrototype implements BuiltinEnum<StringPrototype> {
193194
sub(0),
194195
sup(0),
195196

196-
// ES6
197+
// ES6/ES2015
197198
startsWith(1),
198199
endsWith(1),
199200
includes(1),
@@ -202,13 +203,18 @@ public enum StringPrototype implements BuiltinEnum<StringPrototype> {
202203
_iterator(0),
203204
normalize(0),
204205

205-
// ES8
206+
// ES2017
206207
padStart(1),
207208
padEnd(1),
208209

209210
// ES2020
210211
matchAll(1),
211-
replaceAll(2);
212+
213+
// ES2021
214+
replaceAll(2),
215+
216+
// ES2022
217+
at(1);
212218

213219
private final int length;
214220

@@ -236,6 +242,8 @@ public int getECMAScriptVersion() {
236242
return JSConfig.ECMAScript2020;
237243
} else if (replaceAll == this) {
238244
return JSConfig.ECMAScript2021;
245+
} else if (at == this) {
246+
return JSConfig.ECMAScript2022;
239247
}
240248
return BuiltinEnum.super.getECMAScriptVersion();
241249
}
@@ -364,6 +372,9 @@ protected Object createNode(JSContext context, JSBuiltin builtin, boolean constr
364372
return createHTMLNode(context, builtin, "sub", "");
365373
case sup:
366374
return createHTMLNode(context, builtin, "sup", "");
375+
376+
case at:
377+
return JSStringAtNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
367378
}
368379
return null;
369380
}
@@ -2862,4 +2873,22 @@ private String wrapInTagWithAttribute(String string, String attrVal) {
28622873
return "<" + tag + " " + attribute + "=\"" + escapedVal + "\"" + ">" + string + "</" + tag + ">";
28632874
}
28642875
}
2876+
2877+
public abstract static class JSStringAtNode extends JSStringOperation {
2878+
public JSStringAtNode(JSContext context, JSBuiltin builtin) {
2879+
super(context, builtin);
2880+
}
2881+
2882+
@Specialization
2883+
protected Object at(Object thisObj, Object index) {
2884+
requireObjectCoercible(thisObj);
2885+
String thisStr = toString(thisObj);
2886+
int relativeIndex = toIntegerAsInt(index);
2887+
int k = (relativeIndex >= 0) ? relativeIndex : thisStr.length() + relativeIndex;
2888+
if (k < 0 || k >= thisStr.length()) {
2889+
return Undefined.instance;
2890+
}
2891+
return String.valueOf(thisStr.charAt(k));
2892+
}
2893+
}
28652894
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/builtins/TypedArrayPrototypeBuiltins.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltins.CreateArrayIteratorNode;
6262
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltins.JSArrayOperation;
6363
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltins.JSArrayOperationWithToInt;
64+
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayAtNodeGen;
6465
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayCopyWithinNodeGen;
6566
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayEveryNodeGen;
6667
import com.oracle.truffle.js.builtins.ArrayPrototypeBuiltinsFactory.JSArrayFillNodeGen;
@@ -95,6 +96,7 @@
9596
import com.oracle.truffle.js.runtime.BigInt;
9697
import com.oracle.truffle.js.runtime.Boundaries;
9798
import com.oracle.truffle.js.runtime.Errors;
99+
import com.oracle.truffle.js.runtime.JSConfig;
98100
import com.oracle.truffle.js.runtime.JSContext;
99101
import com.oracle.truffle.js.runtime.JSRuntime;
100102
import com.oracle.truffle.js.runtime.array.ScriptArray;
@@ -145,8 +147,11 @@ public enum TypedArrayPrototype implements BuiltinEnum<TypedArrayPrototype> {
145147
values(0),
146148
entries(0),
147149

148-
// ES7
149-
includes(1);
150+
// ES2016
151+
includes(1),
152+
153+
// ES2022
154+
at(1);
150155

151156
private final int length;
152157

@@ -162,7 +167,9 @@ public int getLength() {
162167
@Override
163168
public int getECMAScriptVersion() {
164169
if (this == includes) {
165-
return 7;
170+
return JSConfig.ECMAScript2016;
171+
} else if (this == at) {
172+
return JSConfig.ECMAScript2022;
166173
}
167174
return BuiltinEnum.super.getECMAScriptVersion();
168175
}
@@ -221,6 +228,8 @@ protected Object createNode(JSContext context, JSBuiltin builtin, boolean constr
221228

222229
case includes:
223230
return JSArrayIncludesNodeGen.create(context, builtin, true, args().withThis().fixedArgs(2).createArgumentNodes(context));
231+
case at:
232+
return JSArrayAtNodeGen.create(context, builtin, true, args().withThis().fixedArgs(1).createArgumentNodes(context));
224233
}
225234
return null;
226235
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/runtime/builtins/JSArray.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ public DynamicObject createPrototype(JSRealm realm, DynamicObject ctor) {
219219

220220
private static List<String> unscopableNameList(JSContext context) {
221221
List<String> names = new ArrayList<>();
222+
if (context.getEcmaScriptVersion() >= JSConfig.ECMAScript2022) {
223+
names.add("at");
224+
}
222225
names.add("copyWithin");
223226
names.add(JSArray.ENTRIES);
224227
names.add("fill");

0 commit comments

Comments
 (0)