This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY-9637
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit aa8d1778037910b188c0604011c970df52eec6ea
Author: Daniel Sun <sun...@apache.org>
AuthorDate: Mon Jul 13 02:40:57 2020 +0800

    GROOVY-9637: Improve the performance of GString
---
 src/main/java/groovy/lang/GString.java | 47 +++++++++++++++++++++++++++++++++-
 src/test/groovy/GStringTest.groovy     | 16 ++++++++++++
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/src/main/java/groovy/lang/GString.java 
b/src/main/java/groovy/lang/GString.java
index 207a1a6..ec1c06a 100644
--- a/src/main/java/groovy/lang/GString.java
+++ b/src/main/java/groovy/lang/GString.java
@@ -27,6 +27,11 @@ import java.io.IOException;
 import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 /**
@@ -48,6 +53,22 @@ public abstract class GString extends GroovyObjectSupport 
implements Comparable,
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 
+    private static final Set<Class<?>> IMMUTABLE_TYPE_LIST =
+            new HashSet<>(
+                    Arrays.asList(
+                            String.class, Class.class,
+                            BigDecimal.class, BigInteger.class,
+                            boolean.class, Boolean.class,
+                            char.class, Character.class,
+                            byte.class, Byte.class,
+                            short.class, Short.class,
+                            int.class, Integer.class,
+                            long.class, Long.class,
+                            float.class, Float.class,
+                            double.class, Double.class
+                    )
+            );
+
     /**
      * A GString containing a single empty String and no values.
      */
@@ -68,13 +89,16 @@ public abstract class GString extends GroovyObjectSupport 
implements Comparable,
 
 
     private final Object[] values;
+    private final boolean immutable;
 
     public GString(Object values) {
         this.values = (Object[]) values;
+        this.immutable = checkValuesImmutable();
     }
 
     public GString(Object[] values) {
         this.values = values;
+        this.immutable = checkValuesImmutable();
     }
 
     // will be static in an instance
@@ -150,8 +174,15 @@ public abstract class GString extends GroovyObjectSupport 
implements Comparable,
         return values[idx];
     }
 
+
+    private String cachedStringLiteral;
+
     @Override
     public String toString() {
+        if (null != cachedStringLiteral) {
+            return cachedStringLiteral;
+        }
+
         Writer buffer = new StringBuilderWriter(calcInitialCapacity());
         try {
             writeTo(buffer);
@@ -159,7 +190,9 @@ public abstract class GString extends GroovyObjectSupport 
implements Comparable,
             throw new StringWriterIOException(e);
         }
 
-        return buffer.toString();
+        String str = buffer.toString();
+
+        return immutable ? (cachedStringLiteral = str) : str;
     }
 
     private int calcInitialCapacity() {
@@ -276,4 +309,16 @@ public abstract class GString extends GroovyObjectSupport 
implements Comparable,
     public byte[] getBytes(String charset) throws UnsupportedEncodingException 
{
         return toString().getBytes(charset);
     }
+
+    private boolean checkValuesImmutable() {
+        for (Object value : values) {
+            if (null == value) continue;
+            if (!(IMMUTABLE_TYPE_LIST.contains(value.getClass())
+                    || (value instanceof GString && ((GString) 
value).immutable))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/src/test/groovy/GStringTest.groovy 
b/src/test/groovy/GStringTest.groovy
index 90fb455..00c75eb 100644
--- a/src/test/groovy/GStringTest.groovy
+++ b/src/test/groovy/GStringTest.groovy
@@ -20,6 +20,8 @@ package groovy
 
 import groovy.test.GroovyTestCase
 
+import java.lang.reflect.Field
+
 class GStringTest extends GroovyTestCase {
 
     void check(template, teststr) {
@@ -582,4 +584,18 @@ class GStringTest extends GroovyTestCase {
         assert Eval.me('''def foo='bar'; /$foo\u002abaz/''') == 'bar*baz'
         assert Eval.me('''def foo='bar'; /${foo}\u002abaz/''') == 'bar*baz'
     }
+
+    void testNestedGString() {
+        def gstr = "a${"${123}"}b"
+        assert 'a123b' == gstr
+        assert gstr.toString() === gstr.toString()
+
+        Field immutableField = GString.getDeclaredFields().find {f -> f.name 
== 'immutable'}
+        immutableField.setAccessible(true)
+        assert true == immutableField.get(gstr)
+
+        Field cachedStringLiteralField = GString.getDeclaredFields().find {f 
-> f.name == 'cachedStringLiteral'}
+        cachedStringLiteralField.setAccessible(true)
+        assert 'a123b' == cachedStringLiteralField.get(gstr)
+    }
 }

Reply via email to