Skip to content

Commit

Permalink
clone scanner if necessary to prevent concurrent use of the same inst…
Browse files Browse the repository at this point in the history
…ance (#3521)

StringLiteral.getLiteralValue and CharacterLiteral.charValue need to use their own local scanner instances in order to avoid issues when used concurrently

Fixes #3424
  • Loading branch information
martinlippert authored Jan 7, 2025
1 parent c73baf6 commit 6d442c3
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
package org.eclipse.jdt.core.tests.dom;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import junit.framework.Test;
import org.eclipse.jdt.core.dom.*;

Expand Down Expand Up @@ -1370,6 +1375,25 @@ public void testCharacterLiteral() {
String result = this.b.toString();
assertTrue(result.equals("[(eCL'q''q'eCL)]")); //$NON-NLS-1$
}
public void testCharacterLiteralConcurrent() throws Exception {
CharacterLiteral x1 = this.ast.newCharacterLiteral();
x1.setCharValue('q');

ExecutorService executorService = Executors.newFixedThreadPool(10);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
x1.charValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executorService);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
executorService.shutdown();
}
/** @deprecated using deprecated code */
public void testClassInstanceCreation() {
ClassInstanceCreation x1 = this.ast.newClassInstanceCreation();
Expand Down Expand Up @@ -2028,6 +2052,26 @@ public void testStringLiteral() {
String result = this.b.toString();
assertTrue(result.equals("[(eSLHHeSL)]")); //$NON-NLS-1$
}
public void testStringLiteralConcurrent() throws Exception {
StringLiteral x1 = this.ast.newStringLiteral();
x1.setEscapedValue("\"hello\\nworld\""); //$NON-NLS-1$

ExecutorService executorService = Executors.newFixedThreadPool(10);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
x1.getLiteralValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executorService);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
executorService.shutdown();
}

/** @deprecated using deprecated code */
public void testSuperConstructorInvocation() {
SuperConstructorInvocation x1 = this.ast.newSuperConstructorInvocation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,18 @@ void internalSetEscapedValue(String value) {
* @exception IllegalArgumentException if the literal value cannot be converted
*/
public char charValue() {
Scanner scanner = this.ast.scanner;
// create a new local scanner to allow concurrent use
Scanner scanner = new Scanner(
this.ast.scanner.tokenizeComments,
this.ast.scanner.tokenizeWhiteSpace,
this.ast.scanner.checkNonExternalizedStringLiterals,
this.ast.scanner.sourceLevel,
this.ast.scanner.complianceLevel,
this.ast.scanner.taskTags,
this.ast.scanner.taskPriorities,
this.ast.scanner.isTaskCaseSensitive,
this.ast.scanner.previewEnabled);

char[] source = this.escapedValue.toCharArray();
scanner.setSource(source);
scanner.resetTo(0, source.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,18 @@ public String getLiteralValue() {
throw new IllegalArgumentException();
}

Scanner scanner = this.ast.scanner;
// create a new local scanner to allow concurrent use
Scanner scanner = new Scanner(
this.ast.scanner.tokenizeComments,
this.ast.scanner.tokenizeWhiteSpace,
this.ast.scanner.checkNonExternalizedStringLiterals,
this.ast.scanner.sourceLevel,
this.ast.scanner.complianceLevel,
this.ast.scanner.taskTags,
this.ast.scanner.taskPriorities,
this.ast.scanner.isTaskCaseSensitive,
this.ast.scanner.previewEnabled);

char[] source = s.toCharArray();
scanner.setSource(source);
scanner.resetTo(0, source.length);
Expand All @@ -226,10 +237,10 @@ public String getLiteralValue() {
case TerminalTokens.TokenNameStringLiteral:
return scanner.getCurrentStringLiteral();
default:
throw new IllegalArgumentException();
throw new IllegalArgumentException("tokenType: " + tokenType); //$NON-NLS-1$
}
} catch(InvalidInputException e) {
throw new IllegalArgumentException();
throw new IllegalArgumentException(e);
}
}

Expand Down

0 comments on commit 6d442c3

Please sign in to comment.