diff options
author | Jeff Hao <jeffhao@google.com> | 2014-01-15 13:49:50 -0800 |
---|---|---|
committer | Jeff Hao <jeffhao@google.com> | 2015-04-27 18:54:52 -0700 |
commit | 848f70a3d73833fc1bf3032a9ff6812e429661d9 (patch) | |
tree | b0349b3a40aab5a915af491b100659a5ca9fbbf6 /runtime | |
parent | d14438f0c5071962be7fab572b54687d32d9d087 (diff) | |
download | art-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')
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 = ®_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; |