Skip to content

Commit 04396e6

Browse files
committed
[GR-24939] Do not unwrap OptionalChainNodes that are not part of a larger optional chain.
PullRequest: js/1591
2 parents 8f6edf5 + f76e262 commit 04396e6

File tree

4 files changed

+89
-4
lines changed

4 files changed

+89
-4
lines changed

graal-js/src/com.oracle.truffle.js.parser/src/com/oracle/truffle/js/parser/GraalJSTranslator.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2162,7 +2162,14 @@ private JavaScriptNode enterDeleteProperty(UnaryNode deleteNode) {
21622162
}
21632163

21642164
private JavaScriptNode filterOptionalChainTarget(JavaScriptNode target, boolean optional) {
2165-
JavaScriptNode innerAccess = target instanceof OptionalChainNode ? ((OptionalChainNode) target).getAccessNode() : target;
2165+
JavaScriptNode innerAccess;
2166+
if (target instanceof OptionalChainNode) {
2167+
innerAccess = ((OptionalChainNode) target).getAccessNode();
2168+
} else if (target instanceof OptionalChainNode.OptionalTargetableNode) {
2169+
innerAccess = ((OptionalChainNode.OptionalTargetableNode) target).getDelegateNode();
2170+
} else {
2171+
innerAccess = target;
2172+
}
21662173
if (optional) {
21672174
innerAccess = factory.createOptionalChainShortCircuit(innerAccess);
21682175
}
@@ -2193,7 +2200,9 @@ private JavaScriptNode enterNewNode(UnaryNode unaryNode) {
21932200
public JavaScriptNode enterCallNode(CallNode callNode) {
21942201
JavaScriptNode function = transform(callNode.getFunction());
21952202
JavaScriptNode[] args = transformArgs(callNode.getArgs());
2196-
function = filterOptionalChainTarget(function, callNode.isOptional());
2203+
if (callNode.isOptionalChain()) {
2204+
function = filterOptionalChainTarget(function, callNode.isOptional());
2205+
}
21972206
JavaScriptNode call;
21982207
if (callNode.isEval() && args.length >= 1) {
21992208
call = createCallEvalNode(function, args);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
load('assert.js');
9+
10+
var argumentsEvaluated = false;
11+
var f = function () {
12+
argumentsEvaluated = true;
13+
};
14+
assertThrows(function () {
15+
(undefined?.foo)(f());
16+
}, TypeError);
17+
assertSame(true, argumentsEvaluated);
18+
19+
true;

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/nodes/NodeFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ public JavaScriptNode createTryFinally(JavaScriptNode tryNode, JavaScriptNode fi
585585

586586
public JavaScriptNode createFunctionCall(@SuppressWarnings("unused") JSContext context, JavaScriptNode expression, JavaScriptNode[] arguments) {
587587
if (expression instanceof PropertyNode || expression instanceof ReadElementNode || expression instanceof WithVarWrapperNode || expression instanceof PrivateFieldGetNode ||
588-
expression instanceof OptionalChainNode.ShortCircuitTargetableNode) {
588+
expression instanceof OptionalChainNode.ShortCircuitTargetableNode || expression instanceof OptionalChainNode.OptionalTargetableNode) {
589589
assert !(expression instanceof PropertyNode) || ((PropertyNode) expression).isMethod();
590590
return JSFunctionCallNode.createInvoke((JSTargetableNode) expression, arguments, false, false);
591591
} else if (expression instanceof JSTargetableWrapperNode) {

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

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ protected OptionalChainNode(JavaScriptNode chainNode, Object result) {
6666
}
6767

6868
public static JavaScriptNode createTarget(JavaScriptNode chainNode) {
69-
return new OptionalChainNode(chainNode, chainNode instanceof DeletePropertyNode ? Boolean.TRUE : Undefined.instance);
69+
Object result = (chainNode instanceof DeletePropertyNode) ? Boolean.TRUE : Undefined.instance;
70+
if (chainNode instanceof JSTargetableNode) {
71+
return new OptionalTargetableNode((JSTargetableNode) chainNode, result);
72+
} else {
73+
return new OptionalChainNode(chainNode, result);
74+
}
7075
}
7176

7277
public static JavaScriptNode createShortCircuit(JavaScriptNode expressionNode) {
@@ -120,6 +125,58 @@ public JavaScriptNode getAccessNode() {
120125
return accessNode;
121126
}
122127

128+
public static final class OptionalTargetableNode extends JSTargetableNode {
129+
private static final Object SHORT_CIRCUIT_MARKER = new Object();
130+
@Child private JSTargetableNode delegateNode;
131+
private final Object result;
132+
133+
protected OptionalTargetableNode(JSTargetableNode delegateNode, Object result) {
134+
this.delegateNode = delegateNode;
135+
this.result = result;
136+
}
137+
138+
@Override
139+
public JavaScriptNode getTarget() {
140+
return delegateNode.getTarget();
141+
}
142+
143+
@Override
144+
public Object evaluateTarget(VirtualFrame frame) {
145+
try {
146+
return delegateNode.evaluateTarget(frame);
147+
} catch (ShortCircuitException ex) {
148+
return SHORT_CIRCUIT_MARKER;
149+
}
150+
}
151+
152+
@Override
153+
public Object executeWithTarget(VirtualFrame frame, Object target) {
154+
if (target == SHORT_CIRCUIT_MARKER) {
155+
return result;
156+
}
157+
return delegateNode.executeWithTarget(frame, target);
158+
}
159+
160+
@Override
161+
public Object execute(VirtualFrame frame) {
162+
try {
163+
return delegateNode.execute(frame);
164+
} catch (ShortCircuitException ex) {
165+
return result;
166+
}
167+
}
168+
169+
@Override
170+
protected JavaScriptNode copyUninitialized(Set<Class<? extends Tag>> materializedTags) {
171+
return new OptionalTargetableNode(cloneUninitialized(delegateNode, materializedTags), result);
172+
}
173+
174+
public JavaScriptNode getDelegateNode() {
175+
return delegateNode;
176+
}
177+
178+
}
179+
123180
/**
124181
* Evaluates an optional expression and if its value is null or undefined, jumps out of the
125182
* short-circuiting expression to the parent {@link OptionalChainNode}.

0 commit comments

Comments
 (0)