|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2023 the original author or authors. |
| 2 | + * Copyright 2002-2024 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
@@ -81,7 +81,7 @@ public final class SpelCompiler implements Opcodes {
|
81 | 81 | private volatile ChildClassLoader childClassLoader;
|
82 | 82 |
|
83 | 83 | // Counter suffix for generated classes within this SpelCompiler instance
|
84 |
| - private final AtomicInteger suffixId = new AtomicInteger(1); |
| 84 | + private final AtomicInteger suffixId = new AtomicInteger(0); |
85 | 85 |
|
86 | 86 |
|
87 | 87 | private SpelCompiler(@Nullable ClassLoader classloader) {
|
@@ -122,21 +122,22 @@ public CompiledExpression compile(SpelNodeImpl expression) {
|
122 | 122 | return null;
|
123 | 123 | }
|
124 | 124 |
|
125 |
| - private int getNextSuffix() { |
126 |
| - return this.suffixId.incrementAndGet(); |
| 125 | + private String getNextSuffix() { |
| 126 | + return "%05d".formatted(this.suffixId.incrementAndGet()); |
127 | 127 | }
|
128 | 128 |
|
129 | 129 | /**
|
130 | 130 | * Generate the class that encapsulates the compiled expression and define it.
|
131 |
| - * The generated class will be a subtype of CompiledExpression. |
| 131 | + * <p>The generated class will be a subtype of {@link CompiledExpression}. |
132 | 132 | * @param expressionToCompile the expression to be compiled
|
133 | 133 | * @return the expression call, or {@code null} if the decision was to opt out of
|
134 | 134 | * compilation during code generation
|
135 | 135 | */
|
136 | 136 | @Nullable
|
137 | 137 | private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
|
138 |
| - // Create class outline 'spel/ExNNN extends org.springframework.expression.spel.CompiledExpression' |
139 |
| - String className = "spel/Ex" + getNextSuffix(); |
| 138 | + // Create class outline: |
| 139 | + // org.springframework.expression.spel.generated.CompiledExpression##### extends org.springframework.expression.spel.CompiledExpression |
| 140 | + String className = "org/springframework/expression/spel/generated/CompiledExpression" + getNextSuffix(); |
140 | 141 | String evaluationContextClass = "org/springframework/expression/EvaluationContext";
|
141 | 142 | ClassWriter cw = new ExpressionClassWriter();
|
142 | 143 | cw.visit(V1_8, ACC_PUBLIC, className, null, "org/springframework/expression/spel/CompiledExpression", null);
|
@@ -184,12 +185,31 @@ private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl e
|
184 | 185 | cf.finish();
|
185 | 186 |
|
186 | 187 | byte[] data = cw.toByteArray();
|
187 |
| - // TODO Save generated class files conditionally based on a debug flag. |
188 |
| - // Source code for the following method resides in SpelCompilationCoverageTests. |
| 188 | + // TODO Save generated class files conditionally based on a flag. |
189 | 189 | // saveGeneratedClassFile(expressionToCompile.toStringAST(), className, data);
|
190 | 190 | return loadClass(StringUtils.replace(className, "/", "."), data);
|
191 | 191 | }
|
192 | 192 |
|
| 193 | + // NOTE: saveGeneratedClassFile() can be uncommented in order to review generated byte code for |
| 194 | + // debugging purposes. See also: https://github.com/spring-projects/spring-framework/issues/29548 |
| 195 | + // |
| 196 | + // private static void saveGeneratedClassFile(String stringAST, String className, byte[] data) { |
| 197 | + // try { |
| 198 | + // // TODO Make target directory configurable. |
| 199 | + // String targetDir = "build/generated-classes"; |
| 200 | + // Path path = Path.of(targetDir, className + ".class"); |
| 201 | + // Files.deleteIfExists(path); |
| 202 | + // Files.createDirectories(path.getParent()); |
| 203 | + // if (logger.isDebugEnabled()) { |
| 204 | + // logger.debug("Saving compiled SpEL expression [%s] to [%s]".formatted(stringAST, path.toAbsolutePath())); |
| 205 | + // } |
| 206 | + // Files.copy(new ByteArrayInputStream(data), path); |
| 207 | + // } |
| 208 | + // catch (IOException ex) { |
| 209 | + // throw new UncheckedIOException(ex); |
| 210 | + // } |
| 211 | + // } |
| 212 | + |
193 | 213 | /**
|
194 | 214 | * Load a compiled expression class. Makes sure the classloaders aren't used too much
|
195 | 215 | * because they anchor compiled classes in memory and prevent GC. If you have expressions
|
|
0 commit comments