summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorJeff Hao <jeffhao@google.com>2014-01-15 13:49:50 -0800
committerJeff Hao <jeffhao@google.com>2015-04-27 18:54:52 -0700
commit848f70a3d73833fc1bf3032a9ff6812e429661d9 (patch)
treeb0349b3a40aab5a915af491b100659a5ca9fbbf6 /runtime
parentd14438f0c5071962be7fab572b54687d32d9d087 (diff)
downloadart-848f70a3d73833fc1bf3032a9ff6812e429661d9.zip
art-848f70a3d73833fc1bf3032a9ff6812e429661d9.tar.gz
art-848f70a3d73833fc1bf3032a9ff6812e429661d9.tar.bz2
Replace String CharArray with internal uint16_t array.
Summary of high level changes: - Adds compiler inliner support to identify string init methods - Adds compiler support (quick & optimizing) with new invoke code path that calls method off the thread pointer - Adds thread entrypoints for all string init methods - Adds map to verifier to log when receiver of string init has been copied to other registers. used by compiler and interpreter Change-Id: I797b992a8feb566f9ad73060011ab6f51eb7ce01
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Android.mk2
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S36
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S50
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S24
-rw-r--r--runtime/arch/quick_alloc_entrypoints.S6
-rw-r--r--runtime/arch/stub_test.cc21
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S71
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S69
-rw-r--r--runtime/asm_support.h11
-rw-r--r--runtime/class_linker.cc10
-rw-r--r--runtime/class_linker_test.cc7
-rw-r--r--runtime/debugger.cc2
-rw-r--r--runtime/entrypoints/quick/quick_alloc_entrypoints.cc38
-rw-r--r--runtime/entrypoints/quick/quick_entrypoints_list.h23
-rw-r--r--runtime/entrypoints_order_test.cc41
-rw-r--r--runtime/hprof/hprof.cc34
-rw-r--r--runtime/interpreter/interpreter_common.cc68
-rw-r--r--runtime/interpreter/interpreter_common.h2
-rw-r--r--runtime/interpreter/interpreter_goto_table_impl.cc18
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc18
-rw-r--r--runtime/jni_internal.cc58
-rw-r--r--runtime/jni_internal_test.cc8
-rw-r--r--runtime/mirror/class-inl.h4
-rw-r--r--runtime/mirror/class.cc4
-rw-r--r--runtime/mirror/class.h15
-rw-r--r--runtime/mirror/object-inl.h17
-rw-r--r--runtime/mirror/object.h7
-rw-r--r--runtime/mirror/object_test.cc11
-rw-r--r--runtime/mirror/string-inl.h141
-rw-r--r--runtime/mirror/string.cc112
-rw-r--r--runtime/mirror/string.h93
-rw-r--r--runtime/modifiers.h2
-rw-r--r--runtime/native/java_lang_Class.cc2
-rw-r--r--runtime/native/java_lang_String.cc74
-rw-r--r--runtime/native/java_lang_StringFactory.cc89
-rw-r--r--runtime/native/java_lang_StringFactory.h28
-rw-r--r--runtime/native/java_lang_reflect_Constructor.cc6
-rw-r--r--runtime/native/libcore_util_CharsetUtils.cc264
-rw-r--r--runtime/native/libcore_util_CharsetUtils.h28
-rw-r--r--runtime/quick/inline_method_analyser.h5
-rw-r--r--runtime/reflection.cc21
-rw-r--r--runtime/runtime.cc4
-rw-r--r--runtime/runtime.h9
-rw-r--r--runtime/thread.cc59
-rw-r--r--runtime/thread.h12
-rw-r--r--runtime/utf.cc9
-rw-r--r--runtime/utils_test.cc3
-rw-r--r--runtime/verifier/method_verifier.cc20
-rw-r--r--runtime/verifier/method_verifier.h16
-rw-r--r--runtime/verifier/register_line.cc13
-rw-r--r--runtime/verifier/register_line.h3
-rw-r--r--runtime/well_known_classes.cc131
-rw-r--r--runtime/well_known_classes.h34
53 files changed, 1548 insertions, 305 deletions
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 86201ba..240799e 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -124,6 +124,7 @@ LIBART_COMMON_SRC_FILES := \
native/java_lang_Object.cc \
native/java_lang_Runtime.cc \
native/java_lang_String.cc \
+ native/java_lang_StringFactory.cc \
native/java_lang_System.cc \
native/java_lang_Thread.cc \
native/java_lang_Throwable.cc \
@@ -136,6 +137,7 @@ LIBART_COMMON_SRC_FILES := \
native/java_lang_reflect_Method.cc \
native/java_lang_reflect_Proxy.cc \
native/java_util_concurrent_atomic_AtomicLong.cc \
+ native/libcore_util_CharsetUtils.cc \
native/org_apache_harmony_dalvik_ddmc_DdmServer.cc \
native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc \
native/sun_misc_Unsafe.cc \
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 599c22a..7488578 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -705,6 +705,22 @@ ENTRY \name
END \name
.endm
+// Macro to facilitate adding new allocation entrypoints.
+.macro FOUR_ARG_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
+ str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
+ .pad #16
+ .cfi_adjust_cfa_offset 16
+ bl \entrypoint
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ \return
+END \name
+.endm
+
ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
@@ -1188,8 +1204,7 @@ ENTRY art_quick_indexof
.cfi_rel_offset r11, 8
.cfi_rel_offset lr, 12
ldr r3, [r0, #MIRROR_STRING_COUNT_OFFSET]
- ldr r12, [r0, #MIRROR_STRING_OFFSET_OFFSET]
- ldr r0, [r0, #MIRROR_STRING_VALUE_OFFSET]
+ add r0, #MIRROR_STRING_VALUE_OFFSET
/* Clamp start to [0..count] */
cmp r2, #0
@@ -1199,10 +1214,6 @@ ENTRY art_quick_indexof
it gt
movgt r2, r3
- /* Build a pointer to the start of string data */
- add r0, #MIRROR_CHAR_ARRAY_DATA_OFFSET
- add r0, r0, r12, lsl #1
-
/* Save a copy in r12 to later compute result */
mov r12, r0
@@ -1308,12 +1319,10 @@ ENTRY art_quick_string_compareto
.cfi_rel_offset r12, 24
.cfi_rel_offset lr, 28
- ldr r4, [r2, #MIRROR_STRING_OFFSET_OFFSET]
- ldr r9, [r1, #MIRROR_STRING_OFFSET_OFFSET]
ldr r7, [r2, #MIRROR_STRING_COUNT_OFFSET]
ldr r10, [r1, #MIRROR_STRING_COUNT_OFFSET]
- ldr r2, [r2, #MIRROR_STRING_VALUE_OFFSET]
- ldr r1, [r1, #MIRROR_STRING_VALUE_OFFSET]
+ add r2, #MIRROR_STRING_VALUE_OFFSET
+ add r1, #MIRROR_STRING_VALUE_OFFSET
/*
* At this point, we have:
@@ -1328,15 +1337,12 @@ ENTRY art_quick_string_compareto
it ls
movls r10, r7
- /* Now, build pointers to the string data */
- add r2, r2, r4, lsl #1
- add r1, r1, r9, lsl #1
/*
* Note: data pointers point to previous element so we can use pre-index
* mode with base writeback.
*/
- add r2, #MIRROR_CHAR_ARRAY_DATA_OFFSET-2 @ offset to contents[-1]
- add r1, #MIRROR_CHAR_ARRAY_DATA_OFFSET-2 @ offset to contents[-1]
+ subs r2, #2 @ offset to contents[-1]
+ subs r1, #2 @ offset to contents[-1]
/*
* At this point we have:
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 1e78877..1d316fc 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1264,7 +1264,7 @@ END art_quick_aput_obj
.macro ONE_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
mov x1, xSELF // pass Thread::Current
bl \entrypoint // (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -1276,7 +1276,7 @@ END \name
.macro TWO_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
mov x2, xSELF // pass Thread::Current
bl \entrypoint // (uint32_t type_idx, Method* method, Thread*)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -1284,11 +1284,11 @@ ENTRY \name
END \name
.endm
-// Macro to facilitate adding new array allocation entrypoints.
+// Macro to facilitate adding new allocation entrypoints.
.macro THREE_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
mov x3, xSELF // pass Thread::Current
bl \entrypoint
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
@@ -1296,6 +1296,19 @@ ENTRY \name
END \name
.endm
+// Macro to facilitate adding new allocation entrypoints.
+.macro FOUR_ARG_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
+ mov x4, xSELF // pass Thread::Current
+ bl \entrypoint //
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ \return
+ DELIVER_PENDING_EXCEPTION
+END \name
+.endm
+
// Macros taking opportunity of code similarities for downcalls with referrer.
.macro ONE_ARG_REF_DOWNCALL name, entrypoint, return
.extern \entrypoint
@@ -1725,8 +1738,7 @@ END art_quick_deoptimize
*/
ENTRY art_quick_indexof
ldr w3, [x0, #MIRROR_STRING_COUNT_OFFSET]
- ldr w4, [x0, #MIRROR_STRING_OFFSET_OFFSET]
- ldr w0, [x0, #MIRROR_STRING_VALUE_OFFSET] // x0 ?
+ add x0, x0, #MIRROR_STRING_VALUE_OFFSET
/* Clamp start to [0..count] */
cmp w2, #0
@@ -1734,10 +1746,6 @@ ENTRY art_quick_indexof
cmp w2, w3
csel w2, w3, w2, gt
- /* Build a pointer to the start of the string data */
- add x0, x0, #MIRROR_CHAR_ARRAY_DATA_OFFSET
- add x0, x0, x4, lsl #1
-
/* Save a copy to compute result */
mov x5, x0
@@ -1829,17 +1837,15 @@ ENTRY art_quick_string_compareto
ret
1: // Different string objects.
- ldr w6, [x2, #MIRROR_STRING_OFFSET_OFFSET]
- ldr w5, [x1, #MIRROR_STRING_OFFSET_OFFSET]
ldr w4, [x2, #MIRROR_STRING_COUNT_OFFSET]
ldr w3, [x1, #MIRROR_STRING_COUNT_OFFSET]
- ldr w2, [x2, #MIRROR_STRING_VALUE_OFFSET]
- ldr w1, [x1, #MIRROR_STRING_VALUE_OFFSET]
+ add x2, x2, #MIRROR_STRING_VALUE_OFFSET
+ add x1, x1, #MIRROR_STRING_VALUE_OFFSET
/*
- * Now: CharArray* Offset Count
- * first arg x2 w6 w4
- * second arg x1 w5 w3
+ * Now: Data* Count
+ * first arg x2 w4
+ * second arg x1 w3
*/
// x0 := str1.length(w4) - str2.length(w3). ldr zero-extended w3/w4 into x3/x4.
@@ -1847,16 +1853,6 @@ ENTRY art_quick_string_compareto
// Min(count1, count2) into w3.
csel x3, x3, x4, ge
- // Build pointer into string data.
-
- // Add offset in array (substr etc.) (sign extend and << 1).
- add x2, x2, w6, sxtw #1
- add x1, x1, w5, sxtw #1
-
- // Add offset in CharArray to array.
- add x2, x2, #MIRROR_CHAR_ARRAY_DATA_OFFSET
- add x1, x1, #MIRROR_CHAR_ARRAY_DATA_OFFSET
-
// TODO: Tune this value.
// Check for long string, do memcmp16 for them.
cmp w3, #28 // Constant from arm32.
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 356a145..af9ef1f 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -993,6 +993,18 @@ END \name
.endm
// Macro to facilitate adding new allocation entrypoints.
+.macro ONE_ARG_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a1, rSELF # pass Thread::Current
+ jal \entrypoint
+ move $a2, $sp # pass $sp
+ \return
+END \name
+.endm
+
.macro TWO_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
@@ -1013,6 +1025,18 @@ ENTRY \name
END \name
.endm
+.macro FOUR_ARG_DOWNCALL name, entrypoint, return
+ .extern \entrypoint
+ENTRY \name
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ sw rSELF, 16($sp) # pass Thread::Current
+ jal \entrypoint
+ sw $sp, 20($sp) # pass $sp
+ \return
+END \name
+.endm
+
// Generate the allocation entrypoints for each allocator.
GENERATE_ALL_ALLOC_ENTRYPOINTS
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index 037c26e..eaee19b 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -35,6 +35,12 @@ THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArr
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+// Called by managed code to allocate a string from bytes
+FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+// Called by managed code to allocate a string from chars
+THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars\c_suffix, artAllocStringFromCharsFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+// Called by managed code to allocate a string from string
+ONE_ARG_DOWNCALL art_quick_alloc_string_from_string\c_suffix, artAllocStringFromStringFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
.endm
.macro GENERATE_ALL_ALLOC_ENTRYPOINTS
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 0d9a888..de7804f 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1229,32 +1229,15 @@ TEST_F(StubTest, StringCompareTo) {
"aacaacaacaacaacaacaacaacaacaacaacaac", // This one's over.
"aacaacaacaacaacaacaacaacaacaacaacaaca" }; // As is this one. We need a separate one to
// defeat object-equal optimizations.
- static constexpr size_t kBaseStringCount = arraysize(c);
- static constexpr size_t kStringCount = 2 * kBaseStringCount;
+ static constexpr size_t kStringCount = arraysize(c);
StackHandleScope<kStringCount> hs(self);
Handle<mirror::String> s[kStringCount];
- for (size_t i = 0; i < kBaseStringCount; ++i) {
+ for (size_t i = 0; i < kStringCount; ++i) {
s[i] = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), c[i]));
}
- RandGen r(0x1234);
-
- for (size_t i = kBaseStringCount; i < kStringCount; ++i) {
- s[i] = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), c[i - kBaseStringCount]));
- int32_t length = s[i]->GetLength();
- if (length > 1) {
- // Set a random offset and length.
- int32_t new_offset = 1 + (r.next() % (length - 1));
- int32_t rest = length - new_offset - 1;
- int32_t new_length = 1 + (rest > 0 ? r.next() % rest : 0);
-
- s[i]->SetField32<false>(mirror::String::CountOffset(), new_length);
- s[i]->SetField32<false>(mirror::String::OffsetOffset(), new_offset);
- }
- }
-
// TODO: wide characters
// Matrix of expectations. First component is first parameter. Note we only check against the
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 55e3dff..6ebeba3 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -685,6 +685,26 @@ MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
END_FUNCTION RAW_VAR(c_name, 0)
END_MACRO
+MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro)
+ DEFINE_FUNCTION RAW_VAR(c_name, 0)
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
+ // Outgoing argument set up
+ subl MACRO_LITERAL(12), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(12)
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH ebx // pass arg4
+ PUSH edx // pass arg3
+ PUSH ecx // pass arg2
+ PUSH eax // pass arg1
+ call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, arg4, Thread*)
+ addl MACRO_LITERAL(32), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-32)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ CALL_MACRO(return_macro, 2) // return or deliver exception
+ END_FUNCTION RAW_VAR(c_name, 0)
+END_MACRO
+
MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
DEFINE_FUNCTION RAW_VAR(c_name, 0)
SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
@@ -789,6 +809,12 @@ END_MACRO
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
+ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
+ THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars ## c_suffix, artAllocStringFromCharsFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(c_suffix, cxx_suffix) \
+ ONE_ARG_DOWNCALL art_quick_alloc_string_from_string ## c_suffix, artAllocStringFromStringFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc)
@@ -799,6 +825,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -809,6 +838,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMalloc
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
@@ -819,6 +851,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
@@ -829,6 +864,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAlloc
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer, BumpPointer)
@@ -839,6 +877,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -849,6 +890,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, Bump
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB)
@@ -859,6 +903,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented)
@@ -869,6 +916,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrume
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
@@ -879,6 +929,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
@@ -889,6 +942,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInst
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
@@ -899,6 +955,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
@@ -909,6 +968,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, Regio
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented)
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
@@ -1567,13 +1629,8 @@ DEFINE_FUNCTION art_quick_string_compareto
PUSH edi // push callee save reg
mov MIRROR_STRING_COUNT_OFFSET(%eax), %edx
mov MIRROR_STRING_COUNT_OFFSET(%ecx), %ebx
- mov MIRROR_STRING_VALUE_OFFSET(%eax), %esi
- mov MIRROR_STRING_VALUE_OFFSET(%ecx), %edi
- mov MIRROR_STRING_OFFSET_OFFSET(%eax), %eax
- mov MIRROR_STRING_OFFSET_OFFSET(%ecx), %ecx
- /* Build pointers to the start of string data */
- lea MIRROR_CHAR_ARRAY_DATA_OFFSET(%esi, %eax, 2), %esi
- lea MIRROR_CHAR_ARRAY_DATA_OFFSET(%edi, %ecx, 2), %edi
+ lea MIRROR_STRING_VALUE_OFFSET(%eax), %esi
+ lea MIRROR_STRING_VALUE_OFFSET(%ecx), %edi
/* Calculate min length and count diff */
mov %edx, %ecx
mov %edx, %eax
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 570624c..da4d92b 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -738,6 +738,17 @@ MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro)
END_FUNCTION VAR(c_name, 0)
END_MACRO
+MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro)
+ DEFINE_FUNCTION VAR(c_name, 0)
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ // Outgoing argument set up
+ movq %gs:THREAD_SELF_OFFSET, %r8 // pass Thread::Current()
+ call VAR(cxx_name, 1) // cxx_name(arg1, arg2, arg3, arg4, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ CALL_MACRO(return_macro, 2) // return or deliver exception
+ END_FUNCTION VAR(c_name, 0)
+END_MACRO
+
MACRO3(ONE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro)
DEFINE_FUNCTION VAR(c_name, 0)
movl 8(%rsp), %esi // pass referrer
@@ -822,6 +833,12 @@ END_MACRO
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
+ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
+ THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars ## c_suffix, artAllocStringFromCharsFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
+#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(c_suffix, cxx_suffix) \
+ ONE_ARG_DOWNCALL art_quick_alloc_string_from_string ## c_suffix, artAllocStringFromStringFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc, DlMalloc)
@@ -832,6 +849,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -842,6 +862,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMalloc
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc)
@@ -852,6 +875,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
@@ -862,6 +888,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAlloc
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer, BumpPointer)
@@ -872,6 +901,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -882,6 +914,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, Bump
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
DEFINE_FUNCTION art_quick_alloc_object_tlab
// Fast path tlab allocation.
@@ -929,6 +964,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab_instrumented, TLABInstrumented)
@@ -939,6 +977,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrume
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region, Region)
@@ -949,6 +990,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_instrumented, RegionInstrumented)
@@ -959,6 +1003,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInst
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
@@ -969,6 +1016,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
@@ -979,6 +1029,9 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, Regio
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented)
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO
ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO
@@ -1622,13 +1675,9 @@ END_FUNCTION art_quick_deoptimize
DEFINE_FUNCTION art_quick_string_compareto
movl MIRROR_STRING_COUNT_OFFSET(%edi), %r8d
movl MIRROR_STRING_COUNT_OFFSET(%esi), %r9d
- movl MIRROR_STRING_VALUE_OFFSET(%edi), %r10d
- movl MIRROR_STRING_VALUE_OFFSET(%esi), %r11d
- movl MIRROR_STRING_OFFSET_OFFSET(%edi), %eax
- movl MIRROR_STRING_OFFSET_OFFSET(%esi), %ecx
/* Build pointers to the start of string data */
- leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%r10d, %eax, 2), %esi
- leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%r11d, %ecx, 2), %edi
+ leal MIRROR_STRING_VALUE_OFFSET(%edi), %edi
+ leal MIRROR_STRING_VALUE_OFFSET(%esi), %esi
/* Calculate min length and count diff */
movl %r8d, %ecx
movl %r8d, %eax
@@ -1638,8 +1687,8 @@ DEFINE_FUNCTION art_quick_string_compareto
* At this point we have:
* eax: value to return if first part of strings are equal
* ecx: minimum among the lengths of the two strings
- * esi: pointer to this string data
- * edi: pointer to comp string data
+ * esi: pointer to comp string data
+ * edi: pointer to this string data
*/
jecxz .Lkeep_length
repe cmpsw // find nonmatching chars in [%esi] and [%edi], up to length %ecx
@@ -1648,8 +1697,8 @@ DEFINE_FUNCTION art_quick_string_compareto
ret
.balign 16
.Lnot_equal:
- movzwl -2(%esi), %eax // get last compared char from this string
- movzwl -2(%edi), %ecx // get last compared char from comp string
+ movzwl -2(%edi), %eax // get last compared char from this string
+ movzwl -2(%esi), %ecx // get last compared char from comp string
subl %ecx, %eax // return the difference
ret
END_FUNCTION art_quick_string_compareto
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 8057dd1..2653c1e 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -108,7 +108,7 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET,
ADD_TEST_EQ(THREAD_SELF_OFFSET,
art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 126 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 145 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
#define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__)
@@ -170,14 +170,11 @@ ADD_TEST_EQ(static_cast<size_t>(MIRROR_OBJECT_ARRAY_COMPONENT_SIZE),
sizeof(art::mirror::HeapReference<art::mirror::Object>))
// Offsets within java.lang.String.
-#define MIRROR_STRING_VALUE_OFFSET MIRROR_OBJECT_HEADER_SIZE
-ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
-
-#define MIRROR_STRING_COUNT_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_STRING_COUNT_OFFSET MIRROR_OBJECT_HEADER_SIZE
ADD_TEST_EQ(MIRROR_STRING_COUNT_OFFSET, art::mirror::String::CountOffset().Int32Value())
-#define MIRROR_STRING_OFFSET_OFFSET (12 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_STRING_OFFSET_OFFSET, art::mirror::String::OffsetOffset().Int32Value())
+#define MIRROR_STRING_VALUE_OFFSET (8 + MIRROR_OBJECT_HEADER_SIZE)
+ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
// Offsets within java.lang.reflect.ArtMethod.
#define MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8a0c315..962e821 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -349,8 +349,8 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
Handle<mirror::Class> java_lang_String(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize())));
mirror::String::SetClass(java_lang_String.Get());
- java_lang_String->SetObjectSize(mirror::String::InstanceSize());
mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self);
+ java_lang_String->SetStringClass();
// Setup java.lang.ref.Reference.
Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle(
@@ -478,7 +478,6 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
LOG(FATAL) << os1.str() << "\n\n" << os2.str();
}
- CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize());
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self);
CHECK_EQ(java_lang_DexCache.Get(), FindSystemClass(self, "Ljava/lang/DexCache;"));
CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize());
@@ -1740,6 +1739,13 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, si
SetupClass(dex_file, dex_class_def, klass, class_loader.Get());
+ // Mark the string class by setting its access flag.
+ if (UNLIKELY(!init_done_)) {
+ if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
+ klass->SetStringClass();
+ }
+ }
+
ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 7bee98f..6837de7 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -394,8 +394,9 @@ struct CheckOffsets {
bool error = false;
- // Art method have a different size due to the padding field.
- if (!klass->IsArtMethodClass() && !klass->IsClassClass() && !is_static) {
+ // Methods and classes have a different size due to padding field. Strings are variable length.
+ if (!klass->IsArtMethodClass() && !klass->IsClassClass() && !klass->IsStringClass() &&
+ !is_static) {
// Currently only required for AccessibleObject since of the padding fields. The class linker
// says AccessibleObject is 9 bytes but sizeof(AccessibleObject) is 12 bytes due to padding.
// The RoundUp is to get around this case.
@@ -538,8 +539,6 @@ struct StringOffsets : public CheckOffsets<mirror::String> {
StringOffsets() : CheckOffsets<mirror::String>(false, "Ljava/lang/String;") {
addOffset(OFFSETOF_MEMBER(mirror::String, count_), "count");
addOffset(OFFSETOF_MEMBER(mirror::String, hash_code_), "hashCode");
- addOffset(OFFSETOF_MEMBER(mirror::String, offset_), "offset");
- addOffset(OFFSETOF_MEMBER(mirror::String, array_), "value");
};
};
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index f3ce552..3fb7b20 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4078,7 +4078,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
StackHandleScope<1> hs(soa.Self());
Handle<mirror::String> name(hs.NewHandle(t->GetThreadName(soa)));
size_t char_count = (name.Get() != nullptr) ? name->GetLength() : 0;
- const jchar* chars = (name.Get() != nullptr) ? name->GetCharArray()->GetData() : nullptr;
+ const jchar* chars = (name.Get() != nullptr) ? name->GetValue() : nullptr;
std::vector<uint8_t> bytes;
JDWP::Append4BE(bytes, t->GetThreadId());
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index c049e3d..fa129af 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -153,6 +153,32 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##
} else { \
return CheckAndAllocArrayFromCodeInstrumented(type_idx, component_count, method, self, true, allocator_type); \
} \
+} \
+extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \
+ mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \
+ Thread* self) \
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+ ScopedQuickEntrypointChecks sqec(self); \
+ StackHandleScope<1> hs(self); \
+ Handle<mirror::ByteArray> handle_array(hs.NewHandle(byte_array)); \
+ return mirror::String::AllocFromByteArray<instrumented_bool>(self, byte_count, handle_array, \
+ offset, high, allocator_type); \
+} \
+extern "C" mirror::String* artAllocStringFromCharsFromCode##suffix##suffix2( \
+ int32_t offset, int32_t char_count, mirror::CharArray* char_array, Thread* self) \
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+ StackHandleScope<1> hs(self); \
+ Handle<mirror::CharArray> handle_array(hs.NewHandle(char_array)); \
+ return mirror::String::AllocFromCharArray<instrumented_bool>(self, char_count, handle_array, \
+ offset, allocator_type); \
+} \
+extern "C" mirror::String* artAllocStringFromStringFromCode##suffix##suffix2( \
+ mirror::String* string, Thread* self) \
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { \
+ StackHandleScope<1> hs(self); \
+ Handle<mirror::String> handle_string(hs.NewHandle(string)); \
+ return mirror::String::AllocFromString<instrumented_bool>(self, handle_string->GetLength(), \
+ handle_string, 0, allocator_type); \
}
#define GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(suffix, allocator_type) \
@@ -176,6 +202,9 @@ extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass
extern "C" void* art_quick_alloc_object_with_access_check##suffix(uint32_t type_idx, mirror::ArtMethod* ref); \
extern "C" void* art_quick_check_and_alloc_array##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_string_from_bytes##suffix(void*, int32_t, int32_t, int32_t); \
+extern "C" void* art_quick_alloc_string_from_chars##suffix(int32_t, int32_t, void*); \
+extern "C" void* art_quick_alloc_string_from_string##suffix(void*); \
extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, mirror::ArtMethod* ref); \
extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
@@ -185,6 +214,9 @@ extern "C" void* art_quick_alloc_object_initialized##suffix##_instrumented(mirro
extern "C" void* art_quick_alloc_object_with_access_check##suffix##_instrumented(uint32_t type_idx, mirror::ArtMethod* ref); \
extern "C" void* art_quick_check_and_alloc_array##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
extern "C" void* art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, mirror::ArtMethod* ref); \
+extern "C" void* art_quick_alloc_string_from_bytes##suffix##_instrumented(void*, int32_t, int32_t, int32_t); \
+extern "C" void* art_quick_alloc_string_from_chars##suffix##_instrumented(int32_t, int32_t, void*); \
+extern "C" void* art_quick_alloc_string_from_string##suffix##_instrumented(void*); \
void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrumented) { \
if (instrumented) { \
qpoints->pAllocArray = art_quick_alloc_array##suffix##_instrumented; \
@@ -196,6 +228,9 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument
qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix##_instrumented; \
qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \
qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \
+ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \
+ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \
+ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \
} else { \
qpoints->pAllocArray = art_quick_alloc_array##suffix; \
qpoints->pAllocArrayResolved = art_quick_alloc_array_resolved##suffix; \
@@ -206,6 +241,9 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument
qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check##suffix; \
qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \
qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \
+ qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \
+ qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \
+ qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \
} \
}
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 6d9e483..035f57a 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -29,6 +29,9 @@
V(AllocObjectWithAccessCheck, void*, uint32_t, mirror::ArtMethod*) \
V(CheckAndAllocArray, void*, uint32_t, int32_t, mirror::ArtMethod*) \
V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, mirror::ArtMethod*) \
+ V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \
+ V(AllocStringFromChars, void*, int32_t, int32_t, void*) \
+ V(AllocStringFromString, void*, void*) \
\
V(InstanceofNonTrivial, uint32_t, const mirror::Class*, const mirror::Class*) \
V(CheckCast, void, const mirror::Class*, const mirror::Class*) \
@@ -123,8 +126,24 @@
V(Deoptimize, void, void) \
\
V(A64Load, int64_t, volatile const int64_t *) \
- V(A64Store, void, volatile int64_t *, int64_t)
-
+ V(A64Store, void, volatile int64_t *, int64_t) \
+\
+ V(NewEmptyString, void) \
+ V(NewStringFromBytes_B, void) \
+ V(NewStringFromBytes_BI, void) \
+ V(NewStringFromBytes_BII, void) \
+ V(NewStringFromBytes_BIII, void) \
+ V(NewStringFromBytes_BIIString, void) \
+ V(NewStringFromBytes_BString, void) \
+ V(NewStringFromBytes_BIICharset, void) \
+ V(NewStringFromBytes_BCharset, void) \
+ V(NewStringFromChars_C, void) \
+ V(NewStringFromChars_CII, void) \
+ V(NewStringFromChars_IIC, void) \
+ V(NewStringFromCodePoints, void) \
+ V(NewStringFromString, void) \
+ V(NewStringFromStringBuffer, void) \
+ V(NewStringFromStringBuilder, void)
#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_
#undef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_ENTRYPOINTS_LIST_H_ // #define is only for lint.
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 0664fa0..1fb45f4 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -167,7 +167,13 @@ class EntrypointsOrderTest : public CommonRuntimeTest {
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck,
- pInstanceofNonTrivial, sizeof(void*));
+ pAllocStringFromBytes, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromString, pInstanceofNonTrivial,
+ sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInstanceofNonTrivial, pCheckCast, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckCast, pInitializeStaticStorage, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeStaticStorage, pInitializeTypeAndVerifyAccess,
@@ -269,7 +275,38 @@ class EntrypointsOrderTest : public CommonRuntimeTest {
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeoptimize, pA64Load, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, sizeof(void*));
- CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pA64Store)
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Store, pNewEmptyString, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewEmptyString, pNewStringFromBytes_B, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_B, pNewStringFromBytes_BI,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BI, pNewStringFromBytes_BII,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BII, pNewStringFromBytes_BIII,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BIII, pNewStringFromBytes_BIIString,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BIIString,
+ pNewStringFromBytes_BString, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BString,
+ pNewStringFromBytes_BIICharset, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BIICharset,
+ pNewStringFromBytes_BCharset, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BCharset,
+ pNewStringFromChars_C, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromChars_C, pNewStringFromChars_CII,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromChars_CII, pNewStringFromChars_IIC,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromChars_IIC, pNewStringFromCodePoints,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromCodePoints, pNewStringFromString,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromString, pNewStringFromStringBuffer,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuffer, pNewStringFromStringBuilder,
+ sizeof(void*));
+
+ CHECKED(OFFSETOF_MEMBER(QuickEntryPoints, pNewStringFromStringBuilder)
+ sizeof(void*) == sizeof(QuickEntryPoints), QuickEntryPoints_all);
}
};
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index fb7ff54..d0eb083 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -981,7 +981,7 @@ void Hprof::DumpHeapClass(mirror::Class* klass) {
// ClassObjects have their static fields appended, so aren't all the same size.
// But they're at least this size.
__ AddU4(sizeof(mirror::Class)); // instance size
- } else if (klass->IsArrayClass() || klass->IsPrimitive()) {
+ } else if (klass->IsArrayClass() || klass->IsStringClass() || klass->IsPrimitive()) {
__ AddU4(0);
} else {
__ AddU4(klass->GetObjectSize()); // instance size
@@ -1036,13 +1036,22 @@ void Hprof::DumpHeapClass(mirror::Class* klass) {
// Instance fields for this class (no superclass fields)
int iFieldCount = klass->IsObjectClass() ? 0 : klass->NumInstanceFields();
- __ AddU2((uint16_t)iFieldCount);
+ if (klass->IsStringClass()) {
+ __ AddU2((uint16_t)iFieldCount + 1);
+ } else {
+ __ AddU2((uint16_t)iFieldCount);
+ }
for (int i = 0; i < iFieldCount; ++i) {
ArtField* f = klass->GetInstanceField(i);
__ AddStringId(LookupStringId(f->GetName()));
HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), nullptr);
__ AddU1(t);
}
+ // Add native value character array for strings.
+ if (klass->IsStringClass()) {
+ __ AddStringId(LookupStringId("value"));
+ __ AddU1(hprof_basic_object);
+ }
}
void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass) {
@@ -1099,6 +1108,7 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) {
// Write the instance data; fields for this class, followed by super class fields,
// and so on. Don't write the klass or monitor fields of Object.class.
+ mirror::Class* orig_klass = klass;
while (!klass->IsObjectClass()) {
int ifieldCount = klass->NumInstanceFields();
for (int i = 0; i < ifieldCount; ++i) {
@@ -1133,8 +1143,24 @@ void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass) {
klass = klass->GetSuperClass();
}
- // Patch the instance field length.
- __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));
+ // Output native value character array for strings.
+ if (orig_klass->IsStringClass()) {
+ mirror::String* s = obj->AsString();
+ __ AddObjectId(reinterpret_cast<mirror::Object*>(s->GetValue()));
+
+ // Patch the instance field length.
+ __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));
+
+ __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+ __ AddObjectId(reinterpret_cast<mirror::Object*>(s->GetValue()));
+ __ AddU4(StackTraceSerialNumber(obj));
+ __ AddU4(s->GetLength());
+ __ AddU1(hprof_basic_char);
+ __ AddU2List(s->GetValue(), s->GetLength());
+ } else {
+ // Patch the instance field length.
+ __ UpdateU4(size_patch_offset, output_->Length() - (size_patch_offset + 4));
+ }
}
void Hprof::VisitRoot(mirror::Object* obj, const RootInfo& info) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 4765ebc..ef3c6e2 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -21,6 +21,7 @@
#include "debugger.h"
#include "mirror/array-inl.h"
#include "unstarted_runtime.h"
+#include "verifier/method_verifier.h"
namespace art {
namespace interpreter {
@@ -485,16 +486,28 @@ void AbortTransactionV(Thread* self, const char* fmt, va_list args) {
template<bool is_range, bool do_assignability_check>
bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
const Instruction* inst, uint16_t inst_data, JValue* result) {
+ bool string_init = false;
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ if (called_method->GetDeclaringClass()->IsStringClass() && called_method->IsConstructor()) {
+ ScopedObjectAccessUnchecked soa(self);
+ jmethodID mid = soa.EncodeMethod(called_method);
+ called_method = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+ string_init = true;
+ }
+
// Compute method information.
const DexFile::CodeItem* code_item = called_method->GetCodeItem();
const uint16_t num_ins = (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
uint16_t num_regs;
if (LIKELY(code_item != nullptr)) {
num_regs = code_item->registers_size_;
- DCHECK_EQ(num_ins, code_item->ins_size_);
} else {
DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
num_regs = num_ins;
+ if (string_init) {
+ // The new StringFactory call is static and has one fewer argument.
+ num_regs--;
+ }
}
// Allocate shadow frame on the stack.
@@ -504,7 +517,7 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
memory));
// Initialize new shadow frame.
- const size_t first_dest_reg = num_regs - num_ins;
+ size_t first_dest_reg = num_regs - num_ins;
if (do_assignability_check) {
// Slow path.
// We might need to do class loading, which incurs a thread state change to kNative. So
@@ -536,6 +549,10 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
new_shadow_frame->SetVRegReference(dest_reg, shadow_frame.GetVRegReference(receiver_reg));
++dest_reg;
++arg_offset;
+ } else if (string_init) {
+ // Skip the referrer for the new static StringFactory call.
+ ++dest_reg;
+ ++arg_offset;
}
for (uint32_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) {
DCHECK_LT(shorty_pos + 1, shorty_len);
@@ -583,7 +600,12 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
} else {
// Fast path: no extra checks.
if (is_range) {
- const uint16_t first_src_reg = inst->VRegC_3rc();
+ uint16_t first_src_reg = inst->VRegC_3rc();
+ if (string_init) {
+ // Skip the referrer for the new static StringFactory call.
+ ++first_src_reg;
+ ++first_dest_reg;
+ }
for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < num_regs;
++dest_reg, ++src_reg) {
AssignRegister(new_shadow_frame, shadow_frame, dest_reg, src_reg);
@@ -592,12 +614,18 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
DCHECK_LE(num_ins, 5U);
uint16_t regList = inst->Fetch16(2);
uint16_t count = num_ins;
+ size_t arg_index = 0;
+ if (string_init) {
+ // Skip the referrer for the new static StringFactory call.
+ regList >>= 4;
+ ++arg_index;
+ }
if (count == 5) {
AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + 4U,
(inst_data >> 8) & 0x0f);
--count;
}
- for (size_t arg_index = 0; arg_index < count; ++arg_index, regList >>= 4) {
+ for (; arg_index < count; ++arg_index, regList >>= 4) {
AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + arg_index, regList & 0x0f);
}
}
@@ -631,6 +659,38 @@ bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
} else {
UnstartedRuntimeInvoke(self, code_item, new_shadow_frame, result, first_dest_reg);
}
+
+ if (string_init && !self->IsExceptionPending()) {
+ // Set the new string result of the StringFactory.
+ uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ shadow_frame.SetVRegReference(vregC, result->GetL());
+ // Overwrite all potential copies of the original result of the new-instance of string with the
+ // new result of the StringFactory. Use the verifier to find this set of registers.
+ mirror::ArtMethod* method = shadow_frame.GetMethod();
+ MethodReference method_ref = method->ToMethodReference();
+ SafeMap<uint32_t, std::set<uint32_t>> string_init_map;
+ SafeMap<uint32_t, std::set<uint32_t>>* string_init_map_ptr;
+ MethodRefToStringInitRegMap& method_to_string_init_map = Runtime::Current()->GetStringInitMap();
+ auto it = method_to_string_init_map.find(method_ref);
+ if (it == method_to_string_init_map.end()) {
+ string_init_map = std::move(verifier::MethodVerifier::FindStringInitMap(method));
+ method_to_string_init_map.Overwrite(method_ref, string_init_map);
+ string_init_map_ptr = &string_init_map;
+ } else {
+ string_init_map_ptr = &it->second;
+ }
+ if (string_init_map_ptr->size() != 0) {
+ uint32_t dex_pc = shadow_frame.GetDexPC();
+ auto map_it = string_init_map_ptr->find(dex_pc);
+ if (map_it != string_init_map_ptr->end()) {
+ const std::set<uint32_t>& reg_set = map_it->second;
+ for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
+ shadow_frame.SetVRegReference(*set_it, result->GetL());
+ }
+ }
+ }
+ }
+
return !self->IsExceptionPending();
}
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index dbedc16..6acc72e 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -369,7 +369,7 @@ static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruc
oss << StringPrintf(" vreg%u=0x%08X", i, raw_value);
if (ref_value != nullptr) {
if (ref_value->GetClass()->IsStringClass() &&
- ref_value->AsString()->GetCharArray() != nullptr) {
+ ref_value->AsString()->GetValue() != nullptr) {
oss << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";
} else {
oss << "/" << PrettyTypeOf(ref_value);
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index dc0b687..878efba 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -526,10 +526,20 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF
HANDLE_INSTRUCTION_END();
HANDLE_INSTRUCTION_START(NEW_INSTANCE) {
- Runtime* runtime = Runtime::Current();
- Object* obj = AllocObjectFromCode<do_access_check, true>(
- inst->VRegB_21c(), shadow_frame.GetMethod(), self,
- runtime->GetHeap()->GetCurrentAllocator());
+ Object* obj = nullptr;
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (LIKELY(c != nullptr)) {
+ if (UNLIKELY(c->IsStringClass())) {
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::SetStringCountVisitor visitor(0);
+ obj = String::Alloc<true>(self, 0, allocator_type, visitor);
+ } else {
+ obj = AllocObjectFromCode<do_access_check, true>(
+ inst->VRegB_21c(), shadow_frame.GetMethod(), self,
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ }
+ }
if (UNLIKELY(obj == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 82f0009..a5e5299 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -428,10 +428,20 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
}
case Instruction::NEW_INSTANCE: {
PREAMBLE();
- Runtime* runtime = Runtime::Current();
- Object* obj = AllocObjectFromCode<do_access_check, true>(
- inst->VRegB_21c(), shadow_frame.GetMethod(), self,
- runtime->GetHeap()->GetCurrentAllocator());
+ Object* obj = nullptr;
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (LIKELY(c != nullptr)) {
+ if (UNLIKELY(c->IsStringClass())) {
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::SetStringCountVisitor visitor(0);
+ obj = String::Alloc<true>(self, 0, allocator_type, visitor);
+ } else {
+ obj = AllocObjectFromCode<do_access_check, true>(
+ inst->VRegB_21c(), shadow_frame.GetMethod(), self,
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ }
+ }
if (UNLIKELY(obj == nullptr)) {
HANDLE_PENDING_EXCEPTION();
} else {
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f5a3a6b..fc3826b 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -573,6 +573,12 @@ class JNI {
if (c == nullptr) {
return nullptr;
}
+ if (c->IsStringClass()) {
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::SetStringCountVisitor visitor(0);
+ return soa.AddLocalReference<jobject>(mirror::String::Alloc<true>(soa.Self(), 0,
+ allocator_type, visitor));
+ }
return soa.AddLocalReference<jobject>(c->AllocObject(soa.Self()));
}
@@ -594,6 +600,11 @@ class JNI {
if (c == nullptr) {
return nullptr;
}
+ if (c->IsStringClass()) {
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ jmethodID sf_mid = WellKnownClasses::StringInitToStringFactoryMethodID(mid);
+ return CallStaticObjectMethodV(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
+ }
mirror::Object* result = c->AllocObject(soa.Self());
if (result == nullptr) {
return nullptr;
@@ -614,6 +625,11 @@ class JNI {
if (c == nullptr) {
return nullptr;
}
+ if (c->IsStringClass()) {
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ jmethodID sf_mid = WellKnownClasses::StringInitToStringFactoryMethodID(mid);
+ return CallStaticObjectMethodA(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
+ }
mirror::Object* result = c->AllocObject(soa.Self());
if (result == nullptr) {
return nullptr;
@@ -1649,7 +1665,7 @@ class JNI {
ThrowSIOOBE(soa, start, length, s->GetLength());
} else {
CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
- const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
+ const jchar* chars = s->GetValue();
memcpy(buf, chars + start, length * sizeof(jchar));
}
}
@@ -1663,7 +1679,7 @@ class JNI {
ThrowSIOOBE(soa, start, length, s->GetLength());
} else {
CHECK_NON_NULL_MEMCPY_ARGUMENT(length, buf);
- const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
+ const jchar* chars = s->GetValue();
ConvertUtf16ToModifiedUtf8(buf, chars + start, length);
}
}
@@ -1672,33 +1688,26 @@ class JNI {
CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
- mirror::CharArray* chars = s->GetCharArray();
gc::Heap* heap = Runtime::Current()->GetHeap();
- if (heap->IsMovableObject(chars)) {
+ if (heap->IsMovableObject(s)) {
+ jchar* chars = new jchar[s->GetLength()];
+ memcpy(chars, s->GetValue(), sizeof(jchar) * s->GetLength());
if (is_copy != nullptr) {
*is_copy = JNI_TRUE;
}
- int32_t char_count = s->GetLength();
- int32_t offset = s->GetOffset();
- jchar* bytes = new jchar[char_count];
- for (int32_t i = 0; i < char_count; i++) {
- bytes[i] = chars->Get(i + offset);
- }
- return bytes;
- } else {
- if (is_copy != nullptr) {
- *is_copy = JNI_FALSE;
- }
- return static_cast<jchar*>(chars->GetData() + s->GetOffset());
+ return chars;
+ }
+ if (is_copy != nullptr) {
+ *is_copy = JNI_FALSE;
}
+ return static_cast<jchar*>(s->GetValue());
}
static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) {
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_string);
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
- mirror::CharArray* s_chars = s->GetCharArray();
- if (chars != (s_chars->GetData() + s->GetOffset())) {
+ if (chars != s->GetValue()) {
delete[] chars;
}
}
@@ -1707,18 +1716,16 @@ class JNI {
CHECK_NON_NULL_ARGUMENT(java_string);
ScopedObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(java_string);
- mirror::CharArray* chars = s->GetCharArray();
- int32_t offset = s->GetOffset();
gc::Heap* heap = Runtime::Current()->GetHeap();
- if (heap->IsMovableObject(chars)) {
+ if (heap->IsMovableObject(s)) {
StackHandleScope<1> hs(soa.Self());
- HandleWrapper<mirror::CharArray> h(hs.NewHandleWrapper(&chars));
+ HandleWrapper<mirror::String> h(hs.NewHandleWrapper(&s));
heap->IncrementDisableMovingGC(soa.Self());
}
if (is_copy != nullptr) {
*is_copy = JNI_FALSE;
}
- return static_cast<jchar*>(chars->GetData() + offset);
+ return static_cast<jchar*>(s->GetValue());
}
static void ReleaseStringCritical(JNIEnv* env, jstring java_string, const jchar* chars) {
@@ -1727,8 +1734,7 @@ class JNI {
ScopedObjectAccess soa(env);
gc::Heap* heap = Runtime::Current()->GetHeap();
mirror::String* s = soa.Decode<mirror::String*>(java_string);
- mirror::CharArray* s_chars = s->GetCharArray();
- if (heap->IsMovableObject(s_chars)) {
+ if (heap->IsMovableObject(s)) {
heap->DecrementDisableMovingGC(soa.Self());
}
}
@@ -1745,7 +1751,7 @@ class JNI {
size_t byte_count = s->GetUtfLength();
char* bytes = new char[byte_count + 1];
CHECK(bytes != nullptr); // bionic aborts anyway.
- const uint16_t* chars = s->GetCharArray()->GetData() + s->GetOffset();
+ const uint16_t* chars = s->GetValue();
ConvertUtf16ToModifiedUtf8(bytes, chars, s->GetLength());
bytes[byte_count] = '\0';
return bytes;
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 77db404..3d14a4e 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -625,8 +625,6 @@ TEST_F(JniInternalTest, AllocObject) {
// ...whose fields haven't been initialized because
// we didn't call a constructor.
ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "count", "I")));
- ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "offset", "I")));
- ASSERT_TRUE(env_->GetObjectField(o, env_->GetFieldID(c, "value", "[C")) == nullptr);
}
TEST_F(JniInternalTest, GetVersion) {
@@ -860,7 +858,9 @@ TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
jstring s = reinterpret_cast<jstring>(env_->AllocObject(c));
ASSERT_NE(s, nullptr);
env_->CallVoidMethod(s, mid2);
- ASSERT_EQ(JNI_FALSE, env_->ExceptionCheck());
+ // With the string change, this should now throw an UnsupportedOperationException.
+ ASSERT_EQ(JNI_TRUE, env_->ExceptionCheck());
+ env_->ExceptionClear();
mid = env_->GetMethodID(c, "length", "()I");
ASSERT_NE(mid, nullptr);
@@ -1538,7 +1538,7 @@ TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) {
jboolean is_copy = JNI_FALSE;
chars = env_->GetStringChars(s, &is_copy);
- if (Runtime::Current()->GetHeap()->IsMovableObject(s_m->GetCharArray())) {
+ if (Runtime::Current()->GetHeap()->IsMovableObject(s_m)) {
EXPECT_EQ(JNI_TRUE, is_copy);
} else {
EXPECT_EQ(JNI_FALSE, is_copy);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 712286f..cc6f5c4 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -548,6 +548,10 @@ inline void Class::CheckObjectAlloc() {
<< PrettyClass(this)
<< "A class object shouldn't be allocated through this "
<< "as it requires a pre-fence visitor that sets the class size.";
+ DCHECK(!IsStringClass())
+ << PrettyClass(this)
+ << "A string shouldn't be allocated through this "
+ << "as it requires a pre-fence visitor that sets the class size.";
DCHECK(IsInstantiable()) << PrettyClass(this);
// TODO: decide whether we want this check. It currently fails during bootstrap.
// DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 1739019..56c586a 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -330,10 +330,6 @@ bool Class::IsInSamePackage(Class* that) {
return IsInSamePackage(klass1->GetDescriptor(&temp1), klass2->GetDescriptor(&temp2));
}
-bool Class::IsStringClass() const {
- return this == String::GetJavaLangString();
-}
-
bool Class::IsThrowableClass() {
return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this);
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 18496fd..d3cfd01 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -235,6 +235,15 @@ class MANAGED Class FINAL : public Object {
SetAccessFlags(flags | kAccClassIsFinalizable);
}
+ ALWAYS_INLINE bool IsStringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return (GetField32(AccessFlagsOffset()) & kAccClassIsStringClass) != 0;
+ }
+
+ ALWAYS_INLINE void SetStringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
+ SetAccessFlags(flags | kAccClassIsStringClass);
+ }
+
// Returns true if the class is abstract.
ALWAYS_INLINE bool IsAbstract() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccAbstract) != 0;
@@ -416,8 +425,6 @@ class MANAGED Class FINAL : public Object {
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsClassClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsStringClass() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
bool IsThrowableClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
@@ -484,10 +491,10 @@ class MANAGED Class FINAL : public Object {
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsVariableSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Classes and arrays vary in size, and so the object_size_ field cannot
+ // Classes, arrays, and strings vary in size, and so the object_size_ field cannot
// be used to Get their instance size
return IsClassClass<kVerifyFlags, kReadBarrierOption>() ||
- IsArrayClass<kVerifyFlags, kReadBarrierOption>();
+ IsArrayClass<kVerifyFlags, kReadBarrierOption>() || IsStringClass();
}
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 2581fad..7c73f9f 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -28,8 +28,9 @@
#include "monitor.h"
#include "object_array-inl.h"
#include "read_barrier-inl.h"
-#include "runtime.h"
#include "reference.h"
+#include "runtime.h"
+#include "string-inl.h"
#include "throwable.h"
namespace art {
@@ -337,9 +338,14 @@ inline DoubleArray* Object::AsDoubleArray() {
return down_cast<DoubleArray*>(this);
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+inline bool Object::IsString() {
+ return GetClass<kVerifyFlags, kReadBarrierOption>()->IsStringClass();
+}
+
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline String* Object::AsString() {
- DCHECK(GetClass<kVerifyFlags>()->IsStringClass());
+ DCHECK((IsString<kVerifyFlags, kReadBarrierOption>()));
return down_cast<String*>(this);
}
@@ -385,6 +391,9 @@ inline size_t Object::SizeOf() {
} else if (IsClass<kNewFlags, kReadBarrierOption>()) {
result = AsClass<kNewFlags, kReadBarrierOption>()->
template SizeOf<kNewFlags, kReadBarrierOption>();
+ } else if (GetClass<kNewFlags, kReadBarrierOption>()->IsStringClass()) {
+ result = AsString<kNewFlags, kReadBarrierOption>()->
+ template SizeOf<kNewFlags>();
} else {
result = GetClass<kNewFlags, kReadBarrierOption>()->
template GetObjectSize<kNewFlags, kReadBarrierOption>();
@@ -947,7 +956,7 @@ inline void Object::VisitReferences(const Visitor& visitor,
mirror::Class* klass = GetClass<kVerifyFlags>();
if (klass == Class::GetJavaLangClass()) {
AsClass<kVerifyNone>()->VisitReferences<kVisitClass>(klass, visitor);
- } else if (klass->IsArrayClass()) {
+ } else if (klass->IsArrayClass() || klass->IsStringClass()) {
if (klass->IsObjectArrayClass<kVerifyNone>()) {
AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences<kVisitClass>(visitor);
} else if (kVisitClass) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 343c9bc..3ab52eb 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -182,7 +182,12 @@ class MANAGED LOCKABLE Object {
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
DoubleArray* AsDoubleArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ bool IsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
String* AsString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index 2262af5..8e50a7a 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -61,14 +61,13 @@ class ObjectTest : public CommonRuntimeTest {
Handle<String> string(
hs.NewHandle(String::AllocFromModifiedUtf8(self, expected_utf16_length, utf8_in)));
ASSERT_EQ(expected_utf16_length, string->GetLength());
- ASSERT_TRUE(string->GetCharArray() != nullptr);
- ASSERT_TRUE(string->GetCharArray()->GetData() != nullptr);
+ ASSERT_TRUE(string->GetValue() != nullptr);
// strlen is necessary because the 1-character string "\x00\x00" is interpreted as ""
ASSERT_TRUE(string->Equals(utf8_in) || (expected_utf16_length == 1 && strlen(utf8_in) == 0));
ASSERT_TRUE(string->Equals(StringPiece(utf8_in)) ||
(expected_utf16_length == 1 && strlen(utf8_in) == 0));
for (int32_t i = 0; i < expected_utf16_length; i++) {
- EXPECT_EQ(utf16_expected[i], string->UncheckedCharAt(i));
+ EXPECT_EQ(utf16_expected[i], string->CharAt(i));
}
EXPECT_EQ(expected_hash, string->GetHashCode());
}
@@ -491,12 +490,6 @@ TEST_F(ObjectTest, StringLength) {
Handle<String> string(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "android")));
EXPECT_EQ(string->GetLength(), 7);
EXPECT_EQ(string->GetUtfLength(), 7);
-
- string->SetOffset(2);
- string->SetCount(5);
- EXPECT_TRUE(string->Equals("droid"));
- EXPECT_EQ(string->GetLength(), 5);
- EXPECT_EQ(string->GetUtfLength(), 5);
}
TEST_F(ObjectTest, DescriptorCompare) {
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index b367cff..8f5a7d4 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -19,6 +19,7 @@
#include "array.h"
#include "class.h"
+#include "gc/heap-inl.h"
#include "intern_table.h"
#include "runtime.h"
#include "string.h"
@@ -29,33 +30,139 @@ namespace art {
namespace mirror {
inline uint32_t String::ClassSize() {
- uint32_t vtable_entries = Object::kVTableLength + 51;
+ uint32_t vtable_entries = Object::kVTableLength + 52;
return Class::ComputeClassSize(true, vtable_entries, 0, 1, 0, 1, 2);
}
-inline uint16_t String::UncheckedCharAt(int32_t index) {
- return GetCharArray()->Get(index + GetOffset());
+// Sets string count in the allocation code path to ensure it is guarded by a CAS.
+class SetStringCountVisitor {
+ public:
+ explicit SetStringCountVisitor(int32_t count) : count_(count) {
+ }
+
+ void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Avoid AsString as object is not yet in live bitmap or allocation stack.
+ String* string = down_cast<String*>(obj);
+ string->SetCount(count_);
+ }
+
+ private:
+ const int32_t count_;
+};
+
+// Sets string count and value in the allocation code path to ensure it is guarded by a CAS.
+class SetStringCountAndBytesVisitor {
+ public:
+ SetStringCountAndBytesVisitor(int32_t count, uint8_t* src, int32_t high_byte)
+ : count_(count), src_(src), high_byte_(high_byte) {
+ }
+
+ void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Avoid AsString as object is not yet in live bitmap or allocation stack.
+ String* string = down_cast<String*>(obj);
+ string->SetCount(count_);
+ uint16_t* value = string->GetValue();
+ for (int i = 0; i < count_; i++) {
+ value[i] = high_byte_ + (src_[i] & 0xFF);
+ }
+ }
+
+ private:
+ const int32_t count_;
+ const uint8_t* const src_;
+ const int32_t high_byte_;
+};
+
+// Sets string count and value in the allocation code path to ensure it is guarded by a CAS.
+class SetStringCountAndValueVisitor {
+ public:
+ SetStringCountAndValueVisitor(int32_t count, uint16_t* src) : count_(count), src_(src) {
+ }
+
+ void operator()(Object* obj, size_t usable_size) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UNUSED(usable_size);
+ // Avoid AsString as object is not yet in live bitmap or allocation stack.
+ String* string = down_cast<String*>(obj);
+ string->SetCount(count_);
+ memcpy(string->GetValue(), src_, count_ * sizeof(uint16_t));
+ }
+
+ private:
+ const int32_t count_;
+ const uint16_t* const src_;
+};
+
+inline String* String::Intern() {
+ return Runtime::Current()->GetInternTable()->InternWeak(this);
}
-inline CharArray* String::GetCharArray() {
- return GetFieldObject<CharArray>(ValueOffset());
+inline uint16_t String::CharAt(int32_t index) {
+ int32_t count = GetField32(OFFSET_OF_OBJECT_MEMBER(String, count_));
+ if (UNLIKELY((index < 0) || (index >= count))) {
+ Thread* self = Thread::Current();
+ self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
+ "length=%i; index=%i", count, index);
+ return 0;
+ }
+ return GetValue()[index];
}
-inline int32_t String::GetLength() {
- int32_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(String, count_));
- DCHECK(result >= 0 && result <= GetCharArray()->GetLength());
- return result;
+template<VerifyObjectFlags kVerifyFlags>
+inline size_t String::SizeOf() {
+ return sizeof(String) + (sizeof(uint16_t) * GetLength<kVerifyFlags>());
}
-inline void String::SetArray(CharArray* new_array) {
- // Array is invariant so use non-transactional mode. Also disable check as we may run inside
- // a transaction.
- DCHECK(new_array != nullptr);
- SetFieldObject<false, false>(OFFSET_OF_OBJECT_MEMBER(String, array_), new_array);
+template <bool kIsInstrumented, typename PreFenceVisitor>
+inline String* String::Alloc(Thread* self, int32_t utf16_length, gc::AllocatorType allocator_type,
+ const PreFenceVisitor& pre_fence_visitor) {
+ size_t header_size = sizeof(String);
+ size_t data_size = sizeof(uint16_t) * utf16_length;
+ size_t size = header_size + data_size;
+ Class* string_class = GetJavaLangString();
+
+ // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
+ if (UNLIKELY(size < data_size)) {
+ self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
+ PrettyDescriptor(string_class).c_str(),
+ utf16_length).c_str());
+ return nullptr;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ return down_cast<String*>(
+ heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, string_class, size,
+ allocator_type, pre_fence_visitor));
}
-inline String* String::Intern() {
- return Runtime::Current()->GetInternTable()->InternWeak(this);
+template <bool kIsInstrumented>
+inline String* String::AllocFromByteArray(Thread* self, int32_t byte_length,
+ Handle<ByteArray> array, int32_t offset,
+ int32_t high_byte, gc::AllocatorType allocator_type) {
+ uint8_t* data = reinterpret_cast<uint8_t*>(array->GetData()) + offset;
+ SetStringCountAndBytesVisitor visitor(byte_length, data, high_byte << 8);
+ String* string = Alloc<kIsInstrumented>(self, byte_length, allocator_type, visitor);
+ return string;
+}
+
+template <bool kIsInstrumented>
+inline String* String::AllocFromCharArray(Thread* self, int32_t array_length,
+ Handle<CharArray> array, int32_t offset,
+ gc::AllocatorType allocator_type) {
+ uint16_t* data = array->GetData() + offset;
+ SetStringCountAndValueVisitor visitor(array_length, data);
+ String* new_string = Alloc<kIsInstrumented>(self, array_length, allocator_type, visitor);
+ return new_string;
+}
+
+template <bool kIsInstrumented>
+inline String* String::AllocFromString(Thread* self, int32_t string_length, Handle<String> string,
+ int32_t offset, gc::AllocatorType allocator_type) {
+ uint16_t* data = string->GetValue() + offset;
+ SetStringCountAndValueVisitor visitor(string_length, data);
+ String* new_string = Alloc<kIsInstrumented>(self, string_length, allocator_type, visitor);
+ return new_string;
}
inline int32_t String::GetHashCode() {
@@ -63,7 +170,7 @@ inline int32_t String::GetHashCode() {
if (UNLIKELY(result == 0)) {
result = ComputeHashCode();
}
- DCHECK(result != 0 || ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()) == 0)
+ DCHECK(result != 0 || ComputeUtf16Hash(GetValue(), GetLength()) == 0)
<< ToModifiedUtf8() << " " << result;
return result;
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index b7fd240..b6236b1 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -20,10 +20,11 @@
#include "array.h"
#include "class-inl.h"
#include "gc/accounting/card_table-inl.h"
+#include "handle_scope-inl.h"
#include "intern_table.h"
#include "object-inl.h"
#include "runtime.h"
-#include "handle_scope-inl.h"
+#include "string-inl.h"
#include "thread.h"
#include "utf-inl.h"
@@ -40,7 +41,7 @@ int32_t String::FastIndexOf(int32_t ch, int32_t start) {
} else if (start > count) {
start = count;
}
- const uint16_t* chars = GetCharArray()->GetData() + GetOffset();
+ const uint16_t* chars = GetValue();
const uint16_t* p = chars + start;
const uint16_t* end = chars + count;
while (p < end) {
@@ -62,36 +63,46 @@ void String::ResetClass() {
java_lang_String_ = GcRoot<Class>(nullptr);
}
-int32_t String::ComputeHashCode() {
- const int32_t hash_code = ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength());
+int String::ComputeHashCode() {
+ const int32_t hash_code = ComputeUtf16Hash(GetValue(), GetLength());
SetHashCode(hash_code);
return hash_code;
}
int32_t String::GetUtfLength() {
- return CountUtf8Bytes(GetCharArray()->GetData() + GetOffset(), GetLength());
+ return CountUtf8Bytes(GetValue(), GetLength());
}
-String* String::AllocFromUtf16(Thread* self,
- int32_t utf16_length,
- const uint16_t* utf16_data_in,
- int32_t hash_code) {
- CHECK(utf16_data_in != nullptr || utf16_length == 0);
- String* string = Alloc(self, utf16_length);
- if (UNLIKELY(string == nullptr)) {
+void String::SetCharAt(int32_t index, uint16_t c) {
+ DCHECK((index >= 0) && (index < count_));
+ GetValue()[index] = c;
+}
+
+String* String::AllocFromStrings(Thread* self, Handle<String> string, Handle<String> string2) {
+ int32_t length = string->GetLength();
+ int32_t length2 = string2->GetLength();
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ SetStringCountVisitor visitor(length + length2);
+ String* new_string = Alloc<true>(self, length + length2, allocator_type, visitor);
+ if (UNLIKELY(new_string == nullptr)) {
return nullptr;
}
- CharArray* array = const_cast<CharArray*>(string->GetCharArray());
- if (UNLIKELY(array == nullptr)) {
+ uint16_t* new_value = new_string->GetValue();
+ memcpy(new_value, string->GetValue(), length * sizeof(uint16_t));
+ memcpy(new_value + length, string2->GetValue(), length2 * sizeof(uint16_t));
+ return new_string;
+}
+
+String* String::AllocFromUtf16(Thread* self, int32_t utf16_length, const uint16_t* utf16_data_in) {
+ CHECK(utf16_data_in != nullptr || utf16_length == 0);
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ SetStringCountVisitor visitor(utf16_length);
+ String* string = Alloc<true>(self, utf16_length, allocator_type, visitor);
+ if (UNLIKELY(string == nullptr)) {
return nullptr;
}
- memcpy(array->GetData(), utf16_data_in, utf16_length * sizeof(uint16_t));
- if (hash_code != 0) {
- DCHECK_EQ(hash_code, ComputeUtf16Hash(utf16_data_in, utf16_length));
- string->SetHashCode(hash_code);
- } else {
- string->ComputeHashCode();
- }
+ uint16_t* array = string->GetValue();
+ memcpy(array, utf16_data_in, utf16_length * sizeof(uint16_t));
return string;
}
@@ -103,33 +114,14 @@ String* String::AllocFromModifiedUtf8(Thread* self, const char* utf) {
String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length,
const char* utf8_data_in) {
- String* string = Alloc(self, utf16_length);
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ SetStringCountVisitor visitor(utf16_length);
+ String* string = Alloc<true>(self, utf16_length, allocator_type, visitor);
if (UNLIKELY(string == nullptr)) {
return nullptr;
}
- uint16_t* utf16_data_out =
- const_cast<uint16_t*>(string->GetCharArray()->GetData());
+ uint16_t* utf16_data_out = string->GetValue();
ConvertModifiedUtf8ToUtf16(utf16_data_out, utf8_data_in);
- string->ComputeHashCode();
- return string;
-}
-
-String* String::Alloc(Thread* self, int32_t utf16_length) {
- StackHandleScope<1> hs(self);
- Handle<CharArray> array(hs.NewHandle(CharArray::Alloc(self, utf16_length)));
- if (UNLIKELY(array.Get() == nullptr)) {
- return nullptr;
- }
- return Alloc(self, array);
-}
-
-String* String::Alloc(Thread* self, Handle<CharArray> array) {
- // Hold reference in case AllocObject causes GC.
- String* string = down_cast<String*>(GetJavaLangString()->AllocObject(self));
- if (LIKELY(string != nullptr)) {
- string->SetArray(array.Get());
- string->SetCount(array->GetLength());
- }
return string;
}
@@ -147,7 +139,7 @@ bool String::Equals(String* that) {
// Note: don't short circuit on hash code as we're presumably here as the
// hash code was already equal
for (int32_t i = 0; i < that->GetLength(); ++i) {
- if (this->UncheckedCharAt(i) != that->UncheckedCharAt(i)) {
+ if (this->CharAt(i) != that->CharAt(i)) {
return false;
}
}
@@ -160,7 +152,7 @@ bool String::Equals(const uint16_t* that_chars, int32_t that_offset, int32_t tha
return false;
} else {
for (int32_t i = 0; i < that_length; ++i) {
- if (this->UncheckedCharAt(i) != that_chars[that_offset + i]) {
+ if (this->CharAt(i) != that_chars[that_offset + i]) {
return false;
}
}
@@ -177,7 +169,7 @@ bool String::Equals(const char* modified_utf8) {
return false;
}
- if (GetLeadingUtf16Char(ch) != UncheckedCharAt(i++)) {
+ if (GetLeadingUtf16Char(ch) != CharAt(i++)) {
return false;
}
@@ -187,7 +179,7 @@ bool String::Equals(const char* modified_utf8) {
return false;
}
- if (UncheckedCharAt(i++) != trailing) {
+ if (CharAt(i++) != trailing) {
return false;
}
}
@@ -201,7 +193,7 @@ bool String::Equals(const StringPiece& modified_utf8) {
for (int32_t i = 0; i < length; ++i) {
uint32_t ch = GetUtf16FromUtf8(&p);
- if (GetLeadingUtf16Char(ch) != UncheckedCharAt(i)) {
+ if (GetLeadingUtf16Char(ch) != CharAt(i)) {
return false;
}
@@ -211,7 +203,7 @@ bool String::Equals(const StringPiece& modified_utf8) {
return false;
}
- if (UncheckedCharAt(++i) != trailing) {
+ if (CharAt(++i) != trailing) {
return false;
}
}
@@ -221,7 +213,7 @@ bool String::Equals(const StringPiece& modified_utf8) {
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string String::ToModifiedUtf8() {
- const uint16_t* chars = GetCharArray()->GetData() + GetOffset();
+ const uint16_t* chars = GetValue();
size_t byte_count = GetUtfLength();
std::string result(byte_count, static_cast<char>(0));
ConvertUtf16ToModifiedUtf8(&result[0], chars, GetLength());
@@ -244,8 +236,8 @@ int32_t String::CompareTo(String* rhs) {
int32_t rhsCount = rhs->GetLength();
int32_t countDiff = lhsCount - rhsCount;
int32_t minCount = (countDiff < 0) ? lhsCount : rhsCount;
- const uint16_t* lhsChars = lhs->GetCharArray()->GetData() + lhs->GetOffset();
- const uint16_t* rhsChars = rhs->GetCharArray()->GetData() + rhs->GetOffset();
+ const uint16_t* lhsChars = lhs->GetValue();
+ const uint16_t* rhsChars = rhs->GetValue();
int32_t otherRes = MemCmp16(lhsChars, rhsChars, minCount);
if (otherRes != 0) {
return otherRes;
@@ -257,5 +249,19 @@ void String::VisitRoots(RootVisitor* visitor) {
java_lang_String_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
}
+CharArray* String::ToCharArray(Thread* self) {
+ StackHandleScope<1> hs(self);
+ Handle<String> string(hs.NewHandle(this));
+ CharArray* result = CharArray::Alloc(self, GetLength());
+ memcpy(result->GetData(), string->GetValue(), string->GetLength() * sizeof(uint16_t));
+ return result;
+}
+
+void String::GetChars(int32_t start, int32_t end, Handle<CharArray> array, int32_t index) {
+ uint16_t* data = array->GetData() + index;
+ uint16_t* value = GetValue() + start;
+ memcpy(data, value, (end - start) * sizeof(uint16_t));
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 0670d0b..fcfe976 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_MIRROR_STRING_H_
#include "gc_root.h"
+#include "gc/allocator_type.h"
#include "object.h"
#include "object_callbacks.h"
@@ -45,22 +46,27 @@ class MANAGED String FINAL : public Object {
}
static MemberOffset ValueOffset() {
- return OFFSET_OF_OBJECT_MEMBER(String, array_);
+ return OFFSET_OF_OBJECT_MEMBER(String, value_);
}
- static MemberOffset OffsetOffset() {
- return OFFSET_OF_OBJECT_MEMBER(String, offset_);
+ uint16_t* GetValue() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return &value_[0];
}
- CharArray* GetCharArray() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- int32_t GetOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- int32_t result = GetField32(OffsetOffset());
- DCHECK_LE(0, result);
- return result;
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetField32<kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(String, count_));
}
- int32_t GetLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetCount(int32_t new_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Count is invariant so use non-transactional mode. Also disable check as we may run inside
+ // a transaction.
+ DCHECK_LE(0, new_count);
+ SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count);
+ }
int32_t GetHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -69,19 +75,47 @@ class MANAGED String FINAL : public Object {
int32_t GetUtfLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint16_t CharAt(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetCharAt(int32_t index, uint16_t c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
String* Intern() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static String* AllocFromUtf16(Thread* self,
- int32_t utf16_length,
- const uint16_t* utf16_data_in,
- int32_t hash_code = 0)
+ template <bool kIsInstrumented, typename PreFenceVisitor>
+ ALWAYS_INLINE static String* Alloc(Thread* self, int32_t utf16_length,
+ gc::AllocatorType allocator_type,
+ const PreFenceVisitor& pre_fence_visitor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <bool kIsInstrumented>
+ ALWAYS_INLINE static String* AllocFromByteArray(Thread* self, int32_t byte_length,
+ Handle<ByteArray> array, int32_t offset,
+ int32_t high_byte,
+ gc::AllocatorType allocator_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <bool kIsInstrumented>
+ ALWAYS_INLINE static String* AllocFromCharArray(Thread* self, int32_t array_length,
+ Handle<CharArray> array, int32_t offset,
+ gc::AllocatorType allocator_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <bool kIsInstrumented>
+ ALWAYS_INLINE static String* AllocFromString(Thread* self, int32_t string_length,
+ Handle<String> string, int32_t offset,
+ gc::AllocatorType allocator_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static String* AllocFromStrings(Thread* self, Handle<String> string, Handle<String> string2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static String* AllocFromUtf16(Thread* self, int32_t utf16_length, const uint16_t* utf16_data_in)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static String* AllocFromModifiedUtf8(Thread* self, const char* utf)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static String* AllocFromModifiedUtf8(Thread* self, int32_t utf16_length,
- const char* utf8_data_in)
+ static String* AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// TODO: This is only used in the interpreter to compare against
@@ -112,13 +146,10 @@ class MANAGED String FINAL : public Object {
int32_t CompareTo(String* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Offset is only used during testing so use non-transactional mode.
- DCHECK_LE(0, new_offset);
- SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset);
- }
+ CharArray* ToCharArray(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void GetChars(int32_t start, int32_t end, Handle<CharArray> array, int32_t index)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Class* GetJavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(!java_lang_String_.IsNull());
@@ -130,9 +161,6 @@ class MANAGED String FINAL : public Object {
static void VisitRoots(RootVisitor* visitor)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // TODO: Make this private. It's only used on ObjectTest at the moment.
- uint16_t UncheckedCharAt(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
private:
void SetHashCode(int32_t new_hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Hash code is invariant so use non-transactional mode. Also disable check as we may run inside
@@ -141,27 +169,12 @@ class MANAGED String FINAL : public Object {
SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code);
}
- void SetCount(int32_t new_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Count is invariant so use non-transactional mode. Also disable check as we may run inside
- // a transaction.
- DCHECK_LE(0, new_count);
- SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count);
- }
-
- static String* Alloc(Thread* self, int32_t utf16_length)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- static String* Alloc(Thread* self, Handle<CharArray> array)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
- HeapReference<CharArray> array_;
-
int32_t count_;
uint32_t hash_code_;
- int32_t offset_;
+ uint16_t value_[0];
static GcRoot<Class> java_lang_String_;
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index e7bd207..8586dd1 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -65,6 +65,8 @@ static constexpr uint32_t kAccClassIsWeakReference = 0x04000000;
static constexpr uint32_t kAccClassIsFinalizerReference = 0x02000000;
// class is a phantom reference
static constexpr uint32_t kAccClassIsPhantomReference = 0x01000000;
+// class is the string class
+static constexpr uint32_t kAccClassIsStringClass = 0x00800000;
static constexpr uint32_t kAccReferenceFlagsMask = (kAccClassIsReference
| kAccClassIsWeakReference
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 48a8bc7..c4aecb1 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -192,7 +192,7 @@ ALWAYS_INLINE static inline ArtField* FindFieldByName(
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
size_t low = 0;
size_t high = num_fields;
- const uint16_t* const data = name->GetCharArray()->GetData() + name->GetOffset();
+ const uint16_t* const data = name->GetValue();
const size_t length = name->GetLength();
while (low < high) {
auto mid = (low + high) / 2;
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 2d153d4..aa64b79 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -18,6 +18,9 @@
#include "common_throws.h"
#include "jni_internal.h"
+#include "mirror/array.h"
+#include "mirror/object-inl.h"
+#include "mirror/string.h"
#include "mirror/string-inl.h"
#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
@@ -26,36 +29,93 @@
namespace art {
-static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
+static jchar String_charAt(JNIEnv* env, jobject java_this, jint index) {
ScopedFastNativeObjectAccess soa(env);
- if (UNLIKELY(javaRhs == nullptr)) {
+ return soa.Decode<mirror::String*>(java_this)->CharAt(index);
+}
+
+static jint String_compareTo(JNIEnv* env, jobject java_this, jobject java_rhs) {
+ ScopedFastNativeObjectAccess soa(env);
+ if (UNLIKELY(java_rhs == nullptr)) {
ThrowNullPointerException("rhs == null");
return -1;
} else {
- return soa.Decode<mirror::String*>(javaThis)->CompareTo(soa.Decode<mirror::String*>(javaRhs));
+ return soa.Decode<mirror::String*>(java_this)->CompareTo(soa.Decode<mirror::String*>(java_rhs));
+ }
+}
+
+static jstring String_concat(JNIEnv* env, jobject java_this, jobject java_string_arg) {
+ ScopedFastNativeObjectAccess soa(env);
+ if (UNLIKELY(java_string_arg == nullptr)) {
+ ThrowNullPointerException("string arg == null");
+ return nullptr;
+ }
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String*>(java_this)));
+ Handle<mirror::String> string_arg(hs.NewHandle(soa.Decode<mirror::String*>(java_string_arg)));
+ int32_t length_this = string_this->GetLength();
+ int32_t length_arg = string_arg->GetLength();
+ if (length_arg > 0 && length_this > 0) {
+ mirror::String* result = mirror::String::AllocFromStrings(soa.Self(), string_this, string_arg);
+ return soa.AddLocalReference<jstring>(result);
}
+ jobject string_original = (length_this == 0) ? java_string_arg : java_this;
+ return reinterpret_cast<jstring>(string_original);
}
static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
ScopedFastNativeObjectAccess soa(env);
// This method does not handle supplementary characters. They're dealt with in managed code.
DCHECK_LE(ch, 0xffff);
+ return soa.Decode<mirror::String*>(java_this)->FastIndexOf(ch, start);
+}
- mirror::String* s = soa.Decode<mirror::String*>(java_this);
- return s->FastIndexOf(ch, start);
+static jstring String_fastSubstring(JNIEnv* env, jobject java_this, jint start, jint length) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::String> string_this(hs.NewHandle(soa.Decode<mirror::String*>(java_this)));
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::String* result = mirror::String::AllocFromString<true>(soa.Self(), length, string_this,
+ start, allocator_type);
+ return soa.AddLocalReference<jstring>(result);
}
-static jstring String_intern(JNIEnv* env, jobject javaThis) {
+static void String_getCharsNoCheck(JNIEnv* env, jobject java_this, jint start, jint end,
+ jcharArray buffer, jint index) {
ScopedFastNativeObjectAccess soa(env);
- mirror::String* s = soa.Decode<mirror::String*>(javaThis);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray*>(buffer)));
+ soa.Decode<mirror::String*>(java_this)->GetChars(start, end, char_array, index);
+}
+
+static jstring String_intern(JNIEnv* env, jobject java_this) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::String* s = soa.Decode<mirror::String*>(java_this);
mirror::String* result = s->Intern();
return soa.AddLocalReference<jstring>(result);
}
+static void String_setCharAt(JNIEnv* env, jobject java_this, jint index, jchar c) {
+ ScopedFastNativeObjectAccess soa(env);
+ soa.Decode<mirror::String*>(java_this)->SetCharAt(index, c);
+}
+
+static jcharArray String_toCharArray(JNIEnv* env, jobject java_this) {
+ ScopedFastNativeObjectAccess soa(env);
+ mirror::String* s = soa.Decode<mirror::String*>(java_this);
+ return soa.AddLocalReference<jcharArray>(s->ToCharArray(soa.Self()));
+}
+
static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(String, charAt, "!(I)C"),
NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"),
+ NATIVE_METHOD(String, concat, "!(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(String, fastIndexOf, "!(II)I"),
+ NATIVE_METHOD(String, fastSubstring, "!(II)Ljava/lang/String;"),
+ NATIVE_METHOD(String, getCharsNoCheck, "!(II[CI)V"),
NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"),
+ NATIVE_METHOD(String, setCharAt, "!(IC)V"),
+ NATIVE_METHOD(String, toCharArray, "!()[C"),
};
void register_java_lang_String(JNIEnv* env) {
diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc
new file mode 100644
index 0000000..34d6a37
--- /dev/null
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "java_lang_StringFactory.h"
+
+#include "common_throws.h"
+#include "jni_internal.h"
+#include "mirror/object-inl.h"
+#include "mirror/string.h"
+#include "scoped_fast_native_object_access.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedLocalRef.h"
+#include "ScopedPrimitiveArray.h"
+
+namespace art {
+
+static jstring StringFactory_newStringFromBytes(JNIEnv* env, jclass, jbyteArray java_data,
+ jint high, jint offset, jint byte_count) {
+ ScopedFastNativeObjectAccess soa(env);
+ if (UNLIKELY(java_data == nullptr)) {
+ ThrowNullPointerException("data == null");
+ return nullptr;
+ }
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray*>(java_data)));
+ int32_t data_size = byte_array->GetLength();
+ if ((offset | byte_count) < 0 || byte_count > data_size - offset) {
+ soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
+ "length=%d; regionStart=%d; regionLength=%d", data_size,
+ offset, byte_count);
+ return nullptr;
+ }
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::String* result = mirror::String::AllocFromByteArray<true>(soa.Self(), byte_count,
+ byte_array, offset, high,
+ allocator_type);
+ return soa.AddLocalReference<jstring>(result);
+}
+
+static jstring StringFactory_newStringFromChars(JNIEnv* env, jclass, jint offset,
+ jint char_count, jcharArray java_data) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::CharArray> char_array(hs.NewHandle(soa.Decode<mirror::CharArray*>(java_data)));
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::String* result = mirror::String::AllocFromCharArray<true>(soa.Self(), char_count,
+ char_array, offset,
+ allocator_type);
+ return soa.AddLocalReference<jstring>(result);
+}
+
+static jstring StringFactory_newStringFromString(JNIEnv* env, jclass, jstring to_copy) {
+ ScopedFastNativeObjectAccess soa(env);
+ if (UNLIKELY(to_copy == nullptr)) {
+ ThrowNullPointerException("toCopy == null");
+ return nullptr;
+ }
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String*>(to_copy)));
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ mirror::String* result = mirror::String::AllocFromString<true>(soa.Self(), string->GetLength(),
+ string, 0, allocator_type);
+ return soa.AddLocalReference<jstring>(result);
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(StringFactory, newStringFromBytes, "!([BIII)Ljava/lang/String;"),
+ NATIVE_METHOD(StringFactory, newStringFromChars, "!(II[C)Ljava/lang/String;"),
+ NATIVE_METHOD(StringFactory, newStringFromString, "!(Ljava/lang/String;)Ljava/lang/String;"),
+};
+
+void register_java_lang_StringFactory(JNIEnv* env) {
+ REGISTER_NATIVE_METHODS("java/lang/StringFactory");
+}
+
+} // namespace art
diff --git a/runtime/native/java_lang_StringFactory.h b/runtime/native/java_lang_StringFactory.h
new file mode 100644
index 0000000..c476ad3
--- /dev/null
+++ b/runtime/native/java_lang_StringFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_STRINGFACTORY_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_STRINGFACTORY_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_lang_StringFactory(JNIEnv* env);
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_JAVA_LANG_STRINGFACTORY_H_
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 04d2e5e..2b2dfbc 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -68,6 +68,12 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA
if (!kMovingClasses && c->IsClassClass()) {
movable = false;
}
+
+ // String constructor is replaced by a StringFactory method in InvokeMethod.
+ if (c->IsStringClass()) {
+ return InvokeMethod(soa, javaMethod, nullptr, javaArgs, 1);
+ }
+
mirror::Object* receiver =
movable ? c->AllocObject(soa.Self()) : c->AllocNonMovableObject(soa.Self());
if (receiver == nullptr) {
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
new file mode 100644
index 0000000..1216824
--- /dev/null
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "jni_internal.h"
+#include "mirror/string.h"
+#include "mirror/string-inl.h"
+#include "native/libcore_util_CharsetUtils.h"
+#include "scoped_fast_native_object_access.h"
+#include "ScopedPrimitiveArray.h"
+#include "unicode/utf16.h"
+
+#include <string.h>
+
+namespace art {
+
+/**
+ * Approximates java.lang.UnsafeByteSequence so we don't have to pay the cost of calling back into
+ * Java when converting a char[] to a UTF-8 byte[]. This lets us have UTF-8 conversions slightly
+ * faster than ICU for large char[]s without paying for the NIO overhead with small char[]s.
+ *
+ * We could avoid this by keeping the UTF-8 bytes on the native heap until we're done and only
+ * creating a byte[] on the Java heap when we know how big it needs to be, but one shouldn't lie
+ * to the garbage collector (nor hide potentially large allocations from it).
+ *
+ * Because a call to append might require an allocation, it might fail. Callers should always
+ * check the return value of append.
+ */
+class NativeUnsafeByteSequence {
+ public:
+ explicit NativeUnsafeByteSequence(JNIEnv* env)
+ : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr), mSize(-1), mOffset(0) {
+ }
+
+ ~NativeUnsafeByteSequence() {
+ // Release our pointer to the raw array, copying changes back to the Java heap.
+ if (mRawArray != nullptr) {
+ mEnv->ReleaseByteArrayElements(mJavaArray, mRawArray, 0);
+ }
+ }
+
+ bool append(jbyte b) {
+ if (mOffset == mSize && !resize(mSize * 2)) {
+ return false;
+ }
+ mRawArray[mOffset++] = b;
+ return true;
+ }
+
+ bool resize(int newSize) {
+ if (newSize == mSize) {
+ return true;
+ }
+
+ // Allocate a new array.
+ jbyteArray newJavaArray = mEnv->NewByteArray(newSize);
+ if (newJavaArray == nullptr) {
+ return false;
+ }
+ jbyte* newRawArray = mEnv->GetByteArrayElements(newJavaArray, nullptr);
+ if (newRawArray == nullptr) {
+ return false;
+ }
+
+ // Copy data out of the old array and then let go of it.
+ // Note that we may be trimming the array.
+ if (mRawArray != nullptr) {
+ memcpy(newRawArray, mRawArray, mOffset);
+ mEnv->ReleaseByteArrayElements(mJavaArray, mRawArray, JNI_ABORT);
+ mEnv->DeleteLocalRef(mJavaArray);
+ }
+
+ // Point ourselves at the new array.
+ mJavaArray = newJavaArray;
+ mRawArray = newRawArray;
+ mSize = newSize;
+ return true;
+ }
+
+ jbyteArray toByteArray() {
+ // Trim any unused space, if necessary.
+ bool okay = resize(mOffset);
+ return okay ? mJavaArray : nullptr;
+ }
+
+ private:
+ JNIEnv* mEnv;
+ jbyteArray mJavaArray;
+ jbyte* mRawArray;
+ jint mSize;
+ jint mOffset;
+
+ // Disallow copy and assignment.
+ NativeUnsafeByteSequence(const NativeUnsafeByteSequence&);
+ void operator=(const NativeUnsafeByteSequence&);
+};
+
+static void CharsetUtils_asciiBytesToChars(JNIEnv* env, jclass, jbyteArray javaBytes, jint offset,
+ jint length, jcharArray javaChars) {
+ ScopedByteArrayRO bytes(env, javaBytes);
+ if (bytes.get() == nullptr) {
+ return;
+ }
+ ScopedCharArrayRW chars(env, javaChars);
+ if (chars.get() == nullptr) {
+ return;
+ }
+
+ const jbyte* src = &bytes[offset];
+ jchar* dst = &chars[0];
+ static const jchar REPLACEMENT_CHAR = 0xfffd;
+ for (int i = length - 1; i >= 0; --i) {
+ jchar ch = static_cast<jchar>(*src++ & 0xff);
+ *dst++ = (ch <= 0x7f) ? ch : REPLACEMENT_CHAR;
+ }
+}
+
+static void CharsetUtils_isoLatin1BytesToChars(JNIEnv* env, jclass, jbyteArray javaBytes,
+ jint offset, jint length, jcharArray javaChars) {
+ ScopedByteArrayRO bytes(env, javaBytes);
+ if (bytes.get() == nullptr) {
+ return;
+ }
+ ScopedCharArrayRW chars(env, javaChars);
+ if (chars.get() == nullptr) {
+ return;
+ }
+
+ const jbyte* src = &bytes[offset];
+ jchar* dst = &chars[0];
+ for (int i = length - 1; i >= 0; --i) {
+ *dst++ = static_cast<jchar>(*src++ & 0xff);
+ }
+}
+
+/**
+ * Translates the given characters to US-ASCII or ISO-8859-1 bytes, using the fact that
+ * Unicode code points between U+0000 and U+007f inclusive are identical to US-ASCII, while
+ * U+0000 to U+00ff inclusive are identical to ISO-8859-1.
+ */
+static jbyteArray charsToBytes(JNIEnv* env, jstring java_string, jint offset, jint length,
+ jchar maxValidChar) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String*>(java_string)));
+ if (string.Get() == nullptr) {
+ return nullptr;
+ }
+
+ jbyteArray javaBytes = env->NewByteArray(length);
+ ScopedByteArrayRW bytes(env, javaBytes);
+ if (bytes.get() == nullptr) {
+ return nullptr;
+ }
+
+ const jchar* src = &(string->GetValue()[offset]);
+ jbyte* dst = &bytes[0];
+ for (int i = length - 1; i >= 0; --i) {
+ jchar ch = *src++;
+ if (ch > maxValidChar) {
+ ch = '?';
+ }
+ *dst++ = static_cast<jbyte>(ch);
+ }
+
+ return javaBytes;
+}
+
+static jbyteArray CharsetUtils_toAsciiBytes(JNIEnv* env, jclass, jstring java_string, jint offset,
+ jint length) {
+ return charsToBytes(env, java_string, offset, length, 0x7f);
+}
+
+static jbyteArray CharsetUtils_toIsoLatin1Bytes(JNIEnv* env, jclass, jstring java_string,
+ jint offset, jint length) {
+ return charsToBytes(env, java_string, offset, length, 0xff);
+}
+
+static jbyteArray CharsetUtils_toUtf8Bytes(JNIEnv* env, jclass, jstring java_string, jint offset,
+ jint length) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String*>(java_string)));
+ if (string.Get() == nullptr) {
+ return nullptr;
+ }
+
+ NativeUnsafeByteSequence out(env);
+ if (!out.resize(length)) {
+ return nullptr;
+ }
+
+ const int end = offset + length;
+ for (int i = offset; i < end; ++i) {
+ jint ch = string->CharAt(i);
+ if (ch < 0x80) {
+ // One byte.
+ if (!out.append(ch)) {
+ return nullptr;
+ }
+ } else if (ch < 0x800) {
+ // Two bytes.
+ if (!out.append((ch >> 6) | 0xc0) || !out.append((ch & 0x3f) | 0x80)) {
+ return nullptr;
+ }
+ } else if (U16_IS_SURROGATE(ch)) {
+ // A supplementary character.
+ jchar high = static_cast<jchar>(ch);
+ jchar low = (i + 1 != end) ? string->CharAt(i + 1) : 0;
+ if (!U16_IS_SURROGATE_LEAD(high) || !U16_IS_SURROGATE_TRAIL(low)) {
+ if (!out.append('?')) {
+ return nullptr;
+ }
+ continue;
+ }
+ // Now we know we have a *valid* surrogate pair, we can consume the low surrogate.
+ ++i;
+ ch = U16_GET_SUPPLEMENTARY(high, low);
+ // Four bytes.
+ jbyte b1 = (ch >> 18) | 0xf0;
+ jbyte b2 = ((ch >> 12) & 0x3f) | 0x80;
+ jbyte b3 = ((ch >> 6) & 0x3f) | 0x80;
+ jbyte b4 = (ch & 0x3f) | 0x80;
+ if (!out.append(b1) || !out.append(b2) || !out.append(b3) || !out.append(b4)) {
+ return nullptr;
+ }
+ } else {
+ // Three bytes.
+ jbyte b1 = (ch >> 12) | 0xe0;
+ jbyte b2 = ((ch >> 6) & 0x3f) | 0x80;
+ jbyte b3 = (ch & 0x3f) | 0x80;
+ if (!out.append(b1) || !out.append(b2) || !out.append(b3)) {
+ return nullptr;
+ }
+ }
+ }
+ return out.toByteArray();
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(CharsetUtils, asciiBytesToChars, "!([BII[C)V"),
+ NATIVE_METHOD(CharsetUtils, isoLatin1BytesToChars, "!([BII[C)V"),
+ NATIVE_METHOD(CharsetUtils, toAsciiBytes, "!(Ljava/lang/String;II)[B"),
+ NATIVE_METHOD(CharsetUtils, toIsoLatin1Bytes, "!(Ljava/lang/String;II)[B"),
+ NATIVE_METHOD(CharsetUtils, toUtf8Bytes, "!(Ljava/lang/String;II)[B"),
+};
+
+void register_libcore_util_CharsetUtils(JNIEnv* env) {
+ REGISTER_NATIVE_METHODS("libcore/util/CharsetUtils");
+}
+
+} // namespace art
diff --git a/runtime/native/libcore_util_CharsetUtils.h b/runtime/native/libcore_util_CharsetUtils.h
new file mode 100644
index 0000000..3518bdb
--- /dev/null
+++ b/runtime/native/libcore_util_CharsetUtils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_LIBCORE_UTIL_CHARSETUTILS_H_
+#define ART_RUNTIME_NATIVE_LIBCORE_UTIL_CHARSETUTILS_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_libcore_util_CharsetUtils(JNIEnv* env);
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_LIBCORE_UTIL_CHARSETUTILS_H_
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 3463025..0d39e22 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -56,8 +56,12 @@ enum InlineMethodOpcode : uint16_t {
kIntrinsicReferenceGetReferent,
kIntrinsicCharAt,
kIntrinsicCompareTo,
+ kIntrinsicGetCharsNoCheck,
kIntrinsicIsEmptyOrLength,
kIntrinsicIndexOf,
+ kIntrinsicNewStringFromBytes,
+ kIntrinsicNewStringFromChars,
+ kIntrinsicNewStringFromString,
kIntrinsicCurrentThread,
kIntrinsicPeek,
kIntrinsicPoke,
@@ -71,6 +75,7 @@ enum InlineMethodOpcode : uint16_t {
kInlineOpNonWideConst,
kInlineOpIGet,
kInlineOpIPut,
+ kInlineStringInit,
};
std::ostream& operator<<(std::ostream& os, const InlineMethodOpcode& rhs);
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index a2ce0cb..329ceb5 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -564,14 +564,21 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM
mirror::Object* receiver = nullptr;
if (!m->IsStatic()) {
- // Check that the receiver is non-null and an instance of the field's declaring class.
- receiver = soa.Decode<mirror::Object*>(javaReceiver);
- if (!VerifyObjectIsClass(receiver, declaring_class)) {
- return nullptr;
- }
+ // Replace calls to String.<init> with equivalent StringFactory call.
+ if (declaring_class->IsStringClass() && m->IsConstructor()) {
+ jmethodID mid = soa.EncodeMethod(m);
+ m = soa.DecodeMethod(WellKnownClasses::StringInitToStringFactoryMethodID(mid));
+ CHECK(javaReceiver == nullptr);
+ } else {
+ // Check that the receiver is non-null and an instance of the field's declaring class.
+ receiver = soa.Decode<mirror::Object*>(javaReceiver);
+ if (!VerifyObjectIsClass(receiver, declaring_class)) {
+ return nullptr;
+ }
- // Find the actual implementation of the virtual method.
- m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m);
+ // Find the actual implementation of the virtual method.
+ m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m);
+ }
}
// Get our arrays of arguments and their types, and check they're the same size.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 48bca62..eb60318 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -91,6 +91,7 @@
#include "native/java_lang_Object.h"
#include "native/java_lang_Runtime.h"
#include "native/java_lang_String.h"
+#include "native/java_lang_StringFactory.h"
#include "native/java_lang_System.h"
#include "native/java_lang_Thread.h"
#include "native/java_lang_Throwable.h"
@@ -103,6 +104,7 @@
#include "native/java_lang_reflect_Method.h"
#include "native/java_lang_reflect_Proxy.h"
#include "native/java_util_concurrent_atomic_AtomicLong.h"
+#include "native/libcore_util_CharsetUtils.h"
#include "native/org_apache_harmony_dalvik_ddmc_DdmServer.h"
#include "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.h"
#include "native/sun_misc_Unsafe.h"
@@ -1170,11 +1172,13 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_java_lang_ref_Reference(env);
register_java_lang_Runtime(env);
register_java_lang_String(env);
+ register_java_lang_StringFactory(env);
register_java_lang_System(env);
register_java_lang_Thread(env);
register_java_lang_Throwable(env);
register_java_lang_VMClassLoader(env);
register_java_util_concurrent_atomic_AtomicLong(env);
+ register_libcore_util_CharsetUtils(env);
register_org_apache_harmony_dalvik_ddmc_DdmServer(env);
register_org_apache_harmony_dalvik_ddmc_DdmVmInternal(env);
register_sun_misc_Unsafe(env);
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c35f4ca..348d5c6 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -31,6 +31,7 @@
#include "gc_root.h"
#include "instrumentation.h"
#include "jobject_comparator.h"
+#include "method_reference.h"
#include "object_callbacks.h"
#include "offsets.h"
#include "profiler_options.h"
@@ -86,6 +87,8 @@ struct TraceConfig;
class Transaction;
typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
+typedef SafeMap<MethodReference, SafeMap<uint32_t, std::set<uint32_t>>,
+ MethodReferenceComparator> MethodRefToStringInitRegMap;
// Not all combinations of flags are valid. You may not visit all roots as well as the new roots
// (no logical reason to do this). You also may not start logging new roots and stop logging new
@@ -558,6 +561,10 @@ class Runtime {
return jit_options_.get();
}
+ MethodRefToStringInitRegMap& GetStringInitMap() {
+ return method_ref_string_init_reg_map_;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -737,6 +744,8 @@ class Runtime {
// zygote.
uint32_t zygote_max_failed_boots_;
+ MethodRefToStringInitRegMap method_ref_string_init_reg_map_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 9f7c303..c8aad1b 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -105,6 +105,43 @@ void Thread::InitTlsEntryPoints() {
&tlsPtr_.quick_entrypoints);
}
+void Thread::InitStringEntryPoints() {
+ ScopedObjectAccess soa(this);
+ QuickEntryPoints* qpoints = &tlsPtr_.quick_entrypoints;
+ qpoints->pNewEmptyString = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newEmptyString));
+ qpoints->pNewStringFromBytes_B = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_B));
+ qpoints->pNewStringFromBytes_BI = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BI));
+ qpoints->pNewStringFromBytes_BII = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BII));
+ qpoints->pNewStringFromBytes_BIII = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIII));
+ qpoints->pNewStringFromBytes_BIIString = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIIString));
+ qpoints->pNewStringFromBytes_BString = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BString));
+ qpoints->pNewStringFromBytes_BIICharset = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIICharset));
+ qpoints->pNewStringFromBytes_BCharset = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BCharset));
+ qpoints->pNewStringFromChars_C = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromChars_C));
+ qpoints->pNewStringFromChars_CII = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromChars_CII));
+ qpoints->pNewStringFromChars_IIC = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromChars_IIC));
+ qpoints->pNewStringFromCodePoints = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromCodePoints));
+ qpoints->pNewStringFromString = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromString));
+ qpoints->pNewStringFromStringBuffer = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromStringBuffer));
+ qpoints->pNewStringFromStringBuilder = reinterpret_cast<void(*)()>(
+ soa.DecodeMethod(WellKnownClasses::java_lang_StringFactory_newStringFromStringBuilder));
+}
+
void Thread::ResetQuickAllocEntryPointsForThread() {
ResetQuickAllocEntryPoints(&tlsPtr_.quick_entrypoints);
}
@@ -163,6 +200,7 @@ void* Thread::CreateCallback(void* arg) {
}
{
ScopedObjectAccess soa(self);
+ self->InitStringEntryPoints();
// Copy peer into self, deleting global reference when done.
CHECK(self->tlsPtr_.jpeer != nullptr);
@@ -409,6 +447,8 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g
}
}
+ self->InitStringEntryPoints();
+
CHECK_NE(self->GetState(), kRunnable);
self->SetState(kNative);
@@ -1930,6 +1970,9 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) {
QUICK_ENTRY_POINT_INFO(pAllocObjectWithAccessCheck)
QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray)
QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck)
+ QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes)
+ QUICK_ENTRY_POINT_INFO(pAllocStringFromChars)
+ QUICK_ENTRY_POINT_INFO(pAllocStringFromString)
QUICK_ENTRY_POINT_INFO(pInstanceofNonTrivial)
QUICK_ENTRY_POINT_INFO(pCheckCast)
QUICK_ENTRY_POINT_INFO(pInitializeStaticStorage)
@@ -2013,6 +2056,22 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) {
QUICK_ENTRY_POINT_INFO(pDeoptimize)
QUICK_ENTRY_POINT_INFO(pA64Load)
QUICK_ENTRY_POINT_INFO(pA64Store)
+ QUICK_ENTRY_POINT_INFO(pNewEmptyString)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_B)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BI)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BII)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BIII)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BIIString)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BString)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BIICharset)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BCharset)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromChars_C)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromChars_CII)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromChars_IIC)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromCodePoints)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromString)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuffer)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuilder)
#undef QUICK_ENTRY_POINT_INFO
os << offset;
diff --git a/runtime/thread.h b/runtime/thread.h
index 35b785d..e766daa 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -541,6 +541,16 @@ class Thread {
}
public:
+ static uint32_t QuickEntryPointOffsetWithSize(size_t quick_entrypoint_offset,
+ size_t pointer_size) {
+ DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size;
+ if (pointer_size == 4) {
+ return QuickEntryPointOffset<4>(quick_entrypoint_offset).Uint32Value();
+ } else {
+ return QuickEntryPointOffset<8>(quick_entrypoint_offset).Uint32Value();
+ }
+ }
+
template<size_t pointer_size>
static ThreadOffset<pointer_size> QuickEntryPointOffset(size_t quick_entrypoint_offset) {
return ThreadOffsetFromTlsPtr<pointer_size>(
@@ -911,6 +921,8 @@ class Thread {
void PushVerifier(verifier::MethodVerifier* verifier);
void PopVerifier(verifier::MethodVerifier* verifier);
+ void InitStringEntryPoints();
+
private:
explicit Thread(bool daemon);
~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 3d13c3e..10600e2 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -107,15 +107,6 @@ void ConvertUtf16ToModifiedUtf8(char* utf8_out, const uint16_t* utf16_in, size_t
}
}
-int32_t ComputeUtf16Hash(mirror::CharArray* chars, int32_t offset,
- size_t char_count) {
- uint32_t hash = 0;
- for (size_t i = 0; i < char_count; i++) {
- hash = hash * 31 + chars->Get(offset + i);
- }
- return static_cast<int32_t>(hash);
-}
-
int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) {
uint32_t hash = 0;
while (char_count--) {
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index d8f8950..259fe33 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -151,9 +151,6 @@ TEST_F(UtilsTest, PrettyField) {
f = java_lang_String->FindDeclaredInstanceField("count", "I");
EXPECT_EQ("int java.lang.String.count", PrettyField(f));
EXPECT_EQ("java.lang.String.count", PrettyField(f, false));
- f = java_lang_String->FindDeclaredInstanceField("value", "[C");
- EXPECT_EQ("char[] java.lang.String.value", PrettyField(f));
- EXPECT_EQ("java.lang.String.value", PrettyField(f, false));
}
TEST_F(UtilsTest, PrettySize) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 065df05..475fe8b 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -516,6 +516,23 @@ mirror::ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) {
return GetQuickInvokedMethod(inst, register_line, is_range, false);
}
+SafeMap<uint32_t, std::set<uint32_t>> MethodVerifier::FindStringInitMap(mirror::ArtMethod* m) {
+ Thread* self = Thread::Current();
+ StackHandleScope<3> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
+ Handle<mirror::ArtMethod> method(hs.NewHandle(m));
+ MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
+ m->GetCodeItem(), m->GetDexMethodIndex(), method, m->GetAccessFlags(),
+ true, true, false, true);
+ return verifier.FindStringInitMap();
+}
+
+SafeMap<uint32_t, std::set<uint32_t>>& MethodVerifier::FindStringInitMap() {
+ Verify();
+ return GetStringInitPcRegMap();
+}
+
bool MethodVerifier::Verify() {
// If there aren't any instructions, make sure that's expected, then exit successfully.
if (code_item_ == nullptr) {
@@ -2445,7 +2462,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
* Replace the uninitialized reference with an initialized one. We need to do this for all
* registers that have the same object instance in them, not just the "this" register.
*/
- work_line_->MarkRefsAsInitialized(this, this_type);
+ const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ work_line_->MarkRefsAsInitialized(this, this_type, this_reg, work_insn_idx_);
}
if (return_type == nullptr) {
return_type = &reg_types_.FromDescriptor(GetClassLoader(), return_type_descriptor,
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 2914b7c..452d1dd 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -199,6 +199,9 @@ class MethodVerifier {
static mirror::ArtMethod* FindInvokedMethodAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static SafeMap<uint32_t, std::set<uint32_t>> FindStringInitMap(mirror::ArtMethod* m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void Shutdown();
@@ -263,6 +266,10 @@ class MethodVerifier {
return (method_access_flags_ & kAccStatic) != 0;
}
+ SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() {
+ return string_init_pc_reg_map_;
+ }
+
private:
// Private constructor for dumping.
MethodVerifier(Thread* self, const DexFile* dex_file, Handle<mirror::DexCache> dex_cache,
@@ -307,6 +314,9 @@ class MethodVerifier {
mirror::ArtMethod* FindInvokedMethodAtDexPc(uint32_t dex_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SafeMap<uint32_t, std::set<uint32_t>>& FindStringInitMap()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Compute the width of the instruction at each address in the instruction stream, and store it in
* insn_flags_. Addresses that are in the middle of an instruction, or that are part of switch
@@ -743,6 +753,12 @@ class MethodVerifier {
MethodVerifier* link_;
friend class art::Thread;
+
+ // Map of dex pcs of invocations of java.lang.String.<init> to the set of other registers that
+ // contain the uninitialized this pointer to that invoke. Will contain no entry if there are
+ // no other registers.
+ SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_;
+
DISALLOW_COPY_AND_ASSIGN(MethodVerifier);
};
std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs);
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index ed588fc..8445751 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -127,14 +127,25 @@ bool RegisterLine::VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsr
return true;
}
-void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) {
+void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type,
+ uint32_t this_reg, uint32_t dex_pc) {
DCHECK(uninit_type.IsUninitializedTypes());
+ bool is_string = !uninit_type.IsUnresolvedTypes() && uninit_type.GetClass()->IsStringClass();
const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type);
size_t changed = 0;
for (uint32_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(verifier, i).Equals(uninit_type)) {
line_[i] = init_type.GetId();
changed++;
+ if (i != this_reg && is_string) {
+ auto it = verifier->GetStringInitPcRegMap().find(dex_pc);
+ if (it != verifier->GetStringInitPcRegMap().end()) {
+ it->second.insert(i);
+ } else {
+ std::set<uint32_t> reg_set = { i };
+ verifier->GetStringInitPcRegMap().Put(dex_pc, reg_set);
+ }
+ }
}
}
DCHECK_GT(changed, 0u);
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 376dbf1..0de0d9c 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -138,7 +138,8 @@ class RegisterLine {
* reference type. This is called when an appropriate constructor is invoked -- all copies of
* the reference must be marked as initialized.
*/
- void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type)
+ void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type,
+ uint32_t this_reg, uint32_t dex_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index a2d0427..2843806 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -49,6 +49,7 @@ jclass WellKnownClasses::java_lang_reflect_Proxy;
jclass WellKnownClasses::java_lang_RuntimeException;
jclass WellKnownClasses::java_lang_StackOverflowError;
jclass WellKnownClasses::java_lang_String;
+jclass WellKnownClasses::java_lang_StringFactory;
jclass WellKnownClasses::java_lang_System;
jclass WellKnownClasses::java_lang_Thread;
jclass WellKnownClasses::java_lang_Thread__UncaughtExceptionHandler;
@@ -79,6 +80,38 @@ jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
jmethodID WellKnownClasses::java_lang_Short_valueOf;
+jmethodID WellKnownClasses::java_lang_String_init;
+jmethodID WellKnownClasses::java_lang_String_init_B;
+jmethodID WellKnownClasses::java_lang_String_init_BI;
+jmethodID WellKnownClasses::java_lang_String_init_BII;
+jmethodID WellKnownClasses::java_lang_String_init_BIII;
+jmethodID WellKnownClasses::java_lang_String_init_BIIString;
+jmethodID WellKnownClasses::java_lang_String_init_BString;
+jmethodID WellKnownClasses::java_lang_String_init_BIICharset;
+jmethodID WellKnownClasses::java_lang_String_init_BCharset;
+jmethodID WellKnownClasses::java_lang_String_init_C;
+jmethodID WellKnownClasses::java_lang_String_init_CII;
+jmethodID WellKnownClasses::java_lang_String_init_IIC;
+jmethodID WellKnownClasses::java_lang_String_init_String;
+jmethodID WellKnownClasses::java_lang_String_init_StringBuffer;
+jmethodID WellKnownClasses::java_lang_String_init_III;
+jmethodID WellKnownClasses::java_lang_String_init_StringBuilder;
+jmethodID WellKnownClasses::java_lang_StringFactory_newEmptyString;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_B;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BI;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BII;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIII;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIIString;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BString;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BIICharset;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromBytes_BCharset;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_C;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_CII;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromChars_IIC;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromString;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromStringBuffer;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromCodePoints;
+jmethodID WellKnownClasses::java_lang_StringFactory_newStringFromStringBuilder;
jmethodID WellKnownClasses::java_lang_System_runFinalization = nullptr;
jmethodID WellKnownClasses::java_lang_Thread_init;
jmethodID WellKnownClasses::java_lang_Thread_run;
@@ -188,6 +221,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
java_lang_String = CacheClass(env, "java/lang/String");
+ java_lang_StringFactory = CacheClass(env, "java/lang/StringFactory");
java_lang_System = CacheClass(env, "java/lang/System");
java_lang_Thread = CacheClass(env, "java/lang/Thread");
java_lang_Thread__UncaughtExceptionHandler = CacheClass(env,
@@ -223,6 +257,62 @@ void WellKnownClasses::Init(JNIEnv* env) {
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ java_lang_String_init = CacheMethod(env, java_lang_String, false, "<init>", "()V");
+ java_lang_String_init_B = CacheMethod(env, java_lang_String, false, "<init>", "([B)V");
+ java_lang_String_init_BI = CacheMethod(env, java_lang_String, false, "<init>", "([BI)V");
+ java_lang_String_init_BII = CacheMethod(env, java_lang_String, false, "<init>", "([BII)V");
+ java_lang_String_init_BIII = CacheMethod(env, java_lang_String, false, "<init>", "([BIII)V");
+ java_lang_String_init_BIIString = CacheMethod(env, java_lang_String, false, "<init>",
+ "([BIILjava/lang/String;)V");
+ java_lang_String_init_BString = CacheMethod(env, java_lang_String, false, "<init>",
+ "([BLjava/lang/String;)V");
+ java_lang_String_init_BIICharset = CacheMethod(env, java_lang_String, false, "<init>",
+ "([BIILjava/nio/charset/Charset;)V");
+ java_lang_String_init_BCharset = CacheMethod(env, java_lang_String, false, "<init>",
+ "([BLjava/nio/charset/Charset;)V");
+ java_lang_String_init_C = CacheMethod(env, java_lang_String, false, "<init>", "([C)V");
+ java_lang_String_init_CII = CacheMethod(env, java_lang_String, false, "<init>", "([CII)V");
+ java_lang_String_init_IIC = CacheMethod(env, java_lang_String, false, "<init>", "(II[C)V");
+ java_lang_String_init_String = CacheMethod(env, java_lang_String, false, "<init>",
+ "(Ljava/lang/String;)V");
+ java_lang_String_init_StringBuffer = CacheMethod(env, java_lang_String, false, "<init>",
+ "(Ljava/lang/StringBuffer;)V");
+ java_lang_String_init_III = CacheMethod(env, java_lang_String, false, "<init>", "([III)V");
+ java_lang_String_init_StringBuilder = CacheMethod(env, java_lang_String, false, "<init>",
+ "(Ljava/lang/StringBuilder;)V");
+ java_lang_StringFactory_newEmptyString = CacheMethod(env, java_lang_StringFactory, true,
+ "newEmptyString", "()Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_B = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromBytes", "([B)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BI = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromBytes", "([BI)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BII = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromBytes", "([BII)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BIII = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromBytes", "([BIII)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BIIString = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromBytes", "([BIILjava/lang/String;)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BString = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromBytes", "([BLjava/lang/String;)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BIICharset = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromBytes", "([BIILjava/nio/charset/Charset;)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromBytes_BCharset = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromBytes", "([BLjava/nio/charset/Charset;)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromChars_C = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromChars", "([C)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromChars_CII = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromChars", "([CII)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromChars_IIC = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromChars", "(II[C)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromString = CacheMethod(env, java_lang_StringFactory, true,
+ "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromStringBuffer = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromStringBuffer", "(Ljava/lang/StringBuffer;)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromCodePoints = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromCodePoints", "([III)Ljava/lang/String;");
+ java_lang_StringFactory_newStringFromStringBuilder = CacheMethod(env, java_lang_StringFactory,
+ true, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;");
+
dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
@@ -265,6 +355,8 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer");
java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long");
java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short");
+
+ Thread::Current()->InitStringEntryPoints();
}
void WellKnownClasses::LateInit(JNIEnv* env) {
@@ -276,4 +368,43 @@ mirror::Class* WellKnownClasses::ToClass(jclass global_jclass) {
return reinterpret_cast<mirror::Class*>(Thread::Current()->DecodeJObject(global_jclass));
}
+jmethodID WellKnownClasses::StringInitToStringFactoryMethodID(jmethodID string_init) {
+ // TODO: Prioritize ordering.
+ if (string_init == java_lang_String_init) {
+ return java_lang_StringFactory_newEmptyString;
+ } else if (string_init == java_lang_String_init_B) {
+ return java_lang_StringFactory_newStringFromBytes_B;
+ } else if (string_init == java_lang_String_init_BI) {
+ return java_lang_StringFactory_newStringFromBytes_BI;
+ } else if (string_init == java_lang_String_init_BII) {
+ return java_lang_StringFactory_newStringFromBytes_BII;
+ } else if (string_init == java_lang_String_init_BIII) {
+ return java_lang_StringFactory_newStringFromBytes_BIII;
+ } else if (string_init == java_lang_String_init_BIIString) {
+ return java_lang_StringFactory_newStringFromBytes_BIIString;
+ } else if (string_init == java_lang_String_init_BString) {
+ return java_lang_StringFactory_newStringFromBytes_BString;
+ } else if (string_init == java_lang_String_init_BIICharset) {
+ return java_lang_StringFactory_newStringFromBytes_BIICharset;
+ } else if (string_init == java_lang_String_init_BCharset) {
+ return java_lang_StringFactory_newStringFromBytes_BCharset;
+ } else if (string_init == java_lang_String_init_C) {
+ return java_lang_StringFactory_newStringFromChars_C;
+ } else if (string_init == java_lang_String_init_CII) {
+ return java_lang_StringFactory_newStringFromChars_CII;
+ } else if (string_init == java_lang_String_init_IIC) {
+ return java_lang_StringFactory_newStringFromChars_IIC;
+ } else if (string_init == java_lang_String_init_String) {
+ return java_lang_StringFactory_newStringFromString;
+ } else if (string_init == java_lang_String_init_StringBuffer) {
+ return java_lang_StringFactory_newStringFromStringBuffer;
+ } else if (string_init == java_lang_String_init_III) {
+ return java_lang_StringFactory_newStringFromCodePoints;
+ } else if (string_init == java_lang_String_init_StringBuilder) {
+ return java_lang_StringFactory_newStringFromStringBuilder;
+ }
+ LOG(FATAL) << "Could not find StringFactory method for String.<init>";
+ return nullptr;
+}
+
} // namespace art
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index cef9d55..acb2656 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -35,6 +35,7 @@ struct WellKnownClasses {
public:
static void Init(JNIEnv* env); // Run before native methods are registered.
static void LateInit(JNIEnv* env); // Run after native methods are registered.
+ static jmethodID StringInitToStringFactoryMethodID(jmethodID string_init);
static mirror::Class* ToClass(jclass global_jclass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -60,6 +61,7 @@ struct WellKnownClasses {
static jclass java_lang_RuntimeException;
static jclass java_lang_StackOverflowError;
static jclass java_lang_String;
+ static jclass java_lang_StringFactory;
static jclass java_lang_System;
static jclass java_lang_Thread;
static jclass java_lang_ThreadGroup;
@@ -90,6 +92,38 @@ struct WellKnownClasses {
static jmethodID java_lang_reflect_Proxy_invoke;
static jmethodID java_lang_Runtime_nativeLoad;
static jmethodID java_lang_Short_valueOf;
+ static jmethodID java_lang_String_init;
+ static jmethodID java_lang_String_init_B;
+ static jmethodID java_lang_String_init_BI;
+ static jmethodID java_lang_String_init_BII;
+ static jmethodID java_lang_String_init_BIII;
+ static jmethodID java_lang_String_init_BIIString;
+ static jmethodID java_lang_String_init_BString;
+ static jmethodID java_lang_String_init_BIICharset;
+ static jmethodID java_lang_String_init_BCharset;
+ static jmethodID java_lang_String_init_C;
+ static jmethodID java_lang_String_init_CII;
+ static jmethodID java_lang_String_init_IIC;
+ static jmethodID java_lang_String_init_String;
+ static jmethodID java_lang_String_init_StringBuffer;
+ static jmethodID java_lang_String_init_III;
+ static jmethodID java_lang_String_init_StringBuilder;
+ static jmethodID java_lang_StringFactory_newEmptyString;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_B;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BI;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BII;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BIII;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BIIString;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BString;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BIICharset;
+ static jmethodID java_lang_StringFactory_newStringFromBytes_BCharset;
+ static jmethodID java_lang_StringFactory_newStringFromChars_C;
+ static jmethodID java_lang_StringFactory_newStringFromChars_CII;
+ static jmethodID java_lang_StringFactory_newStringFromChars_IIC;
+ static jmethodID java_lang_StringFactory_newStringFromString;
+ static jmethodID java_lang_StringFactory_newStringFromStringBuffer;
+ static jmethodID java_lang_StringFactory_newStringFromCodePoints;
+ static jmethodID java_lang_StringFactory_newStringFromStringBuilder;
static jmethodID java_lang_System_runFinalization;
static jmethodID java_lang_Thread_init;
static jmethodID java_lang_Thread_run;