Skip to content

Commit d07d39c

Browse files
committed
[GR-22161] Fix Proxy-related test262 failures.
PullRequest: js/1442
2 parents c31255c + 60dd1e5 commit d07d39c

File tree

17 files changed

+77
-88
lines changed

17 files changed

+77
-88
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ The main focus is on user-observable behavior of the engine.
2020
* Implemented the [FinalizationRegistry](https://github.com/tc39/proposal-weakrefs) proposal. It is available behind the `--js.ecmascript-version=2021` flag.
2121
* Enabled the [Hashbang Grammar](https://github.com/tc39/proposal-hashbang) proposal, by enabling the `--js.shebang` option by default in ECMAScript 2020 or later.
2222
* Updated ICU4J library to version 66.1.
23+
* Moved `String.prototype.replaceAll` to ECMAScript version 2021 (`--js.ecmascript-version=2021`).
2324

2425
## Version 20.0.0
2526
* Implemented support for public and private class fields, both instance and static (based on the [class fields](https://github.com/tc39/proposal-class-fields) and [static class features](https://github.com/tc39/proposal-static-class-features) proposals). This feature is available by default in Node.js and can be enabled using the experimental option `js.class-fields`.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ public class Test262Runnable extends TestRunnable {
218218
}));
219219
private static final Set<String> ES2021_FEATURES = new HashSet<>(Arrays.asList(new String[]{
220220
"FinalizationRegistry",
221+
"String.prototype.replaceAll",
221222
"WeakRef",
222223
"class-fields-private",
223224
"class-fields-public",
@@ -266,7 +267,7 @@ public void run() {
266267
Source[] harnessSources = ((Test262) suite).getHarnessSources(runStrict, asyncTest, getIncludes(scriptCodeList));
267268

268269
boolean supported = true;
269-
int featureVersion = JSConfig.LatestECMAScriptVersion;
270+
int featureVersion = JSConfig.CurrentECMAScriptVersion;
270271
for (String feature : features) {
271272
if (SUPPORTED_FEATURES.contains(feature)) {
272273
assert !UNSUPPORTED_FEATURES.contains(feature) : feature;

graal-js/src/com.oracle.truffle.js.test.external/src/com/oracle/truffle/js/test/external/testv8/TestV8Runnable.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public void run() {
111111
// ecma versions
112112
TestFile.EcmaVersion ecmaVersion = testFile.getEcmaVersion();
113113
if (ecmaVersion == null) {
114-
ecmaVersion = TestFile.EcmaVersion.forVersions(JSConfig.LatestECMAScriptVersion);
114+
ecmaVersion = TestFile.EcmaVersion.forVersions(JSConfig.CurrentECMAScriptVersion);
115115
}
116116

117117
if (flags.contains(HARMONY_PUBLIC_FIELDS) || flags.contains(HARMONY_PRIVATE_FIELDS)) {
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@
5050
import org.junit.Test;
5151

5252
import com.oracle.truffle.js.lang.JavaScriptLanguage;
53+
import com.oracle.truffle.js.runtime.JSContextOptions;
5354
import com.oracle.truffle.js.test.JSTest;
5455

5556
/**
5657
* String.prototype.* behavior not sufficiently tested by test262.
5758
*/
58-
public class StringPrototypeBuiltins {
59+
public class StringPrototypeTest {
5960

6061
private static boolean testIntl(String sourceText) {
6162
try (Context context = JSTest.newContextBuilder().build()) {
@@ -92,7 +93,7 @@ public void testToLocaleUpperCase() {
9293

9394
@Test
9495
public void testReplaceAllRedefinedFlags() {
95-
try (Context context = JSTest.newContextBuilder().build()) {
96+
try (Context context = JSTest.newContextBuilder().option(JSContextOptions.ECMASCRIPT_VERSION_NAME, String.valueOf(2021)).build()) {
9697
String code = "var re = /a/; Object.defineProperty(re, 'flags', {value: 'g'}); 'a'.replaceAll(re, 'b');";
9798
Value result = context.eval(JavaScriptLanguage.ID, code);
9899
assertTrue(result.isString());
@@ -102,7 +103,7 @@ public void testReplaceAllRedefinedFlags() {
102103

103104
@Test
104105
public void testReplaceAllCustomNonGlobalRegExp() {
105-
try (Context context = JSTest.newContextBuilder().build()) {
106+
try (Context context = JSTest.newContextBuilder().option(JSContextOptions.ECMASCRIPT_VERSION_NAME, String.valueOf(2021)).build()) {
106107
String code = "var searchValue = { [Symbol.match]: true, flags: '' }; var expectedError = false; try { 'a'.replaceAll(searchValue, 'b') } catch (e) { expectedError = e instanceof TypeError }";
107108
Value result = context.eval(JavaScriptLanguage.ID, code);
108109
assertTrue(result.isBoolean());
@@ -112,7 +113,7 @@ public void testReplaceAllCustomNonGlobalRegExp() {
112113

113114
@Test
114115
public void testReplaceAllCustomGlobalRegExp() {
115-
try (Context context = JSTest.newContextBuilder().build()) {
116+
try (Context context = JSTest.newContextBuilder().option(JSContextOptions.ECMASCRIPT_VERSION_NAME, String.valueOf(2021)).build()) {
116117
String code = "var searchValue = { [Symbol.match]: true, flags: 'g', [Symbol.replace]: () => 42 }; 'a'.replaceAll(searchValue, 'b');";
117118
Value result = context.eval(JavaScriptLanguage.ID, code);
118119
assertTrue(result.isNumber());
@@ -122,7 +123,7 @@ public void testReplaceAllCustomGlobalRegExp() {
122123

123124
@Test
124125
public void testReplaceAllReplaceValueToString() {
125-
try (Context context = JSTest.newContextBuilder().build()) {
126+
try (Context context = JSTest.newContextBuilder().option(JSContextOptions.ECMASCRIPT_VERSION_NAME, String.valueOf(2021)).build()) {
126127
String code = "var toStringCount = 0; var replaceValue = { toString() { toStringCount++; return 'b'; } }; 'aa'.replaceAll('a', replaceValue); toStringCount;";
127128
Value result = context.eval(JavaScriptLanguage.ID, code);
128129
assertTrue(result.isNumber());

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,20 +1958,13 @@ protected DynamicObject constructJSAdapter(Object proto, Object overrides, Objec
19581958

19591959
@ImportStatic(value = {JSProxy.class})
19601960
public abstract static class ConstructJSProxyNode extends ConstructWithNewTargetNode {
1961-
private final ConditionProfile revoked = ConditionProfile.createBinaryProfile();
19621961
private final ConditionProfile targetNonObject = ConditionProfile.createBinaryProfile();
19631962
private final ConditionProfile handlerNonObject = ConditionProfile.createBinaryProfile();
19641963

19651964
public ConstructJSProxyNode(JSContext context, JSBuiltin builtin, boolean isNewTargetCase) {
19661965
super(context, builtin, isNewTargetCase);
19671966
}
19681967

1969-
private void checkRevokedProxy(Object obj) {
1970-
if (JSProxy.isProxy(obj) && revoked.profile(JSProxy.isRevoked((DynamicObject) obj))) {
1971-
throw Errors.createTypeError("argument cannot be a revoked proxy");
1972-
}
1973-
}
1974-
19751968
@Specialization
19761969
protected DynamicObject constructJSProxy(DynamicObject newTarget, Object target, Object handler) {
19771970
if (targetNonObject.profile(!JSGuards.isTruffleObject(target) || target instanceof Symbol || target == Undefined.instance || target == Null.instance || target instanceof JSLazyString ||
@@ -1982,8 +1975,6 @@ protected DynamicObject constructJSProxy(DynamicObject newTarget, Object target,
19821975
throw Errors.createTypeError("handler expected to be an object");
19831976
}
19841977
DynamicObject handlerObj = (DynamicObject) handler;
1985-
checkRevokedProxy(target);
1986-
checkRevokedProxy(handlerObj);
19871978
return swapPrototype(JSProxy.create(getContext(), target, handlerObj), newTarget);
19881979
}
19891980

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,11 @@ public int getECMAScriptVersion() {
226226
if (EnumSet.range(startsWith, normalize).contains(this)) {
227227
return 6;
228228
} else if (EnumSet.range(padStart, padEnd).contains(this)) {
229-
return 8;
230-
} else if (EnumSet.range(matchAll, replaceAll).contains(this)) {
229+
return JSConfig.ECMAScript2017;
230+
} else if (matchAll == this) {
231231
return JSConfig.ECMAScript2020;
232+
} else if (replaceAll == this) {
233+
return JSConfig.ECMAScript2021;
232234
}
233235
return BuiltinEnum.super.getECMAScriptVersion();
234236
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/JSProxyCallNode.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.oracle.truffle.api.nodes.NodeCost;
4646
import com.oracle.truffle.api.nodes.NodeInfo;
4747
import com.oracle.truffle.api.object.DynamicObject;
48+
import com.oracle.truffle.api.profiles.BranchProfile;
4849
import com.oracle.truffle.api.profiles.ConditionProfile;
4950
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
5051
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
@@ -69,8 +70,8 @@ public abstract class JSProxyCallNode extends JavaScriptBaseNode {
6970
@Child private JSFunctionCallNode callTrapNode;
7071
protected final boolean isNew;
7172
protected final boolean isNewTarget;
72-
private final ConditionProfile callableProfile = ConditionProfile.createBinaryProfile();
7373
private final ConditionProfile pxTrapFunProfile = ConditionProfile.createBinaryProfile();
74+
private final BranchProfile errorBranch = BranchProfile.create();
7475

7576
protected JSProxyCallNode(JSContext context, boolean isNew, boolean isNewTarget) {
7677
this.callNode = isNewTarget ? JSFunctionCallNode.createNewTarget() : isNew ? JSFunctionCallNode.createNew() : JSFunctionCallNode.createCall();
@@ -97,10 +98,11 @@ protected Object doCall(Object[] arguments) {
9798
assert JSProxy.isProxy(function);
9899
DynamicObject proxy = (DynamicObject) function;
99100

100-
if (!callableProfile.profile(JSRuntime.isCallableProxy(proxy))) {
101+
if (!JSRuntime.isCallableProxy(proxy)) {
102+
errorBranch.enter();
101103
throw Errors.createTypeErrorNotAFunction(function, this);
102104
} else {
103-
DynamicObject pxHandler = JSProxy.getHandlerChecked(proxy);
105+
DynamicObject pxHandler = JSProxy.getHandlerChecked(proxy, errorBranch);
104106
Object pxTarget = JSProxy.getTarget(proxy);
105107
Object pxTrapFun = trapGetter.executeWithTarget(pxHandler);
106108
Object[] proxyArguments = JSArguments.extractUserArguments(arguments);
@@ -121,10 +123,11 @@ protected Object doConstruct(Object[] arguments) {
121123
assert JSProxy.isProxy(function);
122124
DynamicObject proxy = (DynamicObject) function;
123125

124-
if (!callableProfile.profile(JSRuntime.isConstructorProxy(proxy))) {
126+
if (!JSRuntime.isConstructorProxy(proxy)) {
127+
errorBranch.enter();
125128
throw Errors.createTypeErrorNotAFunction(function, this);
126129
} else {
127-
DynamicObject pxHandler = JSProxy.getHandlerChecked(proxy);
130+
DynamicObject pxHandler = JSProxy.getHandlerChecked(proxy, errorBranch);
128131
Object pxTarget = JSProxy.getTarget(proxy);
129132
Object pxTrapFun = trapGetter.executeWithTarget(pxHandler);
130133
Object newTarget = isNewTarget ? JSArguments.getNewTarget(arguments) : proxy;
@@ -133,12 +136,14 @@ protected Object doConstruct(Object[] arguments) {
133136
if (!JSObject.isJSObject(pxTarget)) {
134137
return JSInteropUtil.construct(pxTarget, constructorArguments);
135138
}
136-
return callNode.executeCall(isNewTarget ? JSArguments.createWithNewTarget(JSFunction.CONSTRUCT, pxTarget, newTarget, constructorArguments)
139+
return callNode.executeCall(isNewTarget
140+
? JSArguments.createWithNewTarget(JSFunction.CONSTRUCT, pxTarget, newTarget, constructorArguments)
137141
: JSArguments.create(JSFunction.CONSTRUCT, pxTarget, constructorArguments));
138142
}
139143
Object[] trapArgs = new Object[]{pxTarget, JSArray.createConstant(context, constructorArguments), newTarget};
140144
Object result = callTrapNode.executeCall(JSArguments.create(pxHandler, pxTrapFun, trapArgs));
141145
if (!JSRuntime.isObject(result)) {
146+
errorBranch.enter();
142147
throw Errors.createTypeErrorNotAnObject(result, this);
143148
}
144149
return result;

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/JSProxyHasPropertyNode.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.oracle.truffle.api.nodes.NodeCost;
4747
import com.oracle.truffle.api.nodes.NodeInfo;
4848
import com.oracle.truffle.api.object.DynamicObject;
49+
import com.oracle.truffle.api.profiles.BranchProfile;
4950
import com.oracle.truffle.api.profiles.ConditionProfile;
5051
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
5152
import com.oracle.truffle.js.nodes.cast.JSToBooleanNode;
@@ -67,6 +68,7 @@ public abstract class JSProxyHasPropertyNode extends JavaScriptBaseNode {
6768
@Child private JSFunctionCallNode callNode;
6869
@Child private JSToBooleanNode toBooleanNode;
6970
@Child private JSToPropertyKeyNode toPropertyKeyNode;
71+
private final BranchProfile errorBranch = BranchProfile.create();
7072

7173
public JSProxyHasPropertyNode(JSContext context) {
7274
this.callNode = JSFunctionCallNode.createCall();
@@ -81,19 +83,13 @@ public static JSProxyHasPropertyNode create(JSContext context) {
8183

8284
public abstract boolean executeWithTargetAndKeyBoolean(Object shared, Object key);
8385

84-
private void checkTrapResult(boolean accessible, boolean trapResult) {
85-
if (!accessible && !trapResult) {
86-
throw Errors.createTypeError("Proxy can't successfully access a non-writable, non-configurable property", this);
87-
}
88-
}
89-
9086
@Specialization
9187
protected boolean doGeneric(DynamicObject proxy, Object key,
9288
@Cached("createBinaryProfile()") ConditionProfile trapFunProfile) {
9389
assert JSProxy.isProxy(proxy);
9490
Object propertyKey = toPropertyKeyNode.execute(key);
91+
DynamicObject handler = JSProxy.getHandlerChecked(proxy, errorBranch);
9592
Object target = JSProxy.getTarget(proxy);
96-
DynamicObject handler = JSProxy.getHandler(proxy);
9793
Object trapFun = trapGetter.executeWithTarget(handler);
9894
if (trapFunProfile.profile(trapFun == Undefined.instance)) {
9995
if (JSObject.isJSObject(target)) {
@@ -104,8 +100,12 @@ protected boolean doGeneric(DynamicObject proxy, Object key,
104100
} else {
105101
Object callResult = callNode.executeCall(JSArguments.create(handler, trapFun, target, propertyKey));
106102
boolean trapResult = toBooleanNode.executeBoolean(callResult);
107-
boolean accessible = JSProxy.checkPropertyIsSettable(target, propertyKey);
108-
checkTrapResult(accessible, trapResult);
103+
if (!trapResult) {
104+
errorBranch.enter();
105+
if (!JSProxy.checkPropertyIsSettable(target, propertyKey)) {
106+
throw Errors.createTypeError("Proxy can't successfully access a non-writable, non-configurable property", this);
107+
}
108+
}
109109
return trapResult;
110110
}
111111
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/JSProxyPropertyGetNode.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ public abstract class JSProxyPropertyGetNode extends JavaScriptBaseNode {
6969

7070
@Child protected GetMethodNode trapGet;
7171
@Child private JSFunctionCallNode callNode;
72-
@Child private JSToPropertyKeyNode toPropertyKeyNode;
7372
@Child private JSGetOwnPropertyNode getOwnPropertyNode;
7473
@Child private JSIdenticalNode sameValueNode;
7574
private final BranchProfile errorBranch = BranchProfile.create();
@@ -87,12 +86,13 @@ public static JSProxyPropertyGetNode create(JSContext context) {
8786

8887
@Specialization
8988
protected Object doGeneric(DynamicObject proxy, Object receiver, Object key,
89+
@Cached JSToPropertyKeyNode toPropertyKeyNode,
9090
@Cached("createBinaryProfile()") ConditionProfile hasTrap,
9191
@Cached JSClassProfile targetClassProfile) {
9292
assert JSProxy.isProxy(proxy);
9393
assert !(key instanceof HiddenKey);
94-
Object propertyKey = toPropertyKey(key);
95-
DynamicObject handler = JSProxy.getHandler(proxy);
94+
Object propertyKey = toPropertyKeyNode.execute(key);
95+
DynamicObject handler = JSProxy.getHandlerChecked(proxy, errorBranch);
9696
Object target = JSProxy.getTarget(proxy);
9797
Object trapFun = trapGet.executeWithTarget(handler);
9898
if (hasTrap.profile(trapFun == Undefined.instance)) {
@@ -145,12 +145,4 @@ private PropertyDescriptor getOwnProperty(DynamicObject target, Object propertyK
145145
}
146146
return getOwnPropertyNode.execute(target, propertyKey);
147147
}
148-
149-
private Object toPropertyKey(Object key) {
150-
if (toPropertyKeyNode == null) {
151-
CompilerDirectives.transferToInterpreterAndInvalidate();
152-
toPropertyKeyNode = insert(JSToPropertyKeyNode.create());
153-
}
154-
return toPropertyKeyNode.execute(key);
155-
}
156148
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/access/JSProxyPropertySetNode.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.oracle.truffle.api.nodes.NodeInfo;
4949
import com.oracle.truffle.api.object.DynamicObject;
5050
import com.oracle.truffle.api.object.HiddenKey;
51+
import com.oracle.truffle.api.profiles.BranchProfile;
5152
import com.oracle.truffle.api.profiles.ConditionProfile;
5253
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
5354
import com.oracle.truffle.js.nodes.cast.JSToBooleanNode;
@@ -74,6 +75,7 @@ public abstract class JSProxyPropertySetNode extends JavaScriptBaseNode {
7475
@Child private JSToPropertyKeyNode toPropertyKeyNode;
7576
@Child private InteropLibrary interopNode;
7677
@Child private ExportValueNode exportValueNode;
78+
private final BranchProfile errorBranch = BranchProfile.create();
7779

7880
protected JSProxyPropertySetNode(JSContext context, boolean isStrict) {
7981
this.call = JSFunctionCallNode.createCall();
@@ -97,7 +99,7 @@ protected boolean doGeneric(DynamicObject proxy, Object receiver, Object value,
9799
assert JSProxy.isProxy(proxy);
98100
assert !(key instanceof HiddenKey);
99101
Object propertyKey = toPropertyKey(key);
100-
DynamicObject handler = JSProxy.getHandler(proxy);
102+
DynamicObject handler = JSProxy.getHandlerChecked(proxy, errorBranch);
101103
Object target = JSProxy.getTarget(proxy);
102104
Object trapFun = trapGet.executeWithTarget(handler);
103105
if (hasTrap.profile(trapFun == Undefined.instance)) {
@@ -111,6 +113,7 @@ protected boolean doGeneric(DynamicObject proxy, Object receiver, Object value,
111113
Object trapResult = call.executeCall(JSArguments.create(handler, trapFun, target, propertyKey, value, receiver));
112114
boolean booleanTrapResult = toBoolean.executeBoolean(trapResult);
113115
if (!booleanTrapResult) {
116+
errorBranch.enter();
114117
if (isStrict) {
115118
throw Errors.createTypeErrorTrapReturnedFalsish(JSProxy.SET, propertyKey);
116119
} else {

0 commit comments

Comments
 (0)