diff options
Diffstat (limited to 'runtime/native')
-rw-r--r-- | runtime/native/java_lang_Class.cc | 2 | ||||
-rw-r--r-- | runtime/native/java_lang_String.cc | 74 | ||||
-rw-r--r-- | runtime/native/java_lang_StringFactory.cc | 89 | ||||
-rw-r--r-- | runtime/native/java_lang_StringFactory.h | 28 | ||||
-rw-r--r-- | runtime/native/java_lang_reflect_Constructor.cc | 6 | ||||
-rw-r--r-- | runtime/native/libcore_util_CharsetUtils.cc | 264 | ||||
-rw-r--r-- | runtime/native/libcore_util_CharsetUtils.h | 28 |
7 files changed, 483 insertions, 8 deletions
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 48a8bc76a0..c4aecb11bb 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 2d153d4490..aa64b79994 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 0000000000..34d6a37ab2 --- /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 0000000000..c476ad3db9 --- /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 04d2e5e324..2b2dfbceb8 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 0000000000..1216824b5a --- /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 0000000000..3518bdb6f7 --- /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_ |