Merges changes/spoon/defBindStringBuilder into trunk. This gives
StringBuffer (and StringBuilder) browser-specific deferred bindings.
svn merge -r 2934:2962 https://google-web-toolkit.googlecode.com/svn/changes/spoon/defBindStringBuilder .
svn merge -r 2963:3190 https://google-web-toolkit.googlecode.com/svn/changes/spoon/defBindStringBuilder .
svn commit
Patch by: ecc, scottb, spoon
Review by: ecc, scottb, spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3191 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java b/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java
new file mode 100644
index 0000000..5c8df7d
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImpl.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * <p>
+ * The interface to defer bound implementations of {@link StringBuilder} and
+ * {@link StringBuffer}.
+ * </p>
+ *
+ * <p>
+ * All of the implementations have been carefully tweaked to get the most
+ * inlining possible, so be sure to check with
+ * {@link com.google.gwt.emultest.java.lang.StringBuilderBenchmark StringBuilderBenchmark}
+ * whenever these classes are modified.
+ * </p>
+ */
+public abstract class StringBufferImpl {
+
+ /**
+ * Append for primitive; the value can be stored and only later converted to a
+ * string.
+ */
+ public abstract void append(Object data, boolean x);
+
+ /**
+ * Append for primitive; the value can be stored and only later converted to a
+ * string.
+ */
+ public abstract void append(Object data, double x);
+
+ /**
+ * Append for primitive; the value can be stored and only later converted to a
+ * string.
+ */
+ public abstract void append(Object data, float x);
+
+ /**
+ * Append for primitive; the value can be stored and only later converted to a
+ * string.
+ */
+ public abstract void append(Object data, int x);
+
+ /**
+ * Append for object. It is important to immediately convert the object to a
+ * string, because the conversion can give different results if it is
+ * deferred.
+ */
+ public abstract void append(Object data, Object x);
+
+ /**
+ * Append for a possibly null string object.
+ */
+ public abstract void append(Object data, String x);
+
+ /**
+ * Append for a string that is definitely not null.
+ */
+ public abstract void appendNonNull(Object data, String x);
+
+ /**
+ * Returns a data holder object for use with subsequent calls.
+ */
+ public abstract Object createData();
+
+ /**
+ * Returns the current length of the string buffer.
+ */
+ public abstract int length(Object data);
+
+ /**
+ * Replaces a segment of the string buffer.
+ */
+ public abstract void replace(Object data, int start, int end, String toInsert);
+
+ /**
+ * Returns the string buffer as a String.
+ */
+ public abstract String toString(Object data);
+}
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java
new file mode 100644
index 0000000..6dd2ed5
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplAppend.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * A {@link StringBufferImpl} that uses += for appending strings. This appears
+ * to be the fastest implementation everywhere except IE, where it's terrible.
+ */
+public class StringBufferImplAppend extends StringBufferImpl {
+ private String string = "";
+
+ @Override
+ public void append(Object data, boolean x) {
+ string += x;
+ }
+
+ @Override
+ public void append(Object data, double x) {
+ string += x;
+ }
+
+ @Override
+ public void append(Object data, float x) {
+ string += x;
+ }
+
+ @Override
+ public void append(Object data, int x) {
+ string += x;
+ }
+
+ @Override
+ public void append(Object data, Object x) {
+ string += x;
+ }
+
+ @Override
+ public void append(Object data, String x) {
+ string += x;
+ }
+
+ @Override
+ public void appendNonNull(Object data, String x) {
+ string += x;
+ }
+
+ @Override
+ public Object createData() {
+ return null;
+ }
+
+ @Override
+ public int length(Object data) {
+ return string.length();
+ }
+
+ @Override
+ public void replace(Object data, int start, int end, String toInsert) {
+ string = string.substring(0, start) + toInsert + string.substring(end);
+ }
+
+ @Override
+ public String toString(Object data) {
+ return string;
+ }
+}
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplArray.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplArray.java
new file mode 100644
index 0000000..306c390
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplArray.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * A {@link StringBufferImpl} that uses an array and an explicit length for
+ * appending strings. Note that the length of the array is stored as a property
+ * of the underlying JavaScriptObject because making it a field on this object
+ * causes difficulty with inlining. This is the best implementation on IE, and
+ * generally second best on all other browsers, making it the best default when
+ * the user agent is unknown.
+ */
+public class StringBufferImplArray extends StringBufferImplArrayBase {
+}
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java
new file mode 100644
index 0000000..227ef93
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplArrayBase.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * Superclass for all array-based string builder implementations.
+ */
+public abstract class StringBufferImplArrayBase extends StringBufferImpl {
+
+ @Override
+ public native void append(Object a, boolean x) /*-{
+ a[a.explicitLength++] = x;
+ }-*/;
+
+ @Override
+ public native void append(Object a, double x) /*-{
+ a[a.explicitLength++] = x;
+ }-*/;
+
+ @Override
+ public native void append(Object a, float x) /*-{
+ a[a.explicitLength++] = x;
+ }-*/;
+
+ @Override
+ public native void append(Object a, int x) /*-{
+ a[a.explicitLength++] = x;
+ }-*/;
+
+ @Override
+ public final void append(Object a, Object x) {
+ appendNonNull(a, "" + x);
+ }
+
+ @Override
+ public void append(Object a, String x) {
+ appendNonNull(a, (x == null) ? "null" : x);
+ }
+
+ @Override
+ public native void appendNonNull(Object a, String x) /*-{
+ a[a.explicitLength++] = x;
+ }-*/;
+
+ @Override
+ public final native Object createData() /*-{
+ var array = [];
+ array.explicitLength = 0;
+ return array;
+ }-*/;
+
+ @Override
+ public int length(Object a) {
+ return toString(a).length();
+ }
+
+ @Override
+ public final void replace(Object a, int start, int end, String toInsert) {
+ String s = takeString(a);
+ appendNonNull(a, s.substring(0, start));
+ append(a, toInsert);
+ appendNonNull(a, s.substring(end));
+ }
+
+ @Override
+ public final String toString(Object a) {
+ String s = takeString(a);
+ appendNonNull(a, s);
+ return s;
+ }
+
+ protected native String takeString(Object a) /*-{
+ var s = a.join('');
+ a.length = a.explicitLength = 0;
+ return s;
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplConcat.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplConcat.java
new file mode 100644
index 0000000..b0eec79
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplConcat.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * A {@link StringBufferImplArrayBase} that does a concat for toString(). The
+ * performance of this implementation is generally slightly worse than
+ * {@link StringBufferImplArray} for everything except IE, where it's terrible.
+ */
+public class StringBufferImplConcat extends StringBufferImplArrayBase {
+ /**
+ * We don't need to do the null check because concat does it automagically.
+ */
+ @Override
+ public native void append(Object a, String x) /*-{
+ a[a.explicitLength++] = x;
+ }-*/;
+
+ @Override
+ protected native String takeString(Object a) /*-{
+ var s = String.prototype.concat.apply('', a);
+ a.length = a.explicitLength = 0;
+ return s;
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/impl/StringBufferImplPush.java b/user/src/com/google/gwt/core/client/impl/StringBufferImplPush.java
new file mode 100644
index 0000000..c9806f6
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBufferImplPush.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * A {@link StringBufferImplArrayBase} that uses <code>push</code> for
+ * appending strings. Some external benchmarks suggest this implementation, but
+ * in practice our measurements indication that {@link StringBufferImplArray}
+ * has a slight edge on every browser; the performance is often very close to
+ * {@link StringBufferImplConcat}.
+ */
+public class StringBufferImplPush extends StringBufferImplArrayBase {
+
+ @Override
+ public native void append(Object a, boolean x) /*-{
+ a.push(x);
+ }-*/;
+
+ @Override
+ public native void append(Object a, double x) /*-{
+ a.push(x);
+ }-*/;
+
+ @Override
+ public native void append(Object a, float x) /*-{
+ a.push(x);
+ }-*/;
+
+ @Override
+ public native void append(Object a, int x) /*-{
+ a.push(x);
+ }-*/;
+
+ @Override
+ public native void appendNonNull(Object a, String x) /*-{
+ a.push(x);
+ }-*/;
+}
diff --git a/user/src/com/google/gwt/core/client/impl/StringBuilderImpl.java b/user/src/com/google/gwt/core/client/impl/StringBuilderImpl.java
new file mode 100644
index 0000000..81a55d2
--- /dev/null
+++ b/user/src/com/google/gwt/core/client/impl/StringBuilderImpl.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.impl;
+
+/**
+ * <p>
+ * An implementation for a {@link StringBuilder} or {@link StringBuffer}. This
+ * class holds a default implementation based on an array of strings and the
+ * JavaScript join function. Deferred bindings can substitute a subclass
+ * optimized for a particular browser.
+ * </p>
+ *
+ * <p>
+ * The main implementations are static classes nested within this one. All of
+ * the implementations have been carefully tweaked to get the most inlining
+ * possible, so be sure to check with
+ * {@link com.google.gwt.emultest.java.lang.StringBuilderBenchmark StringBuilderBenchmark}
+ * whenever these classes are modified.
+ * </p>
+ */
+public class StringBuilderImpl {
+
+ /**
+ * A {@link StringBuilderImpl} that uses an array and an explicit length for
+ * appending strings. Note that the length of the array is stored as a
+ * property of the underlying JavaScriptObject. Making it a field of
+ * {@link ImplArray} causes difficulty with inlining.
+ */
+
+ public static class ImplArray extends StringBuilderImpl {
+ private static native void setArrayLength(String[] array, int length) /*-{
+ array.explicitLength = length;
+ }-*/;
+
+ @SuppressWarnings("unused")
+ private String[] array = new String[0];
+
+ public ImplArray() {
+ setArrayLength(array, 0);
+ }
+
+ @Override
+ public native void append(String s) /*-{
+ var a = this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplArray::array;
+ a[a.explicitLength++] = s==null ? "null" : s;
+ }-*/;
+
+ @Override
+ public int length() {
+ return toString().length();
+ }
+
+ @Override
+ public void replace(int start, int end, String toInsert) {
+ String s = toString();
+ array = new String[] {s.substring(0, start), toInsert, s.substring(end)};
+ }
+
+ @Override
+ public native String toString() /*-{
+ this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplArray::array =
+ [ this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplArray::array.join('') ];
+ this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplArray::array.explicitLength = 1;
+ return this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplArray::array[0];
+ }-*/;
+ }
+
+ /**
+ * A {@link StringBuilderImpl} that uses <code>push</code> for appending
+ * strings.
+ */
+ public static class ImplPush extends StringBuilderImpl {
+ @SuppressWarnings("unused")
+ private String[] array = new String[0];
+
+ @Override
+ public native void append(String s) /*-{
+ this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplPush::array.push(s == null ? "null" : s);
+ }-*/;
+
+ @Override
+ public int length() {
+ return toString().length();
+ }
+
+ @Override
+ public void replace(int start, int end, String toInsert) {
+ String s = toString();
+ array = new String[] {s.substring(0, start), toInsert, s.substring(end)};
+ }
+
+ @Override
+ public native String toString() /*-{
+ this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplPush::array =
+ [ this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplPush::array.join('') ];
+ return this.@com.google.gwt.core.client.impl.StringBuilderImpl.ImplPush::array[0];
+ }-*/;
+ }
+
+ /**
+ * A {@link StringBuilderImpl} that uses += for appending strings.
+ */
+ public static class ImplStringAppend extends StringBuilderImpl {
+ private String string = "";
+
+ @Override
+ public void append(String s) {
+ string += s;
+ }
+
+ @Override
+ public int length() {
+ return string.length();
+ }
+
+ @Override
+ public void replace(int start, int end, String toInsert) {
+ string = string.substring(0, start) + toInsert + string.substring(end);
+ }
+
+ @Override
+ public String toString() {
+ return string;
+ }
+ }
+
+ private static native String join(String[] stringArray) /*-{
+ return stringArray.join('');
+ }-*/;
+
+ private static native String setLength(String[] stringArray, int length) /*-{
+ stringArray.length = length;
+ }-*/;
+
+ private int arrayLen = 0;
+ private String[] stringArray = new String[0];
+ private int stringLength = 0;
+
+ public void append(String toAppend) {
+ // Coerce to "null" if null.
+ if (toAppend == null) {
+ toAppend = "null";
+ }
+ int appendLength = toAppend.length();
+ if (appendLength > 0) {
+ stringArray[arrayLen++] = toAppend == null ? "null" : toAppend;
+ stringLength += appendLength;
+ /*
+ * If we hit 1k elements, let's do a join to reduce the array size. This
+ * number was arrived at experimentally through benchmarking.
+ */
+ if (arrayLen > 1024) {
+ toString();
+ // Preallocate the next 1024 (faster on FF).
+ setLength(stringArray, 1024);
+ }
+ }
+ }
+
+ public int length() {
+ return stringLength;
+ }
+
+ public void replace(int start, int end, String toInsert) {
+ // Get the joined string.
+ String s = toString();
+
+ // Build a new buffer in pieces (will throw exceptions).
+ stringArray = new String[] {
+ s.substring(0, start), toInsert, s.substring(end)};
+ arrayLen = 3;
+
+ // Calculate the new string length.
+ stringLength += toInsert.length() - (end - start);
+ }
+
+ @Override
+ public String toString() {
+ /*
+ * Normalize the array to exactly one element (even if it's completely
+ * empty), so we can unconditionally grab the first element.
+ */
+ if (arrayLen != 1) {
+ setLength(stringArray, arrayLen);
+ String s = join(stringArray);
+ // Create a new array to allow everything to get GC'd.
+ stringArray = new String[] {s};
+ arrayLen = 1;
+ }
+ return stringArray[0];
+ }
+}
diff --git a/user/src/com/google/gwt/user/UserAgent.gwt.xml b/user/src/com/google/gwt/user/UserAgent.gwt.xml
index 42433f6..6e4a49f 100644
--- a/user/src/com/google/gwt/user/UserAgent.gwt.xml
+++ b/user/src/com/google/gwt/user/UserAgent.gwt.xml
@@ -49,4 +49,6 @@
return "unknown";
]]></property-provider>
+ <!-- Deferred binding to optimize JRE classes based on user agent. -->
+ <inherits name="com.google.gwt.emul.EmulationWithUserAgent"/>
</module>
diff --git a/user/super/com/google/gwt/emul/Emulation.gwt.xml b/user/super/com/google/gwt/emul/Emulation.gwt.xml
index e41d58f..d74ae89 100644
--- a/user/super/com/google/gwt/emul/Emulation.gwt.xml
+++ b/user/super/com/google/gwt/emul/Emulation.gwt.xml
@@ -1,5 +1,5 @@
<!-- -->
-<!-- Copyright 2007 Google Inc. -->
+<!-- Copyright 2008 Google Inc. -->
<!-- Licensed under the Apache License, Version 2.0 (the "License"); you -->
<!-- may not use this file except in compliance with the License. You may -->
<!-- may obtain a copy of the License at -->
@@ -15,5 +15,9 @@
<!-- A JavaScript-based emulation of the Java Runtime library. -->
<!-- Do not inherit this module directly; inherit com.google.gwt.core.Core. -->
<module>
- <super-source/>
+ <replace-with class="com.google.gwt.core.client.impl.StringBufferImplArray">
+ <when-type-is class="com.google.gwt.core.client.impl.StringBufferImpl"/>
+ </replace-with>
+
+ <super-source/>
</module>
diff --git a/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml b/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
new file mode 100644
index 0000000..4add1cf
--- /dev/null
+++ b/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
@@ -0,0 +1,33 @@
+<!-- -->
+<!-- Copyright 2008 Google Inc. -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you -->
+<!-- may not use this file except in compliance with the License. You may -->
+<!-- may obtain a copy of the License at -->
+<!-- -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0 -->
+<!-- -->
+<!-- Unless required by applicable law or agreed to in writing, software -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS, -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -->
+<!-- implied. License for the specific language governing permissions and -->
+<!-- limitations under the License. -->
+
+<!-- Deferred binding rules for optimized JRE implementations based on user agent. -->
+<module>
+ <inherits name="com.google.gwt.emul.Emulation"/>
+
+ <!-- If the user agent is known, use Append as the default StringBuffer implementation. -->
+ <replace-with class="com.google.gwt.core.client.impl.StringBufferImplAppend">
+ <when-type-is class="com.google.gwt.core.client.impl.StringBufferImpl"/>
+ </replace-with>
+
+ <!-- Append is awful on IE. Use Array instead. -->
+ <replace-with class="com.google.gwt.core.client.impl.StringBufferImplArray">
+ <when-type-is class="com.google.gwt.core.client.impl.StringBufferImpl"/>
+ <any>
+ <when-property-is name="user.agent" value="ie6"/>
+ </any>
+ </replace-with>
+
+ <super-source/>
+</module>
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
index b253f42..f851bef 100644
--- a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
+++ b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
@@ -15,13 +15,24 @@
*/
package java.lang;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.impl.StringBufferImpl;
+
/**
* A fast way to create strings using multiple appends. This is implemented
- * using {@link StringBuilder}, so see that class for implementation notes and
- * performance characteristics.
+ * using a {@link StringBufferImpl} that is chosen with deferred binding.
+ *
+ * Most methods will give expected performance results. Exceptions are
+ * {@link #setCharAt(int, char)}, which is O(n), and {@link #length()}, which
+ * forces a {@link #toString()} and thus should not be used many times on the
+ * same <code>StringBuffer</code>.
+ *
+ * This class is an exact clone of {@link StringBuilder} except for the name.
+ * Any change made to one should be mirrored in the other.
*/
public class StringBuffer implements CharSequence {
- private final StringBuilder builder = new StringBuilder();
+ private final StringBufferImpl impl = GWT.create(StringBufferImpl.class);
+ private final Object data = impl.createData();
public StringBuffer() {
}
@@ -43,67 +54,70 @@
}
public StringBuffer append(boolean x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
public StringBuffer append(char x) {
- builder.append(x);
+ impl.appendNonNull(data, String.valueOf(x));
return this;
}
public StringBuffer append(char[] x) {
- builder.append(x);
+ impl.appendNonNull(data, String.valueOf(x));
return this;
}
public StringBuffer append(char[] x, int start, int len) {
- builder.append(x, start, len);
+ impl.appendNonNull(data, String.valueOf(x, start, len));
return this;
}
public StringBuffer append(CharSequence x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
public StringBuffer append(CharSequence x, int start, int end) {
- builder.append(x, start, end);
+ if (x == null) {
+ x = "null";
+ }
+ impl.append(data, x.subSequence(start, end));
return this;
}
public StringBuffer append(double x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
public StringBuffer append(float x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
public StringBuffer append(int x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
public StringBuffer append(long x) {
- builder.append(x);
+ impl.appendNonNull(data, String.valueOf(x));
return this;
}
public StringBuffer append(Object x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
- public StringBuffer append(String toAppend) {
- builder.append(toAppend);
+ public StringBuffer append(String x) {
+ impl.append(data, x);
return this;
- };
+ }
public StringBuffer append(StringBuffer x) {
- builder.append(x);
+ impl.append(data, x);
return this;
}
@@ -112,146 +126,146 @@
* {@link Integer#MAX_VALUE}.
*/
public int capacity() {
- return builder.capacity();
+ return Integer.MAX_VALUE;
}
public char charAt(int index) {
- return builder.charAt(index);
+ return toString().charAt(index);
}
public StringBuffer delete(int start, int end) {
- builder.delete(start, end);
- return this;
+ return replace(start, end, "");
}
public StringBuffer deleteCharAt(int start) {
- builder.deleteCharAt(start);
- return this;
+ return delete(start, start + 1);
}
/**
* This implementation does not track capacity; calling this method has no
* effect.
*/
+ @SuppressWarnings("unused")
public void ensureCapacity(int ignoredCapacity) {
- builder.ensureCapacity(ignoredCapacity);
}
public void getChars(int srcStart, int srcEnd, char[] dst, int dstStart) {
- builder.getChars(srcStart, srcEnd, dst, dstStart);
+ String.__checkBounds(length(), srcStart, srcEnd);
+ String.__checkBounds(dst.length, dstStart, dstStart + (srcEnd - srcStart));
+ String s = toString();
+ while (srcStart < srcEnd) {
+ dst[dstStart++] = s.charAt(srcStart++);
+ }
}
public int indexOf(String x) {
- return builder.indexOf(x);
+ return toString().indexOf(x);
}
public int indexOf(String x, int start) {
- return builder.indexOf(x, start);
+ return toString().indexOf(x, start);
}
public StringBuffer insert(int index, boolean x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, char x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, char[] x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, char[] x, int offset, int len) {
- builder.insert(index, x, offset, len);
- return this;
+ return insert(index, String.valueOf(x, offset, len));
}
public StringBuffer insert(int index, CharSequence chars) {
- builder.insert(index, chars);
- return this;
+ return insert(index, chars.toString());
}
public StringBuffer insert(int index, CharSequence chars, int start, int end) {
- builder.insert(index, chars, start, end);
- return this;
+ return insert(index, chars.subSequence(start, end).toString());
}
public StringBuffer insert(int index, double x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, float x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, int x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, long x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, Object x) {
- builder.insert(index, x);
- return this;
+ return insert(index, String.valueOf(x));
}
public StringBuffer insert(int index, String x) {
- builder.insert(index, x);
- return this;
+ return replace(index, index, x);
}
public int lastIndexOf(String s) {
- return builder.lastIndexOf(s);
+ return toString().lastIndexOf(s);
}
public int lastIndexOf(String s, int start) {
- return builder.lastIndexOf(s, start);
+ return toString().lastIndexOf(s, start);
}
public int length() {
- return builder.length();
+ return impl.length(data);
}
public StringBuffer replace(int start, int end, String toInsert) {
- builder.replace(start, end, toInsert);
+ impl.replace(data, start, end, toInsert);
return this;
}
+ /**
+ * Warning! This method is <b>much</b> slower than the JRE implementation. If
+ * you need to do character level manipulation, you are strongly advised to
+ * use a char[] directly.
+ */
public void setCharAt(int index, char x) {
- builder.setCharAt(index, x);
+ replace(index, index + 1, String.valueOf(x));
}
public void setLength(int newLength) {
- builder.setLength(newLength);
+ int oldLength = length();
+ if (newLength < oldLength) {
+ delete(newLength, oldLength);
+ } else if (newLength > oldLength) {
+ append(new char[newLength - oldLength]);
+ }
}
public CharSequence subSequence(int start, int end) {
- return builder.subSequence(start, end);
+ return this.substring(start, end);
}
public String substring(int begin) {
- return builder.substring(begin);
+ return toString().substring(begin);
}
public String substring(int begin, int end) {
- return builder.substring(begin, end);
+ return toString().substring(begin, end);
}
@Override
public String toString() {
- return builder.toString();
+ return impl.toString(data);
}
public void trimToSize() {
- builder.trimToSize();
}
}
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
index 1a7416d..00f9d19 100644
--- a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
+++ b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
@@ -15,28 +15,24 @@
*/
package java.lang;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.impl.StringBufferImpl;
+
/**
- * A fast way to create strings using multiple appends. This
- * implementation is optimized for fast appends. Most methods will give expected
- * performance results, with the notable exception of
- * {@link #setCharAt(int, char)}, which is extremely slow and should be avoided
- * if possible.
+ * A fast way to create strings using multiple appends. This is implemented
+ * using a {@link StringBufferImpl} that is chosen with deferred binding.
+ *
+ * Most methods will give expected performance results. Exceptions are
+ * {@link #setCharAt(int, char)}, which is O(n), and {@link #length()}, which
+ * forces a {@link #toString()} and thus should not be used many times on the
+ * same <code>StringBuilder</code>.
+ *
+ * This class is an exact clone of {@link StringBuffer} except for the name. Any
+ * change made to one should be mirrored in the other.
*/
public class StringBuilder implements CharSequence {
-
- private static native String join(String[] stringArray) /*-{
- return stringArray.join('');
- }-*/;
-
- private static native String setLength(String[] stringArray, int length) /*-{
- stringArray.length = length;
- }-*/;
-
- private int arrayLen = 0;
-
- private String[] stringArray = new String[0];
-
- private int stringLength = 0;
+ private final StringBufferImpl impl = GWT.create(StringBufferImpl.class);
+ private final Object data = impl.createData();
public StringBuilder() {
}
@@ -58,73 +54,71 @@
}
public StringBuilder append(boolean x) {
- return append(String.valueOf(x));
+ impl.append(data, x);
+ return this;
}
public StringBuilder append(char x) {
- return append(String.valueOf(x));
+ impl.appendNonNull(data, String.valueOf(x));
+ return this;
}
public StringBuilder append(char[] x) {
- return append(String.valueOf(x));
+ impl.appendNonNull(data, String.valueOf(x));
+ return this;
}
public StringBuilder append(char[] x, int start, int len) {
- return append(String.valueOf(x, start, len));
+ impl.appendNonNull(data, String.valueOf(x, start, len));
+ return this;
}
public StringBuilder append(CharSequence x) {
- return append(x.toString());
+ impl.append(data, x);
+ return this;
}
public StringBuilder append(CharSequence x, int start, int end) {
- return append(x.subSequence(start, end));
+ if (x == null) {
+ x = "null";
+ }
+ impl.append(data, x.subSequence(start, end));
+ return this;
}
public StringBuilder append(double x) {
- return append(String.valueOf(x));
+ impl.append(data, x);
+ return this;
}
public StringBuilder append(float x) {
- return append(String.valueOf(x));
+ impl.append(data, x);
+ return this;
}
public StringBuilder append(int x) {
- return append(String.valueOf(x));
+ impl.append(data, x);
+ return this;
}
public StringBuilder append(long x) {
- return append(String.valueOf(x));
+ impl.appendNonNull(data, String.valueOf(x));
+ return this;
}
public StringBuilder append(Object x) {
- return append(String.valueOf(x));
+ impl.append(data, x);
+ return this;
}
- public StringBuilder append(String toAppend) {
- // Coerce to "null" if null.
- if (toAppend == null) {
- toAppend = "null";
- }
- int appendLength = toAppend.length();
- if (appendLength > 0) {
- stringArray[arrayLen++] = toAppend;
- stringLength += appendLength;
- /*
- * If we hit 1k elements, let's do a join to reduce the array size. This
- * number was arrived at experimentally through benchmarking.
- */
- if (arrayLen > 1024) {
- toString();
- // Preallocate the next 1024 (faster on FF).
- setLength(stringArray, 1024);
- }
- }
+ public StringBuilder append(String x) {
+ impl.append(data, x);
return this;
- };
+ }
- public StringBuilder append(StringBuffer x) {
- return append(String.valueOf(x));
+ public StringBuilder append(StringBuilder x) {
+ impl.append(data, x);
+ return this;
}
/**
@@ -156,7 +150,7 @@
}
public void getChars(int srcStart, int srcEnd, char[] dst, int dstStart) {
- String.__checkBounds(stringLength, srcStart, srcEnd);
+ String.__checkBounds(length(), srcStart, srcEnd);
String.__checkBounds(dst.length, dstStart, dstStart + (srcEnd - srcStart));
String s = toString();
while (srcStart < srcEnd) {
@@ -229,21 +223,11 @@
}
public int length() {
- return stringLength;
+ return impl.length(data);
}
public StringBuilder replace(int start, int end, String toInsert) {
- // Get the joined string.
- String s = toString();
-
- // Build a new buffer in pieces (will throw exceptions).
- stringArray = new String[] {
- s.substring(0, start), toInsert, s.substring(end)};
- arrayLen = 3;
-
- // Calculate the new string length.
- stringLength += toInsert.length() - (end - start);
-
+ impl.replace(data, start, end, toInsert);
return this;
}
@@ -257,7 +241,7 @@
}
public void setLength(int newLength) {
- int oldLength = stringLength;
+ int oldLength = length();
if (newLength < oldLength) {
delete(newLength, oldLength);
} else if (newLength > oldLength) {
@@ -279,18 +263,7 @@
@Override
public String toString() {
- /*
- * Normalize the array to exactly one element (even if it's completely
- * empty), so we can unconditionally grab the first element.
- */
- if (arrayLen != 1) {
- setLength(stringArray, arrayLen);
- String s = join(stringArray);
- // Create a new array to allow everything to get GC'd.
- stringArray = new String[] {s};
- arrayLen = 1;
- }
- return stringArray[0];
+ return impl.toString(data);
}
public void trimToSize() {
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 74a017b..c2646fa 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -515,6 +515,35 @@
assertTrue(b);
assertEquals("null true", test);
}
+
+ public void testInliningOrder() {
+ final StringBuffer buf = new StringBuffer();
+ class Misc {
+ // TODO(spoon) rename all this
+ String effect1() {
+ if (FALSE) {
+ return effect1();
+ }
+ buf.append("effect1");
+ return "effect2";
+ }
+ void effect2() {
+ if (FALSE) {
+ effect2();
+ }
+ buf.append("effect2");
+ }
+ void useBoth(Object x, Object y) {
+ StringBuffer b = buf;
+ b.append(x);
+ b.append(y);
+ }
+ }
+
+ Misc misc = new Misc();
+ misc.useBoth("blah", buf.toString());
+ assertEquals("blah", buf.toString());
+ }
public void testJavaScriptReservedWords() {
boolean delete = TRUE;
diff --git a/user/test/com/google/gwt/emultest/EmulSuite.java b/user/test/com/google/gwt/emultest/EmulSuite.java
index 6d37d9e..34266e4 100644
--- a/user/test/com/google/gwt/emultest/EmulSuite.java
+++ b/user/test/com/google/gwt/emultest/EmulSuite.java
@@ -25,6 +25,7 @@
import com.google.gwt.emultest.java.lang.LongTest;
import com.google.gwt.emultest.java.lang.ObjectTest;
import com.google.gwt.emultest.java.lang.ShortTest;
+import com.google.gwt.emultest.java.lang.StringBufferDefaultImplTest;
import com.google.gwt.emultest.java.lang.StringBufferTest;
import com.google.gwt.emultest.java.lang.StringTest;
import com.google.gwt.emultest.java.lang.SystemTest;
@@ -71,6 +72,7 @@
suite.addTestSuite(ObjectTest.class);
suite.addTestSuite(ShortTest.class);
suite.addTestSuite(StringBufferTest.class);
+ suite.addTestSuite(StringBufferDefaultImplTest.class);
suite.addTestSuite(StringTest.class);
suite.addTestSuite(SystemTest.class);
diff --git a/user/test/com/google/gwt/emultest/EmulSuiteUnknownAgent.gwt.xml b/user/test/com/google/gwt/emultest/EmulSuiteUnknownAgent.gwt.xml
new file mode 100644
index 0000000..718c824
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/EmulSuiteUnknownAgent.gwt.xml
@@ -0,0 +1,22 @@
+<!-- -->
+<!-- Copyright 2007 Google Inc. -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you -->
+<!-- may not use this file except in compliance with the License. You may -->
+<!-- may obtain a copy of the License at -->
+<!-- -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0 -->
+<!-- -->
+<!-- Unless required by applicable law or agreed to in writing, software -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS, -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -->
+<!-- implied. License for the specific language governing permissions and -->
+<!-- limitations under the License. -->
+
+<module>
+ <inherits name='com.google.gwt.emultest.EmulSuite'/>
+
+ <!-- Remove JRE deferred bindings, so that the default implementations can be tested -->
+ <replace-with class="com.google.gwt.core.client.impl.StringBufferImplArray">
+ <when-type-is class="com.google.gwt.core.client.impl.StringBufferImpl"/>
+ </replace-with>
+</module>
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringBufferBenchmark.java b/user/test/com/google/gwt/emultest/java/lang/StringBufferBenchmark.java
new file mode 100644
index 0000000..b1855a2
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/lang/StringBufferBenchmark.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.lang;
+
+import com.google.gwt.benchmarks.client.Benchmark;
+import com.google.gwt.benchmarks.client.IntRange;
+import com.google.gwt.benchmarks.client.Operator;
+import com.google.gwt.benchmarks.client.RangeField;
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * A {@link Benchmark} for {@link StringBuilder StringBuilders}. This includes
+ * a benchmark from Ray Cromwell that builds display commands in one of two
+ * ways. One version uses a StringBuilder, and the other uses raw pushes with
+ * JavaScript. Note that there is no actual DisplayList interface, because
+ * otherwise the benchmark might have some dynamic dispatch involved. By default
+ * this benchmarks only the standard <code>StringBuilder</code> and
+ * <code>StringBuffer</code>. To run the full suite, comment in the alternate
+ * version of {@link #appendKindsRange}.
+ */
+public class StringBufferBenchmark extends Benchmark {
+ /**
+ * The type of StringBuilder to use for a test.
+ */
+ protected enum SBType {
+ JS("raw JavaScrpt"), STRBLD("StringBuilder"), STRBUF("StringBuffer");
+
+ public String description;
+
+ private SBType(String description) {
+ this.description = description;
+ }
+ }
+
+ /**
+ * A DisplayList represented using a native JavaScript array, and updated via
+ * the JavaScript push() method.
+ */
+ @SuppressWarnings("unused")
+ private static class JSArrayDisplayList {
+ private JavaScriptObject jso = JavaScriptObject.createArray();
+
+ public void begin() {
+ jso = JavaScriptObject.createArray();
+ }
+
+ public native void cmd(String cmd) /*-{
+ this.@com.google.gwt.emultest.java.lang.StringBufferBenchmark$JSArrayDisplayList::jso.push(cmd, 0);
+ }-*/;
+
+ public native void cmd(String cmd, int a) /*-{
+ this.@com.google.gwt.emultest.java.lang.StringBufferBenchmark$JSArrayDisplayList::jso.push(cmd, 1, a);
+ }-*/;
+
+ public native void cmd(String cmd, int a, int b) /*-{
+ this.@com.google.gwt.emultest.java.lang.StringBufferBenchmark$JSArrayDisplayList::jso.push(cmd, 2, a, b);
+ }-*/;
+
+ public native void cmd(String cmd, int a, int b, int c) /*-{
+ this.@com.google.gwt.emultest.java.lang.StringBufferBenchmark$JSArrayDisplayList::jso.push(cmd, 3, a, b, c);
+ }-*/;
+
+ public native String end() /*-{
+ return this.@com.google.gwt.emultest.java.lang.StringBufferBenchmark$JSArrayDisplayList::jso.join('');
+ }-*/;
+
+ public void fill() {
+ cmd("F");
+ }
+
+ public void lineTo(int x, int y) {
+ cmd("L", 0, 0);
+ }
+
+ public void moveTo(int x, int y) {
+ cmd("M", 0, 0);
+ }
+
+ public void rotate(int angle) {
+ cmd("R", angle);
+ }
+
+ public void stroke() {
+ cmd("S");
+ }
+
+ public void translate(int x, int y) {
+ cmd("T", x, y);
+ }
+ }
+
+ /**
+ * A DisplayList represented as a {@link StringBuffer} of commands and
+ * arguments. To contrast, see {@link JSArrayDisplayList}.
+ */
+ @SuppressWarnings("unused")
+ private static class StringBufferDisplayList {
+ private StringBuffer strbld = new StringBuffer();
+
+ public void begin() {
+ strbld = new StringBuffer();
+ }
+
+ public void cmd(String cmd) {
+ strbld.append(cmd);
+ strbld.append(0);
+ }
+
+ public void cmd(String cmd, int a) {
+ strbld.append(cmd);
+ strbld.append(1);
+ strbld.append(a);
+ }
+
+ public void cmd(String cmd, int a, int b) {
+ strbld.append(cmd);
+ strbld.append(2);
+ strbld.append(a);
+ strbld.append(b);
+ }
+
+ public void cmd(String cmd, int a, int b, int c) {
+ strbld.append(cmd);
+ strbld.append(3);
+ strbld.append(a);
+ strbld.append(b);
+ strbld.append(c);
+ }
+
+ public String end() {
+ return strbld.toString();
+ }
+
+ public void fill() {
+ cmd("F");
+ }
+
+ public void lineTo(int x, int y) {
+ cmd("L", 0, 0);
+ }
+
+ public void moveTo(int x, int y) {
+ cmd("M", 0, 0);
+ }
+
+ public void rotate(int angle) {
+ cmd("R", angle);
+ }
+
+ public void stroke() {
+ cmd("S");
+ }
+
+ public void translate(int x, int y) {
+ cmd("T", x, y);
+ }
+ }
+
+ /**
+ * A DisplayList represented as a {@link StringBuilder} of commands and
+ * arguments. To contrast, see {@link JSArrayDisplayList}.
+ */
+ @SuppressWarnings("unused")
+ private static class StringBuilderDisplayList {
+ private StringBuilder strbld = new StringBuilder();
+
+ public void begin() {
+ strbld = new StringBuilder();
+ }
+
+ public void cmd(String cmd) {
+ strbld.append(cmd);
+ strbld.append(0);
+ }
+
+ public void cmd(String cmd, int a) {
+ strbld.append(cmd);
+ strbld.append(1);
+ strbld.append(a);
+ }
+
+ public void cmd(String cmd, int a, int b) {
+ strbld.append(cmd);
+ strbld.append(2);
+ strbld.append(a);
+ strbld.append(b);
+ }
+
+ public void cmd(String cmd, int a, int b, int c) {
+ strbld.append(cmd);
+ strbld.append(3);
+ strbld.append(a);
+ strbld.append(b);
+ strbld.append(c);
+ }
+
+ public String end() {
+ return strbld.toString();
+ }
+
+ public void fill() {
+ cmd("F");
+ }
+
+ public void lineTo(int x, int y) {
+ cmd("L", 0, 0);
+ }
+
+ public void moveTo(int x, int y) {
+ cmd("M", 0, 0);
+ }
+
+ public void rotate(int angle) {
+ cmd("R", angle);
+ }
+
+ public void stroke() {
+ cmd("S");
+ }
+
+ public void translate(int x, int y) {
+ cmd("T", x, y);
+ }
+ }
+
+ private static final String P_CLOSE = "</p>";
+ private static final String P_OPEN = "<p>";
+
+ public final SBType[] appendKindsRange = new SBType[] {
+ SBType.STRBUF, SBType.STRBLD};
+ public final IntRange appendTimesRange = new IntRange(32, 4096,
+ Operator.MULTIPLY, 2);
+ public final SBType[] displayListKindsRange = new SBType[] {
+ SBType.STRBUF, SBType.STRBLD, SBType.JS};
+ public final IntRange displayListTimesRange = new IntRange(32, 4096,
+ Operator.MULTIPLY, 2);
+
+ private volatile String abcde = "abcde";
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.emultest.EmulSuite";
+ }
+
+ /**
+ * Needed for JUnit.
+ */
+ public void testAppend() {
+ }
+
+ public void testAppend(@RangeField("appendTimesRange")
+ Integer times, @RangeField("appendKindsRange")
+ SBType sbtype) {
+ int outerTimes = 1;
+ switch (sbtype) {
+ case STRBLD:
+ for (int i = 0; i < outerTimes; i++) {
+ appendWithStringBuilder(times);
+ }
+ break;
+
+ case STRBUF:
+ for (int i = 0; i < outerTimes; i++) {
+ appendWithStringBuffer(times);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Needed for JUnit.
+ */
+ public void testDisplayList() {
+ }
+
+ /**
+ * Test creating a display list command sequence.
+ */
+ public void testDisplayList(@RangeField("displayListTimesRange")
+ Integer times, @RangeField("displayListKindsRange")
+ SBType sbtype) {
+ switch (sbtype) {
+ case JS:
+ drawWithJSArrayDisplayList(times);
+ break;
+
+ case STRBUF:
+ drawWithStringBufferDisplayList(times);
+ break;
+
+ case STRBLD:
+ drawWithStringBuilderDisplayList(times);
+ break;
+ }
+ }
+
+ private void appendWithStringBuffer(int times) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < times; i++) {
+ sb.append(P_OPEN);
+ sb.append(abcde);
+ sb.append(P_CLOSE);
+ }
+ pretendToUse(sb.toString().length());
+ }
+
+ private void appendWithStringBuilder(int times) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < times; i++) {
+ sb.append(P_OPEN);
+ sb.append(abcde);
+ sb.append(P_CLOSE);
+ }
+ pretendToUse(sb.toString().length());
+ }
+
+ /**
+ * Test drawing commands using JSArrayDisplayList.
+ */
+ private void drawWithJSArrayDisplayList(int times) {
+ JSArrayDisplayList dl = new JSArrayDisplayList();
+ dl.begin();
+ for (int i = 0; i < times; i++) {
+ // draw a box
+ dl.translate(50, 50);
+ dl.rotate(45);
+ dl.moveTo(0, 0);
+ dl.lineTo(100, 0);
+ dl.lineTo(100, 100);
+ dl.lineTo(0, 100);
+ dl.lineTo(0, 0);
+ dl.stroke();
+ dl.fill();
+ }
+ pretendToUse(dl.end().length());
+ }
+
+ /**
+ * Test drawing commands using {@link StringBufferDisplayList}.
+ */
+ private void drawWithStringBufferDisplayList(int times) {
+ final StringBufferDisplayList dl = new StringBufferDisplayList();
+ dl.begin();
+ for (int i = 0; i < times; i++) {
+ // draw a box
+ dl.translate(50, 50);
+ dl.rotate(45);
+ dl.moveTo(0, 0);
+ dl.lineTo(100, 0);
+ dl.lineTo(100, 100);
+ dl.lineTo(0, 100);
+ dl.lineTo(0, 0);
+ dl.stroke();
+ dl.fill();
+ }
+ pretendToUse(dl.end().length());
+ }
+
+ /**
+ * Test drawing commands using {@link StringBufferDisplayList}.
+ */
+ private void drawWithStringBuilderDisplayList(int times) {
+ final StringBuilderDisplayList dl = new StringBuilderDisplayList();
+ dl.begin();
+ for (int i = 0; i < times; i++) {
+ // draw a box
+ dl.translate(50, 50);
+ dl.rotate(45);
+ dl.moveTo(0, 0);
+ dl.lineTo(100, 0);
+ dl.lineTo(100, 100);
+ dl.lineTo(0, 100);
+ dl.lineTo(0, 0);
+ dl.stroke();
+ dl.fill();
+ }
+ pretendToUse(dl.end().length());
+ }
+
+ /**
+ * Make a value appear to be live, so that dead code elimination will not
+ * strip it out.
+ */
+ private native void pretendToUse(int x) /*-{
+ $wnd.completelyUselessField = x
+ }-*/;
+}
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringBufferDefaultImplTest.java b/user/test/com/google/gwt/emultest/java/lang/StringBufferDefaultImplTest.java
new file mode 100644
index 0000000..75f9b02
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/lang/StringBufferDefaultImplTest.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java.lang;
+
+/**
+ * The same as {@link StringBufferTest} except that it uses the default string
+ * buffer implementation instead of the browser-specific deferred binding.
+ */
+public class StringBufferDefaultImplTest extends StringBufferTest {
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.emultest.EmulSuiteUnknownAgent";
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringBufferImplBenchmark.java b/user/test/com/google/gwt/emultest/java/lang/StringBufferImplBenchmark.java
new file mode 100644
index 0000000..6c06e0e
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java/lang/StringBufferImplBenchmark.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.emultest.java.lang;
+
+import com.google.gwt.benchmarks.client.Benchmark;
+import com.google.gwt.benchmarks.client.IntRange;
+import com.google.gwt.benchmarks.client.IterationTimeLimit;
+import com.google.gwt.benchmarks.client.Operator;
+import com.google.gwt.benchmarks.client.RangeField;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.impl.StringBufferImpl;
+import com.google.gwt.core.client.impl.StringBufferImplAppend;
+import com.google.gwt.core.client.impl.StringBufferImplArray;
+import com.google.gwt.core.client.impl.StringBufferImplConcat;
+import com.google.gwt.core.client.impl.StringBufferImplPush;
+
+/**
+ * Tests StringBuilder impl directly against each other. Useful when profiling
+ * browser behavior.
+ */
+public class StringBufferImplBenchmark extends Benchmark {
+
+ /**
+ * The type of StringBuilder to use for a test.
+ */
+ protected enum SBType {
+ APPEND("Append"), ARRAY("Array"), CONCAT("Concat"), PUSH("Push");
+
+ public String description;
+
+ private SBType(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static volatile String result;
+
+ private static volatile Object[] stashSomeGarbage;
+
+ static {
+ if (GWT.isClient()) {
+ stashSomeGarbage = new Object[10000];
+ for (int i = 0; i < stashSomeGarbage.length; ++i) {
+ stashSomeGarbage[i] = new Object();
+ }
+ }
+ }
+
+ final SBType[] appendKindsRange = new SBType[] {
+ SBType.APPEND, SBType.ARRAY, SBType.CONCAT, SBType.PUSH};
+
+ final IntRange manyTimesRange = new IntRange(32, 8192, Operator.MULTIPLY, 2);
+
+ final IntRange singleTimesRange = new IntRange(32, 8192, Operator.MULTIPLY, 2);
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.emultest.EmulSuite";
+ }
+
+ public void testManyAppends() {
+ }
+
+ @IterationTimeLimit(0)
+ public void testManyAppends(@RangeField("manyTimesRange")
+ Integer times, @RangeField("appendKindsRange")
+ SBType sbtype) {
+ int number = (int) Math.sqrt(times.intValue());
+ switch (sbtype) {
+ case APPEND:
+ for (int i = 0; i < number; ++i) {
+ result = doAppend(number);
+ result = null;
+ }
+ break;
+ case ARRAY:
+ for (int i = 0; i < number; ++i) {
+ result = doArray(number);
+ result = null;
+ }
+ break;
+ case CONCAT:
+ for (int i = 0; i < number; ++i) {
+ result = doConcat(number);
+ result = null;
+ }
+ break;
+ case PUSH:
+ for (int i = 0; i < number; ++i) {
+ result = doPush(number);
+ result = null;
+ }
+ break;
+ }
+ }
+
+ public void testSingleAppend() {
+ }
+
+ @IterationTimeLimit(0)
+ public void testSingleAppend(@RangeField("singleTimesRange")
+ Integer times, @RangeField("appendKindsRange")
+ SBType sbtype) {
+ int number = times;
+ switch (sbtype) {
+ case APPEND:
+ result = doAppend(number);
+ break;
+ case ARRAY:
+ result = doArray(number);
+ break;
+ case CONCAT:
+ result = doConcat(number);
+ break;
+ case PUSH:
+ result = doPush(number);
+ break;
+ }
+ result = null;
+ }
+
+ private String doAppend(int limit) {
+ StringBufferImpl impl = new StringBufferImplAppend();
+ Object data = impl.createData();
+ for (int i = 0; i < limit; i++) {
+ impl.appendNonNull(data, "hello");
+ }
+ return impl.toString(data);
+ }
+
+ private String doArray(int limit) {
+ StringBufferImpl impl = new StringBufferImplArray();
+ Object data = impl.createData();
+ for (int i = 0; i < limit; i++) {
+ impl.appendNonNull(data, "hello");
+ }
+ return impl.toString(data);
+ }
+
+ private String doConcat(int limit) {
+ StringBufferImpl impl = new StringBufferImplConcat();
+ Object data = impl.createData();
+ for (int i = 0; i < limit; i++) {
+ impl.appendNonNull(data, "hello");
+ }
+ return impl.toString(data);
+ }
+
+ private String doPush(int limit) {
+ StringBufferImpl impl = new StringBufferImplPush();
+ Object data = impl.createData();
+ for (int i = 0; i < limit; i++) {
+ impl.appendNonNull(data, "hello");
+ }
+ return impl.toString(data);
+ }
+}
diff --git a/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java b/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java
index 27b568a..b5e2241 100644
--- a/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/StringBufferTest.java
@@ -69,13 +69,12 @@
x.append((CharSequence) "abc");
assertEquals("abc", x.toString());
x = new StringBuffer();
- x.append((CharSequence) "abcde", 2, 3);
+ x.append("abcde", 2, 3);
assertEquals("c", x.toString());
}
/**
- * Check that capacity methods are present, even though
- * they do nothing.
+ * Check that capacity methods are present, even though they do nothing.
*/
public void testCapacity() {
StringBuffer buf = new StringBuffer();
@@ -170,7 +169,7 @@
x.insert(2, (CharSequence) "abcde");
assertEquals("01abcde234", x.toString());
x = new StringBuffer("01234");
- x.insert(2, (CharSequence) "abcde", 2, 4);
+ x.insert(2, "abcde", 2, 4);
assertEquals("01cd234", x.toString());
x = new StringBuffer("!");
x.insert(1, C.FALSE_VALUE);
@@ -330,16 +329,16 @@
assertEquals("abcde", bld.toString());
bld = new StringBuilder();
- bld.append((CharSequence) "abcde", 2, 4);
+ bld.append("abcde", 2, 4);
assertEquals("cd", bld.toString());
bld = new StringBuilder();
- bld.append(1.0);
- assertEquals("1.0", bld.toString());
+ bld.append(1.5);
+ assertEquals("1.5", bld.toString());
bld = new StringBuilder();
- bld.append(1.0F);
- assertEquals("1.0", bld.toString());
+ bld.append(1.5F);
+ assertEquals("1.5", bld.toString());
bld = new StringBuilder();
bld.append(5);
@@ -420,7 +419,7 @@
assertEquals("01abcde234", bld.toString());
bld = new StringBuilder("01234");
- bld.insert(2, (CharSequence) "abcde", 2, 4);
+ bld.insert(2, "abcde", 2, 4);
assertEquals("01cd234", bld.toString());
bld = new StringBuilder("01234");