Skip to content

Commit ec8b1b9

Browse files
Remove serialization workarounds for ie6/7 and rhino (#9578)
1 parent f439c5e commit ec8b1b9

File tree

2 files changed

+45
-177
lines changed

2 files changed

+45
-177
lines changed

user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java

Lines changed: 40 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -42,78 +42,6 @@
4242
public final class ServerSerializationStreamWriter extends
4343
AbstractSerializationStreamWriter {
4444

45-
/**
46-
* Builds a string that evaluates into an array containing the given elements.
47-
* This class exists to work around a bug in IE6/7 that limits the size of
48-
* array literals.
49-
*/
50-
public static class LengthConstrainedArray {
51-
public static final int MAXIMUM_ARRAY_LENGTH = 1 << 15;
52-
private static final String POSTLUDE = "])";
53-
private static final String PRELUDE = "].concat([";
54-
55-
private final StringBuffer buffer;
56-
private int count = 0;
57-
private boolean needsComma = false;
58-
private int total = 0;
59-
private boolean javascript = false;
60-
61-
public LengthConstrainedArray() {
62-
buffer = new StringBuffer();
63-
}
64-
65-
public LengthConstrainedArray(int capacityGuess) {
66-
buffer = new StringBuffer(capacityGuess);
67-
}
68-
69-
public void addToken(CharSequence token) {
70-
total++;
71-
if (count++ == MAXIMUM_ARRAY_LENGTH) {
72-
if (total == MAXIMUM_ARRAY_LENGTH + 1) {
73-
buffer.append(PRELUDE);
74-
javascript = true;
75-
} else {
76-
buffer.append("],[");
77-
}
78-
count = 0;
79-
needsComma = false;
80-
}
81-
82-
if (needsComma) {
83-
buffer.append(",");
84-
} else {
85-
needsComma = true;
86-
}
87-
88-
buffer.append(token);
89-
}
90-
91-
public void addEscapedToken(String token) {
92-
addToken(escapeString(token, true, this));
93-
}
94-
95-
public void addToken(int i) {
96-
addToken(String.valueOf(i));
97-
}
98-
99-
public boolean isJavaScript() {
100-
return javascript;
101-
}
102-
103-
public void setJavaScript(boolean javascript) {
104-
this.javascript = javascript;
105-
}
106-
107-
@Override
108-
public String toString() {
109-
if (total > MAXIMUM_ARRAY_LENGTH) {
110-
return "[" + buffer.toString() + POSTLUDE;
111-
} else {
112-
return "[" + buffer.toString() + "]";
113-
}
114-
}
115-
}
116-
11745
/**
11846
* Enumeration used to provided typed instance writers.
11947
*/
@@ -337,14 +265,6 @@ abstract void write(ServerSerializationStreamWriter stream, Object instance)
337265

338266
private static final char NON_BREAKING_HYPHEN = '\u2011';
339267

340-
/**
341-
* Maximum length of a string node in RPC responses, not including surrounding
342-
* quote characters (2 ^ 16 - 1) = 65535.
343-
* This exists to work around a Rhino parser bug in the hosted mode client
344-
* that limits string node lengths to 64KB.
345-
*/
346-
private static final int MAX_STRING_NODE_LENGTH = 0xFFFF;
347-
348268
static {
349269
/*
350270
* NOTE: The JS VM in IE6 & IE7 do not interpret \v correctly. They convert
@@ -382,36 +302,15 @@ abstract void write(ServerSerializationStreamWriter stream, Object instance)
382302
CLASS_TO_VALUE_WRITER.put(String.class, ValueWriter.STRING);
383303
}
384304

385-
/**
386-
* This method takes a string and outputs a JavaScript string literal. The
387-
* data is surrounded with quotes, and any contained characters that need to
388-
* be escaped are mapped onto their escape sequence.
389-
*
390-
* Assumptions: We are targeting a version of JavaScript that that is later
391-
* than 1.3 that supports unicode strings.
392-
*/
393-
public static String escapeString(String toEscape) {
394-
return escapeString(toEscape, false, null);
395-
}
396-
397305
/**
398306
* This method takes a string and outputs a JavaScript string literal. The
399307
* data is surrounded with quotes, and any contained characters that need to
400308
* be escaped are mapped onto their escape sequence.
401309
*
402-
* This splits strings into 64KB chunks to workaround an issue with the hosted mode client where
403-
* the Rhino parser can't handle string nodes larger than 64KB, e.g. {@code "longstring"} is
404-
* converted to {@code "long" + "string"}.
405-
*
406310
* Assumptions: We are targeting a version of JavaScript that that is later
407311
* than 1.3 that supports unicode strings.
408312
*/
409-
public static String escapeStringSplitNodes(String toEscape) {
410-
return escapeString(toEscape, true, null);
411-
}
412-
413-
private static String escapeString(String toEscape, boolean splitNodes,
414-
LengthConstrainedArray array) {
313+
public static String escapeString(String toEscape) {
415314
// Since escaped characters will increase the output size, allocate extra room to start.
416315
int length = toEscape.length();
417316
int capacityIncrement = Math.max(length, 16);
@@ -422,30 +321,14 @@ private static String escapeString(String toEscape, boolean splitNodes,
422321
int i = 0;
423322
while (i < length) {
424323

425-
// Add one segment at a time, up to maxNodeLength characters. Note this always leave room
426-
// for at least 6 characters at the end (maximum unicode escaped character size).
427-
int maxSegmentVectorSize = splitNodes
428-
? (charVector.getSize() + MAX_STRING_NODE_LENGTH - 5)
429-
: Integer.MAX_VALUE;
430-
431-
while (i < length && charVector.getSize() < maxSegmentVectorSize) {
324+
while (i < length) {
432325
char c = toEscape.charAt(i++);
433326
if (needsUnicodeEscape(c)) {
434327
unicodeEscape(c, charVector);
435328
} else {
436329
charVector.add(c);
437330
}
438331
}
439-
440-
// If there's another segment left, insert a '+' operator.
441-
if (splitNodes && i < length) {
442-
charVector.add(JS_QUOTE_CHAR);
443-
charVector.add('+');
444-
charVector.add(JS_QUOTE_CHAR);
445-
if (array != null) {
446-
array.setJavaScript(true);
447-
}
448-
}
449332
}
450333

451334
charVector.add(JS_QUOTE_CHAR);
@@ -502,7 +385,7 @@ private static Class<?> getClassForSerialization(Object instance) {
502385
* <li>Total Characters Escaped: 2082</li></li>
503386
* </ul> </li>
504387
* </ol>
505-
*
388+
*
506389
* @param ch character to check
507390
* @return <code>true</code> if the character requires the \\uXXXX unicode
508391
* character escape
@@ -557,7 +440,7 @@ private static boolean needsUnicodeEscape(char ch) {
557440
* Writes a safe escape sequence for a character. Some characters have a short
558441
* form, such as \n for U+000D, while others are represented as \\xNN or
559442
* \\uNNNN.
560-
*
443+
*
561444
* @param ch character to unicode escape
562445
* @param charVector char vector to receive the unicode escaped representation
563446
*/
@@ -610,7 +493,7 @@ public void serializeValue(Object value, Class<?> type)
610493
/**
611494
* Build an array of JavaScript string literals that can be decoded by the
612495
* client via the eval function.
613-
*
496+
*
614497
* NOTE: We build the array in reverse so the client can simply use the pop
615498
* function to remove the next item from the list.
616499
*/
@@ -620,14 +503,14 @@ public String toString() {
620503
// We take a guess at how big to make to buffer to avoid numerous resizes.
621504
//
622505
int capacityGuess = 2 * tokenListCharCount + 2 * tokenList.size();
623-
LengthConstrainedArray stream = new LengthConstrainedArray(capacityGuess);
624-
writePayload(stream);
625-
writeStringTable(stream);
626-
writeHeader(stream);
506+
StringBuffer buffer = new StringBuffer(capacityGuess);
507+
writePayload(buffer);
508+
writeStringTable(buffer);
509+
writeHeader(buffer);
627510

628-
return stream.toString();
511+
return "[" + buffer.toString() + "]";
629512
}
630-
513+
631514
@Override
632515
public void writeLong(long value) {
633516
if (getVersion() == SERIALIZATION_STREAM_MIN_VERSION) {
@@ -702,7 +585,7 @@ protected void serialize(Object instance, String typeSignature)
702585
* Serialize an instance that is an array. Will default to serializing the
703586
* instance as an Object vector if the instance is not a vector of primitives,
704587
* Strings or Object.
705-
*
588+
*
706589
* @param instanceClass
707590
* @param instance
708591
* @throws SerializationException
@@ -735,14 +618,14 @@ private void serializeClass(Object instance, Class<?> instanceClass)
735618
List<Field> serverFields = new ArrayList<Field>();
736619
for (Field declField : serializableFields) {
737620
assert (declField != null);
738-
621+
739622
// Identify server-only fields
740623
if (!clientFieldNames.contains(declField.getName())) {
741624
serverFields.add(declField);
742625
continue;
743626
}
744627
}
745-
628+
746629
// Serialize the server-only fields into a byte array and encode as a String
747630
try {
748631
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -765,7 +648,7 @@ private void serializeClass(Object instance, Class<?> instanceClass)
765648
throw new SerializationException(e);
766649
}
767650
}
768-
651+
769652
// Write the client-visible field data
770653
for (Field declField : serializableFields) {
771654
if ((clientFieldNames != null) && !clientFieldNames.contains(declField.getName())) {
@@ -861,29 +744,39 @@ private void serializeWithCustomSerializer(Class<?> customSerializer,
861744
* Notice that the field are written in reverse order that the client can just
862745
* pop items out of the stream.
863746
*/
864-
private void writeHeader(LengthConstrainedArray stream) {
865-
stream.addToken(getFlags());
866-
if (stream.isJavaScript() && getVersion() >= SERIALIZATION_STREAM_JSON_VERSION) {
867-
// Ensure we are not using the JSON supported version if stream is Javascript instead of JSON
868-
stream.addToken(SERIALIZATION_STREAM_JSON_VERSION - 1);
869-
} else {
870-
stream.addToken(getVersion());
871-
}
747+
private void writeHeader(StringBuffer buffer) {
748+
addToken(buffer, getFlags());
749+
addToken(buffer, getVersion());
872750
}
873751

874-
private void writePayload(LengthConstrainedArray stream) {
752+
private void writePayload(StringBuffer buffer) {
875753
ListIterator<String> tokenIterator = tokenList.listIterator(tokenList.size());
876754
while (tokenIterator.hasPrevious()) {
877-
stream.addToken(tokenIterator.previous());
755+
addToken(buffer, tokenIterator.previous());
878756
}
879757
}
880758

881-
private void writeStringTable(LengthConstrainedArray stream) {
882-
LengthConstrainedArray tableStream = new LengthConstrainedArray();
759+
private void writeStringTable(StringBuffer buffer) {
760+
StringBuffer tableBuffer = new StringBuffer();
883761
for (String s : getStringTable()) {
884-
tableStream.addEscapedToken(s);
762+
addEscapedToken(tableBuffer, s);
885763
}
886-
stream.addToken(tableStream.toString());
887-
stream.setJavaScript(stream.isJavaScript() || tableStream.isJavaScript());
764+
addToken(buffer, "[" + tableBuffer + "]");
765+
}
766+
767+
public void addToken(StringBuffer buffer, CharSequence token) {
768+
if (buffer.length() > 0) {
769+
buffer.append(",");
770+
}
771+
772+
buffer.append(token);
773+
}
774+
775+
public void addEscapedToken(StringBuffer buffer, String token) {
776+
addToken(buffer, escapeString(token));
777+
}
778+
779+
public void addToken(StringBuffer buffer, int i) {
780+
addToken(buffer, String.valueOf(i));
888781
}
889782
}

user/test/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriterTest.java

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ public void testEscapeString() {
3939
}
4040

4141
public void testEscapeStringSplitNodes() {
42-
String escaped = ServerSerializationStreamWriter.escapeStringSplitNodes("test");
42+
String escaped = ServerSerializationStreamWriter.escapeString("test");
4343
assertEquals("\"test\"", escaped);
4444
}
4545

4646
public void testEscapeStringSplitNodes_unicodeEscape() {
47-
String escaped = ServerSerializationStreamWriter.escapeStringSplitNodes(
47+
String escaped = ServerSerializationStreamWriter.escapeString(
4848
"测试" // Unicode characters
4949
+ "\"" // JS quote char
5050
+ "\\" // JS escape char
@@ -75,11 +75,11 @@ public void testEscapeStringSplitNodes_over64KB() {
7575
secondNodeBuilder.append('2');
7676
}
7777

78-
String escaped = ServerSerializationStreamWriter.escapeStringSplitNodes(
78+
String escaped = ServerSerializationStreamWriter.escapeString(
7979
firstNodeBuilder.toString() + secondNodeBuilder.toString());
8080

8181
assertEquals(
82-
"\"" + firstNodeBuilder.toString() + "\"+\"" + secondNodeBuilder.toString() + "\"",
82+
"\"" + firstNodeBuilder.toString() + secondNodeBuilder.toString() + "\"",
8383
escaped);
8484
}
8585

@@ -104,12 +104,11 @@ public void testEscapeStringSplitNodes_over64KBEscaped() {
104104
}
105105
String secondNode = secondNodeBuilder.toString();
106106

107-
String escaped = ServerSerializationStreamWriter.escapeStringSplitNodes(
107+
String escaped = ServerSerializationStreamWriter.escapeString(
108108
firstNodeBuilder.toString() + secondNode);
109109

110110
assertEquals(
111111
"\"" + firstNodeNoUnicode + "\\u2011" // first node (including escaped unicode character)
112-
+ "\"+\""
113112
+ secondNode + "\"", // second node
114113
escaped);
115114
}
@@ -122,28 +121,4 @@ public void testWritingRpcVersion8Message() {
122121
assertEquals("[\"NaN\",\"Infinity\",\"-Infinity\",[],0,8]", writer.toString());
123122
}
124123

125-
public void testVersion8Fallbacks() {
126-
StringBuilder longString = new StringBuilder(66000);
127-
for (int i = 0; i < 660000; i++) {
128-
longString.append("a");
129-
}
130-
131-
// Fallbacks to 7 if string gets concatenated
132-
ServerSerializationStreamWriter writer = new ServerSerializationStreamWriter(null, 8);
133-
writer.writeString(longString.toString());
134-
String encoded = writer.toString();
135-
assertEquals("7", encoded.substring(encoded.lastIndexOf(",") + 1, encoded.lastIndexOf("]")));
136-
137-
// Fallbacks to 7 if array size reached maximum
138-
int maxArrayLength =
139-
ServerSerializationStreamWriter.LengthConstrainedArray.MAXIMUM_ARRAY_LENGTH + 100;
140-
writer = new ServerSerializationStreamWriter(null, 8);
141-
for (int i = 0; i < maxArrayLength; i++) {
142-
writer.writeInt(i);
143-
}
144-
145-
encoded = writer.toString();
146-
assertEquals("7", encoded.substring(encoded.lastIndexOf(",") + 1, encoded.lastIndexOf("]")));
147-
}
148-
149124
}

0 commit comments

Comments
 (0)