ART: Add integer & long parsing cutout to unstarted runtime
Add a cutout for Integer.parseInt and Long.parseLong. Only handle
(some) successful cases, and abort the transaction in the rest.
Add tests.
Allows to compile-time initialize:
* com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
* com.android.org.bouncycastle.asn1.x509.X509ObjectIdentifiers
* sun.security.x509.InhibitAnyPolicyExtension
Bug: 27265238
(cherry picked from commit 9c62dab16e4be0c19a2e6c6c0cc2b24c2814c6f2)
Change-Id: I14c0836fd9ec7c9bf49d8186ff14a3c6830ff711
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 239b825..0e18945 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -16,7 +16,11 @@
#include "unstarted_runtime.h"
+#include <errno.h>
+#include <stdlib.h>
+
#include <cmath>
+#include <limits>
#include <unordered_map>
#include "ScopedLocalRef.h"
@@ -1069,6 +1073,93 @@
}
}
+// A cutout for Integer.parseInt(String). Note: this code is conservative and will bail instead
+// of correctly handling the corner cases.
+void UnstartedRuntime::UnstartedIntegerParseInt(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime.");
+ return;
+ }
+
+ std::string string_value = obj->AsString()->ToModifiedUtf8();
+ if (string_value.empty()) {
+ AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime.");
+ return;
+ }
+
+ const char* c_str = string_value.c_str();
+ char *end;
+ // Can we set errno to 0? Is this always a variable, and not a macro?
+ // Worst case, we'll incorrectly fail a transaction. Seems OK.
+ int64_t l = strtol(c_str, &end, 10);
+
+ if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
+ (errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ if (l == 0) {
+ // Check whether the string wasn't exactly zero.
+ if (string_value != "0") {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ } else if (*end != '\0') {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+
+ result->SetI(static_cast<int32_t>(l));
+}
+
+// A cutout for Long.parseLong.
+//
+// Note: for now use code equivalent to Integer.parseInt, as the full range may not be supported
+// well.
+void UnstartedRuntime::UnstartedLongParseLong(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime.");
+ return;
+ }
+
+ std::string string_value = obj->AsString()->ToModifiedUtf8();
+ if (string_value.empty()) {
+ AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime.");
+ return;
+ }
+
+ const char* c_str = string_value.c_str();
+ char *end;
+ // Can we set errno to 0? Is this always a variable, and not a macro?
+ // Worst case, we'll incorrectly fail a transaction. Seems OK.
+ int64_t l = strtol(c_str, &end, 10);
+
+ // Note: comparing against int32_t min/max is intentional here.
+ if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
+ (errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ if (l == 0) {
+ // Check whether the string wasn't exactly zero.
+ if (string_value != "0") {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ } else if (*end != '\0') {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+
+ result->SetJ(l);
+}
+
void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(
Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED,
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 7aeec61..dc7bcfe 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -57,7 +57,9 @@
V(UnsafeCompareAndSwapLong, "boolean sun.misc.Unsafe.compareAndSwapLong(java.lang.Object, long, long, long)") \
V(UnsafeCompareAndSwapObject, "boolean sun.misc.Unsafe.compareAndSwapObject(java.lang.Object, long, java.lang.Object, java.lang.Object)") \
V(UnsafeGetObjectVolatile, "java.lang.Object sun.misc.Unsafe.getObjectVolatile(java.lang.Object, long)") \
- V(UnsafePutOrderedObject, "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)")
+ V(UnsafePutOrderedObject, "void sun.misc.Unsafe.putOrderedObject(java.lang.Object, long, java.lang.Object)") \
+ V(IntegerParseInt, "int java.lang.Integer.parseInt(java.lang.String)") \
+ V(LongParseLong, "long java.lang.Long.parseLong(java.lang.String)")
// Methods that are native.
#define UNSTARTED_RUNTIME_JNI_LIST(V) \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index fb53b1d..f40e4e3 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -508,5 +508,100 @@
ShadowFrame::DeleteDeoptimizedFrame(tmp);
}
+TEST_F(UnstartedRuntimeTest, IntegerParseIntTest) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
+ // suffixes).
+ constexpr const char* test_string = "-2147483646";
+ constexpr int32_t test_values[] = {
+ 6,
+ 46,
+ 646,
+ 3646,
+ 83646,
+ 483646,
+ 7483646,
+ 47483646,
+ 147483646,
+ 2147483646,
+ -2147483646
+ };
+
+ static_assert(arraysize(test_values) == 11U, "test_values");
+ CHECK_EQ(strlen(test_string), 11U);
+
+ for (size_t i = 0; i <= 10; ++i) {
+ const char* test_value = &test_string[10 - i];
+
+ StackHandleScope<1> hs_str(self);
+ Handle<mirror::String> h_str(
+ hs_str.NewHandle(mirror::String::AllocFromModifiedUtf8(self, test_value)));
+ ASSERT_NE(h_str.Get(), nullptr);
+ ASSERT_FALSE(self->IsExceptionPending());
+
+ tmp->SetVRegReference(0, h_str.Get());
+
+ JValue result;
+ UnstartedIntegerParseInt(self, tmp, &result, 0);
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(result.GetI(), test_values[i]);
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+// Right now the same as Integer.Parse
+TEST_F(UnstartedRuntimeTest, LongParseLongTest) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ // Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
+ // suffixes).
+ constexpr const char* test_string = "-2147483646";
+ constexpr int64_t test_values[] = {
+ 6,
+ 46,
+ 646,
+ 3646,
+ 83646,
+ 483646,
+ 7483646,
+ 47483646,
+ 147483646,
+ 2147483646,
+ -2147483646
+ };
+
+ static_assert(arraysize(test_values) == 11U, "test_values");
+ CHECK_EQ(strlen(test_string), 11U);
+
+ for (size_t i = 0; i <= 10; ++i) {
+ const char* test_value = &test_string[10 - i];
+
+ StackHandleScope<1> hs_str(self);
+ Handle<mirror::String> h_str(
+ hs_str.NewHandle(mirror::String::AllocFromModifiedUtf8(self, test_value)));
+ ASSERT_NE(h_str.Get(), nullptr);
+ ASSERT_FALSE(self->IsExceptionPending());
+
+ tmp->SetVRegReference(0, h_str.Get());
+
+ JValue result;
+ UnstartedLongParseLong(self, tmp, &result, 0);
+
+ ASSERT_FALSE(self->IsExceptionPending());
+ EXPECT_EQ(result.GetJ(), test_values[i]);
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
} // namespace interpreter
} // namespace art