Merge "Move native code of ICU4C regex usage into external/icu"
diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/ReachabilitySensitive.java b/dalvik/src/main/java/dalvik/annotation/optimization/ReachabilitySensitive.java
index 4eea1a5..7b82c3c 100644
--- a/dalvik/src/main/java/dalvik/annotation/optimization/ReachabilitySensitive.java
+++ b/dalvik/src/main/java/dalvik/annotation/optimization/ReachabilitySensitive.java
@@ -79,6 +79,7 @@
  *
  * @hide
  */
+@libcore.api.IntraCoreApi
 @Retention(RetentionPolicy.RUNTIME)  // Let the GC or interpreter ask, if they need to.
                                      // TODO(b/72332040): Reconsider retention later.
 @Target({ElementType.FIELD, ElementType.METHOD})
diff --git a/luni/src/main/java/libcore/util/NativeAllocationRegistry.java b/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
index 5dc1e98..e114ebe 100644
--- a/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
+++ b/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
@@ -41,6 +41,7 @@
  * @hide
  */
 @libcore.api.CorePlatformApi
+@libcore.api.IntraCoreApi
 public class NativeAllocationRegistry {
 
     private final ClassLoader classLoader;
@@ -118,6 +119,7 @@
      *                     kind of native allocation
      */
     @libcore.api.CorePlatformApi
+    @libcore.api.IntraCoreApi
     public static NativeAllocationRegistry createMalloced(
             ClassLoader classLoader, long freeFunction) {
         return new NativeAllocationRegistry(classLoader, freeFunction, 0, true);
@@ -227,6 +229,7 @@
      *                           thrown.
      */
     @libcore.api.CorePlatformApi
+    @libcore.api.IntraCoreApi
     public Runnable registerNativeAllocation(Object referent, long nativePtr) {
         if (referent == null) {
             throw new IllegalArgumentException("referent is null");
diff --git a/luni/src/main/native/Android.bp b/luni/src/main/native/Android.bp
index 85c6430..f94ff95 100644
--- a/luni/src/main/native/Android.bp
+++ b/luni/src/main/native/Android.bp
@@ -31,8 +31,6 @@
         "java_lang_invoke_MethodHandle.cpp",
         "java_lang_invoke_VarHandle.cpp",
         "java_math_NativeBN.cpp",
-        "java_util_regex_Matcher.cpp",
-        "java_util_regex_Pattern.cpp",
         "libcore_icu_ICU.cpp",
         "libcore_icu_NativeConverter.cpp",
         "libcore_icu_TimeZoneNames.cpp",
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
index bf567aa..a6b7b9e 100644
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -61,7 +61,6 @@
 jclass longClass;
 jclass netlinkSocketAddressClass;
 jclass packetSocketAddressClass;
-jclass patternSyntaxExceptionClass;
 jclass stringClass;
 jclass structAddrinfoClass;
 jclass structGroupReqClass;
@@ -108,7 +107,6 @@
     longClass = findClass(env, "java/lang/Long");
     netlinkSocketAddressClass = findClass(env, "android/system/NetlinkSocketAddress");
     packetSocketAddressClass = findClass(env, "android/system/PacketSocketAddress");
-    patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException");
     stringClass = findClass(env, "java/lang/String");
     structAddrinfoClass = findClass(env, "android/system/StructAddrinfo");
     structGroupReqClass = findClass(env, "android/system/StructGroupReq");
@@ -223,11 +221,6 @@
     return packetSocketAddressClass;
 }
 
-jclass JniConstants::GetPatternSyntaxExceptionClass(JNIEnv* env) {
-    EnsureJniConstantsInitialized(env);
-    return patternSyntaxExceptionClass;
-}
-
 jclass JniConstants::GetStringClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return stringClass;
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
index 021dc53..2931411 100644
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -46,7 +46,6 @@
     static jclass GetLongClass(JNIEnv* env);
     static jclass GetNetlinkSocketAddressClass(JNIEnv* env);
     static jclass GetPacketSocketAddressClass(JNIEnv* env);
-    static jclass GetPatternSyntaxExceptionClass(JNIEnv* env);
     static jclass GetStringClass(JNIEnv* env);
     static jclass GetStructAddrinfoClass(JNIEnv* env);
     static jclass GetStructFlockClass(JNIEnv* env);
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 0b4a94a..40e580e 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -40,8 +40,6 @@
     REGISTER(register_java_lang_invoke_MethodHandle);
     REGISTER(register_java_lang_invoke_VarHandle);
     REGISTER(register_java_math_NativeBN);
-    REGISTER(register_java_util_regex_Matcher);
-    REGISTER(register_java_util_regex_Pattern);
     REGISTER(register_libcore_icu_ICU);
     REGISTER(register_libcore_icu_NativeConverter);
     REGISTER(register_libcore_icu_TimeZoneNames);
diff --git a/luni/src/main/native/java_util_regex_Matcher.cpp b/luni/src/main/native/java_util_regex_Matcher.cpp
deleted file mode 100644
index 12fb764..0000000
--- a/luni/src/main/native/java_util_regex_Matcher.cpp
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "Matcher"
-
-#include <memory>
-#include <stdlib.h>
-
-#include <android-base/logging.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedStringChars.h>
-#include <nativehelper/jni_macros.h>
-
-#include "IcuUtilities.h"
-#include "JniException.h"
-#include "ScopedJavaUnicodeString.h"
-#include "unicode/parseerr.h"
-#include "unicode/regex.h"
-
-// ICU documentation: http://icu-project.org/apiref/icu4c/classRegexMatcher.html
-
-/**
- * Encapsulates an instance of ICU4C's RegexMatcher class along with a copy of
- * the input it's currently operating on in the native heap.
- *
- * Rationale: We choose to make a copy here because it turns out to be a lot
- * cheaper when a moving GC and/or string compression is enabled. This is
- * because env->GetStringChars() always copies in this scenario. This becomes
- * especially bad when the String in question is long and/or contains a large
- * number of matches.
- *
- * Drawbacks: The native allocation associated with this class is no longer
- * fixed size, so we're effectively lying to the NativeAllocationRegistry about
- * the size of the object(s) we're allocating on the native heap. The peak
- * memory usage doesn't change though, given that GetStringChars would have
- * made an allocation of precisely the same size.
- */
-class MatcherState {
-public:
-    MatcherState(icu::RegexMatcher* matcher) :
-        mMatcher(matcher),
-        mUChars(nullptr),
-        mUText(nullptr),
-        mStatus(U_ZERO_ERROR) {
-    }
-
-    bool updateInput(JNIEnv* env, jstring input) {
-        // First, close the UText struct, since we're about to allocate a new one.
-        if (mUText != nullptr) {
-            utext_close(mUText);
-            mUText = nullptr;
-        }
-
-        // Then delete the UChar* associated with the UText struct..
-        mUChars.reset(nullptr);
-
-        // TODO: We should investigate whether we can avoid an additional copy
-        // in the native heap when is_copy == JNI_TRUE. The problem with doing
-        // that is that we might call ReleaseStringChars with a different
-        // JNIEnv* on a different downcall. This is currently safe as
-        // implemented in ART, but is unlikely to be portable and the spec stays
-        // silent on the matter.
-        ScopedStringChars inputChars(env, input);
-        if (inputChars.get() == nullptr) {
-            // There will be an exception pending if we get here.
-            return false;
-        }
-
-        // Make a copy of |input| on the native heap. This copy will be live
-        // until the next call to updateInput or close.
-        mUChars.reset(new (std::nothrow) UChar[inputChars.size()]);
-        if (mUChars.get() == nullptr) {
-            env->ThrowNew(env->FindClass("Ljava/lang/OutOfMemoryError;"), "Out of memory");
-            return false;
-        }
-
-        static_assert(sizeof(UChar) == sizeof(jchar), "sizeof(Uchar) != sizeof(jchar)");
-        memcpy(mUChars.get(), inputChars.get(), inputChars.size() * sizeof(jchar));
-
-        // Reset any errors that might have occurred on previous patches.
-        mStatus = U_ZERO_ERROR;
-        mUText = utext_openUChars(nullptr, mUChars.get(), inputChars.size(), &mStatus);
-        if (mUText == nullptr) {
-            CHECK(maybeThrowIcuException(env, "utext_openUChars", mStatus));
-            return false;
-        }
-
-        // It is an error for ICU to have returned a non-null mUText but to
-        // still have indicated an error.
-        CHECK(U_SUCCESS(mStatus));
-
-        mMatcher->reset(mUText);
-        return true;
-    }
-
-    ~MatcherState() {
-        if (mUText != nullptr) {
-            utext_close(mUText);
-        }
-    }
-
-    icu::RegexMatcher* matcher() {
-        return mMatcher.get();
-    }
-
-    UErrorCode& status() {
-        return mStatus;
-    }
-
-    void updateOffsets(JNIEnv* env, jintArray javaOffsets) {
-        ScopedIntArrayRW offsets(env, javaOffsets);
-        if (offsets.get() == NULL) {
-            return;
-        }
-
-        for (size_t i = 0, groupCount = mMatcher->groupCount(); i <= groupCount; ++i) {
-            offsets[2*i + 0] = mMatcher->start(i, mStatus);
-            offsets[2*i + 1] = mMatcher->end(i, mStatus);
-        }
-    }
-
-private:
-    std::unique_ptr<icu::RegexMatcher> mMatcher;
-    std::unique_ptr<UChar[]> mUChars;
-    UText* mUText;
-    UErrorCode mStatus;
-
-    // Disallow copy and assignment.
-    MatcherState(const MatcherState&);
-    void operator=(const MatcherState&);
-};
-
-static inline MatcherState* toMatcherState(jlong address) {
-    return reinterpret_cast<MatcherState*>(static_cast<uintptr_t>(address));
-}
-
-static void Matcher_free(void* address) {
-    MatcherState* state = reinterpret_cast<MatcherState*>(address);
-    delete state;
-}
-
-static jlong Matcher_getNativeFinalizer(JNIEnv*, jclass) {
-    return reinterpret_cast<jlong>(&Matcher_free);
-}
-
-static jboolean Matcher_findImpl(JNIEnv* env, jclass, jlong addr, jint startIndex, jintArray offsets) {
-    MatcherState* state = toMatcherState(addr);
-    UBool result = state->matcher()->find(startIndex, state->status());
-    if (result) {
-        state->updateOffsets(env, offsets);
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
-static jboolean Matcher_findNextImpl(JNIEnv* env, jclass, jlong addr, jintArray offsets) {
-    MatcherState* state = toMatcherState(addr);
-    UBool result = state->matcher()->find();
-    if (result) {
-        state->updateOffsets(env, offsets);
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
-static jint Matcher_groupCountImpl(JNIEnv*, jclass, jlong addr) {
-    MatcherState* state = toMatcherState(addr);
-    return state->matcher()->groupCount();
-}
-
-static jboolean Matcher_hitEndImpl(JNIEnv*, jclass, jlong addr) {
-    MatcherState* state = toMatcherState(addr);
-    if (state->matcher()->hitEnd() != 0) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
-static jboolean Matcher_lookingAtImpl(JNIEnv* env, jclass, jlong addr, jintArray offsets) {
-    MatcherState* state = toMatcherState(addr);
-    UBool result = state->matcher()->lookingAt(state->status());
-    if (result) {
-        state->updateOffsets(env, offsets);
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
-static jboolean Matcher_matchesImpl(JNIEnv* env, jclass, jlong addr, jintArray offsets) {
-    MatcherState* state = toMatcherState(addr);
-    UBool result = state->matcher()->matches(state->status());
-    if (result) {
-        state->updateOffsets(env, offsets);
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
-static jlong Matcher_openImpl(JNIEnv* env, jclass, jlong patternAddr) {
-    icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
-    UErrorCode status = U_ZERO_ERROR;
-    icu::RegexMatcher* result = pattern->matcher(status);
-    if (maybeThrowIcuException(env, "RegexPattern::matcher", status)) {
-        return 0;
-    }
-
-    return reinterpret_cast<uintptr_t>(new MatcherState(result));
-}
-
-static jboolean Matcher_requireEndImpl(JNIEnv*, jclass, jlong addr) {
-    MatcherState* state = toMatcherState(addr);
-    if (state->matcher()->requireEnd() != 0) {
-        return JNI_TRUE;
-    } else {
-        return JNI_FALSE;
-    }
-}
-
-static void Matcher_setInputImpl(JNIEnv* env, jclass, jlong addr, jstring javaText, jint start, jint end) {
-    MatcherState* state = toMatcherState(addr);
-    if (state->updateInput(env, javaText)) {
-        state->matcher()->region(start, end, state->status());
-    }
-}
-
-static void Matcher_useAnchoringBoundsImpl(JNIEnv*, jclass, jlong addr, jboolean value) {
-    MatcherState* state = toMatcherState(addr);
-    state->matcher()->useAnchoringBounds(value);
-}
-
-static void Matcher_useTransparentBoundsImpl(JNIEnv*, jclass, jlong addr, jboolean value) {
-    MatcherState* state = toMatcherState(addr);
-    state->matcher()->useTransparentBounds(value);
-}
-
-static jint Matcher_getMatchedGroupIndex0(JNIEnv* env, jclass, jlong patternAddr, jstring javaGroupName) {
-  icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
-  ScopedJavaUnicodeString groupName(env, javaGroupName);
-  UErrorCode status = U_ZERO_ERROR;
-
-  jint result = pattern->groupNumberFromName(groupName.unicodeString(), status);
-  if (U_SUCCESS(status)) {
-    return result;
-  }
-  if (status == U_REGEX_INVALID_CAPTURE_GROUP_NAME) {
-    return -1;
-  }
-  maybeThrowIcuException(env, "RegexPattern::groupNumberFromName", status);
-  return -1;
-}
-
-
-static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(Matcher, getMatchedGroupIndex0, "(JLjava/lang/String;)I"),
-    NATIVE_METHOD(Matcher, findImpl, "(JI[I)Z"),
-    NATIVE_METHOD(Matcher, findNextImpl, "(J[I)Z"),
-    NATIVE_METHOD(Matcher, getNativeFinalizer, "()J"),
-    NATIVE_METHOD(Matcher, groupCountImpl, "(J)I"),
-    NATIVE_METHOD(Matcher, hitEndImpl, "(J)Z"),
-    NATIVE_METHOD(Matcher, lookingAtImpl, "(J[I)Z"),
-    NATIVE_METHOD(Matcher, matchesImpl, "(J[I)Z"),
-    NATIVE_METHOD(Matcher, openImpl, "(J)J"),
-    NATIVE_METHOD(Matcher, requireEndImpl, "(J)Z"),
-    NATIVE_METHOD(Matcher, setInputImpl, "(JLjava/lang/String;II)V"),
-    NATIVE_METHOD(Matcher, useAnchoringBoundsImpl, "(JZ)V"),
-    NATIVE_METHOD(Matcher, useTransparentBoundsImpl, "(JZ)V"),
-};
-void register_java_util_regex_Matcher(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "java/util/regex/Matcher", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/java_util_regex_Pattern.cpp b/luni/src/main/native/java_util_regex_Pattern.cpp
deleted file mode 100644
index a893bab..0000000
--- a/luni/src/main/native/java_util_regex_Pattern.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "Pattern"
-
-#include <stdlib.h>
-
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/jni_macros.h>
-
-#include "unicode/parseerr.h"
-#include "unicode/regex.h"
-
-#include "JniConstants.h"
-#include "ScopedJavaUnicodeString.h"
-
-// ICU documentation: http://icu-project.org/apiref/icu4c/classRegexPattern.html
-
-static const char* regexDetailMessage(UErrorCode status) {
-    // These human-readable error messages were culled from "utypes.h", and then slightly tuned
-    // to make more sense in context.
-    // If we don't have a special-case, we'll just return the textual name of
-    // the enum value (such as U_REGEX_RULE_SYNTAX), which is better than nothing.
-    switch (status) {
-    case U_REGEX_INTERNAL_ERROR: return "An internal error was detected";
-    case U_REGEX_RULE_SYNTAX: return "Syntax error in regexp pattern";
-    case U_REGEX_INVALID_STATE: return "Matcher in invalid state for requested operation";
-    case U_REGEX_BAD_ESCAPE_SEQUENCE: return "Unrecognized backslash escape sequence in pattern";
-    case U_REGEX_PROPERTY_SYNTAX: return "Incorrect Unicode property";
-    case U_REGEX_UNIMPLEMENTED: return "Use of unimplemented feature";
-    case U_REGEX_MISMATCHED_PAREN: return "Incorrectly nested parentheses in regexp pattern";
-    case U_REGEX_NUMBER_TOO_BIG: return "Decimal number is too large";
-    case U_REGEX_BAD_INTERVAL: return "Error in {min,max} interval";
-    case U_REGEX_MAX_LT_MIN: return "In {min,max}, max is less than min";
-    case U_REGEX_INVALID_BACK_REF: return "Back-reference to a non-existent capture group";
-    case U_REGEX_INVALID_FLAG: return "Invalid value for match mode flags";
-    case U_REGEX_LOOK_BEHIND_LIMIT: return "Look-behind pattern matches must have a bounded maximum length";
-    case U_REGEX_SET_CONTAINS_STRING: return "Regular expressions cannot have UnicodeSets containing strings";
-    case U_REGEX_OCTAL_TOO_BIG: return "Octal character constants must be <= 0377.";
-    case U_REGEX_MISSING_CLOSE_BRACKET: return "Missing closing bracket in character class";
-    case U_REGEX_INVALID_RANGE: return "In a character range [x-y], x is greater than y";
-    case U_REGEX_STACK_OVERFLOW: return "Regular expression backtrack stack overflow";
-    case U_REGEX_TIME_OUT: return "Maximum allowed match time exceeded";
-    case U_REGEX_STOPPED_BY_CALLER: return "Matching operation aborted by user callback function";
-    default:
-        return u_errorName(status);
-    }
-}
-
-static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status, jstring pattern, UParseError error) {
-    static jmethodID method = env->GetMethodID(JniConstants::GetPatternSyntaxExceptionClass(env),
-            "<init>", "(Ljava/lang/String;Ljava/lang/String;I)V");
-    jstring message = env->NewStringUTF(regexDetailMessage(status));
-    jclass exceptionClass = JniConstants::GetPatternSyntaxExceptionClass(env);
-    jobject exception = env->NewObject(exceptionClass, method, message, pattern, error.offset);
-    env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
-static void Pattern_free(void* addr) {
-    delete reinterpret_cast<icu::RegexPattern*>(addr);
-}
-
-static jlong Pattern_getNativeFinalizer(JNIEnv*, jclass) {
-    return reinterpret_cast<jlong>(&Pattern_free);
-}
-
-static jlong Pattern_compileImpl(JNIEnv* env, jclass, jstring javaRegex, jint flags) {
-    flags |= UREGEX_ERROR_ON_UNKNOWN_ESCAPES;
-
-    UErrorCode status = U_ZERO_ERROR;
-    UParseError error;
-    error.offset = -1;
-
-    ScopedJavaUnicodeString regex(env, javaRegex);
-    if (!regex.valid()) {
-        return 0;
-    }
-    icu::UnicodeString& regexString(regex.unicodeString());
-    icu::RegexPattern* result = icu::RegexPattern::compile(regexString, flags, error, status);
-    if (!U_SUCCESS(status)) {
-        throwPatternSyntaxException(env, status, javaRegex, error);
-    }
-    return static_cast<jlong>(reinterpret_cast<uintptr_t>(result));
-}
-
-static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(Pattern, compileImpl, "(Ljava/lang/String;I)J"),
-    NATIVE_METHOD(Pattern, getNativeFinalizer, "()J"),
-};
-
-void register_java_util_regex_Pattern(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "java/util/regex/Pattern", gMethods, NELEM(gMethods));
-}
diff --git a/mmodules/intracoreapi/api/intra/current-api.txt b/mmodules/intracoreapi/api/intra/current-api.txt
index 02cedb4..c2024d6 100644
--- a/mmodules/intracoreapi/api/intra/current-api.txt
+++ b/mmodules/intracoreapi/api/intra/current-api.txt
@@ -48,6 +48,13 @@
 
 }
 
+package dalvik.annotation.optimization {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) @libcore.api.IntraCoreApi public @interface ReachabilitySensitive {
+  }
+
+}
+
 package dalvik.system {
 
   @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public final class BlockGuard {
@@ -120,6 +127,11 @@
 
 package libcore.util {
 
+  @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public class NativeAllocationRegistry {
+    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public static libcore.util.NativeAllocationRegistry createMalloced(ClassLoader, long);
+    method @libcore.api.CorePlatformApi @libcore.api.IntraCoreApi public Runnable registerNativeAllocation(Object, long);
+  }
+
   @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) @libcore.api.IntraCoreApi public @interface NonNull {
   }
 
diff --git a/ojluni/src/main/java/java/util/regex/Matcher.java b/ojluni/src/main/java/java/util/regex/Matcher.java
index fd60baf..eca98db 100644
--- a/ojluni/src/main/java/java/util/regex/Matcher.java
+++ b/ojluni/src/main/java/java/util/regex/Matcher.java
@@ -26,8 +26,7 @@
 
 package java.util.regex;
 
-import dalvik.annotation.optimization.ReachabilitySensitive;
-import libcore.util.NativeAllocationRegistry;
+import com.android.icu.util.regex.NativeMatcher;
 
 /**
  * An engine that performs match operations on a {@linkplain java.lang.CharSequence
@@ -109,10 +108,6 @@
     /**
      * The Pattern object that created this Matcher.
      */
-    // Patterns also contain cleanup code and a ReachabilitySensitive field.
-    // This ensures that "this" and pattern remain reachable while we're using pattern.address
-    // directly.
-    @ReachabilitySensitive
     private Pattern parentPattern;
 
     /**
@@ -137,21 +132,7 @@
      */
     private boolean matchFound;
 
-    /**
-     * The address of the native peer.
-     * Uses of this must be manually synchronized to avoid native crashes.
-     */
-    @ReachabilitySensitive
-    private long address;
-
-    /**
-     * If non-null, a Runnable that can be used to explicitly deallocate address.
-     */
-    private Runnable nativeFinalizer;
-
-    private static final NativeAllocationRegistry registry =
-            NativeAllocationRegistry.createMalloced(Matcher.class.getClassLoader(),
-            getNativeFinalizer());
+    private NativeMatcher nativeMatcher;
 
     /**
      * The index of the last position appended in a substitution.
@@ -230,13 +211,8 @@
         parentPattern = newPattern;
 
         synchronized (this) {
-            if (nativeFinalizer != null) {
-                nativeFinalizer.run();
-                address = 0; // In case openImpl throws.
-                nativeFinalizer = null;
-            }
-            address = openImpl(parentPattern.address);
-            nativeFinalizer = registry.registerNativeAllocation(this, address);
+            nativeMatcher = null; // In case NativeMatcher.create throws.
+            nativeMatcher = NativeMatcher.create(parentPattern.nativePattern);
         }
 
         if (text != null) {
@@ -533,7 +509,7 @@
      */
     public int groupCount() {
         synchronized (this) {
-            return groupCountImpl(address);
+            return nativeMatcher.groupCount();
         }
     }
 
@@ -548,7 +524,7 @@
      */
     public boolean matches() {
         synchronized (this) {
-            matchFound = matchesImpl(address, groups);
+            matchFound = nativeMatcher.matches(groups);
         }
         return matchFound;
     }
@@ -570,7 +546,7 @@
      */
     public boolean find() {
         synchronized (this) {
-            matchFound = findNextImpl(address, groups);
+            matchFound = nativeMatcher.findNext(groups);
         }
         return matchFound;
     }
@@ -600,7 +576,7 @@
             throw new IndexOutOfBoundsException("Illegal start index");
         reset();
         synchronized (this) {
-            matchFound = findImpl(address, start, groups);
+            matchFound = nativeMatcher.find(start, groups);
         }
         return matchFound;
     }
@@ -621,7 +597,7 @@
      */
     public boolean lookingAt() {
         synchronized (this) {
-            matchFound = lookingAtImpl(address, groups);
+            matchFound = nativeMatcher.lookingAt(groups);
         }
         return matchFound;
     }
@@ -1023,7 +999,7 @@
     public Matcher useTransparentBounds(boolean b) {
         synchronized (this) {
             transparentBounds = b;
-            useTransparentBoundsImpl(address, b);
+            nativeMatcher.useTransparentBounds(b);
         }
         return this;
     }
@@ -1072,7 +1048,7 @@
     public Matcher useAnchoringBounds(boolean b) {
         synchronized (this) {
             anchoringBounds = b;
-            useAnchoringBoundsImpl(address, b);
+            nativeMatcher.useAnchoringBounds(b);
         }
         return this;
     }
@@ -1112,7 +1088,7 @@
      */
     public boolean hitEnd() {
         synchronized (this) {
-            return hitEndImpl(address);
+            return nativeMatcher.hitEnd();
         }
     }
 
@@ -1132,7 +1108,7 @@
      */
     public boolean requireEnd() {
         synchronized (this) {
-            return requireEndImpl(address);
+            return nativeMatcher.requireEnd();
         }
     }
 
@@ -1195,9 +1171,9 @@
 
     private void resetForInput() {
         synchronized (this) {
-            setInputImpl(address, text, from, to);
-            useAnchoringBoundsImpl(address, anchoringBounds);
-            useTransparentBoundsImpl(address, transparentBounds);
+            nativeMatcher.setInput(text, from, to);
+            nativeMatcher.useAnchoringBounds(anchoringBounds);
+            nativeMatcher.useTransparentBounds(transparentBounds);
         }
     }
 
@@ -1216,7 +1192,7 @@
 
     private int getMatchedGroupIndex(String name) {
         ensureMatch();
-        int result = getMatchedGroupIndex0(parentPattern.address, name);
+        int result = nativeMatcher.getMatchedGroupIndex(name);
         if (result < 0) {
             throw new IllegalArgumentException("No capturing group in the pattern " +
                                                "with the name " + name);
@@ -1224,20 +1200,6 @@
         return result;
     }
 
-    private static native int getMatchedGroupIndex0(long patternAddr, String name);
-    private static native boolean findImpl(long addr, int startIndex, int[] offsets);
-    private static native boolean findNextImpl(long addr, int[] offsets);
-    private static native long getNativeFinalizer();
-    private static native int groupCountImpl(long addr);
-    private static native boolean hitEndImpl(long addr);
-    private static native boolean lookingAtImpl(long addr, int[] offsets);
-    private static native boolean matchesImpl(long addr, int[] offsets);
-    private static native long openImpl(long patternAddr);
-    private static native boolean requireEndImpl(long addr);
-    private static native void setInputImpl(long addr, String s, int start, int end);
-    private static native void useAnchoringBoundsImpl(long addr, boolean value);
-    private static native void useTransparentBoundsImpl(long addr, boolean value);
-
     /**
      * A trivial match result implementation that's based on an array of integers
      * representing match offsets. The array is of the form
diff --git a/ojluni/src/main/java/java/util/regex/Pattern.java b/ojluni/src/main/java/java/util/regex/Pattern.java
index 55a48de..3a5ad63 100644
--- a/ojluni/src/main/java/java/util/regex/Pattern.java
+++ b/ojluni/src/main/java/java/util/regex/Pattern.java
@@ -26,11 +26,9 @@
 
 package java.util.regex;
 
-import dalvik.annotation.optimization.ReachabilitySensitive;
+import com.android.icu.util.regex.NativePattern;
 import dalvik.system.VMRuntime;
 
-import libcore.util.NativeAllocationRegistry;
-
 import java.util.Iterator;
 import java.util.ArrayList;
 import java.util.NoSuchElementException;
@@ -945,12 +943,7 @@
     // BEGIN Android-changed: reimplement matching logic natively via ICU.
     // We only need some tie-ins to native memory, instead of a large number
     // of fields on the .java side.
-    @ReachabilitySensitive
-    transient long address;
-
-    private static final NativeAllocationRegistry registry =
-            NativeAllocationRegistry.createMalloced(Pattern.class.getClassLoader(),
-            getNativeFinalizer());
+    /* package */ transient NativePattern nativePattern;
     // END Android-changed: reimplement matching logic natively via ICU.
 
     /**
@@ -1430,12 +1423,8 @@
         // These are the flags natively supported by ICU.
         // They even have the same value in native code.
         int icuFlags = flags & (CASE_INSENSITIVE | COMMENTS | MULTILINE | DOTALL | UNIX_LINES);
-        address = compileImpl(icuPattern, icuFlags);
-        registry.registerNativeAllocation(this, address);
+        nativePattern = NativePattern.create(icuPattern, icuFlags);
     }
-
-    private static native long compileImpl(String regex, int flags);
-    private static native long getNativeFinalizer();
     // END Android-changed: reimplement matching logic natively via ICU.
 
     /**