1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 public class Main {
18   public static String smallString = generateString(100);
19   public static String mediumString = generateString(300);
20   public static String largeString = generateString(2000);
21 
generateString(int length)22   public static String generateString(int length) {
23     // Generate a string in the ASCII range that will
24     // use string compression.
25     StringBuilder sb = new StringBuilder();
26     for (int i = 0; i < length; i++) {
27       // Generate repeating alphabet.
28       sb.append(Character.valueOf((char)('a' + (i % 26))));
29     }
30     return sb.toString();
31   }
32 
assertIntEquals(int expected, int result)33   public static void assertIntEquals(int expected, int result) {
34     if (expected != result) {
35       throw new Error("Expected: " + expected + ", found: " + result);
36     }
37   }
38 
assertBooleanEquals(boolean expected, boolean result)39   public static void assertBooleanEquals(boolean expected, boolean result) {
40     if (expected != result) {
41       throw new Error("Expected: " + expected + ", found: " + result);
42     }
43   }
44 
assertCharEquals(char expected, char result)45   public static void assertCharEquals(char expected, char result) {
46     if (expected != result) {
47       throw new Error("Expected: " + expected + ", found: " + result);
48     }
49   }
50 
assertStringEquals(String expected, String result)51   public static void assertStringEquals(String expected, String result) {
52     if (!expected.equals(result)) {
53       throw new Error("Expected: " + expected + ", found: " + result);
54     }
55   }
56 
assertStringContains(String searchTerm, String result)57   public static void assertStringContains(String searchTerm, String result) {
58     if (result == null || !result.contains(searchTerm)) {
59       throw new Error("Search term: " + searchTerm + ", not found in: " + result);
60     }
61   }
62 
main(String[] args)63   public static void main(String[] args) {
64     stringEqualsSame();
65     stringArgumentNotNull("Foo");
66 
67     assertIntEquals(0, $opt$noinline$getStringLength(""));
68     assertIntEquals(3, $opt$noinline$getStringLength("abc"));
69     assertIntEquals(10, $opt$noinline$getStringLength("0123456789"));
70 
71     assertBooleanEquals(true, $opt$noinline$isStringEmpty(""));
72     assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc"));
73     assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789"));
74 
75     assertCharEquals('a', $opt$noinline$stringCharAt("a", 0));
76     assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0));
77     assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1));
78     assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2));
79     assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7));
80 
81     // Single character.
82     assertStringEquals("a", stringGetCharsAndBack("a"));
83     // Strings < 8 characters.
84     assertStringEquals("foobar", stringGetCharsAndBack("foobar"));
85     // Strings > 8 characters of various lengths.
86     assertStringEquals(smallString, stringGetCharsAndBack(smallString));
87     assertStringEquals(mediumString, stringGetCharsAndBack(mediumString));
88     assertStringEquals(largeString, stringGetCharsAndBack(largeString));
89 
90     // Get only a substring:
91     // Substring < 8 characters.
92     assertStringEquals(smallString.substring(5, 10), stringGetCharsRange(smallString, 5, 10, 0));
93     // Substring > 8 characters.
94     assertStringEquals(smallString.substring(7, 28), stringGetCharsRange(smallString, 7, 28, 0));
95 
96     // Get full string with offset in the char array.
97     assertStringEquals(smallString, stringGetCharsAndBackOffset(smallString, 17));
98 
99     // Get a substring with an offset in the char array.
100     // Substring < 8 characters.
101     assertStringEquals(smallString.substring(5, 10), stringGetCharsRange(smallString, 5, 10, 17));
102     // Substring > 8 characters.
103     assertStringEquals(smallString.substring(7, 28), stringGetCharsRange(smallString, 7, 28, 17));
104 
105     try {
106       $opt$noinline$stringCharAt("abc", -1);
107       throw new Error("Should throw SIOOB.");
108     } catch (StringIndexOutOfBoundsException sioob) {
109       assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
110       assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
111     }
112     try {
113       $opt$noinline$stringCharAt("abc", 3);
114       throw new Error("Should throw SIOOB.");
115     } catch (StringIndexOutOfBoundsException sioob) {
116       assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
117       assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
118     }
119     try {
120       $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE);
121       throw new Error("Should throw SIOOB.");
122     } catch (StringIndexOutOfBoundsException sioob) {
123       assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
124       assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
125     }
126 
127     assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7));
128     assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10));
129 
130     assertCharEquals('7', $opt$noinline$stringCharAtCatchPhiReturn("0123456789", 7));
131     assertCharEquals('\0', $opt$noinline$stringCharAtCatchPhiReturn("0123456789", 10));
132 
133     assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc"));
134     assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3));
135     try {
136       $opt$noinline$stringSumLeadingChars("abcdef", 7);
137       throw new Error("Should throw SIOOB.");
138     } catch (StringIndexOutOfBoundsException sioob) {
139       assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
140       assertStringContains("Main.$opt$noinline$stringSumLeadingChars",
141                            sioob.getStackTrace()[1].toString());
142     }
143     assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef"));
144     try {
145       $opt$noinline$stringSum4LeadingChars("abc");
146       throw new Error("Should throw SIOOB.");
147     } catch (StringIndexOutOfBoundsException sioob) {
148       assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
149       assertStringContains("Main.$opt$noinline$stringSum4LeadingChars",
150                            sioob.getStackTrace()[1].toString());
151     }
152   }
153 
154   /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) builder (after)
155   /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
156   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
157   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
158   /// CHECK-DAG:                    Return [<<Length>>]
159 
160   /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) builder (after)
161   /// CHECK-NOT:                    InvokeVirtual
162 
$opt$noinline$getStringLength(String s)163   static public int $opt$noinline$getStringLength(String s) {
164     return s.length();
165   }
166 
167   /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) builder (after)
168   /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
169   /// CHECK-DAG:  <<Const0:i\d+>>   IntConstant 0
170   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
171   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
172   /// CHECK-DAG:  <<IsEmpty:z\d+>>  Equal [<<Length>>,<<Const0>>]
173   /// CHECK-DAG:                    Return [<<IsEmpty>>]
174 
175   /// CHECK-START: boolean Main.$opt$noinline$isStringEmpty(java.lang.String) builder (after)
176   /// CHECK-NOT:                    InvokeVirtual
177 
$opt$noinline$isStringEmpty(String s)178   static public boolean $opt$noinline$isStringEmpty(String s) {
179     return s.isEmpty();
180   }
181 
182   /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) builder (after)
183   /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
184   /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
185   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
186   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
187   /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
188   /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
189   /// CHECK-DAG:                    Return [<<Char>>]
190 
191   /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) builder (after)
192   /// CHECK-NOT:                    InvokeVirtual
193 
$opt$noinline$stringCharAt(String s, int pos)194   static public char $opt$noinline$stringCharAt(String s, int pos) {
195     return s.charAt(pos);
196   }
197 
198   /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) builder (after)
199   /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
200   /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
201   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
202   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
203   /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
204   /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
205   /// CHECK-DAG:                    Return [{{(c|i)\d+}}]
206 
207   /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) builder (after)
208   /// CHECK-NOT:                    InvokeVirtual
209 
$opt$noinline$stringCharAtCatch(String s, int pos)210   static public char $opt$noinline$stringCharAtCatch(String s, int pos) {
211     try {
212       return s.charAt(pos);
213     } catch (StringIndexOutOfBoundsException ignored) {
214       return '\0';
215     }
216   }
217 
218   /// CHECK-START: char Main.$opt$noinline$stringCharAtCatchPhiReturn(java.lang.String, int) builder (after)
219   /// CHECK-DAG:  <<String:l\d+>>   ParameterValue
220   /// CHECK-DAG:  <<Pos:i\d+>>      ParameterValue
221   /// CHECK-DAG:  <<Int:i\d+>>      IntConstant 0
222   /// CHECK-DAG:  <<NullCk:l\d+>>   NullCheck [<<String>>]
223   /// CHECK-DAG:  <<Length:i\d+>>   ArrayLength [<<NullCk>>] is_string_length:true
224   /// CHECK-DAG:  <<Bounds:i\d+>>   BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
225   /// CHECK-DAG:  <<Char:c\d+>>     ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
226   /// CHECK-DAG:  <<Phi:i\d+>>      Phi [<<Char>>,<<Int>>]
227   /// CHECK-DAG:                    Return [<<Phi>>]
228 
229   /// CHECK-START: char Main.$opt$noinline$stringCharAtCatchPhiReturn(java.lang.String, int) instruction_simplifier (after)
230   /// CHECK-NOT:                    InvokeVirtual intrinsic:StringCharAt
231 
$opt$noinline$stringCharAtCatchPhiReturn(String s, int pos)232   static public char $opt$noinline$stringCharAtCatchPhiReturn(String s, int pos) {
233     char result;
234     try {
235       result = s.charAt(pos);
236     } catch (StringIndexOutOfBoundsException ignored) {
237       result = '\0';
238     }
239     return result;
240   }
241 
242   /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) builder (after)
243   /// CHECK-DAG:                    ArrayLength is_string_length:true
244   /// CHECK-DAG:                    ArrayLength is_string_length:true
245   /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
246   /// CHECK-DAG:                    ArrayGet is_string_char_at:true
247 
248   /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) builder (after)
249   /// CHECK-NOT:                    InvokeVirtual
250 
251   /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after)
252   /// CHECK-DAG:                    ArrayLength is_string_length:true
253   /// CHECK-NOT:                    ArrayLength is_string_length:true
254 
255   /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after)
256   /// CHECK-NOT:                    BoundsCheck
257 
$opt$noinline$stringSumChars(String s)258   static public int $opt$noinline$stringSumChars(String s) {
259     int sum = 0;
260     int len = s.length();
261     for (int i = 0; i < len; ++i) {
262       sum += s.charAt(i);
263     }
264     return sum;
265   }
266 
267   /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) builder (after)
268   /// CHECK-DAG:                    ArrayLength is_string_length:true
269   /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
270   /// CHECK-DAG:                    ArrayGet is_string_char_at:true
271 
272   /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) builder (after)
273   /// CHECK-NOT:                    InvokeVirtual
274 
275   /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
276   /// CHECK-DAG:                    Deoptimize env:[[{{[^\]]*}}]]
277 
278   /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
279   /// CHECK-NOT:                    BoundsCheck is_string_char_at:true
280 
$opt$noinline$stringSumLeadingChars(String s, int n)281   static public int $opt$noinline$stringSumLeadingChars(String s, int n) {
282     int sum = 0;
283     for (int i = 0; i < n; ++i) {
284       sum += s.charAt(i);
285     }
286     return sum;
287   }
288 
289   /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) builder (after)
290   /// CHECK-DAG:                    ArrayLength is_string_length:true
291   /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
292   /// CHECK-DAG:                    ArrayGet is_string_char_at:true
293   /// CHECK-DAG:                    ArrayLength is_string_length:true
294   /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
295   /// CHECK-DAG:                    ArrayGet is_string_char_at:true
296   /// CHECK-DAG:                    ArrayLength is_string_length:true
297   /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
298   /// CHECK-DAG:                    ArrayGet is_string_char_at:true
299   /// CHECK-DAG:                    ArrayLength is_string_length:true
300   /// CHECK-DAG:                    BoundsCheck is_string_char_at:true
301   /// CHECK-DAG:                    ArrayGet is_string_char_at:true
302 
303   /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) builder (after)
304   /// CHECK-NOT:                    InvokeVirtual
305 
306   /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
307   /// CHECK-DAG:                    Deoptimize env:[[{{[^\]]*}}]]
308 
309   /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
310   /// CHECK-NOT:                    BoundsCheck is_string_char_at:true
311 
$opt$noinline$stringSum4LeadingChars(String s)312   static public int $opt$noinline$stringSum4LeadingChars(String s) {
313     int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3);
314     return sum;
315   }
316 
317   /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
318   /// CHECK:      InvokeStaticOrDirect
319 
320   /// CHECK-START: boolean Main.stringEqualsSame() register (before)
321   /// CHECK:      <<Const1:i\d+>> IntConstant 1
322   /// CHECK:      Return [<<Const1>>]
323 
324   /// CHECK-START: boolean Main.stringEqualsSame() register (before)
325   /// CHECK-NOT:  InvokeStaticOrDirect
stringEqualsSame()326   public static boolean stringEqualsSame() {
327     return $inline$callStringEquals("obj", "obj");
328   }
329 
330   /// CHECK-START: boolean Main.stringEqualsNull() register (after)
331   /// CHECK:      <<Invoke:z\d+>> InvokeVirtual
332   /// CHECK:      Return [<<Invoke>>]
stringEqualsNull()333   public static boolean stringEqualsNull() {
334     String o = (String)myObject;
335     return $inline$callStringEquals(o, o);
336   }
337 
$inline$callStringEquals(String a, String b)338   public static boolean $inline$callStringEquals(String a, String b) {
339     return a.equals(b);
340   }
341 
342   /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
343   /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
344   /// CHECK-NOT:      test
345 
346   /// CHECK-START-X86_64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
347   /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
348   /// CHECK-NOT:      test
349 
350   /// CHECK-START-ARM: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
351   /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
352   // CompareAndBranchIfZero() may emit either CBZ or CMP+BEQ.
353   /// CHECK-NOT:      cbz
354   /// CHECK-NOT:      cmp {{r\d+}}, #0
355   // Terminate the scope for the CHECK-NOT search at the reference or length comparison,
356   // whichever comes first.
357   /// CHECK:          cmp {{r\d+}}, {{r\d+}}
358 
359   /// CHECK-START-ARM64: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after)
360   /// CHECK:          InvokeVirtual {{.*\.equals.*}} intrinsic:StringEquals
361   /// CHECK-NOT:      cbz
362   // Terminate the scope for the CHECK-NOT search at the reference or length comparison,
363   // whichever comes first.
364   /// CHECK:          cmp {{w.*,}} {{w.*|#.*}}
stringArgumentNotNull(Object obj)365   public static boolean stringArgumentNotNull(Object obj) {
366     obj.getClass();
367     return "foo".equals(obj);
368   }
369 
370   // Test is very brittle as it depends on the order we emit instructions.
371   /// CHECK-START-X86: boolean Main.stringArgumentIsString() disassembly (after)
372   /// CHECK:          InvokeVirtual intrinsic:StringEquals
373   /// CHECK:          test
374   /// CHECK:          jz/eq
375   // Check that we don't try to compare the classes.
376   /// CHECK-NOT:      mov
377   /// CHECK:          cmp
378 
379   // Test is very brittle as it depends on the order we emit instructions.
380   /// CHECK-START-X86_64: boolean Main.stringArgumentIsString() disassembly (after)
381   /// CHECK:          InvokeVirtual intrinsic:StringEquals
382   /// CHECK:          test
383   /// CHECK:          jz/eq
384   // Check that we don't try to compare the classes.
385   /// CHECK-NOT:      mov
386   /// CHECK:          cmp
387 
388   // Test is brittle as it depends on the class offset being 0.
389   /// CHECK-START-ARM: boolean Main.stringArgumentIsString() disassembly (after)
390   /// CHECK:          InvokeVirtual intrinsic:StringEquals
391   /// CHECK:          {{cbz|cmp}}
392   // Check that we don't try to compare the classes.
393   // The dissassembler currently explicitly emits the offset 0 but don't rely on it.
394   // We want to terminate the CHECK-NOT search after two CMPs, one for reference
395   // equality and one for length comparison but these may be emitted in different order,
396   // so repeat the check twice.
397   /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}]
398   /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}, #0]
399   /// CHECK:          cmp {{r\d+}}, {{r\d+}}
400   /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}]
401   /// CHECK-NOT:      ldr{{(|.w)}} {{r\d+}}, [{{r\d+}}, #0]
402   /// CHECK:          cmp {{r\d+}}, {{r\d+}}
403 
404   // Test is brittle as it depends on the class offset being 0.
405   /// CHECK-START-ARM64: boolean Main.stringArgumentIsString() disassembly (after)
406   /// CHECK:          InvokeVirtual intrinsic:StringEquals
407   /// CHECK:          cbz
408   // Check that we don't try to compare the classes.
409   // The dissassembler currently does not explicitly emits the offset 0 but don't rely on it.
410   // We want to terminate the CHECK-NOT search after two CMPs, one for reference
411   // equality and one for length comparison but these may be emitted in different order,
412   // so repeat the check twice.
413   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
414   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
415   /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
416   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}]
417   /// CHECK-NOT:      ldr {{w\d+}}, [{{x\d+}}, #0]
418   /// CHECK:          cmp {{w\d+}}, {{w\d+|#.*}}
stringArgumentIsString()419   public static boolean stringArgumentIsString() {
420     return "foo".equals(myString);
421   }
422 
423   static String myString;
424   static Object myObject;
425 
stringGetCharsAndBack(String src)426   public static String stringGetCharsAndBack(String src) {
427     char[] dst = new char[src.length()];
428     src.getChars(0, src.length(), dst, 0);
429     return new String(dst);
430   }
431 
stringGetCharsAndBackOffset(String src, int offset)432   public static String stringGetCharsAndBackOffset(String src, int offset) {
433     char[] dst = new char[src.length() + offset];
434     src.getChars(0, src.length(), dst, offset);
435     return new String(dst, offset, src.length());
436   }
437 
stringGetCharsRange(String src, int srcBegin, int srcEnd, int offset)438   public static String stringGetCharsRange(String src, int srcBegin, int srcEnd, int offset) {
439     char[] dst = new char[srcEnd - srcBegin + offset];
440     src.getChars(srcBegin, srcEnd, dst, offset);
441     return new String(dst, offset, srcEnd - srcBegin);
442   }
443 }
444