Skip to content

Commit 03dca03

Browse files
committed
8354908: javac mishandles supplementary character in character literal
Reviewed-by: naoto, vromero
1 parent 6e08469 commit 03dca03

File tree

4 files changed

+62
-2
lines changed

4 files changed

+62
-2
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,10 @@ private void scanLitChar(int pos, boolean isString) {
389389
break;
390390
}
391391
} else {
392+
if (!isString && !Character.isBmpCodePoint(getCodepoint())) {
393+
lexError(pos, Errors.IllegalCharLiteralMultipleSurrogates);
394+
}
395+
392396
putThenNext();
393397
}
394398
}

src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,9 @@ compiler.err.illegal.combination.of.modifiers=\
694694
compiler.err.illegal.enum.static.ref=\
695695
illegal reference to static field from initializer
696696

697+
compiler.err.illegal.char.literal.multiple.surrogates=\
698+
character literal contains more than one UTF-16 code unit
699+
697700
compiler.err.illegal.esc.char=\
698701
illegal escape character
699702

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
// key: compiler.err.illegal.char.literal.multiple.surrogates
25+
26+
class IllegalCharLiteralMultipleSurrogates {
27+
char c = '\uD83D\uDE0A';
28+
}

test/langtools/tools/javac/lexer/JavaLexerTest.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -30,8 +30,11 @@
3030
*/
3131

3232
import java.net.URI;
33+
import java.util.ArrayList;
34+
import java.util.List;
3335
import java.util.Objects;
3436

37+
import javax.tools.DiagnosticListener;
3538
import javax.tools.SimpleJavaFileObject;
3639

3740
import com.sun.tools.javac.parser.JavaTokenizer;
@@ -101,17 +104,29 @@ public class JavaLexerTest {
101104
new TestTuple(ERROR, "\'\'"),
102105
new TestTuple(ERROR, "\'\\q\'", "\'\\q\'"),
103106
new TestTuple(ERROR, "\'\\{1+2}\'", "\'\\{1+2}\'"),
107+
new TestTuple(ERROR, "'\uD83D\uDE0A'",
108+
List.of("compiler.err.illegal.char.literal.multiple.surrogates")),
104109
};
105110

106111
static class TestTuple {
107112
String input;
108113
TokenKind kind;
109114
String expected;
115+
List<String> expectedErrors;
110116

111-
TestTuple(TokenKind kind, String input, String expected) {
117+
TestTuple(TokenKind kind, String input, String expected, List<String> expectedErrors) {
112118
this.input = input;
113119
this.kind = kind;
114120
this.expected = expected;
121+
this.expectedErrors = expectedErrors;
122+
}
123+
124+
TestTuple(TokenKind kind, String input, List<String> expectedErrors) {
125+
this(kind, input, input, expectedErrors);
126+
}
127+
128+
TestTuple(TokenKind kind, String input, String expected) {
129+
this(kind, input, expected, null);
115130
}
116131

117132
TestTuple(TokenKind kind, String input) {
@@ -121,6 +136,12 @@ static class TestTuple {
121136

122137
void test(TestTuple test, boolean willFail) throws Exception {
123138
Context ctx = new Context();
139+
List<String> errors = new ArrayList();
140+
141+
if (test.expectedErrors != null) {
142+
ctx.put(DiagnosticListener.class, (DiagnosticListener) d -> errors.add(d.getCode()));
143+
}
144+
124145
Log log = Log.instance(ctx);
125146

126147
log.useSource(SimpleJavaFileObject.forSource(URI.create("mem://Test.java"),
@@ -149,6 +170,10 @@ void test(TestTuple test, boolean willFail) throws Exception {
149170
System.err.println("input: " + test.input);
150171
throw new AssertionError("Unexpected token content: " + actual);
151172
}
173+
174+
if (test.expectedErrors != null && !test.expectedErrors.equals(errors)) {
175+
throw new AssertionError("Unexpected errors: " + errors);
176+
}
152177
}
153178

154179
void run() throws Exception {

0 commit comments

Comments
 (0)