Skip to content

Commit 540b35d

Browse files
committed
[GR-44464] Implement Well-Formed Unicode Strings proposal.
PullRequest: js/2738
2 parents 95767f4 + 99ab54a commit 540b35d

File tree

4 files changed

+89
-3
lines changed

4 files changed

+89
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ See [release calendar](https://www.graalvm.org/release-calendar/) for release da
99
* Implemented the [WebAssembly threads](https://github.com/WebAssembly/threads) proposal.
1010
* Implemented the [Promise.withResolvers](https://github.com/tc39/proposal-promise-with-resolvers) proposal. It is available in ECMAScript staging mode (`--js.ecmascript-version=staging`).
1111
* Implementation of [Async Iterator Helpers](https://github.com/tc39/proposal-async-iterator-helpers) proposal (that was split out from Iterator Helpers proposal) was moved behind the experimental option `--js.async-iterator-helpers`.
12+
* Implemented the [Well-Formed Unicode Strings](https://github.com/tc39/proposal-is-usv-string) proposal. It is available in ECMAScript staging mode (`--js.ecmascript-version=staging`).
1213

1314
## Version 23.1.0
1415
* NOTE: GraalVM no longer ships with a "js" ScriptEngine. Please either use the Maven dependency or explicitly put `js-scriptengine.jar` on the module path. See [ScriptEngine documentation](docs/user/ScriptEngine.md) for details.

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,10 @@ public class Test262Runnable extends TestRunnable {
149149
"String.prototype.at",
150150
"String.prototype.endsWith",
151151
"String.prototype.includes",
152+
"String.prototype.isWellFormed",
152153
"String.prototype.matchAll",
153154
"String.prototype.replaceAll",
155+
"String.prototype.toWellFormed",
154156
"String.prototype.trimEnd",
155157
"String.prototype.trimStart",
156158
"Symbol",
@@ -256,8 +258,6 @@ public class Test262Runnable extends TestRunnable {
256258
"Array.fromAsync",
257259
"Intl.DurationFormat",
258260
"IsHTMLDDA",
259-
"String.prototype.isWellFormed",
260-
"String.prototype.toWellFormed",
261261
"arbitrary-module-namespace-names",
262262
"resizable-arraybuffer",
263263
"tail-call-optimization",
@@ -267,6 +267,8 @@ public class Test262Runnable extends TestRunnable {
267267
"FinalizationRegistry.prototype.cleanupSome",
268268
"Intl.Locale-info",
269269
"ShadowRealm",
270+
"String.prototype.isWellFormed",
271+
"String.prototype.toWellFormed",
270272
"array-grouping",
271273
"arraybuffer-transfer",
272274
"decorators",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2023, 2023, 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 String.prototype.isWellFormed() and String.prototype.toWellFormed().
9+
*
10+
* @option ecmascript-version=staging
11+
*/
12+
load("assert.js");
13+
14+
assertTrue("\u0000".isWellFormed());
15+
assertTrue("\ud000".isWellFormed());
16+
assertTrue("\ue000".isWellFormed());
17+
assertTrue("\uffff".isWellFormed());
18+
19+
assertFalse("\ud800".isWellFormed());
20+
assertFalse("\udbff".isWellFormed());
21+
assertFalse("\udc00".isWellFormed());
22+
assertFalse("\udfff".isWellFormed());
23+
24+
assertSame("\ufffd", "\ud800".toWellFormed());
25+
assertSame("\ufffd", "\udbff".toWellFormed());
26+
assertSame("\ufffd", "\udc00".toWellFormed());
27+
assertSame("\ufffd", "\udfff".toWellFormed());
28+
29+
assertTrue("\ud83d\ude00".isWellFormed());
30+
assertSame("\ud83d\ude00", "\ud83d\ude00".toWellFormed());

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringEndsWithNodeGen;
8484
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringIncludesNodeGen;
8585
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringIndexOfNodeGen;
86+
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringIsWellFormedNodeGen;
8687
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringLastIndexOfNodeGen;
8788
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringLocaleCompareIntlNodeGen;
8889
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringLocaleCompareNodeGen;
@@ -106,6 +107,7 @@
106107
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringToLowerCaseNodeGen;
107108
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringToStringNodeGen;
108109
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringToUpperCaseNodeGen;
110+
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringToWellFormedNodeGen;
109111
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringTrimLeftNodeGen;
110112
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringTrimNodeGen;
111113
import com.oracle.truffle.js.builtins.StringPrototypeBuiltinsFactory.JSStringTrimRightNodeGen;
@@ -235,7 +237,11 @@ public enum StringPrototype implements BuiltinEnum<StringPrototype> {
235237
replaceAll(2),
236238

237239
// ES2022
238-
at(1);
240+
at(1),
241+
242+
// ES2024
243+
isWellFormed(0),
244+
toWellFormed(0);
239245

240246
private final int length;
241247

@@ -265,6 +271,8 @@ public int getECMAScriptVersion() {
265271
return JSConfig.ECMAScript2021;
266272
} else if (at == this) {
267273
return JSConfig.ECMAScript2022;
274+
} else if (EnumSet.of(isWellFormed, toWellFormed).contains(this)) {
275+
return JSConfig.ECMAScript2024;
268276
}
269277
return BuiltinEnum.super.getECMAScriptVersion();
270278
}
@@ -396,6 +404,11 @@ protected Object createNode(JSContext context, JSBuiltin builtin, boolean constr
396404

397405
case at:
398406
return JSStringAtNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
407+
408+
case isWellFormed:
409+
return JSStringIsWellFormedNodeGen.create(context, builtin, args().withThis().createArgumentNodes(context));
410+
case toWellFormed:
411+
return JSStringToWellFormedNodeGen.create(context, builtin, args().withThis().createArgumentNodes(context));
399412
}
400413
return null;
401414
}
@@ -2971,6 +2984,46 @@ protected Object pad(Object thisObj, Object[] args,
29712984

29722985
}
29732986

2987+
public abstract static class JSStringIsWellFormedNode extends JSStringOperation {
2988+
2989+
public JSStringIsWellFormedNode(JSContext context, JSBuiltin builtin) {
2990+
super(context, builtin);
2991+
}
2992+
2993+
@Specialization
2994+
protected static Object doString(TruffleString thisStr,
2995+
@Shared @Cached TruffleString.IsValidNode isValidNode) {
2996+
return isValidNode.execute(thisStr, TruffleString.Encoding.UTF_16);
2997+
}
2998+
2999+
@Specialization(guards = "!isString(thisObj)")
3000+
protected final Object doOther(Object thisObj,
3001+
@Shared @Cached TruffleString.IsValidNode isValidNode) {
3002+
requireObjectCoercible(thisObj);
3003+
return doString(toString(thisObj), isValidNode);
3004+
}
3005+
}
3006+
3007+
public abstract static class JSStringToWellFormedNode extends JSStringOperation {
3008+
3009+
public JSStringToWellFormedNode(JSContext context, JSBuiltin builtin) {
3010+
super(context, builtin);
3011+
}
3012+
3013+
@Specialization
3014+
protected static TruffleString doString(TruffleString thisStr,
3015+
@Shared @Cached TruffleString.ToValidStringNode toValidNode) {
3016+
return toValidNode.execute(thisStr, TruffleString.Encoding.UTF_16);
3017+
}
3018+
3019+
@Specialization(guards = "!isString(thisObj)")
3020+
protected final TruffleString doOther(Object thisObj,
3021+
@Shared @Cached TruffleString.ToValidStringNode toValidNode) {
3022+
requireObjectCoercible(thisObj);
3023+
return doString(toString(thisObj), toValidNode);
3024+
}
3025+
}
3026+
29743027
/**
29753028
* Implementation of the CreateRegExpStringIterator abstract operation as specified by the
29763029
* String.prototype.matchAll draft proposal.

0 commit comments

Comments
 (0)