Skip to content

Commit 97934f7

Browse files
authored
Implement Java 14 switch expressions (#9981)
Java switch expressions are implemented in JS as a function wrapped around a switch block, with `return` used for `yield`, and multi-valued `case` statements split into multiple entries. Most other optimizations that work on a switch statement at this time will not apply to switch expressions, these can be re-added at a later time, but other optimizations that have some specialization for switch statements have been tested or updated to handle switch expressions. JEP https://openjdk.org/jeps/361 Fixes #9878
1 parent a53bb51 commit 97934f7

File tree

20 files changed

+628
-130
lines changed

20 files changed

+628
-130
lines changed

dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
import com.google.gwt.dev.jjs.impl.RewriteConstructorCallsForUnboxedTypes;
120120
import com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer;
121121
import com.google.gwt.dev.jjs.impl.SourceInfoCorrelator;
122+
import com.google.gwt.dev.jjs.impl.SplitCaseStatementValues;
122123
import com.google.gwt.dev.jjs.impl.TypeCoercionNormalizer;
123124
import com.google.gwt.dev.jjs.impl.TypeReferencesRecorder;
124125
import com.google.gwt.dev.jjs.impl.TypeTightener;
@@ -493,6 +494,7 @@ protected TypeMapper<?> normalizeSemantics() {
493494
LongCastNormalizer.exec(jprogram);
494495
LongEmulationNormalizer.exec(jprogram);
495496
TypeCoercionNormalizer.exec(jprogram);
497+
SplitCaseStatementValues.exec(jprogram);
496498

497499
if (options.isIncrementalCompileEnabled()) {
498500
// Per file compilation reuses type JS even as references (like casts) in other files

dev/core/src/com/google/gwt/dev/jjs/ast/JCaseStatement.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,59 @@
1717

1818
import com.google.gwt.dev.jjs.SourceInfo;
1919

20+
import java.util.ArrayList;
21+
import java.util.Collection;
22+
import java.util.Collections;
23+
import java.util.List;
24+
2025
/**
2126
* Java case statement.
2227
*/
2328
public class JCaseStatement extends JStatement {
2429

25-
private JExpression expr;
30+
private List<JExpression> exprs;
2631

2732
public JCaseStatement(SourceInfo info, JExpression expr) {
2833
super(info);
29-
this.expr = expr;
34+
this.exprs = Collections.singletonList(expr);
35+
}
36+
37+
public JCaseStatement(SourceInfo info, Collection<JExpression> exprs) {
38+
super(info);
39+
this.exprs = Collections.unmodifiableList(new ArrayList<>(exprs));
40+
}
41+
42+
public boolean isDefault() {
43+
return exprs.isEmpty();
3044
}
3145

32-
public JExpression getExpr() {
33-
return expr;
46+
public List<JExpression> getExprs() {
47+
return exprs;
48+
}
49+
50+
public JBinaryOperation convertToCompareExpression(JExpression value) {
51+
if (isDefault()) {
52+
throw new IllegalStateException("Can't replace a default statement with a comparison");
53+
}
54+
JBinaryOperation compareOperation = null;
55+
for (JExpression expr : getExprs()) {
56+
JBinaryOperation caseComparison = new JBinaryOperation(getSourceInfo(),
57+
JPrimitiveType.BOOLEAN, JBinaryOperator.EQ, value, expr);
58+
if (compareOperation == null) {
59+
compareOperation = caseComparison;
60+
} else {
61+
compareOperation = new JBinaryOperation(getSourceInfo(), JPrimitiveType.BOOLEAN,
62+
JBinaryOperator.OR, compareOperation, caseComparison);
63+
}
64+
}
65+
assert compareOperation != null : this;
66+
return compareOperation;
3467
}
3568

3669
@Override
3770
public void traverse(JVisitor visitor, Context ctx) {
3871
if (visitor.visit(this, ctx)) {
39-
if (expr != null) {
40-
expr = visitor.accept(expr);
41-
}
72+
exprs = Collections.unmodifiableList(visitor.acceptImmutable(exprs));
4273
}
4374
visitor.endVisit(this, ctx);
4475
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2024 GWT Project Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.google.gwt.dev.jjs.ast;
17+
18+
import com.google.gwt.dev.jjs.SourceInfo;
19+
20+
/**
21+
* Java switch statement/expression.
22+
*/
23+
public class JSwitchExpression extends JExpression {
24+
private JBlock body;
25+
private JExpression expr;
26+
private JType type;
27+
28+
public JSwitchExpression(SourceInfo info, JExpression expr, JBlock body, JType type) {
29+
super(info);
30+
this.expr = expr;
31+
this.body = body;
32+
this.type = type;
33+
}
34+
35+
public JBlock getBody() {
36+
return body;
37+
}
38+
39+
public JExpression getExpr() {
40+
return expr;
41+
}
42+
43+
@Override
44+
public boolean hasSideEffects() {
45+
return true;
46+
}
47+
48+
@Override
49+
public void traverse(JVisitor visitor, Context ctx) {
50+
if (visitor.visit(this, ctx)) {
51+
expr = visitor.accept(expr);
52+
body = (JBlock) visitor.accept(body);
53+
}
54+
visitor.endVisit(this, ctx);
55+
}
56+
57+
@Override
58+
public JType getType() {
59+
return type;
60+
}
61+
}

dev/core/src/com/google/gwt/dev/jjs/ast/JSwitchStatement.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,34 @@
1818
import com.google.gwt.dev.jjs.SourceInfo;
1919

2020
/**
21-
* Java switch statement.
21+
* Wrapper to represent a Java switch expression as a JStatement.
2222
*/
2323
public class JSwitchStatement extends JStatement {
2424

25-
private final JBlock body;
26-
private JExpression expr;
25+
private final JSwitchExpression expr;
2726

28-
public JSwitchStatement(SourceInfo info, JExpression expr, JBlock body) {
29-
super(info);
27+
public JSwitchStatement(SourceInfo info, JExpression expr, JBlock block) {
28+
this(new JSwitchExpression(info, expr, block, JPrimitiveType.VOID));
29+
}
30+
31+
public JSwitchStatement(JSwitchExpression expr) {
32+
super(expr.getSourceInfo());
3033
this.expr = expr;
31-
this.body = body;
3234
}
3335

3436
public JBlock getBody() {
35-
return body;
37+
return expr.getBody();
3638
}
3739

3840
public JExpression getExpr() {
39-
return expr;
41+
return expr.getExpr();
4042
}
4143

4244
@Override
4345
public void traverse(JVisitor visitor, Context ctx) {
4446
if (visitor.visit(this, ctx)) {
45-
expr = visitor.accept(expr);
46-
visitor.accept(body);
47+
visitor.accept(expr);
4748
}
4849
visitor.endVisit(this, ctx);
4950
}
50-
5151
}

dev/core/src/com/google/gwt/dev/jjs/ast/JTransformer.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ public T transformStringLiteral(JStringLiteral x) {
304304
return transformValueLiteral(x);
305305
}
306306

307+
public T transformSwitchExpression(JSwitchExpression x) {
308+
return transformExpression(x);
309+
}
310+
307311
public T transformSwitchStatement(JSwitchStatement x) {
308312
return transformStatement(x);
309313
}
@@ -348,6 +352,10 @@ public T transformWhileStatement(JWhileStatement x) {
348352
return transformStatement(x);
349353
}
350354

355+
public T transformYieldStatement(JYieldStatement x) {
356+
return transformStatement(x);
357+
}
358+
351359
private class JRewriterVisitor extends JVisitor {
352360
T result = null;
353361

@@ -556,6 +564,10 @@ public final void endVisit(JStatement x, Context ctx) {
556564
public final void endVisit(JStringLiteral x, Context ctx) {
557565
}
558566

567+
@Override
568+
public void endVisit(JSwitchExpression x, Context ctx) {
569+
}
570+
559571
public final void endVisit(JSwitchStatement x, Context ctx) {
560572
}
561573

@@ -574,6 +586,10 @@ public final void endVisit(JType x, Context ctx) {
574586
public final void endVisit(JUnaryOperation x, Context ctx) {
575587
}
576588

589+
@Override
590+
public void endVisit(JUnsafeTypeCoercion x, Context ctx) {
591+
}
592+
577593
public final void endVisit(JValueLiteral x, Context ctx) {
578594
}
579595

@@ -586,6 +602,10 @@ public final void endVisit(JVariableRef x, Context ctx) {
586602
public final void endVisit(JWhileStatement x, Context ctx) {
587603
}
588604

605+
@Override
606+
public void endVisit(JYieldStatement x, Context ctx) {
607+
}
608+
589609
public final boolean visit(JAbstractMethodBody x, Context ctx) {
590610
assert result == null;
591611
result = transformAbstractMethodBody(x);
@@ -992,6 +1012,12 @@ public final boolean visit(JStringLiteral x, Context ctx) {
9921012
return false;
9931013
}
9941014

1015+
public final boolean visit(JSwitchExpression x, Context ctx) {
1016+
assert result == null;
1017+
result = transformSwitchExpression(x);
1018+
return false;
1019+
}
1020+
9951021
public final boolean visit(JSwitchStatement x, Context ctx) {
9961022
assert result == null;
9971023
result = transformSwitchStatement(x);
@@ -1057,6 +1083,13 @@ public final boolean visit(JWhileStatement x, Context ctx) {
10571083
result = transformWhileStatement(x);
10581084
return false;
10591085
}
1086+
1087+
@Override
1088+
public boolean visit(JYieldStatement x, Context ctx) {
1089+
assert result == null;
1090+
result = transformYieldStatement(x);
1091+
return false;
1092+
}
10601093
}
10611094

10621095
public final <T> T transform(JNode node) {

dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@ public void endVisit(JStringLiteral x, Context ctx) {
453453
endVisit((JValueLiteral) x, ctx);
454454
}
455455

456+
public void endVisit(JSwitchExpression x, Context ctx) {
457+
endVisit((JExpression) x, ctx);
458+
}
459+
456460
public void endVisit(JSwitchStatement x, Context ctx) {
457461
endVisit((JStatement) x, ctx);
458462
}
@@ -497,6 +501,10 @@ public void endVisit(JWhileStatement x, Context ctx) {
497501
endVisit((JStatement) x, ctx);
498502
}
499503

504+
public void endVisit(JYieldStatement x, Context ctx) {
505+
endVisit((JStatement) x, ctx);
506+
}
507+
500508
public boolean visit(JAbstractMethodBody x, Context ctx) {
501509
return visit((JNode) x, ctx);
502510
}
@@ -773,6 +781,10 @@ public boolean visit(JStringLiteral x, Context ctx) {
773781
return visit((JValueLiteral) x, ctx);
774782
}
775783

784+
public boolean visit(JSwitchExpression x, Context ctx) {
785+
return visit((JExpression) x, ctx);
786+
}
787+
776788
public boolean visit(JSwitchStatement x, Context ctx) {
777789
return visit((JStatement) x, ctx);
778790
}
@@ -817,4 +829,7 @@ public boolean visit(JWhileStatement x, Context ctx) {
817829
return visit((JStatement) x, ctx);
818830
}
819831

832+
public boolean visit(JYieldStatement x, Context ctx) {
833+
return visit((JStatement) x, ctx);
834+
}
820835
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2024 GWT Project Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.google.gwt.dev.jjs.ast;
17+
18+
import com.google.gwt.dev.jjs.SourceInfo;
19+
20+
/**
21+
* Java yield statement for switch statement/expressions.
22+
*/
23+
public class JYieldStatement extends JStatement {
24+
25+
private JExpression expr;
26+
27+
public JYieldStatement(SourceInfo info, JExpression expr) {
28+
super(info);
29+
this.expr = expr;
30+
}
31+
32+
public JExpression getExpr() {
33+
return expr;
34+
}
35+
36+
@Override
37+
public void traverse(JVisitor visitor, Context ctx) {
38+
if (visitor.visit(this, ctx)) {
39+
if (expr != null) {
40+
expr = visitor.accept(expr);
41+
}
42+
}
43+
visitor.endVisit(this, ctx);
44+
}
45+
46+
@Override
47+
public boolean unconditionalControlBreak() {
48+
return true;
49+
}
50+
}

dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
4747
import com.google.gwt.dev.jjs.ast.JRunAsync;
4848
import com.google.gwt.dev.jjs.ast.JStringLiteral;
49+
import com.google.gwt.dev.jjs.ast.JSwitchExpression;
4950
import com.google.gwt.dev.jjs.ast.JThisRef;
5051
import com.google.gwt.dev.jjs.ast.JUnsafeTypeCoercion;
5152
import com.google.gwt.dev.jjs.ast.JVisitor;
@@ -255,6 +256,11 @@ public void endVisit(JPermutationDependentValue x, Context ctx) {
255256
"this point but contains " + x);
256257
}
257258

259+
@Override
260+
public boolean visit(JSwitchExpression x, Context ctx) {
261+
throw new UnsupportedOperationException("switch expression cannot be cloned");
262+
}
263+
258264
@Override
259265
public boolean visit(JPostfixOperation x, Context ctx) {
260266
expression = new JPostfixOperation(x.getSourceInfo(), x.getOp(), cloneExpression(x.getArg()));

0 commit comments

Comments
 (0)