diff --git a/pom.xml b/pom.xml
index 8e09b040..c9b7f607 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,12 +61,12 @@
org.unlaxer
unlaxer-common
- 3.0.10
+ 3.0.11
org.unlaxer
unlaxer-dsl
- 3.0.10
+ 3.0.11
org.jetbrains
diff --git a/src/test/java/org/unlaxer/tinyexpression/evaluator/p4/P4PackratFraudFormulaTest.java b/src/test/java/org/unlaxer/tinyexpression/evaluator/p4/P4PackratFraudFormulaTest.java
index 545e0c34..be3d8762 100644
--- a/src/test/java/org/unlaxer/tinyexpression/evaluator/p4/P4PackratFraudFormulaTest.java
+++ b/src/test/java/org/unlaxer/tinyexpression/evaluator/p4/P4PackratFraudFormulaTest.java
@@ -38,12 +38,12 @@ private long parseMillis(String formula) {
/**
* Formulas #1–4 are the boolean / parenthesis-ambiguity shape whose exponential backtracking
* packrat memoization collapses — they parse in well under a second (measured ~0.02–0.5s vs the
- * 53-min hang in #19). Formula #5 is the deeply nested-{@code if} shape: memoization helps but
- * does NOT bring it under a second (residual ~24s; even allowing success memoization everywhere
- * it stays ~12s), because its bottleneck is not {@code (rule, position)} re-derivation — it is the
- * per-character {@code StringSource.peek} allocation that #19's profile flagged as independent of
- * memoization. #5 is therefore logged and only guarded against the exponential hang here, and is
- * tracked as a separate follow-up; in production it still falls back via the parse deadline.
+ * 53-min hang in #19). Formula #5 is the deeply nested-{@code if} shape. Packrat (parse-phase)
+ * memoization alone left it at 12–30s; #49 found the real bottleneck was the mapping phase, not
+ * parsing: {@code findBestMappedToken} re-constructed the same subtrees ~10M times (each via
+ * per-token reflection + an unbounded {@code IdentityHashMap} of source spans). Memoizing
+ * {@code mapToken} by token identity and dropping the reflection (unlaxer-dsl MapperGenerator,
+ * 3.0.11) brings #5 to ~30ms — so it is now held to the same strict sub-second bound as the rest.
*/
@Test(timeout = 90_000)
public void fraudFormulasParseFastWithMemoization() {
@@ -51,16 +51,9 @@ public void fraudFormulasParseFastWithMemoization() {
try {
for (int index = 0; index < FRAUD_FORMULAS.length; index++) {
long elapsed = parseMillis(FRAUD_FORMULAS[index]);
- boolean nestedIfResidual = index == 4; // #5
- System.out.println("fraud formula #" + (index + 1) + " parsed in " + elapsed + "ms (memoize on)"
- + (nestedIfResidual ? " [nested-if residual — tracked separately]" : ""));
- if (nestedIfResidual) {
- // Guard only against the return of the exponential hang (was 53+ min).
- assertTrue("formula #5 must not exponentially hang (was " + elapsed + "ms)", elapsed < 60_000);
- } else {
- assertTrue("formula #" + (index + 1) + " should parse in well under a second with "
- + "memoization (was " + elapsed + "ms)", elapsed < 2_000);
- }
+ System.out.println("fraud formula #" + (index + 1) + " parsed in " + elapsed + "ms (memoize on)");
+ assertTrue("formula #" + (index + 1) + " should parse in well under a second with "
+ + "memoization (was " + elapsed + "ms)", elapsed < 2_000);
}
} finally {
System.clearProperty("tinyexpression.p4.memoize");