Skip to content

Commit d7ad94d

Browse files
l46kokcopybara-github
authored andcommitted
Fix a thread safety issue with CelParser's toBuilder
PiperOrigin-RevId: 835341182
1 parent ff4174a commit d7ad94d

File tree

1 file changed

+38
-22
lines changed

1 file changed

+38
-22
lines changed

parser/src/main/java/dev/cel/parser/CelParserImpl.java

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
package dev.cel.parser;
1616

1717
import static com.google.common.base.Preconditions.checkNotNull;
18+
import static java.util.stream.Collectors.toCollection;
1819

1920
import com.google.common.annotations.VisibleForTesting;
21+
import com.google.common.collect.ImmutableList;
2022
import com.google.common.collect.ImmutableMap;
2123
import com.google.common.collect.ImmutableSet;
2224
import com.google.common.collect.Iterables;
@@ -32,9 +34,11 @@
3234
import java.util.ArrayList;
3335
import java.util.Arrays;
3436
import java.util.HashMap;
37+
import java.util.HashSet;
3538
import java.util.List;
3639
import java.util.Map;
3740
import java.util.Optional;
41+
import java.util.stream.Collectors;
3842

3943
/**
4044
* Modernized parser implementation for CEL.
@@ -54,9 +58,10 @@ public final class CelParserImpl implements CelParser, EnvVisitable {
5458
// Specific options for limits on parsing power.
5559
private final CelOptions options;
5660

57-
// Builder is mutable by design. APIs must make defensive copies in and out of this class.
58-
@SuppressWarnings("Immutable")
59-
private final Builder parserBuilder;
61+
private final ImmutableList<CelStandardMacro> standardMacros;
62+
63+
@SuppressWarnings("Immutable") // Interface not marked as immutable, however it should be.
64+
private final ImmutableSet<CelParserLibrary> parserLibraries;
6065

6166
/** Creates a new {@link Builder}. */
6267
public static CelParserBuilder newBuilder() {
@@ -75,7 +80,20 @@ public CelValidationResult parse(CelSource source) {
7580

7681
@Override
7782
public CelParserBuilder toParserBuilder() {
78-
return new Builder(parserBuilder);
83+
HashSet<String> standardMacroKeys =
84+
standardMacros.stream()
85+
.map(s -> s.getDefinition().getKey())
86+
.collect(Collectors.toCollection(HashSet::new));
87+
88+
return new Builder()
89+
.setOptions(options)
90+
.setStandardMacros(standardMacros)
91+
.addMacros(
92+
// Separate standard macros from the custom macros before constructing the builder
93+
macros.values().stream()
94+
.filter(m -> !standardMacroKeys.contains(m.getKey()))
95+
.collect(toCollection(ArrayList::new)))
96+
.addLibraries(parserLibraries);
7997
}
8098

8199
Optional<CelMacro> findMacro(String key) {
@@ -175,37 +193,35 @@ public CelParserImpl build() {
175193
// Add libraries, such as extensions
176194
parserLibrarySet.forEach(celLibrary -> celLibrary.setParserOptions(this));
177195

178-
ImmutableMap.Builder<String, CelMacro> builder = ImmutableMap.builder();
179-
builder.putAll(macros);
196+
ImmutableMap.Builder<String, CelMacro> macroMapBuilder = ImmutableMap.builder();
197+
macroMapBuilder.putAll(macros);
180198
standardMacros.stream()
181199
.map(CelStandardMacro::getDefinition)
182-
.forEach(celMacro -> builder.put(celMacro.getKey(), celMacro));
183-
return new CelParserImpl(builder.buildOrThrow(), checkNotNull(options), this);
200+
.forEach(celMacro -> macroMapBuilder.put(celMacro.getKey(), celMacro));
201+
202+
return new CelParserImpl(
203+
macroMapBuilder.buildOrThrow(),
204+
options,
205+
ImmutableList.copyOf(standardMacros),
206+
celParserLibraries.build());
184207
}
185208

186209
private Builder() {
187210
this.macros = new HashMap<>();
188211
this.celParserLibraries = ImmutableSet.builder();
189212
this.standardMacros = new ArrayList<>();
190213
}
191-
192-
private Builder(Builder builder) {
193-
// The following properties are either immutable or simple primitives, thus can be assigned
194-
// directly.
195-
this.options = builder.options;
196-
// The following needs to be deep copied as they are collection builders
197-
this.macros = new HashMap<>(builder.macros);
198-
this.standardMacros = new ArrayList<>(builder.standardMacros);
199-
this.celParserLibraries = ImmutableSet.builder();
200-
this.celParserLibraries.addAll(builder.celParserLibraries.build());
201-
}
202214
}
203215

204216
private CelParserImpl(
205-
ImmutableMap<String, CelMacro> macros, CelOptions options, Builder parserBuilder) {
217+
ImmutableMap<String, CelMacro> macros,
218+
CelOptions options,
219+
ImmutableList<CelStandardMacro> standardMacros,
220+
ImmutableSet<CelParserLibrary> parserLibraries) {
206221
this.macros = macros;
207-
this.options = options;
208-
this.parserBuilder = new Builder(parserBuilder);
222+
this.options = checkNotNull(options);
223+
this.standardMacros = standardMacros;
224+
this.parserLibraries = parserLibraries;
209225
}
210226

211227
@Override

0 commit comments

Comments
 (0)