diff options
58 files changed, 1999 insertions, 2698 deletions
diff --git a/archive/src/main/java/java/util/zip/Deflater.java b/archive/src/main/java/java/util/zip/Deflater.java index 38771a8..ad17cf2 100644 --- a/archive/src/main/java/java/util/zip/Deflater.java +++ b/archive/src/main/java/java/util/zip/Deflater.java @@ -17,9 +17,6 @@ package java.util.zip; -// BEGIN android-changed -// import org.apache.harmony.luni.platform.OSResourcesMonitor; -// END android-changed /** * This class compresses data using the <i>DEFLATE</i> algorithm (see <a @@ -149,8 +146,7 @@ public class Deflater { throw new IllegalArgumentException(); } compressLevel = level; - streamHandle = createStreamWithMemoryEnsurance(compressLevel, strategy, - noHeader); + streamHandle = createStream(compressLevel, strategy, noHeader); } /** @@ -495,13 +491,5 @@ public class Deflater { return getTotalOutImpl(streamHandle); } - private long createStreamWithMemoryEnsurance(int level, int strategy1, - boolean noHeader1) { - // BEGIN android-changed - // OSResourcesMonitor.ensurePhysicalMemoryCapacity(); - // END android-changed - return createStream(level, strategy1, noHeader1); - } - private native long createStream(int level, int strategy1, boolean noHeader1); } diff --git a/archive/src/main/java/java/util/zip/ZipFile.java b/archive/src/main/java/java/util/zip/ZipFile.java index 653b2c9..b5f3678 100644 --- a/archive/src/main/java/java/util/zip/ZipFile.java +++ b/archive/src/main/java/java/util/zip/ZipFile.java @@ -52,12 +52,12 @@ public class ZipFile implements ZipConstants { File fileToDeleteOnClose; /** - * Open zip file for read. + * Open ZIP file for read. */ public static final int OPEN_READ = 1; /** - * Delete zip file when closed. + * Delete ZIP file when closed. */ public static final int OPEN_DELETE = 4; @@ -140,7 +140,7 @@ public class ZipFile implements ZipConstants { } /** - * Closes this ZIP file. + * Closes this ZIP file. This method is idempotent. * * @throws IOException * if an IOException occurs. @@ -166,23 +166,32 @@ public class ZipFile implements ZipConstants { } } + private void checkNotClosed() { + if (mRaf == null) { + throw new IllegalStateException("Zip File closed."); + } + } + /** * Returns an enumeration of the entries. The entries are listed in the * order in which they appear in the ZIP archive. * * @return the enumeration of the entries. + * @throws IllegalStateException if this ZIP file has been closed. */ public Enumeration<? extends ZipEntry> entries() { + checkNotClosed(); + return new Enumeration<ZipEntry>() { private int i = 0; public boolean hasMoreElements() { - if (mRaf == null) throw new IllegalStateException("Zip File closed."); + checkNotClosed(); return i < mEntryList.size(); } public ZipEntry nextElement() { - if (mRaf == null) throw new IllegalStateException("Zip File closed."); + checkNotClosed(); if (i >= mEntryList.size()) throw new NoSuchElementException(); return (ZipEntry) mEntryList.get(i++); @@ -197,8 +206,10 @@ public class ZipFile implements ZipConstants { * the name of the entry in the ZIP file. * @return a {@code ZipEntry} or {@code null} if the entry name does not * exist in the ZIP file. + * @throws IllegalStateException if this ZIP file has been closed. */ public ZipEntry getEntry(String entryName) { + checkNotClosed(); if (entryName != null) { ZipEntry ze = mFastLookup.get(entryName); if (ze == null) ze = mFastLookup.get(entryName + "/"); @@ -215,6 +226,7 @@ public class ZipFile implements ZipConstants { * @return an input stream of the data contained in the {@code ZipEntry}. * @throws IOException * if an {@code IOException} occurs. + * @throws IllegalStateException if this ZIP file has been closed. */ public InputStream getInputStream(ZipEntry entry) throws IOException { /* @@ -229,27 +241,25 @@ public class ZipFile implements ZipConstants { * Create a ZipInputStream at the right part of the file. */ RandomAccessFile raf = mRaf; - if (raf != null) { - synchronized (raf) { - // Unfortunately we don't know the entry data's start position. - // All we have is the position of the entry's local header. - // At position 28 we find the length of the extra data. - // In some cases this length differs from the one coming in - // the central header!!! - RAFStream rafstrm = new RAFStream(raf, entry.mLocalHeaderRelOffset + 28); - int localExtraLenOrWhatever = ler.readShortLE(rafstrm); - // Now we need to skip the name - // and this "extra" data or whatever it is: - rafstrm.skip(entry.nameLen + localExtraLenOrWhatever); - rafstrm.mLength = rafstrm.mOffset + entry.compressedSize; - if (entry.compressionMethod == ZipEntry.DEFLATED) { - return new InflaterInputStream(rafstrm, new Inflater(true)); - } else { - return rafstrm; - } + synchronized (raf) { + // Unfortunately we don't know the entry data's start position. + // All we have is the position of the entry's local header. + // At position 28 we find the length of the extra data. + // In some cases this length differs from the one coming in + // the central header!!! + RAFStream rafstrm = new RAFStream(raf, + entry.mLocalHeaderRelOffset + 28); + int localExtraLenOrWhatever = ler.readShortLE(rafstrm); + // Now we need to skip the name + // and this "extra" data or whatever it is: + rafstrm.skip(entry.nameLen + localExtraLenOrWhatever); + rafstrm.mLength = rafstrm.mOffset + entry.compressedSize; + if (entry.compressionMethod == ZipEntry.DEFLATED) { + return new InflaterInputStream(rafstrm, new Inflater(true)); + } else { + return rafstrm; } } - throw new IllegalStateException("Zip File closed"); } /** @@ -265,8 +275,10 @@ public class ZipFile implements ZipConstants { * Returns the number of {@code ZipEntries} in this {@code ZipFile}. * * @return the number of entries in this file. + * @throws IllegalStateException if this ZIP file has been closed. */ public int size() { + checkNotClosed(); return mEntryList.size(); } diff --git a/archive/src/main/native/java_util_zip_Adler32.c b/archive/src/main/native/java_util_zip_Adler32.c index 1b02a11..0fcf549 100644 --- a/archive/src/main/native/java_util_zip_Adler32.c +++ b/archive/src/main/native/java_util_zip_Adler32.c @@ -25,16 +25,11 @@ Java_java_util_zip_Adler32_updateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong crc) { - jbyte *b; - jboolean isCopy; - jlong result; - - b = (*env)->GetPrimitiveArrayCritical (env, buf, &isCopy); + jbyte* b = (*env)->GetPrimitiveArrayCritical (env, buf, NULL); if (b == NULL) { - throwNewOutOfMemoryError(env, ""); return 0; } - result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); + jlong result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); (*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT); return result; diff --git a/archive/src/main/native/java_util_zip_CRC32.c b/archive/src/main/native/java_util_zip_CRC32.c index cee25e5..fe50fca 100644 --- a/archive/src/main/native/java_util_zip_CRC32.c +++ b/archive/src/main/native/java_util_zip_CRC32.c @@ -25,15 +25,11 @@ Java_java_util_zip_CRC32_updateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong crc) { - jbyte *b; - jlong result; - - b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); + jbyte* b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); if (b == NULL) { - throwNewOutOfMemoryError(env, ""); return -1; } - result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); + jlong result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT)); return result; } diff --git a/archive/src/main/native/java_util_zip_Deflater.c b/archive/src/main/native/java_util_zip_Deflater.c index 2e0e268..af0bfcc 100644 --- a/archive/src/main/native/java_util_zip_Deflater.c +++ b/archive/src/main/native/java_util_zip_Deflater.c @@ -162,29 +162,19 @@ Java_java_util_zip_Deflater_setInputImpl (JNIEnv * env, jobject recv, { PORT_ACCESS_FROM_ENV (env); - jbyte *in; - JCLZipStream *stream; - - stream = (JCLZipStream *) ((IDATA) handle); - if (stream->inaddr != NULL) /*Input has already been provided, free the old buffer */ + JCLZipStream* stream = (JCLZipStream *) ((IDATA) handle); + if (stream->inaddr != NULL) { + /* Input has already been provided, free the old buffer. */ jclmem_free_memory (env, stream->inaddr); + } stream->inaddr = jclmem_allocate_memory (env, len); - if (stream->inaddr == NULL) - { - throwNewOutOfMemoryError (env, ""); - return; - } - in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) { - throwNewOutOfMemoryError(env, ""); + if (stream->inaddr == NULL) { + throwNewOutOfMemoryError (env, ""); return; } - memcpy (stream->inaddr, (in + off), len); - ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); + (*env)->GetByteArrayRegion(env, buf, off, len, (jbyte*) stream->inaddr); stream->stream->next_in = (Bytef *) stream->inaddr; stream->stream->avail_in = len; - - return; } JNIEXPORT jint JNICALL @@ -209,7 +199,6 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); if (out == NULL) { - throwNewOutOfMemoryError(env, ""); return -1; } stream->stream->next_out = (Bytef *) out + off; diff --git a/archive/src/main/native/java_util_zip_Inflater.c b/archive/src/main/native/java_util_zip_Inflater.c index 4b30d4e..c04a223 100644 --- a/archive/src/main/native/java_util_zip_Inflater.c +++ b/archive/src/main/native/java_util_zip_Inflater.c @@ -104,29 +104,20 @@ Java_java_util_zip_Inflater_setInputImpl (JNIEnv * env, jobject recv, { PORT_ACCESS_FROM_ENV (env); - jbyte *in; - U_8 *baseAddr; JCLZipStream *stream = (JCLZipStream *) ((IDATA) handle); - - if (stream->inaddr != NULL) /*Input has already been provided, free the old buffer */ + if (stream->inaddr != NULL) { + /* Input has already been provided, free the old buffer. */ jclmem_free_memory (env, stream->inaddr); - baseAddr = jclmem_allocate_memory (env, len); - if (baseAddr == NULL) - { - throwNewOutOfMemoryError (env, ""); - return; - } + } + U_8* baseAddr = jclmem_allocate_memory (env, len); + if (baseAddr == NULL) { + throwNewOutOfMemoryError (env, ""); + return; + } stream->inaddr = baseAddr; stream->stream->next_in = (Bytef *) baseAddr; stream->stream->avail_in = len; - in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) { - throwNewOutOfMemoryError(env, ""); - return; - } - memcpy (baseAddr, (in + off), len); - ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); - return; + (*env)->GetByteArrayRegion(env, buf, off, len, (jbyte*) baseAddr); } JNIEXPORT jint JNICALL diff --git a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java index b025e11..146c679 100644 --- a/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java +++ b/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java @@ -271,14 +271,23 @@ public class ZipFileTest extends junit.framework.TestCase { Enumeration<? extends ZipEntry> enumeration = zfile.entries(); zfile.close(); - zfile = null; - boolean pass = false; + try { + enumeration.nextElement(); + fail("did not detect closed file"); + } catch (IllegalStateException expected) { + } + try { enumeration.hasMoreElements(); - } catch (IllegalStateException e) { - pass = true; + fail("did not detect closed file"); + } catch (IllegalStateException expected) { + } + + try { + zfile.entries(); + fail("did not detect closed file"); + } catch (IllegalStateException expected) { } - assertTrue("did not detect closed jar file", pass); } /** @@ -349,20 +358,15 @@ public class ZipFileTest extends junit.framework.TestCase { method = "getEntry", args = {java.lang.String.class} ) - @KnownFailure("Android does not throw IllegalStateException when using " - + "getEntry() after close().") public void test_getEntryLjava_lang_String_Ex() throws IOException { java.util.zip.ZipEntry zentry = zfile.getEntry("File1.txt"); assertNotNull("Could not obtain ZipEntry", zentry); - int r; - InputStream in; zfile.close(); try { - zentry = zfile.getEntry("File2.txt"); - fail("IllegalStateException expected"); // Android fails here! + zfile.getEntry("File2.txt"); + fail("IllegalStateException expected"); } catch (IllegalStateException ee) { - // expected } } @@ -435,16 +439,13 @@ public class ZipFileTest extends junit.framework.TestCase { method = "size", args = {} ) - @KnownFailure("IllegalStateException not thrown when using ZipFile.size() " - + "after close().") public void test_size() throws IOException { assertEquals(6, zfile.size()); zfile.close(); try { zfile.size(); - fail("IllegalStateException expected"); // Android fails here! - } catch (IllegalStateException ee) { - // expected + fail("IllegalStateException expected"); + } catch (IllegalStateException expected) { } } diff --git a/dalvik/src/main/java/dalvik/system/DexClassLoader.java b/dalvik/src/main/java/dalvik/system/DexClassLoader.java index 73e6fe4..4a440b5 100644 --- a/dalvik/src/main/java/dalvik/system/DexClassLoader.java +++ b/dalvik/src/main/java/dalvik/system/DexClassLoader.java @@ -114,7 +114,7 @@ public class DexClassLoader extends ClassLoader { /* open all Zip and DEX files up front */ for (int i = 0; i < length; i++) { - System.out.println("My path is: " + dexPathList[i]); + //System.out.println("My path is: " + dexPathList[i]); File pathFile = new File(dexPathList[i]); mFiles[i] = pathFile; diff --git a/icu/src/main/native/BidiWrapperInterface.c b/icu/src/main/native/BidiWrapperInterface.c index 2c6b3cd..55be7a0 100644 --- a/icu/src/main/native/BidiWrapperInterface.c +++ b/icu/src/main/native/BidiWrapperInterface.c @@ -43,44 +43,34 @@ JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1close ubidi_close ((*data).pBiDi); - if ((*data).embeddingLevels != NULL) - free((*data).embeddingLevels); - free(data); + free((*data).embeddingLevels); + free(data); } JNIEXPORT void JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setPara (JNIEnv * env, jclass clazz, jlong pBiDi, jcharArray text, jint length, - jbyte paraLevel, jbyteArray embeddingLevels) + jbyte paraLevel, jbyteArray newEmbeddingLevels) { - UErrorCode err = 0; - jchar *_text = NULL; BiDiData *data = (BiDiData *)pBiDi; - /* Remembering old embedding levels */ - void *embLvls = (*data).embeddingLevels; - - _text = (*env)->GetCharArrayElements (env, text, NULL); - - if (embeddingLevels != NULL) - { - jbyte *el = (*env)->GetByteArrayElements (env, embeddingLevels, NULL); - (*data).embeddingLevels = malloc(length); - memcpy(((*data).embeddingLevels), el, length); - (*env)->ReleaseByteArrayElements (env, embeddingLevels, el, 0); - } else - { - (*data).embeddingLevels = NULL; - } + void *oldEmbeddingLevels = (*data).embeddingLevels; + + // Copy the new embedding levels from the Java heap to the native heap. + if (newEmbeddingLevels != NULL) { + (*data).embeddingLevels = malloc(length); + (*env)->GetByteArrayRegion(env, newEmbeddingLevels, 0, length, + (*data).embeddingLevels); + } else { + (*data).embeddingLevels = NULL; + } + UErrorCode err = 0; + jchar* _text = (*env)->GetCharArrayElements(env, text, NULL); ubidi_setPara ((*data).pBiDi, _text, length, paraLevel, ((*data).embeddingLevels), &err); + (*env)->ReleaseCharArrayElements (env, text, _text, 0); check_fail (env, err); - /* Freeing old embedding levels */ - if (embLvls != NULL) { - free(embLvls); - } - - (*env)->ReleaseCharArrayElements (env, text, _text, 0); + free(oldEmbeddingLevels); } JNIEXPORT jlong JNICALL Java_org_apache_harmony_text_BidiWrapper_ubidi_1setLine diff --git a/icu/src/main/native/DecimalFormatInterface.cpp b/icu/src/main/native/DecimalFormatInterface.cpp index 7e37d6c..252ec33 100644 --- a/icu/src/main/native/DecimalFormatInterface.cpp +++ b/icu/src/main/native/DecimalFormatInterface.cpp @@ -29,33 +29,6 @@ #include <string.h> #include "cutils/log.h" - -static UBool icuError(JNIEnv *env, UErrorCode errorcode) -{ - const char *emsg = u_errorName(errorcode); - jclass exception; - - if (U_FAILURE(errorcode)) {// errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { - switch (errorcode) { - case U_ILLEGAL_ARGUMENT_ERROR : - exception = env->FindClass("java/lang/IllegalArgumentException"); - break; - case U_INDEX_OUTOFBOUNDS_ERROR : - case U_BUFFER_OVERFLOW_ERROR : - exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); - break; - case U_UNSUPPORTED_ERROR : - exception = env->FindClass("java/lang/UnsupportedOperationException"); - break; - default : - exception = env->FindClass("java/lang/RuntimeException"); - } - - return (env->ThrowNew(exception, emsg) != 0); - } - return 0; -} - static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, jstring pattern) { @@ -78,7 +51,7 @@ static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, env->ReleaseStringUTFChars(locale, localeChars); // check for an error - if ( icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { return 0; } @@ -115,8 +88,8 @@ static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol, // release previously allocated space env->ReleaseStringChars(text, textChars); - // check if an error occured - icuError(env, status); + // check if an error occurred + icu4jni_error(env, status); } static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) { @@ -144,7 +117,7 @@ static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) { reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, resultlength, &status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { return NULL; } @@ -189,7 +162,7 @@ static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, env->ReleaseStringChars(text, textChars); - icuError(env, status); + icu4jni_error(env, status); } static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, @@ -219,7 +192,7 @@ static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, (UNumberFormatTextAttribute) symbol, result, resultlength, &status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { return NULL; } @@ -246,7 +219,7 @@ static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, env->ReleaseStringChars(pattern, pattChars); - icuError(env, status); + icu4jni_error(env, status); } static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, @@ -274,7 +247,7 @@ static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, reslenneeded=unum_toPattern(fmt, localized, result, resultlength, &status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { return NULL; } @@ -335,7 +308,7 @@ static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value, res->extract(result, reslenneeded + 1, status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { free(attrBuffer->buffer); free(attrBuffer); free(result); @@ -444,7 +417,7 @@ static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value, res->extract(result, reslenneeded + 1, status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { free(attrBuffer->buffer); free(attrBuffer); free(result); @@ -510,7 +483,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val // env->ReleaseStringUTFChars(value, valueUTF); if (scale < 0) { - icuError(env, U_ILLEGAL_ARGUMENT_ERROR); + icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR); return NULL; } @@ -582,7 +555,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val res.extract(result, reslenneeded + 1, status); - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { if(fieldType != NULL) { env->ReleaseStringUTFChars(fieldType, fieldName); } @@ -671,28 +644,12 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, jobject position) { - - const char * textUTF = env->GetStringUTFChars(text, NULL); - env->ReleaseStringUTFChars(text, textUTF); - - const char * parsePositionClassName = "java/text/ParsePosition"; - const char * longClassName = "java/lang/Long"; - const char * doubleClassName = "java/lang/Double"; - const char * bigDecimalClassName = "java/math/BigDecimal"; - const char * bigIntegerClassName = "java/math/BigInteger"; - - UErrorCode status = U_ZERO_ERROR; - - UNumberFormat *fmt = (UNumberFormat *)(int)addr; - - jchar *str = (UChar *)env->GetStringChars(text, NULL); - int strlength = env->GetStringLength(text); - - jclass parsePositionClass = env->FindClass(parsePositionClassName); - jclass longClass = env->FindClass(longClassName); - jclass doubleClass = env->FindClass(doubleClassName); - jclass bigDecimalClass = env->FindClass(bigDecimalClassName); - jclass bigIntegerClass = env->FindClass(bigIntegerClassName); + // TODO: cache these? + jclass parsePositionClass = env->FindClass("java/text/ParsePosition"); + jclass longClass = env->FindClass("java/lang/Long"); + jclass doubleClass = env->FindClass("java/lang/Double"); + jclass bigDecimalClass = env->FindClass("java/math/BigDecimal"); + jclass bigIntegerClass = env->FindClass("java/math/BigInteger"); jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, "getIndex", "()I"); @@ -707,27 +664,26 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, jmethodID bigIntegerInitMethodID = env->GetMethodID(bigIntegerClass, "<init>", "(Ljava/lang/String;)V"); jmethodID doubleValueMethodID = env->GetMethodID(bigDecimalClass, "doubleValue", "()D"); - bool resultAssigned; - int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); - // make sure the ParsePosition is valid. Actually icu4c would parse a number // correctly even if the parsePosition is set to -1, but since the RI fails // for that case we have to fail too + int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); + const int strlength = env->GetStringLength(text); if(parsePos < 0 || parsePos > strlength) { return NULL; } - - Formattable res; - - const UnicodeString src((UChar*)str, strlength, strlength); + ParsePosition pp; - pp.setIndex(parsePos); DigitList digits; - + + UNumberFormat *fmt = (UNumberFormat *)(int)addr; + Formattable res; + bool resultAssigned; + jchar *str = (UChar *)env->GetStringChars(text, NULL); + const UnicodeString src((UChar*)str, strlength, strlength); ((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits); - env->ReleaseStringChars(text, str); if(pp.getErrorIndex() == -1) { @@ -738,20 +694,14 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, return NULL; } - Formattable::Type numType; - numType = res.getType(); + Formattable::Type numType = res.getType(); UErrorCode fmtStatus; double resultDouble; long resultLong; int64_t resultInt64; - UnicodeString resultString; jstring resultStr; - int resLength; - const char * resultUTF; jobject resultObject1, resultObject2; - jdouble doubleTest; - jchar * result; if (resultAssigned) { @@ -809,7 +759,7 @@ static jint cloneImpl(JNIEnv *env, jclass clazz, jint addr) { UNumberFormat *result = unum_clone(fmt, &status); - if(icuError(env, status) != FALSE) { + if(icu4jni_error(env, status) != FALSE) { return 0; } diff --git a/icu/src/main/native/ErrorCode.c b/icu/src/main/native/ErrorCode.c index b3e43cd..94c4239 100644 --- a/icu/src/main/native/ErrorCode.c +++ b/icu/src/main/native/ErrorCode.c @@ -8,50 +8,32 @@ */ #include "ErrorCode.h" - -/* private data members ----------------------------------------------------*/ - -/** -* Name of the java runtime exception classes -*/ -#define ILLEGALARGUMENTEXCEPTION_ "java/lang/IllegalArgumentException" -#define ARRAYINDEXOUTOFBOUNDSEXCEPTION_ "java/lang/ArrayIndexOutOfBoundsException" -#define UNSUPPORTEDOPERATIONEXCEPTION_ "java/lang/UnsupportedOperationException" -#define RUNTIMEEXCEPTION_ "java/lang/RuntimeException" - -/* public methods ---------------------------------------------------------*/ +#include "JNIHelp.h" /** -* Checks if an error has occured. -* Throws a generic Java RuntimeException if an error has occured. -* @param env JNI environment variable -* @param errorcode code to determine if it is an erro -* @return 0 if errorcode is not an error, 1 if errorcode is an error, but the +* Checks if an error has occurred, throwing a suitable exception if so. +* @param env JNI environment +* @param errorCode code to determine if it is an error +* @return 0 if errorCode is not an error, 1 if errorCode is an error, but the * creation of the exception to be thrown fails -* @exception thrown if errorcode represents an error + * @exception thrown if errorCode represents an error */ -UBool icu4jni_error(JNIEnv *env, UErrorCode errorcode) +UBool icu4jni_error(JNIEnv *env, UErrorCode errorCode) { - const char *emsg = u_errorName(errorcode); - jclass exception; - - if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { - switch (errorcode) { - case U_ILLEGAL_ARGUMENT_ERROR : - exception = (*env)->FindClass(env, ILLEGALARGUMENTEXCEPTION_); - break; - case U_INDEX_OUTOFBOUNDS_ERROR : - case U_BUFFER_OVERFLOW_ERROR : - exception = (*env)->FindClass(env, ARRAYINDEXOUTOFBOUNDSEXCEPTION_); - break; - case U_UNSUPPORTED_ERROR : - exception = (*env)->FindClass(env, UNSUPPORTEDOPERATIONEXCEPTION_); - break; - default : - exception = (*env)->FindClass(env, RUNTIMEEXCEPTION_); + const char* message = u_errorName(errorCode); + if (errorCode <= U_ZERO_ERROR || errorCode >= U_ERROR_LIMIT) { + return 0; + } + + switch (errorCode) { + case U_ILLEGAL_ARGUMENT_ERROR: + return jniThrowException(env, "java/lang/IllegalArgumentException", message); + case U_INDEX_OUTOFBOUNDS_ERROR: + case U_BUFFER_OVERFLOW_ERROR: + return jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", message); + case U_UNSUPPORTED_ERROR: + return jniThrowException(env, "java/lang/UnsupportedOperationException", message); + default: + return jniThrowException(env, "java/lang/RuntimeException", message); } - - return ((*env)->ThrowNew(env, exception, emsg) != 0); - } - return 0; } diff --git a/icu/src/main/native/ErrorCode.h b/icu/src/main/native/ErrorCode.h index a5bbfc6..e42a519 100644 --- a/icu/src/main/native/ErrorCode.h +++ b/icu/src/main/native/ErrorCode.h @@ -13,6 +13,10 @@ #include "unicode/utypes.h" #include "unicode/putil.h" +#ifdef __cplusplus +extern "C" { +#endif + /** * Checks if an error has occured. * Throws a generic Java RuntimeException if an error has occured. @@ -24,4 +28,8 @@ */ UBool icu4jni_error(JNIEnv *env, UErrorCode errorcode); +#ifdef __cplusplus +} +#endif + #endif diff --git a/icu/src/main/native/RBNFInterface.cpp b/icu/src/main/native/RBNFInterface.cpp index d9bf460..c7486e1 100644 --- a/icu/src/main/native/RBNFInterface.cpp +++ b/icu/src/main/native/RBNFInterface.cpp @@ -25,32 +25,6 @@ #include <stdlib.h> #include <string.h> -static UBool icuError(JNIEnv *env, UErrorCode errorcode) -{ - const char *emsg = u_errorName(errorcode); - jclass exception; - - if (errorcode > U_ZERO_ERROR && errorcode < U_ERROR_LIMIT) { - switch (errorcode) { - case U_ILLEGAL_ARGUMENT_ERROR : - exception = env->FindClass("java/lang/IllegalArgumentException"); - break; - case U_INDEX_OUTOFBOUNDS_ERROR : - case U_BUFFER_OVERFLOW_ERROR : - exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); - break; - case U_UNSUPPORTED_ERROR : - exception = env->FindClass("java/lang/UnsupportedOperationException"); - break; - default : - exception = env->FindClass("java/lang/RuntimeException"); - } - - return (env->ThrowNew(exception, emsg) != 0); - } - return 0; -} - static jint openRBNFImpl1(JNIEnv* env, jclass clazz, jint type, jstring locale) { @@ -72,7 +46,7 @@ static jint openRBNFImpl1(JNIEnv* env, jclass clazz, } else if(type == 3) { style = URBNF_COUNT; } else { - icuError(env, U_ILLEGAL_ARGUMENT_ERROR); + icu4jni_error(env, U_ILLEGAL_ARGUMENT_ERROR); } Locale loc = Locale::createFromName(localeChars); @@ -84,7 +58,7 @@ static jint openRBNFImpl1(JNIEnv* env, jclass clazz, env->ReleaseStringUTFChars(locale, localeChars); // check for an error - if ( icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { return 0; } @@ -117,7 +91,7 @@ static jint openRBNFImpl2(JNIEnv* env, jclass clazz, env->ReleaseStringUTFChars(locale, localeChars); // check for an error - if ( icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { return 0; } @@ -183,7 +157,7 @@ static jstring formatLongRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jlong va res.extract(result, reslenneeded + 1, status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { free(result); return NULL; } @@ -245,7 +219,7 @@ static jstring formatDoubleRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jdoubl res.extract(result, reslenneeded + 1, status); } - if (icuError(env, status) != FALSE) { + if (icu4jni_error(env, status) != FALSE) { free(result); return NULL; } @@ -265,22 +239,11 @@ static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text, jobject position, jboolean lenient) { // LOGI("ENTER parseRBNFImpl"); - - const char * parsePositionClassName = "java/text/ParsePosition"; - const char * longClassName = "java/lang/Long"; - const char * doubleClassName = "java/lang/Double"; - - - UErrorCode status = U_ZERO_ERROR; - - UNumberFormat *fmt = (UNumberFormat *)(int)addr; - - jchar *str = (UChar *)env->GetStringChars(text, NULL); - int strlength = env->GetStringLength(text); - - jclass parsePositionClass = env->FindClass(parsePositionClassName); - jclass longClass = env->FindClass(longClassName); - jclass doubleClass = env->FindClass(doubleClassName); + + // TODO: cache these? + jclass parsePositionClass = env->FindClass("java/text/ParsePosition"); + jclass longClass = env->FindClass("java/lang/Long"); + jclass doubleClass = env->FindClass("java/lang/Double"); jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, "getIndex", "()I"); @@ -292,22 +255,25 @@ static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text, jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V"); jmethodID dblInitMethodID = env->GetMethodID(doubleClass, "<init>", "(D)V"); - int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); - // make sure the ParsePosition is valid. Actually icu4c would parse a number // correctly even if the parsePosition is set to -1, but since the RI fails // for that case we have to fail too + int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); + const int strlength = env->GetStringLength(text); if(parsePos < 0 || parsePos > strlength) { return NULL; } - + Formattable res; - + + jchar *str = (UChar *)env->GetStringChars(text, NULL); + const UnicodeString src((UChar*)str, strlength, strlength); ParsePosition pp; pp.setIndex(parsePos); + UNumberFormat *fmt = (UNumberFormat *)(int)addr; if(lenient) { unum_setAttribute(fmt, UNUM_LENIENT_PARSE, JNI_TRUE); } @@ -328,35 +294,23 @@ static jobject parseRBNFImpl(JNIEnv *env, jclass clazz, jint addr, jstring text, return NULL; } - Formattable::Type numType; - numType = res.getType(); - UErrorCode fmtStatus; - - double resultDouble; - long resultLong; - int64_t resultInt64; - - switch(numType) { - case Formattable::kDouble: - resultDouble = res.getDouble(); - env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); - return env->NewObject(doubleClass, dblInitMethodID, - (jdouble) resultDouble); - case Formattable::kLong: - resultLong = res.getLong(); - env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); - return env->NewObject(longClass, longInitMethodID, - (jlong) resultLong); - case Formattable::kInt64: - resultInt64 = res.getInt64(); - env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); - return env->NewObject(longClass, longInitMethodID, - (jlong) resultInt64); - default: - break; + Formattable::Type numType = res.getType(); + if (numType == Formattable::kDouble) { + double resultDouble = res.getDouble(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(doubleClass, dblInitMethodID, + (jdouble) resultDouble); + } else if (numType == Formattable::kLong) { + long resultLong = res.getLong(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(longClass, longInitMethodID, (jlong) resultLong); + } else if (numType == Formattable::kInt64) { + int64_t resultInt64 = res.getInt64(); + env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); + return env->NewObject(longClass, longInitMethodID, (jlong) resultInt64); + } else { + return NULL; } - - return NULL; } static JNINativeMethod gMethods[] = { diff --git a/icu/src/main/native/RegExInterface.cpp b/icu/src/main/native/RegExInterface.cpp index afa5cc4..0ca3d06 100644 --- a/icu/src/main/native/RegExInterface.cpp +++ b/icu/src/main/native/RegExInterface.cpp @@ -32,12 +32,12 @@ static jchar EMPTY_STRING = 0; * character data it refers to (but does not have a copy of), so we can * manage memory properly. */ -typedef struct RegExDataStruct { +struct RegExData { // A pointer to the ICU regular expression URegularExpression* regex; // A pointer to (a copy of) the input text that *we* manage jchar* text; -} RegExData; +}; static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status, jstring pattern, UParseError error) @@ -63,8 +63,8 @@ static void _close(JNIEnv* env, jclass clazz, RegExData* data) uregex_close(data->regex); } - if (data->text != NULL && data->text != &EMPTY_STRING) { - free(data->text); + if (data->text != &EMPTY_STRING) { + delete[] data->text; } free(data); @@ -125,8 +125,8 @@ static void setText(JNIEnv* env, jclass clazz, RegExData* data, jstring text) return; } - if (data->text != NULL && data->text != &EMPTY_STRING) { - free(data->text); + if (data->text != &EMPTY_STRING) { + delete[] data->text; data->text = NULL; } @@ -134,11 +134,9 @@ static void setText(JNIEnv* env, jclass clazz, RegExData* data, jstring text) if (textLen == 0) { data->text = &EMPTY_STRING; } else { - jchar const * textRaw = env->GetStringChars(text, NULL); - data->text = (jchar*)malloc((textLen + 1) * sizeof(jchar)); - memcpy(data->text, textRaw, textLen * sizeof(jchar)); + data->text = new jchar[textLen + 1]; + env->GetStringRegion(text, 0, textLen, data->text); data->text[textLen] = 0; - env->ReleaseStringChars(text, textRaw); } uregex_setText(data->regex, data->text, textLen, &status); diff --git a/icu/src/main/native/ResourceInterface.cpp b/icu/src/main/native/ResourceInterface.cpp index a88e15c..731cf3f 100644 --- a/icu/src/main/native/ResourceInterface.cpp +++ b/icu/src/main/native/ResourceInterface.cpp @@ -40,32 +40,6 @@ jclass string_class; -static UBool icuError(JNIEnv *env, UErrorCode errorcode) -{ - const char *emsg = u_errorName(errorcode); - jclass exception; - - if (U_FAILURE(errorcode)) { - switch (errorcode) { - case U_ILLEGAL_ARGUMENT_ERROR : - exception = env->FindClass("java/lang/IllegalArgumentException"); - break; - case U_INDEX_OUTOFBOUNDS_ERROR : - case U_BUFFER_OVERFLOW_ERROR : - exception = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); - break; - case U_UNSUPPORTED_ERROR : - exception = env->FindClass("java/lang/UnsupportedOperationException"); - break; - default : - exception = env->FindClass("java/lang/RuntimeException"); - } - - return (env->ThrowNew(exception, emsg) != 0); - } - return 0; -} - static Locale getLocale(JNIEnv *env, jstring locale) { const char *name = env->GetStringUTFChars(locale, NULL); Locale result = Locale::createFromName(name); diff --git a/luni-kernel/src/main/java/java/lang/Package.java b/luni-kernel/src/main/java/java/lang/Package.java index 2817404..4d98959 100644 --- a/luni-kernel/src/main/java/java/lang/Package.java +++ b/luni-kernel/src/main/java/java/lang/Package.java @@ -268,8 +268,8 @@ public class Package implements AnnotatedElement { */ public boolean isCompatibleWith(String version) throws NumberFormatException { - String[] requested = version.split("."); - String[] provided = specVersion.split("."); + String[] requested = version.split("\\."); + String[] provided = specVersion.split("\\."); for (int i = 0; i < Math.min(requested.length, provided.length); i++) { int reqNum = Integer.parseInt(requested[i]); @@ -318,4 +318,3 @@ public class Package implements AnnotatedElement { return "package " + name; } } - diff --git a/luni-kernel/src/main/java/java/lang/ref/Reference.java b/luni-kernel/src/main/java/java/lang/ref/Reference.java index ca7290b..c695830 100644 --- a/luni-kernel/src/main/java/java/lang/ref/Reference.java +++ b/luni-kernel/src/main/java/java/lang/ref/Reference.java @@ -49,7 +49,7 @@ public abstract class Reference<T> { * VM requirement: this field <em>must</em> be called "referent" * and be an object. */ - T referent; + volatile T referent; /** * If non-null, the queue on which this reference will be enqueued @@ -58,7 +58,7 @@ public abstract class Reference<T> { * and be a java.lang.ref.ReferenceQueue. */ @SuppressWarnings("unchecked") - ReferenceQueue queue; + volatile ReferenceQueue queue; /** * Used internally by java.lang.ref.ReferenceQueue. @@ -66,7 +66,7 @@ public abstract class Reference<T> { * and be a java.lang.ref.Reference. */ @SuppressWarnings("unchecked") - Reference queueNext; + volatile Reference queueNext; /** * Used internally by Dalvik. @@ -74,7 +74,7 @@ public abstract class Reference<T> { * and be an int. */ @SuppressWarnings("unused") - private int vmData; + volatile private int vmData; /** * Constructs a new instance of this class. diff --git a/luni-kernel/src/main/native/java_lang_ProcessManager.c b/luni-kernel/src/main/native/java_lang_ProcessManager.c index eaefc9f..46e78f5 100644 --- a/luni-kernel/src/main/native/java_lang_ProcessManager.c +++ b/luni-kernel/src/main/native/java_lang_ProcessManager.c @@ -64,8 +64,7 @@ static void java_lang_ProcessManager_close(JNIEnv* env, jclass clazz, jobject javaDescriptor) { int fd = (*env)->GetIntField(env, javaDescriptor, descriptorField); if (closeNow(fd) == -1) { - jclass ioException = (*env)->FindClass(env, "java/io/IOException"); - (*env)->ThrowNew(env, ioException, strerror(errno)); + jniThrowIOException(env, errno); } } diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java index 1243262..c236888 100644 --- a/luni/src/main/java/java/io/FileInputStream.java +++ b/luni/src/main/java/java/io/FileInputStream.java @@ -312,25 +312,24 @@ public class FileInputStream extends InputStream implements Closeable { } openCheck(); synchronized (repositioningLock) { - // stdin requires special handling - if (fd == FileDescriptor.in) { - return (int) fileSystem.ttyRead(buffer, offset, count); - } + // BEGIN android-changed + // If you only support Linux, there's nothing special about stdin. return (int) fileSystem.read(fd.descriptor, buffer, offset, count); + // END android-changed } } /** * Skips {@code count} number of bytes in this stream. Subsequent - * {@code read()}'s will not return these bytes unless {@code reset()} is - * used. This method may perform multiple reads to read {@code count} bytes. + * {@code read()}s will not return these bytes unless {@code reset()} is + * used. If the underlying stream is unseekable, an IOException is thrown. * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. * @throws IOException - * if {@code count < 0}, this stream is closed or another - * IOException occurs. + * if {@code count < 0}, this stream is closed or unseekable, + * or another IOException occurs. */ @Override public long skip(long count) throws IOException { @@ -344,29 +343,18 @@ public class FileInputStream extends InputStream implements Closeable { throw new IOException(Msg.getString("KA013")); //$NON-NLS-1$ } - // stdin requires special handling - if (fd == FileDescriptor.in) { - // Read and discard count bytes in 8k chunks - long skipped = 0, numRead; - int chunk = count < 8192 ? (int) count : 8192; - byte[] buffer = new byte[chunk]; - for (long i = count / chunk; i >= 0; i--) { - numRead = fileSystem.ttyRead(buffer, 0, chunk); - skipped += numRead; - if (numRead < chunk) { - return skipped; - } - } - return skipped; - } - + // BEGIN android-changed + // The RI doesn't treat stdin as special. It throws IOException for + // all non-seekable streams, so we do too. If you did want to support + // non-seekable streams, the best way to do it would be to recognize + // when lseek(2) fails with ESPIPE and call super.skip(count). synchronized (repositioningLock) { - final long currentPosition = fileSystem.seek(fd.descriptor, 0L, - IFileSystem.SEEK_CUR); - final long newPosition = fileSystem.seek(fd.descriptor, - currentPosition + count, IFileSystem.SEEK_SET); - return newPosition - currentPosition; + // Our seek returns the new offset, but we know it will throw an + // exception if it couldn't perform exactly the seek we asked for. + fileSystem.seek(fd.descriptor, count, IFileSystem.SEEK_CUR); + return count; } + // END android-changed } private synchronized void openCheck() throws IOException { diff --git a/luni/src/main/java/java/util/ArrayList.java b/luni/src/main/java/java/util/ArrayList.java index b8c7056..7c46e89 100644 --- a/luni/src/main/java/java/util/ArrayList.java +++ b/luni/src/main/java/java/util/ArrayList.java @@ -15,12 +15,16 @@ * limitations under the License. */ +// BEGIN android-note +// New implementation: simpler and faster than Harmony implementation. +// BEGIN android-note + package java.util; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.ObjectStreamField; import java.io.Serializable; import java.lang.reflect.Array; @@ -29,37 +33,38 @@ import java.lang.reflect.Array; * optional operations adding, removing, and replacing are supported. The * elements can be any objects. * + * @param <E> The element type of this list. * @since 1.2 */ -public class ArrayList<E> extends AbstractList<E> implements List<E>, - Cloneable, Serializable, RandomAccess { - - private static final long serialVersionUID = 8683452581122892189L; - - // BEGIN android-added - /** zero-element array */ - private static final Object[] emptyArray = new Object[0]; - // END android-added - - private transient int firstIndex; +public class ArrayList<E> extends AbstractList<E> + implements Cloneable, Serializable, RandomAccess { + /** + * An empty array of objects (to be shared among all empty lists). + */ + private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; - private transient int lastIndex; + /** + * The minimum amount by which the capacity of an ArrayList will increase. + * This tuning parameter controls a time-space tradeoff. This value (12) + * gives empirically good results and is arguably consistent with the + * RI's specified default initial capacity of 10: instead of 10, we start + * with 0 (sans allocation) and jump to 12. + */ + private static final int MIN_CAPACITY_INCREMENT = 12; - private transient E[] array; + /** + * The number of elements in this list. + */ + int size; /** - * Constructs a new instance of {@code ArrayList} with zero capacity. + * The elements in this list, followed by nulls. */ - public ArrayList() { - // BEGIN android-changed - // default capacity is zero, not ten - this(0); - // END android-changed - } + transient Object[] array; /** * Constructs a new instance of {@code ArrayList} with the specified - * capacity. + * initial capacity. * * @param capacity * the initial capacity of this {@code ArrayList}. @@ -68,37 +73,55 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, if (capacity < 0) { throw new IllegalArgumentException(); } - firstIndex = lastIndex = 0; - array = newElementArray(capacity); + array = (capacity == 0 ? EMPTY_OBJECT_ARRAY : new Object[capacity]); + } + + /** + * Constructs a new {@code ArrayList} instance with zero initial capacity. + */ + public ArrayList() { + array = EMPTY_OBJECT_ARRAY; } /** * Constructs a new instance of {@code ArrayList} containing the elements of - * the specified collection. The initial size of the {@code ArrayList} will - * be 10% higher than the size of the specified collection. + * the specified collection. * * @param collection * the collection of elements to add. */ public ArrayList(Collection<? extends E> collection) { - firstIndex = 0; - Object[] objects = collection.toArray(); - int size = objects.length; - array = newElementArray(size + (size / 10)); - System.arraycopy(objects, 0, array, 0, size); - lastIndex = size; - modCount = 1; + Object[] a = collection.toArray(); + if (a.getClass() != Object[].class) { + Object[] newArray = new Object[a.length]; + System.arraycopy(a, 0, newArray, 0, a.length); + a = newArray; + } + array = a; + size = a.length; } - @SuppressWarnings("unchecked") - private E[] newElementArray(int size) { - // BEGIN android-added - if (size == 0) { - return (E[])emptyArray; + /** + * Adds the specified object at the end of this {@code ArrayList}. + * + * @param object + * the object to add. + * @return always true + */ + @Override public boolean add(E object) { + Object[] a = array; + int s = size; + if (s == a.length) { + Object[] newArray = new Object[s + + (s < (MIN_CAPACITY_INCREMENT / 2) ? + MIN_CAPACITY_INCREMENT : s >> 1)]; + System.arraycopy(a, 0, newArray, 0, s); + array = a = newArray; } - // END android-added - - return (E[]) new Object[size]; + a[s] = object; + size = s + 1; + modCount++; + return true; } /** @@ -107,164 +130,136 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * specified location. If the location is equal to the size of this * {@code ArrayList}, the object is added at the end. * - * @param location + * @param index * the index at which to insert the object. * @param object * the object to add. * @throws IndexOutOfBoundsException * when {@code location < 0 || > size()} */ - @Override - public void add(int location, E object) { - int size = lastIndex - firstIndex; - if (0 < location && location < size) { - if (firstIndex == 0 && lastIndex == array.length) { - growForInsert(location, 1); - } else if ((location < size / 2 && firstIndex > 0) - || lastIndex == array.length) { - System.arraycopy(array, firstIndex, array, --firstIndex, - location); - } else { - int index = location + firstIndex; - System.arraycopy(array, index, array, index + 1, size - - location); - lastIndex++; - } - array[location + firstIndex] = object; - } else if (location == 0) { - if (firstIndex == 0) { - growAtFront(1); - } - array[--firstIndex] = object; - } else if (location == size) { - if (lastIndex == array.length) { - growAtEnd(1); - } - array[lastIndex++] = object; - } else { - throw new IndexOutOfBoundsException(); + @Override public void add(int index, E object) { + Object[] a = array; + int s = size; + if (index > s) { + throwIndexOutOfBoundsException(index, s); } + if (s < a.length) { + System.arraycopy(a, index, a, index + 1, s - index); + } else { + // assert s == a.length; + Object[] newArray = new Object[newCapacity(s)]; + System.arraycopy(a, 0, newArray, 0, index); + System.arraycopy(a, index, newArray, index + 1, s - index); + array = a = newArray; + } + a[index] = object; + size = s + 1; modCount++; } /** - * Adds the specified object at the end of this {@code ArrayList}. + * This method controls the growth of ArrayList capacities. It represents + * a time-space tradeoff: we don't want to grow lists too frequently + * (which wastes time and fragments storage), but we don't want to waste + * too much space in unused excess capacity. * - * @param object - * the object to add. - * @return always true + * NOTE: This method is inlined into {@link #add(Object)} for performance. + * If you change the method, change it there too! */ - @Override - public boolean add(E object) { - if (lastIndex == array.length) { - growAtEnd(1); - } - array[lastIndex++] = object; - modCount++; - return true; + private static int newCapacity(int currentCapacity) { + int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ? + MIN_CAPACITY_INCREMENT : currentCapacity >> 1); + return currentCapacity + increment; } /** - * Inserts the objects in the specified collection at the specified location - * in this List. The objects are added in the order they are returned from - * the collection's iterator. + * Adds the objects in the specified collection to this {@code ArrayList}. * - * @param location - * the index at which to insert. * @param collection * the collection of objects. * @return {@code true} if this {@code ArrayList} is modified, {@code false} * otherwise. - * @throws IndexOutOfBoundsException - * when {@code location < 0 || > size()} */ - @Override - public boolean addAll(int location, Collection<? extends E> collection) { - int size = lastIndex - firstIndex; - if (location < 0 || location > size) { - throw new IndexOutOfBoundsException(); - } - if (this == collection) { - collection = (ArrayList)clone(); - } - Object[] dumparray = collection.toArray(); - int growSize = dumparray.length; - if (growSize == 0) { + @Override public boolean addAll(Collection<? extends E> collection) { + Object[] newPart = collection.toArray(); + int newPartSize = newPart.length; + if (newPartSize == 0) { return false; } - - if (0 < location && location < size) { - if (array.length - size < growSize) { - growForInsert(location, growSize); - } else if ((location < size / 2 && firstIndex > 0) - || lastIndex > array.length - growSize) { - int newFirst = firstIndex - growSize; - if (newFirst < 0) { - int index = location + firstIndex; - System.arraycopy(array, index, array, index - newFirst, - size - location); - lastIndex -= newFirst; - newFirst = 0; - } - System.arraycopy(array, firstIndex, array, newFirst, location); - firstIndex = newFirst; - } else { - int index = location + firstIndex; - System.arraycopy(array, index, array, index + growSize, size - - location); - lastIndex += growSize; - } - } else if (location == 0) { - growAtFront(growSize); - firstIndex -= growSize; - } else if (location == size) { - if (lastIndex > array.length - growSize) { - growAtEnd(growSize); - } - lastIndex += growSize; + Object[] a = array; + int s = size; + int newSize = s + newPartSize; // If add overflows, arraycopy will fail + if (newSize > a.length) { + int newCapacity = newCapacity(newSize - 1); // ~33% growth room + Object[] newArray = new Object[newCapacity]; + System.arraycopy(a, 0, newArray, 0, s); + array = a = newArray; } - - System.arraycopy(dumparray, 0, this.array, location + firstIndex, - growSize); + System.arraycopy(newPart, 0, a, s, newPartSize); + size = newSize; modCount++; return true; } /** - * Adds the objects in the specified collection to this {@code ArrayList}. + * Inserts the objects in the specified collection at the specified location + * in this List. The objects are added in the order they are returned from + * the collection's iterator. * + * @param index + * the index at which to insert. * @param collection * the collection of objects. * @return {@code true} if this {@code ArrayList} is modified, {@code false} * otherwise. + * @throws IndexOutOfBoundsException + * when {@code location < 0 || > size()} */ @Override - public boolean addAll(Collection<? extends E> collection) { - Object[] dumpArray = collection.toArray(); - if (dumpArray.length == 0) { + public boolean addAll(int index, Collection<? extends E> collection) { + Object[] newPart = collection.toArray(); + int newPartSize = newPart.length; + if (newPartSize == 0) { return false; } - if (dumpArray.length > array.length - lastIndex) { - growAtEnd(dumpArray.length); + Object[] a = array; + int s = size; + if (index > s) { + throwIndexOutOfBoundsException(index, s); } - System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length); - lastIndex += dumpArray.length; + int newSize = s + newPartSize; // If add overflows, arraycopy will fail + if (newSize <= a.length) { + System.arraycopy(a, index, a, index + newPartSize, s - index); + } else { + int newCapacity = newCapacity(newSize - 1); // ~33% growth room + Object[] newArray = new Object[newCapacity]; + System.arraycopy(a, 0, newArray, 0, index); + System.arraycopy(a, index, newArray, index + newPartSize, s-index); + array = a = newArray; + } + System.arraycopy(newPart, 0, a, index, newPartSize); + size = newSize; modCount++; return true; } + /** This method was extracted to encourage VM to inline callers. */ + private static void throwIndexOutOfBoundsException(int index, int size) { + throw new IndexOutOfBoundsException("Invalid index " + index + + ", size is " + size); + } + /** * Removes all elements from this {@code ArrayList}, leaving it empty. * * @see #isEmpty * @see #size */ - @Override - public void clear() { - if (firstIndex != lastIndex) { - Arrays.fill(array, firstIndex, lastIndex, null); - firstIndex = lastIndex = 0; + @Override public void clear() { + if (size != 0) { + Arrays.fill(array, 0, size, null); + size = 0; modCount++; } } @@ -276,45 +271,17 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * @return a shallow copy of this {@code ArrayList} * @see java.lang.Cloneable */ - @Override - @SuppressWarnings("unchecked") - public Object clone() { + @Override public Object clone() { try { - ArrayList<E> newList = (ArrayList<E>) super.clone(); - newList.array = array.clone(); - return newList; + ArrayList<?> result = (ArrayList<?>) super.clone(); + result.array = array.clone(); + return result; } catch (CloneNotSupportedException e) { - return null; + throw new AssertionError(); } } /** - * Searches this {@code ArrayList} for the specified object. - * - * @param object - * the object to search for. - * @return {@code true} if {@code object} is an element of this - * {@code ArrayList}, {@code false} otherwise - */ - @Override - public boolean contains(Object object) { - if (object != null) { - for (int i = firstIndex; i < lastIndex; i++) { - if (object.equals(array[i])) { - return true; - } - } - } else { - for (int i = firstIndex; i < lastIndex; i++) { - if (array[i] == null) { - return true; - } - } - } - return false; - } - - /** * Ensures that after this operation the {@code ArrayList} can hold the * specified number of elements without further growing. * @@ -322,145 +289,93 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * the minimum capacity asked for. */ public void ensureCapacity(int minimumCapacity) { - if (array.length < minimumCapacity) { - if (firstIndex > 0) { - growAtFront(minimumCapacity - array.length); - } else { - growAtEnd(minimumCapacity - array.length); - } + Object[] a = array; + if (a.length < minimumCapacity) { + Object[] newArray = new Object[minimumCapacity]; + System.arraycopy(a, 0, newArray, 0, size); + array = newArray; + modCount++; } } - @Override - public E get(int location) { - // BEGIN android-changed: slight performance improvement - int _firstIndex = firstIndex; - if (0 <= location && location < lastIndex - _firstIndex) { - return array[_firstIndex + location]; - } - throw new IndexOutOfBoundsException("Invalid location " + location - + ", size is " + (lastIndex - _firstIndex)); - // END android-changed + @SuppressWarnings("unchecked") @Override public E get(int index) { + if (index >= size) { + throwIndexOutOfBoundsException(index, size); + } + return (E) array[index]; } - private void growAtEnd(int required) { - int size = lastIndex - firstIndex; - if (firstIndex >= required - (array.length - lastIndex)) { - int newLast = lastIndex - firstIndex; - if (size > 0) { - System.arraycopy(array, firstIndex, array, 0, size); - int start = newLast < firstIndex ? firstIndex : newLast; - Arrays.fill(array, start, array.length, null); - } - firstIndex = 0; - lastIndex = newLast; - } else { - int increment = size / 2; - if (required > increment) { - increment = required; - } - if (increment < 12) { - increment = 12; - } - E[] newArray = newElementArray(size + increment); - if (size > 0) { - System.arraycopy(array, firstIndex, newArray, 0, size); - firstIndex = 0; - lastIndex = size; - } - array = newArray; - } + /** + * Returns the number of elements in this {@code ArrayList}. + * + * @return the number of elements in this {@code ArrayList}. + */ + @Override public int size() { + return size; + } + + @Override public boolean isEmpty() { + return size == 0; } - private void growAtFront(int required) { - int size = lastIndex - firstIndex; - if (array.length - lastIndex + firstIndex >= required) { - int newFirst = array.length - size; - if (size > 0) { - System.arraycopy(array, firstIndex, array, newFirst, size); - int length = firstIndex + size > newFirst ? newFirst - : firstIndex + size; - Arrays.fill(array, firstIndex, length, null); + /** + * Searches this {@code ArrayList} for the specified object. + * + * @param object + * the object to search for. + * @return {@code true} if {@code object} is an element of this + * {@code ArrayList}, {@code false} otherwise + */ + @Override public boolean contains(Object object) { + Object[] a = array; + int s = size; + if (object != null) { + for (int i = 0; i < s; i++) { + if (object.equals(a[i])) { + return true; + } } - firstIndex = newFirst; - lastIndex = array.length; } else { - int increment = size / 2; - if (required > increment) { - increment = required; - } - if (increment < 12) { - increment = 12; - } - E[] newArray = newElementArray(size + increment); - if (size > 0) { - System.arraycopy(array, firstIndex, newArray, newArray.length - - size, size); + for (int i = 0; i < s; i++) { + if (a[i] == null) { + return true; + } } - firstIndex = newArray.length - size; - lastIndex = newArray.length; - array = newArray; } + return false; } - private void growForInsert(int location, int required) { - int size = lastIndex - firstIndex; - int increment = size / 2; - if (required > increment) { - increment = required; - } - if (increment < 12) { - increment = 12; - } - E[] newArray = newElementArray(size + increment); - int newFirst = increment - required; - // Copy elements after location to the new array skipping inserted - // elements - System.arraycopy(array, location + firstIndex, newArray, newFirst - + location + required, size - location); - // Copy elements before location to the new array from firstIndex - System.arraycopy(array, firstIndex, newArray, newFirst, location); - firstIndex = newFirst; - lastIndex = size + increment; - - array = newArray; - } - - @Override - public int indexOf(Object object) { + @Override public int indexOf(Object object) { + Object[] a = array; + int s = size; if (object != null) { - for (int i = firstIndex; i < lastIndex; i++) { - if (object.equals(array[i])) { - return i - firstIndex; + for (int i = 0; i < s; i++) { + if (object.equals(a[i])) { + return i; } } } else { - for (int i = firstIndex; i < lastIndex; i++) { - if (array[i] == null) { - return i - firstIndex; + for (int i = 0; i < s; i++) { + if (a[i] == null) { + return i; } } } return -1; } - @Override - public boolean isEmpty() { - return lastIndex == firstIndex; - } - - @Override - public int lastIndexOf(Object object) { + @Override public int lastIndexOf(Object object) { + Object[] a = array; if (object != null) { - for (int i = lastIndex - 1; i >= firstIndex; i--) { - if (object.equals(array[i])) { - return i - firstIndex; + for (int i = size - 1; i >= 0; i--) { + if (object.equals(a[i])) { + return i; } } } else { - for (int i = lastIndex - 1; i >= firstIndex; i--) { - if (array[i] == null) { - return i - firstIndex; + for (int i = size - 1; i >= 0; i--) { + if (a[i] == null) { + return i; } } } @@ -470,99 +385,81 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, /** * Removes the object at the specified location from this list. * - * @param location + * @param index * the index of the object to remove. * @return the removed object. * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} */ - @Override - public E remove(int location) { - E result; - int size = lastIndex - firstIndex; - if (0 <= location && location < size) { - if (location == size - 1) { - result = array[--lastIndex]; - array[lastIndex] = null; - } else if (location == 0) { - result = array[firstIndex]; - array[firstIndex++] = null; - } else { - int elementIndex = firstIndex + location; - result = array[elementIndex]; - if (location < size / 2) { - System.arraycopy(array, firstIndex, array, firstIndex + 1, - location); - array[firstIndex++] = null; - } else { - System.arraycopy(array, elementIndex + 1, array, - elementIndex, size - location - 1); - array[--lastIndex] = null; - } - } - if (firstIndex == lastIndex) { - firstIndex = lastIndex = 0; - } - } else { - throw new IndexOutOfBoundsException(); + @Override public E remove(int index) { + Object[] a = array; + int s = size; + if (index >= s) { + throwIndexOutOfBoundsException(index, s); } - + @SuppressWarnings("unchecked") E result = (E) a[index]; + System.arraycopy(a, index + 1, a, index, --s - index); + a[s] = null; // Prevent memory leak + size = s; modCount++; return result; } - @Override - public boolean remove(Object object) { - int location = indexOf(object); - if (location >= 0) { - remove(location); - return true; + @Override public boolean remove(Object object) { + Object[] a = array; + int s = size; + if (object != null) { + for (int i = 0; i < s; i++) { + if (object.equals(a[i])) { + System.arraycopy(a, i + 1, a, i, --s - i); + a[s] = null; // Prevent memory leak + size = s; + modCount++; + return true; + } + } + } else { + for (int i = 0; i < s; i++) { + if (a[i] == null) { + System.arraycopy(a, i + 1, a, i, --s - i); + a[s] = null; // Prevent memory leak + size = s; + modCount++; + return true; + } + } } return false; } - /** - * Removes the objects in the specified range from the start to the end, but - * not including the end index. - * - * @param start - * the index at which to start removing. - * @param end - * the index one after the end of the range to remove. - * @throws IndexOutOfBoundsException - * when {@code start < 0, start > end} or {@code end > size()} - */ - @Override - protected void removeRange(int start, int end) { - if (start >= 0 && start <= end && end <= (lastIndex - firstIndex)) { - if (start == end) { - return; - } - int size = lastIndex - firstIndex; - if (end == size) { - Arrays.fill(array, firstIndex + start, lastIndex, null); - lastIndex = firstIndex + start; - } else if (start == 0) { - Arrays.fill(array, firstIndex, firstIndex + end, null); - firstIndex += end; - } else { - System.arraycopy(array, firstIndex + end, array, firstIndex - + start, size - end); - int newLast = lastIndex + start - end; - Arrays.fill(array, newLast, lastIndex, null); - lastIndex = newLast; - } - modCount++; - } else { - throw new IndexOutOfBoundsException(); + @Override protected void removeRange(int fromIndex, int toIndex) { + Object[] a = array; + int s = size; + if (fromIndex >= s) { + throw new IndexOutOfBoundsException("fromIndex " + fromIndex + + " >= size " + size); } + if (toIndex > s) { + throw new IndexOutOfBoundsException("toIndex " + toIndex + + " > size " + size); + } + if (fromIndex > toIndex) { + throw new IndexOutOfBoundsException("fromIndex " + fromIndex + + " > toIndex " + toIndex); + } + + System.arraycopy(a, toIndex, a, fromIndex, s - toIndex); + int rangeSize = toIndex - fromIndex; + Arrays.fill(a, s - rangeSize, s, null); + size = s - rangeSize; + modCount++; } /** * Replaces the element at the specified location in this {@code ArrayList} * with the specified object. * - * @param location + * @param index * the index at which to put the specified object. * @param object * the object to add. @@ -570,24 +467,14 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} */ - @Override - public E set(int location, E object) { - if (0 <= location && location < (lastIndex - firstIndex)) { - E result = array[firstIndex + location]; - array[firstIndex + location] = object; - return result; + @Override public E set(int index, E object) { + Object[] a = array; + if (index >= size) { + throwIndexOutOfBoundsException(index, size); } - throw new IndexOutOfBoundsException(); - } - - /** - * Returns the number of elements in this {@code ArrayList}. - * - * @return the number of elements in this {@code ArrayList}. - */ - @Override - public int size() { - return lastIndex - firstIndex; + @SuppressWarnings("unchecked") E result = (E) a[index]; + a[index] = object; + return result; } /** @@ -596,11 +483,10 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * * @return an array of the elements from this {@code ArrayList} */ - @Override - public Object[] toArray() { - int size = lastIndex - firstIndex; - Object[] result = new Object[size]; - System.arraycopy(array, firstIndex, result, 0, size); + @Override public Object[] toArray() { + int s = size; + Object[] result = new Object[s]; + System.arraycopy(array, 0, result, 0, s); return result; } @@ -619,17 +505,16 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * when the type of an element in this {@code ArrayList} cannot * be stored in the type of the specified array. */ - @Override - @SuppressWarnings("unchecked") - public <T> T[] toArray(T[] contents) { - int size = lastIndex - firstIndex; - if (size > contents.length) { - Class<?> ct = contents.getClass().getComponentType(); - contents = (T[]) Array.newInstance(ct, size); + @Override public <T> T[] toArray(T[] contents) { + int s = size; + if (contents.length < s) { + @SuppressWarnings("unchecked") T[] newArray + = (T[]) Array.newInstance(contents.getClass().getComponentType(), s); + contents = newArray; } - System.arraycopy(array, firstIndex, contents, 0, size); - if (size < contents.length) { - contents[size] = null; + System.arraycopy(this.array, 0, contents, 0, s); + if (contents.length > s) { + contents[s] = null; } return contents; } @@ -641,37 +526,132 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, * @see #size */ public void trimToSize() { - int size = lastIndex - firstIndex; - E[] newArray = newElementArray(size); - System.arraycopy(array, firstIndex, newArray, 0, size); - array = newArray; - firstIndex = 0; - lastIndex = array.length; - modCount = 0; + int s = size; + if (s == array.length) { + return; + } + if (s == 0) { + array = EMPTY_OBJECT_ARRAY; + } else { + Object[] newArray = new Object[s]; + System.arraycopy(array, 0, newArray, 0, s); + array = newArray; + } + modCount++; + } + + @Override public Iterator<E> iterator() { + return new ArrayListIterator(); + } + + private class ArrayListIterator implements Iterator<E> { + /** Number of elements remaining in this iteration */ + private int remaining = size; + + /** Index of element that remove() would remove, or -1 if no such elt */ + private int removalIndex = -1; + + /** The expected modCount value */ + private int expectedModCount = modCount; + + public boolean hasNext() { + return remaining != 0; + } + + @SuppressWarnings("unchecked") public E next() { + ArrayList<E> ourList = ArrayList.this; + int rem = remaining; + if (ourList.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + if (rem == 0) { + throw new NoSuchElementException(); + } + remaining = rem - 1; + return (E) ourList.array[removalIndex = ourList.size - rem]; + } + + public void remove() { + Object[] a = array; + int removalIdx = removalIndex; + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + if (removalIdx < 0) { + throw new IllegalStateException(); + } + System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining); + a[--size] = null; // Prevent memory leak + removalIndex = -1; + expectedModCount = ++modCount; + } } - private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField( - "size", Integer.TYPE) }; //$NON-NLS-1$ + @Override public int hashCode() { + Object[] a = array; + int hashCode = 1; + for (int i = 0, s = size; i < s; i++) { + Object e = a[i]; + hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); + } + return hashCode; + } + + @Override public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof List)) { + return false; + } + List<?> that = (List<?>) o; + int s = size; + if (that.size() != s) { + return false; + } + Object[] a = array; + if (that instanceof RandomAccess) { + for (int i = 0; i < s; i++) { + Object eThis = a[i]; + Object ethat = that.get(i); + if (eThis == null ? ethat != null : !eThis.equals(ethat)) { + return false; + } + } + } else { // Argument list is not random access; use its iterator + Iterator<?> it = that.iterator(); + for (int i = 0; i < s; i++) { + Object eThis = a[i]; + Object eThat = it.next(); + if (eThis == null ? eThat != null : !eThis.equals(eThat)) { + return false; + } + } + } + return true; + } + + private static final long serialVersionUID = 8683452581122892189L; private void writeObject(ObjectOutputStream stream) throws IOException { - ObjectOutputStream.PutField fields = stream.putFields(); - fields.put("size", lastIndex - firstIndex); //$NON-NLS-1$ - stream.writeFields(); + stream.defaultWriteObject(); stream.writeInt(array.length); - Iterator<?> it = iterator(); - while (it.hasNext()) { - stream.writeObject(it.next()); + for (int i = 0; i < size; i++) { + stream.writeObject(array[i]); } } - @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - ObjectInputStream.GetField fields = stream.readFields(); - lastIndex = fields.get("size", 0); //$NON-NLS-1$ - array = newElementArray(stream.readInt()); - for (int i = 0; i < lastIndex; i++) { - array[i] = (E) stream.readObject(); + stream.defaultReadObject(); + int cap = stream.readInt(); + if (cap < size) { + throw new InvalidObjectException( + "Capacity: " + cap + " < size: " + size); + } + array = (cap == 0 ? EMPTY_OBJECT_ARRAY : new Object[cap]); + for (int i = 0; i < size; i++) { + array[i] = stream.readObject(); } } -} + } diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java index d1dd417..3d8b94e 100644 --- a/luni/src/main/java/java/util/Formatter.java +++ b/luni/src/main/java/java/util/Formatter.java @@ -885,7 +885,6 @@ public final class Formatter implements Closeable, Flushable { if (transformer == null || ! transformer.locale.equals(l)) { transformer = new Transformer(this, l); } - // END android-changed int currentObjectIndex = 0; Object lastArgument = null; @@ -893,12 +892,13 @@ public final class Formatter implements Closeable, Flushable { while (formatBuffer.hasRemaining()) { parser.reset(); FormatToken token = parser.getNextFormatToken(); - String result; String plainText = token.getPlainText(); if (token.getConversionType() == (char) FormatToken.UNSET) { - result = plainText; + outputCharSequence(plainText); } else { plainText = plainText.substring(0, plainText.indexOf('%')); + outputCharSequence(plainText); + Object argument = null; if (token.requireArgument()) { int index = token.getArgIndex() == FormatToken.UNSET ? currentObjectIndex++ @@ -908,21 +908,26 @@ public final class Formatter implements Closeable, Flushable { lastArgument = argument; hasLastArgumentSet = true; } - result = transformer.transform(token, argument); - result = (null == result ? plainText : plainText + result); - } - // if output is made by formattable callback - if (null != result) { - try { - out.append(result); - } catch (IOException e) { - lastIOException = e; - } + outputCharSequence(transformer.transform(token, argument)); } } + // END android-changed return this; } + // BEGIN android-added + // Fixes http://code.google.com/p/android/issues/detail?id=1767. + private void outputCharSequence(CharSequence cs) { + if (cs != null) { + try { + out.append(cs); + } catch (IOException e) { + lastIOException = e; + } + } + } + // END android-added + private Object getArgument(Object[] args, int index, FormatToken token, Object lastArgument, boolean hasLastArgumentSet) { if (index == FormatToken.LAST_ARGUMENT_INDEX && !hasLastArgumentSet) { @@ -1184,13 +1189,13 @@ public final class Formatter implements Closeable, Flushable { * Gets the formatted string according to the format token and the * argument. */ - String transform(FormatToken token, Object argument) { + CharSequence transform(FormatToken token, Object argument) { /* init data member to print */ this.formatToken = token; this.arg = argument; - String result; + CharSequence result; switch (token.getConversionType()) { case 'B': case 'b': { @@ -1254,7 +1259,7 @@ public final class Formatter implements Closeable, Flushable { if (Character.isUpperCase(token.getConversionType())) { if (null != result) { - result = result.toUpperCase(Locale.US); + result = result.toString().toUpperCase(Locale.US); } } return result; @@ -1263,7 +1268,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the Boolean argument to a formatted string. */ - private String transformFromBoolean() { + private CharSequence transformFromBoolean() { StringBuilder result = new StringBuilder(); int startIndex = 0; int flags = formatToken.getFlags(); @@ -1294,7 +1299,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the hashcode of the argument to a formatted string. */ - private String transformFromHashCode() { + private CharSequence transformFromHashCode() { StringBuilder result = new StringBuilder(); int startIndex = 0; @@ -1324,7 +1329,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the String to a formatted string. */ - private String transformFromString() { + private CharSequence transformFromString() { StringBuilder result = new StringBuilder(); int startIndex = 0; int flags = formatToken.getFlags(); @@ -1374,7 +1379,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms the Character to a formatted string. */ - private String transformFromCharacter() { + private CharSequence transformFromCharacter() { StringBuilder result = new StringBuilder(); int startIndex = 0; @@ -1434,7 +1439,7 @@ public final class Formatter implements Closeable, Flushable { * Transforms percent to a formatted string. Only '-' is legal flag. * Precision is illegal. */ - private String transformFromPercent() { + private CharSequence transformFromPercent() { StringBuilder result = new StringBuilder("%"); //$NON-NLS-1$ int startIndex = 0; @@ -1462,7 +1467,7 @@ public final class Formatter implements Closeable, Flushable { * Transforms line separator to a formatted string. Any flag, the width * or the precision is illegal. */ - private String transformFromLineSeparator() { + private CharSequence transformFromLineSeparator() { if (formatToken.isPrecisionSet()) { throw new IllegalFormatPrecisionException(formatToken .getPrecision()); @@ -1492,7 +1497,7 @@ public final class Formatter implements Closeable, Flushable { /* * Pads characters to the formatted string. */ - private String padding(StringBuilder source, int startIndex) { + private CharSequence padding(StringBuilder source, int startIndex) { int start = startIndex; boolean paddingRight = formatToken .isFlagSet(FormatToken.FLAG_MINUS); @@ -1520,7 +1525,7 @@ public final class Formatter implements Closeable, Flushable { width = Math.max(source.length(), width); } if (length >= width) { - return source.toString(); + return source; } char[] paddings = new char[width - length]; @@ -1532,13 +1537,13 @@ public final class Formatter implements Closeable, Flushable { } else { source.insert(start, insertString); } - return source.toString(); + return source; } /* * Transforms the Integer to a formatted string. */ - private String transformFromInteger() { + private CharSequence transformFromInteger() { int startIndex = 0; boolean isNegative = false; StringBuilder result = new StringBuilder(); @@ -1651,7 +1656,7 @@ public final class Formatter implements Closeable, Flushable { if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) { result = wrapParentheses(result); - return result.toString(); + return result; } if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) { @@ -1680,7 +1685,7 @@ public final class Formatter implements Closeable, Flushable { return result; } - private String transformFromSpecialNumber() { + private CharSequence transformFromSpecialNumber() { String source = null; if (!(arg instanceof Number) || arg instanceof BigDecimal) { @@ -1713,12 +1718,12 @@ public final class Formatter implements Closeable, Flushable { formatToken.setPrecision(FormatToken.UNSET); formatToken.setFlags(formatToken.getFlags() & (~FormatToken.FLAG_ZERO)); - source = padding(new StringBuilder(source), 0); + return padding(new StringBuilder(source), 0); } return source; } - private String transformFromNull() { + private CharSequence transformFromNull() { formatToken.setFlags(formatToken.getFlags() & (~FormatToken.FLAG_ZERO)); return padding(new StringBuilder("null"), 0); //$NON-NLS-1$ @@ -1727,7 +1732,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms a BigInteger to a formatted string. */ - private String transformFromBigInteger() { + private CharSequence transformFromBigInteger() { int startIndex = 0; boolean isNegative = false; StringBuilder result = new StringBuilder(); @@ -1817,7 +1822,7 @@ public final class Formatter implements Closeable, Flushable { if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) { result = wrapParentheses(result); - return result.toString(); + return result; } if (isNegative && formatToken.isFlagSet(FormatToken.FLAG_ZERO)) { @@ -1829,7 +1834,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms a Float,Double or BigDecimal to a formatted string. */ - private String transformFromFloat() { + private CharSequence transformFromFloat() { StringBuilder result = new StringBuilder(); int startIndex = 0; char currentConversionType = formatToken.getConversionType(); @@ -1883,7 +1888,7 @@ public final class Formatter implements Closeable, Flushable { currentConversionType, arg.getClass()); } - String specialNumberResult = transformFromSpecialNumber(); + CharSequence specialNumberResult = transformFromSpecialNumber(); if (null != specialNumberResult) { return specialNumberResult; } @@ -1896,7 +1901,7 @@ public final class Formatter implements Closeable, Flushable { } // output result FloatUtil floatUtil = new FloatUtil(result, formatToken, - (DecimalFormat) NumberFormat.getInstance(locale), arg); + (DecimalFormat) getNumberFormat(), arg); floatUtil.transform(formatToken, result); formatToken.setPrecision(FormatToken.UNSET); @@ -1904,7 +1909,7 @@ public final class Formatter implements Closeable, Flushable { if (getDecimalFormatSymbols().getMinusSign() == result.charAt(0)) { if (formatToken.isFlagSet(FormatToken.FLAG_PARENTHESIS)) { result = wrapParentheses(result); - return result.toString(); + return result; } } else { if (formatToken.isFlagSet(FormatToken.FLAG_SPACE)) { @@ -1933,7 +1938,7 @@ public final class Formatter implements Closeable, Flushable { /* * Transforms a Date to a formatted string. */ - private String transformFromDateTime() { + private CharSequence transformFromDateTime() { int startIndex = 0; char currentConversionType = formatToken.getConversionType(); diff --git a/luni/src/main/java/java/util/HashMap.java b/luni/src/main/java/java/util/HashMap.java index 28978d4..f79601f 100644 --- a/luni/src/main/java/java/util/HashMap.java +++ b/luni/src/main/java/java/util/HashMap.java @@ -36,7 +36,7 @@ import java.io.Serializable; * @param <V> the type of mapped values */ public class HashMap<K, V> extends AbstractMap<K, V> - implements Cloneable, Serializable, Map<K, V> { + implements Cloneable, Serializable { /** * Min capacity (other than zero) for a HashMap. Must be a power of two * greater than 1 (and less than 1 << 30). diff --git a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java index 110a0fd..01319e1 100644 --- a/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java +++ b/luni/src/main/java/org/apache/harmony/luni/internal/util/ZoneInfo.java @@ -111,12 +111,23 @@ public class ZoneInfo extends TimeZone { // Subtract the raw offset from all offsets so it can be changed // and affect them too. - // Find whether there exist any observances of DST. - for (int i = 0; i < mGmtOffs.length; i++) { mGmtOffs[i] -= mRawOffset; + } - if (mIsDsts[i] != 0) { + // Is this zone still observing DST? + // We don't care if they've historically used it: most places have at least once. + // We want to know whether the last "schedule info" (the unix times in the mTransitions + // array) is in the future. If it is, DST is still relevant. + // See http://code.google.com/p/android/issues/detail?id=877. + // This test means that for somewhere like Morocco, which tried DST in 2009 but has + // no future plans (and thus no future schedule info) will report "true" from + // useDaylightTime at the start of 2009 but "false" at the end. This seems appropriate. + long currentUnixTime = System.currentTimeMillis() / 1000; + if (mTransitions.length > 0) { + // (We're really dealing with uint32_t values, so long is most convenient in Java.) + long latestScheduleTime = mTransitions[mTransitions.length - 1] & 0xffffffff; + if (currentUnixTime < latestScheduleTime) { mUseDst = true; } } diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java index 7613f0e..bee1557 100644 --- a/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java +++ b/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java @@ -108,10 +108,9 @@ public interface IFileSystem { // BEGIN android-deleted // public long ttyAvailable() throws IOException; + // public long ttyRead(byte[] bytes, int offset, int length) throws IOException; // END android-deleted - public long ttyRead(byte[] bytes, int offset, int length) throws IOException; - // BEGIN android-added public int ioctlAvailable(int fileDescriptor) throws IOException; // END android-added diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java index 08bdac6..b7a62e2 100644 --- a/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java +++ b/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java @@ -69,7 +69,7 @@ class OSFileSystem implements IFileSystem { * Note that this value for Windows differs from the one for the * page size (64K and 4K respectively). */ - public native int getAllocGranularity() throws IOException; + public native int getAllocGranularity(); public boolean lock(int fileDescriptor, long start, long length, int type, boolean waitFlag) throws IOException { @@ -79,160 +79,71 @@ class OSFileSystem implements IFileSystem { return result != -1; } - private native int unlockImpl(int fileDescriptor, long start, long length); + // BEGIN android-changed + private native void unlockImpl(int fileDescriptor, long start, long length) throws IOException; public void unlock(int fileDescriptor, long start, long length) throws IOException { // Validate arguments validateLockArgs(IFileSystem.SHARED_LOCK_TYPE, start, length); - int result = unlockImpl(fileDescriptor, start, length); - if (result == -1) { - throw new IOException(); - } + unlockImpl(fileDescriptor, start, length); } - private native int fflushImpl(int fd, boolean metadata); - - public void fflush(int fileDescriptor, boolean metadata) - throws IOException { - int result = fflushImpl(fileDescriptor, metadata); - if (result == -1) { - throw new IOException(); - } - } + public native void fflush(int fileDescriptor, boolean metadata) throws IOException; /* * File position seeking. */ - - private native long seekImpl(int fd, long offset, int whence); - - public long seek(int fileDescriptor, long offset, int whence) - throws IOException { - long pos = seekImpl(fileDescriptor, offset, whence); - if (pos == -1) { - throw new IOException(); - } - return pos; - } + public native long seek(int fd, long offset, int whence) throws IOException; /* * Direct read/write APIs work on addresses. */ - private native long readDirectImpl(int fileDescriptor, int address, - int offset, int length); - - public long readDirect(int fileDescriptor, int address, int offset, - int length) throws IOException { - long bytesRead = readDirectImpl(fileDescriptor, address, offset, length); - if (bytesRead < -1) { - throw new IOException(); - } - return bytesRead; - } - - private native long writeDirectImpl(int fileDescriptor, int address, - int offset, int length); + public native long readDirect(int fileDescriptor, int address, int offset, int length); - public long writeDirect(int fileDescriptor, int address, int offset, - int length) throws IOException { - long bytesWritten = writeDirectImpl(fileDescriptor, address, offset, - length); - if (bytesWritten < 0) { - throw new IOException(); - } - return bytesWritten; - } + public native long writeDirect(int fileDescriptor, int address, int offset, int length) + throws IOException; /* * Indirect read/writes work on byte[]'s */ private native long readImpl(int fileDescriptor, byte[] bytes, int offset, - int length); + int length) throws IOException; public long read(int fileDescriptor, byte[] bytes, int offset, int length) throws IOException { if (bytes == null) { throw new NullPointerException(); } - long bytesRead = readImpl(fileDescriptor, bytes, offset, length); - if (bytesRead < -1) { - /* - * TODO: bytesRead is never less than -1 so this code - * does nothing? - * The native code throws an exception in only one case - * so perhaps this should be 'bytesRead < 0' to handle - * any other cases. But the other cases have been - * ignored until now so fixing this could break things - */ - throw new IOException(); - } - return bytesRead; + return readImpl(fileDescriptor, bytes, offset, length); } private native long writeImpl(int fileDescriptor, byte[] bytes, - int offset, int length); + int offset, int length) throws IOException; public long write(int fileDescriptor, byte[] bytes, int offset, int length) throws IOException { - long bytesWritten = writeImpl(fileDescriptor, bytes, offset, length); - if (bytesWritten < 0) { - throw new IOException(); + if (bytes == null) { + throw new NullPointerException(); } - return bytesWritten; + return writeImpl(fileDescriptor, bytes, offset, length); } + // END android-changed /* * Scatter/gather calls. */ - public long readv(int fileDescriptor, int[] addresses, int[] offsets, - int[] lengths, int size) throws IOException { - long bytesRead = readvImpl(fileDescriptor, addresses, offsets, lengths, - size); - if (bytesRead < -1) { - throw new IOException(); - } - return bytesRead; - } + public native long readv(int fileDescriptor, int[] addresses, + int[] offsets, int[] lengths, int size) throws IOException; - private native long readvImpl(int fileDescriptor, int[] addresses, - int[] offsets, int[] lengths, int size); + public native long writev(int fileDescriptor, int[] addresses, int[] offsets, + int[] lengths, int size) throws IOException; - public long writev(int fileDescriptor, int[] addresses, int[] offsets, - int[] lengths, int size) throws IOException { - long bytesWritten = writevImpl(fileDescriptor, addresses, offsets, - lengths, size); - if (bytesWritten < 0) { - throw new IOException(); - } - return bytesWritten; - } - - private native long writevImpl(int fileDescriptor, int[] addresses, - int[] offsets, int[] lengths, int size); - - private native int closeImpl(int fileDescriptor); - - /* - * (non-Javadoc) - * - * @see org.apache.harmony.luni.platform.IFileSystem#close(long) - */ - public void close(int fileDescriptor) throws IOException { - int rc = closeImpl(fileDescriptor); - if (rc == -1) { - throw new IOException(); - } - } - - public void truncate(int fileDescriptor, long size) throws IOException { - int rc = truncateImpl(fileDescriptor, size); - if (rc < 0) { - throw new IOException(); - } - } + // BEGIN android-changed + public native void close(int fileDescriptor) throws IOException; - private native int truncateImpl(int fileDescriptor, long size); + public native void truncate(int fileDescriptor, long size) throws IOException; + // END android-changed public int open(byte[] fileName, int mode) throws FileNotFoundException { if (fileName == null) { @@ -254,16 +165,10 @@ class OSFileSystem implements IFileSystem { private native int openImpl(byte[] fileName, int mode); - public long transfer(int fileHandler, FileDescriptor socketDescriptor, - long offset, long count) throws IOException { - long result = transferImpl(fileHandler, socketDescriptor, offset, count); - if (result < 0) - throw new IOException(); - return result; - } - - private native long transferImpl(int fileHandler, - FileDescriptor socketDescriptor, long offset, long count); + // BEGIN android-changed + public native long transfer(int fd, FileDescriptor sd, long offset, long count) + throws IOException; + // END android-changed // BEGIN android-deleted // public long ttyAvailable() throws IOException { @@ -277,17 +182,15 @@ class OSFileSystem implements IFileSystem { // private native long ttyAvailableImpl(); // END android-deleted - public long ttyRead(byte[] bytes, int offset, int length) throws IOException { - long nChar = ttyReadImpl(bytes, offset, length); - // BEGIN android-changed - if (nChar < -1) { - throw new IOException(); - } - // END android-changed - return nChar; - } - - private native long ttyReadImpl(byte[] bytes, int offset, int length); + // BEGIN android-deleted + // public long ttyRead(byte[] bytes, int offset, int length) throws IOException { + // if (bytes == null) { + // throw new NullPointerException(); + // } + // return ttyReadImpl(bytes, offset, length); + // } + // private native long ttyReadImpl(byte[] bytes, int offset, int length) throws IOException; + // END android-deleted // BEGIN android-added public native int ioctlAvailable(int fileDescriptor) throws IOException; diff --git a/luni/src/main/native/java_net_InetAddress.cpp b/luni/src/main/native/java_net_InetAddress.cpp index d7b4931..4e58a1f 100644 --- a/luni/src/main/native/java_net_InetAddress.cpp +++ b/luni/src/main/native/java_net_InetAddress.cpp @@ -24,7 +24,6 @@ #include <stdio.h> #include <string.h> -#include <assert.h> #include <netdb.h> #include <errno.h> @@ -48,19 +47,6 @@ static jstring InetAddress_gethostname(JNIEnv* env, jobject obj) } } -static void throwNullPointerException(JNIEnv* env) -{ - const char* className = "java/lang/NullPointerException"; - - jclass exClass = env->FindClass(className); - - if (exClass == NULL) { - LOGE("Unable to find class %s", className); - } else { - env->ThrowNew(exClass, NULL); - } -} - #if LOG_DNS static void logIpString(struct addrinfo* ai, const char* name) { @@ -208,7 +194,7 @@ jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj, jboolean preferIPv4Stack) { if (javaName == NULL) { - throwNullPointerException(env); + jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; } @@ -247,57 +233,44 @@ static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj, jbyteArray javaAddress) { if (javaAddress == NULL) { - throwNullPointerException(env); - return NULL; - } - - size_t addrlen = env->GetArrayLength(javaAddress); - jbyte* rawAddress = env->GetByteArrayElements(javaAddress, NULL); - if (rawAddress == NULL) { - throwNullPointerException(env); + jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; } // Convert the raw address bytes into a socket address structure. - int ret = 0; struct sockaddr_storage ss; - struct sockaddr_in *sin = (struct sockaddr_in *) &ss; - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; - size_t socklen; memset(&ss, 0, sizeof(ss)); - switch (addrlen) { - case 4: - socklen = sizeof(struct sockaddr_in); - sin->sin_family = AF_INET; - memcpy(&sin->sin_addr.s_addr, rawAddress, addrlen); - env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - break; - case 16: - socklen = sizeof(struct sockaddr_in6); - sin6->sin6_family = AF_INET6; - memcpy(&sin6->sin6_addr.s6_addr, rawAddress, addrlen); - env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - break; - default: - // The caller already throws an exception in this case. Don't worry - // about it here. - env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - return NULL; + + size_t socklen; + const size_t addressLength = env->GetArrayLength(javaAddress); + if (addressLength == 4) { + struct sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss); + sin->sin_family = AF_INET; + socklen = sizeof(struct sockaddr_in); + jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr); + env->GetByteArrayRegion(javaAddress, 0, 4, dst); + } else if (addressLength == 16) { + struct sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(&ss); + sin6->sin6_family = AF_INET6; + socklen = sizeof(struct sockaddr_in6); + jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr); + env->GetByteArrayRegion(javaAddress, 0, 16, dst); + } else { + // The caller already throws an exception in this case. Don't worry + // about it here. + return NULL; } // Look up the host name from the IP address. char name[NI_MAXHOST]; - if (ret == 0) { - ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name), - NULL, 0, NI_NAMEREQD); - } - - if (ret == 0) { - return env->NewStringUTF(name); + int ret = getnameinfo(reinterpret_cast<sockaddr*>(&ss), socklen, + name, sizeof(name), NULL, 0, NI_NAMEREQD); + if (ret != 0) { + jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); + return NULL; } - jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); - return NULL; + return env->NewStringUTF(name); } /* diff --git a/luni/src/main/native/java_net_NetworkInterface.c b/luni/src/main/native/java_net_NetworkInterface.c index db6d503..e994aea 100644 --- a/luni/src/main/native/java_net_NetworkInterface.c +++ b/luni/src/main/native/java_net_NetworkInterface.c @@ -34,27 +34,7 @@ * Throws an IOException with the given message. */ static void throwSocketException(JNIEnv *env, const char *message) { - jclass exClass = (*env)->FindClass(env, "java/net/SocketException"); - - if(exClass == NULL) { - LOGE("Unable to find class java/net/SocketException"); - } else { - (*env)->ThrowNew(env, exClass, message); - } -} - - -/** - * Throws a NullPointerException. - */ -static void throwNullPointerException(JNIEnv *env) { - jclass exClass = (*env)->FindClass(env, "java/lang/NullPointerException"); - - if(exClass == NULL) { - LOGE("Unable to find class java/lang/NullPointerException"); - } else { - (*env)->ThrowNew(env, exClass, NULL); - } + jniThrowException(env, "java/net/SocketException", message); } /** @@ -241,24 +221,18 @@ static char * netLookupErrorString(int anErrorNum) { static int structInToJavaAddress( JNIEnv *env, struct in_addr *address, jbyteArray java_address) { - if(java_address == NULL) { - throwNullPointerException(env); + if (java_address == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); return -1; } - if((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) { + if ((*env)->GetArrayLength(env, java_address) != sizeof(address->s_addr)) { jniThrowIOException(env, errno); return -1; } - jbyte *java_address_bytes; - - java_address_bytes = (*env)->GetByteArrayElements(env, java_address, NULL); - - memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); - - (*env)->ReleaseByteArrayElements(env, java_address, java_address_bytes, 0); - + jbyte* src = (jbyte*)(&(address->s_addr)); + (*env)->SetByteArrayRegion(env, java_address, 0, sizeof(address->s_addr), src); return 0; } diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp index 38f3d36..3e229d0 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSFileSystem.cpp @@ -14,401 +14,316 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +// BEGIN android-note +// This file corresponds to harmony's OSFileSystem.c and OSFileSystemLinux32.c. +// It has been greatly simplified by the assumption that the underlying +// platform is always Linux. +// END android-note + /* * Common natives supporting the file system interface. */ #define HyMaxPath 1024 -#define HyOpenRead 1 /* Values for HyFileOpen */ + +/* Values for HyFileOpen */ +#define HyOpenRead 1 #define HyOpenWrite 2 #define HyOpenCreate 4 #define HyOpenTruncate 8 #define HyOpenAppend 16 #define HyOpenText 32 - /* Use this flag with HyOpenCreate, if this flag is specified then - * trying to create an existing file will fail + * trying to create an existing file will fail */ #define HyOpenCreateNew 64 -#define HyOpenSync 128 +#define HyOpenSync 128 #define SHARED_LOCK_TYPE 1L #include "JNIHelp.h" #include "AndroidSystemNatives.h" -#include <string.h> -#include <stdio.h> +#include <assert.h> #include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> #include <sys/sendfile.h> #include <sys/uio.h> -#include <fcntl.h> -#include <sys/ioctl.h> -typedef struct socket_struct { - int sock; - unsigned short family; -} socket_struct; +// An equivalent of the glibc macro of the same name. +// We want to hide EINTR from Java by simply retrying directly in +// the native code. We care about all other errors, though. +#define EINTR_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) static void convertToPlatform(char *path) { char *pathIndex; pathIndex = path; while (*pathIndex != '\0') { - if(*pathIndex == '\\') { + if (*pathIndex == '\\') { *pathIndex = '/'; } pathIndex++; } } -static int -EsTranslateOpenFlags(int flags) { +static int EsTranslateOpenFlags(int flags) { int realFlags = 0; - if(flags & HyOpenAppend) { + if (flags & HyOpenAppend) { realFlags |= O_APPEND; } - if(flags & HyOpenTruncate) { + if (flags & HyOpenTruncate) { realFlags |= O_TRUNC; } - if(flags & HyOpenCreate) { + if (flags & HyOpenCreate) { realFlags |= O_CREAT; } - if(flags & HyOpenCreateNew) { + if (flags & HyOpenCreateNew) { realFlags |= O_EXCL | O_CREAT; } #ifdef O_SYNC - if(flags & HyOpenSync) { - realFlags |= O_SYNC; - } -#endif - if(flags & HyOpenRead) { - if(flags & HyOpenWrite) { + if (flags & HyOpenSync) { + realFlags |= O_SYNC; + } +#endif + if (flags & HyOpenRead) { + if (flags & HyOpenWrite) { return (O_RDWR | realFlags); } return (O_RDONLY | realFlags); } - if(flags & HyOpenWrite) { + if (flags & HyOpenWrite) { return (O_WRONLY | realFlags); } return -1; } -/** - * Lock the file identified by the given handle. - * The range and lock type are given. - */ -static jint harmony_io_lockImpl(JNIEnv * env, jobject thiz, jint handle, - jlong start, jlong length, jint typeFlag, jboolean waitFlag) { +// Checks whether we can safely treat the given jlong as an off_t without +// accidental loss of precision. +// TODO: this is bogus; we should use _FILE_OFFSET_BITS=64. +static bool offsetTooLarge(JNIEnv* env, jlong longOffset) { + if (sizeof(off_t) >= sizeof(jlong)) { + // We're only concerned about the possibility that off_t is + // smaller than jlong. off_t is signed, so we don't need to + // worry about signed/unsigned. + return false; + } - int rc; - int waitMode = (waitFlag) ? F_SETLKW : F_SETLK; - struct flock lock; + // TODO: use std::numeric_limits<off_t>::max() and min() when we have them. + assert(sizeof(off_t) == sizeof(int)); + static const off_t off_t_max = INT_MAX; + static const off_t off_t_min = INT_MIN; - memset(&lock, 0, sizeof(lock)); - - // If start or length overflow the max values we can represent, then max them out. - if(start > 0x7fffffffL) { - start = 0x7fffffffL; - } - if(length > 0x7fffffffL) { - length = 0x7fffffffL; + if (longOffset > off_t_max || longOffset < off_t_min) { + // "Value too large for defined data type". + jniThrowIOException(env, EOVERFLOW); + return true; } + return false; +} + +static jlong translateLockLength(jlong length) { + // FileChannel.tryLock uses Long.MAX_VALUE to mean "lock the whole + // file", where POSIX would use 0. We can support that special case, + // even for files whose actual length we can't represent. For other + // out of range lengths, though, we want our range checking to fire. + return (length == 0x7fffffffffffffffLL) ? 0 : length; +} + +static struct flock flockFromStartAndLength(jlong start, jlong length) { + struct flock lock; + memset(&lock, 0, sizeof(lock)); lock.l_whence = SEEK_SET; lock.l_start = start; lock.l_len = length; - if((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) { + return lock; +} + +static jint harmony_io_lockImpl(JNIEnv* env, jobject, jint handle, + jlong start, jlong length, jint typeFlag, jboolean waitFlag) { + + length = translateLockLength(length); + if (offsetTooLarge(env, start) || offsetTooLarge(env, length)) { + return -1; + } + + struct flock lock(flockFromStartAndLength(start, length)); + + if ((typeFlag & SHARED_LOCK_TYPE) == SHARED_LOCK_TYPE) { lock.l_type = F_RDLCK; } else { lock.l_type = F_WRLCK; } - do { - rc = fcntl(handle, waitMode, &lock); - } while ((rc < 0) && (errno == EINTR)); - - return (rc == -1) ? -1 : 0; + int waitMode = (waitFlag) ? F_SETLKW : F_SETLK; + return EINTR_RETRY(fcntl(handle, waitMode, &lock)); } -/** - * Unlocks the specified region of the file. - */ -static jint harmony_io_unlockImpl(JNIEnv * env, jobject thiz, jint handle, +static void harmony_io_unlockImpl(JNIEnv* env, jobject, jint handle, jlong start, jlong length) { - int rc; - struct flock lock; - - memset(&lock, 0, sizeof(lock)); - - // If start or length overflow the max values we can represent, then max them out. - if(start > 0x7fffffffL) { - start = 0x7fffffffL; - } - if(length > 0x7fffffffL) { - length = 0x7fffffffL; + length = translateLockLength(length); + if (offsetTooLarge(env, start) || offsetTooLarge(env, length)) { + return; } - lock.l_whence = SEEK_SET; - lock.l_start = start; - lock.l_len = length; + struct flock lock(flockFromStartAndLength(start, length)); lock.l_type = F_UNLCK; - do { - rc = fcntl(handle, F_SETLKW, &lock); - } while ((rc < 0) && (errno == EINTR)); - - return (rc == -1) ? -1 : 0; + int rc = EINTR_RETRY(fcntl(handle, F_SETLKW, &lock)); + if (rc == -1) { + jniThrowIOException(env, errno); + } } /** * Returns the granularity of the starting address for virtual memory allocation. * (It's the same as the page size.) - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: getAllocGranularity - * Signature: ()I */ -static jint harmony_io_getAllocGranularity(JNIEnv * env, jobject thiz) { - static int allocGranularity = 0; - if(allocGranularity == 0) { - allocGranularity = getpagesize(); - } +static jint harmony_io_getAllocGranularity(JNIEnv* env, jobject) { + static int allocGranularity = getpagesize(); return allocGranularity; } -/* - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: readvImpl - * Signature: (I[J[I[I)J - */ -static jlong harmony_io_readvImpl(JNIEnv *env, jobject thiz, jint fd, - jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) { - - jboolean bufsCopied = JNI_FALSE; - jboolean offsetsCopied = JNI_FALSE; - jboolean lengthsCopied = JNI_FALSE; - jint *bufs; - jint *offsets; - jint *lengths; - int i = 0; - long totalRead = 0; - struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec)); - if(vectors == NULL) { +static jlong harmony_io_readv(JNIEnv* env, jobject, jint fd, + jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) { + iovec* vectors = new iovec[size]; + if (vectors == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", "native heap"); return -1; } - bufs = env->GetIntArrayElements(jbuffers, &bufsCopied); - offsets = env->GetIntArrayElements(joffsets, &offsetsCopied); - lengths = env->GetIntArrayElements(jlengths, &lengthsCopied); - while(i < size) { - vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i])); + jint *buffers = env->GetIntArrayElements(jBuffers, NULL); + jint *offsets = env->GetIntArrayElements(jOffsets, NULL); + jint *lengths = env->GetIntArrayElements(jLengths, NULL); + for (int i = 0; i < size; ++i) { + vectors[i].iov_base = (void *)((int)(buffers[i]+offsets[i])); vectors[i].iov_len = lengths[i]; - i++; - } - totalRead = readv(fd, vectors, size); - if(bufsCopied) { - env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT); } - if(offsetsCopied) { - env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT); - } - if(lengthsCopied) { - env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT); + long result = readv(fd, vectors, size); + env->ReleaseIntArrayElements(jBuffers, buffers, JNI_ABORT); + env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT); + env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT); + delete[] vectors; + if (result == -1) { + jniThrowIOException(env, errno); } - free(vectors); - return totalRead; + return result; } -/* - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: writevImpl - * Signature: (I[J[I[I)J - */ -static jlong harmony_io_writevImpl(JNIEnv *env, jobject thiz, jint fd, - jintArray jbuffers, jintArray joffsets, jintArray jlengths, jint size) { - - jboolean bufsCopied = JNI_FALSE; - jboolean offsetsCopied = JNI_FALSE; - jboolean lengthsCopied = JNI_FALSE; - jint *bufs; - jint *offsets; - jint *lengths; - int i = 0; - long totalRead = 0; - struct iovec *vectors = (struct iovec *)malloc(size * sizeof(struct iovec)); - if(vectors == NULL) { +static jlong harmony_io_writev(JNIEnv* env, jobject, jint fd, + jintArray jBuffers, jintArray jOffsets, jintArray jLengths, jint size) { + iovec* vectors = new iovec[size]; + if (vectors == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", "native heap"); return -1; } - bufs = env->GetIntArrayElements(jbuffers, &bufsCopied); - offsets = env->GetIntArrayElements(joffsets, &offsetsCopied); - lengths = env->GetIntArrayElements(jlengths, &lengthsCopied); - while(i < size) { - vectors[i].iov_base = (void *)((int)(bufs[i]+offsets[i])); + jint *buffers = env->GetIntArrayElements(jBuffers, NULL); + jint *offsets = env->GetIntArrayElements(jOffsets, NULL); + jint *lengths = env->GetIntArrayElements(jLengths, NULL); + for (int i = 0; i < size; ++i) { + vectors[i].iov_base = (void *)((int)(buffers[i]+offsets[i])); vectors[i].iov_len = lengths[i]; - i++; } - totalRead = writev(fd, vectors, size); - if(bufsCopied) { - env->ReleaseIntArrayElements(jbuffers, bufs, JNI_ABORT); - } - if(offsetsCopied) { - env->ReleaseIntArrayElements(joffsets, offsets, JNI_ABORT); - } - if(lengthsCopied) { - env->ReleaseIntArrayElements(jlengths, lengths, JNI_ABORT); + long result = writev(fd, vectors, size); + env->ReleaseIntArrayElements(jBuffers, buffers, JNI_ABORT); + env->ReleaseIntArrayElements(jOffsets, offsets, JNI_ABORT); + env->ReleaseIntArrayElements(jLengths, lengths, JNI_ABORT); + delete[] vectors; + if (result == -1) { + jniThrowIOException(env, errno); } - free(vectors); - return totalRead; + return result; } -/* - * Class: org_apache_harmony_luni_platform_OSFileSystem - * Method: transferImpl - * Signature: (IJJ)J - */ -static jlong harmony_io_transferImpl(JNIEnv *env, jobject thiz, jint fd, - jobject sd, jlong offset, jlong count) { - - int socket; - off_t off; +static jlong harmony_io_transfer(JNIEnv* env, jobject, jint fd, jobject sd, + jlong offset, jlong count) { - socket = jniGetFDFromFileDescriptor(env, sd); - if(socket == 0 || socket == -1) { + int socket = jniGetFDFromFileDescriptor(env, sd); + if (socket == -1) { return -1; } /* Value of offset is checked in jint scope (checked in java layer) The conversion here is to guarantee no value lost when converting offset to off_t */ - off = offset; + off_t off = offset; - return sendfile(socket,(int)fd,(off_t *)&off,(size_t)count); + ssize_t rc = sendfile(socket, fd, &off, count); + if (rc == -1) { + jniThrowIOException(env, errno); + } + return rc; } -/* - * Class: org_apache_harmony_io - * Method: readDirectImpl - * Signature: (IJI)J - */ -static jlong harmony_io_readDirectImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_readDirect(JNIEnv* env, jobject, jint fd, jint buf, jint offset, jint nbytes) { - jint result; - if(nbytes == 0) { - return (jlong) 0; + if (nbytes == 0) { + return 0; } - result = read(fd, (void *) ((jint *)(buf+offset)), (int) nbytes); - if(result == 0) { - return (jlong) -1; - } else { - return (jlong) result; + jbyte* dst = reinterpret_cast<jbyte*>(buf + offset); + jlong rc = EINTR_RETRY(read(fd, dst, nbytes)); + if (rc == 0) { + return -1; } + if (rc == -1) { + jniThrowIOException(env, errno); + } + return rc; } -/* - * Class: org_apache_harmony_io - * Method: writeDirectImpl - * Signature: (IJI)J - */ -static jlong harmony_io_writeDirectImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_writeDirect(JNIEnv* env, jobject, jint fd, jint buf, jint offset, jint nbytes) { - - - int rc = 0; - - /* write will just do the right thing for HYPORT_TTY_OUT and HYPORT_TTY_ERR */ - rc = write (fd, (const void *) ((jint *)(buf+offset)), (int) nbytes); - - if(rc == -1) { - jniThrowException(env, "java/io/IOException", strerror(errno)); - return -2; + jbyte* src = reinterpret_cast<jbyte*>(buf + offset); + jlong rc = EINTR_RETRY(write(fd, src, nbytes)); + if (rc == -1) { + jniThrowIOException(env, errno); } - return (jlong) rc; - + return rc; } -// BEGIN android-changed -/* - * Class: org_apache_harmony_io - * Method: readImpl - * Signature: (I[BII)J - */ -static jlong harmony_io_readImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_readImpl(JNIEnv* env, jobject, jint fd, jbyteArray byteArray, jint offset, jint nbytes) { - jboolean isCopy; - jbyte *bytes; - jlong result; - if (nbytes == 0) { return 0; } - bytes = env->GetByteArrayElements(byteArray, &isCopy); - - for (;;) { - result = read(fd, (void *) (bytes + offset), (int) nbytes); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the read() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. Note that this is different - * from EAGAIN, which should result in this code throwing - * an InterruptedIOException. - */ - } - + jbyte* bytes = env->GetByteArrayElements(byteArray, NULL); + jlong rc = EINTR_RETRY(read(fd, bytes + offset, nbytes)); env->ReleaseByteArrayElements(byteArray, bytes, 0); - if (result == 0) { + if (rc == 0) { return -1; } - - if (result == -1) { + if (rc == -1) { if (errno == EAGAIN) { jniThrowException(env, "java/io/InterruptedIOException", "Read timed out"); } else { - jniThrowException(env, "java/io/IOException", strerror(errno)); + jniThrowIOException(env, errno); } } - - return result; + return rc; } -/* - * Class: org_apache_harmony_io - * Method: writeImpl - * Signature: (I[BII)J - */ -static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd, +static jlong harmony_io_writeImpl(JNIEnv* env, jobject, jint fd, jbyteArray byteArray, jint offset, jint nbytes) { - jboolean isCopy; - jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy); - jlong result; - - for (;;) { - result = write(fd, (const char *) bytes + offset, (int) nbytes); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the read() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. Note that this is different - * from EAGAIN, which should result in this code throwing - * an InterruptedIOException. - */ - } - + jbyte* bytes = env->GetByteArrayElements(byteArray, NULL); + jlong result = EINTR_RETRY(write(fd, bytes + offset, nbytes)); env->ReleaseByteArrayElements(byteArray, bytes, JNI_ABORT); if (result == -1) { @@ -416,157 +331,77 @@ static jlong harmony_io_writeImpl(JNIEnv * env, jobject thiz, jint fd, jniThrowException(env, "java/io/InterruptedIOException", "Write timed out"); } else { - jniThrowException(env, "java/io/IOException", strerror(errno)); + jniThrowIOException(env, errno); } } - return result; } -// END android-changed - -/** - * Seeks a file descriptor to a given file position. - * - * @param env pointer to Java environment - * @param thiz pointer to object receiving the message - * @param fd handle of file to be seeked - * @param offset distance of movement in bytes relative to whence arg - * @param whence enum value indicating from where the offset is relative - * The valid values are defined in fsconstants.h. - * @return the new file position from the beginning of the file, in bytes; - * or -1 if a problem occurs. - */ -static jlong harmony_io_seekImpl(JNIEnv * env, jobject thiz, jint fd, - jlong offset, jint whence) { - - int mywhence = 0; +static jlong harmony_io_seek(JNIEnv* env, jobject, jint fd, jlong offset, + jint javaWhence) { /* Convert whence argument */ - switch (whence) { - case 1: - mywhence = 0; - break; - case 2: - mywhence = 1; - break; - case 4: - mywhence = 2; - break; - default: - return -1; + int nativeWhence = 0; + switch (javaWhence) { + case 1: + nativeWhence = SEEK_SET; + break; + case 2: + nativeWhence = SEEK_CUR; + break; + case 4: + nativeWhence = SEEK_END; + break; + default: + return -1; } - - off_t localOffset = (int) offset; - - if((mywhence < 0) || (mywhence > 2)) { + // If the offset is relative, lseek(2) will tell us whether it's too large. + // We're just worried about too large an absolute offset, which would cause + // us to lie to lseek(2). + if (offsetTooLarge(env, offset)) { return -1; } - /* If file offsets are 32 bit, truncate the seek to that range */ - if(sizeof (off_t) < sizeof (jlong)) { - if(offset > 0x7FFFFFFF) { - localOffset = 0x7FFFFFFF; - } else if(offset < -0x7FFFFFFF) { - localOffset = -0x7FFFFFFF; - } + jlong result = lseek(fd, offset, nativeWhence); + if (result == -1) { + jniThrowIOException(env, errno); } - - return (jlong) lseek(fd, localOffset, mywhence); + return result; } -/** - * Flushes a file state to disk. - * - * @param env pointer to Java environment - * @param thiz pointer to object receiving the message - * @param fd handle of file to be flushed - * @param metadata if true also flush metadata, - * otherwise just flush data is possible. - * @return zero on success and -1 on failure - * - * Method: fflushImpl - * Signature: (IZ)I - */ -static jint harmony_io_fflushImpl(JNIEnv * env, jobject thiz, jint fd, +// TODO: are we supposed to support the 'metadata' flag? (false => fdatasync.) +static void harmony_io_fflush(JNIEnv* env, jobject, jint fd, jboolean metadata) { - return (jint) fsync(fd); + int rc = fsync(fd); + if (rc == -1) { + jniThrowIOException(env, errno); + } } -// BEGIN android-changed -/** - * Closes the given file handle - * - * @param env pointer to Java environment - * @param thiz pointer to object receiving the message - * @param fd handle of file to be closed - * @return zero on success and -1 on failure - * - * Class: org_apache_harmony_io - * Method: closeImpl - * Signature: (I)I - */ -static jint harmony_io_closeImpl(JNIEnv * env, jobject thiz, jint fd) { - jint result; - - for (;;) { - result = (jint) close(fd); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the close() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. - */ +static jint harmony_io_close(JNIEnv* env, jobject, jint fd) { + jint rc = EINTR_RETRY(close(fd)); + if (rc == -1) { + jniThrowIOException(env, errno); } - - return result; + return rc; } -// END android-changed - -/* - * Class: org_apache_harmony_io - * Method: truncateImpl - * Signature: (IJ)I - */ -static jint harmony_io_truncateImpl(JNIEnv * env, jobject thiz, jint fd, - jlong size) { - - int rc; - off_t length = (off_t) size; - - // If file offsets are 32 bit, truncate the newLength to that range - if(sizeof (off_t) < sizeof (jlong)) { - if(length > 0x7FFFFFFF) { - length = 0x7FFFFFFF; - } else if(length < -0x7FFFFFFF) { - length = -0x7FFFFFFF; - } +static jint harmony_io_truncate(JNIEnv* env, jobject, jint fd, jlong length) { + if (offsetTooLarge(env, length)) { + return -1; } - rc = ftruncate((int)fd, length); - - return (jint) rc; - + int rc = ftruncate(fd, length); + if (rc == -1) { + jniThrowIOException(env, errno); + } + return rc; } -/* - * Class: org_apache_harmony_io - * Method: openImpl - * Signature: ([BI)I - */ -static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, +static jint harmony_io_openImpl(JNIEnv* env, jobject, jbyteArray path, jint jflags) { - int flags = 0; - int mode = 0; - jint * portFD; - jsize length; - char pathCopy[HyMaxPath]; + int mode = 0; // BEGIN android-changed // don't want default permissions to allow global access. @@ -588,7 +423,7 @@ static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, mode = 0600; break; case 256: - flags = HyOpenWrite | HyOpenCreate | HyOpenAppend; + flags = HyOpenWrite | HyOpenCreate | HyOpenAppend; mode = 0600; break; } @@ -596,116 +431,56 @@ static jint harmony_io_openImpl(JNIEnv * env, jobject obj, jbyteArray path, flags = EsTranslateOpenFlags(flags); - length = env->GetArrayLength (path); + // TODO: clean this up when we clean up the java.io.File equivalent. + jsize length = env->GetArrayLength (path); length = length < HyMaxPath - 1 ? length : HyMaxPath - 1; + char pathCopy[HyMaxPath]; env->GetByteArrayRegion (path, 0, length, (jbyte *)pathCopy); pathCopy[length] = '\0'; convertToPlatform (pathCopy); - int cc; - - if(pathCopy == NULL) { - jniThrowException(env, "java/lang/NullPointerException", NULL); - return -1; - } - - do { - cc = open(pathCopy, flags, mode); - } while(cc < 0 && errno == EINTR); - - if(cc < 0 && errno > 0) { + jint cc = EINTR_RETRY(open(pathCopy, flags, mode)); + // TODO: chase up the callers of this and check they wouldn't rather + // have us throw a meaningful IOException right here. + if (cc < 0 && errno > 0) { cc = -errno; } - return cc; - - } -// BEGIN android-deleted -#if 0 -/* - * Answers the number of remaining chars in the stdin. - * - * Class: org_apache_harmony_io - * Method: ttyAvailableImpl - * Signature: ()J - */ -static jlong harmony_io_ttyAvailableImpl(JNIEnv *env, jobject thiz) { - - int rc; - off_t curr, end; - - int avail = 0; - - // when redirected from a file - curr = lseek(STDIN_FILENO, 0L, 2); /* don't use tell(), it doesn't exist on all platforms, i.e. linux */ - if(curr != -1) { - end = lseek(STDIN_FILENO, 0L, 4); - lseek(STDIN_FILENO, curr, 1); - if(end >= curr) { - return (jlong) (end - curr); - } - } - - /* ioctl doesn't work for files on all platforms (i.e. SOLARIS) */ - - rc = ioctl (STDIN_FILENO, FIONREAD, &avail); - - /* 64 bit platforms use a 32 bit value, using IDATA fails on big endian */ - /* Pass in IDATA because ioctl() is device dependent, some devices may write 64 bits */ - if(rc != -1) { - return (jlong) *(jint *) & avail; - } - return (jlong) 0; -} -#endif -// END android-deleted - -// BEGIN android-added -/* - * Answers the number of remaining bytes in a file descriptor - * using IOCTL. - * - * Class: org_apache_harmony_io - * Method: ioctlAvailable - * Signature: ()I - */ -static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) { - int avail = 0; - int rc = ioctl(fd, FIONREAD, &avail); - +static jint harmony_io_ioctlAvailable(JNIEnv*env, jobject, jint fd) { /* * On underlying platforms Android cares about (read "Linux"), * ioctl(fd, FIONREAD, &avail) is supposed to do the following: - * + * * If the fd refers to a regular file, avail is set to * the difference between the file size and the current cursor. * This may be negative if the cursor is past the end of the file. - * + * * If the fd refers to an open socket or the read end of a * pipe, then avail will be set to a number of bytes that are * available to be read without blocking. - * + * * If the fd refers to a special file/device that has some concept * of buffering, then avail will be set in a corresponding way. - * + * * If the fd refers to a special device that does not have any * concept of buffering, then the ioctl call will return a negative * number, and errno will be set to ENOTTY. - * + * * If the fd refers to a special file masquerading as a regular file, * then avail may be returned as negative, in that the special file * may appear to have zero size and yet a previous read call may have * actually read some amount of data and caused the cursor to be * advanced. */ - + int avail = 0; + int rc = ioctl(fd, FIONREAD, &avail); if (rc >= 0) { /* * Success, but make sure not to return a negative number (see * above). - */ + */ if (avail < 0) { avail = 0; } @@ -714,61 +489,10 @@ static jint harmony_io_ioctlAvailable(JNIEnv *env, jobject thiz, jint fd) { avail = 0; } else { /* Something strange is happening. */ - jniThrowException(env, "java/io/IOException", strerror(errno)); - avail = 0; - } - - return (jint) avail; -} -// END android-added - -/* - * Reads the number of bytes from stdin. - * - * Class: org_apache_harmony_io - * Method: ttyReadImpl - * Signature: ([BII)J - */ -static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz, - jbyteArray byteArray, jint offset, jint nbytes) { - - jboolean isCopy; - jbyte *bytes = env->GetByteArrayElements(byteArray, &isCopy); - jlong result; - - for(;;) { - - result = (jlong) read(STDIN_FILENO, (char *)(bytes + offset), (int) nbytes); - - if ((result != -1) || (errno != EINTR)) { - break; - } - - /* - * If we didn't break above, that means that the read() call - * returned due to EINTR. We shield Java code from this - * possibility by trying again. Note that this is different - * from EAGAIN, which should result in this code throwing - * an InterruptedIOException. - */ - } - - env->ReleaseByteArrayElements(byteArray, bytes, 0); - - if (result == 0) { - return -1; - } - - if (result == -1) { - if (errno == EAGAIN) { - jniThrowException(env, "java/io/InterruptedIOException", - "Read timed out"); - } else { - jniThrowException(env, "java/io/IOException", strerror(errno)); - } + jniThrowIOException(env, errno); } - return result; + return (jint) avail; } /* @@ -776,32 +500,26 @@ static jlong harmony_io_ttyReadImpl(JNIEnv *env, jobject thiz, */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ + { "close", "(I)V", (void*) harmony_io_close }, + { "fflush", "(IZ)V", (void*) harmony_io_fflush }, + { "getAllocGranularity","()I", (void*) harmony_io_getAllocGranularity }, + { "ioctlAvailable", "(I)I", (void*) harmony_io_ioctlAvailable }, { "lockImpl", "(IJJIZ)I", (void*) harmony_io_lockImpl }, - { "getAllocGranularity","()I", (void*) harmony_io_getAllocGranularity }, - { "unlockImpl", "(IJJ)I", (void*) harmony_io_unlockImpl }, - { "fflushImpl", "(IZ)I", (void*) harmony_io_fflushImpl }, - { "seekImpl", "(IJI)J", (void*) harmony_io_seekImpl }, - { "readDirectImpl", "(IIII)J", (void*) harmony_io_readDirectImpl }, - { "writeDirectImpl", "(IIII)J", (void*) harmony_io_writeDirectImpl }, + { "openImpl", "([BI)I", (void*) harmony_io_openImpl }, + { "readDirect", "(IIII)J", (void*) harmony_io_readDirect }, { "readImpl", "(I[BII)J", (void*) harmony_io_readImpl }, + { "readv", "(I[I[I[II)J",(void*) harmony_io_readv }, + { "seek", "(IJI)J", (void*) harmony_io_seek }, + { "transfer", "(ILjava/io/FileDescriptor;JJ)J", + (void*) harmony_io_transfer }, + { "truncate", "(IJ)V", (void*) harmony_io_truncate }, + { "unlockImpl", "(IJJ)V", (void*) harmony_io_unlockImpl }, + { "writeDirect", "(IIII)J", (void*) harmony_io_writeDirect }, { "writeImpl", "(I[BII)J", (void*) harmony_io_writeImpl }, - { "readvImpl", "(I[I[I[II)J",(void*) harmony_io_readvImpl }, - { "writevImpl", "(I[I[I[II)J",(void*) harmony_io_writevImpl }, - { "closeImpl", "(I)I", (void*) harmony_io_closeImpl }, - { "truncateImpl", "(IJ)I", (void*) harmony_io_truncateImpl }, - { "openImpl", "([BI)I", (void*) harmony_io_openImpl }, - { "transferImpl", "(ILjava/io/FileDescriptor;JJ)J", - (void*) harmony_io_transferImpl }, - // BEGIN android-deleted - //{ "ttyAvailableImpl", "()J", (void*) harmony_io_ttyAvailableImpl }, - // END android-deleted - // BEGIN android-added - { "ioctlAvailable", "(I)I", (void*) harmony_io_ioctlAvailable }, - // END android added - { "ttyReadImpl", "([BII)J", (void*) harmony_io_ttyReadImpl } + { "writev", "(I[I[I[II)J",(void*) harmony_io_writev }, }; -int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv *_env) { - return jniRegisterNativeMethods(_env, - "org/apache/harmony/luni/platform/OSFileSystem", gMethods, - NELEM(gMethods)); +int register_org_apache_harmony_luni_platform_OSFileSystem(JNIEnv* _env) { + return jniRegisterNativeMethods(_env, + "org/apache/harmony/luni/platform/OSFileSystem", gMethods, + NELEM(gMethods)); } diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp index 2e814cc..b1493f8 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp @@ -144,9 +144,8 @@ static jbyte harmony_nio_getByteImpl(JNIEnv *_env, jobject _this, */ static void harmony_nio_getBytesImpl(JNIEnv *_env, jobject _this, jint pointer, jbyteArray dst, jint offset, jint length) { - jbyte *dst_ = (jbyte *)_env->GetPrimitiveArrayCritical(dst, (jboolean *)0); - memcpy(dst_ + offset, (jbyte *)pointer, length); - _env->ReleasePrimitiveArrayCritical(dst, dst_, 0); + jbyte* src = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(pointer)); + _env->SetByteArrayRegion(dst, offset, length, src); } /* @@ -166,9 +165,8 @@ static void harmony_nio_putByteImpl(JNIEnv *_env, jobject _this, jint pointer, */ static void harmony_nio_putBytesImpl(JNIEnv *_env, jobject _this, jint pointer, jbyteArray src, jint offset, jint length) { - jbyte *src_ = (jbyte *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); - memcpy((jbyte *)pointer, src_ + offset, length); - _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); + jbyte* dst = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(pointer)); + _env->GetByteArrayRegion(src, offset, length, dst); } static void diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp index 7b6023a..fd3820b 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -178,8 +178,6 @@ struct CachedFields { jclass byte_class; jmethodID byte_class_init; jfieldID byte_class_value; - jclass string_class; - jmethodID string_class_init; jclass socketimpl_class; jfieldID socketimpl_address; jfieldID socketimpl_port; @@ -211,13 +209,6 @@ static void throwSocketException(JNIEnv *env, int errorCode) { } /** - * Throws an IOException with the given message. - */ -static void throwIOExceptionStr(JNIEnv *env, const char *message) { - jniThrowException(env, "java/io/IOException", message); -} - -/** * Throws a NullPointerException. */ static void throwNullPointerException(JNIEnv *env) { @@ -311,8 +302,7 @@ static bool isJavaMappedAddress(jbyte *addressBytes) { /** * Converts a native address structure to an InetAddress object. * Throws a NullPointerException or an IOException in case of - * error. This is signaled by a return value of -1. The normal - * return value is 0. + * error. * * @param sockaddress the sockaddr_storage structure to convert * @@ -377,30 +367,30 @@ static void convertIpv4ToMapped(struct sockaddr_in *sin, * @exception SocketError if the address family is unknown */ static int byteArrayToSocketAddress(JNIEnv *env, - jbyteArray addressByteArray, int port, sockaddr_storage *sockaddress) { - if (addressByteArray == NULL) { - throwNullPointerException(env); - return EFAULT; + jbyteArray addressBytes, int port, sockaddr_storage *sockaddress) { + if (addressBytes == NULL) { + throwNullPointerException(env); + return EFAULT; } - size_t addressLength = env->GetArrayLength(addressByteArray); + size_t addressLength = env->GetArrayLength(addressBytes); // Convert the IP address bytes to the proper IP address type. if (addressLength == 4) { // IPv4 address. - sockaddr_in *sin = (sockaddr_in *) sockaddress; + sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(sockaddress); memset(sin, 0, sizeof(sockaddr_in)); sin->sin_family = AF_INET; sin->sin_port = htons(port); - jbyte *rawBytes = (jbyte *) &sin->sin_addr.s_addr; - env->GetByteArrayRegion(addressByteArray, 0, 4, rawBytes); + jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr); + env->GetByteArrayRegion(addressBytes, 0, 4, dst); } else if (addressLength == 16) { // IPv6 address. - sockaddr_in6 *sin6 = (sockaddr_in6 *) sockaddress; + sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6*>(sockaddress); memset(sin6, 0, sizeof(sockaddr_in6)); sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); - jbyte *rawBytes = (jbyte *) &sin6->sin6_addr.s6_addr; - env->GetByteArrayRegion(addressByteArray, 0, 16, rawBytes); + jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr); + env->GetByteArrayRegion(addressBytes, 0, 16, dst); } else { // Unknown address family. throwSocketException(env, SOCKERR_BADAF); @@ -419,20 +409,23 @@ static int byteArrayToSocketAddress(JNIEnv *env, * @param port the port number * @param sockaddress the sockaddr_storage structure to write to * - * @return 0 on success, -1 on failure + * @return 0 on success, a system error code on failure * @throw UnknownHostException if any error occurs * * @exception SocketError if the address family is unknown */ -static int inetAddressToSocketAddress(JNIEnv *env, - jobject inetaddress, int port, sockaddr_storage *sockaddress) { - +static int inetAddressToSocketAddress(JNIEnv *env, jobject inetaddress, + int port, sockaddr_storage *sockaddress) { // Get the byte array that stores the IP address bytes in the InetAddress. - jbyteArray addressByteArray; - addressByteArray = (jbyteArray)env->GetObjectField(inetaddress, - gCachedFields.iaddr_ipaddress); + if (inetaddress == NULL) { + throwNullPointerException(env); + return EFAULT; + } + jbyteArray addressBytes = + reinterpret_cast<jbyteArray>(env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress)); - return byteArrayToSocketAddress(env, addressByteArray, port, sockaddress); + return byteArrayToSocketAddress(env, addressBytes, port, sockaddress); } /** @@ -652,22 +645,17 @@ jobject newJavaLangInteger(JNIEnv * env, jint anInt) { return env->NewObject(tempClass, tempMethod, anInt); } -/** - * Answer a new java.lang.String object. - * - * @param env pointer to the JNI library - * @param anInt the byte[] constructor argument - * - * @return the new String - */ - -jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { - jclass tempClass; - jmethodID tempMethod; +// Converts a number of milliseconds to a timeval. +static timeval toTimeval(long ms) { + timeval tv; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms - tv.tv_sec*1000) * 1000; + return tv; +} - tempClass = gCachedFields.string_class; - tempMethod = gCachedFields.string_class_init; - return env->NewObject(tempClass, tempMethod, (jbyteArray) bytes); +// Converts a timeval to a number of milliseconds. +static long toMs(const timeval& tv) { + return tv.tv_sec * 1000 + tv.tv_usec / 1000; } /** @@ -684,11 +672,10 @@ jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { */ static int time_msec_clock() { - struct timeval tp; + timeval tp; struct timezone tzp; - gettimeofday(&tp, &tzp); - return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); + return toMs(tp); } /** @@ -1102,7 +1089,6 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { return (unsigned short )(~sum); } - /** * Wrapper for connect() that converts IPv4 addresses to IPv4-mapped IPv6 * addresses if necessary. @@ -1177,9 +1163,8 @@ static int doBind(int socket, struct sockaddr_storage *socketAddress) { * @return 0, if no errors occurred, otherwise the (negative) error code. */ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, - unsigned int timeout, unsigned int step, jbyte *ctxt) { + int timeout, unsigned int step, jbyte *ctxt) { int rc = 0; - struct timeval passedTimeout; int errorVal; socklen_t errorValLen = sizeof(int); struct selectFDSet *context = NULL; @@ -1239,13 +1224,13 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, * set the timeout value to be used. Because on some unix platforms we * don't get notified when a socket is closed we only sleep for 100ms * at a time + * + * TODO: is this relevant for Android? */ - passedTimeout.tv_sec = 0; if (timeout > 100) { - passedTimeout.tv_usec = 100 * 1000; - } else if ((int)timeout >= 0) { - passedTimeout.tv_usec = timeout * 1000; + timeout = 100; } + timeval passedTimeout(toTimeval(timeout)); /* initialize the FD sets for the select */ FD_ZERO(&(context->exceptionSet)); @@ -1259,7 +1244,7 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, &(context->readSet), &(context->writeSet), &(context->exceptionSet), - (int)timeout >= 0 ? &passedTimeout : NULL); + timeout >= 0 ? &passedTimeout : NULL); /* if there is at least one descriptor ready to be checked */ if (0 < rc) { @@ -1613,7 +1598,6 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, {&c->integer_class, "java/lang/Integer"}, {&c->boolean_class, "java/lang/Boolean"}, {&c->byte_class, "java/lang/Byte"}, - {&c->string_class, "java/lang/String"}, {&c->socketimpl_class, "java/net/SocketImpl"}, {&c->dpack_class, "java/net/DatagramPacket"} }; @@ -1635,7 +1619,6 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, {&c->integer_class_init, c->integer_class, "<init>", "(I)V", false}, {&c->boolean_class_init, c->boolean_class, "<init>", "(Z)V", false}, {&c->byte_class_init, c->byte_class, "<init>", "(B)V", false}, - {&c->string_class_init, c->string_class, "<init>", "([B)V", false}, {&c->iaddr_getbyaddress, c->iaddr_class, "getByAddress", "([B)Ljava/net/InetAddress;", true} }; @@ -2684,13 +2667,10 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, int spaceAvailable = env->GetArrayLength(data) - offset; int localCount = count < spaceAvailable? count : spaceAvailable; - jboolean isCopy; - jbyte *body = env->GetByteArrayElements(data, &isCopy); + jbyte* body = env->GetByteArrayElements(data, NULL); // set timeout - struct timeval tv; - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; + timeval tv(toTimeval(timeout)); setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); @@ -2728,8 +2708,7 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, int handle = 0; int result = 0, sent = 0; - jboolean isCopy; - jbyte *message = env->GetByteArrayElements(data, &isCopy); + jbyte *message = env->GetByteArrayElements(data, NULL); // Cap write length to available buf size int spaceAvailable = env->GetArrayLength(data) - offset; @@ -2875,111 +2854,87 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, return sent; } -static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, - jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, - jint countWriteC, jintArray outFlags, jlong timeout) { - // LOGD("ENTER selectImpl"); - - struct timeval timeP; - int result = 0; - int size = 0; - jobject gotFD; - fd_set *fdset_read,*fdset_write; - int handle; - jboolean isCopy ; - jint *flagArray; - int val; - unsigned int time_sec = (unsigned int)timeout/1000; - unsigned int time_msec = (unsigned int)(timeout%1000); - - fdset_read = (fd_set *)malloc(sizeof(fd_set)); - fdset_write = (fd_set *)malloc(sizeof(fd_set)); - - FD_ZERO(fdset_read); - FD_ZERO(fdset_write); - - for (val = 0; val<countReadC; val++) { - - gotFD = env->GetObjectArrayElement(readFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - FD_SET(handle, fdset_read); - - if (0 > (size - handle)) { - size = handle; +static bool initFdSet(JNIEnv* env, jobjectArray fdArray, jint count, fd_set* fdSet, int* maxFd) { + for (int i = 0; i < count; ++i) { + jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i); + if (fileDescriptor == NULL) { + return false; } - } - - for (val = 0; val<countWriteC; val++) { - - gotFD = env->GetObjectArrayElement(writeFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - FD_SET(handle, fdset_write); - - if (0 > (size - handle)) { - size = handle; + + const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd < 0 || fd > 1024) { + LOGE("selectImpl: invalid fd %i", fd); + continue; } - } - - /* the size is the max_fd + 1 */ - size =size + 1; - - if (0 > size) { - result = SOCKERR_FDSET_SIZEBAD; - } else { - /* only set when timeout >= 0 (non-block)*/ - if (0 <= timeout) { - - timeP.tv_sec = time_sec; - timeP.tv_usec = time_msec*1000; - - result = sockSelect(size, fdset_read, fdset_write, NULL, &timeP); - - } else { - result = sockSelect(size, fdset_read, fdset_write, NULL, NULL); + + FD_SET(fd, fdSet); + + if (fd > *maxFd) { + *maxFd = fd; } } + return true; +} - if (0 < result) { - /*output the result to a int array*/ - flagArray = env->GetIntArrayElements(outFlags, &isCopy); - - for (val=0; val<countReadC; val++) { - gotFD = env->GetObjectArrayElement(readFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - if (FD_ISSET(handle,fdset_read)) { - flagArray[val] = SOCKET_OP_READ; - } else { - flagArray[val] = SOCKET_OP_NONE; - } +static bool translateFdSet(JNIEnv* env, jobjectArray fdArray, jint count, const fd_set& fdSet, jint* flagArray, size_t offset, jint op) { + for (int i = 0; i < count; ++i) { + jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i); + if (fileDescriptor == NULL) { + return false; } - - for (val=0; val<countWriteC; val++) { - - gotFD = env->GetObjectArrayElement(writeFDArray,val); - - handle = jniGetFDFromFileDescriptor(env, gotFD); - - if (FD_ISSET(handle,fdset_write)) { - flagArray[val+countReadC] = SOCKET_OP_WRITE; - } else { - flagArray[val+countReadC] = SOCKET_OP_NONE; - } + + const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + const bool valid = fd >= 0 && fd < 1024; + + if (valid && FD_ISSET(fd, &fdSet)) { + flagArray[i + offset] = op; + } else { + flagArray[i + offset] = SOCKET_OP_NONE; } - - env->ReleaseIntArrayElements(outFlags, flagArray, 0); } + return true; +} - free(fdset_write); - free(fdset_read); - - /* return both correct and error result, let java handle the exception*/ - return result; +static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, + jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, + jint countWriteC, jintArray outFlags, jlong timeoutMs) { + // LOGD("ENTER selectImpl"); + + // Initialize the fd_sets. + int maxFd = -1; + fd_set readFds; + fd_set writeFds; + FD_ZERO(&readFds); + FD_ZERO(&writeFds); + bool initialized = initFdSet(env, readFDArray, countReadC, &readFds, &maxFd) && + initFdSet(env, writeFDArray, countWriteC, &writeFds, &maxFd); + if (!initialized) { + return -1; + } + + // Initialize the timeout, if any. + timeval tv; + timeval* tvp = NULL; + if (timeoutMs >= 0) { + tv = toTimeval(timeoutMs); + tvp = &tv; + } + + // Perform the select. + int result = sockSelect(maxFd + 1, &readFds, &writeFds, NULL, tvp); + if (result < 0) { + return result; + } + + // Translate the result into the int[] we're supposed to fill in. + jint* flagArray = env->GetIntArrayElements(outFlags, NULL); + if (flagArray == NULL) { + return -1; + } + bool okay = translateFdSet(env, readFDArray, countReadC, readFds, flagArray, 0, SOCKET_OP_READ) && + translateFdSet(env, writeFDArray, countWriteC, writeFds, flagArray, countReadC, SOCKET_OP_WRITE); + env->ReleaseIntArrayElements(outFlags, flagArray, 0); + return okay ? 0 : -1; } static jobject osNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, @@ -3212,7 +3167,7 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, throwSocketException(env, convertError(errno)); return NULL; } - return newJavaLangInteger(env, timeout.tv_sec * 1000 + timeout.tv_usec/1000); + return newJavaLangInteger(env, toMs(timeout)); } default: { throwSocketException(env, SOCKERR_OPTUNSUPP); @@ -3457,9 +3412,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, } case JAVASOCKOPT_SO_RCVTIMEOUT: { - struct timeval timeout; - timeout.tv_sec = intVal / 1000; - timeout.tv_usec = (intVal % 1000) * 1000; + timeval timeout(toTimeval(intVal)); result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)); if (0 != result) { diff --git a/luni/src/test/java/com/google/coretests/CoreTestRunner.java b/luni/src/test/java/com/google/coretests/CoreTestRunner.java index d469c86..d80fa4e 100644 --- a/luni/src/test/java/com/google/coretests/CoreTestRunner.java +++ b/luni/src/test/java/com/google/coretests/CoreTestRunner.java @@ -15,6 +15,8 @@ */ package com.google.coretests; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -134,10 +136,10 @@ public class CoreTestRunner extends TestRunner { * Prints a help screen on the console. */ private void showHelp() { - System.out.println("Usage: run-core-tests {<param>} <test>"); + System.out.println("Usage: run-core-tests [OPTION]... [TEST]..."); System.out.println(); - System.out.println("Where <test> is a class name, optionally followed"); - System.out.println("by \"#\" and a method name, and <param> is one of"); + System.out.println("Where each TEST is a class name, optionally followed"); + System.out.println("by \"#\" and a method name, and each OPTION is one of"); System.out.println("the following:"); System.out.println(); System.out.println(" --include-all"); @@ -185,26 +187,29 @@ public class CoreTestRunner extends TestRunner { } /** - * Tries to create a Test instance from a given string. The string might + * Tries to create a Test instance from the given strings. The strings might * either specify a class only or a class plus a method name, separated by * a "#". */ - private Test createTest(String testCase) throws Exception { - int p = testCase.indexOf("#"); - if (p != -1) { - String testName = testCase.substring(p + 1); - testCase = testCase.substring(0, p); - - return TestSuite.createTest(Class.forName(testCase), testName); - } else { - return getTest(testCase); + private Test createTest(List<String> testCases) throws Exception { + TestSuite result = new TestSuite(); + for (String testCase : testCases) { + int p = testCase.indexOf("#"); + if (p != -1) { + String testName = testCase.substring(p + 1); + testCase = testCase.substring(0, p); + + result.addTest(TestSuite.createTest(Class.forName(testCase), testName)); + } else { + result.addTest(getTest(testCase)); + } } - + return result; } @Override protected TestResult start(String args[]) throws Exception { - String testName = null; + List<String> testNames = new ArrayList<String>(); // String victimName = null; boolean wait = false; @@ -269,12 +274,12 @@ public class CoreTestRunner extends TestRunner { showHelp(); System.exit(1); } else { - System.err.println("Unknown argument " + args[i] + - ", try --help"); - System.exit(1); + unknownArgument(args[i]); } + } else if (args[i].startsWith("-")) { + unknownArgument(args[i]); } else { - testName = args[i]; + testNames.add(args[i]); } } @@ -288,7 +293,7 @@ public class CoreTestRunner extends TestRunner { System.out.println(); try { - return doRun(createTest(testName), wait); + return doRun(createTest(testNames), wait); } catch(Exception e) { e.printStackTrace(); @@ -296,4 +301,8 @@ public class CoreTestRunner extends TestRunner { } } + private static void unknownArgument(String arg) { + System.err.println("Unknown argument " + arg + ", try --help"); + System.exit(1); + } } diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java index cb35324..283c1db 100644 --- a/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java +++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/PackageTest.java @@ -312,7 +312,7 @@ public class PackageTest extends junit.framework.TestCase { method = "isCompatibleWith", args = {java.lang.String.class} ) - @KnownFailure("isCompatibleWith returns incorrect value.") + @KnownFailure("Dalvik packages are always version '0.0'.") public void test_isCompatibleWithLjava_lang_String() throws Exception { Package p = getTestPackage("hyts_c.jar", "p.C"); diff --git a/luni/src/test/java/tests/api/java/io/FileTest.java b/luni/src/test/java/tests/api/java/io/FileTest.java index 2850826..5c0cb2a 100644 --- a/luni/src/test/java/tests/api/java/io/FileTest.java +++ b/luni/src/test/java/tests/api/java/io/FileTest.java @@ -688,10 +688,7 @@ public class FileTest extends junit.framework.TestCase { method = "delete", args = {} ) - @KnownFailure("Non empty directories are deleted on Android.") public void test_delete() { - // this test passes in the emulator, but it fails on the device - // Test for method boolean java.io.File.delete() try { File dir = new File(System.getProperty("java.io.tmpdir"), platformId diff --git a/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java b/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java index 3b545e3..dc35610 100644 --- a/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java +++ b/luni/src/test/java/tests/api/java/io/RandomAccessFileTest.java @@ -1200,16 +1200,25 @@ public class RandomAccessFileTest extends junit.framework.TestCase { } catch (IOException e) { // Expected. } + // BEGIN android-added + try { + // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work. + raf.seek(((long) Integer.MAX_VALUE) + 1); + fail("Test 2: IOException expected."); + } catch (IOException e) { + // Expected. + } + // END android-added raf.write(testString.getBytes(), 0, testLength); raf.seek(12); - assertEquals("Test 2: Seek failed to set file pointer.", 12, + assertEquals("Test 3: Seek failed to set file pointer.", 12, raf.getFilePointer()); raf.close(); try { raf.seek(1); - fail("Test 1: IOException expected."); + fail("Test 4: IOException expected."); } catch (IOException e) { // Expected. } @@ -1296,10 +1305,21 @@ public class RandomAccessFileTest extends junit.framework.TestCase { assertEquals("Test 7: Incorrect file length;", testLength + 2, raf.length()); + // BEGIN android-added + // Exception testing. + try { + // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work. + raf.setLength(((long) Integer.MAX_VALUE) + 1); + fail("Test 8: IOException expected."); + } catch (IOException e) { + // Expected. + } + // END android-added + // Exception testing. try { raf.setLength(-1); - fail("Test 8: IllegalArgumentException expected."); + fail("Test 9: IllegalArgumentException expected."); } catch (IllegalArgumentException e) { // Expected. } @@ -1307,7 +1327,7 @@ public class RandomAccessFileTest extends junit.framework.TestCase { raf.close(); try { raf.setLength(truncLength); - fail("Test 9: IOException expected."); + fail("Test 10: IOException expected."); } catch (IOException e) { // Expected. } @@ -1501,4 +1521,4 @@ public class RandomAccessFileTest extends junit.framework.TestCase { super.tearDown(); } -}
\ No newline at end of file +} diff --git a/luni/src/test/java/tests/api/java/util/ArrayListTest.java b/luni/src/test/java/tests/api/java/util/ArrayListTest.java index 8aa77cc..0356731 100644 --- a/luni/src/test/java/tests/api/java/util/ArrayListTest.java +++ b/luni/src/test/java/tests/api/java/util/ArrayListTest.java @@ -19,7 +19,7 @@ package tests.api.java.util; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetClass; import java.util.ArrayList; import java.util.Arrays; @@ -33,13 +33,13 @@ import java.util.Vector; import tests.support.Support_ListTest; -@TestTargetClass(ArrayList.class) +@TestTargetClass(ArrayList.class) public class ArrayListTest extends junit.framework.TestCase { List alist; Object[] objArray; - + /** * @tests java.util.ArrayList#ArrayList() */ @@ -72,7 +72,7 @@ public class ArrayListTest extends junit.framework.TestCase { // Test for method java.util.ArrayList(int) ArrayList al = new ArrayList(5); assertEquals("Incorrect arrayList created", 0, al.size()); - + try { new ArrayList(-10); fail("IllegalArgumentException expected"); @@ -130,14 +130,14 @@ public class ArrayListTest extends junit.framework.TestCase { assertNull("Should have returned null", alist.get(25)); assertTrue("Should have returned the old item from slot 25", alist .get(26) == oldItem); - + try { alist.add(-1, null); fail("IndexOutOfBoundsException expected"); } catch (IndexOutOfBoundsException e) { //expected } - + try { alist.add(alist.size() + 1, null); fail("IndexOutOfBoundsException expected"); @@ -198,9 +198,9 @@ public class ArrayListTest extends junit.framework.TestCase { assertTrue("Incorrect size: " + alist.size(), alist.size() == 205); assertNull("Item at slot 100 should be null", alist.get(100)); assertNull("Item at slot 101 should be null", alist.get(101)); - assertEquals("Item at slot 102 should be 'yoink'", + assertEquals("Item at slot 102 should be 'yoink'", "yoink", alist.get(102)); - assertEquals("Item at slot 103 should be 'kazoo'", + assertEquals("Item at slot 103 should be 'kazoo'", "kazoo", alist.get(103)); assertNull("Item at slot 104 should be null", alist.get(104)); alist.addAll(205, listWithNulls); @@ -228,24 +228,29 @@ public class ArrayListTest extends junit.framework.TestCase { } } - /** - * @tests java.util.ArrayList#addAll(int, java.util.Collection) - */ - @TestTargetNew( - level = TestLevel.PARTIAL_COMPLETE, - notes = "Verifies IndexOutOfBoundsException.", - method = "addAll", - args = {int.class, java.util.Collection.class} - ) - public void test_addAllILjava_util_Collection_2() { - // Regression for HARMONY-467 - ArrayList obj = new ArrayList(); - try { - obj.addAll((int) -1, (Collection) null); - fail("IndexOutOfBoundsException expected"); - } catch (IndexOutOfBoundsException e) { - } - } +// BEGIN android-removed +// The spec does not mandate that IndexOutOfBoundsException be thrown in +// preference to NullPointerException when the caller desserves both. +// +// /** +// * @tests java.util.ArrayList#addAll(int, java.util.Collection) +// */ +// @TestTargetNew( +// level = TestLevel.PARTIAL_COMPLETE, +// notes = "Verifies IndexOutOfBoundsException.", +// method = "addAll", +// args = {int.class, java.util.Collection.class} +// ) +// public void test_addAllILjava_util_Collection_2() { +// // Regression for HARMONY-467 +// ArrayList obj = new ArrayList(); +// try { +// obj.addAll((int) -1, (Collection) null); +// fail("IndexOutOfBoundsException expected"); +// } catch (IndexOutOfBoundsException e) { +// } +// } +// END android-removed /** * @tests java.util.ArrayList#addAll(java.util.Collection) @@ -287,17 +292,17 @@ public class ArrayListTest extends junit.framework.TestCase { .get(101) == i.next()); assertTrue("Item at slot 103 is wrong: " + alist.get(102), alist .get(102) == i.next()); - - + + // Regression test for Harmony-3481 ArrayList<Integer> originalList = new ArrayList<Integer>(12); for (int j = 0; j < 12; j++) { originalList.add(j); } - + originalList.remove(0); originalList.remove(0); - + ArrayList<Integer> additionalList = new ArrayList<Integer>(11); for (int j = 0; j < 11; j++) { additionalList.add(j); @@ -672,7 +677,7 @@ public class ArrayListTest extends junit.framework.TestCase { assertTrue("Returned incorrect array: " + i, retArray[i] == objArray[i]); } - + String[] strArray = new String[100]; try { alist.toArray(strArray); @@ -746,16 +751,16 @@ public class ArrayListTest extends junit.framework.TestCase { list.remove(0); assertEquals(1, list.size()); - + ArrayList collection = new ArrayList(); collection.add("1"); collection.add("2"); collection.add("3"); assertEquals(3, collection.size()); - + list.addAll(0, collection); assertEquals(4, list.size()); - + list.remove(0); list.remove(0); assertEquals(2, list.size()); @@ -769,13 +774,13 @@ public class ArrayListTest extends junit.framework.TestCase { collection.add("10"); collection.add("11"); collection.add("12"); - + assertEquals(12, collection.size()); - + list.addAll(0, collection); assertEquals(14, list.size()); } - + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -818,7 +823,7 @@ public class ArrayListTest extends junit.framework.TestCase { mal.add("f"); mal.add("g"); mal.add("h"); - + mal.removeRange(2, 4); String[] result = new String[6]; @@ -827,30 +832,30 @@ public class ArrayListTest extends junit.framework.TestCase { new String[] { "a", "b", "e", "f", "g", "h"})); } - + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. */ protected void setUp() throws Exception { super.setUp(); - + objArray = new Object[100]; for (int i = 0; i < objArray.length; i++) { objArray[i] = new Integer(i); } - + alist = new ArrayList(); for (int i = 0; i < objArray.length; i++) { alist.add(objArray[i]); } } - + @Override protected void tearDown() throws Exception { objArray = null; alist = null; - + super.tearDown(); } } diff --git a/luni/src/test/java/tests/api/java/util/FormatterTest.java b/luni/src/test/java/tests/api/java/util/FormatterTest.java index b2030c9..6f86818 100644 --- a/luni/src/test/java/tests/api/java/util/FormatterTest.java +++ b/luni/src/test/java/tests/api/java/util/FormatterTest.java @@ -800,6 +800,36 @@ public class FormatterTest extends TestCase { } } + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Tests that supplying a Formattable works. See http://code.google.com/p/android/issues/detail?id=1767.", + method = "format", + args = {} + ) + public void test_Formattable() { + Formattable ones = new Formattable() { + public void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException { + try { + formatter.out().append("111"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + Formattable twos = new Formattable() { + public void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException { + try { + formatter.out().append("222"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + + assertEquals("aaa 111?", new Formatter().format("aaa %s?", ones).toString()); + assertEquals("aaa 111 bbb 222?", new Formatter().format("aaa %s bbb %s?", ones, twos).toString()); + } + /** * @tests java.util.Formatter#out() */ diff --git a/luni/src/test/java/tests/api/java/util/TimeZoneTest.java b/luni/src/test/java/tests/api/java/util/TimeZoneTest.java index efdb8a1..75e9ae3 100644 --- a/luni/src/test/java/tests/api/java/util/TimeZoneTest.java +++ b/luni/src/test/java/tests/api/java/util/TimeZoneTest.java @@ -383,6 +383,19 @@ public class TimeZoneTest extends junit.framework.TestCase { @TestTargetNew( level = TestLevel.COMPLETE, notes = "", + method = "useDaylightTime", + args = {} + ) + public void test_useDaylightTime() { + // http://code.google.com/p/android/issues/detail?id=877 + + TimeZone asiaTaipei = TimeZone.getTimeZone("Asia/Taipei"); + assertFalse("Taiwan doesn't use DST", asiaTaipei.useDaylightTime()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", method = "setID", args = {java.lang.String.class} ) diff --git a/math/src/main/java/java/math/BigInt.java b/math/src/main/java/java/math/BigInt.java index cb7990a..581c22f 100644 --- a/math/src/main/java/java/math/BigInt.java +++ b/math/src/main/java/java/math/BigInt.java @@ -225,12 +225,6 @@ class BigInt return a; } - public byte[] bigEndianTwosComplement() { - byte[] a = NativeBN.bn2twosComp(this.bignum, null); - return a; - } - - public int sign() { return NativeBN.sign(this.bignum); } diff --git a/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java b/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java index 91d6d06..a846e70 100644 --- a/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java +++ b/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/FileChannelTest.java @@ -1155,6 +1155,25 @@ public class FileChannelTest extends TestCase { } catch (IllegalArgumentException e) { // expected } + + // BEGIN android-added + // Android uses 32-bit off_t, so anything larger than a signed 32-bit int won't work... + // ...except for the special case of length == Long.MAX_VALUE, which is used to mean "the + // whole file". The special case is tested elsewhere. + long tooBig = ((long) Integer.MAX_VALUE) + 1; + try { + readWriteFileChannel.tryLock(tooBig, 1, false); + fail("should throw IOException"); + } catch (IOException e) { + // expected + } + try { + readWriteFileChannel.tryLock(0, tooBig, false); + fail("should throw IOException"); + } catch (IOException e) { + // expected + } + // END android-added } /** diff --git a/openssl/src/main/java/org/openssl/NativeBN.java b/openssl/src/main/java/org/openssl/NativeBN.java index 44464e0..3597e3c 100644 --- a/openssl/src/main/java/org/openssl/NativeBN.java +++ b/openssl/src/main/java/org/openssl/NativeBN.java @@ -79,9 +79,6 @@ public class NativeBN { public static native int[] bn2litEndInts(int a, int[] to); - public static native byte[] bn2twosComp(int a, byte[] to); - - public static native int sign(int a); // Returns -1, 0, 1 AND NOT boolean. // #define BN_is_negative(a) ((a)->neg != 0) diff --git a/openssl/src/main/native/BNInterface.c b/openssl/src/main/native/BNInterface.c index 4132e4f..1a3eb16 100644 --- a/openssl/src/main/native/BNInterface.c +++ b/openssl/src/main/native/BNInterface.c @@ -33,12 +33,6 @@ static void -throwOutOfMemoryException(JNIEnv* env, const char* message) -{ - jniThrowException(env, "java/lang/OutOfMemoryError", message); -} - -static void throwNewNullPointerException (JNIEnv* env, const char* message) { jniThrowException(env, "java/lang/NullPointerException", message); @@ -456,27 +450,6 @@ static jintArray NativeBN_bn2litEndInts(JNIEnv* env, jclass cls, BIGNUM* a, jint } } -/** - * public static native byte[] bn2twosComp(int, byte[]) - */ -static jbyteArray NativeBN_bn2twosComp(JNIEnv* env, jclass cls, BIGNUM* a, jbyteArray to) { - if (!oneValidHandle(env, a)) return NULL; - jbyteArray returnJBytes = to; - unsigned char * tmpBytes; - int len, byteCnt; - byteCnt = BN_num_bytes(a); -// FIXME: Currently ignoring array passed in to: - returnJBytes = (*env)->NewByteArray(env, byteCnt); -// FIXME: is it neccessary to check for returnJBytes != NULL? - tmpBytes = (unsigned char *)((*env)->GetPrimitiveArrayCritical(env, returnJBytes, NULL)); - if (tmpBytes != NULL) { - len = BN_bn2bin(a, tmpBytes); - (*env)->ReleasePrimitiveArrayCritical(env, returnJBytes, tmpBytes, 0); - return returnJBytes; - } - else return NULL; -} - /** * public static native int sign(int) @@ -833,7 +806,6 @@ static JNINativeMethod METHODS[] = { { "BN_bn2hex", "(I)Ljava/lang/String;", (void*)NativeBN_BN_bn2hex }, { "BN_bn2bin", "(I[B)[B", (void*)NativeBN_BN_bn2bin }, { "bn2litEndInts", "(I[I)[I", (void*)NativeBN_bn2litEndInts }, - { "bn2twosComp", "(I[B)[B", (void*)NativeBN_bn2twosComp }, { "sign", "(I)I", (void*)NativeBN_sign }, { "BN_set_negative", "(II)V", (void*)NativeBN_BN_set_negative }, { "twosCompFitsIntoBytes", "(II)Z", (void*)NativeBN_twosCompFitsIntoBytes }, diff --git a/regex/src/main/java/java/util/regex/MatchResult.java b/regex/src/main/java/java/util/regex/MatchResult.java index fa67ba6..76c17a8 100644 --- a/regex/src/main/java/java/util/regex/MatchResult.java +++ b/regex/src/main/java/java/util/regex/MatchResult.java @@ -1,17 +1,18 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 * - * 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 * - * 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. + * 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. */ package java.util.regex; @@ -22,91 +23,75 @@ package java.util.regex; * pair of parentheses in the regular expression and an additional group for * the whole regular expression. The start, end, and contents of each group * can be queried. - * + * * @see Matcher * @see Matcher#toMatchResult() - * - * @since Android 1.0 */ public interface MatchResult { /** * Returns the index of the first character following the text that matched - * the whole regular expression. - * + * the whole regular expression. + * * @return the character index. - * - * @since Android 1.0 */ int end(); /** * Returns the index of the first character following the text that matched * a given group. - * + * * @param group * the group, ranging from 0 to groupCount() - 1, with 0 * representing the whole pattern. - * + * * @return the character index. - * - * @since Android 1.0 */ int end(int group); /** - * Returns the text that matched the whole regular expression. - * + * Returns the text that matched the whole regular expression. + * * @return the text. - * - * @since Android 1.0 */ String group(); /** * Returns the text that matched a given group of the regular expression. - * + * * @param group * the group, ranging from 0 to groupCount() - 1, with 0 * representing the whole pattern. - * + * * @return the text that matched the group. - * - * @since Android 1.0 */ String group(int group); /** * Returns the number of groups in the result, which is always equal to * the number of groups in the original regular expression. - * + * * @return the number of groups. - * - * @since Android 1.0 */ int groupCount(); /** * Returns the index of the first character of the text that matched - * the whole regular expression. - * + * the whole regular expression. + * * @return the character index. - * - * @since Android 1.0 */ int start(); /** * Returns the index of the first character of the text that matched a given * group. - * + * * @param group * the group, ranging from 0 to groupCount() - 1, with 0 * representing the whole pattern. - * + * * @return the character index. - * - * @since Android 1.0 */ int start(int group); } diff --git a/regex/src/main/java/java/util/regex/Matcher.java b/regex/src/main/java/java/util/regex/Matcher.java index e3e4874..be5c782 100644 --- a/regex/src/main/java/java/util/regex/Matcher.java +++ b/regex/src/main/java/java/util/regex/Matcher.java @@ -44,8 +44,6 @@ import com.ibm.icu4jni.regex.NativeRegEx; * {@code Pattern} was successful and at which position the next attempt would * resume the search. Depending on the application's needs, it may become * necessary to explicitly {@link #reset()} this state from time to time. - * - * @since Android 1.0 */ public final class Matcher implements MatchResult { @@ -128,39 +126,99 @@ public final class Matcher implements MatchResult { } /** + * Appends a literal part of the input plus a replacement for the current + * match to a given {@link StringBuffer}. The literal part is exactly the + * part of the input between the previous match and the current match. The + * method can be used in conjunction with {@link #find()} and + * {@link #appendTail(StringBuffer)} to walk through the input and replace + * all occurrences of the {@code Pattern} with something else. + * + * @param buffer + * the {@code StringBuffer} to append to. + * @param replacement + * the replacement text. + * @return the {@code Matcher} itself. + * @throws IllegalStateException + * if no successful match has been made. + */ + public Matcher appendReplacement(StringBuffer buffer, String replacement) { + buffer.append(input.substring(appendPos, start())); + appendEvaluated(buffer, replacement); + appendPos = end(); + + return this; + } + + /** + * Internal helper method to append a given string to a given string buffer. + * If the string contains any references to groups, these are replaced by + * the corresponding group's contents. + * + * @param buffer + * the string buffer. + * @param s + * the string to append. + */ + private void appendEvaluated(StringBuffer buffer, String s) { + boolean escape = false; + boolean dollar = false; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\' && !escape) { + escape = true; + } else if (c == '$' && !escape) { + dollar = true; + } else if (c >= '0' && c <= '9' && dollar) { + buffer.append(group(c - '0')); + dollar = false; + } else { + buffer.append(c); + dollar = false; + escape = false; + } + } + + // This seemingly stupid piece of code reproduces a JDK bug. + if (escape) { + throw new ArrayIndexOutOfBoundsException(s.length()); + } + } + + /** * Resets the Matcher. A new input sequence and a new region can be * specified. Results of a previous find get lost. The next attempt to find * an occurrence of the Pattern in the string will start at the beginning of * the region. This is the internal version of reset() to which the several * public versions delegate. - * + * * @param input * the input sequence. * @param start * the start of the region. * @param end * the end of the region. - * + * * @return the matcher itself. */ private Matcher reset(CharSequence input, int start, int end) { if (input == null) { throw new IllegalArgumentException(); } - - if (start < 0 || end < 0 || start > input.length() || + + if (start < 0 || end < 0 || start > input.length() || end > input.length() || start > end) { throw new IllegalArgumentException(); } // Maybe should have a reset() here, but it makes thing worse... // NativeRegEx.reset(nativePattern, 0); - + if (!input.equals(this.input)) { this.input = input.toString(); - + NativeRegEx.setText(nativePattern, this.input); - + regionStart = 0; regionEnd = input.length(); } @@ -176,22 +234,8 @@ public final class Matcher implements MatchResult { matchFound = false; findPos = regionStart; appendPos = 0; - - return this; - } - /** - * Resets the {@code Matcher}. This results in the region being set to the - * whole input. Results of a previous find get lost. The next attempt to - * find an occurrence of the {@link Pattern} in the string will start at the - * beginning of the input. - * - * @return the {@code Matcher} itself. - * - * @since Android 1.0 - */ - public Matcher reset() { - return reset(input, 0, input.length()); + return this; } /** @@ -204,73 +248,24 @@ public final class Matcher implements MatchResult { * the new input sequence. * * @return the {@code Matcher} itself. - * - * @since Android 1.0 */ public Matcher reset(CharSequence input) { return reset(input, 0, input.length()); } /** - * Sets a new pattern for the {@code Matcher}. Results of a previous find - * get lost. The next attempt to find an occurrence of the {@link Pattern} - * in the string will start at the beginning of the input. - * - * @param pattern - * the new {@code Pattern}. - * + * Resets the {@code Matcher}. This results in the region being set to the + * whole input. Results of a previous find get lost. The next attempt to + * find an occurrence of the {@link Pattern} in the string will start at the + * beginning of the input. + * * @return the {@code Matcher} itself. - * - * @since Android 1.0 */ - public Matcher usePattern(Pattern pattern) { - if (pattern == null) { - throw new IllegalArgumentException(); - } - - this.pattern = pattern; - - if (nativePattern != 0) { - NativeRegEx.close(nativePattern); - } - nativePattern = NativeRegEx.clone(pattern.mNativePattern); - - if (input != null) { - NativeRegEx.setText(nativePattern, input); - NativeRegEx.setRegion(nativePattern, regionStart, regionEnd); - NativeRegEx.useAnchoringBounds(nativePattern, anchoringBounds); - NativeRegEx.useTransparentBounds(nativePattern, transparentBounds); - } - - matchOffsets = new int[(this.pattern.mGroupCount + 1) * 2]; - matchFound = false; - return this; - } - - /** - * Returns the {@link Pattern} instance used inside this matcher. - * - * @return the {@code Pattern} instance. - * - * @since Android 1.0 - */ - public Pattern pattern() { - return pattern; + public Matcher reset() { + return reset(input, 0, input.length()); } /** - * Returns the number of groups in the results, which is always equal to - * the number of groups in the original regular expression. - * - * @return the number of groups. - * - * @since Android 1.0 - */ - public int groupCount() { - return pattern.mGroupCount; - } - - /** * Resets this matcher and sets a region. Only characters inside the region * are considered for a match. * @@ -279,109 +274,150 @@ public final class Matcher implements MatchResult { * @param end * the first character after the end of the region. * @return the {@code Matcher} itself. - * @since Android 1.0 */ public Matcher region(int start, int end) { return reset(input, start, end); } + /** - * Returns this matcher's region start, that is, the first character that is - * considered for a match. - * - * @return the start of the region. - * @since Android 1.0 + * Appends the (unmatched) remainder of the input to the given + * {@link StringBuffer}. The method can be used in conjunction with + * {@link #find()} and {@link #appendReplacement(StringBuffer, String)} to + * walk through the input and replace all matches of the {@code Pattern} + * with something else. + * + * @param buffer + * the {@code StringBuffer} to append to. + * @return the {@code StringBuffer}. + * @throws IllegalStateException + * if no successful match has been made. */ - public int regionStart() { - return regionStart; + public StringBuffer appendTail(StringBuffer buffer) { + if (appendPos < regionEnd) { + buffer.append(input.substring(appendPos, regionEnd)); + } + + return buffer; } /** - * Returns this matcher's region end, that is, the first character that is - * not considered for a match. - * - * @return the end of the region. - * @since Android 1.0 + * Replaces the first occurrence of this matcher's pattern in the input with + * a given string. + * + * @param replacement + * the replacement text. + * @return the modified input string. */ - public int regionEnd() { - return regionEnd; + public String replaceFirst(String replacement) { + StringBuffer buffer = new StringBuffer(input.length()); + + findPos = 0; + appendPos = 0; + matchFound = false; + searching = false; + + if (find()) { + appendReplacement(buffer, replacement); + } + + return appendTail(buffer).toString(); } /** - * Determines whether this matcher has anchoring bounds enabled or not. When - * anchoring bounds are enabled, the start and end of the input match the - * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled - * by default. - * - * @param value - * the new value for anchoring bounds. - * @return the {@code Matcher} itself. - * @since Android 1.0 + * Replaces all occurrences of this matcher's pattern in the input with a + * given string. + * + * @param replacement + * the replacement text. + * @return the modified input string. */ - public Matcher useAnchoringBounds(boolean value) { - anchoringBounds = value; - NativeRegEx.useAnchoringBounds(nativePattern, value); - return this; + public String replaceAll(String replacement) { + StringBuffer buffer = new StringBuffer(input.length()); + + findPos = 0; + appendPos = 0; + matchFound = false; + searching = false; + + while (find()) { + appendReplacement(buffer, replacement); + } + + return appendTail(buffer).toString(); } - + /** - * Indicates whether this matcher has anchoring bounds enabled. When - * anchoring bounds are enabled, the start and end of the input match the - * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled - * by default. - * - * @return true if (and only if) the {@code Matcher} uses anchoring bounds. - * @since Android 1.0 + * Returns the {@link Pattern} instance used inside this matcher. + * + * @return the {@code Pattern} instance. */ - public boolean hasAnchoringBounds() { - return anchoringBounds; + public Pattern pattern() { + return pattern; } /** - * Determines whether this matcher has transparent bounds enabled or not. - * When transparent bounds are enabled, the parts of the input outside the - * region are subject to lookahead and lookbehind, otherwise they are not. - * Transparent bounds are disabled by default. - * - * @param value - * the new value for transparent bounds. - * @return the {@code Matcher} itself. - * @since Android 1.0 + * Returns the text that matched a given group of the regular expression. + * + * @param group + * the group, ranging from 0 to groupCount() - 1, with 0 + * representing the whole pattern. + * @return the text that matched the group. + * @throws IllegalStateException + * if no successful match has been made. */ - public Matcher useTransparentBounds(boolean value) { - transparentBounds = value; - NativeRegEx.useTransparentBounds(nativePattern, value); - return this; + public String group(int group) { + ensureMatch(); + int from = matchOffsets[group * 2]; + int to = matchOffsets[(group * 2) + 1]; + if (from == -1 || to == -1) { + return null; + } else { + return input.substring(from, to); + } } - + /** - * Indicates whether this matcher has transparent bounds enabled. When - * transparent bounds are enabled, the parts of the input outside the region - * are subject to lookahead and lookbehind, otherwise they are not. - * Transparent bounds are disabled by default. - * - * @return true if (and only if) the {@code Matcher} uses anchoring bounds. - * @since Android 1.0 + * Returns the text that matched the whole regular expression. + * + * @return the text. + * @throws IllegalStateException + * if no successful match has been made. */ - public boolean hasTransparentBounds() { - return transparentBounds; + public String group() { + return group(0); } - + /** - * Makes sure that a successful match has been made. Is invoked internally - * from various places in the class. - * - * @throws IllegalStateException - * if no successful match has been made. - * - * @since Android 1.0 + * Returns the next occurrence of the {@link Pattern} in the input. The + * method starts the search from the given character in the input. + * + * @param start + * The index in the input at which the find operation is to + * begin. If this is less than the start of the region, it is + * automatically adjusted to that value. If it is beyond the end + * of the region, the method will fail. + * @return true if (and only if) a match has been found. */ - private void ensureMatch() throws IllegalStateException { - if (!matchFound) { - throw new IllegalStateException("No successful match so far"); + public boolean find(int start) { + findPos = start; + + if (findPos < regionStart) { + findPos = regionStart; + } else if (findPos >= regionEnd) { + matchFound = false; + return false; } + + matchFound = NativeRegEx.find(nativePattern, findPos); + if (matchFound) { + NativeRegEx.startEnd(nativePattern, matchOffsets); + findPos = matchOffsets[1]; + } + + return matchFound; } - + /** * Returns the next occurrence of the {@link Pattern} in the input. If a * previous match was successful, the method continues the search from the @@ -389,7 +425,6 @@ public final class Matcher implements MatchResult { * either from the region start (if one has been set), or from position 0. * * @return true if (and only if) a match has been found. - * @since Android 1.0 */ public boolean find() { if (!searching) { @@ -408,34 +443,35 @@ public final class Matcher implements MatchResult { } /** - * Returns the next occurrence of the {@link Pattern} in the input. The - * method starts the search from the given character in the input. - * - * @param start - * The index in the input at which the find operation is to - * begin. If this is less than the start of the region, it is - * automatically adjusted to that value. If it is beyond the end - * of the region, the method will fail. - * @return true if (and only if) a match has been found. - * @since Android 1.0 + * Returns the index of the first character of the text that matched a given + * group. + * + * @param group + * the group, ranging from 0 to groupCount() - 1, with 0 + * representing the whole pattern. + * @return the character index. + * @throws IllegalStateException + * if no successful match has been made. */ - public boolean find(int start) { - findPos = start; - - if (findPos < regionStart) { - findPos = regionStart; - } else if (findPos >= regionEnd) { - matchFound = false; - return false; - } - - matchFound = NativeRegEx.find(nativePattern, findPos); - if (matchFound) { - NativeRegEx.startEnd(nativePattern, matchOffsets); - findPos = matchOffsets[1]; - } - - return matchFound; + public int start(int group) throws IllegalStateException { + ensureMatch(); + return matchOffsets[group * 2]; + } + + /** + * Returns the index of the first character following the text that matched + * a given group. + * + * @param group + * the group, ranging from 0 to groupCount() - 1, with 0 + * representing the whole pattern. + * @return the character index. + * @throws IllegalStateException + * if no successful match has been made. + */ + public int end(int group) { + ensureMatch(); + return matchOffsets[(group * 2) + 1]; } /** @@ -444,8 +480,6 @@ public final class Matcher implements MatchResult { * * @return true if (and only if) the {@code Pattern} matches the entire * region. - * - * @since Android 1.0 */ public boolean matches() { matchFound = NativeRegEx.matches(nativePattern, -1); @@ -458,13 +492,34 @@ public final class Matcher implements MatchResult { } /** + * Returns a replacement string for the given one that has all backslashes + * and dollar signs escaped. + * + * @param s + * the input string. + * @return the input string, with all backslashes and dollar signs having + * been escaped. + */ + public static String quoteReplacement(String s) { + StringBuffer buffer = new StringBuffer(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\' || c == '$') { + buffer.append('\\'); + } + buffer.append(c); + } + + return buffer.toString(); + } + + /** * Tries to match the {@link Pattern}, starting from the beginning of the * region (or the beginning of the input, if no region has been set). * Doesn't require the {@code Pattern} to match against the whole region. * * @return true if (and only if) the {@code Pattern} matches. - * - * @since Android 1.0 */ public boolean lookingAt() { matchFound = NativeRegEx.lookingAt(nativePattern, -1); @@ -483,27 +538,19 @@ public final class Matcher implements MatchResult { * @return the character index. * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ - public int start() throws IllegalStateException { + public int start() { return start(0); } /** - * Returns the index of the first character of the text that matched a given - * group. - * - * @param group - * the group, ranging from 0 to groupCount() - 1, with 0 - * representing the whole pattern. - * @return the character index. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Returns the number of groups in the results, which is always equal to + * the number of groups in the original regular expression. + * + * @return the number of groups. */ - public int start(int group) throws IllegalStateException { - ensureMatch(); - return matchOffsets[group * 2]; + public int groupCount() { + return pattern.mGroupCount; } /** @@ -513,255 +560,166 @@ public final class Matcher implements MatchResult { * @return the character index. * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ public int end() { return end(0); } /** - * Returns the index of the first character following the text that matched - * a given group. - * - * @param group - * the group, ranging from 0 to groupCount() - 1, with 0 - * representing the whole pattern. - * @return the character index. + * Converts the current match into a separate {@link MatchResult} instance + * that is independent from this matcher. The new object is unaffected when + * the state of this matcher changes. + * + * @return the new {@code MatchResult}. * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ - public int end(int group) { + public MatchResult toMatchResult() { ensureMatch(); - return matchOffsets[(group * 2) + 1]; + return new MatchResultImpl(input, matchOffsets); } /** - * Returns the text that matched the whole regular expression. - * - * @return the text. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Determines whether this matcher has anchoring bounds enabled or not. When + * anchoring bounds are enabled, the start and end of the input match the + * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled + * by default. + * + * @param value + * the new value for anchoring bounds. + * @return the {@code Matcher} itself. */ - public String group() { - return group(0); + public Matcher useAnchoringBounds(boolean value) { + anchoringBounds = value; + NativeRegEx.useAnchoringBounds(nativePattern, value); + return this; } /** - * Returns the text that matched a given group of the regular expression. - * - * @param group - * the group, ranging from 0 to groupCount() - 1, with 0 - * representing the whole pattern. - * @return the text that matched the group. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Indicates whether this matcher has anchoring bounds enabled. When + * anchoring bounds are enabled, the start and end of the input match the + * '^' and '$' meta-characters, otherwise not. Anchoring bounds are enabled + * by default. + * + * @return true if (and only if) the {@code Matcher} uses anchoring bounds. */ - public String group(int group) { - ensureMatch(); - int from = matchOffsets[group * 2]; - int to = matchOffsets[(group * 2) + 1]; - if (from == -1 || to == -1) { - return null; - } else { - return input.substring(from, to); - } + public boolean hasAnchoringBounds() { + return anchoringBounds; } /** - * Indicates whether the last match hit the end of the input. - * - * @return true if (and only if) the last match hit the end of the input. - * @since Android 1.0 - */ - public boolean hitEnd() { - return NativeRegEx.hitEnd(nativePattern); - } - - /** - * Indicates whether more input might change a successful match into an - * unsuccessful one. - * - * @return true if (and only if) more input might change a successful match - * into an unsuccessful one. - * @since Android 1.0 + * Determines whether this matcher has transparent bounds enabled or not. + * When transparent bounds are enabled, the parts of the input outside the + * region are subject to lookahead and lookbehind, otherwise they are not. + * Transparent bounds are disabled by default. + * + * @param value + * the new value for transparent bounds. + * @return the {@code Matcher} itself. */ - public boolean requireEnd() { - return NativeRegEx.requireEnd(nativePattern); + public Matcher useTransparentBounds(boolean value) { + transparentBounds = value; + NativeRegEx.useTransparentBounds(nativePattern, value); + return this; } - + /** - * Converts the current match into a separate {@link MatchResult} instance - * that is independent from this matcher. The new object is unaffected when - * the state of this matcher changes. - * - * @return the new {@code MatchResult}. + * Makes sure that a successful match has been made. Is invoked internally + * from various places in the class. + * * @throws IllegalStateException * if no successful match has been made. - * @since Android 1.0 */ - public MatchResult toMatchResult() { - ensureMatch(); - return new MatchResultImpl(input, matchOffsets); + private void ensureMatch() { + if (!matchFound) { + throw new IllegalStateException("No successful match so far"); + } } /** - * Appends a literal part of the input plus a replacement for the current - * match to a given {@link StringBuffer}. The literal part is exactly the - * part of the input between the previous match and the current match. The - * method can be used in conjunction with {@link #find()} and - * {@link #appendTail(StringBuffer)} to walk through the input and replace - * all occurrences of the {@code Pattern} with something else. - * - * @param buffer - * the {@code StringBuffer} to append to. - * @param replacement - * the replacement text. - * @return the {@code Matcher} itself. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Indicates whether this matcher has transparent bounds enabled. When + * transparent bounds are enabled, the parts of the input outside the region + * are subject to lookahead and lookbehind, otherwise they are not. + * Transparent bounds are disabled by default. + * + * @return true if (and only if) the {@code Matcher} uses anchoring bounds. */ - public Matcher appendReplacement(StringBuffer buffer, String replacement) - throws IllegalStateException { - - buffer.append(input.substring(appendPos, start())); - appendEvaluated(buffer, replacement); - appendPos = end(); - - return this; + public boolean hasTransparentBounds() { + return transparentBounds; } /** - * Appends the (unmatched) remainder of the input to the given - * {@link StringBuffer}. The method can be used in conjunction with - * {@link #find()} and {@link #appendReplacement(StringBuffer, String)} to - * walk through the input and replace all matches of the {@code Pattern} - * with something else. - * - * @param buffer - * the {@code StringBuffer} to append to. - * @return the {@code StringBuffer}. - * @throws IllegalStateException - * if no successful match has been made. - * @since Android 1.0 + * Returns this matcher's region start, that is, the first character that is + * considered for a match. + * + * @return the start of the region. */ - public StringBuffer appendTail(StringBuffer buffer) { - if (appendPos < regionEnd) { - buffer.append(input.substring(appendPos, regionEnd)); - } - - return buffer; + public int regionStart() { + return regionStart; } /** - * Internal helper method to append a given string to a given string buffer. - * If the string contains any references to groups, these are replaced by - * the corresponding group's contents. - * - * @param buffer - * the string buffer. - * @param s - * the string to append. + * Returns this matcher's region end, that is, the first character that is + * not considered for a match. + * + * @return the end of the region. */ - private void appendEvaluated(StringBuffer buffer, String s) { - boolean escape = false; - boolean dollar = false; - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\\' && !escape) { - escape = true; - } else if (c == '$' && !escape) { - dollar = true; - } else if (c >= '0' && c <= '9' && dollar) { - buffer.append(group(c - '0')); - dollar = false; - } else { - buffer.append(c); - dollar = false; - escape = false; - } - } - - // This seemingly stupid piece of code reproduces a JDK bug. - if (escape) { - throw new ArrayIndexOutOfBoundsException(s.length()); - } + public int regionEnd() { + return regionEnd; } - + /** - * Replaces all occurrences of this matcher's pattern in the input with a - * given string. + * Indicates whether more input might change a successful match into an + * unsuccessful one. * - * @param replacement - * the replacement text. - * @return the modified input string. - * @since Android 1.0 + * @return true if (and only if) more input might change a successful match + * into an unsuccessful one. */ - public String replaceAll(String replacement) { - StringBuffer buffer = new StringBuffer(input.length()); - - findPos = 0; - appendPos = 0; - matchFound = false; - searching = false; - - while (find()) { - appendReplacement(buffer, replacement); - } - - return appendTail(buffer).toString(); + public boolean requireEnd() { + return NativeRegEx.requireEnd(nativePattern); } /** - * Replaces the first occurrence of this matcher's pattern in the input with - * a given string. - * - * @param replacement - * the replacement text. - * @return the modified input string. - * @since Android 1.0 + * Indicates whether the last match hit the end of the input. + * + * @return true if (and only if) the last match hit the end of the input. */ - public String replaceFirst(String replacement) { - StringBuffer buffer = new StringBuffer(input.length()); - - findPos = 0; - appendPos = 0; - matchFound = false; - searching = false; - - if (find()) { - appendReplacement(buffer, replacement); - } - - return appendTail(buffer).toString(); + public boolean hitEnd() { + return NativeRegEx.hitEnd(nativePattern); } /** - * Returns a replacement string for the given one that has all backslashes - * and dollar signs escaped. - * - * @param s - * the input string. - * @return the input string, with all backslashes and dollar signs having - * been escaped. - * @since Android 1.0 + * Sets a new pattern for the {@code Matcher}. Results of a previous find + * get lost. The next attempt to find an occurrence of the {@link Pattern} + * in the string will start at the beginning of the input. + * + * @param pattern + * the new {@code Pattern}. + * + * @return the {@code Matcher} itself. */ - public static String quoteReplacement(String s) { - StringBuffer buffer = new StringBuffer(s.length()); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c == '\\' || c == '$') { - buffer.append('\\'); - } - buffer.append(c); + public Matcher usePattern(Pattern pattern) { + if (pattern == null) { + throw new IllegalArgumentException(); } - - return buffer.toString(); + + this.pattern = pattern; + + if (nativePattern != 0) { + NativeRegEx.close(nativePattern); + } + nativePattern = NativeRegEx.clone(pattern.mNativePattern); + + if (input != null) { + NativeRegEx.setText(nativePattern, input); + NativeRegEx.setRegion(nativePattern, regionStart, regionEnd); + NativeRegEx.useAnchoringBounds(nativePattern, anchoringBounds); + NativeRegEx.useTransparentBounds(nativePattern, transparentBounds); + } + + matchOffsets = new int[(this.pattern.mGroupCount + 1) * 2]; + matchFound = false; + return this; } @Override diff --git a/regex/src/main/java/java/util/regex/Pattern.java b/regex/src/main/java/java/util/regex/Pattern.java index 2c71de1..db3bc21 100644 --- a/regex/src/main/java/java/util/regex/Pattern.java +++ b/regex/src/main/java/java/util/regex/Pattern.java @@ -47,7 +47,7 @@ import com.ibm.icu4jni.regex.NativeRegEx; * boolean b2 = Pattern.matches("Hello, A[a-z]*!", "Hello, Robot!"); // false * </pre> * <p/> - * Please consult the <a href="package-summary.html">package documentation</a> for an + * Please consult the <a href="package.html">package documentation</a> for an * overview of the regular expression syntax used in this class as well as * Android-specific implementation details. * @@ -61,8 +61,6 @@ public final class Pattern implements Serializable { /** * This constant specifies that a pattern matches Unix line endings ('\n') * only against the '.', '^', and '$' meta characters. - * - * @since Android 1.0 */ public static final int UNIX_LINES = 0x01; @@ -76,8 +74,6 @@ public final class Pattern implements Serializable { * constant. So if case insensitivity is enabled, this automatically extends * to all Unicode characters. The {@code UNICODE_CASE} constant itself has * no special consequences. - * - * @since Android 1.0 */ public static final int CASE_INSENSITIVE = 0x02; @@ -85,8 +81,6 @@ public final class Pattern implements Serializable { * This constant specifies that a {@code Pattern} may contain whitespace or * comments. Otherwise comments and whitespace are taken as literal * characters. - * - * @since Android 1.0 */ public static final int COMMENTS = 0x04; @@ -94,24 +88,18 @@ public final class Pattern implements Serializable { * This constant specifies that the meta characters '^' and '$' match only * the beginning and end end of an input line, respectively. Normally, they * match the beginning and the end of the complete input. - * - * @since Android 1.0 */ public static final int MULTILINE = 0x08; /** * This constant specifies that the whole {@code Pattern} is to be taken * literally, that is, all meta characters lose their meanings. - * - * @since Android 1.0 */ public static final int LITERAL = 0x10; /** * This constant specifies that the '.' meta character matches arbitrary * characters, including line endings, which is normally not the case. - * - * @since Android 1.0 */ public static final int DOTALL = 0x20; @@ -126,8 +114,6 @@ public final class Pattern implements Serializable { * constant. So if case insensitivity is enabled, this automatically extends * to all Unicode characters. The {@code UNICODE_CASE} constant then has no * special consequences. - * - * @since Android 1.0 */ public static final int UNICODE_CASE = 0x40; @@ -135,8 +121,6 @@ public final class Pattern implements Serializable { * This constant specifies that a character in a {@code Pattern} and a * character in the input string only match if they are canonically * equivalent. It is (currently) not supported in Android. - * - * @since Android 1.0 */ public static final int CANON_EQ = 0x80; @@ -159,24 +143,144 @@ public final class Pattern implements Serializable { * Holds the number of groups in the pattern. */ transient int mGroupCount; - + + /** - * Compiles a regular expression, creating a new Pattern instance in the - * process. This is actually a convenience method that calls {@link - * #compile(String, int)} with a {@code flags} value of zero. - * - * @param pattern - * the regular expression. - * - * @return the new {@code Pattern} instance. - * - * @throws PatternSyntaxException - * if the regular expression is syntactically incorrect. - * - * @since Android 1.0 + * Returns a {@link Matcher} for the {@code Pattern} and a given input. The + * {@code Matcher} can be used to match the {@code Pattern} against the + * whole input, find occurrences of the {@code Pattern} in the input, or + * replace parts of the input. + * + * @param input + * the input to process. + * + * @return the resulting {@code Matcher}. */ - public static Pattern compile(String pattern) throws PatternSyntaxException { - return new Pattern(pattern, 0); + public Matcher matcher(CharSequence input) { + return new Matcher(this, input); + } + + /** + * Splits the given input sequence at occurrences of this {@code Pattern}. + * + * <p>If this {@code Pattern} does not occur in the input, the result is an + * array containing the input (converted from a {@code CharSequence} to + * a {@code String}). + * + * <p>Otherwise, the {@code limit} parameter controls the contents of the + * returned array as described below. + * + * @param inputSeq + * the input sequence. + * @param limit + * Determines the maximum number of entries in the resulting + * array, and the treatment of trailing empty strings. + * <ul> + * <li>For n > 0, the resulting array contains at most n + * entries. If this is fewer than the number of matches, the + * final entry will contain all remaining input. + * <li>For n < 0, the length of the resulting array is + * exactly the number of occurrences of the {@code Pattern} + * plus one for the text after the final separator. + * All entries are included. + * <li>For n == 0, the result is as for n < 0, except + * trailing empty strings will not be returned. (Note that + * the case where the input is itself an empty string is + * special, as described above, and the limit parameter does + * not apply there.) + * </ul> + * + * @return the resulting array. + */ + public String[] split(CharSequence inputSeq, int limit) { + if (inputSeq.length() == 0) { + // Unlike Perl, which considers the result of splitting the empty + // string to be the empty array, Java returns an array containing + // the empty string. + return new String[] { "" }; + } + + int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit; + + String input = inputSeq.toString(); + ArrayList<String> list = new ArrayList<String>(); + + Matcher matcher = new Matcher(this, inputSeq); + int savedPos = 0; + + // Add text preceding each occurrence, if enough space. + while(matcher.find() && list.size() + 1 < maxLength) { + list.add(input.substring(savedPos, matcher.start())); + savedPos = matcher.end(); + } + + // Add trailing text if enough space. + if (list.size() < maxLength) { + if (savedPos < input.length()) { + list.add(input.substring(savedPos)); + } else { + list.add(""); + } + } + + // Remove trailing empty matches in the limit == 0 case. + if (limit == 0) { + int i = list.size() - 1; + while (i >= 0 && "".equals(list.get(i))) { + list.remove(i); + i--; + } + } + + return list.toArray(new String[list.size()]); + } + + /** + * Splits a given input around occurrences of a regular expression. This is + * a convenience method that is equivalent to calling the method + * {@link #split(java.lang.CharSequence, int)} with a limit of 0. + * + * @param input + * the input sequence. + * + * @return the resulting array. + */ + public String[] split(CharSequence input) { + return split(input, 0); + } + + /** + * Returns the regular expression that was compiled into this + * {@code Pattern}. + * + * @return the regular expression. + */ + public String pattern() { + return pattern; + } + + @Override + public String toString() { + return pattern; + } + + /** + * Returns the flags that have been set for this {@code Pattern}. + * + * @return the flags that have been set. A combination of the constants + * defined in this class. + * + * @see #CANON_EQ + * @see #CASE_INSENSITIVE + * @see #COMMENTS + * @see #DOTALL + * @see #LITERAL + * @see #MULTILINE + * @see #UNICODE_CASE + * @see #UNIX_LINES + */ + public int flags() { + return flags; } /** @@ -208,8 +312,6 @@ public final class Pattern implements Serializable { * @see #MULTILINE * @see #UNICODE_CASE * @see #UNIX_LINES - * - * @since Android 1.0 */ public static Pattern compile(String pattern, int flags) throws PatternSyntaxException { return new Pattern(pattern, flags); @@ -238,7 +340,24 @@ public final class Pattern implements Serializable { compileImpl(pattern, flags); } - + + /** + * Compiles a regular expression, creating a new Pattern instance in the + * process. This is actually a convenience method that calls {@link + * #compile(String, int)} with a {@code flags} value of zero. + * + * @param pattern + * the regular expression. + * + * @return the new {@code Pattern} instance. + * + * @throws PatternSyntaxException + * if the regular expression is syntactically incorrect. + */ + public static Pattern compile(String pattern) { + return new Pattern(pattern, 0); + } + /** * Compiles the given regular expression using the given flags. Used * internally only. @@ -266,56 +385,6 @@ public final class Pattern implements Serializable { } /** - * Returns the regular expression that was compiled into this - * {@code Pattern}. - * - * @return the regular expression. - * - * @since Android 1.0 - */ - public String pattern() { - return pattern; - } - - /** - * Returns the flags that have been set for this {@code Pattern}. - * - * @return the flags that have been set. A combination of the constants - * defined in this class. - * - * @see #CANON_EQ - * @see #CASE_INSENSITIVE - * @see #COMMENTS - * @see #DOTALL - * @see #LITERAL - * @see #MULTILINE - * @see #UNICODE_CASE - * @see #UNIX_LINES - * - * @since Android 1.0 - */ - public int flags() { - return flags; - } - - /** - * Returns a {@link Matcher} for the {@code Pattern} and a given input. The - * {@code Matcher} can be used to match the {@code Pattern} against the - * whole input, find occurrences of the {@code Pattern} in the input, or - * replace parts of the input. - * - * @param input - * the input to process. - * - * @return the resulting {@code Matcher}. - * - * @since Android 1.0 - */ - public Matcher matcher(CharSequence input) { - return new Matcher(this, input); - } - - /** * Tries to match a given regular expression against a given input. This is * actually nothing but a convenience method that compiles the regular * expression into a {@code Pattern}, builds a {@link Matcher} for it, and @@ -332,107 +401,12 @@ public final class Pattern implements Serializable { * * @see Pattern#compile(java.lang.String, int) * @see Matcher#matches() - * - * @since Android 1.0 */ - static public boolean matches(String regex, CharSequence input) { + public static boolean matches(String regex, CharSequence input) { return new Matcher(new Pattern(regex, 0), input).matches(); } /** - * Splits a given input around occurrences of a regular expression. This is - * a convenience method that is equivalent to calling the method - * {@link #split(java.lang.CharSequence, int)} with a limit of 0. - * - * @param input - * the input sequence. - * - * @return the resulting array. - * - * @since Android 1.0 - */ - public String[] split(CharSequence input) { - return split(input, 0); - } - - /** - * Splits the given input sequence at occurrences of this {@code Pattern}. - * - * If this {@code Pattern} does not occur in the input, the result is an - * array containing the input (converted from a {@code CharSequence} to - * a {@code String}). - * - * Otherwise, the {@code limit} parameter controls the contents of the - * returned array as described below. - * - * @param inputSeq - * the input sequence. - * @param limit - * Determines the maximum number of entries in the resulting - * array, and the treatment of trailing empty strings. - * <ul> - * <li>For n > 0, the resulting array contains at most n - * entries. If this is fewer than the number of matches, the - * final entry will contain all remaining input. - * <li>For n < 0, the length of the resulting array is - * exactly the number of occurrences of the {@code Pattern} - * plus one for the text after the final separator. - * All entries are included. - * <li>For n == 0, the result is as for n < 0, except - * trailing empty strings will not be returned. (Note that - * the case where the input is itself an empty string is - * special, as described above, and the limit parameter does - * not apply there.) - * </ul> - * - * @return the resulting array. - * - * @since Android 1.0 - */ - public String[] split(CharSequence inputSeq, int limit) { - if (inputSeq.length() == 0) { - // Unlike Perl, which considers the result of splitting the empty - // string to be the empty array, Java returns an array containing - // the empty string. - return new String[] { "" }; - } - - int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit; - - String input = inputSeq.toString(); - ArrayList<String> list = new ArrayList<String>(); - - Matcher matcher = new Matcher(this, inputSeq); - int savedPos = 0; - - // Add text preceding each occurrence, if enough space. - while(matcher.find() && list.size() + 1 < maxLength) { - list.add(input.substring(savedPos, matcher.start())); - savedPos = matcher.end(); - } - - // Add trailing text if enough space. - if (list.size() < maxLength) { - if (savedPos < input.length()) { - list.add(input.substring(savedPos)); - } else { - list.add(""); - } - } - - // Remove trailing empty matches in the limit == 0 case. - if (limit == 0) { - int i = list.size() - 1; - while (i >= 0 && "".equals(list.get(i))) { - list.remove(i); - i--; - } - } - - return list.toArray(new String[list.size()]); - } - - /** * Quotes a given string using "\Q" and "\E", so that all other * meta-characters lose their special meaning. If the string is used for a * {@code Pattern} afterwards, it can only be matched literally. @@ -441,27 +415,20 @@ public final class Pattern implements Serializable { * the string to quote. * * @return the quoted string. - * - * @since Android 1.0 */ public static String quote(String s) { - StringBuffer sb = new StringBuffer().append("\\Q"); + StringBuilder sb = new StringBuilder().append("\\Q"); //$NON-NLS-1$ int apos = 0; int k; - while ((k = s.indexOf("\\E", apos)) >= 0) { - sb.append(s.substring(apos, k + 2)).append("\\\\E\\Q"); + while ((k = s.indexOf("\\E", apos)) >= 0) { //$NON-NLS-1$ + sb.append(s.substring(apos, k + 2)).append("\\\\E\\Q"); //$NON-NLS-1$ apos = k + 2; } - return sb.append(s.substring(apos)).append("\\E").toString(); + return sb.append(s.substring(apos)).append("\\E").toString(); //$NON-NLS-1$ } @Override - public String toString() { - return pattern; - } - - @Override protected void finalize() throws Throwable { try { if (mNativePattern != 0) { @@ -474,7 +441,7 @@ public final class Pattern implements Serializable { } /** - * Provides serialization support + * Serialization support */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { diff --git a/regex/src/main/java/java/util/regex/PatternSyntaxException.java b/regex/src/main/java/java/util/regex/PatternSyntaxException.java index e4d5abd..d59bdd4 100644 --- a/regex/src/main/java/java/util/regex/PatternSyntaxException.java +++ b/regex/src/main/java/java/util/regex/PatternSyntaxException.java @@ -1,17 +1,18 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 * - * 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 * - * 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. + * 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. */ package java.util.regex; @@ -25,24 +26,22 @@ import java.util.Arrays; * * @see Pattern#compile(String) * @see Pattern#compile(java.lang.String,int) - * - * @since Android 1.0 */ public class PatternSyntaxException extends IllegalArgumentException { private static final long serialVersionUID = -3864639126226059218L; - - /** - * Holds the syntactically incorrect regular expression, or null if the - * regular expression is not known. - */ - private String pattern; /** * Holds the description of the syntax error, or null if the description is * not known. */ - private String description; + private String desc; + + /** + * Holds the syntactically incorrect regular expression, or null if the + * regular expression is not known. + */ + private String pattern; /** * Holds the index around which the error occured, or -1, in case it is @@ -63,11 +62,10 @@ public class PatternSyntaxException extends IllegalArgumentException { * @param index * the character index around which the error occurred, or -1 if * the index is not known. - * @since Android 1.0 */ public PatternSyntaxException(String description, String pattern, int index) { + this.desc = description; this.pattern = pattern; - this.description = description; this.index = index; } @@ -76,7 +74,6 @@ public class PatternSyntaxException extends IllegalArgumentException { * * @return the regular expression. * - * @since Android 1.0 */ public String getPattern() { return pattern; @@ -88,16 +85,15 @@ public class PatternSyntaxException extends IllegalArgumentException { * original regular expression, and the index at which the error occured. * * @return the error message. - * - * @since Android 1.0 */ @Override public String getMessage() { + // BEGIN android-changed StringBuilder builder = new StringBuilder("Syntax error"); - if (description != null) { + if (desc != null) { builder.append(' '); - builder.append(description); + builder.append(desc); } if (index >= 0) { @@ -118,6 +114,7 @@ public class PatternSyntaxException extends IllegalArgumentException { } return builder.toString(); + // END android-changed } /** @@ -125,10 +122,9 @@ public class PatternSyntaxException extends IllegalArgumentException { * description is not known. * * @return the description. - * @since Android 1.0 */ public String getDescription() { - return description; + return desc; } /** @@ -137,10 +133,8 @@ public class PatternSyntaxException extends IllegalArgumentException { * * @return the index. * - * @since Android 1.0 */ public int getIndex() { return index; } - } diff --git a/sql/src/main/native/sqlite_jni.c b/sql/src/main/native/sqlite_jni.c index 4923869..1a23769 100644 --- a/sql/src/main/native/sqlite_jni.c +++ b/sql/src/main/native/sqlite_jni.c @@ -1,3 +1,5 @@ +#include "JNIHelp.h" + #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -276,23 +278,13 @@ freep(char **strp) static void throwex(JNIEnv *env, const char *msg) { - jclass except = (*env)->FindClass(env, "SQLite/Exception"); - - (*env)->ExceptionClear(env); - if (except) { - (*env)->ThrowNew(env, except, msg); - } + jniThrowException(env, "SQLite/Exception", msg); } static void throwoom(JNIEnv *env, const char *msg) { - jclass except = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); - - (*env)->ExceptionClear(env); - if (except) { - (*env)->ThrowNew(env, except, msg); - } + jniThrowException(env, "java/lang/OutOfMemoryError", msg); } static void @@ -305,12 +297,7 @@ throwclosed(JNIEnv *env) static void throwioex(JNIEnv *env, const char *msg) { - jclass except = (*env)->FindClass(env, "java/io/IOException"); - - (*env)->ExceptionClear(env); - if (except) { - (*env)->ThrowNew(env, except, msg); - } + jniThrowException(env, "java/io/IOException", msg); } #endif @@ -331,16 +318,10 @@ trans2iso(JNIEnv *env, int haveutf, jstring enc, jstring src, dest->result = 0; dest->tofree = 0; if (haveutf) { - const char *utf = (*env)->GetStringUTFChars(env, src, 0); - - if (!utf) { - return dest->result; - } - dest->tofree = malloc(strlen(utf) + 1); - dest->result = dest->tofree; - strcpy(dest->result, utf); - (*env)->ReleaseStringUTFChars(env, src, utf); - return dest->result; + const jsize utfLength = (*env)->GetStringUTFLength(env, src); + dest->result = dest->tofree = malloc(utfLength + 1); + (*env)->GetStringUTFRegion(env, src, 0, utfLength, dest->result); + return dest->result; } if (enc) { bytes = (*env)->CallObjectMethod(env, src, @@ -1331,11 +1312,10 @@ Java_SQLite_Database__1exec__Ljava_lang_String_2LSQLite_Callback_2_3Ljava_lang_S } if (h) { if (h->sqlite) { - jboolean b; jthrowable exc; int rc = SQLITE_ERROR, nargs, i; char *err = 0, *p; - const char *str = (*env)->GetStringUTFChars(env, sql, &b); + const char *str = (*env)->GetStringUTFChars(env, sql, NULL); transstr sqlstr; struct args { char *arg; @@ -3009,10 +2989,9 @@ Java_SQLite_Database_vm_1compile_1args(JNIEnv *env, hvm *v; jvalue vv; jthrowable exc; - jboolean b; int rc = SQLITE_ERROR, nargs, i; char *p; - const char *str = (*env)->GetStringUTFChars(env, sql, &b); + const char *str = (*env)->GetStringUTFChars(env, sql, NULL); const char *tail; transstr sqlstr; struct args { @@ -3401,7 +3380,7 @@ Java_SQLite_Database_stmt_1prepare(JNIEnv *env, jobject obj, jstring sql, return; } len16 = len16 + sizeof (jchar) - ((char *) tail - (char *) sql16); - if (len16 < sizeof (jchar)) { + if (len16 < (jsize) sizeof (jchar)) { len16 = sizeof (jchar); } v = malloc(sizeof (hvm) + len16); @@ -3674,19 +3653,16 @@ Java_SQLite_Stmt_bind__ILjava_lang_String_2(JNIEnv *env, jobject obj, return; } if (val) { - len = (*env)->GetStringLength(env, val); + const jsize charCount = (*env)->GetStringLength(env, val); + len = charCount * sizeof(jchar); if (len > 0) { - const jchar *ch; - - len *= sizeof (jchar); data = sqlite3_malloc(len); if (!data) { throwoom(env, "unable to get blob parameter"); return; } - ch = (*env)->GetStringChars(env, val, 0); - memcpy(data, ch, len); - (*env)->ReleaseStringChars(env, val, ch); + + (*env)->GetStringRegion(env, val, 0, charCount, (jchar*) data); ret = sqlite3_bind_text16((sqlite3_stmt *) v->vm, pos, data, len, sqlite3_free); } else { diff --git a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java index 6c23a91..507e14f 100644 --- a/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java +++ b/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java @@ -94,10 +94,11 @@ public abstract class SSLInputStream extends InputStream { * Reads and returns uint64 value. */ public long readUint64() throws IOException { - return (read() << 56) | (read() << 48) - | (read() << 40) | (read() << 32) - | (read() << 24) | (read() << 16) - | (read() << 8) | (read() & 0x00FF); + // BEGIN android-changed + long hi = readUint32(); + long lo = readUint32(); + return (hi << 32) | lo; + // END android-changed } /** @@ -131,4 +132,3 @@ public abstract class SSLInputStream extends InputStream { return i; } } - diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp index 13a1e61..2a55bbc 100644 --- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp +++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl.cpp @@ -41,20 +41,10 @@ static jfieldID field_ssl_ctx; /** - * Throws java.io.IOexception with the provided message. + * Throws java.io.IOException with the provided message. */ -static void throwIOExceptionStr(JNIEnv* env, const char* message) -{ - jclass exClass = env->FindClass("java/io/IOException"); - - if (exClass == NULL) - { - LOGE("Unable to find class java/io/IOException"); - } - else - { - env->ThrowNew(exClass, message); - } +static void throwIOExceptionStr(JNIEnv* env, const char* message) { + jniThrowException(env, "java/io/IOException", message); } /** @@ -87,9 +77,9 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_init(J // 'seed == null' when no SecureRandom Object is set // in the SSLContext. if (seed != NULL) { - jboolean iscopy = JNI_FALSE; - jbyte* randseed = env->GetByteArrayElements(seed, &iscopy); + jbyte* randseed = env->GetByteArrayElements(seed, NULL); RAND_seed((unsigned char*) randseed, 1024); + env->ReleaseByteArrayElements(seed, randseed, 0); } else { RAND_load_file("/dev/urandom", 1024); } @@ -251,8 +241,8 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLServerSocketImpl_setena ret = SSL_CTX_set_cipher_list(ctx, str); if(ret == 0) { - jclass exClass = env->FindClass("java/lang/IllegalArgumentException"); - env->ThrowNew(exClass, "Illegal cipher suite strings."); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Illegal cipher suite strings."); } } diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp index feae690..324dacf 100644 --- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp +++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp @@ -47,20 +47,6 @@ static SSL_SESSION *getSslSessionPointer(JNIEnv* env, jobject object) { } /** - * Throws java.io.IOexception with the provided message. - */ -static void throwIOExceptionStr(JNIEnv* env, const char* message) -{ - jclass exClass = env->FindClass("java/io/IOException"); - - if (exClass == NULL) { - LOGE("Unable to find class java/io/IOException"); - } else { - env->ThrowNew(exClass, message); - } -} - -/** * Gets the peer certificate in the chain and fills a byte array with the * information therein. */ @@ -150,20 +136,15 @@ static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_deserialize */ static jbyteArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getid(JNIEnv* env, jobject object) { - SSL_SESSION * ssl_session; - jbyteArray bytes; - jbyte *tmp; - - ssl_session = getSslSessionPointer(env, object); + SSL_SESSION* ssl_session = getSslSessionPointer(env, object); - bytes = env->NewByteArray(ssl_session->session_id_length); - if (bytes != NULL) { - tmp = env->GetByteArrayElements(bytes, NULL); - memcpy(tmp, ssl_session->session_id, ssl_session->session_id_length); - env->ReleaseByteArrayElements(bytes, tmp, 0); + jbyteArray result = env->NewByteArray(ssl_session->session_id_length); + if (result != NULL) { + jbyte* src = reinterpret_cast<jbyte*>(ssl_session->session_id); + env->SetByteArrayRegion(result, 0, ssl_session->session_id_length, src); } - return bytes; + return result; } /** diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp index 87f2af3..2e1adb1 100644 --- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp +++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp @@ -989,9 +989,9 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_init(JNIEnv* // 'seed == null' when no SecureRandom Object is set // in the SSLContext. if (seed != NULL) { - jboolean iscopy = JNI_FALSE; - jbyte* randseed = env->GetByteArrayElements(seed, &iscopy); + jbyte* randseed = env->GetByteArrayElements(seed, NULL); RAND_seed((unsigned char*) randseed, 1024); + env->ReleaseByteArrayElements(seed, randseed, 0); } else { RAND_load_file("/dev/urandom", 1024); } @@ -1500,8 +1500,8 @@ static void org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl_setenabledci if (ret == 0) { freeSslErrorState(); - jclass exClass = env->FindClass("java/lang/IllegalArgumentException"); - env->ThrowNew(exClass, "Illegal cipher suite strings."); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Illegal cipher suite strings."); } } diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h index 9ed75be..409b4ec 100644 --- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h +++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_common.h @@ -76,9 +76,8 @@ static jobjectArray getcertificatebytes(JNIEnv* env, joa = NULL; break; } else { - jbyte *tmp = env->GetByteArrayElements(bytes, NULL); - memcpy(tmp, bptr->data, bptr->length); - env->ReleaseByteArrayElements(bytes, tmp, 0); + jbyte* src = reinterpret_cast<jbyte*>(bptr->data); + env->SetByteArrayRegion(bytes, 0, bptr->length, src); env->SetObjectArrayElement(joa, i, bytes); } } diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java b/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java index 3c1fb2e..c4bae0a 100644 --- a/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java +++ b/x-net/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.security.KeyStore; +import java.security.SecureRandom; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; @@ -585,4 +586,26 @@ public class SSLServerSocketTest extends TestCase { .createServerSocket(); return sss; } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Guard against native resource leakage.", + method = "SSLSocket", + args = {} + ) + public void test_creationStressTest() throws Exception { + KeyManager[] keyManagers = getKeyManagers(); + // Test the default codepath, which uses /dev/urandom. + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyManagers, null, null); + for (int i = 0; i < 2048; ++i) { + sslContext.getServerSocketFactory().createServerSocket(); + } + + // Test the other codepath, which copies a seed from a byte[]. + sslContext.init(keyManagers, null, new SecureRandom()); + for (int i = 0; i < 2048; ++i) { + sslContext.getServerSocketFactory().createServerSocket(); + } + } } diff --git a/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java b/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java index 5e39cb1..13a0e59 100644 --- a/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java +++ b/x-net/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java @@ -26,6 +26,7 @@ import javax.security.cert.X509Certificate; import java.net.*; import java.security.KeyStore; +import java.security.SecureRandom; import java.lang.String; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -336,6 +337,27 @@ public class SSLSocketTest extends TestCase { } } + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Guard against native resource leakage.", + method = "SSLSocket", + args = {} + ) + public void test_creationStressTest() throws Exception { + // Test the default codepath, which uses /dev/urandom. + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, null, null); + for (int i = 0; i < 2048; ++i) { + sslContext.getSocketFactory().createSocket(); + } + + // Test the other codepath, which copies a seed from a byte[]. + sslContext.init(null, null, new SecureRandom()); + for (int i = 0; i < 2048; ++i) { + sslContext.getSocketFactory().createSocket(); + } + } + /** * @throws IOException * @tests javax.net.ssl.SSLSocket#addHandshakeCompletedListener(HandshakeCompletedListener listener) diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java index f71d289..2fd16d0 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java @@ -69,7 +69,7 @@ public abstract class InnerNodeImpl extends LeafNodeImpl { } public Node getNextSibling() { - if (parent == null || index >= parent.children.size()) { + if (parent == null || index + 1 >= parent.children.size()) { return null; } diff --git a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp index 40a4116..15f1d28 100644 --- a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp +++ b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp @@ -105,16 +105,7 @@ static jclass stringClass; static jstring emptyString; /** - * Throws a NullPointerException. - * - * @param msg exception message - */ -static void throw_NullPointerException(JNIEnv* env, const char* msg) { - jniThrowException(env, "java/lang/NullPointerException", msg); -} - -/** - * Throw a NullPointerException. + * Throws OutOfMemoryError. */ static void throw_OutOfMemoryError(JNIEnv* env) { jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory."); @@ -336,15 +327,9 @@ static jstring internStringOfLength(JNIEnv* env, ParsingContext* parsingContext, return internString(env, parsingContext, nullTerminated); } -/** - * Throw an assertion error. - * - * @param message to show - */ -static void fail(JNIEnv* env, const char* message) { - jclass clazz; - clazz = env->FindClass("java/lang/AssertionError"); - env->ThrowNew(clazz, message); +static void jniThrowExpatException(JNIEnv* env, XML_Error error) { + const char* message = XML_ErrorString(error); + jniThrowException(env, "org/apache/harmony/xml/ExpatException", message); } /** @@ -1010,9 +995,7 @@ static void appendString(JNIEnv* env, jobject object, jint pointer, jstring xml, if (!XML_Parse(parser, (char*) characters, length, isFinal) && !env->ExceptionCheck()) { - jclass clazz = env->FindClass("org/apache/harmony/xml/ExpatException"); - const char* errorMessage = XML_ErrorString(XML_GetErrorCode(parser)); - env->ThrowNew(clazz, errorMessage); + jniThrowExpatException(env, XML_GetErrorCode(parser)); } // We have to temporarily clear an exception before we can release local @@ -1050,9 +1033,7 @@ static void appendCharacters(JNIEnv* env, jobject object, jint pointer, if (!XML_Parse(parser, ((char*) characters) + (offset << 1), length << 1, XML_FALSE) && !env->ExceptionCheck()) { - jclass clazz = env->FindClass("org/apache/harmony/xml/ExpatException"); - const char* errorMessage = XML_ErrorString(XML_GetErrorCode(parser)); - env->ThrowNew(clazz, errorMessage); + jniThrowExpatException(env, XML_GetErrorCode(parser)); } // We have to temporarily clear an exception before we can release local @@ -1090,9 +1071,7 @@ static void appendBytes(JNIEnv* env, jobject object, jint pointer, if (!XML_Parse(parser, ((char*) bytes) + offset, length, XML_FALSE) && !env->ExceptionCheck()) { - jclass clazz = env->FindClass("org/apache/harmony/xml/ExpatException"); - const char* errorMessage = XML_ErrorString(XML_GetErrorCode(parser)); - env->ThrowNew(clazz, errorMessage); + jniThrowExpatException(env, XML_GetErrorCode(parser)); } // We have to temporarily clear an exception before we can release local @@ -1308,26 +1287,21 @@ static jint getAttributeIndex(JNIEnv* env, jobject clazz, return getAttributeIndexForQName( env, clazz, attributePointer, localName); } - int localNameLength = env->GetStringUTFLength(localName); // Create string in the same format used by Expat: "uri|localName" + // TODO: do we have a guarantee that uriLength and localNameLength are small? char concatenated[uriLength + localNameLength + 2]; // Append uri. - const char* uriBytes = env->GetStringUTFChars(uri, NULL); - if (uriBytes == NULL) return -1; - strcpy(concatenated, uriBytes); - env->ReleaseStringUTFChars(uri, uriBytes); + env->GetStringUTFRegion(uri, 0, uriLength, concatenated); - // Separarator. + // Separator. concatenated[uriLength] = '|'; // Append local name. - const char* localNameBytes = env->GetStringUTFChars(localName, NULL); - if (localNameBytes == NULL) return -1; - strcpy(concatenated + uriLength + 1, localNameBytes); - env->ReleaseStringUTFChars(localName, localNameBytes); + env->GetStringUTFRegion(localName, 0, localNameLength, + concatenated + uriLength + 1); return findAttributeByName(attributes, concatenated); } diff --git a/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java b/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java index bc5e6a1..a1627ba 100644 --- a/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java +++ b/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTestSupport.java @@ -54,7 +54,7 @@ class SAXParserTestSupport { public static final String KEY_ERROR = "error"; public static final String KEY_FATAL_ERROR = "fatalError"; public static final String KEY_WARNING = "warning"; - public static final String KEY_END_ELEMENT = "endEement"; + public static final String KEY_END_ELEMENT = "endElement"; public static final String KEY_END_PREFIX_MAPPING = "endPrefixMapping"; public static final String KEY_IGNORABLE_WHITE_SPACE = "ignorableWhitespace"; diff --git a/xml/src/test/java/tests/xml/AllTests.java b/xml/src/test/java/tests/xml/AllTests.java index eefae50..8c089e3 100644 --- a/xml/src/test/java/tests/xml/AllTests.java +++ b/xml/src/test/java/tests/xml/AllTests.java @@ -26,6 +26,7 @@ public class AllTests { suite.addTestSuite(SimpleParserTest.class); suite.addTestSuite(SimpleBuilderTest.class); + suite.addTestSuite(NodeTests.class); //suite.addTest(tests.org.w3c.dom.AllTests.suite()); suite.addTest(tests.api.javax.xml.parsers.AllTests.suite()); diff --git a/xml/src/test/java/tests/xml/NodeTests.java b/xml/src/test/java/tests/xml/NodeTests.java new file mode 100644 index 0000000..e46e216 --- /dev/null +++ b/xml/src/test/java/tests/xml/NodeTests.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 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. + */ + +package tests.xml; + +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; +import dalvik.annotation.TestTargetClass; + +import junit.framework.TestCase; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import java.io.ByteArrayInputStream; +import javax.xml.parsers.DocumentBuilderFactory; + +@TestTargetClass(Node.class) +public class NodeTests extends TestCase { + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Issue #779: org.w3c.dom.Node#getNextSibling throws IndexOutOfBoundsException.", + method = "getNextSibling", + args = {} + ) + public void test_getNextSibling() throws Exception { + // Calling getNextSibling when there is no next sibling should return null. + // From http://code.google.com/p/android/issues/detail?id=779. + ByteArrayInputStream bis = new ByteArrayInputStream("<root/>".getBytes()); + Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(bis); + Node root = document.getDocumentElement(); + assertNull(root.getNextSibling()); + } +} |