diff options
Diffstat (limited to 'libart')
22 files changed, 1927 insertions, 1281 deletions
diff --git a/libart/src/main/java/dalvik/system/TransactionAbortError.java b/libart/src/main/java/dalvik/system/TransactionAbortError.java new file mode 100644 index 0000000..cfe4ca2 --- /dev/null +++ b/libart/src/main/java/dalvik/system/TransactionAbortError.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 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 dalvik.system; + +/** + * An exception only used by the compiler to abort a transaction. + * + * @hide + */ +final class TransactionAbortError extends InternalError { + /** + * Constructs a new {@code TransactionAbortError} with its stack trace filled in. + */ + private TransactionAbortError() { + } + + /** + * Constructs a new {@code TransactionAbortError} with its stack trace and detail + * message filled in. + * + * @param detailMessage the detail message for the exception. + */ + private TransactionAbortError(String detailMessage) { + super(detailMessage); + } + + /** + * Constructs a new {@code TransactionAbortError} with detail message and cause + * filled in. + * + * @param message the detail message for the exception. + * @param cause the detail cause for the exception. + */ + private TransactionAbortError(String message, Throwable cause) { + super(message); + initCause(cause); + } + + /** + * Constructs a new {@code TransactionAbortError} with its detail cause filled in. + * + * @param cause the detail cause for the exception. + */ + private TransactionAbortError(Throwable cause) { + this(cause == null ? null : cause.toString(), cause); + } + +} diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java index 43fa00e..aa3f154 100644 --- a/libart/src/main/java/dalvik/system/VMRuntime.java +++ b/libart/src/main/java/dalvik/system/VMRuntime.java @@ -16,6 +16,7 @@ package dalvik.system; +import java.lang.ref.FinalizerReference; import java.util.HashMap; import java.util.Map; @@ -33,6 +34,10 @@ public final class VMRuntime { */ private static final VMRuntime THE_ONE = new VMRuntime(); + // Note: Instruction set names are used to construct the names of some + // system properties. To be sure that the properties stay valid the + // instruction set name should not exceed 7 characters. See installd + // and the package manager for the actual propeties. private static final Map<String, String> ABI_TO_INSTRUCTION_SET_MAP = new HashMap<String, String>(); static { @@ -272,6 +277,12 @@ public final class VMRuntime { public native void clearGrowthLimit(); /** + * Make the current growth limit the new non growth limit capacity by releasing pages which + * are after the growth limit but before the non growth limit capacity. + */ + public native void clampGrowthLimit(); + + /** * Returns true if either a Java debugger or native debugger is active. */ public native boolean isDebuggerActive(); @@ -291,8 +302,37 @@ public final class VMRuntime { */ public native void registerNativeFree(int bytes); - public native void trimHeap(); + /** + * Wait for objects to be finalized. + * + * If finalization takes longer than timeout, then the function returns before all objects are + * finalized. + * + * @param timeout + * timeout in nanoseconds of the maximum time to wait until all pending finalizers + * are run. If timeout is 0, then there is no timeout. Note that the timeout does + * not stop the finalization process, it merely stops the wait. + * + * @see #Runtime.runFinalization() + * @see #wait(long,int) + */ + public static void runFinalization(long timeout) { + try { + FinalizerReference.finalizeAllEnqueued(timeout); + } catch (InterruptedException e) { + // Interrupt the current thread without actually throwing the InterruptionException + // for the caller. + Thread.currentThread().interrupt(); + } + } + + public native void requestConcurrentGC(); public native void concurrentGC(); + public native void requestHeapTrim(); + public native void trimHeap(); + public native void startHeapTaskProcessor(); + public native void stopHeapTaskProcessor(); + public native void runHeapTasks(); /** * Let the heap know of the new process state. This can change allocation and garbage collection diff --git a/libart/src/main/java/dalvik/system/VMStack.java b/libart/src/main/java/dalvik/system/VMStack.java index ee0a0db..b69ab60 100644 --- a/libart/src/main/java/dalvik/system/VMStack.java +++ b/libart/src/main/java/dalvik/system/VMStack.java @@ -48,11 +48,10 @@ public final class VMStack { native public static Class<?> getStackClass2(); /** - * Returns the first ClassLoader on the call stack that isn't either of - * the passed-in ClassLoaders. + * Returns the first ClassLoader on the call stack that isn't the + * bootstrap class loader. */ - public native static ClassLoader getClosestUserClassLoader(ClassLoader bootstrap, - ClassLoader system); + public native static ClassLoader getClosestUserClassLoader(); /** * Retrieves the stack trace from the specified thread. diff --git a/libart/src/main/java/java/lang/AbstractStringBuilder.java b/libart/src/main/java/java/lang/AbstractStringBuilder.java new file mode 100644 index 0000000..c8c8c5a --- /dev/null +++ b/libart/src/main/java/java/lang/AbstractStringBuilder.java @@ -0,0 +1,886 @@ +/* + * 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 + * + * 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 java.lang; + +import libcore.util.EmptyArray; + +import java.io.InvalidObjectException; +import java.util.Arrays; + +/** + * A modifiable {@link CharSequence sequence of characters} for use in creating + * and modifying Strings. This class is intended as a base class for + * {@link StringBuffer} and {@link StringBuilder}. + * + * @see StringBuffer + * @see StringBuilder + * @since 1.5 + */ +abstract class AbstractStringBuilder { + + static final int INITIAL_CAPACITY = 16; + + private char[] value; + + private int count; + + private boolean shared; + + /* + * Returns the character array. + */ + final char[] getValue() { + return value; + } + + /* + * Returns the underlying buffer and sets the shared flag. + */ + final char[] shareValue() { + shared = true; + return value; + } + + /* + * Restores internal state after deserialization. + */ + final void set(char[] val, int len) throws InvalidObjectException { + if (val == null) { + val = EmptyArray.CHAR; + } + if (val.length < len) { + throw new InvalidObjectException("count out of range"); + } + + shared = false; + value = val; + count = len; + } + + AbstractStringBuilder() { + value = new char[INITIAL_CAPACITY]; + } + + AbstractStringBuilder(int capacity) { + if (capacity < 0) { + throw new NegativeArraySizeException(Integer.toString(capacity)); + } + value = new char[capacity]; + } + + AbstractStringBuilder(String string) { + count = string.length(); + shared = false; + value = new char[count + INITIAL_CAPACITY]; + string.getCharsNoCheck(0, count, value, 0); + } + + private void enlargeBuffer(int min) { + int newCount = ((value.length >> 1) + value.length) + 2; + char[] newData = new char[min > newCount ? min : newCount]; + System.arraycopy(value, 0, newData, 0, count); + value = newData; + shared = false; + } + + final void appendNull() { + int newCount = count + 4; + if (newCount > value.length) { + enlargeBuffer(newCount); + } + value[count++] = 'n'; + value[count++] = 'u'; + value[count++] = 'l'; + value[count++] = 'l'; + } + + final void append0(char[] chars) { + int newCount = count + chars.length; + if (newCount > value.length) { + enlargeBuffer(newCount); + } + System.arraycopy(chars, 0, value, count, chars.length); + count = newCount; + } + + final void append0(char[] chars, int offset, int length) { + Arrays.checkOffsetAndCount(chars.length, offset, length); + int newCount = count + length; + if (newCount > value.length) { + enlargeBuffer(newCount); + } + System.arraycopy(chars, offset, value, count, length); + count = newCount; + } + + final void append0(char ch) { + if (count == value.length) { + enlargeBuffer(count + 1); + } + value[count++] = ch; + } + + final void append0(String string) { + if (string == null) { + appendNull(); + return; + } + int length = string.length(); + int newCount = count + length; + if (newCount > value.length) { + enlargeBuffer(newCount); + } + string.getCharsNoCheck(0, length, value, count); + count = newCount; + } + + final void append0(CharSequence s, int start, int end) { + if (s == null) { + s = "null"; + } + if ((start | end) < 0 || start > end || end > s.length()) { + throw new IndexOutOfBoundsException(); + } + + int length = end - start; + int newCount = count + length; + if (newCount > value.length) { + enlargeBuffer(newCount); + } else if (shared) { + value = value.clone(); + shared = false; + } + + if (s instanceof String) { + ((String) s).getCharsNoCheck(start, end, value, count); + } else if (s instanceof AbstractStringBuilder) { + AbstractStringBuilder other = (AbstractStringBuilder) s; + System.arraycopy(other.value, start, value, count, length); + } else { + int j = count; // Destination index. + for (int i = start; i < end; i++) { + value[j++] = s.charAt(i); + } + } + + this.count = newCount; + } + + /** + * Returns the number of characters that can be held without growing. + * + * @return the capacity + * @see #ensureCapacity + * @see #length + */ + public int capacity() { + return value.length; + } + + /** + * Returns the character at {@code index}. + * @throws IndexOutOfBoundsException if {@code index < 0} or {@code index >= length()}. + */ + public char charAt(int index) { + if (index < 0 || index >= count) { + throw indexAndLength(index); + } + return value[index]; + } + + private StringIndexOutOfBoundsException indexAndLength(int index) { + throw new StringIndexOutOfBoundsException(count, index); + } + + private StringIndexOutOfBoundsException startEndAndLength(int start, int end) { + throw new StringIndexOutOfBoundsException(count, start, end - start); + } + + final void delete0(int start, int end) { + // NOTE: StringBuilder#delete(int, int) is specified not to throw if + // the end index is >= count, as long as it's >= start. This means + // we have to clamp it to count here. + if (end > count) { + end = count; + } + + if (start < 0 || start > count || start > end) { + throw startEndAndLength(start, end); + } + + // NOTE: StringBuilder#delete(int, int) throws only if start > count + // (start == count is considered valid, oddly enough). Since 'end' is + // already a clamped value, that case is handled here. + if (end == start) { + return; + } + + // At this point we know for sure that end > start. + int length = count - end; + if (length >= 0) { + if (!shared) { + System.arraycopy(value, end, value, start, length); + } else { + char[] newData = new char[value.length]; + System.arraycopy(value, 0, newData, 0, start); + System.arraycopy(value, end, newData, start, length); + value = newData; + shared = false; + } + } + count -= end - start; + } + + final void deleteCharAt0(int index) { + if (index < 0 || index >= count) { + throw indexAndLength(index); + } + + delete0(index, index + 1); + } + + /** + * Ensures that this object has a minimum capacity available before + * requiring the internal buffer to be enlarged. The general policy of this + * method is that if the {@code minimumCapacity} is larger than the current + * {@link #capacity()}, then the capacity will be increased to the largest + * value of either the {@code minimumCapacity} or the current capacity + * multiplied by two plus two. Although this is the general policy, there is + * no guarantee that the capacity will change. + * + * @param min + * the new minimum capacity to set. + */ + public void ensureCapacity(int min) { + if (min > value.length) { + int ourMin = value.length*2 + 2; + enlargeBuffer(Math.max(ourMin, min)); + } + } + + /** + * Copies the requested sequence of characters into {@code dst} passed + * starting at {@code dst}. + * + * @param start + * the inclusive start index of the characters to copy. + * @param end + * the exclusive end index of the characters to copy. + * @param dst + * the {@code char[]} to copy the characters to. + * @param dstStart + * the inclusive start index of {@code dst} to begin copying to. + * @throws IndexOutOfBoundsException + * if the {@code start} is negative, the {@code dstStart} is + * negative, the {@code start} is greater than {@code end}, the + * {@code end} is greater than the current {@link #length()} or + * {@code dstStart + end - begin} is greater than + * {@code dst.length}. + */ + public void getChars(int start, int end, char[] dst, int dstStart) { + if (start > count || end > count || start > end) { + throw startEndAndLength(start, end); + } + System.arraycopy(value, start, dst, dstStart, end - start); + } + + final void insert0(int index, char[] chars) { + if (index < 0 || index > count) { + throw indexAndLength(index); + } + if (chars.length != 0) { + move(chars.length, index); + System.arraycopy(chars, 0, value, index, chars.length); + count += chars.length; + } + } + + final void insert0(int index, char[] chars, int start, int length) { + if (index >= 0 && index <= count) { + // start + length could overflow, start/length maybe MaxInt + if (start >= 0 && length >= 0 && length <= chars.length - start) { + if (length != 0) { + move(length, index); + System.arraycopy(chars, start, value, index, length); + count += length; + } + return; + } + } + throw new StringIndexOutOfBoundsException("this.length=" + count + + "; index=" + index + "; chars.length=" + chars.length + + "; start=" + start + "; length=" + length); + } + + final void insert0(int index, char ch) { + if (index < 0 || index > count) { + // RI compatible exception type + throw new ArrayIndexOutOfBoundsException(count, index); + } + move(1, index); + value[index] = ch; + count++; + } + + final void insert0(int index, String string) { + if (index >= 0 && index <= count) { + if (string == null) { + string = "null"; + } + int min = string.length(); + if (min != 0) { + move(min, index); + string.getCharsNoCheck(0, min, value, index); + count += min; + } + } else { + throw indexAndLength(index); + } + } + + final void insert0(int index, CharSequence s, int start, int end) { + if (s == null) { + s = "null"; + } + if ((index | start | end) < 0 || index > count || start > end || end > s.length()) { + throw new IndexOutOfBoundsException(); + } + insert0(index, s.subSequence(start, end).toString()); + } + + /** + * The current length. + * + * @return the number of characters contained in this instance. + */ + public int length() { + return count; + } + + private void move(int size, int index) { + int newCount; + if (value.length - count >= size) { + if (!shared) { + // index == count case is no-op + System.arraycopy(value, index, value, index + size, count - index); + return; + } + newCount = value.length; + } else { + newCount = Math.max(count + size, value.length*2 + 2); + } + + char[] newData = new char[newCount]; + System.arraycopy(value, 0, newData, 0, index); + // index == count case is no-op + System.arraycopy(value, index, newData, index + size, count - index); + value = newData; + shared = false; + } + + final void replace0(int start, int end, String string) { + if (start >= 0) { + if (end > count) { + end = count; + } + if (end > start) { + int stringLength = string.length(); + int diff = end - start - stringLength; + if (diff > 0) { // replacing with fewer characters + if (!shared) { + // index == count case is no-op + System.arraycopy(value, end, value, start + + stringLength, count - end); + } else { + char[] newData = new char[value.length]; + System.arraycopy(value, 0, newData, 0, start); + // index == count case is no-op + System.arraycopy(value, end, newData, start + + stringLength, count - end); + value = newData; + shared = false; + } + } else if (diff < 0) { + // replacing with more characters...need some room + move(-diff, end); + } else if (shared) { + value = value.clone(); + shared = false; + } + string.getCharsNoCheck(0, stringLength, value, start); + count -= diff; + return; + } + if (start == end) { + if (string == null) { + throw new NullPointerException("string == null"); + } + insert0(start, string); + return; + } + } + throw startEndAndLength(start, end); + } + + final void reverse0() { + if (count < 2) { + return; + } + if (!shared) { + int end = count - 1; + char frontHigh = value[0]; + char endLow = value[end]; + boolean allowFrontSur = true, allowEndSur = true; + for (int i = 0, mid = count / 2; i < mid; i++, --end) { + char frontLow = value[i + 1]; + char endHigh = value[end - 1]; + boolean surAtFront = allowFrontSur && frontLow >= 0xdc00 + && frontLow <= 0xdfff && frontHigh >= 0xd800 + && frontHigh <= 0xdbff; + if (surAtFront && (count < 3)) { + return; + } + boolean surAtEnd = allowEndSur && endHigh >= 0xd800 + && endHigh <= 0xdbff && endLow >= 0xdc00 + && endLow <= 0xdfff; + allowFrontSur = allowEndSur = true; + if (surAtFront == surAtEnd) { + if (surAtFront) { + // both surrogates + value[end] = frontLow; + value[end - 1] = frontHigh; + value[i] = endHigh; + value[i + 1] = endLow; + frontHigh = value[i + 2]; + endLow = value[end - 2]; + i++; + end--; + } else { + // neither surrogates + value[end] = frontHigh; + value[i] = endLow; + frontHigh = frontLow; + endLow = endHigh; + } + } else { + if (surAtFront) { + // surrogate only at the front + value[end] = frontLow; + value[i] = endLow; + endLow = endHigh; + allowFrontSur = false; + } else { + // surrogate only at the end + value[end] = frontHigh; + value[i] = endHigh; + frontHigh = frontLow; + allowEndSur = false; + } + } + } + if ((count & 1) == 1 && (!allowFrontSur || !allowEndSur)) { + value[end] = allowFrontSur ? endLow : frontHigh; + } + } else { + char[] newData = new char[value.length]; + for (int i = 0, end = count; i < count; i++) { + char high = value[i]; + if ((i + 1) < count && high >= 0xd800 && high <= 0xdbff) { + char low = value[i + 1]; + if (low >= 0xdc00 && low <= 0xdfff) { + newData[--end] = low; + i++; + } + } + newData[--end] = high; + } + value = newData; + shared = false; + } + } + + /** + * Sets the character at the {@code index}. + * + * @param index + * the zero-based index of the character to replace. + * @param ch + * the character to set. + * @throws IndexOutOfBoundsException + * if {@code index} is negative or greater than or equal to the + * current {@link #length()}. + */ + public void setCharAt(int index, char ch) { + if (index < 0 || index >= count) { + throw indexAndLength(index); + } + if (shared) { + value = value.clone(); + shared = false; + } + value[index] = ch; + } + + /** + * Sets the current length to a new value. If the new length is larger than + * the current length, then the new characters at the end of this object + * will contain the {@code char} value of {@code \u0000}. + * + * @param length + * the new length of this StringBuffer. + * @throws IndexOutOfBoundsException + * if {@code length < 0}. + * @see #length + */ + public void setLength(int length) { + if (length < 0) { + throw new StringIndexOutOfBoundsException("length < 0: " + length); + } + if (length > value.length) { + enlargeBuffer(length); + } else { + if (shared) { + char[] newData = new char[value.length]; + System.arraycopy(value, 0, newData, 0, count); + value = newData; + shared = false; + } else { + if (count < length) { + Arrays.fill(value, count, length, (char) 0); + } + } + } + count = length; + } + + /** + * Returns the String value of the subsequence from the {@code start} index + * to the current end. + * + * @param start + * the inclusive start index to begin the subsequence. + * @return a String containing the subsequence. + * @throws StringIndexOutOfBoundsException + * if {@code start} is negative or greater than the current + * {@link #length()}. + */ + public String substring(int start) { + if (start >= 0 && start <= count) { + if (start == count) { + return ""; + } + + // Remove String sharing for more performance + return new String(value, start, count - start); + } + throw indexAndLength(start); + } + + /** + * Returns the String value of the subsequence from the {@code start} index + * to the {@code end} index. + * + * @param start + * the inclusive start index to begin the subsequence. + * @param end + * the exclusive end index to end the subsequence. + * @return a String containing the subsequence. + * @throws StringIndexOutOfBoundsException + * if {@code start} is negative, greater than {@code end} or if + * {@code end} is greater than the current {@link #length()}. + */ + public String substring(int start, int end) { + if (start >= 0 && start <= end && end <= count) { + if (start == end) { + return ""; + } + + // Remove String sharing for more performance + return new String(value, start, end - start); + } + throw startEndAndLength(start, end); + } + + /** + * Returns the current String representation. + * + * @return a String containing the characters in this instance. + */ + @Override + public String toString() { + if (count == 0) { + return ""; + } + return StringFactory.newStringFromChars(0, count, value); + } + + /** + * Returns a {@code CharSequence} of the subsequence from the {@code start} + * index to the {@code end} index. + * + * @param start + * the inclusive start index to begin the subsequence. + * @param end + * the exclusive end index to end the subsequence. + * @return a CharSequence containing the subsequence. + * @throws IndexOutOfBoundsException + * if {@code start} is negative, greater than {@code end} or if + * {@code end} is greater than the current {@link #length()}. + * @since 1.4 + */ + public CharSequence subSequence(int start, int end) { + return substring(start, end); + } + + /** + * Searches for the first index of the specified character. The search for + * the character starts at the beginning and moves towards the end. + * + * @param string + * the string to find. + * @return the index of the specified character, -1 if the character isn't + * found. + * @see #lastIndexOf(String) + * @since 1.4 + */ + public int indexOf(String string) { + return indexOf(string, 0); + } + + /** + * Searches for the index of the specified character. The search for the + * character starts at the specified offset and moves towards the end. + * + * @param subString + * the string to find. + * @param start + * the starting offset. + * @return the index of the specified character, -1 if the character isn't + * found + * @see #lastIndexOf(String,int) + * @since 1.4 + */ + public int indexOf(String subString, int start) { + if (start < 0) { + start = 0; + } + int subCount = subString.length(); + if (subCount > 0) { + if (subCount + start > count) { + return -1; + } + // TODO optimize charAt to direct array access + char firstChar = subString.charAt(0); + while (true) { + int i = start; + boolean found = false; + for (; i < count; i++) { + if (value[i] == firstChar) { + found = true; + break; + } + } + if (!found || subCount + i > count) { + return -1; // handles subCount > count || start >= count + } + int o1 = i, o2 = 0; + while (++o2 < subCount && value[++o1] == subString.charAt(o2)) { + // Intentionally empty + } + if (o2 == subCount) { + return i; + } + start = i + 1; + } + } + return (start < count || start == 0) ? start : count; + } + + /** + * Searches for the last index of the specified character. The search for + * the character starts at the end and moves towards the beginning. + * + * @param string + * the string to find. + * @return the index of the specified character, -1 if the character isn't + * found. + * @throws NullPointerException + * if {@code string} is {@code null}. + * @see String#lastIndexOf(java.lang.String) + * @since 1.4 + */ + public int lastIndexOf(String string) { + return lastIndexOf(string, count); + } + + /** + * Searches for the index of the specified character. The search for the + * character starts at the specified offset and moves towards the beginning. + * + * @param subString + * the string to find. + * @param start + * the starting offset. + * @return the index of the specified character, -1 if the character isn't + * found. + * @throws NullPointerException + * if {@code subString} is {@code null}. + * @see String#lastIndexOf(String,int) + * @since 1.4 + */ + public int lastIndexOf(String subString, int start) { + int subCount = subString.length(); + if (subCount <= count && start >= 0) { + if (subCount > 0) { + if (start > count - subCount) { + start = count - subCount; // count and subCount are both + } + // >= 1 + // TODO optimize charAt to direct array access + char firstChar = subString.charAt(0); + while (true) { + int i = start; + boolean found = false; + for (; i >= 0; --i) { + if (value[i] == firstChar) { + found = true; + break; + } + } + if (!found) { + return -1; + } + int o1 = i, o2 = 0; + while (++o2 < subCount + && value[++o1] == subString.charAt(o2)) { + // Intentionally empty + } + if (o2 == subCount) { + return i; + } + start = i - 1; + } + } + return start < count ? start : count; + } + return -1; + } + + /** + * Trims off any extra capacity beyond the current length. Note, this method + * is NOT guaranteed to change the capacity of this object. + * + * @since 1.5 + */ + public void trimToSize() { + if (count < value.length) { + char[] newValue = new char[count]; + System.arraycopy(value, 0, newValue, 0, count); + value = newValue; + shared = false; + } + } + + /** + * Retrieves the Unicode code point value at the {@code index}. + * + * @param index + * the index to the {@code char} code unit. + * @return the Unicode code point value. + * @throws IndexOutOfBoundsException + * if {@code index} is negative or greater than or equal to + * {@link #length()}. + * @see Character + * @see Character#codePointAt(char[], int, int) + * @since 1.5 + */ + public int codePointAt(int index) { + if (index < 0 || index >= count) { + throw indexAndLength(index); + } + return Character.codePointAt(value, index, count); + } + + /** + * Retrieves the Unicode code point value that precedes the {@code index}. + * + * @param index + * the index to the {@code char} code unit within this object. + * @return the Unicode code point value. + * @throws IndexOutOfBoundsException + * if {@code index} is less than 1 or greater than + * {@link #length()}. + * @see Character + * @see Character#codePointBefore(char[], int, int) + * @since 1.5 + */ + public int codePointBefore(int index) { + if (index < 1 || index > count) { + throw indexAndLength(index); + } + return Character.codePointBefore(value, index); + } + + /** + * Calculates the number of Unicode code points between {@code start} + * and {@code end}. + * + * @param start + * the inclusive beginning index of the subsequence. + * @param end + * the exclusive end index of the subsequence. + * @return the number of Unicode code points in the subsequence. + * @throws IndexOutOfBoundsException + * if {@code start} is negative or greater than + * {@code end} or {@code end} is greater than + * {@link #length()}. + * @see Character + * @see Character#codePointCount(char[], int, int) + * @since 1.5 + */ + public int codePointCount(int start, int end) { + if (start < 0 || end > count || start > end) { + throw startEndAndLength(start, end); + } + return Character.codePointCount(value, start, end - start); + } + + /** + * Returns the index that is offset {@code codePointOffset} code points from + * {@code index}. + * + * @param index + * the index to calculate the offset from. + * @param codePointOffset + * the number of code points to count. + * @return the index that is {@code codePointOffset} code points away from + * index. + * @throws IndexOutOfBoundsException + * if {@code index} is negative or greater than + * {@link #length()} or if there aren't enough code points + * before or after {@code index} to match + * {@code codePointOffset}. + * @see Character + * @see Character#offsetByCodePoints(char[], int, int, int, int) + * @since 1.5 + */ + public int offsetByCodePoints(int index, int codePointOffset) { + return Character.offsetByCodePoints(value, 0, count, index, + codePointOffset); + } +} diff --git a/libart/src/main/java/java/lang/CaseMapper.java b/libart/src/main/java/java/lang/CaseMapper.java new file mode 100644 index 0000000..f23a4ef --- /dev/null +++ b/libart/src/main/java/java/lang/CaseMapper.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.lang; + +import java.util.Locale; +import libcore.icu.ICU; +import libcore.icu.Transliterator; + +/** + * Performs case operations as described by http://unicode.org/reports/tr21/tr21-5.html. + */ +class CaseMapper { + private static final char[] upperValues = "SS\u0000\u02bcN\u0000J\u030c\u0000\u0399\u0308\u0301\u03a5\u0308\u0301\u0535\u0552\u0000H\u0331\u0000T\u0308\u0000W\u030a\u0000Y\u030a\u0000A\u02be\u0000\u03a5\u0313\u0000\u03a5\u0313\u0300\u03a5\u0313\u0301\u03a5\u0313\u0342\u1f08\u0399\u0000\u1f09\u0399\u0000\u1f0a\u0399\u0000\u1f0b\u0399\u0000\u1f0c\u0399\u0000\u1f0d\u0399\u0000\u1f0e\u0399\u0000\u1f0f\u0399\u0000\u1f08\u0399\u0000\u1f09\u0399\u0000\u1f0a\u0399\u0000\u1f0b\u0399\u0000\u1f0c\u0399\u0000\u1f0d\u0399\u0000\u1f0e\u0399\u0000\u1f0f\u0399\u0000\u1f28\u0399\u0000\u1f29\u0399\u0000\u1f2a\u0399\u0000\u1f2b\u0399\u0000\u1f2c\u0399\u0000\u1f2d\u0399\u0000\u1f2e\u0399\u0000\u1f2f\u0399\u0000\u1f28\u0399\u0000\u1f29\u0399\u0000\u1f2a\u0399\u0000\u1f2b\u0399\u0000\u1f2c\u0399\u0000\u1f2d\u0399\u0000\u1f2e\u0399\u0000\u1f2f\u0399\u0000\u1f68\u0399\u0000\u1f69\u0399\u0000\u1f6a\u0399\u0000\u1f6b\u0399\u0000\u1f6c\u0399\u0000\u1f6d\u0399\u0000\u1f6e\u0399\u0000\u1f6f\u0399\u0000\u1f68\u0399\u0000\u1f69\u0399\u0000\u1f6a\u0399\u0000\u1f6b\u0399\u0000\u1f6c\u0399\u0000\u1f6d\u0399\u0000\u1f6e\u0399\u0000\u1f6f\u0399\u0000\u1fba\u0399\u0000\u0391\u0399\u0000\u0386\u0399\u0000\u0391\u0342\u0000\u0391\u0342\u0399\u0391\u0399\u0000\u1fca\u0399\u0000\u0397\u0399\u0000\u0389\u0399\u0000\u0397\u0342\u0000\u0397\u0342\u0399\u0397\u0399\u0000\u0399\u0308\u0300\u0399\u0308\u0301\u0399\u0342\u0000\u0399\u0308\u0342\u03a5\u0308\u0300\u03a5\u0308\u0301\u03a1\u0313\u0000\u03a5\u0342\u0000\u03a5\u0308\u0342\u1ffa\u0399\u0000\u03a9\u0399\u0000\u038f\u0399\u0000\u03a9\u0342\u0000\u03a9\u0342\u0399\u03a9\u0399\u0000FF\u0000FI\u0000FL\u0000FFIFFLST\u0000ST\u0000\u0544\u0546\u0000\u0544\u0535\u0000\u0544\u053b\u0000\u054e\u0546\u0000\u0544\u053d\u0000".toCharArray(); + private static final char[] upperValues2 = "\u000b\u0000\f\u0000\r\u0000\u000e\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>\u0000\u0000?@A\u0000BC\u0000\u0000\u0000\u0000D\u0000\u0000\u0000\u0000\u0000EFG\u0000HI\u0000\u0000\u0000\u0000J\u0000\u0000\u0000\u0000\u0000KL\u0000\u0000MN\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000OPQ\u0000RS\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000TUV\u0000WX\u0000\u0000\u0000\u0000Y".toCharArray(); + + private static final char LATIN_CAPITAL_I_WITH_DOT = '\u0130'; + private static final char GREEK_CAPITAL_SIGMA = '\u03a3'; + private static final char GREEK_SMALL_FINAL_SIGMA = '\u03c2'; + + /** + * Our current GC makes short-lived objects more expensive than we'd like. When that's fixed, + * this class should be changed so that you instantiate it with the String and its value, + * and count fields. + */ + private CaseMapper() { + } + + /** + * Implements String.toLowerCase. The original String instance is returned if nothing changes. + */ + public static String toLowerCase(Locale locale, String s) { + // Punt hard cases to ICU4C. + // Note that Greek isn't a particularly hard case for toLowerCase, only toUpperCase. + String languageCode = locale.getLanguage(); + if (languageCode.equals("tr") || languageCode.equals("az") || languageCode.equals("lt")) { + return ICU.toLowerCase(s, locale); + } + + String newString = null; + for (int i = 0, end = s.length(); i < end; ++i) { + char ch = s.charAt(i); + char newCh; + if (ch == LATIN_CAPITAL_I_WITH_DOT || Character.isHighSurrogate(ch)) { + // Punt these hard cases. + return ICU.toLowerCase(s, locale); + } else if (ch == GREEK_CAPITAL_SIGMA && isFinalSigma(s, i)) { + newCh = GREEK_SMALL_FINAL_SIGMA; + } else { + newCh = Character.toLowerCase(ch); + } + if (ch != newCh) { + if (newString == null) { + newString = StringFactory.newStringFromString(s); + } + newString.setCharAt(i, newCh); + } + } + return newString != null ? newString : s; + } + + /** + * True if 'index' is preceded by a sequence consisting of a cased letter and a case-ignorable + * sequence, and 'index' is not followed by a sequence consisting of an ignorable sequence and + * then a cased letter. + */ + private static boolean isFinalSigma(String s, int index) { + // TODO: we don't skip case-ignorable sequences like we should. + // TODO: we should add a more direct way to test for a cased letter. + if (index <= 0) { + return false; + } + char previous = s.charAt(index - 1); + if (!(Character.isLowerCase(previous) || Character.isUpperCase(previous) || Character.isTitleCase(previous))) { + return false; + } + if (index + 1 >= s.length()) { + return true; + } + char next = s.charAt(index + 1); + if (Character.isLowerCase(next) || Character.isUpperCase(next) || Character.isTitleCase(next)) { + return false; + } + return true; + } + + /** + * Return the index of the specified character into the upperValues table. + * The upperValues table contains three entries at each position. These + * three characters are the upper case conversion. If only two characters + * are used, the third character in the table is \u0000. + * @return the index into the upperValues table, or -1 + */ + private static int upperIndex(int ch) { + int index = -1; + if (ch >= 0xdf) { + if (ch <= 0x587) { + switch (ch) { + case 0xdf: return 0; + case 0x149: return 1; + case 0x1f0: return 2; + case 0x390: return 3; + case 0x3b0: return 4; + case 0x587: return 5; + } + } else if (ch >= 0x1e96) { + if (ch <= 0x1e9a) { + index = 6 + ch - 0x1e96; + } else if (ch >= 0x1f50 && ch <= 0x1ffc) { + index = upperValues2[ch - 0x1f50]; + if (index == 0) { + index = -1; + } + } else if (ch >= 0xfb00) { + if (ch <= 0xfb06) { + index = 90 + ch - 0xfb00; + } else if (ch >= 0xfb13 && ch <= 0xfb17) { + index = 97 + ch - 0xfb13; + } + } + } + } + return index; + } + + private static final ThreadLocal<Transliterator> EL_UPPER = new ThreadLocal<Transliterator>() { + @Override protected Transliterator initialValue() { + return new Transliterator("el-Upper"); + } + }; + + public static String toUpperCase(Locale locale, String s, int count) { + String languageCode = locale.getLanguage(); + if (languageCode.equals("tr") || languageCode.equals("az") || languageCode.equals("lt")) { + return ICU.toUpperCase(s, locale); + } + if (languageCode.equals("el")) { + return EL_UPPER.get().transliterate(s); + } + + char[] output = null; + String newString = null; + int i = 0; + for (int o = 0, end = count; o < end; o++) { + char ch = s.charAt(o); + if (Character.isHighSurrogate(ch)) { + return ICU.toUpperCase(s, locale); + } + int index = upperIndex(ch); + if (index == -1) { + if (output != null && i >= output.length) { + char[] newoutput = new char[output.length + (count / 6) + 2]; + System.arraycopy(output, 0, newoutput, 0, output.length); + output = newoutput; + } + char upch = Character.toUpperCase(ch); + if (output != null) { + output[i++] = upch; + } else if (ch != upch) { + if (newString == null) { + newString = StringFactory.newStringFromString(s); + } + newString.setCharAt(o, upch); + } + } else { + int target = index * 3; + char val3 = upperValues[target + 2]; + if (output == null) { + output = new char[count + (count / 6) + 2]; + i = o; + if (newString != null) { + System.arraycopy(newString.toCharArray(), 0, output, 0, i); + } else { + System.arraycopy(s.toCharArray(), 0, output, 0, i); + } + } else if (i + (val3 == 0 ? 1 : 2) >= output.length) { + char[] newoutput = new char[output.length + (count / 6) + 3]; + System.arraycopy(output, 0, newoutput, 0, output.length); + output = newoutput; + } + + char val = upperValues[target]; + output[i++] = val; + val = upperValues[target + 1]; + output[i++] = val; + if (val3 != 0) { + output[i++] = val3; + } + } + } + if (output == null) { + if (newString != null) { + return newString; + } else { + return s; + } + } + return output.length == i || output.length - i < 8 ? new String(0, i, output) : new String(output, 0, i); + } +} diff --git a/libart/src/main/java/java/lang/Class.java b/libart/src/main/java/java/lang/Class.java index 8833776..fc6a0f8 100644 --- a/libart/src/main/java/java/lang/Class.java +++ b/libart/src/main/java/java/lang/Class.java @@ -39,8 +39,6 @@ import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.ArtField; -import java.lang.reflect.ArtMethod; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericDeclaration; @@ -55,6 +53,7 @@ import java.nio.charset.StandardCharsets; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import libcore.reflect.AnnotationAccess; import libcore.reflect.GenericSignatureParser; @@ -124,16 +123,16 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe private static final long serialVersionUID = 3206093459760846163L; - /** defining class loader, or NULL for the "bootstrap" system loader. */ + /** defining class loader, or null for the "bootstrap" system loader. */ private transient ClassLoader classLoader; /** * For array classes, the component class object for instanceof/checkcast (for String[][][], - * this will be String[][]). NULL for non-array classes. + * this will be String[][]). null for non-array classes. */ private transient Class<?> componentType; /** - * DexCache of resolved constant pool entries. Will be null for certain VM-generated classes + * DexCache of resolved constant pool entries. Will be null for certain runtime-generated classes * e.g. arrays and primitive classes. */ private transient DexCache dexCache; @@ -141,19 +140,6 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe /** Short-cut to dexCache.strings */ private transient String[] dexCacheStrings; - /** static, private, and <init> methods. */ - private transient ArtMethod[] directMethods; - - /** - * Instance fields. These describe the layout of the contents of an Object. Note that only the - * fields directly declared by this class are listed in iFields; fields declared by a - * superclass are listed in the superclass's Class.iFields. - * - * All instance fields that refer to objects are guaranteed to be at the beginning of the field - * list. {@link Class#numReferenceInstanceFields} specifies the number of reference fields. - */ - private transient ArtField[] iFields; - /** * The interface table (iftable_) contains pairs of a interface class and an array of the * interface methods. There is one pair per interface supported by this class. That @@ -173,29 +159,42 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe /** Lazily computed name of this class; always prefer calling getName(). */ private transient String name; - /** Static fields */ - private transient ArtField[] sFields; - - /** The superclass, or NULL if this is java.lang.Object, an interface or primitive type. */ + /** The superclass, or null if this is java.lang.Object, an interface or primitive type. */ private transient Class<? super T> superClass; /** If class verify fails, we must return same error on subsequent tries. */ private transient Class<?> verifyErrorClass; - /** Virtual methods defined in this class; invoked through vtable. */ - private transient ArtMethod[] virtualMethods; - /** * Virtual method table (vtable), for use by "invoke-virtual". The vtable from the superclass * is copied in, and virtual methods from our class either replace those from the super or are * appended. For abstract classes, methods may be created in the vtable that aren't in * virtual_ methods_ for miranda methods. */ - private transient ArtMethod[] vtable; + private transient Object vtable; /** access flags; low 16 bits are defined by VM spec */ private transient int accessFlags; + /** static, private, and <init> methods. */ + private transient long directMethods; + + /** + * Instance fields. These describe the layout of the contents of an Object. Note that only the + * fields directly declared by this class are listed in iFields; fields declared by a + * superclass are listed in the superclass's Class.iFields. + * + * All instance fields that refer to objects are guaranteed to be at the beginning of the field + * list. {@link Class#numReferenceInstanceFields} specifies the number of reference fields. + */ + private transient long iFields; + + /** Static fields */ + private transient long sFields; + + /** Virtual methods defined in this class; invoked through vtable. */ + private transient long virtualMethods; + /** * Total size of the Class instance; used when allocating storage on GC heap. * See also {@link Class#objectSize}. @@ -221,32 +220,45 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe */ private transient volatile int dexTypeIndex; + /** Number of direct methods. */ + private transient int numDirectMethods; + + /** Number of instance fields. */ + private transient int numInstanceFields; + /** Number of instance fields that are object references. */ private transient int numReferenceInstanceFields; /** Number of static fields that are object references. */ private transient int numReferenceStaticFields; + /** Number of static fields. */ + private transient int numStaticFields; + + /** Number of virtual methods. */ + private transient int numVirtualMethods; + /** * Total object size; used when allocating storage on GC heap. For interfaces and abstract * classes this will be zero. See also {@link Class#classSize}. */ private transient int objectSize; - /** Primitive type value, or 0 if not a primitive type; set for generated primitive classes. */ + /** + * The lower 16 bits is the primitive type value, or 0 if not a primitive type; set for + * generated primitive classes. + */ private transient int primitiveType; /** Bitmap of offsets of iFields. */ private transient int referenceInstanceOffsets; - /** Bitmap of offsets of sFields. */ - private transient int referenceStaticOffsets; - /** State of class initialization */ private transient int status; private Class() { - // Prevent this class to be instantiated, instance should be created by JVM only + // Prevent this class from being instantiated, + // instances should be created by the runtime only. } /** @@ -284,6 +296,9 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * If the class has not yet been initialized and {@code shouldInitialize} is true, * the class will be initialized. * + * <p>If the provided {@code classLoader} is {@code null}, the bootstrap + * class loader will be used to load the class. + * * @throws ClassNotFoundException * if the requested class cannot be found. * @throws LinkageError @@ -296,7 +311,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe ClassLoader classLoader) throws ClassNotFoundException { if (classLoader == null) { - classLoader = ClassLoader.getSystemClassLoader(); + classLoader = BootClassLoader.getInstance(); } // Catch an Exception thrown by the underlying native code. It wraps // up everything inside a ClassNotFoundException, even if e.g. an @@ -407,24 +422,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe return null; } - ClassLoader loader = getClassLoaderImpl(); - if (loader == null) { - loader = BootClassLoader.getInstance(); - } - return loader; - } - - /** - * This must be provided by the VM vendor, as it is used by other provided - * class implementations in this package. Outside of this class, it is used - * by SecurityManager.classLoaderDepth(), - * currentClassLoader() and currentLoadedClass(). Return the ClassLoader for - * this Class without doing any security checks. The bootstrap ClassLoader - * is returned, unlike getClassLoader() which returns null in place of the - * bootstrap ClassLoader. - */ - ClassLoader getClassLoaderImpl() { - ClassLoader loader = classLoader; + final ClassLoader loader = classLoader; return loader == null ? BootClassLoader.getInstance() : loader; } @@ -456,10 +454,10 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @hide */ public String getDexCacheString(Dex dex, int dexStringIndex) { - String s = dexCacheStrings[dexStringIndex]; + String s = dexCache.getResolvedString(dexStringIndex); if (s == null) { s = dex.strings().get(dexStringIndex).intern(); - dexCacheStrings[dexStringIndex] = s; + dexCache.setResolvedString(dexStringIndex, s); } return s; } @@ -471,13 +469,12 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @hide */ public Class<?> getDexCacheType(Dex dex, int dexTypeIndex) { - Class<?>[] dexCacheResolvedTypes = dexCache.resolvedTypes; - Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex]; + Class<?> resolvedType = dexCache.getResolvedType(dexTypeIndex); if (resolvedType == null) { int descriptorIndex = dex.typeIds().get(dexTypeIndex); String descriptor = getDexCacheString(dex, descriptorIndex); resolvedType = InternalNames.getClass(getClassLoader(), descriptor); - dexCacheResolvedTypes[dexTypeIndex] = resolvedType; + dexCache.setResolvedType(dexTypeIndex, resolvedType); } return resolvedType; } @@ -534,30 +531,12 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe } /** - * Returns the constructor with the given parameters if it is defined by this class; null - * otherwise. This may return a non-public member. + * Returns the constructor with the given parameters if it is defined by this class; + * {@code null} otherwise. This may return a non-public member. * * @param args the types of the parameters to the constructor. */ - private Constructor<T> getDeclaredConstructorInternal(Class<?>[] args) { - if (directMethods != null) { - for (ArtMethod m : directMethods) { - int modifiers = m.getAccessFlags(); - if (Modifier.isStatic(modifiers)) { - // skip <clinit> which is a static constructor - continue; - } - if (!Modifier.isConstructor(modifiers)) { - continue; - } - if (!ArtMethod.equalConstructorParameters(m, args)) { - continue; - } - return new Constructor<T>(m); - } - } - return null; - } + private native Constructor<T> getDeclaredConstructorInternal(Class<?>[] args); /** * Returns an array containing {@code Constructor} objects for all public @@ -568,9 +547,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @see #getDeclaredConstructors() */ public Constructor<?>[] getConstructors() { - ArrayList<Constructor<T>> constructors = new ArrayList(); - getDeclaredConstructors(true, constructors); - return constructors.toArray(new Constructor[constructors.size()]); + return getDeclaredConstructorsInternal(true); } /** @@ -582,28 +559,11 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @see #getConstructors() */ public Constructor<?>[] getDeclaredConstructors() { - ArrayList<Constructor<T>> constructors = new ArrayList(); - getDeclaredConstructors(false, constructors); - return constructors.toArray(new Constructor[constructors.size()]); - } - - private void getDeclaredConstructors(boolean publicOnly, List<Constructor<T>> constructors) { - if (directMethods != null) { - for (ArtMethod m : directMethods) { - int modifiers = m.getAccessFlags(); - if (!publicOnly || Modifier.isPublic(modifiers)) { - if (Modifier.isStatic(modifiers)) { - // skip <clinit> which is a static constructor - continue; - } - if (Modifier.isConstructor(modifiers)) { - constructors.add(new Constructor<T>(m)); - } - } - } - } + return getDeclaredConstructorsInternal(false); } + private native Constructor<?>[] getDeclaredConstructorsInternal(boolean publicOnly); + /** * Returns a {@code Method} object which represents the method matching the * specified name and parameter types that is declared by the class @@ -689,68 +649,13 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe } /** - * Returns the method if it is defined by this class; null otherwise. This may return a + * Returns the method if it is defined by this class; {@code null} otherwise. This may return a * non-public member. * * @param name the method name * @param args the method's parameter types */ - private Method getDeclaredMethodInternal(String name, Class<?>[] args) { - // Covariant return types permit the class to define multiple - // methods with the same name and parameter types. Prefer to - // return a non-synthetic method in such situations. We may - // still return a synthetic method to handle situations like - // escalated visibility. We never return miranda methods that - // were synthesized by the VM. - int skipModifiers = Modifier.MIRANDA | Modifier.SYNTHETIC; - ArtMethod artMethodResult = null; - if (virtualMethods != null) { - for (ArtMethod m : virtualMethods) { - String methodName = ArtMethod.getMethodName(m); - if (!name.equals(methodName)) { - continue; - } - if (!ArtMethod.equalMethodParameters(m, args)) { - continue; - } - int modifiers = m.getAccessFlags(); - if ((modifiers & skipModifiers) == 0) { - return new Method(m); - } - if ((modifiers & Modifier.MIRANDA) == 0) { - // Remember as potential result if it's not a miranda method. - artMethodResult = m; - } - } - } - if (artMethodResult == null) { - if (directMethods != null) { - for (ArtMethod m : directMethods) { - int modifiers = m.getAccessFlags(); - if (Modifier.isConstructor(modifiers)) { - continue; - } - String methodName = ArtMethod.getMethodName(m); - if (!name.equals(methodName)) { - continue; - } - if (!ArtMethod.equalMethodParameters(m, args)) { - continue; - } - if ((modifiers & skipModifiers) == 0) { - return new Method(m); - } - // Direct methods cannot be miranda methods, - // so this potential result must be synthetic. - artMethodResult = m; - } - } - } - if (artMethodResult == null) { - return null; - } - return new Method(artMethodResult); - } + private native Method getDeclaredMethodInternal(String name, Class<?>[] args); /** * Returns an array containing {@code Method} objects for all methods @@ -761,11 +666,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @see #getMethods() */ public Method[] getDeclaredMethods() { - int initial_size = virtualMethods == null ? 0 : virtualMethods.length; - initial_size += directMethods == null ? 0 : directMethods.length; - ArrayList<Method> methods = new ArrayList<Method>(initial_size); - getDeclaredMethodsUnchecked(false, methods); - Method[] result = methods.toArray(new Method[methods.size()]); + Method[] result = getDeclaredMethodsUnchecked(false); for (Method m : result) { // Throw NoClassDefFoundError if types cannot be resolved. m.getReturnType(); @@ -783,30 +684,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @param methods A list to populate with declared methods. * @hide */ - public void getDeclaredMethodsUnchecked(boolean publicOnly, List<Method> methods) { - if (virtualMethods != null) { - for (ArtMethod m : virtualMethods) { - int modifiers = m.getAccessFlags(); - if (!publicOnly || Modifier.isPublic(modifiers)) { - // Add non-miranda virtual methods. - if ((modifiers & Modifier.MIRANDA) == 0) { - methods.add(new Method(m)); - } - } - } - } - if (directMethods != null) { - for (ArtMethod m : directMethods) { - int modifiers = m.getAccessFlags(); - if (!publicOnly || Modifier.isPublic(modifiers)) { - // Add non-constructor direct/static methods. - if (!Modifier.isConstructor(modifiers)) { - methods.add(new Method(m)); - } - } - } - } - } + public native Method[] getDeclaredMethodsUnchecked(boolean publicOnly); /** * Returns an array containing {@code Method} objects for all public methods @@ -836,11 +714,11 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * superclasses, and all implemented interfaces, including overridden methods. */ private void getPublicMethodsInternal(List<Method> result) { - getDeclaredMethodsUnchecked(true, result); + Collections.addAll(result, getDeclaredMethodsUnchecked(true)); if (!isInterface()) { // Search superclasses, for interfaces don't search java.lang.Object. for (Class<?> c = superClass; c != null; c = c.superClass) { - c.getDeclaredMethodsUnchecked(true, result); + Collections.addAll(result, c.getDeclaredMethodsUnchecked(true)); } } // Search iftable which has a flattened and uniqued list of interfaces. @@ -848,7 +726,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe if (iftable != null) { for (int i = 0; i < iftable.length; i += 2) { Class<?> ifc = (Class<?>) iftable[i]; - ifc.getDeclaredMethodsUnchecked(true, result); + Collections.addAll(result, ifc.getDeclaredMethodsUnchecked(true)); } } } @@ -881,18 +759,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @throws NoSuchFieldException if the requested field can not be found. * @see #getField(String) */ - public Field getDeclaredField(String name) throws NoSuchFieldException { - if (name == null) { - throw new NullPointerException("name == null"); - } - Field result = getDeclaredFieldInternal(name); - if (result == null) { - throw new NoSuchFieldException(name); - } else { - result.getType(); // Throw NoClassDefFoundError if type cannot be resolved. - } - return result; - } + public native Field getDeclaredField(String name) throws NoSuchFieldException; /** * Returns an array containing {@code Field} objects for all fields declared @@ -902,17 +769,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * * @see #getFields() */ - public Field[] getDeclaredFields() { - int initial_size = sFields == null ? 0 : sFields.length; - initial_size += iFields == null ? 0 : iFields.length; - ArrayList<Field> fields = new ArrayList(initial_size); - getDeclaredFieldsUnchecked(false, fields); - Field[] result = fields.toArray(new Field[fields.size()]); - for (Field f : result) { - f.getType(); // Throw NoClassDefFoundError if type cannot be resolved. - } - return result; - } + public native Field[] getDeclaredFields(); /** * Populates a list of fields without performing any security or type @@ -922,66 +779,18 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @param fields A list to populate with declared fields. * @hide */ - public void getDeclaredFieldsUnchecked(boolean publicOnly, List<Field> fields) { - if (iFields != null) { - for (ArtField f : iFields) { - if (!publicOnly || Modifier.isPublic(f.getAccessFlags())) { - fields.add(new Field(f)); - } - } - } - if (sFields != null) { - for (ArtField f : sFields) { - if (!publicOnly || Modifier.isPublic(f.getAccessFlags())) { - fields.add(new Field(f)); - } - } - } - } + public native Field[] getDeclaredFieldsUnchecked(boolean publicOnly); /** - * Returns the field if it is defined by this class; null otherwise. This + * Returns the field if it is defined by this class; {@code null} otherwise. This * may return a non-public member. */ - private Field getDeclaredFieldInternal(String name) { - - if (iFields != null) { - final ArtField matched = findByName(name, iFields); - if (matched != null) { - return new Field(matched); - } - } - if (sFields != null) { - final ArtField matched = findByName(name, sFields); - if (matched != null) { - return new Field(matched); - } - } - - return null; - } + private native Field getDeclaredFieldInternal(String name); /** - * Performs a binary search through {@code fields} for a field whose name - * is {@code name}. Returns {@code null} if no matching field exists. + * Returns the subset of getDeclaredFields which are public. */ - private static ArtField findByName(String name, ArtField[] fields) { - int low = 0, high = fields.length - 1; - while (low <= high) { - final int mid = (low + high) >>> 1; - final ArtField f = fields[mid]; - final int result = f.getName().compareTo(name); - if (result < 0) { - low = mid + 1; - } else if (result == 0) { - return f; - } else { - high = mid - 1; - } - } - - return null; - } + private native Field[] getPublicDeclaredFields(); /** * Returns the class that this class is a member of, or {@code null} if this @@ -1080,8 +889,6 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe Field result = getPublicFieldRecursive(name); if (result == null) { throw new NoSuchFieldException(name); - } else { - result.getType(); // Throw NoClassDefFoundError if type cannot be resolved. } return result; } @@ -1098,8 +905,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe // search iftable which has a flattened and uniqued list of interfaces if (ifTable != null) { for (int i = 0; i < ifTable.length; i += 2) { - Class<?> ifc = (Class<?>) ifTable[i]; - Field result = ifc.getPublicFieldRecursive(name); + Field result = ((Class<?>) ifTable[i]).getPublicFieldRecursive(name); if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) { return result; } @@ -1123,11 +929,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe public Field[] getFields() { List<Field> fields = new ArrayList<Field>(); getPublicFieldsRecursive(fields); - Field[] result = fields.toArray(new Field[fields.size()]); - for (Field f : result) { - f.getType(); // Throw NoClassDefFoundError if type cannot be resolved. - } - return result; + return fields.toArray(new Field[fields.size()]); } /** @@ -1137,15 +939,14 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe private void getPublicFieldsRecursive(List<Field> result) { // search superclasses for (Class<?> c = this; c != null; c = c.superClass) { - c.getDeclaredFieldsUnchecked(true, result); + Collections.addAll(result, c.getPublicDeclaredFields()); } // search iftable which has a flattened and uniqued list of interfaces Object[] iftable = ifTable; if (iftable != null) { for (int i = 0; i < iftable.length; i += 2) { - Class<?> ifc = (Class<?>) iftable[i]; - ifc.getDeclaredFieldsUnchecked(true, result); + Collections.addAll(result, ((Class<?>) iftable[i]).getPublicDeclaredFields()); } } } @@ -1295,21 +1096,21 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe } /** - * Returns the simple name of a member or local class, or null otherwise. + * Returns the simple name of a member or local class, or {@code null} otherwise. */ private String getInnerClassName() { return AnnotationAccess.getInnerClassName(this); } /** - * Returns null. + * Returns {@code null}. */ public ProtectionDomain getProtectionDomain() { return null; } /** - * Returns the URL of the given resource, or null if the resource is not found. + * Returns the URL of the given resource, or {@code null} if the resource is not found. * The mapping between the resource name and the URL is managed by the class' class loader. * * @see ClassLoader @@ -1340,8 +1141,8 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe } /** - * Returns a read-only stream for the contents of the given resource, or null if the resource - * is not found. + * Returns a read-only stream for the contents of the given resource, or {@code null} if the + * resource is not found. * The mapping between the resource name and the stream is managed by the class' class loader. * * @see ClassLoader @@ -1372,8 +1173,8 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe } /** - * Returns null. (On Android, a {@code ClassLoader} can load classes from multiple dex files. - * All classes from any given dex file will have the same signers, but different dex + * Returns {@code null}. (On Android, a {@code ClassLoader} can load classes from multiple dex + * files. All classes from any given dex file will have the same signers, but different dex * files may have different signers. This does not fit well with the original * {@code ClassLoader}-based model of {@code getSigners}.) */ @@ -1475,8 +1276,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe Object[] iftable = c.ifTable; if (iftable != null) { for (int i = 0; i < iftable.length; i += 2) { - Class<?> ifc = (Class<?>) iftable[i]; - if (ifc == this) { + if (iftable[i] == this) { return true; } } @@ -1547,7 +1347,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * Tests whether this {@code Class} represents a primitive type. */ public boolean isPrimitive() { - return primitiveType != 0; + return (primitiveType & 0xFFFF) != 0; } /** @@ -1582,33 +1382,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * @throws InstantiationException * if the instance cannot be created. */ - public T newInstance() throws InstantiationException, IllegalAccessException { - if (isPrimitive() || isInterface() || isArray() || Modifier.isAbstract(accessFlags)) { - throw new InstantiationException(this + " cannot be instantiated"); - } - Class<?> caller = VMStack.getStackClass1(); - if (!caller.canAccess(this)) { - throw new IllegalAccessException(this + " is not accessible from " + caller); - } - Constructor<T> init; - try { - init = getDeclaredConstructor(); - } catch (NoSuchMethodException e) { - InstantiationException t = - new InstantiationException(this + " has no zero argument constructor"); - t.initCause(e); - throw t; - } - if (!caller.canAccessMember(this, init.getAccessFlags())) { - throw new IllegalAccessException(init + " is not accessible from " + caller); - } - try { - return init.newInstance(null, init.isAccessible()); - } catch (InvocationTargetException e) { - SneakyThrow.sneakyThrow(e.getCause()); - return null; // Unreachable. - } - } + public native T newInstance() throws InstantiationException, IllegalAccessException; private boolean canAccess(Class<?> c) { if(Modifier.isPublic(c.accessFlags)) { @@ -1664,7 +1438,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe * object was created by the class loader of the class. */ public Package getPackage() { - // TODO This might be a hack, but the VM doesn't have the necessary info. + // TODO This might be a hack, but the runtime doesn't have the necessary info. ClassLoader loader = getClassLoader(); if (loader != null) { String packageName = getPackageName$(); @@ -1674,7 +1448,7 @@ public final class Class<T> implements Serializable, AnnotatedElement, GenericDe } /** - * Returns the package name of this class. This returns null for classes in + * Returns the package name of this class. This returns {@code null} for classes in * the default package. * * @hide diff --git a/libart/src/main/java/java/lang/ClassLoader.java b/libart/src/main/java/java/lang/ClassLoader.java index 9079dc4..dfbeeb5 100644 --- a/libart/src/main/java/java/lang/ClassLoader.java +++ b/libart/src/main/java/java/lang/ClassLoader.java @@ -646,8 +646,8 @@ public abstract class ClassLoader { throw new IllegalArgumentException("Package " + name + " already defined"); } - Package newPackage = new Package(name, specTitle, specVersion, specVendor, implTitle, - implVersion, implVendor, sealBase); + Package newPackage = new Package(this, name, specTitle, specVersion, specVendor, + implTitle, implVersion, implVendor, sealBase); packages.put(name, newPackage); diff --git a/libart/src/main/java/java/lang/Daemons.java b/libart/src/main/java/java/lang/Daemons.java index 485f2c9..a6ac449 100644 --- a/libart/src/main/java/java/lang/Daemons.java +++ b/libart/src/main/java/java/lang/Daemons.java @@ -16,6 +16,8 @@ package java.lang; +import android.system.Os; +import android.system.OsConstants; import dalvik.system.VMRuntime; import java.lang.ref.FinalizerReference; import java.lang.ref.Reference; @@ -40,16 +42,14 @@ public final class Daemons { ReferenceQueueDaemon.INSTANCE.start(); FinalizerDaemon.INSTANCE.start(); FinalizerWatchdogDaemon.INSTANCE.start(); - HeapTrimmerDaemon.INSTANCE.start(); - GCDaemon.INSTANCE.start(); + HeapTaskDaemon.INSTANCE.start(); } public static void stop() { + HeapTaskDaemon.INSTANCE.stop(); ReferenceQueueDaemon.INSTANCE.stop(); FinalizerDaemon.INSTANCE.stop(); FinalizerWatchdogDaemon.INSTANCE.stop(); - HeapTrimmerDaemon.INSTANCE.stop(); - GCDaemon.INSTANCE.stop(); } /** @@ -59,12 +59,17 @@ public final class Daemons { */ private static abstract class Daemon implements Runnable { private Thread thread; + private String name; + + protected Daemon(String name) { + this.name = name; + } public synchronized void start() { if (thread != null) { throw new IllegalStateException("already running"); } - thread = new Thread(ThreadGroup.systemThreadGroup, this, getClass().getSimpleName()); + thread = new Thread(ThreadGroup.systemThreadGroup, this, name); thread.setDaemon(true); thread.start(); } @@ -80,6 +85,10 @@ public final class Daemons { } public synchronized void interrupt() { + interrupt(thread); + } + + public synchronized void interrupt(Thread thread) { if (thread == null) { throw new IllegalStateException("not running"); } @@ -99,7 +108,7 @@ public final class Daemons { if (threadToStop == null) { throw new IllegalStateException("not running"); } - threadToStop.interrupt(); + interrupt(threadToStop); while (true) { try { threadToStop.join(); @@ -125,6 +134,10 @@ public final class Daemons { private static class ReferenceQueueDaemon extends Daemon { private static final ReferenceQueueDaemon INSTANCE = new ReferenceQueueDaemon(); + ReferenceQueueDaemon() { + super("ReferenceQueueDaemon"); + } + @Override public void run() { while (isRunning()) { Reference<?> list; @@ -144,20 +157,14 @@ public final class Daemons { } private void enqueue(Reference<?> list) { - while (list != null) { - Reference<?> reference; - // pendingNext is owned by the GC so no synchronization is required - if (list == list.pendingNext) { - reference = list; - reference.pendingNext = null; - list = null; - } else { - reference = list.pendingNext; - list.pendingNext = reference.pendingNext; - reference.pendingNext = null; - } - reference.enqueueInternal(); - } + Reference<?> start = list; + do { + // pendingNext is owned by the GC so no synchronization is required. + Reference<?> next = list.pendingNext; + list.pendingNext = null; + list.enqueueInternal(); + list = next; + } while (list != start); } } @@ -167,6 +174,10 @@ public final class Daemons { private volatile Object finalizingObject; private volatile long finalizingStartedNanos; + FinalizerDaemon() { + super("FinalizerDaemon"); + } + @Override public void run() { while (isRunning()) { // Take a reference, blocking until one is ready or the thread should stop @@ -207,6 +218,10 @@ public final class Daemons { private static class FinalizerWatchdogDaemon extends Daemon { private static final FinalizerWatchdogDaemon INSTANCE = new FinalizerWatchdogDaemon(); + FinalizerWatchdogDaemon() { + super("FinalizerWatchdogDaemon"); + } + @Override public void run() { while (isRunning()) { boolean waitSuccessful = waitForObject(); @@ -282,6 +297,14 @@ public final class Daemons { // We use the stack from where finalize() was running to show where it was stuck. syntheticException.setStackTrace(FinalizerDaemon.INSTANCE.getStackTrace()); Thread.UncaughtExceptionHandler h = Thread.getDefaultUncaughtExceptionHandler(); + // Send SIGQUIT to get native stack traces. + try { + Os.kill(Os.getpid(), OsConstants.SIGQUIT); + // Sleep a few seconds to let the stack traces print. + Thread.sleep(5000); + } catch (Exception e) { + System.logE("failed to send SIGQUIT", e); + } if (h == null) { // If we have no handler, log and exit. System.logE(message, syntheticException); @@ -294,59 +317,42 @@ public final class Daemons { } } - // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap. + // Adds a heap trim task ot the heap event processor, not called from java. Left for + // compatibility purposes due to reflection. public static void requestHeapTrim() { - synchronized (HeapTrimmerDaemon.INSTANCE) { - HeapTrimmerDaemon.INSTANCE.notify(); - } - } - - private static class HeapTrimmerDaemon extends Daemon { - private static final HeapTrimmerDaemon INSTANCE = new HeapTrimmerDaemon(); - - @Override public void run() { - while (isRunning()) { - try { - synchronized (this) { - wait(); - } - VMRuntime.getRuntime().trimHeap(); - } catch (InterruptedException ignored) { - } - } - } + VMRuntime.getRuntime().requestHeapTrim(); } - // Invoked by the GC to request that the HeapTrimmerDaemon thread attempt to trim the heap. + // Adds a concurrent GC request task ot the heap event processor, not called from java. Left + // for compatibility purposes due to reflection. public static void requestGC() { - GCDaemon.INSTANCE.requestGC(); + VMRuntime.getRuntime().requestConcurrentGC(); } - private static class GCDaemon extends Daemon { - private static final GCDaemon INSTANCE = new GCDaemon(); - private static final AtomicBoolean atomicBoolean = new AtomicBoolean(); + private static class HeapTaskDaemon extends Daemon { + private static final HeapTaskDaemon INSTANCE = new HeapTaskDaemon(); - public void requestGC() { - if (atomicBoolean.getAndSet(true)) { - return; - } - synchronized (this) { - notify(); - } - atomicBoolean.set(false); + HeapTaskDaemon() { + super("HeapTaskDaemon"); + } + + // Overrides the Daemon.interupt method which is called from Daemons.stop. + public synchronized void interrupt(Thread thread) { + VMRuntime.getRuntime().stopHeapTaskProcessor(); } @Override public void run() { - while (isRunning()) { - try { - synchronized (this) { - // Wait until a request comes in. - wait(); - } - VMRuntime.getRuntime().concurrentGC(); - } catch (InterruptedException ignored) { + synchronized (this) { + if (isRunning()) { + // Needs to be synchronized or else we there is a race condition where we start + // the thread, call stopHeapTaskProcessor before we start the heap task + // processor, resulting in a deadlock since startHeapTaskProcessor restarts it + // while the other thread is waiting in Daemons.stop(). + VMRuntime.getRuntime().startHeapTaskProcessor(); } } + // This runs tasks until we are stopped and there is no more pending task. + VMRuntime.getRuntime().runHeapTasks(); } } } diff --git a/libart/src/main/java/java/lang/DexCache.java b/libart/src/main/java/java/lang/DexCache.java index e4caffa..73e35cd 100644 --- a/libart/src/main/java/java/lang/DexCache.java +++ b/libart/src/main/java/java/lang/DexCache.java @@ -33,8 +33,6 @@ package java.lang; import com.android.dex.Dex; -import java.lang.reflect.ArtField; -import java.lang.reflect.ArtMethod; /** * A dex cache holds resolved copies of strings, fields, methods, and classes from the dexfile. @@ -47,16 +45,16 @@ final class DexCache { String location; /** - * References to fields as they become resolved following interpreter semantics. May refer to - * fields defined in other dex files. + * References to methods as they become resolved following interpreter semantics. May refer to + * methods defined in other dex files. */ - ArtField[] resolvedFields; + Object resolvedMethods; /** - * References to methods as they become resolved following interpreter semantics. May refer to - * methods defined in other dex files. + * References to fields as they become resolved following interpreter semantics. May refer to + * fields defined in other dex files. Either an int array or long array. */ - ArtMethod[] resolvedMethods; + private Object resolvedFields; /** * References to types as they become resolved following interpreter semantics. May refer to @@ -89,6 +87,10 @@ final class DexCache { return result; } + native Class<?> getResolvedType(int typeIndex); + native String getResolvedString(int stringIndex); + native void setResolvedType(int typeIndex, Class<?> type); + native void setResolvedString(int stringIndex, String string); private native Dex getDexNative(); } diff --git a/libart/src/main/java/java/lang/Object.java b/libart/src/main/java/java/lang/Object.java index 20fdbf9..5b820e3 100644 --- a/libart/src/main/java/java/lang/Object.java +++ b/libart/src/main/java/java/lang/Object.java @@ -274,10 +274,11 @@ public class Object { */ public int hashCode() { int lockWord = shadow$_monitor_; - final int lockWordMask = 0xC0000000; // Top 2 bits. + final int lockWordStateMask = 0xC0000000; // Top 2 bits. final int lockWordStateHash = 0x80000000; // Top 2 bits are value 2 (kStateHash). - if ((lockWord & lockWordMask) == lockWordStateHash) { - return lockWord & ~lockWordMask; + final int lockWordHashMask = 0x0FFFFFFF; // Low 28 bits. + if ((lockWord & lockWordStateMask) == lockWordStateHash) { + return lockWord & lockWordHashMask; } return System.identityHashCode(this); } diff --git a/libart/src/main/java/java/lang/String.java b/libart/src/main/java/java/lang/String.java index 0107b6e..0875d1a 100644 --- a/libart/src/main/java/java/lang/String.java +++ b/libart/src/main/java/java/lang/String.java @@ -22,12 +22,12 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; -import java.nio.charset.Charsets; import java.util.Arrays; import java.util.Comparator; import java.util.Formatter; import java.util.Locale; import java.util.regex.Pattern; +import libcore.util.CharsetUtils; import libcore.util.EmptyArray; /** @@ -35,23 +35,6 @@ import libcore.util.EmptyArray; * See {@link Character} for details about the relationship between {@code char} and * Unicode code points. * - * <a name="backing_array"><h3>Backing Arrays</h3></a> - * This class is implemented using a {@code char[]}. The length of the array may exceed - * the length of the string. For example, the string "Hello" may be backed by - * the array {@code ['H', 'e', 'l', 'l', 'o', 'W'. 'o', 'r', 'l', 'd']} with - * offset 0 and length 5. - * - * <p>Multiple strings can share the same {@code char[]} because strings are immutable. - * The {@link #substring} method <strong>always</strong> returns a string that - * shares the backing array of its source string. Generally this is an - * optimization: fewer {@code char[]}s need to be allocated, and less copying - * is necessary. But this can also lead to unwanted heap retention. Taking a - * short substring of long string means that the long shared {@code char[]} won't be - * garbage until both strings are garbage. This typically happens when parsing - * small substrings out of a large input. To avoid this where necessary, call - * {@code new String(longString.subString(...))}. The string copy constructor - * always ensures that the backing array is no larger than necessary. - * * @see StringBuffer * @see StringBuilder * @see Charset @@ -93,10 +76,6 @@ public final class String implements Serializable, Comparable<String>, CharSeque } } - private final char[] value; - - private final int offset; - private final int count; private int hashCode; @@ -105,9 +84,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque * Creates an empty string. */ public String() { - value = EmptyArray.CHAR; - offset = 0; - count = 0; + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -116,7 +93,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque */ @FindBugsSuppressWarnings("DM_DEFAULT_ENCODING") public String(byte[] data) { - this(data, 0, data.length); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -133,7 +110,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque */ @Deprecated public String(byte[] data, int high) { - this(data, high, 0, data.length); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -146,7 +123,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque * if {@code byteCount < 0 || offset < 0 || offset + byteCount > data.length}. */ public String(byte[] data, int offset, int byteCount) { - this(data, offset, byteCount, Charset.defaultCharset()); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -162,16 +139,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque */ @Deprecated public String(byte[] data, int high, int offset, int byteCount) { - if ((offset | byteCount) < 0 || byteCount > data.length - offset) { - throw failedBoundsCheck(data.length, offset, byteCount); - } - this.offset = 0; - this.value = new char[byteCount]; - this.count = byteCount; - high <<= 8; - for (int i = 0; i < count; i++) { - value[i] = (char) (high + (data[offset++] & 0xff)); - } + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -188,7 +156,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque * if the named charset is not supported. */ public String(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException { - this(data, offset, byteCount, Charset.forNameUEE(charsetName)); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -203,7 +171,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque * if {@code charsetName} is not supported. */ public String(byte[] data, String charsetName) throws UnsupportedEncodingException { - this(data, 0, data.length, Charset.forNameUEE(charsetName)); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -221,144 +189,7 @@ public final class String implements Serializable, Comparable<String>, CharSeque * @since 1.6 */ public String(byte[] data, int offset, int byteCount, Charset charset) { - if ((offset | byteCount) < 0 || byteCount > data.length - offset) { - throw failedBoundsCheck(data.length, offset, byteCount); - } - - // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because 'count' and - // 'value' are final. - String canonicalCharsetName = charset.name(); - if (canonicalCharsetName.equals("UTF-8")) { - byte[] d = data; - char[] v = new char[byteCount]; - - int idx = offset; - int last = offset + byteCount; - int s = 0; -outer: - while (idx < last) { - byte b0 = d[idx++]; - if ((b0 & 0x80) == 0) { - // 0xxxxxxx - // Range: U-00000000 - U-0000007F - int val = b0 & 0xff; - v[s++] = (char) val; - } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) || - ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) { - int utfCount = 1; - if ((b0 & 0xf0) == 0xe0) utfCount = 2; - else if ((b0 & 0xf8) == 0xf0) utfCount = 3; - else if ((b0 & 0xfc) == 0xf8) utfCount = 4; - else if ((b0 & 0xfe) == 0xfc) utfCount = 5; - - // 110xxxxx (10xxxxxx)+ - // Range: U-00000080 - U-000007FF (count == 1) - // Range: U-00000800 - U-0000FFFF (count == 2) - // Range: U-00010000 - U-001FFFFF (count == 3) - // Range: U-00200000 - U-03FFFFFF (count == 4) - // Range: U-04000000 - U-7FFFFFFF (count == 5) - - if (idx + utfCount > last) { - v[s++] = REPLACEMENT_CHAR; - continue; - } - - // Extract usable bits from b0 - int val = b0 & (0x1f >> (utfCount - 1)); - for (int i = 0; i < utfCount; ++i) { - byte b = d[idx++]; - if ((b & 0xc0) != 0x80) { - v[s++] = REPLACEMENT_CHAR; - idx--; // Put the input char back - continue outer; - } - // Push new bits in from the right side - val <<= 6; - val |= b & 0x3f; - } - - // Note: Java allows overlong char - // specifications To disallow, check that val - // is greater than or equal to the minimum - // value for each count: - // - // count min value - // ----- ---------- - // 1 0x80 - // 2 0x800 - // 3 0x10000 - // 4 0x200000 - // 5 0x4000000 - - // Allow surrogate values (0xD800 - 0xDFFF) to - // be specified using 3-byte UTF values only - if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) { - v[s++] = REPLACEMENT_CHAR; - continue; - } - - // Reject chars greater than the Unicode maximum of U+10FFFF. - if (val > 0x10FFFF) { - v[s++] = REPLACEMENT_CHAR; - continue; - } - - // Encode chars from U+10000 up as surrogate pairs - if (val < 0x10000) { - v[s++] = (char) val; - } else { - int x = val & 0xffff; - int u = (val >> 16) & 0x1f; - int w = (u - 1) & 0xffff; - int hi = 0xd800 | (w << 6) | (x >> 10); - int lo = 0xdc00 | (x & 0x3ff); - v[s++] = (char) hi; - v[s++] = (char) lo; - } - } else { - // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff - v[s++] = REPLACEMENT_CHAR; - } - } - - if (s == byteCount) { - // We guessed right, so we can use our temporary array as-is. - this.offset = 0; - this.value = v; - this.count = s; - } else { - // Our temporary array was too big, so reallocate and copy. - this.offset = 0; - this.value = new char[s]; - this.count = s; - System.arraycopy(v, 0, value, 0, s); - } - } else if (canonicalCharsetName.equals("ISO-8859-1")) { - this.offset = 0; - this.value = new char[byteCount]; - this.count = byteCount; - Charsets.isoLatin1BytesToChars(data, offset, byteCount, value); - } else if (canonicalCharsetName.equals("US-ASCII")) { - this.offset = 0; - this.value = new char[byteCount]; - this.count = byteCount; - Charsets.asciiBytesToChars(data, offset, byteCount, value); - } else { - CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount)); - this.offset = 0; - this.count = cb.length(); - if (count > 0) { - // We could use cb.array() directly, but that would mean we'd have to trust - // the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later, - // which would break String's immutability guarantee. It would also tend to - // mean that we'd be wasting memory because CharsetDecoder doesn't trim the - // array. So we copy. - this.value = new char[count]; - System.arraycopy(cb.array(), 0, value, 0, count); - } else { - this.value = EmptyArray.CHAR; - } - } + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -368,7 +199,7 @@ outer: * @since 1.6 */ public String(byte[] data, Charset charset) { - this(data, 0, data.length, charset); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -379,7 +210,7 @@ outer: * @throws NullPointerException if {@code data == null} */ public String(char[] data) { - this(data, 0, data.length); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -393,36 +224,25 @@ outer: * if {@code charCount < 0 || offset < 0 || offset + charCount > data.length} */ public String(char[] data, int offset, int charCount) { - if ((offset | charCount) < 0 || charCount > data.length - offset) { - throw failedBoundsCheck(data.length, offset, charCount); - } - this.offset = 0; - this.value = new char[charCount]; - this.count = charCount; - System.arraycopy(data, offset, value, 0, count); + throw new UnsupportedOperationException("Use StringFactory instead."); } /* * Internal version of the String(char[], int, int) constructor. - * Does not range check, null check, or copy the array. + * Does not range check or null check. */ + // TODO: Replace calls to this with calls to StringFactory, will require + // splitting other files in java.lang. String(int offset, int charCount, char[] chars) { - this.value = chars; - this.offset = offset; - this.count = charCount; + throw new UnsupportedOperationException("Use StringFactory instead."); } /** - * Constructs a copy of the given string. - * The returned string's <a href="#backing_array">backing array</a> - * is no larger than necessary. + * Constructs a new string with the same sequence of characters as {@code + * toCopy}. */ public String(String toCopy) { - value = (toCopy.value.length == toCopy.count) - ? toCopy.value - : Arrays.copyOfRange(toCopy.value, toCopy.offset, toCopy.offset + toCopy.length()); - offset = 0; - count = value.length; + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -430,11 +250,7 @@ outer: * {@code StringBuffer}. */ public String(StringBuffer stringBuffer) { - offset = 0; - synchronized (stringBuffer) { - value = stringBuffer.shareValue(); - count = stringBuffer.length(); - } + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -451,20 +267,7 @@ outer: * @since 1.5 */ public String(int[] codePoints, int offset, int count) { - if (codePoints == null) { - throw new NullPointerException("codePoints == null"); - } - if ((offset | count) < 0 || count > codePoints.length - offset) { - throw failedBoundsCheck(codePoints.length, offset, count); - } - this.offset = 0; - this.value = new char[count * 2]; - int end = offset + count; - int c = 0; - for (int i = offset; i < end; i++) { - c += Character.toChars(codePoints[i], this.value, c); - } - this.count = c; + throw new UnsupportedOperationException("Use StringFactory instead."); } /** @@ -476,25 +279,16 @@ outer: * @since 1.5 */ public String(StringBuilder stringBuilder) { - if (stringBuilder == null) { - throw new NullPointerException("stringBuilder == null"); - } - this.offset = 0; - this.count = stringBuilder.length(); - this.value = new char[this.count]; - stringBuilder.getChars(0, this.count, this.value, 0); + throw new UnsupportedOperationException("Use StringFactory instead."); } /** * Returns the {@code char} at {@code index}. * @throws IndexOutOfBoundsException if {@code index < 0} or {@code index >= length()}. */ - public char charAt(int index) { - if (index < 0 || index >= count) { - throw indexAndLength(index); - } - return value[offset + index]; - } + public native char charAt(int index); + + native void setCharAt(int index, char c); private StringIndexOutOfBoundsException indexAndLength(int index) { throw new StringIndexOutOfBoundsException(this, index); @@ -557,12 +351,11 @@ outer: * if {@code string} is {@code null}. */ public int compareToIgnoreCase(String string) { - int o1 = offset, o2 = string.offset, result; - int end = offset + (count < string.count ? count : string.count); + int result; + int end = count < string.count ? count : string.count; char c1, c2; - char[] target = string.value; - while (o1 < end) { - if ((c1 = value[o1++]) == (c2 = target[o2++])) { + for (int i = 0; i < end; ++i) { + if ((c1 = charAt(i)) == (c2 = string.charAt(i))) { continue; } c1 = foldCase(c1); @@ -582,15 +375,7 @@ outer: * @return a new string which is the concatenation of this string and the * specified string. */ - public String concat(String string) { - if (string.count > 0 && count > 0) { - char[] buffer = new char[count + string.count]; - System.arraycopy(value, offset, buffer, 0, count); - System.arraycopy(string.value, string.offset, buffer, count, string.count); - return new String(0, buffer.length, buffer); - } - return count == 0 ? string : this; - } + public native String concat(String string); /** * Creates a new string by copying the given {@code char[]}. @@ -601,7 +386,7 @@ outer: * if {@code data} is {@code null}. */ public static String copyValueOf(char[] data) { - return new String(data, 0, data.length); + return StringFactory.newStringFromChars(data, 0, data.length); } /** @@ -616,7 +401,7 @@ outer: * data.length}. */ public static String copyValueOf(char[] data, int start, int length) { - return new String(data, start, length); + return StringFactory.newStringFromChars(data, start, length); } /** @@ -654,16 +439,10 @@ outer: if (hashCode() != s.hashCode()) { return false; } - char[] value1 = value; - int offset1 = offset; - char[] value2 = s.value; - int offset2 = s.offset; - for (int end = offset1 + count; offset1 < end; ) { - if (value1[offset1] != value2[offset2]) { + for (int i = 0; i < count; ++i) { + if (charAt(i) != s.charAt(i)) { return false; } - offset1++; - offset2++; } return true; } else { @@ -686,12 +465,9 @@ outer: if (string == null || count != string.count) { return false; } - int o1 = offset, o2 = string.offset; - int end = offset + count; - char[] target = string.value; - while (o1 < end) { - char c1 = value[o1++]; - char c2 = target[o2++]; + for (int i = 0; i < count; ++i) { + char c1 = charAt(i); + char c2 = string.charAt(i); if (c1 != c2 && foldCase(c1) != foldCase(c2)) { return false; } @@ -721,10 +497,9 @@ outer: @Deprecated public void getBytes(int start, int end, byte[] data, int index) { if (start >= 0 && start <= end && end <= count) { - end += offset; try { - for (int i = offset + start; i < end; i++) { - data[index++] = (byte) value[i]; + for (int i = start; i < end; ++i) { + data[index++] = (byte) charAt(i); } } catch (ArrayIndexOutOfBoundsException ignored) { throw failedBoundsCheck(data.length, index, end - start); @@ -772,16 +547,15 @@ outer: public byte[] getBytes(Charset charset) { String canonicalCharsetName = charset.name(); if (canonicalCharsetName.equals("UTF-8")) { - return Charsets.toUtf8Bytes(value, offset, count); + return CharsetUtils.toUtf8Bytes(this, 0, count); } else if (canonicalCharsetName.equals("ISO-8859-1")) { - return Charsets.toIsoLatin1Bytes(value, offset, count); + return CharsetUtils.toIsoLatin1Bytes(this, 0, count); } else if (canonicalCharsetName.equals("US-ASCII")) { - return Charsets.toAsciiBytes(value, offset, count); + return CharsetUtils.toAsciiBytes(this, 0, count); } else if (canonicalCharsetName.equals("UTF-16BE")) { - return Charsets.toBigEndianUtf16Bytes(value, offset, count); + return CharsetUtils.toBigEndianUtf16Bytes(this, 0, count); } else { - CharBuffer chars = CharBuffer.wrap(this.value, this.offset, this.count); - ByteBuffer buffer = charset.encode(chars.asReadOnlyBuffer()); + ByteBuffer buffer = charset.encode(this); byte[] bytes = new byte[buffer.limit()]; buffer.get(bytes); return bytes; @@ -809,7 +583,16 @@ outer: */ public void getChars(int start, int end, char[] buffer, int index) { if (start >= 0 && start <= end && end <= count) { - System.arraycopy(value, start + offset, buffer, index, end - start); + if (buffer == null) { + throw new NullPointerException("buffer == null"); + } + if (index < 0) { + throw new IndexOutOfBoundsException("index < 0"); + } + if (end - start > buffer.length - index) { + throw new ArrayIndexOutOfBoundsException("end - start > buffer.length - index"); + } + getCharsNoCheck(start, end, buffer, index); } else { // We throw StringIndexOutOfBoundsException rather than System.arraycopy's AIOOBE. throw startEndAndLength(start, end); @@ -821,9 +604,7 @@ outer: * within the java.lang package only. The caller is responsible for * ensuring that start >= 0 && start <= end && end <= count. */ - void _getChars(int start, int end, char[] buffer, int index) { - System.arraycopy(value, start + offset, buffer, index, end - start); - } + native void getCharsNoCheck(int start, int end, char[] buffer, int index); @Override public int hashCode() { int hash = hashCode; @@ -831,10 +612,8 @@ outer: if (count == 0) { return 0; } - final int end = count + offset; - final char[] chars = value; - for (int i = offset; i < end; ++i) { - hash = 31*hash + chars[i]; + for (int i = 0; i < count; ++i) { + hash = 31 * hash + charAt(i); } hashCode = hash; } @@ -893,21 +672,17 @@ outer: if (subCount > _count) { return -1; } - char[] target = string.value; - int subOffset = string.offset; - char firstChar = target[subOffset]; - int end = subOffset + subCount; + char firstChar = string.charAt(0); while (true) { int i = indexOf(firstChar, start); if (i == -1 || subCount + i > _count) { return -1; // handles subCount > count || start >= count } - int o1 = offset + i, o2 = subOffset; - char[] _value = value; - while (++o2 < end && _value[++o1] == target[o2]) { + int o1 = i, o2 = 0; + while (++o2 < subCount && charAt(++o1) == string.charAt(o2)) { // Intentionally empty } - if (o2 == end) { + if (o2 == subCount) { return i; } start = i + 1; @@ -934,21 +709,17 @@ outer: if (subCount + start > _count) { return -1; } - char[] target = subString.value; - int subOffset = subString.offset; - char firstChar = target[subOffset]; - int end = subOffset + subCount; + char firstChar = subString.charAt(0); while (true) { int i = indexOf(firstChar, start); if (i == -1 || subCount + i > _count) { return -1; // handles subCount > count || start >= count } - int o1 = offset + i, o2 = subOffset; - char[] _value = value; - while (++o2 < end && _value[++o1] == target[o2]) { + int o1 = i, o2 = 0; + while (++o2 < subCount && charAt(++o1) == subString.charAt(o2)) { // Intentionally empty } - if (o2 == end) { + if (o2 == subCount) { return i; } start = i + 1; @@ -991,11 +762,9 @@ outer: return lastIndexOfSupplementary(c, Integer.MAX_VALUE); } int _count = count; - int _offset = offset; - char[] _value = value; - for (int i = _offset + _count - 1; i >= _offset; --i) { - if (_value[i] == c) { - return i - _offset; + for (int i = _count - 1; i >= 0; --i) { + if (charAt(i) == c) { + return i; } } return -1; @@ -1011,15 +780,13 @@ outer: return lastIndexOfSupplementary(c, start); } int _count = count; - int _offset = offset; - char[] _value = value; if (start >= 0) { if (start >= _count) { start = _count - 1; } - for (int i = _offset + start; i >= _offset; --i) { - if (_value[i] == c) { - return i - _offset; + for (int i = start; i >= 0; --i) { + if (charAt(i) == c) { + return i; } } } @@ -1031,7 +798,7 @@ outer: return -1; } char[] chars = Character.toChars(c); - String needle = new String(0, chars.length, chars); + String needle = StringFactory.newStringFromChars(0, chars.length, chars); return lastIndexOf(needle, start); } @@ -1065,20 +832,17 @@ outer: start = count - subCount; } // count and subCount are both >= 1 - char[] target = subString.value; - int subOffset = subString.offset; - char firstChar = target[subOffset]; - int end = subOffset + subCount; + char firstChar = subString.charAt(0); while (true) { int i = lastIndexOf(firstChar, start); if (i == -1) { return -1; } - int o1 = offset + i, o2 = subOffset; - while (++o2 < end && value[++o1] == target[o2]) { + int o1 = i, o2 = 0; + while (++o2 < subCount && charAt(++o1) == subString.charAt(o2)) { // Intentionally empty } - if (o2 == end) { + if (o2 == subCount) { return i; } start = i - 1; @@ -1121,11 +885,8 @@ outer: if (length <= 0) { return true; } - int o1 = offset + thisStart, o2 = string.offset + start; - char[] value1 = value; - char[] value2 = string.value; for (int i = 0; i < length; ++i) { - if (value1[o1 + i] != value2[o2 + i]) { + if (charAt(thisStart + i) != string.charAt(start + i)) { return false; } } @@ -1164,13 +925,10 @@ outer: if (start < 0 || length > string.count - start) { return false; } - thisStart += offset; - start += string.offset; int end = thisStart + length; - char[] target = string.value; while (thisStart < end) { - char c1 = value[thisStart++]; - char c2 = target[start++]; + char c1 = charAt(thisStart++); + char c2 = string.charAt(start++); if (c1 != c2 && foldCase(c1) != foldCase(c2)) { return false; } @@ -1182,29 +940,20 @@ outer: * Returns a copy of this string after replacing occurrences of the given {@code char} with another. */ public String replace(char oldChar, char newChar) { - char[] buffer = value; - int _offset = offset; + String s = null; int _count = count; - - int idx = _offset; - int last = _offset + _count; boolean copied = false; - while (idx < last) { - if (buffer[idx] == oldChar) { + for (int i = 0; i < _count; ++i) { + if (charAt(i) == oldChar) { if (!copied) { - char[] newBuffer = new char[_count]; - System.arraycopy(buffer, _offset, newBuffer, 0, _count); - buffer = newBuffer; - idx -= _offset; - last -= _offset; + s = StringFactory.newStringFromString(this); copied = true; } - buffer[idx] = newChar; + s.setCharAt(i, newChar); } - idx++; } - return copied ? new String(0, count, buffer) : this; + return copied ? s : this; } /** @@ -1241,9 +990,8 @@ outer: int resultLength = count + (count + 1) * replacementString.length(); StringBuilder result = new StringBuilder(resultLength); result.append(replacementString); - int end = offset + count; - for (int i = offset; i != end; ++i) { - result.append(value[i]); + for (int i = 0; i != count; ++i) { + result.append(charAt(i)); result.append(replacementString); } return result.toString(); @@ -1252,15 +1000,21 @@ outer: StringBuilder result = new StringBuilder(count); int searchStart = 0; do { - // Copy chars before the match... - result.append(value, offset + searchStart, matchStart - searchStart); + // Copy characters before the match... + // TODO: Perform this faster than one char at a time? + for (int i = searchStart; i < matchStart; ++i) { + result.append(charAt(i)); + } // Insert the replacement... result.append(replacementString); // And skip over the match... searchStart = matchStart + targetLength; } while ((matchStart = indexOf(targetString, searchStart)) != -1); // Copy any trailing chars... - result.append(value, offset + searchStart, count - searchStart); + // TODO: Perform this faster than one char at a time? + for (int i = searchStart; i < count; ++i) { + result.append(charAt(i)); + } return result.toString(); } @@ -1308,7 +1062,7 @@ outer: return this; } if (start >= 0 && start <= count) { - return new String(offset + start, count - start, value); + return fastSubstring(start, count - start); } throw indexAndLength(start); } @@ -1328,21 +1082,19 @@ outer: } // Fast range check. if (start >= 0 && start <= end && end <= count) { - return new String(offset + start, end - start, value); + return fastSubstring(start, end - start); } throw startEndAndLength(start, end); } + private native String fastSubstring(int start, int length); + /** * Returns a new {@code char} array containing a copy of the {@code char}s in this string. * This is expensive and rarely useful. If you just want to iterate over the {@code char}s in * the string, use {@link #charAt} instead. */ - public char[] toCharArray() { - char[] buffer = new char[count]; - System.arraycopy(value, offset, buffer, 0, count); - return buffer; - } + public native char[] toCharArray(); /** * Converts this string to lower case, using the rules of the user's default locale. @@ -1351,7 +1103,7 @@ outer: * @return a new lower case string, or {@code this} if it's already all lower case. */ public String toLowerCase() { - return CaseMapper.toLowerCase(Locale.getDefault(), this, value, offset, count); + return CaseMapper.toLowerCase(Locale.getDefault(), this); } /** @@ -1368,7 +1120,7 @@ outer: * @return a new lower case string, or {@code this} if it's already all lower case. */ public String toLowerCase(Locale locale) { - return CaseMapper.toLowerCase(locale, this, value, offset, count); + return CaseMapper.toLowerCase(locale, this); } /** @@ -1386,7 +1138,7 @@ outer: * @return a new upper case string, or {@code this} if it's already all upper case. */ public String toUpperCase() { - return CaseMapper.toUpperCase(Locale.getDefault(), this, value, offset, count); + return CaseMapper.toUpperCase(Locale.getDefault(), this, count); } /** @@ -1403,7 +1155,7 @@ outer: * @return a new upper case string, or {@code this} if it's already all upper case. */ public String toUpperCase(Locale locale) { - return CaseMapper.toUpperCase(locale, this, value, offset, count); + return CaseMapper.toUpperCase(locale, this, count); } /** @@ -1411,18 +1163,18 @@ outer: * the beginning or end. */ public String trim() { - int start = offset, last = offset + count - 1; + int start = 0, last = count - 1; int end = last; - while ((start <= end) && (value[start] <= ' ')) { + while ((start <= end) && (charAt(start) <= ' ')) { start++; } - while ((end >= start) && (value[end] <= ' ')) { + while ((end >= start) && (charAt(end) <= ' ')) { end--; } - if (start == offset && end == last) { + if (start == 0 && end == last) { return this; } - return new String(start, end - start + 1, value); + return fastSubstring(start, end - start + 1); } /** @@ -1434,7 +1186,7 @@ outer: * if {@code data} is {@code null}. */ public static String valueOf(char[] data) { - return new String(data, 0, data.length); + return StringFactory.newStringFromChars(data, 0, data.length); } /** @@ -1448,7 +1200,7 @@ outer: * if {@code data} is {@code null}. */ public static String valueOf(char[] data, int start, int length) { - return new String(data, start, length); + return StringFactory.newStringFromChars(data, start, length); } /** @@ -1457,9 +1209,9 @@ outer: public static String valueOf(char value) { String s; if (value < 128) { - s = new String(value, 1, ASCII); + s = StringFactory.newStringFromChars(value, 1, ASCII); } else { - s = new String(0, 1, new char[] { value }); + s = StringFactory.newStringFromChars(0, 1, new char[] { value }); } s.hashCode = value; return s; @@ -1533,7 +1285,8 @@ outer: if (count != size) { return false; } - return regionMatches(0, new String(0, size, sb.getValue()), 0, size); + String s = StringFactory.newStringFromChars(0, size, sb.getValue()); + return regionMatches(0, s, 0, size); } } @@ -1682,7 +1435,7 @@ outer: if (index < 0 || index >= count) { throw indexAndLength(index); } - return Character.codePointAt(value, offset + index, offset + count); + return Character.codePointAt(this, index); } /** @@ -1696,7 +1449,7 @@ outer: if (index < 1 || index > count) { throw indexAndLength(index); } - return Character.codePointBefore(value, offset + index, offset); + return Character.codePointBefore(this, index); } /** @@ -1717,7 +1470,7 @@ outer: if (start < 0 || end > count || start > end) { throw startEndAndLength(start, end); } - return Character.codePointCount(value, offset + start, end - start); + return Character.codePointCount(this, start, end); } /** @@ -1748,9 +1501,7 @@ outer: * @since 1.5 */ public int offsetByCodePoints(int index, int codePointOffset) { - int s = index + offset; - int r = Character.offsetByCodePoints(value, offset, count, s, codePointOffset); - return r - offset; + return Character.offsetByCodePoints(this, index, codePointOffset); } /** @@ -1816,31 +1567,26 @@ outer: @SuppressWarnings("unused") private static int indexOf(String haystackString, String needleString, int cache, int md2, char lastChar) { - char[] haystack = haystackString.value; - int haystackOffset = haystackString.offset; int haystackLength = haystackString.count; - char[] needle = needleString.value; - int needleOffset = needleString.offset; int needleLength = needleString.count; int needleLengthMinus1 = needleLength - 1; - int haystackEnd = haystackOffset + haystackLength; - outer_loop: for (int i = haystackOffset + needleLengthMinus1; i < haystackEnd;) { - if (lastChar == haystack[i]) { + outer_loop: for (int i = needleLengthMinus1; i < haystackLength;) { + if (lastChar == haystackString.charAt(i)) { for (int j = 0; j < needleLengthMinus1; ++j) { - if (needle[j + needleOffset] != haystack[i + j - - needleLengthMinus1]) { + if (needleString.charAt(j) != + haystackString.charAt(i + j - needleLengthMinus1)) { int skip = 1; - if ((cache & (1 << haystack[i])) == 0) { + if ((cache & (1 << haystackString.charAt(i))) == 0) { skip += j; } i += Math.max(md2, skip); continue outer_loop; } } - return i - needleLengthMinus1 - haystackOffset; + return i - needleLengthMinus1; } - if ((cache & (1 << haystack[i])) == 0) { + if ((cache & (1 << haystackString.charAt(i))) == 0) { i += needleLengthMinus1; } i++; diff --git a/libart/src/main/java/java/lang/StringFactory.java b/libart/src/main/java/java/lang/StringFactory.java new file mode 100644 index 0000000..4fc3eba --- /dev/null +++ b/libart/src/main/java/java/lang/StringFactory.java @@ -0,0 +1,251 @@ +/* + * 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 + * + * 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 java.lang; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Comparator; +import libcore.util.CharsetUtils; +import libcore.util.EmptyArray; + +/** + * Class used to generate strings instead of calling String.<init>. + * + * @hide + */ +public final class StringFactory { + + // TODO: Remove once native methods are in place. + private static final char REPLACEMENT_CHAR = (char) 0xfffd; + + public static String newEmptyString() { + return newStringFromChars(EmptyArray.CHAR, 0, 0); + } + + public static String newStringFromBytes(byte[] data) { + return newStringFromBytes(data, 0, data.length); + } + + public static String newStringFromBytes(byte[] data, int high) { + return newStringFromBytes(data, high, 0, data.length); + } + + public static String newStringFromBytes(byte[] data, int offset, int byteCount) { + return newStringFromBytes(data, offset, byteCount, Charset.defaultCharset()); + } + + public static native String newStringFromBytes(byte[] data, int high, int offset, int byteCount); + + public static String newStringFromBytes(byte[] data, int offset, int byteCount, String charsetName) throws UnsupportedEncodingException { + return newStringFromBytes(data, offset, byteCount, Charset.forNameUEE(charsetName)); + } + + public static String newStringFromBytes(byte[] data, String charsetName) throws UnsupportedEncodingException { + return newStringFromBytes(data, 0, data.length, Charset.forNameUEE(charsetName)); + } + + // TODO: Implement this method natively. + public static String newStringFromBytes(byte[] data, int offset, int byteCount, Charset charset) { + if ((offset | byteCount) < 0 || byteCount > data.length - offset) { + throw new StringIndexOutOfBoundsException(data.length, offset, byteCount); + } + + char[] value; + int length; + + // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed. + String canonicalCharsetName = charset.name(); + if (canonicalCharsetName.equals("UTF-8")) { + byte[] d = data; + char[] v = new char[byteCount]; + + int idx = offset; + int last = offset + byteCount; + int s = 0; +outer: + while (idx < last) { + byte b0 = d[idx++]; + if ((b0 & 0x80) == 0) { + // 0xxxxxxx + // Range: U-00000000 - U-0000007F + int val = b0 & 0xff; + v[s++] = (char) val; + } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) || + ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) { + int utfCount = 1; + if ((b0 & 0xf0) == 0xe0) utfCount = 2; + else if ((b0 & 0xf8) == 0xf0) utfCount = 3; + else if ((b0 & 0xfc) == 0xf8) utfCount = 4; + else if ((b0 & 0xfe) == 0xfc) utfCount = 5; + + // 110xxxxx (10xxxxxx)+ + // Range: U-00000080 - U-000007FF (count == 1) + // Range: U-00000800 - U-0000FFFF (count == 2) + // Range: U-00010000 - U-001FFFFF (count == 3) + // Range: U-00200000 - U-03FFFFFF (count == 4) + // Range: U-04000000 - U-7FFFFFFF (count == 5) + + if (idx + utfCount > last) { + v[s++] = REPLACEMENT_CHAR; + continue; + } + + // Extract usable bits from b0 + int val = b0 & (0x1f >> (utfCount - 1)); + for (int i = 0; i < utfCount; ++i) { + byte b = d[idx++]; + if ((b & 0xc0) != 0x80) { + v[s++] = REPLACEMENT_CHAR; + idx--; // Put the input char back + continue outer; + } + // Push new bits in from the right side + val <<= 6; + val |= b & 0x3f; + } + + // Note: Java allows overlong char + // specifications To disallow, check that val + // is greater than or equal to the minimum + // value for each count: + // + // count min value + // ----- ---------- + // 1 0x80 + // 2 0x800 + // 3 0x10000 + // 4 0x200000 + // 5 0x4000000 + + // Allow surrogate values (0xD800 - 0xDFFF) to + // be specified using 3-byte UTF values only + if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) { + v[s++] = REPLACEMENT_CHAR; + continue; + } + + // Reject chars greater than the Unicode maximum of U+10FFFF. + if (val > 0x10FFFF) { + v[s++] = REPLACEMENT_CHAR; + continue; + } + + // Encode chars from U+10000 up as surrogate pairs + if (val < 0x10000) { + v[s++] = (char) val; + } else { + int x = val & 0xffff; + int u = (val >> 16) & 0x1f; + int w = (u - 1) & 0xffff; + int hi = 0xd800 | (w << 6) | (x >> 10); + int lo = 0xdc00 | (x & 0x3ff); + v[s++] = (char) hi; + v[s++] = (char) lo; + } + } else { + // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff + v[s++] = REPLACEMENT_CHAR; + } + } + + if (s == byteCount) { + // We guessed right, so we can use our temporary array as-is. + value = v; + length = s; + } else { + // Our temporary array was too big, so reallocate and copy. + value = new char[s]; + length = s; + System.arraycopy(v, 0, value, 0, s); + } + } else if (canonicalCharsetName.equals("ISO-8859-1")) { + value = new char[byteCount]; + length = byteCount; + CharsetUtils.isoLatin1BytesToChars(data, offset, byteCount, value); + } else if (canonicalCharsetName.equals("US-ASCII")) { + value = new char[byteCount]; + length = byteCount; + CharsetUtils.asciiBytesToChars(data, offset, byteCount, value); + } else { + CharBuffer cb = charset.decode(ByteBuffer.wrap(data, offset, byteCount)); + length = cb.length(); + if (length > 0) { + // We could use cb.array() directly, but that would mean we'd have to trust + // the CharsetDecoder doesn't hang on to the CharBuffer and mutate it later, + // which would break String's immutability guarantee. It would also tend to + // mean that we'd be wasting memory because CharsetDecoder doesn't trim the + // array. So we copy. + value = new char[length]; + System.arraycopy(cb.array(), 0, value, 0, length); + } else { + value = EmptyArray.CHAR; + } + } + return newStringFromChars(value, 0, length); + } + + public static String newStringFromBytes(byte[] data, Charset charset) { + return newStringFromBytes(data, 0, data.length, charset); + } + + public static String newStringFromChars(char[] data) { + return newStringFromChars(data, 0, data.length); + } + + public static String newStringFromChars(char[] data, int offset, int charCount) { + if ((offset | charCount) < 0 || charCount > data.length - offset) { + throw new StringIndexOutOfBoundsException(data.length, offset, charCount); + } + return newStringFromChars(offset, charCount, data); + } + + static native String newStringFromChars(int offset, int charCount, char[] data); + + public static native String newStringFromString(String toCopy); + + public static String newStringFromStringBuffer(StringBuffer stringBuffer) { + synchronized (stringBuffer) { + return newStringFromChars(stringBuffer.getValue(), 0, stringBuffer.length()); + } + } + + // TODO: Implement this method natively. + public static String newStringFromCodePoints(int[] codePoints, int offset, int count) { + if (codePoints == null) { + throw new NullPointerException("codePoints == null"); + } + if ((offset | count) < 0 || count > codePoints.length - offset) { + throw new StringIndexOutOfBoundsException(codePoints.length, offset, count); + } + char[] value = new char[count * 2]; + int end = offset + count; + int length = 0; + for (int i = offset; i < end; i++) { + length += Character.toChars(codePoints[i], value, length); + } + return newStringFromChars(value, 0, length); + } + + public static String newStringFromStringBuilder(StringBuilder stringBuilder) { + return newStringFromChars(stringBuilder.getValue(), 0, stringBuilder.length()); + } +} diff --git a/libart/src/main/java/java/lang/Thread.java b/libart/src/main/java/java/lang/Thread.java index 852e2cf..7b3666b 100644 --- a/libart/src/main/java/java/lang/Thread.java +++ b/libart/src/main/java/java/lang/Thread.java @@ -1158,7 +1158,7 @@ public class Thread implements Runnable { * * @hide for Unsafe */ - public void unpark() { + public final void unpark$() { synchronized (lock) { switch (parkState) { case ParkState.PREEMPTIVELY_UNPARKED: { @@ -1204,7 +1204,7 @@ public class Thread implements Runnable { * * @hide for Unsafe */ - public void parkFor(long nanos) { + public final void parkFor$(long nanos) { synchronized (lock) { switch (parkState) { case ParkState.PREEMPTIVELY_UNPARKED: { @@ -1260,7 +1260,7 @@ public class Thread implements Runnable { * * @hide for Unsafe */ - public void parkUntil(long time) { + public final void parkUntil$(long time) { synchronized (lock) { /* * Note: This conflates the two time bases of "wall clock" @@ -1281,7 +1281,7 @@ public class Thread implements Runnable { if (delayMillis <= 0) { parkState = ParkState.UNPARKED; } else { - parkFor(delayMillis * NANOS_PER_MILLI); + parkFor$(delayMillis * NANOS_PER_MILLI); } } } diff --git a/libart/src/main/java/java/lang/reflect/AbstractMethod.java b/libart/src/main/java/java/lang/reflect/AbstractMethod.java index 7e6491d..95d90cc 100644 --- a/libart/src/main/java/java/lang/reflect/AbstractMethod.java +++ b/libart/src/main/java/java/lang/reflect/AbstractMethod.java @@ -39,28 +39,39 @@ import libcore.reflect.AnnotationAccess; import libcore.reflect.GenericSignatureParser; import libcore.reflect.ListOfTypes; import libcore.reflect.Types; +import libcore.util.EmptyArray; /** * This class represents an abstract method. Abstract methods are either methods or constructors. * @hide */ public abstract class AbstractMethod extends AccessibleObject { + /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */ + protected int accessFlags; /** + * The ArtMethod associated with this Method, requried for dispatching due to entrypoints + * Classloader is held live by the declaring class. * Hidden to workaround b/16828157. * @hide */ - protected final ArtMethod artMethod; + protected long artMethod; + + /** Method's declaring class */ + protected Class<?> declaringClass; + + /** Overriden method's declaring class (same as declaringClass unless declaringClass + * is a proxy class) */ + protected Class<?> declaringClassOfOverriddenMethod; + + /** The method index of this method within its defining dex file */ + protected int dexMethodIndex; /** * Hidden to workaround b/16828157. * @hide */ - protected AbstractMethod(ArtMethod artMethod) { - if (artMethod == null) { - throw new NullPointerException("artMethod == null"); - } - this.artMethod = artMethod; + protected AbstractMethod() { } public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { @@ -90,33 +101,33 @@ public abstract class AbstractMethod extends AccessibleObject { } int getModifiers() { - return fixMethodFlags(artMethod.getAccessFlags()); + return fixMethodFlags(accessFlags); } boolean isVarArgs() { - return (artMethod.getAccessFlags() & Modifier.VARARGS) != 0; + return (accessFlags & Modifier.VARARGS) != 0; } boolean isBridge() { - return (artMethod.getAccessFlags() & Modifier.BRIDGE) != 0; + return (accessFlags & Modifier.BRIDGE) != 0; } boolean isSynthetic() { - return (artMethod.getAccessFlags() & Modifier.SYNTHETIC) != 0; + return (accessFlags & Modifier.SYNTHETIC) != 0; } /** * @hide */ public final int getAccessFlags() { - return artMethod.getAccessFlags(); + return accessFlags; } /** * Returns the class that declares this constructor or method. */ Class<?> getDeclaringClass() { - return artMethod.getDeclaringClass(); + return declaringClass; } /** @@ -125,7 +136,7 @@ public abstract class AbstractMethod extends AccessibleObject { * @hide */ public final int getDexMethodIndex() { - return artMethod.getDexMethodIndex(); + return dexMethodIndex; } /** @@ -144,7 +155,17 @@ public abstract class AbstractMethod extends AccessibleObject { * @return the parameter types */ Class<?>[] getParameterTypes() { - return artMethod.getParameterTypes(); + Dex dex = declaringClassOfOverriddenMethod.getDex(); + short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); + if (types.length == 0) { + return EmptyArray.CLASS; + } + Class<?>[] parametersArray = new Class[types.length]; + for (int i = 0; i < types.length; i++) { + // Note, in the case of a Proxy the dex cache types are equal. + parametersArray[i] = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]); + } + return parametersArray; } /** @@ -155,8 +176,10 @@ public abstract class AbstractMethod extends AccessibleObject { if (!(other instanceof AbstractMethod)) { return false; } - // exactly one instance of each member in this runtime - return this.artMethod == ((AbstractMethod) other).artMethod; + // Exactly one instance of each member in this runtime, todo, does this work for proxies? + AbstractMethod otherMethod = (AbstractMethod) other; + return this.declaringClass == otherMethod.declaringClass && + this.dexMethodIndex == otherMethod.dexMethodIndex; } String toGenericString() { @@ -252,6 +275,37 @@ public abstract class AbstractMethod extends AccessibleObject { parser.returnType, parser.formalTypeParameters); } + protected boolean equalMethodParameters(Class<?>[] params) { + Dex dex = declaringClassOfOverriddenMethod.getDex(); + short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); + if (types.length != params.length) { + return false; + } + for (int i = 0; i < types.length; i++) { + if (declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]) != params[i]) { + return false; + } + } + return true; + } + + protected int compareParameters(Class<?>[] params) { + Dex dex = declaringClassOfOverriddenMethod.getDex(); + short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); + int length = Math.min(types.length, params.length); + for (int i = 0; i < length; i++) { + Class<?> aType = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]); + Class<?> bType = params[i]; + if (aType != bType) { + int comparison = aType.getName().compareTo(bType.getName()); + if (comparison != 0) { + return comparison; + } + } + } + return types.length - params.length; + } + /** * Helper for Method and Constructor for toGenericString */ diff --git a/libart/src/main/java/java/lang/reflect/AccessibleObject.java b/libart/src/main/java/java/lang/reflect/AccessibleObject.java index a1e2743..f623880 100644 --- a/libart/src/main/java/java/lang/reflect/AccessibleObject.java +++ b/libart/src/main/java/java/lang/reflect/AccessibleObject.java @@ -71,15 +71,8 @@ public class AccessibleObject implements AnnotatedElement { * IllegalAccessExceptions}. */ public void setAccessible(boolean flag) { - try { - if (equals(Class.class.getDeclaredConstructor())) { - throw new SecurityException("Can't make class constructor accessible"); - } - } catch (NoSuchMethodException e) { - throw new AssertionError("Couldn't find class constructor"); - } this.flag = flag; - } + } /** * Attempts to set the accessible flag for all objects in {@code objects}. diff --git a/libart/src/main/java/java/lang/reflect/ArtField.java b/libart/src/main/java/java/lang/reflect/ArtField.java deleted file mode 100644 index 6fdcdb2..0000000 --- a/libart/src/main/java/java/lang/reflect/ArtField.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 - * - * 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. - */ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package java.lang.reflect; - -import com.android.dex.Dex; - -/** - * @hide - */ -public final class ArtField { - - private Class<?> declaringClass; - /** Field access flags (modifiers) */ - private int accessFlags; - /** Index into DexFile's field ids */ - private int fieldDexIndex; - /** Offset of field in object or class */ - private int offset; - - /** - * Only created by art directly. - */ - private ArtField() {} - - public int getAccessFlags() { - return accessFlags; - } - - int getDexFieldIndex() { - return fieldDexIndex; - } - - int getOffset() { - return offset; - } - - public String getName() { - if (fieldDexIndex == -1) { - // Proxy classes have 1 synthesized static field with no valid dex index - if (!declaringClass.isProxy()) { - throw new AssertionError(); - } - return "throws"; - } - Dex dex = declaringClass.getDex(); - int nameIndex = dex.nameIndexFromFieldIndex(fieldDexIndex); - return declaringClass.getDexCacheString(dex, nameIndex); - } - - Class<?> getDeclaringClass() { - return declaringClass; - } - - Class<?> getType() { - if (fieldDexIndex == -1) { - // The type of the synthesized field in a Proxy class is Class[][] - if (!declaringClass.isProxy()) { - throw new AssertionError(); - } - return Class[][].class; - } - Dex dex = declaringClass.getDex(); - int typeIndex = dex.typeIndexFromFieldIndex(fieldDexIndex); - return declaringClass.getDexCacheType(dex, typeIndex); - } -} diff --git a/libart/src/main/java/java/lang/reflect/ArtMethod.java b/libart/src/main/java/java/lang/reflect/ArtMethod.java deleted file mode 100644 index 95e03c6..0000000 --- a/libart/src/main/java/java/lang/reflect/ArtMethod.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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 - * - * 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. - */ -/* - * Copyright (C) 2012 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 java.lang.reflect; - -import com.android.dex.Dex; -import java.lang.annotation.Annotation; -import libcore.reflect.AnnotationAccess; -import libcore.util.EmptyArray; - -/** - * This class represents methods and constructors. - * @hide - */ -public final class ArtMethod { - /* A note on the field order here, it reflects the same field order as laid out by ART. */ - - /** Method's declaring class */ - private Class<?> declaringClass; - - /** Short-cut to declaringClass.dexCache.resolvedMethods */ - private ArtMethod[] dexCacheResolvedMethods; - - /** Short-cut to declaringClass.dexCache.resolvedTypes */ - /* package */ Class<?>[] dexCacheResolvedTypes; - - /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */ - private int accessFlags; - - /* Dex file fields. The defining dex file is available via declaringClass.dexCache */ - - /** The offset of the code item associated with this method within its defining dex file */ - private int dexCodeItemOffset; - - /** The method index of this method within its defining dex file */ - private int dexMethodIndex; - - /* End of dex file fields. */ - - /** - * Entry within a dispatch table for this method. For static/direct methods the index is - * into the declaringClass.directMethods, for virtual methods the vtable and for - * interface methods the ifTable. - */ - private int methodIndex; - - /** Only created by ART directly. */ - private ArtMethod() {} - - Class getDeclaringClass() { - return declaringClass; - } - - public int getAccessFlags() { - return accessFlags; - } - - int getDexMethodIndex() { - return dexMethodIndex; - } - - public static String getMethodName(ArtMethod artMethod) { - artMethod = artMethod.findOverriddenMethodIfProxy(); - Dex dex = artMethod.getDeclaringClass().getDex(); - int nameIndex = dex.nameIndexFromMethodIndex(artMethod.getDexMethodIndex()); - // Note, in the case of a Proxy the dex cache strings are equal. - return artMethod.getDexCacheString(dex, nameIndex); - } - - /** - * Returns true if the given parameters match those of the method in the given order. - * - * @hide - */ - public static boolean equalConstructorParameters(ArtMethod artMethod, Class<?>[] params) { - Dex dex = artMethod.getDeclaringClass().getDex(); - short[] types = dex.parameterTypeIndicesFromMethodIndex(artMethod.getDexMethodIndex()); - if (types.length != params.length) { - return false; - } - for (int i = 0; i < types.length; i++) { - if (artMethod.getDexCacheType(dex, types[i]) != params[i]) { - return false; - } - } - return true; - } - - /** - * Returns true if the given parameters match those of this method in the given order. - * - * @hide - */ - public static boolean equalMethodParameters(ArtMethod artMethod, Class<?>[] params) { - return equalConstructorParameters(artMethod.findOverriddenMethodIfProxy(), params); - } - - Class<?>[] getParameterTypes() { - Dex dex = getDeclaringClass().getDex(); - short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); - if (types.length == 0) { - return EmptyArray.CLASS; - } - Class<?>[] parametersArray = new Class[types.length]; - for (int i = 0; i < types.length; i++) { - // Note, in the case of a Proxy the dex cache types are equal. - parametersArray[i] = getDexCacheType(dex, types[i]); - } - return parametersArray; - } - - Class<?> getReturnType() { - Dex dex = declaringClass.getDex(); - int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(dexMethodIndex); - // Note, in the case of a Proxy the dex cache types are equal. - return getDexCacheType(dex, returnTypeIndex); - } - - /** - * Performs a comparison of the parameters to this method with the given parameters. - * - * @hide - */ - int compareParameters(Class<?>[] params) { - Dex dex = getDeclaringClass().getDex(); - short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); - int length = Math.min(types.length, params.length); - for (int i = 0; i < length; i++) { - Class<?> aType = getDexCacheType(dex, types[i]); - Class<?> bType = params[i]; - if (aType != bType) { - int comparison = aType.getName().compareTo(bType.getName()); - if (comparison != 0) { - return comparison; - } - } - } - return types.length - params.length; - } - - Annotation[][] getParameterAnnotations() { - return AnnotationAccess.getParameterAnnotations(declaringClass, dexMethodIndex); - } - - /** - * Returns a string from the dex cache, computing the string from the dex file if necessary. - * Note this method replicates {@link java.lang.Class#getDexCacheString(Dex, int)}, but in - * Method we can avoid one indirection. - */ - private String getDexCacheString(Dex dex, int dexStringIndex) { - return declaringClass.getDexCacheString(dex, dexStringIndex); - } - - /** - * Returns a resolved type from the dex cache, computing the string from the dex file if - * necessary. Note this method delegates to {@link java.lang.Class#getDexCacheType(Dex, int)}, - * but in Method we can avoid one indirection. - */ - private Class<?> getDexCacheType(Dex dex, int dexTypeIndex) { - Class<?> resolvedType = dexCacheResolvedTypes[dexTypeIndex]; - if (resolvedType == null) { - resolvedType = declaringClass.getDexCacheType(dex, dexTypeIndex); - } - return resolvedType; - } - - /** - * Returns the {@code ArtMethod} that this method overrides for - * proxy methods, otherwise returns this method. Used to determine - * the interface method overridden by a proxy method (as the proxy - * method doesn't directly support operations such as {@link - * Method#getName}). - */ - ArtMethod findOverriddenMethodIfProxy() { - if (declaringClass.isProxy()) { - // Proxy method's declaring class' dex cache refers to that of Proxy. The local cache in - // Method refers to the original interface's dex cache and is ensured to be resolved by - // proxy generation. - return dexCacheResolvedMethods[dexMethodIndex]; - } - return this; - } -} diff --git a/libart/src/main/java/java/lang/reflect/Constructor.java b/libart/src/main/java/java/lang/reflect/Constructor.java index 2eb12b0..9711ef4 100644 --- a/libart/src/main/java/java/lang/reflect/Constructor.java +++ b/libart/src/main/java/java/lang/reflect/Constructor.java @@ -49,11 +49,7 @@ public final class Constructor<T> extends AbstractMethod implements GenericDecla private static final Comparator<Method> ORDER_BY_SIGNATURE = null; // Unused; must match Method. - /** - * @hide - */ - public Constructor(ArtMethod artMethod) { - super(artMethod); + private Constructor() { } public Annotation[] getAnnotations() { @@ -213,7 +209,8 @@ public final class Constructor<T> extends AbstractMethod implements GenericDecla * @return an array of arrays of {@code Annotation} instances */ public Annotation[][] getParameterAnnotations() { - return artMethod.getParameterAnnotations(); + return AnnotationAccess.getParameterAnnotations( + declaringClassOfOverriddenMethod, dexMethodIndex); } /** @@ -283,13 +280,7 @@ public final class Constructor<T> extends AbstractMethod implements GenericDecla * * @see AccessibleObject */ - public T newInstance(Object... args) throws InstantiationException, - IllegalAccessException, IllegalArgumentException, InvocationTargetException { - return newInstance(args, isAccessible()); - } - - /** @hide */ - public native T newInstance(Object[] args, boolean accessible) throws InstantiationException, + public native T newInstance(Object... args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException; /** @@ -330,4 +321,20 @@ public final class Constructor<T> extends AbstractMethod implements GenericDecla return result.toString(); } + + /** + * Attempts to set the accessible flag. Setting this to true prevents {@code + * IllegalAccessExceptions}. + */ + public void setAccessible(boolean flag) { + Class<?> declaringClass = getDeclaringClass(); + if (declaringClass == Class.class) { + throw new SecurityException("Can't make class constructor accessible"); + } else if (declaringClass == Field.class) { + throw new SecurityException("Can't make field constructor accessible"); + } else if (declaringClass == Method.class) { + throw new SecurityException("Can't make method constructor accessible"); + } + super.setAccessible(flag); + } } diff --git a/libart/src/main/java/java/lang/reflect/Field.java b/libart/src/main/java/java/lang/reflect/Field.java index 11e8a6e..37f2ad0 100644 --- a/libart/src/main/java/java/lang/reflect/Field.java +++ b/libart/src/main/java/java/lang/reflect/Field.java @@ -71,16 +71,13 @@ public final class Field extends AccessibleObject implements Member { } }; - private final ArtField artField; + private int accessFlags; + private Class<?> declaringClass; + private int dexFieldIndex; + private int offset; + private Class<?> type; - /** - * @hide - */ - public Field(ArtField artField) { - if (artField == null) { - throw new NullPointerException("artField == null"); - } - this.artField = artField; + private Field() { } /** @@ -91,7 +88,7 @@ public final class Field extends AccessibleObject implements Member { * @see Modifier */ @Override public int getModifiers() { - return artField.getAccessFlags() & 0xffff; // mask out bits not used by Java + return accessFlags & 0xffff; // mask out bits not used by Java } /** @@ -101,7 +98,7 @@ public final class Field extends AccessibleObject implements Member { * false} otherwise */ public boolean isEnumConstant() { - return (artField.getAccessFlags() & Modifier.ENUM) != 0; + return (accessFlags & Modifier.ENUM) != 0; } /** @@ -110,7 +107,7 @@ public final class Field extends AccessibleObject implements Member { * @return {@code true} if this field is synthetic, {@code false} otherwise */ @Override public boolean isSynthetic() { - return (artField.getAccessFlags() & Modifier.SYNTHETIC) != 0; + return (accessFlags & Modifier.SYNTHETIC) != 0; } /** @@ -119,11 +116,20 @@ public final class Field extends AccessibleObject implements Member { * @return the name of this field */ @Override public String getName() { - return artField.getName(); + if (dexFieldIndex == -1) { + // Proxy classes have 1 synthesized static field with no valid dex index. + if (!declaringClass.isProxy()) { + throw new AssertionError(); + } + return "throws"; + } + Dex dex = declaringClass.getDex(); + int nameIndex = dex.nameIndexFromFieldIndex(dexFieldIndex); + return declaringClass.getDexCacheString(dex, nameIndex); } @Override public Class<?> getDeclaringClass() { - return artField.getDeclaringClass(); + return declaringClass; } /** @@ -132,7 +138,7 @@ public final class Field extends AccessibleObject implements Member { * @return the type of this field */ public Class<?> getType() { - return artField.getType(); + return type; } /** @@ -141,7 +147,7 @@ public final class Field extends AccessibleObject implements Member { * @hide */ public int getDexFieldIndex() { - return artField.getDexFieldIndex(); + return dexFieldIndex; } /** @@ -150,7 +156,7 @@ public final class Field extends AccessibleObject implements Member { * @hide */ public int getOffset() { - return artField.getOffset(); + return offset; } /** @@ -170,8 +176,10 @@ public final class Field extends AccessibleObject implements Member { if (!(other instanceof Field)) { return false; } - // exactly one instance of each member in this runtime - return this.artField == ((Field) other).artField; + // Given same declaring class and offset, it must be the same field since no two distinct + // fields can have the same offset. + Field field = (Field)other; + return this.declaringClass == field.declaringClass && this.offset == field.offset; } /** @@ -182,10 +190,12 @@ public final class Field extends AccessibleObject implements Member { */ public String toGenericString() { StringBuilder sb = new StringBuilder(80); + // Limit modifier bits to the ones that toStringGeneric should return for fields. + + String modifiers = Modifier.getDeclarationFieldModifiers(getModifiers()); // append modifiers if any - int modifier = getModifiers(); - if (modifier != 0) { - sb.append(Modifier.toString(modifier)).append(' '); + if (!modifiers.isEmpty()) { + sb.append(modifiers).append(' '); } // append generic type Types.appendGenericType(sb, getGenericType()); @@ -275,11 +285,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public Object get(Object object) throws IllegalAccessException, IllegalArgumentException { - return get(object, isAccessible()); - } - - private native Object get(Object object, boolean accessible) + public native Object get(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -305,12 +311,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public boolean getBoolean(Object object) throws IllegalAccessException, - IllegalArgumentException { - return getBoolean(object, isAccessible()); - } - - private native boolean getBoolean(Object object, boolean accessible) + public native boolean getBoolean(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -336,11 +337,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException { - return getByte(object, isAccessible()); - } - - private native byte getByte(Object object, boolean accessible) + public native byte getByte(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -366,11 +363,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public char getChar(Object object) throws IllegalAccessException, IllegalArgumentException { - return getChar(object, isAccessible()); - } - - private native char getChar(Object object, boolean accessible) + public native char getChar(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -396,11 +389,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException { - return getDouble(object, isAccessible()); - } - - private native double getDouble(Object object, boolean accessible) + public native double getDouble(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -426,11 +415,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException { - return getFloat(object, isAccessible()); - } - - private native float getFloat(Object object, boolean accessible) + public native float getFloat(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -456,11 +441,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public int getInt(Object object) throws IllegalAccessException, IllegalArgumentException { - return getInt(object, isAccessible()); - } - - private native int getInt(Object object, boolean accessible) + public native int getInt(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -486,11 +467,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public long getLong(Object object) throws IllegalAccessException, IllegalArgumentException { - return getLong(object, isAccessible()); - } - - private native long getLong(Object object, boolean accessible) + public native long getLong(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -516,11 +493,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public short getShort(Object object) throws IllegalAccessException, IllegalArgumentException { - return getShort(object, isAccessible()); - } - - private native short getShort(Object object, boolean accessible) + public native short getShort(Object object) throws IllegalAccessException, IllegalArgumentException; /** @@ -552,12 +525,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void set(Object object, Object value) throws IllegalAccessException, - IllegalArgumentException { - set(object, value, isAccessible()); - } - - private native void set(Object object, Object value, boolean accessible) + public native void set(Object object, Object value) throws IllegalAccessException, IllegalArgumentException; /** @@ -588,12 +556,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setBoolean(Object object, boolean value) throws IllegalAccessException, - IllegalArgumentException { - setBoolean(object, value, isAccessible()); - } - - private native void setBoolean(Object object, boolean value, boolean accessible) + public native void setBoolean(Object object, boolean value) throws IllegalAccessException, IllegalArgumentException; /** @@ -623,14 +586,8 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setByte(Object object, byte value) throws IllegalAccessException, - IllegalArgumentException { - setByte(object, value, isAccessible()); - } - - private native void setByte(Object object, byte value, boolean accessible) + public native void setByte(Object object, byte value) throws IllegalAccessException, IllegalArgumentException; - /** * Sets the value of the field in the specified object to the {@code char} * value. This reproduces the effect of {@code object.fieldName = value} @@ -658,12 +615,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setChar(Object object, char value) throws IllegalAccessException, - IllegalArgumentException { - setChar(object, value, isAccessible()); - } - - private native void setChar(Object object, char value, boolean accessible) + public native void setChar(Object object, char value) throws IllegalAccessException, IllegalArgumentException; /** @@ -693,12 +645,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setDouble(Object object, double value) throws IllegalAccessException, - IllegalArgumentException { - setDouble(object, value, isAccessible()); - } - - private native void setDouble(Object object, double value, boolean accessible) + public native void setDouble(Object object, double value) throws IllegalAccessException, IllegalArgumentException; /** @@ -728,12 +675,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setFloat(Object object, float value) throws IllegalAccessException, - IllegalArgumentException { - setFloat(object, value, isAccessible()); - } - - private native void setFloat(Object object, float value, boolean accessible) + public native void setFloat(Object object, float value) throws IllegalAccessException, IllegalArgumentException; /** @@ -763,12 +705,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setInt(Object object, int value) throws IllegalAccessException, - IllegalArgumentException { - setInt(object, value, isAccessible()); - } - - private native void setInt(Object object, int value, boolean accessible) + public native void setInt(Object object, int value) throws IllegalAccessException, IllegalArgumentException; /** @@ -798,12 +735,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setLong(Object object, long value) throws IllegalAccessException, - IllegalArgumentException { - setLong(object, value, isAccessible()); - } - - private native void setLong(Object object, long value, boolean accessible) + public native void setLong(Object object, long value) throws IllegalAccessException, IllegalArgumentException; /** @@ -833,12 +765,7 @@ public final class Field extends AccessibleObject implements Member { * @throws IllegalAccessException * if this field is not accessible */ - public void setShort(Object object, short value) throws IllegalAccessException, - IllegalArgumentException { - setShort(object, value, isAccessible()); - } - - private native void setShort(Object object, short value, boolean accessible) + public native void setShort(Object object, short value) throws IllegalAccessException, IllegalArgumentException; /** @@ -861,7 +788,8 @@ public final class Field extends AccessibleObject implements Member { */ @Override public String toString() { - StringBuilder result = new StringBuilder(Modifier.toString(getModifiers())); + StringBuilder result = new StringBuilder( + Modifier.getDeclarationFieldModifiers(getModifiers())); if (result.length() != 0) { result.append(' '); } diff --git a/libart/src/main/java/java/lang/reflect/Method.java b/libart/src/main/java/java/lang/reflect/Method.java index 058fb96..a07ec6f 100644 --- a/libart/src/main/java/java/lang/reflect/Method.java +++ b/libart/src/main/java/java/lang/reflect/Method.java @@ -57,8 +57,7 @@ public final class Method extends AbstractMethod implements GenericDeclaration, } int comparison = a.getName().compareTo(b.getName()); if (comparison == 0) { - comparison = a.artMethod.findOverriddenMethodIfProxy().compareParameters( - b.getParameterTypes()); + comparison = a.compareParameters(b.getParameterTypes()); if (comparison == 0) { // This is necessary for methods that have covariant return types. Class<?> aReturnType = a.getReturnType(); @@ -77,12 +76,7 @@ public final class Method extends AbstractMethod implements GenericDeclaration, /** * @hide */ - public Method(ArtMethod artMethod) { - super(artMethod); - } - - ArtMethod getArtMethod() { - return artMethod; + private Method() { } public Annotation[] getAnnotations() { @@ -136,7 +130,9 @@ public final class Method extends AbstractMethod implements GenericDeclaration, * @return the name of this method */ @Override public String getName() { - return ArtMethod.getMethodName(artMethod); + Dex dex = declaringClassOfOverriddenMethod.getDex(); + int nameIndex = dex.nameIndexFromMethodIndex(dexMethodIndex); + return declaringClassOfOverriddenMethod.getDexCacheString(dex, nameIndex); } /** @@ -171,7 +167,7 @@ public final class Method extends AbstractMethod implements GenericDeclaration, * @return the parameter types */ @Override public Class<?>[] getParameterTypes() { - return artMethod.findOverriddenMethodIfProxy().getParameterTypes(); + return super.getParameterTypes(); } /** @@ -181,9 +177,13 @@ public final class Method extends AbstractMethod implements GenericDeclaration, * @return the return type */ public Class<?> getReturnType() { - return artMethod.findOverriddenMethodIfProxy().getReturnType(); + Dex dex = declaringClassOfOverriddenMethod.getDex(); + int returnTypeIndex = dex.returnTypeIndexFromMethodIndex(dexMethodIndex); + // Note, in the case of a Proxy the dex cache types are equal. + return declaringClassOfOverriddenMethod.getDexCacheType(dex, returnTypeIndex); } + /** * {@inheritDoc} * @@ -209,8 +209,7 @@ public final class Method extends AbstractMethod implements GenericDeclaration, * @hide needed by Proxy */ boolean equalNameAndParameters(Method m) { - return getName().equals(m.getName()) && - ArtMethod.equalMethodParameters(artMethod,m.getParameterTypes()); + return getName().equals(m.getName()) && equalMethodParameters(m.getParameterTypes()); } /** @@ -310,7 +309,8 @@ public final class Method extends AbstractMethod implements GenericDeclaration, * @return an array of arrays of {@code Annotation} instances */ public Annotation[][] getParameterAnnotations() { - return artMethod.findOverriddenMethodIfProxy().getParameterAnnotations(); + return AnnotationAccess.getParameterAnnotations( + declaringClassOfOverriddenMethod, dexMethodIndex); } /** @@ -367,12 +367,7 @@ public final class Method extends AbstractMethod implements GenericDeclaration, * @throws InvocationTargetException * if an exception was thrown by the invoked method */ - public Object invoke(Object receiver, Object... args) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { - return invoke(receiver, args, isAccessible()); - } - - private native Object invoke(Object receiver, Object[] args, boolean accessible) + public native Object invoke(Object receiver, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; /** @@ -398,7 +393,8 @@ public final class Method extends AbstractMethod implements GenericDeclaration, */ @Override public String toString() { - StringBuilder result = new StringBuilder(Modifier.toString(getModifiers())); + StringBuilder result = new StringBuilder( + Modifier.getDeclarationMethodModifiers(getModifiers())); if (result.length() != 0) { result.append(' '); diff --git a/libart/src/main/java/java/lang/reflect/Proxy.java b/libart/src/main/java/java/lang/reflect/Proxy.java index 31f9cd9..18ad49c 100644..100755 --- a/libart/src/main/java/java/lang/reflect/Proxy.java +++ b/libart/src/main/java/java/lang/reflect/Proxy.java @@ -166,11 +166,7 @@ public class Proxy implements Serializable { Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); validateReturnTypes(methods); List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods); - - ArtMethod[] methodsArray = new ArtMethod[methods.size()]; - for (int i = 0; i < methodsArray.length; i++) { - methodsArray[i] = methods.get(i).getArtMethod(); - } + Method[] methodsArray = methods.toArray(new Method[methods.size()]); Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]); String baseName = commonPackageName != null && !commonPackageName.isEmpty() @@ -179,7 +175,7 @@ public class Proxy implements Serializable { Class<?> result; synchronized (loader.proxyCache) { - result = loader.proxyCache.get(interfaceSet); + result = loader.proxyCache.get(interfaceList); if (result == null) { String name = baseName + nextClassNameIndex++; result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray); @@ -383,7 +379,7 @@ public class Proxy implements Serializable { } private static native Class<?> generateProxy(String name, Class<?>[] interfaces, - ClassLoader loader, ArtMethod[] methods, + ClassLoader loader, Method[] methods, Class<?>[][] exceptions); /* @@ -392,8 +388,8 @@ public class Proxy implements Serializable { */ private static native void constructorPrototype(InvocationHandler h); - static Object invoke(Proxy proxy, ArtMethod method, Object[] args) throws Throwable { + private static Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable { InvocationHandler h = proxy.h; - return h.invoke(proxy, new Method(method), args); + return h.invoke(proxy, method, args); } } diff --git a/libart/src/main/java/sun/misc/Unsafe.java b/libart/src/main/java/sun/misc/Unsafe.java index 6f5f5ee..1f938de 100644 --- a/libart/src/main/java/sun/misc/Unsafe.java +++ b/libart/src/main/java/sun/misc/Unsafe.java @@ -294,9 +294,9 @@ public final class Unsafe { */ public void park(boolean absolute, long time) { if (absolute) { - Thread.currentThread().parkUntil(time); + Thread.currentThread().parkUntil$(time); } else { - Thread.currentThread().parkFor(time); + Thread.currentThread().parkFor$(time); } } @@ -310,7 +310,7 @@ public final class Unsafe { */ public void unpark(Object obj) { if (obj instanceof Thread) { - ((Thread) obj).unpark(); + ((Thread) obj).unpark$(); } else { throw new IllegalArgumentException("valid for Threads only"); } |