diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 14:03:56 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 14:03:56 -0800 |
| commit | 31e30105703263782efd450d356cd67ea01af3b7 (patch) | |
| tree | 41af8ef10ee58881490f5e4620861a8853159be6 | |
| parent | 6dcac3deb3c19dc634470eb30b2daedf2b201bd4 (diff) | |
| download | android_dalvik-31e30105703263782efd450d356cd67ea01af3b7.tar.gz android_dalvik-31e30105703263782efd450d356cd67ea01af3b7.tar.bz2 android_dalvik-31e30105703263782efd450d356cd67ea01af3b7.zip | |
auto import from //depot/cupcake/@132589
51 files changed, 1107 insertions, 3307 deletions
diff --git a/docs/dalvik-bytecode.html b/docs/dalvik-bytecode.html index 4945d60f4..2bbffe6b0 100644 --- a/docs/dalvik-bytecode.html +++ b/docs/dalvik-bytecode.html @@ -1170,7 +1170,7 @@ round-to-nearest and gradual underflow, except where stated otherwise.</p> <td>float a;<br/> int64 result = (int64) a; </td> - <td>Conversion of <code>float</code> to <code>int64</code>, using + <td>Conversion of <code>float</code> to <code>int32</code>, using round-toward-zero. The same special case rules as for <code>float-to-int</code> apply here, except that out-of-range values get converted to either <code>0x7fffffffffffffff</code> or diff --git a/docs/debugger.html b/docs/debugger.html deleted file mode 100644 index 6e23f0df2..000000000 --- a/docs/debugger.html +++ /dev/null @@ -1,211 +0,0 @@ -<html> -<head> -<title>Dalvik Debugger Support</title> -</head> - -<body> -<h1>Dalvik Debugger Support</h1> - -<p> -The Dalvik virtual machine supports source-level debugging with many popular -development environments. Any tool that allows remote debugging over JDWP -(the -<a href="http://java.sun.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html"> -Java Debug Wire Protocol</a>) is expected work. Supported debuggers -include jdb, Eclipse, IntelliJ, and JSwat. -</p><p> -The VM does not support tools based on JVMTI (Java Virtual -Machine Tool Interface). This is a relatively intrusive approach that -relies on bytecode insertion, something the Dalvik VM does not currently -support. -</p><p> -Dalvik's implementation of JDWP also includes hooks for supporting -DDM (Dalvik Debug Monitor) features, notably as implemented by DDMS -(Dalvik Debug Monitor Server) and the Eclipse ADT plugin. The protocol -and VM interaction is described in some detail -<a href="debugmon.html">here</a>. -</p><p> -All of the debugger support in the VM lives in the <code>dalvik/vm/jdwp</code> -directory, and is almost entirely isolated from the rest of the VM sources. -<code>dalvik/vm/Debugger.c</code> bridges the gap. The goal in doing so -was to make it easier to re-use the JDWP code in other projects. -</p><p> - - -<h2>Implementation</h2> - -<p> -Every VM that has debugging enabled starts a "JDWP" thread. The thread -typically sits idle until DDMS or a debugger connects. The thread is -only responsible for handling requests from the debugger; VM-initated -communication, such as notifying the debugger when the VM has stopped at -a breakpoint, are sent from the affected thread. -</p><p> -When the VM is embedded in the Android framework, -debugging is enabled in the VM unless the system property -<code>ro.secure</code> is set to </code>1</code>. On these -"secure" devices, debugging is only enabled in app processes whose -manifest contains <code>android:debuggable="true"</code> in the -<code><application></code> element. - -</p><p> -The VM recognizes the difference between a connection from DDMS and a -connection from a debugger (either directly or in concert with DDMS). -A connection from DDMS alone doesn't result in a change in VM behavior, -but when the VM sees debugger packets it allocates additional data -structures and may switch to a different implementation of the interpreter. -</p><p> -Because Dalvik maps bytecode into memory read-only, some common -techniques are difficult to implement without allocating additional memory. -For example, suppose the debugger sets a breakpoint in a method. The -quick way to handle this is to insert a breakpoint instruction directly -into the code. When the instruction is reached, the breakpoint handler -engages. Without this, it's necessary to perform an "is there a breakpoint -here" scan. Even with some optimizations, the debug-enabled interpreter -is much slower than the regular interpreter (perhaps 5x). -</p><p> -The JDWP protocol is stateless, so the VM handles individual debugger -requests as they arrive, and posts events to the debugger as they happen. -</p><p> - - -<h2>Debug Data</h2> -<p> Source code debug data, which includes mappings of source code to -bytecode and lists describing which registers are used to hold method -arguments and local variables, are optionally emitted by the Java compiler. -When <code>dx</code> converts Java bytecode to Dalvik bytecode, it must -also convert this debug data. -</p><p> -<code>dx</code> must also ensure that it doesn't perform operations -that confuse the debugger. For example, re-using registers that hold -method arguments and the "<code>this</code>" pointer is allowed in -Dalvik bytecode if the values are never used or no longer needed. -This can be very confusing for the debugger (and the programmer) -since the values have method scope and aren't expected to disappear. For -this reason, <code>dx</code> generates sub-optimal code in some situations -when debugging support is enabled. -</p><p> -Some of the debug data is used for other purposes; in particular, having -filename and line number data is necessary for generating useful exception -stack traces. This data can be omitted by <code>dx</code> to make the DEX -file smaller. -</p><p> - - -<h2>Usage</h2> - -<p> -The Dalvik VM supports many of the same command-line flags that other popular -desktop VMs do. To start a VM with debugging enabled, you add a command-line -flag with some basic options. The basic incantation looks something -like this: - -<pre>-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y</pre> -or -<pre>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y</pre> - -</p><p> -After the initial prefix, options are provided as name=value pairs. The -options currently supported by the Dalvik VM are: -<dl> - <dt>transport (no default)</dt> - <dd>Communication transport mechanism to use. Dalvik supports - TCP/IP sockets (<code>dt_socket</code>) and connection over USB - through ADB (<code>dt_android_adb</code>). - </dd> - - <dt>server (default='n')</dt> - <dd>Determines whether the VM acts as a client or a server. When - acting as a server, the VM waits for a debugger to connect to it. - When acting as a client, the VM attempts to connect to a waiting - debugger. - </dd> - - <dt>suspend (default='n')</dt> - <dd>If set to 'y', the VM will wait for a debugger connection - before executing application code. When the debugger connects (or - when the VM finishes connecting to the debugger), the VM tells the - debugger that it has suspended, and will not proceed until told - to resume. If set to 'n', the VM just plows ahead. - </dd> - - <dt>address (default="")</dt> - <dd>This must be <code>hostname:port</code> when <code>server=n</code>, - but can be just <code>port</code> when <code>server=y</code>. This - specifies the IP address and port number to connect or listen to. - <br> - Listening on port 0 has a special meaning: try to - listen on port 8000; if that fails, try 8001, 8002, and so on. (This - behavior is non-standard and may be removed from a future release.) - <br>This option has no meaning for <code>transport=dt_android_adb</code>. - </dd> - - <dt>help (no arguments)</dt> - <dd>If this is the only option, a brief usage message is displayed. - </dd> - - <dt>launch, onthrow, oncaught, timeout</dt> - <dd>These options are accepted but ignored. - </dd> -</dl> - -</p><p> -To debug a program on an Android device using DDMS over USB, you could -use a command like this: -<pre>% dalvikvm -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y -cp /data/foo.jar Foo</pre> - -This tells the Dalvik VM to run the program with debugging enabled, listening -for a connection from DDMS, and waiting for a debugger. The program will show -up with an app name of "?" in the process list, because it wasn't started -from the Android application framework. From here you would connect your -debugger to the appropriate DDMS listen port (e.g. -<code>jdb -attach localhost:8700</code> after selecting it in the app list). - -</p><p> -To debug a program on an Android device using TCP/IP bridged across ADB, -you would first need to set up forwarding: -<pre>% adb forward tcp:8000 tcp:8000 -% adb shell dalvikvm -agentlib:jdwp=transport=dt_socket,address=8000,suspend=y,server=y -cp /data/foo.jar Foo</pre> -and then <code>jdb -attach localhost:8000</code>. -</p><p> -(In the above examples, the VM will be suspended when you attach. In jdb, -type <code>cont</code> to continue.) -</p><p> -The DDMS integration makes the <code>dt_android_adb</code> transport much -more convenient when debugging on an Android device, but when working with -Dalvik on the desktop it makes sense to use the TCP/IP transport. -</p><p> - - -<h2>Known Issues and Limitations</h2> - -</p><p> -Most of the optional features JDWP allows are not implemented. These -include field access watchpoints and better tracking of monitors. -</p><p> -Not all JDWP requests are implemented. In particular, anything that -never gets emitted by the debuggers we've used is not supported and will -result in error messages being logged. Support will be added when a -use case is uncovered. -</p><p> - -</p><p> -The debugger and garbage collector are somewhat loosely -integrated at present. The VM currently guarantees that any object the -debugger is aware of will not be garbage collected until after the -debugger disconnects. This can result in a build-up over time while the -debugger is connected. -</p><p> -The situation is exacerbated by a flaw in the exception processing code, -which results in nearly all exceptions being added to the "do not discard" -list, even if the debugger never sees them. Having a debugger attached -to a program that throws lots of exceptions can result in out-of-memory -errors. This will be fixed in a future release. -</p><p> - - -<address>Copyright © 2009 The Android Open Source Project</address> -</p> - -</body> -</html> diff --git a/docs/instruction-formats.html b/docs/instruction-formats.html index d7bf69057..941689eb6 100644 --- a/docs/instruction-formats.html +++ b/docs/instruction-formats.html @@ -366,15 +366,15 @@ the correspondence.</p> <tr> <td>B|A|<i>op</i> DDCC H|G|F|E</td> <td>35fs</td> - <td><i>[<code>B=5</code>] <code>op</code></i> {vE, vF, vG, vH, vA}, + <td><i>[<code>B=5</code>] <code>op</code></i> vB, {vE, vF, vG, vH, vA}, vtaboff@CC, iface@DD<br/> - <i>[<code>B=4</code>] <code>op</code></i> {vE, vF, vG, vH}, + <i>[<code>B=4</code>] <code>op</code></i> vB, {vE, vF, vG, vH}, vtaboff@CC, iface@DD<br/> - <i>[<code>B=3</code>] <code>op</code></i> {vE, vF, vG}, + <i>[<code>B=3</code>] <code>op</code></i> vB, {vE, vF, vG}, vtaboff@CC, iface@DD<br/> - <i>[<code>B=2</code>] <code>op</code></i> {vE, vF}, + <i>[<code>B=2</code>] <code>op</code></i> vB, {vE, vF}, vtaboff@CC, iface@DD<br/> - <i>[<code>B=1</code>] <code>op</code></i> {vE}, + <i>[<code>B=1</code>] <code>op</code></i> vB, {vE}, vtaboff@CC, iface@DD<br/> </td> <td><i>(suggested format for statically linked <code>invoke-interface</code> diff --git a/docs/libraries.html b/docs/libraries.html index e1c3035f1..9fd199cca 100644 --- a/docs/libraries.html +++ b/docs/libraries.html @@ -34,7 +34,7 @@ concurrency, I/O, and more.</p> <ul> <li><a href="#vm-specific">Dalvik VM-specific libraries</a></li> - <li><a href="#interop">Java programming language interoperability + <li><a href="#interop">Jave programming language interoperability libraries</a></li> </ul> diff --git a/dx/src/com/android/dx/dex/code/LocalEnd.java b/dx/src/com/android/dx/dex/code/LocalEnd.java index c19a8dcec..87934dbd4 100644 --- a/dx/src/com/android/dx/dex/code/LocalEnd.java +++ b/dx/src/com/android/dx/dex/code/LocalEnd.java @@ -29,10 +29,9 @@ import com.android.dx.rop.code.SourcePosition; public final class LocalEnd extends ZeroSizeInsn { /** * non-null; register spec representing the local variable ended - * by this instance. <b>Note:</b> Technically, only the register - * number needs to be recorded here as the rest of the information - * is implicit in the ambient local variable state, but other code - * will check the other info for consistency. + * by this instance. <b>Note:</b> The only salient part of the spec + * is the register number; the rest of the info may be useful for + * debugging but shouldn't affect any actual processing */ private final RegisterSpec local; diff --git a/dx/src/com/android/dx/dex/code/LocalList.java b/dx/src/com/android/dx/dex/code/LocalList.java index 4614fc41e..d963fca08 100644 --- a/dx/src/com/android/dx/dex/code/LocalList.java +++ b/dx/src/com/android/dx/dex/code/LocalList.java @@ -23,9 +23,7 @@ import com.android.dx.rop.cst.CstUtf8; import com.android.dx.rop.type.Type; import com.android.dx.util.FixedSizeList; -import java.io.PrintStream; import java.util.ArrayList; -import java.util.Arrays; /** * List of local variables. Each local variable entry indicates a @@ -36,9 +34,174 @@ public final class LocalList extends FixedSizeList { /** non-null; empty instance */ public static final LocalList EMPTY = new LocalList(0); - /** whether to run the self-check code */ - private static final boolean DEBUG = false; - + /** + * Constructs an instance for the given method, based on the given + * block order and intermediate local information. + * + * @param insns non-null; instructions to convert + * @return non-null; the constructed list + */ + public static LocalList make(DalvInsnList insns) { + ArrayList<Entry> result = new ArrayList<Entry>(100); + int codeSize = insns.codeSize(); + int sz = insns.size(); + RegisterSpecSet state = null; + int stateMax = 0; + + for (int i = 0; i < sz; i++) { + DalvInsn insn = insns.get(i); + + if (insn instanceof LocalSnapshot) { + RegisterSpecSet newState = ((LocalSnapshot) insn).getLocals(); + boolean first = (state == null); + + if (first) { + stateMax = newState.getMaxSize(); + } + + for (int j = 0; j < stateMax; j++) { + RegisterSpec oldSpec = first ? null : state.get(j); + RegisterSpec newSpec = newState.get(j); + boolean oldEnds = false; + boolean newStarts = false; + + if (oldSpec == null) { + if (newSpec != null) { + /* + * This is a newly-introduced local, not + * replacing an existing local. + */ + newStarts = true; + } + } else if (newSpec == null) { + /* + * This is a local going out of scope, with no + * replacement. + */ + oldEnds = true; + } else if (!oldSpec.equals(newSpec)) { + /* + * This is a local going out of scope, immediately + * replaced by a different local. + */ + oldEnds = true; + newStarts = true; + } + + if (oldEnds) { + endScope(result, oldSpec, insn.getAddress()); + } + + if (newStarts) { + startScope(result, newSpec, insn.getAddress(), + codeSize); + } + } + + state = newState; + } else if (insn instanceof LocalStart) { + RegisterSpec newSpec = ((LocalStart) insn).getLocal(); + RegisterSpec oldSpec = state.get(newSpec); + + boolean oldEnds = false; + boolean newStarts = false; + + if (oldSpec == null) { + /* + * This is a newly-introduced local, not replacing an + * existing local. + */ + newStarts = true; + } else if (!oldSpec.equals(newSpec)) { + /* + * This is a local going out of scope, immediately + * replaced by a different local. + */ + oldEnds = true; + newStarts = true; + } + + if (newStarts) { + int address = insn.getAddress(); + + if (oldEnds) { + endScope(result, oldSpec, address); + } + + startScope(result, newSpec, address, codeSize); + + if (state.isImmutable()) { + state = state.mutableCopy(); + } + + state.put(newSpec); + } + } + } + + int resultSz = result.size(); + + if (resultSz == 0) { + return EMPTY; + } + + LocalList resultList = new LocalList(resultSz); + + for (int i = 0; i < resultSz; i++) { + resultList.set(i, result.get(i)); + } + + resultList.setImmutable(); + return resultList; + } + + /** + * Helper for {@link #make}, to indicate that the given variable has + * been introduced. + * + * @param result non-null; result in-progress + * @param spec non-null; register spec for the variable in question + * @param startAddress >= 0; address at which the scope starts + * (inclusive) + * @param endAddress > startAddress; initial scope end address + * (exclusive) + */ + private static void startScope(ArrayList<Entry> result, RegisterSpec spec, + int startAddress, int endAddress) { + result.add(new Entry(startAddress, endAddress, spec)); + } + + /** + * Helper for {@link #make}, to indicate that the given variable's + * scope has closed. + * + * @param result non-null; result in-progress + * @param spec non-null; register spec for the variable in question + * @param endAddress >= 0; address at which the scope ends (exclusive) + */ + private static void endScope(ArrayList<Entry> result, RegisterSpec spec, + int endAddress) { + int sz = result.size(); + + for (int i = sz - 1; i >= 0; i--) { + Entry e = result.get(i); + if (e.matches(spec)) { + if (e.getStart() == endAddress) { + /* + * It turns out that the indicated entry doesn't actually + * cover any code. + */ + result.remove(i); + } else { + result.set(i, e.withEnd(endAddress)); + } + return; + } + } + + throw new RuntimeException("unmatched variable: " + spec); + } + /** * Constructs an instance. All indices initially contain <code>null</code>. * @@ -64,86 +227,54 @@ public final class LocalList extends FixedSizeList { * Sets the entry at the given index. * * @param n >= 0, < size(); which index - * @param entry non-null; the entry to set at <code>n</code> + * @param start >= 0; start address + * @param end > start; end address (exclusive) + * @param spec non-null; register spec representing the variable */ - public void set(int n, Entry entry) { - set0(n, entry); + public void set(int n, int start, int end, RegisterSpec spec) { + set0(n, new Entry(start, end, spec)); } /** - * Does a human-friendly dump of this instance. + * Sets the entry at the given index. * - * @param out non-null; where to dump - * @param prefix non-null; prefix to attach to each line of output - */ - public void debugPrint(PrintStream out, String prefix) { - int sz = size(); - - for (int i = 0; i < sz; i++) { - out.print(prefix); - out.println(get(i)); - } - } - - /** - * Disposition of a local entry. + * @param n >= 0, < size(); which index + * @param entry non-null; the entry to set at <code>n</code> */ - public static enum Disposition { - /** local started (introduced) */ - START, - - /** local ended without being replaced */ - END_SIMPLY, - - /** local ended because it was directly replaced */ - END_REPLACED, - - /** local ended because it was moved to a different register */ - END_MOVED, - - /** - * local ended because the previous local clobbered this one - * (because it is category-2) - */ - END_CLOBBERED_BY_PREV, - - /** - * local ended because the next local clobbered this one - * (because this one is a category-2) - */ - END_CLOBBERED_BY_NEXT; + public void set(int n, Entry entry) { + set0(n, entry); } /** * Entry in a local list. */ - public static class Entry implements Comparable<Entry> { - /** >= 0; address */ - private final int address; + public static class Entry { + /** >= 0; start address */ + private final int start; - /** non-null; disposition of the local */ - private final Disposition disposition; + /** > start; end address (exclusive) */ + private final int end; /** non-null; register spec representing the variable */ private final RegisterSpec spec; - /** non-null; variable type (derived from {@code spec}) */ + /** non-null; variable type */ private final CstType type; - + /** * Constructs an instance. * - * @param address >= 0; address - * @param disposition non-null; disposition of the local + * @param start >= 0; start address + * @param end > start; end address (exclusive) * @param spec non-null; register spec representing the variable */ - public Entry(int address, Disposition disposition, RegisterSpec spec) { - if (address < 0) { - throw new IllegalArgumentException("address < 0"); + public Entry(int start, int end, RegisterSpec spec) { + if (start < 0) { + throw new IllegalArgumentException("start < 0"); } - if (disposition == null) { - throw new NullPointerException("disposition == null"); + if (end <= start) { + throw new IllegalArgumentException("end <= start"); } try { @@ -156,78 +287,37 @@ public final class LocalList extends FixedSizeList { throw new NullPointerException("spec == null"); } - this.address = address; - this.disposition = disposition; + this.start = start; + this.end = end; this.spec = spec; - this.type = CstType.intern(spec.getType()); - } - /** {@inheritDoc} */ - public String toString() { - return Integer.toHexString(address) + " " + disposition + " " + - spec; - } - - /** {@inheritDoc} */ - public boolean equals(Object other) { - if (!(other instanceof Entry)) { - return false; - } - - return (compareTo((Entry) other) == 0); - } - - /** - * Compares by (in priority order) address, end then start - * disposition (variants of end are all consistered - * equivalent), and spec. - * - * @param other non-null; entry to compare to - * @return {@code -1..1}; standard result of comparison - */ - public int compareTo(Entry other) { - if (address < other.address) { - return -1; - } else if (address > other.address) { - return 1; - } - - boolean thisIsStart = isStart(); - boolean otherIsStart = other.isStart(); - - if (thisIsStart != otherIsStart) { - return thisIsStart ? 1 : -1; + if (spec.getType() == Type.KNOWN_NULL) { + /* + * KNOWN_NULL's descriptor is '<null>', which we do + * not want to emit. Everything else is as expected. + */ + this.type = CstType.OBJECT; + } else { + this.type = CstType.intern(spec.getType()); } - - return spec.compareTo(other.spec); } /** - * Gets the address. + * Gets the start address. * - * @return >= 0; the address + * @return >= 0; the start address */ - public int getAddress() { - return address; + public int getStart() { + return start; } /** - * Gets the disposition. + * Gets the end address (exclusive). * - * @return non-null; the disposition + * @return > start; the end address (exclusive) */ - public Disposition getDisposition() { - return disposition; - } - - /** - * Gets whether this is a local start. This is just shorthand for - * {@code getDisposition() == Disposition.START}. - * - * @return {@code true} iff this is a start - */ - public boolean isStart() { - return disposition == Disposition.START; + public int getEnd() { + return end; } /** @@ -282,8 +372,8 @@ public final class LocalList extends FixedSizeList { * @return <code>true</code> iff this instance matches * <code>spec</code> */ - public boolean matches(RegisterSpec otherSpec) { - return spec.equalsUsingSimpleType(otherSpec); + public boolean matches(RegisterSpec spec) { + return spec.equals(this.spec); } /** @@ -295,619 +385,18 @@ public final class LocalList extends FixedSizeList { * <code>other</code> */ public boolean matches(Entry other) { - return matches(other.spec); + return other.spec.equals(this.spec); } /** - * Returns an instance just like this one but with the disposition - * set as given + * Returns an instance just like this one, except with the end + * address altered to be the one given. * - * @param disposition non-null; the new disposition + * @param newEnd > getStart(); the end address of the new instance * @return non-null; an appropriately-constructed instance */ - public Entry withDisposition(Disposition disposition) { - if (disposition == this.disposition) { - return this; - } - - return new Entry(address, disposition, spec); - } - } - - /** - * Constructs an instance for the given method, based on the given - * block order and intermediate local information. - * - * @param insns non-null; instructions to convert - * @return non-null; the constructed list - */ - public static LocalList make(DalvInsnList insns) { - int sz = insns.size(); - - /* - * Go through the insn list, looking for all the local - * variable pseudoinstructions, splitting out LocalSnapshots - * into separate per-variable starts, adding explicit ends - * wherever a variable is replaced or moved, and collecting - * these and all the other local variable "activity" - * together into an output list (without the other insns). - * - * Note: As of this writing, this method won't be handed any - * insn lists that contain local ends, but I (danfuzz) expect - * that to change at some point, when we start feeding that - * info explicitly into the rop layer rather than only trying - * to infer it. So, given that expectation, this code is - * written to deal with them. - */ - - MakeState state = new MakeState(sz); - - for (int i = 0; i < sz; i++) { - DalvInsn insn = insns.get(i); - - if (insn instanceof LocalSnapshot) { - RegisterSpecSet snapshot = - ((LocalSnapshot) insn).getLocals(); - state.snapshot(insn.getAddress(), snapshot); - } else if (insn instanceof LocalStart) { - RegisterSpec local = ((LocalStart) insn).getLocal(); - state.startLocal(insn.getAddress(), local); - } else if (insn instanceof LocalEnd) { - RegisterSpec local = ((LocalEnd) insn).getLocal(); - state.endLocal(insn.getAddress(), local); - } - } - - LocalList result = state.finish(); - - if (DEBUG) { - debugVerify(result); + public Entry withEnd(int newEnd) { + return new Entry(start, newEnd, spec); } - - return result; - } - - /** - * Debugging helper that verifies the constraint that a list doesn't - * contain any redundant local starts and that local ends that are - * due to replacements are properly annotated. - */ - private static void debugVerify(LocalList locals) { - try { - debugVerify0(locals); - } catch (RuntimeException ex) { - int sz = locals.size(); - for (int i = 0; i < sz; i++) { - System.err.println(locals.get(i)); - } - throw ex; - } - } - - /** - * Helper for {@link #debugVerify} which does most of the work. - */ - private static void debugVerify0(LocalList locals) { - int sz = locals.size(); - Entry[] active = new Entry[65536]; - - for (int i = 0; i < sz; i++) { - Entry e = locals.get(i); - int reg = e.getRegister(); - - if (e.isStart()) { - Entry already = active[reg]; - - if ((already != null) && e.matches(already)) { - throw new RuntimeException("redundant start at " + - Integer.toHexString(e.getAddress()) + ": got " + - e + "; had " + already); - } - - active[reg] = e; - } else { - if (active[reg] == null) { - throw new RuntimeException("redundant end at " + - Integer.toHexString(e.getAddress())); - } - - int addr = e.getAddress(); - boolean foundStart = false; - - for (int j = i + 1; j < sz; j++) { - Entry test = locals.get(j); - if (test.getAddress() != addr) { - break; - } - if (test.getRegisterSpec().getReg() == reg) { - if (test.isStart()) { - if (e.getDisposition() - != Disposition.END_REPLACED) { - throw new RuntimeException( - "improperly marked end at " + - Integer.toHexString(addr)); - } - foundStart = true; - } else { - throw new RuntimeException( - "redundant end at " + - Integer.toHexString(addr)); - } - } - } - - if (!foundStart && - (e.getDisposition() == Disposition.END_REPLACED)) { - throw new RuntimeException( - "improper end replacement claim at " + - Integer.toHexString(addr)); - } - - active[reg] = null; - } - } - } - - /** - * Intermediate state when constructing a local list. - */ - public static class MakeState { - /** non-null; result being collected */ - private final ArrayList<Entry> result; - - /** - * >= 0; running count of nulled result entries, to help with - * sizing the final list - */ - private int nullResultCount; - - /** null-ok; current register mappings */ - private RegisterSpecSet regs; - - /** null-ok; result indices where local ends are stored */ - private int[] endIndices; - - /** >= 0; last address seen */ - private int lastAddress; - - /** - * >= 0; result index where the first element for the most - * recent address is stored - */ - private int startIndexForAddress; - - /** - * Constructs an instance. - */ - public MakeState(int initialSize) { - result = new ArrayList<Entry>(initialSize); - nullResultCount = 0; - regs = null; - endIndices = null; - lastAddress = 0; - startIndexForAddress = 0; - } - - /** - * Checks the address and other vitals as a prerequisite to - * further processing. - * - * @param address >= 0; address about to be processed - * @param reg >= 0; register number about to be processed - */ - private void aboutToProcess(int address, int reg) { - boolean first = (endIndices == null); - - if ((address == lastAddress) && !first) { - return; - } - - if (address < lastAddress) { - throw new RuntimeException("shouldn't happen"); - } - - if (first || (reg >= endIndices.length)) { - /* - * This is the first allocation of the state set and - * index array, or we need to grow. (The latter doesn't - * happen much; in fact, we have only ever observed - * it happening in test cases, never in "real" code.) - */ - int newSz = reg + 1; - RegisterSpecSet newRegs = new RegisterSpecSet(newSz); - int[] newEnds = new int[newSz]; - Arrays.fill(newEnds, -1); - - if (!first) { - newRegs.putAll(regs); - System.arraycopy(endIndices, 0, newEnds, 0, - endIndices.length); - } - - regs = newRegs; - endIndices = newEnds; - } - } - - /** - * Sets the local state at the given address to the given snapshot. - * The first call on this instance must be to this method, so that - * the register state can be properly sized. - * - * @param address >= 0; the address - * @param specs non-null; spec set representing the locals - */ - public void snapshot(int address, RegisterSpecSet specs) { - int sz = specs.getMaxSize(); - aboutToProcess(address, sz - 1); - - for (int i = 0; i < sz; i++) { - RegisterSpec oldSpec = regs.get(i); - RegisterSpec newSpec = filterSpec(specs.get(i)); - - if (oldSpec == null) { - if (newSpec != null) { - startLocal(address, newSpec); - } - } else if (newSpec == null) { - endLocal(address, oldSpec); - } else if (! newSpec.equalsUsingSimpleType(oldSpec)) { - endLocal(address, oldSpec); - startLocal(address, newSpec); - } - } - } - - /** - * Starts a local at the given address. - * - * @param address >= 0; the address - * @param startedLocal non-null; spec representing the started local - */ - public void startLocal(int address, RegisterSpec startedLocal) { - int regNum = startedLocal.getReg(); - - startedLocal = filterSpec(startedLocal); - aboutToProcess(address, regNum); - - RegisterSpec existingLocal = regs.get(regNum); - - if (startedLocal.equalsUsingSimpleType(existingLocal)) { - // Silently ignore a redundant start. - return; - } - - RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal); - if (movedLocal != null) { - /* - * The same variable was moved from one register to another. - * So add an end for its old location. - */ - addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal); - } - - int endAt = endIndices[regNum]; - - if (existingLocal != null) { - /* - * There is an existing (but non-matching) local. - * Add an explicit end for it. - */ - add(address, Disposition.END_REPLACED, existingLocal); - } else if (endAt >= 0) { - /* - * Look for an end local for the same register at the - * same address. If found, then update it or delete - * it, depending on whether or not it represents the - * same variable as the one being started. - */ - Entry endEntry = result.get(endAt); - if (endEntry.getAddress() == address) { - if (endEntry.matches(startedLocal)) { - /* - * There was already an end local for the same - * variable at the same address. This turns - * out to be superfluous, as we are starting - * up the exact same local. This situation can - * happen when a single local variable got - * somehow "split up" during intermediate - * processing. In any case, rather than represent - * the end-then-start, just remove the old end. - */ - result.set(endAt, null); - nullResultCount++; - regs.put(startedLocal); - endIndices[regNum] = -1; - return; - } else { - /* - * There was a different variable ended at the - * same address. Update it to indicate that - * it was ended due to a replacement (rather than - * ending for no particular reason). - */ - endEntry = endEntry.withDisposition( - Disposition.END_REPLACED); - result.set(endAt, endEntry); - } - } - } - - /* - * The code above didn't find and remove an unnecessary - * local end, so we now have to add one or more entries to - * the output to capture the transition. - */ - - /* - * If the local just below (in the register set at reg-1) - * is of category-2, then it is ended by this new start. - */ - if (regNum > 0) { - RegisterSpec justBelow = regs.get(regNum - 1); - if ((justBelow != null) && justBelow.isCategory2()) { - addOrUpdateEnd(address, - Disposition.END_CLOBBERED_BY_NEXT, - justBelow); - } - } - - /* - * Similarly, if this local is category-2, then the local - * just above (if any) is ended by the start now being - * emitted. - */ - if (startedLocal.isCategory2()) { - RegisterSpec justAbove = regs.get(regNum + 1); - if (justAbove != null) { - addOrUpdateEnd(address, - Disposition.END_CLOBBERED_BY_PREV, - justAbove); - } - } - - /* - * TODO: Add an end for the same local in a different reg, - * if any (that is, if the local migrates from vX to vY, - * we should note that as a local end in vX). - */ - - add(address, Disposition.START, startedLocal); - } - - /** - * Ends a local at the given address. - * - * @param address >= 0; the address - * @param endedLocal non-null; spec representing the local being ended - */ - public void endLocal(int address, RegisterSpec endedLocal) { - int regNum = endedLocal.getReg(); - - endedLocal = filterSpec(endedLocal); - aboutToProcess(address, regNum); - - int endAt = endIndices[regNum]; - - if (endAt >= 0) { - /* - * The local in the given register is already ended. - * Silently return without adding anything to the result. - */ - return; - } - - // Check for start and end at the same address. - if (checkForEmptyRange(address, endedLocal)) { - return; - } - - add(address, Disposition.END_SIMPLY, endedLocal); - } - - /** - * Helper for {@link #endLocal}, which handles the cases where - * and end local is issued at the same address as a start local - * for the same register. If this case is found, then this - * method will remove the start (as the local was never actually - * active), update the {@link #endIndices} to be accurate, and - * if needed update the newly-active end to reflect an altered - * disposition. - * - * @param address >= 0; the address - * @param endedLocal non-null; spec representing the local being ended - * @return {@code true} iff this method found the case in question - * and adjusted things accordingly - */ - private boolean checkForEmptyRange(int address, - RegisterSpec endedLocal) { - int at = result.size() - 1; - Entry entry; - - // Look for a previous entry at the same address. - for (/*at*/; at >= 0; at--) { - entry = result.get(at); - - if (entry == null) { - continue; - } - - if (entry.getAddress() != address) { - // We didn't find any match at the same address. - return false; - } - - if (entry.matches(endedLocal)) { - break; - } - } - - /* - * In fact, we found that the endedLocal had started at the - * same address, so do all the requisite cleanup. - */ - - regs.remove(endedLocal); - result.set(at, null); - nullResultCount++; - - int regNum = endedLocal.getReg(); - boolean found = false; - entry = null; - - // Now look back further to update where the register ended. - for (at--; at >= 0; at--) { - entry = result.get(at); - - if (entry == null) { - continue; - } - - if (entry.getRegisterSpec().getReg() == regNum) { - found = true; - break; - } - } - - if (found) { - // We found an end for the same register. - endIndices[regNum] = at; - - if (entry.getAddress() == address) { - /* - * It's still the same address, so update the - * disposition. - */ - result.set(at, - entry.withDisposition(Disposition.END_SIMPLY)); - } - } - - return true; - } - - /** - * Converts a given spec into the form acceptable for use in a - * local list. This, in particular, transforms the "known - * null" type into simply {@code Object}. This method needs to - * be called for any spec that is on its way into a locals - * list. - * - * <p>This isn't necessarily the cleanest way to achieve the - * goal of not representing known nulls in a locals list, but - * it gets the job done.</p> - * - * @param orig null-ok; the original spec - * @return null-ok; an appropriately modified spec, or the - * original if nothing needs to be done - */ - private static RegisterSpec filterSpec(RegisterSpec orig) { - if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) { - return orig.withType(Type.OBJECT); - } - - return orig; - } - - /** - * Adds an entry to the result, updating the adjunct tables - * accordingly. - * - * @param address >= 0; the address - * @param disposition non-null; the disposition - * @param spec non-null; spec representing the local - */ - private void add(int address, Disposition disposition, - RegisterSpec spec) { - int regNum = spec.getReg(); - - result.add(new Entry(address, disposition, spec)); - - if (disposition == Disposition.START) { - regs.put(spec); - endIndices[regNum] = -1; - } else { - regs.remove(spec); - endIndices[regNum] = result.size() - 1; - } - } - - /** - * Adds or updates an end local (changing its disposition). - * - * @param address >= 0; the address - * @param disposition non-null; the disposition - * @param spec non-null; spec representing the local - */ - private void addOrUpdateEnd(int address, Disposition disposition, - RegisterSpec spec) { - if (disposition == Disposition.START) { - throw new RuntimeException("shouldn't happen"); - } - - int regNum = spec.getReg(); - int endAt = endIndices[regNum]; - - if (endAt >= 0) { - Entry endEntry = result.get(endAt); - if ((endEntry.getAddress() == address) && - endEntry.getRegisterSpec().equals(spec)) { - result.set(endAt, endEntry.withDisposition(disposition)); - regs.remove(spec); - return; - } - } - - add(address, disposition, spec); - } - - /** - * Finishes processing altogether and gets the result. - * - * @return non-null; the result list - */ - public LocalList finish() { - aboutToProcess(Integer.MAX_VALUE, 0); - - int resultSz = result.size(); - int finalSz = resultSz - nullResultCount; - - if (finalSz == 0) { - return EMPTY; - } - - /* - * Collect an array of only the non-null entries, and then - * sort it to get a consistent order for everything: Local - * ends and starts for a given address could come in any - * order, but we want ends before starts as well as - * registers in order (within ends or starts). - */ - - Entry[] resultArr = new Entry[finalSz]; - - if (resultSz == finalSz) { - result.toArray(resultArr); - } else { - int at = 0; - for (Entry e : result) { - if (e != null) { - resultArr[at++] = e; - } - } - } - - Arrays.sort(resultArr); - - LocalList resultList = new LocalList(finalSz); - - for (int i = 0; i < finalSz; i++) { - resultList.set(i, resultArr[i]); - } - - resultList.setImmutable(); - return resultList; - } - } } diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java index 3ffd27696..519452b64 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java @@ -16,8 +16,6 @@ package com.android.dx.dex.file; -import com.android.dx.dex.code.DalvCode; -import com.android.dx.dex.code.DalvInsnList; import com.android.dx.dex.code.LocalList; import com.android.dx.dex.code.PositionList; import com.android.dx.rop.cst.CstMethodRef; @@ -43,28 +41,20 @@ import static com.android.dx.dex.file.DebugInfoConstants.*; public class DebugInfoDecoder { /** encoded debug info */ private final byte[] encoded; - /** positions decoded */ private final ArrayList<PositionEntry> positions; - /** locals decoded */ private final ArrayList<LocalEntry> locals; - /** size of code block in code units */ private final int codesize; - /** indexed by register, the last local variable live in a reg */ private final LocalEntry[] lastEntryForReg; - /** method descriptor of method this debug info is for */ private final Prototype desc; - /** true if method is static */ private final boolean isStatic; - /** dex file this debug info will be stored in */ private final DexFile file; - /** * register size, in register units, of the register space * used by this method @@ -91,11 +81,8 @@ public class DebugInfoDecoder { * @param ref method descriptor of method this debug info is for * @param file dex file this debug info will be stored in */ - DebugInfoDecoder(byte[] encoded, int codesize, int regSize, + DebugInfoDecoder (byte[] encoded, int codesize, int regSize, boolean isStatic, CstMethodRef ref, DexFile file) { - if (encoded == null) { - throw new NullPointerException("encoded == null"); - } this.encoded = encoded; this.isStatic = isStatic; @@ -103,37 +90,24 @@ public class DebugInfoDecoder { this.file = file; this.regSize = regSize; - positions = new ArrayList<PositionEntry>(); - locals = new ArrayList<LocalEntry>(); + positions = new ArrayList(); + locals = new ArrayList(); this.codesize = codesize; lastEntryForReg = new LocalEntry[regSize]; - int idx = -1; - - try { - idx = file.getStringIds().indexOf(new CstUtf8("this")); - } catch (IllegalArgumentException ex) { - /* - * Silently tolerate not finding "this". It just means that - * no method has local variable info that looks like - * a standard instance method. - */ - } - - thisStringIdx = idx; + thisStringIdx = file.getStringIds().indexOf(new CstUtf8("this")); } /** * An entry in the resulting postions table */ - static private class PositionEntry { + static class PositionEntry { /** bytecode address */ - public int address; - + int address; /** line number */ - public int line; + int line; - public PositionEntry(int address, int line) { + PositionEntry(int address, int line) { this.address = address; this.line = line; } @@ -142,40 +116,37 @@ public class DebugInfoDecoder { /** * An entry in the resulting locals table */ - static private class LocalEntry { - /** address of event */ - public int address; + static class LocalEntry { + LocalEntry(int start, int reg, int nameIndex, int typeIndex, + int signatureIndex) { + this.start = start; + this.reg = reg; + this.nameIndex = nameIndex; + this.typeIndex = typeIndex; + this.signatureIndex = signatureIndex; + } - /** {@code true} iff it's a local start */ - public boolean isStart; + /** start of address range */ + int start; + + /** + * End of address range. Initialized to MAX_VALUE here but will + * be set to no more than 1 + max bytecode address of method. + */ + int end = Integer.MAX_VALUE; /** register number */ - public int reg; + int reg; /** index of name in strings table */ - public int nameIndex; + int nameIndex; /** index of type in types table */ - public int typeIndex; + int typeIndex; /** index of type signature in strings table */ - public int signatureIndex; + int signatureIndex; - public LocalEntry(int address, boolean isStart, int reg, int nameIndex, - int typeIndex, int signatureIndex) { - this.address = address; - this.isStart = isStart; - this.reg = reg; - this.nameIndex = nameIndex; - this.typeIndex = typeIndex; - this.signatureIndex = signatureIndex; - } - - public String toString() { - return String.format("[%x %s v%d %04x %04x %04x]", - address, isStart ? "start" : "end", reg, - nameIndex, typeIndex, signatureIndex); - } } /** @@ -195,6 +166,13 @@ public class DebugInfoDecoder { * @return locals list in ascending address order. */ public List<LocalEntry> getLocals() { + // TODO move this loop: + // Any variable that didnt end ends now + for (LocalEntry local: locals) { + if (local.end == Integer.MAX_VALUE) { + local.end = codesize; + } + } return locals; } @@ -251,8 +229,8 @@ public class DebugInfoDecoder { if (!isStatic) { // Start off with implicit 'this' entry - LocalEntry thisEntry = - new LocalEntry(0, true, curReg, thisStringIdx, 0, 0); + LocalEntry thisEntry + = new LocalEntry(0, curReg, thisStringIdx, 0, 0); locals.add(thisEntry); lastEntryForReg[curReg] = thisEntry; curReg++; @@ -264,19 +242,15 @@ public class DebugInfoDecoder { int nameIdx = readStringIndex(bs); - if (nameIdx == -1) { - /* - * Unnamed parameter; often but not always filled in by an - * extended start op after the prologue - */ - le = new LocalEntry(0, true, curReg, -1, 0, 0); + if(nameIdx == -1) { + // unnamed parameter } else { - // TODO: Final 0 should be idx of paramType.getDescriptor(). - le = new LocalEntry(0, true, curReg, nameIdx, 0, 0); + // final '0' should be idx of paramType.getDescriptor() + le = new LocalEntry(0, curReg, nameIdx, 0, 0); + locals.add(le); + lastEntryForReg[curReg] = le; } - locals.add(le); - lastEntryForReg[curReg] = le; curReg += paramType.getCategory(); } @@ -295,7 +269,15 @@ public class DebugInfoDecoder { int nameIdx = readStringIndex(bs); int typeIdx = readStringIndex(bs); LocalEntry le = new LocalEntry( - address, true, reg, nameIdx, typeIdx, 0); + address, reg, nameIdx, typeIdx, 0); + + // a "start" is implicitly the "end" of whatever was + // previously defined in the register + if (lastEntryForReg[reg] != null + && lastEntryForReg[reg].end == Integer.MAX_VALUE) { + + lastEntryForReg[reg].end = address; + } locals.add(le); lastEntryForReg[reg] = le; @@ -308,7 +290,21 @@ public class DebugInfoDecoder { int typeIdx = readStringIndex(bs); int sigIdx = readStringIndex(bs); LocalEntry le = new LocalEntry( - address, true, reg, nameIdx, typeIdx, sigIdx); + address, reg, nameIdx, typeIdx, sigIdx); + + // a "start" is implicitly the "end" of whatever was + // previously defined in the register + if (lastEntryForReg[reg] != null + && lastEntryForReg[reg].end == Integer.MAX_VALUE) { + + lastEntryForReg[reg].end = address; + + // A 0-length entry. Almost certainly a "this" + // with a signature. + if (lastEntryForReg[reg].start == address) { + locals.remove(lastEntryForReg[reg]); + } + } locals.add(le); lastEntryForReg[reg] = le; @@ -323,17 +319,16 @@ public class DebugInfoDecoder { try { prevle = lastEntryForReg[reg]; - if (prevle.isStart) { - throw new RuntimeException("nonsensical " - + "RESTART_LOCAL on live register v" - + reg); + if (lastEntryForReg[reg].end == Integer.MAX_VALUE) { + throw new RuntimeException ("nonsensical " + + "RESTART_LOCAL on live register v"+reg); } - - le = new LocalEntry(address, true, reg, + le = new LocalEntry(address, reg, prevle.nameIndex, prevle.typeIndex, 0); + } catch (NullPointerException ex) { - throw new RuntimeException( - "Encountered RESTART_LOCAL on new v" + reg); + throw new RuntimeException + ("Encountered RESTART_LOCAL on new v" +reg); } locals.add(le); @@ -343,27 +338,20 @@ public class DebugInfoDecoder { case DBG_END_LOCAL: { int reg = readUnsignedLeb128(bs); - LocalEntry prevle; - LocalEntry le; - - try { - prevle = lastEntryForReg[reg]; - - if (!prevle.isStart) { - throw new RuntimeException("nonsensical " - + "END_LOCAL on dead register v" + reg); + boolean found = false; + for (int i = locals.size() - 1; i >= 0; i--) { + if (locals.get(i).reg == reg) { + locals.get(i).end = address; + found = true; + break; } - - le = new LocalEntry(address, false, reg, - prevle.nameIndex, prevle.typeIndex, - prevle.signatureIndex); - } catch (NullPointerException ex) { - throw new RuntimeException( - "Encountered END_LOCAL on new v" + reg); } - locals.add(le); - lastEntryForReg[reg] = le; + if (!found) { + throw new RuntimeException( + "Encountered LOCAL_END without local start: v" + + reg); + } } break; @@ -415,48 +403,40 @@ public class DebugInfoDecoder { * throwing an exception if they do not match. Used to validate the * encoder. * - * @param info encoded debug info - * @param file non-null; file to refer to during decoding - * @param ref non-null; method whose info is being decoded - * @param code non-null; original code object that was encoded - * @param isStatic whether the method is static + * @param linecodes encoded debug info + * @param codeSize size of insn block in code units + * @param countRegisters size of used register block in register units + * @param pl position list to verify against + * @param ll locals list to verify against. */ - public static void validateEncode(byte[] info, DexFile file, - CstMethodRef ref, DalvCode code, boolean isStatic) { - PositionList pl = code.getPositions(); - LocalList ll = code.getLocals(); - DalvInsnList insns = code.getInsns(); - int codeSize = insns.codeSize(); - int countRegisters = insns.getRegistersSize(); - + public static void validateEncode(byte[] linecodes, int codeSize, + int countRegisters, PositionList pl, LocalList ll, + boolean isStatic, CstMethodRef ref, DexFile file) { + try { - validateEncode0(info, codeSize, countRegisters, + validateEncode0(linecodes, codeSize, countRegisters, isStatic, ref, file, pl, ll); } catch (RuntimeException ex) { - System.err.println("instructions:"); - insns.debugPrint(System.err, " ", true); - System.err.println("local list:"); - ll.debugPrint(System.err, " "); +// System.err.println(ex.toString() +// + " while processing " + ref.toHuman()); throw ExceptionWithContext.withContext(ex, "while processing " + ref.toHuman()); } } + - private static void validateEncode0(byte[] info, int codeSize, + private static void validateEncode0(byte[] linecodes, int codeSize, int countRegisters, boolean isStatic, CstMethodRef ref, DexFile file, PositionList pl, LocalList ll) { DebugInfoDecoder decoder - = new DebugInfoDecoder(info, codeSize, countRegisters, + = new DebugInfoDecoder(linecodes, codeSize, countRegisters, isStatic, ref, file); decoder.decode(); - /* - * Go through the decoded position entries, matching up - * with original entries. - */ + List<PositionEntry> decodedEntries; - List<PositionEntry> decodedEntries = decoder.getPositionList(); + decodedEntries = decoder.getPositionList(); if (decodedEntries.size() != pl.size()) { throw new RuntimeException( @@ -464,7 +444,7 @@ public class DebugInfoDecoder { + decodedEntries.size() + " expected " + pl.size()); } - for (PositionEntry entry : decodedEntries) { + for (PositionEntry entry: decodedEntries) { boolean found = false; for (int i = pl.size() - 1; i >= 0; i--) { PositionList.Entry ple = pl.get(i); @@ -482,111 +462,41 @@ public class DebugInfoDecoder { } } - /* - * Go through the original local list, in order, matching up - * with decoded entries. - */ + List<LocalEntry> decodedLocals; + + decodedLocals = decoder.getLocals(); - List<LocalEntry> decodedLocals = decoder.getLocals(); - int thisStringIdx = decoder.thisStringIdx; - int decodedSz = decodedLocals.size(); int paramBase = decoder.getParamBase(); - /* - * Preflight to fill in any parameters that were skipped in - * the prologue (including an implied "this") but then - * identified by full signature. - */ - for (int i = 0; i < decodedSz; i++) { - LocalEntry entry = decodedLocals.get(i); - int idx = entry.nameIndex; - - if ((idx < 0) || (idx == thisStringIdx)) { - for (int j = i + 1; j < decodedSz; j++) { - LocalEntry e2 = decodedLocals.get(j); - if (e2.address != 0) { - break; - } - if ((entry.reg == e2.reg) && e2.isStart) { - decodedLocals.set(i, e2); - decodedLocals.remove(j); - decodedSz--; - break; - } - } - } - } - - int origSz = ll.size(); - int decodeAt = 0; - boolean problem = false; + int matchedLocalsEntries = 0; - for (int i = 0; i < origSz; i++) { - LocalList.Entry origEntry = ll.get(i); + for (LocalEntry entry: decodedLocals) { + boolean found = false; + for (int i = ll.size() - 1; i >= 0; i--) { + LocalList.Entry le = ll.get(i); - if (origEntry.getDisposition() - == LocalList.Disposition.END_REPLACED) { /* - * The encoded list doesn't represent replacements, so - * ignore them for the sake of comparison. + * If an entry is a method parameter, then the original + * entry may not be marked as starting at 0. However, the + * end address should still match. */ - continue; - } - - LocalEntry decodedEntry; - - do { - decodedEntry = decodedLocals.get(decodeAt); - if (decodedEntry.nameIndex >= 0) { + if ((entry.start == le.getStart() + || (entry.start == 0 && entry.reg >= paramBase)) + && entry.end == le.getEnd() + && entry.reg == le.getRegister()) { + found = true; + matchedLocalsEntries++; break; } - /* - * A negative name index means this is an anonymous - * parameter, and we shouldn't expect to see it in the - * original list. So, skip it. - */ - decodeAt++; - } while (decodeAt < decodedSz); - - int decodedAddress = decodedEntry.address; - - if (decodedEntry.reg != origEntry.getRegister()) { - System.err.println("local register mismatch at orig " + i + - " / decoded " + decodeAt); - problem = true; - break; - } - - if (decodedEntry.isStart != origEntry.isStart()) { - System.err.println("local start/end mismatch at orig " + i + - " / decoded " + decodeAt); - problem = true; - break; } - /* - * The secondary check here accounts for the fact that a - * parameter might not be marked as starting at 0 in the - * original list. - */ - if ((decodedAddress != origEntry.getAddress()) - && !((decodedAddress == 0) - && (decodedEntry.reg >= paramBase))) { - System.err.println("local address mismatch at orig " + i + - " / decoded " + decodeAt); - problem = true; - break; + if (!found) { + throw new RuntimeException("Could not match local entry"); } - - decodeAt++; } - if (problem) { - System.err.println("decoded locals:"); - for (LocalEntry e : decodedLocals) { - System.err.println(" " + e); - } - throw new RuntimeException("local table problem"); + if (matchedLocalsEntries != ll.size()) { + throw new RuntimeException("Locals tables did not match"); } } diff --git a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java index 780e18d5b..49781bdd8 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java @@ -56,10 +56,10 @@ public final class DebugInfoEncoder { private static final boolean DEBUG = false; /** null-ok; positions (line numbers) to encode */ - private final PositionList positions; + private final PositionList positionlist; /** null-ok; local variables to encode */ - private final LocalList locals; + private final LocalList locallist; private final ByteArrayAnnotatedOutput output; private final DexFile file; @@ -108,8 +108,8 @@ public final class DebugInfoEncoder { public DebugInfoEncoder(PositionList pl, LocalList ll, DexFile file, int codeSize, int regSize, boolean isStatic, CstMethodRef ref) { - this.positions = pl; - this.locals = ll; + this.positionlist = pl; + this.locallist = ll; this.file = file; output = new ByteArrayAnnotatedOutput(); this.desc = ref.getPrototype(); @@ -193,11 +193,18 @@ public final class DebugInfoEncoder { private byte[] convert0() throws IOException { ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions(); - ArrayList<LocalList.Entry> methodArgs = extractMethodArguments(); + ArrayList<LocalList.Entry> sortedLocalsStart = buildLocalsStart(); + + // Parameter locals are removed from sortedLocalsStart here. + ArrayList<LocalList.Entry> methodArgs + = extractMethodArguments(sortedLocalsStart); + + ArrayList<LocalList.Entry> sortedLocalsEnd + = buildLocalsEnd(sortedLocalsStart); emitHeader(sortedPositions, methodArgs); - // TODO: Make this mark be the actual prologue end. + // TODO: Make this mark the actual prologue end. output.writeByte(DBG_SET_PROLOGUE_END); if (annotateTo != null || debugPrint != null) { @@ -205,37 +212,56 @@ public final class DebugInfoEncoder { } int szp = sortedPositions.size(); - int szl = locals.size(); + int szl = sortedLocalsStart.size(); // Current index in sortedPositions int curp = 0; - // Current index in locals - int curl = 0; + // Current index in sortedLocalsStart + int curls = 0; + // Current index in sortedLocalsEnd + int curle = 0; for (;;) { /* * Emit any information for the current address. */ - curl = emitLocalsAtAddress(curl); + curle = emitLocalEndsAtAddress(curle, sortedLocalsEnd, curls, + sortedLocalsStart); + + /* + * Our locals-sorted-by-range-end has reached the end + * of the code block. Ignore everything else. + */ + if (address == codeSize) { + curle = szl; + } + + curls = emitLocalStartsAtAddress(curls, sortedLocalsStart); + curp = emitPositionsAtAddress(curp, sortedPositions); /* * Figure out what the next important address is. */ - int nextAddrL = Integer.MAX_VALUE; // local variable - int nextAddrP = Integer.MAX_VALUE; // position (line number) + int nextAddrLS = Integer.MAX_VALUE; // local start + int nextAddrLE = Integer.MAX_VALUE; // local end + int nextAddrP = Integer.MAX_VALUE; // position (line number) + + if (curls < szl) { + nextAddrLS = sortedLocalsStart.get(curls).getStart(); + } - if (curl < szl) { - nextAddrL = locals.get(curl).getAddress(); + if (curle < szl) { + nextAddrLE = sortedLocalsEnd.get(curle).getEnd(); } if (curp < szp) { nextAddrP = sortedPositions.get(curp).getAddress(); } - int next = Math.min(nextAddrP, nextAddrL); + int next = Math.min(nextAddrP, Math.min(nextAddrLS, nextAddrLE)); // No next important address == done. if (next == Integer.MAX_VALUE) { @@ -247,7 +273,7 @@ public final class DebugInfoEncoder { * block, stop here. Those are implied anyway. */ if (next == codeSize - && nextAddrL == Integer.MAX_VALUE + && nextAddrLS == Integer.MAX_VALUE && nextAddrP == Integer.MAX_VALUE) { break; } @@ -266,24 +292,76 @@ public final class DebugInfoEncoder { } /** - * Emits all local variable activity that occurs at the current - * {@link #address} starting at the given index into {@code - * locals} and including all subsequent activity at the same - * address. + * Emits all local ends that occur at the current <code>address</code> * - * @param curl Current index in locals - * @return new value for <code>curl</code> + * @param curle Current index in sortedLocalsEnd + * @param sortedLocalsEnd Locals, sorted by ascending end address + * @param curls Current index in sortedLocalsStart + * @param sortedLocalsStart Locals, sorted by ascending start address + * @return new value for <code>curle</code> * @throws IOException */ - private int emitLocalsAtAddress(int curl) + private int emitLocalEndsAtAddress(int curle, + ArrayList<LocalList.Entry> sortedLocalsEnd, int curls, + ArrayList<LocalList.Entry> sortedLocalsStart) throws IOException { - int sz = locals.size(); - // TODO: Don't emit ends implied by starts. + int szl = sortedLocalsEnd.size(); - while ((curl < sz) - && (locals.get(curl).getAddress() == address)) { - LocalList.Entry lle = locals.get(curl++); + // Ignore "local ends" at end of code. + while (curle < szl + && sortedLocalsEnd.get(curle).getEnd() == address + && address != codeSize) { + + boolean skipLocalEnd = false; + + /* + * Check to see if there's a range-start that appears at + * the same address for the same register. If so, the + * end-range is implicit so skip it. + */ + for (int j = curls; j < szl + && sortedLocalsStart.get(j).getStart() == address + ; j++) { + + if (sortedLocalsStart.get(j).getRegister() + == sortedLocalsEnd.get(curle).getRegister()) { + skipLocalEnd = true; + + if (DEBUG) { + System.err.printf("skip local end v%d\n", + sortedLocalsEnd.get(curle).getRegister()); + } + break; + } + } + + if (!skipLocalEnd) { + emitLocalEnd(sortedLocalsEnd.get(curle)); + } + + curle++; + } + return curle; + } + + /** + * Emits all local starts that occur at the current <code>address</code> + * + * @param curls Current index in sortedLocalsStart + * @param sortedLocalsStart Locals, sorted by ascending start address + * @return new value for <code>curls</code> + * @throws IOException + */ + private int emitLocalStartsAtAddress(int curls, + ArrayList<LocalList.Entry> sortedLocalsStart) + throws IOException { + + int szl = sortedLocalsStart.size(); + + while (curls < szl + && sortedLocalsStart.get(curls).getStart() == address) { + LocalList.Entry lle = sortedLocalsStart.get(curls++); int reg = lle.getRegister(); LocalList.Entry prevlle = lastEntryForReg[reg]; @@ -296,45 +374,26 @@ public final class DebugInfoEncoder { continue; } - // At this point we have a new entry one way or another. + // At this point we have a new live entry one way or another. lastEntryForReg[reg] = lle; - if (lle.isStart()) { - if ((prevlle != null) && lle.matches(prevlle)) { + if ((prevlle != null) && lle.matches(prevlle)) { + if (prevlle.getEnd() == lle.getStart()) { /* - * The previous local in this register has the same - * name and type as the one being introduced now, so - * use the more efficient "restart" form. + * There is nothing more to do in this case: It's + * an adjacent range with the same register. The + * previous emitLocalEndsAtAddress() call skipped + * this local end, so we'll skip this local start + * as well. */ - if (prevlle.isStart()) { - /* - * We should never be handed a start when a - * a matching local is already active. - */ - throw new RuntimeException("shouldn't happen"); - } - emitLocalRestart(lle); } else { - emitLocalStart(lle); + emitLocalRestart(lle); } } else { - /* - * Only emit a local end if it is *not* due to a direct - * replacement. Direct replacements imply an end of the - * previous local in the same register. - * - * TODO: Make sure the runtime can deal with implied - * local ends from category-2 interactions, and when so, - * also stop emitting local ends for those cases. - */ - if (lle.getDisposition() - != LocalList.Disposition.END_REPLACED) { - emitLocalEnd(lle); - } + emitLocalStart(lle); } } - - return curl; + return curls; } /** @@ -465,7 +524,7 @@ public final class DebugInfoEncoder { * a LOCAL_RESTART_EXTENDED */ - for (LocalList.Entry arg : lastEntryForReg) { + for (LocalList.Entry arg: lastEntryForReg) { if (arg == null) { continue; } @@ -484,11 +543,11 @@ public final class DebugInfoEncoder { * @return A sorted positions list */ private ArrayList<PositionList.Entry> buildSortedPositions() { - int sz = (positions == null) ? 0 : positions.size(); + int sz = (positionlist == null) ? 0 : positionlist.size(); ArrayList<PositionList.Entry> result = new ArrayList(sz); for (int i = 0; i < sz; i++) { - result.add(positions.get(i)); + result.add(positionlist.get(i)); } // Sort ascending by address. @@ -505,6 +564,58 @@ public final class DebugInfoEncoder { } /** + * Builds a list of locals entries sorted by ascending start address. + * + * @return A sorted locals list list + */ + private ArrayList<LocalList.Entry> buildLocalsStart() { + int sz = (locallist == null) ? 0 : locallist.size(); + ArrayList<LocalList.Entry> result = new ArrayList(sz); + + // Add all the entries + for (int i = 0; i < sz; i++) { + LocalList.Entry e = locallist.get(i); + result.add(locallist.get(i)); + } + + // Sort ascending by start address. + Collections.sort (result, new Comparator<LocalList.Entry>() { + public int compare (LocalList.Entry a, LocalList.Entry b) { + return a.getStart() - b.getStart(); + } + + public boolean equals (Object obj) { + return obj == this; + } + }); + return result; + } + + /** + * Builds a list of locals entries sorted by ascending end address. + * + * @param list locals list in any order + * @return a sorted locals list + */ + private ArrayList<LocalList.Entry> buildLocalsEnd( + ArrayList<LocalList.Entry> list) { + + ArrayList<LocalList.Entry> sortedLocalsEnd = new ArrayList(list); + + // Sort ascending by end address. + Collections.sort (sortedLocalsEnd, new Comparator<LocalList.Entry>() { + public int compare (LocalList.Entry a, LocalList.Entry b) { + return a.getEnd() - b.getEnd(); + } + + public boolean equals (Object obj) { + return obj == this; + } + }); + return sortedLocalsEnd; + } + + /** * Gets the register that begins the method's parameter range (including * the 'this' parameter for non-static methods). The range continues until * <code>regSize</code> @@ -521,18 +632,24 @@ public final class DebugInfoEncoder { * from the input list and sorted by ascending register in the * returned list. * + * @param sortedLocals locals list, sorted by ascending start address, + * to process; left unmodified * @return list of non-<code>this</code> method argument locals, * sorted by ascending register */ - private ArrayList<LocalList.Entry> extractMethodArguments() { + private ArrayList<LocalList.Entry> extractMethodArguments ( + ArrayList<LocalList.Entry> sortedLocals) { + ArrayList<LocalList.Entry> result = new ArrayList(desc.getParameterTypes().size()); + int argBase = getParamBase(); + BitSet seen = new BitSet(regSize - argBase); - int sz = locals.size(); + int sz = sortedLocals.size(); for (int i = 0; i < sz; i++) { - LocalList.Entry e = locals.get(i); + LocalList.Entry e = sortedLocals.get(i); int reg = e.getRegister(); if (reg < argBase) { @@ -549,12 +666,12 @@ public final class DebugInfoEncoder { } // Sort by ascending register. - Collections.sort(result, new Comparator<LocalList.Entry>() { - public int compare(LocalList.Entry a, LocalList.Entry b) { + Collections.sort (result, new Comparator<LocalList.Entry>() { + public int compare (LocalList.Entry a, LocalList.Entry b) { return a.getRegister() - b.getRegister(); } - public boolean equals(Object obj) { + public boolean equals (Object obj) { return obj == this; } }); diff --git a/dx/src/com/android/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/dx/dex/file/DebugInfoItem.java index 0e4329b14..b0745935d 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoItem.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java @@ -141,37 +141,6 @@ public class DebugInfoItem extends OffsettedItem { */ private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint, AnnotatedOutput out, boolean consume) { - byte[] result = encode0(file, prefix, debugPrint, out, consume); - - if (ENABLE_ENCODER_SELF_CHECK && (file != null)) { - try { - DebugInfoDecoder.validateEncode(result, file, ref, code, - isStatic); - } catch (RuntimeException ex) { - // Reconvert, annotating to System.err. - encode0(file, "", new PrintWriter(System.err, true), null, - false); - throw ex; - } - } - - return result; - } - - /** - * Helper for {@link #encode} to do most of the work. - * - * @param file null-ok; file to refer to during encoding - * @param prefix null-ok; prefix to attach to each line of output - * @param debugPrint null-ok; if specified, an alternate output for - * annotations - * @param out null-ok; if specified, where annotations should go - * @param consume whether to claim to have consumed output for - * <code>out</code> - * @return non-null; the encoded array - */ - private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint, - AnnotatedOutput out, boolean consume) { PositionList positions = code.getPositions(); LocalList locals = code.getLocals(); DalvInsnList insns = code.getInsns(); @@ -187,8 +156,13 @@ public class DebugInfoItem extends OffsettedItem { if ((debugPrint == null) && (out == null)) { result = encoder.convert(); } else { - result = encoder.convertAndAnnotate(prefix, debugPrint, out, - consume); + result = encoder.convertAndAnnotate( + prefix, debugPrint, out, consume); + } + + if (ENABLE_ENCODER_SELF_CHECK && (file != null)) { + DebugInfoDecoder.validateEncode(encoded, codeSize, + regSize, positions, locals, isStatic, ref, file); } return result; diff --git a/dx/src/com/android/dx/rop/code/LocalItem.java b/dx/src/com/android/dx/rop/code/LocalItem.java index bac6ce2a4..b1e1a4be6 100644 --- a/dx/src/com/android/dx/rop/code/LocalItem.java +++ b/dx/src/com/android/dx/rop/code/LocalItem.java @@ -22,6 +22,7 @@ import com.android.dx.rop.cst.CstUtf8; * A local variable item: either a name or a signature or both. */ public class LocalItem implements Comparable<LocalItem> { + /** null-ok; local variable name */ private final CstUtf8 name; @@ -37,7 +38,7 @@ public class LocalItem implements Comparable<LocalItem> { * @param signature null-ok; local variable signature * @return non-null; appropriate instance. */ - public static LocalItem make(CstUtf8 name, CstUtf8 signature) { + public static LocalItem make (CstUtf8 name, CstUtf8 signature) { if (name == null && signature == null) { return null; } @@ -51,14 +52,14 @@ public class LocalItem implements Comparable<LocalItem> { * @param name null-ok; local variable name * @param signature null-ok; local variable signature */ - private LocalItem(CstUtf8 name, CstUtf8 signature) { + private LocalItem (CstUtf8 name, CstUtf8 signature) { this.name = name; this.signature = signature; } /** {@inheritDoc} */ @Override - public boolean equals(Object other) { + public boolean equals (Object other) { if (!(other instanceof LocalItem)) { return false; } @@ -88,7 +89,7 @@ public class LocalItem implements Comparable<LocalItem> { } /** {@inheritDoc} */ - public int compareTo(LocalItem local) { + public int compareTo (LocalItem local) { int ret; ret = compareHandlesNulls(name, local.name); @@ -105,7 +106,7 @@ public class LocalItem implements Comparable<LocalItem> { /** {@inheritDoc} */ @Override - public int hashCode() { + public int hashCode () { return (name == null ? 0 : name.hashCode()) * 31 + (signature == null ? 0 : signature.hashCode()); } @@ -120,7 +121,7 @@ public class LocalItem implements Comparable<LocalItem> { } return "[" + (name == null ? "" : name.toQuoted()) - + "|" + (signature == null ? "" : signature.toQuoted()); + + "|" + (signature == null ? "" : signature.toQuoted()); } /** diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java index 73af91fbc..09f7f1829 100644 --- a/dx/src/com/android/dx/rop/code/RegisterSpec.java +++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java @@ -29,7 +29,7 @@ import java.util.HashMap; * destinations of register-based operations. */ public final class RegisterSpec - implements TypeBearer, ToHuman, Comparable<RegisterSpec> { + implements TypeBearer, ToHuman { /** non-null; string to prefix register numbers with */ public static final String PREFIX = "v"; @@ -97,8 +97,7 @@ public final class RegisterSpec * @param local non-null; the associated local variable * @return non-null; an appropriately-constructed instance */ - public static RegisterSpec make(int reg, TypeBearer type, - LocalItem local) { + public static RegisterSpec make(int reg, TypeBearer type, LocalItem local) { if (local == null) { throw new NullPointerException("local == null"); } @@ -173,43 +172,6 @@ public final class RegisterSpec } /** - * Like {@code equals}, but only consider the simple types of the - * registers. That is, this compares {@code getType()} on the types - * to ignore whatever arbitrary extra stuff might be carried around - * by an outer {@link TypeBearer}. - * - * @param other null-ok; spec to compare to - * @return {@code true} iff {@code this} and {@code other} are equal - * in the stated way - */ - public boolean equalsUsingSimpleType(RegisterSpec other) { - if (!matchesVariable(other)) { - return false; - } - - return (reg == other.reg); - } - - /** - * Like {@link #equalsUsingSimpleType} but ignoring the register number. - * This is useful to determine if two instances refer to the "same" - * local variable. - * - * @param other null-ok; spec to compare to - * @return {@code true} iff {@code this} and {@code other} are equal - * in the stated way - */ - public boolean matchesVariable(RegisterSpec other) { - if (other == null) { - return false; - } - - return type.getType().equals(other.type.getType()) - && ((local == other.local) - || ((local != null) && local.equals(other.local))); - } - - /** * Helper for {@link #equals} and {@link #ForComparison.equals}, * which actually does the test. * @@ -226,35 +188,6 @@ public final class RegisterSpec || ((this.local != null) && this.local.equals(local))); } - /** - * Compares by (in priority order) register number, unwrapped type - * (that is types not {@link TypeBearer}s, and local info. - * - * @param other non-null; spec to compare to - * @return {@code -1..1}; standard result of comparison - */ - public int compareTo(RegisterSpec other) { - if (this.reg < other.reg) { - return -1; - } else if (this.reg > other.reg) { - return 1; - } - - int compare = type.getType().compareTo(other.type.getType()); - - if (compare != 0) { - return compare; - } - - if (this.local == null) { - return (other.local == null) ? 0 : -1; - } else if (other.local == null) { - return 1; - } - - return this.local.compareTo(other.local); - } - /** {@inheritDoc} */ @Override public int hashCode() { @@ -359,8 +292,6 @@ public final class RegisterSpec * Gets the category of this instance's type. This is just a convenient * shorthand for <code>getType().getCategory()</code>. * - * @see #isCategory1 - * @see #isCategory2 * @return 1..2; the category of this instance's type */ public int getCategory() { @@ -368,30 +299,6 @@ public final class RegisterSpec } /** - * Gets whether this instance's type is category 1. This is just a - * convenient shorthand for <code>getType().isCategory1()</code>. - * - * @see #getCategory - * @see #isCategory2 - * @return whether or not this instance's type is of category 1 - */ - public boolean isCategory1() { - return type.getType().isCategory1(); - } - - /** - * Gets whether this instance's type is category 2. This is just a - * convenient shorthand for <code>getType().isCategory2()</code>. - * - * @see #getCategory - * @see #isCategory1 - * @return whether or not this instance's type is of category 2 - */ - public boolean isCategory2() { - return type.getType().isCategory2(); - } - - /** * Gets the string form for just the register number of this instance. * * @return non-null; the register string form @@ -416,16 +323,16 @@ public final class RegisterSpec * are <code>equals()</code>, then the intersection's type bearer * is the one from this instance. Otherwise, the intersection's * type bearer is the <code>getType()</code> of this instance.</li> - * <li>If the locals are <code>equals()</code>, then the local info - * of the intersection is the local info of this instance. Otherwise, - * the local info of the intersection is <code>null</code>.</li> + * <li>If the locals are <code>equals()</code>, then the local info of the + * intersection is the local info of this instance. Otherwise, the local info + * of the intersection is <code>null</code>.</li> * </ul> * * @param other null-ok; instance to intersect with (or <code>null</code>) - * @param localPrimary whether local variables are primary to the - * intersection; if <code>true</code>, then the only non-null - * results occur when registers being intersected have equal local - * infos (or both have <code>null</code> local infos) + * @param localPrimary whether local variables are primary to + * the intersection; if <code>true</code>, then the only non-null + * results occur when registers being intersected have equal local infos (or + * both have <code>null</code> local infos) * @return null-ok; the intersection */ public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) { @@ -439,8 +346,7 @@ public final class RegisterSpec } LocalItem resultLocal = - ((local == null) || !local.equals(other.getLocalItem())) - ? null : local; + ((local == null) || !local.equals(other.getLocalItem())) ? null : local; boolean sameName = (resultLocal == local); if (localPrimary && !sameName) { diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java index adc77c35e..4eb2f65e8 100644 --- a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java +++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java @@ -187,47 +187,16 @@ public final class RegisterSpecSet } /** - * Returns the spec in this set that's currently associated with a - * given local (type, name, and signature), or {@code null} if there is - * none. This ignores the register number of the given spec but - * matches on everything else. - * - * @param spec non-null; local to look for - * @return null-ok; first register found that matches, if any - */ - public RegisterSpec findMatchingLocal(RegisterSpec spec) { - int length = specs.length; - - for (int reg = 0; reg < length; reg++) { - RegisterSpec s = specs[reg]; - - if (s == null) { - continue; - } - - if (spec.matchesVariable(s)) { - return s; - } - } - - return null; - } - - /** * Returns the spec in this set that's currently associated with a given - * local (name and signature), or {@code null} if there is none. + * name, or null if there is none. * * @param local non-null; local item to search for - * @return null-ok; first register found with matching name and signature + * @return null-ok; first register found with name. */ public RegisterSpec localItemToSpec(LocalItem local) { - int length = specs.length; - - for (int reg = 0; reg < length; reg++) { - RegisterSpec spec = specs[reg]; - - if ((spec != null) && local.equals(spec.getLocalItem())) { - return spec; + for (int reg = 0; reg < specs.length; reg++) { + if (specs[reg] != null && local.equals(specs[reg].getLocalItem())) { + return specs[reg]; } } @@ -291,22 +260,6 @@ public final class RegisterSpecSet } /** - * Put the entire contents of the given set into this one. - * - * @param set non-null; the set to put into this instance - */ - public void putAll(RegisterSpecSet set) { - int max = set.getMaxSize(); - - for (int i = 0; i < max; i++) { - RegisterSpec spec = set.get(i); - if (spec != null) { - put(spec); - } - } - } - - /** * Intersects this instance with the given one, modifying this * instance. The intersection consists of the pairwise * {@link RegisterSpec#intersect} of corresponding elements from diff --git a/libcore/icu/src/main/native/DecimalFormatInterface.cpp b/libcore/icu/src/main/native/DecimalFormatInterface.cpp index 6221826f0..fb5cf9fa8 100644 --- a/libcore/icu/src/main/native/DecimalFormatInterface.cpp +++ b/libcore/icu/src/main/native/DecimalFormatInterface.cpp @@ -50,13 +50,13 @@ static UBool icuError(JNIEnv *env, UErrorCode errorcode) default : exception = env->FindClass("java/lang/RuntimeException"); } - + return (env->ThrowNew(exception, emsg) != 0); } return 0; } -static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, +static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, jstring pattern) { // the errorcode returned by unum_open @@ -70,9 +70,9 @@ static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, const char *localeChars = env->GetStringUTFChars(locale, NULL); // open a default type number format - UNumberFormat *fmt = unum_open(UNUM_PATTERN_DECIMAL, pattChars, pattLen, + UNumberFormat *fmt = unum_open(UNUM_PATTERN_DECIMAL, pattChars, pattLen, localeChars, NULL, &status); - + // release the allocated strings env->ReleaseStringChars(pattern, pattChars); env->ReleaseStringUTFChars(locale, localeChars); @@ -88,20 +88,20 @@ static jint openDecimalFormatImpl(JNIEnv *env, jclass clazz, jstring locale, static void closeDecimalFormatImpl(JNIEnv *env, jclass clazz, jint addr) { - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; // close this number format unum_close(fmt); } -static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol, +static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol, jstring text) { - + // the errorcode returned by unum_setSymbol UErrorCode status = U_ZERO_ERROR; - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; // prepare the symbol string for the call to unum_setSymbol @@ -109,9 +109,9 @@ static void setSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol, int textLen = env->GetStringLength(text); // set the symbol - unum_setSymbol(fmt, (UNumberFormatSymbol) symbol, textChars, textLen, + unum_setSymbol(fmt, (UNumberFormatSymbol) symbol, textChars, textLen, &status); - + // release previously allocated space env->ReleaseStringChars(text, textChars); @@ -126,14 +126,14 @@ static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) { // the errorcode returned by unum_getSymbol UErrorCode status = U_ZERO_ERROR; - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; UChar* result = NULL; resultlength=0; // find out how long the result will be - reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, + reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, resultlength, &status); result = NULL; @@ -141,7 +141,7 @@ static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) { status=U_ZERO_ERROR; resultlength=reslenneeded+1; result=(UChar*)malloc(sizeof(UChar) * resultlength); - reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, + reslenneeded=unum_getSymbol(fmt, (UNumberFormatSymbol) symbol, result, resultlength, &status); } if (icuError(env, status) != FALSE) { @@ -154,17 +154,17 @@ static jstring getSymbol(JNIEnv *env, jclass clazz, jint addr, jint symbol) { return res; } - -static void setAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, + +static void setAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, jint value) { - + UNumberFormat *fmt = (UNumberFormat *)(int)addr; unum_setAttribute(fmt, (UNumberFormatAttribute) symbol, value); } - + static jint getAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol) { - + UNumberFormat *fmt = (UNumberFormat *)(int)addr; int res = unum_getAttribute(fmt, (UNumberFormatAttribute) symbol); @@ -172,27 +172,27 @@ static jint getAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol) { return res; } -static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, +static void setTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol, jstring text) { // the errorcode returned by unum_setTextAttribute UErrorCode status = U_ZERO_ERROR; - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; const UChar *textChars = env->GetStringChars(text, NULL); int textLen = env->GetStringLength(text); - unum_setTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, textChars, + unum_setTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, textChars, textLen, &status); - + env->ReleaseStringChars(text, textChars); icuError(env, status); } -static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, +static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, jint symbol) { uint32_t resultlength, reslenneeded; @@ -200,14 +200,14 @@ static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, // the errorcode returned by unum_getTextAttribute UErrorCode status = U_ZERO_ERROR; - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; UChar* result = NULL; resultlength=0; // find out how long the result will be - reslenneeded=unum_getTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, + reslenneeded=unum_getTextAttribute(fmt, (UNumberFormatTextAttribute) symbol, result, resultlength, &status); result = NULL; @@ -215,8 +215,8 @@ static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, status=U_ZERO_ERROR; resultlength=reslenneeded+1; result=(UChar*)malloc(sizeof(UChar) * resultlength); - reslenneeded=unum_getTextAttribute(fmt, - (UNumberFormatTextAttribute) symbol, result, resultlength, + reslenneeded=unum_getTextAttribute(fmt, + (UNumberFormatTextAttribute) symbol, result, resultlength, &status); } if (icuError(env, status) != FALSE) { @@ -230,13 +230,13 @@ static jstring getTextAttribute(JNIEnv *env, jclass clazz, jint addr, return res; } -static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, +static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, jboolean localized, jstring pattern) { // the errorcode returned by unum_applyPattern UErrorCode status = U_ZERO_ERROR; - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; const UChar *pattChars = env->GetStringChars(pattern, NULL); @@ -249,7 +249,7 @@ static void applyPatternImpl(JNIEnv *env, jclass clazz, jint addr, icuError(env, status); } -static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, +static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, jboolean localized) { uint32_t resultlength, reslenneeded; @@ -257,7 +257,7 @@ static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, // the errorcode returned by unum_toPattern UErrorCode status = U_ZERO_ERROR; - // get the pointer to the number format + // get the pointer to the number format UNumberFormat *fmt = (UNumberFormat *)(int)addr; UChar* result = NULL; @@ -271,7 +271,7 @@ static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, status=U_ZERO_ERROR; resultlength=reslenneeded+1; result=(UChar*)malloc(sizeof(UChar) * resultlength); - reslenneeded=unum_toPattern(fmt, localized, result, resultlength, + reslenneeded=unum_toPattern(fmt, localized, result, resultlength, &status); } if (icuError(env, status) != FALSE) { @@ -284,19 +284,19 @@ static jstring toPatternImpl(JNIEnv *env, jclass clazz, jint addr, return res; } - -static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value, + +static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value, jobject field, jstring fieldType, jobject attributes) { const char * fieldPositionClassName = "java/text/FieldPosition"; const char * stringBufferClassName = "java/lang/StringBuffer"; jclass fieldPositionClass = env->FindClass(fieldPositionClassName); jclass stringBufferClass = env->FindClass(stringBufferClassName); - jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, + jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, "setBeginIndex", "(I)V"); - jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, + jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, "setEndIndex", "(I)V"); - jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); const char * fieldName = NULL; @@ -331,7 +331,7 @@ static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value, if(status==U_BUFFER_OVERFLOW_ERROR) { status=U_ZERO_ERROR; - result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); res->extract(result, reslenneeded + 1, status); } @@ -393,18 +393,18 @@ static jstring formatLong(JNIEnv *env, jclass clazz, jint addr, jlong value, return resulting; } -static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value, +static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value, jobject field, jstring fieldType, jobject attributes) { const char * fieldPositionClassName = "java/text/FieldPosition"; const char * stringBufferClassName = "java/lang/StringBuffer"; jclass fieldPositionClass = env->FindClass(fieldPositionClassName); jclass stringBufferClass = env->FindClass(stringBufferClassName); - jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, + jmethodID setBeginIndexMethodID = env->GetMethodID(fieldPositionClass, "setBeginIndex", "(I)V"); - jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, + jmethodID setEndIndexMethodID = env->GetMethodID(fieldPositionClass, "setEndIndex", "(I)V"); - jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); const char * fieldName = NULL; @@ -439,7 +439,7 @@ static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value, if(status==U_BUFFER_OVERFLOW_ERROR) { status=U_ZERO_ERROR; - result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); res->extract(result, reslenneeded + 1, status); @@ -502,7 +502,7 @@ static jstring formatDouble(JNIEnv *env, jclass clazz, jint addr, jdouble value, return resulting; } -static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring value, +static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring value, jobject field, jstring fieldType, jobject attributes, jint scale) { // const char * valueUTF = env->GetStringUTFChars(value, NULL); @@ -521,36 +521,21 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val uint32_t reslenneeded; + bool isInteger = (scale == 0); + // prepare digit list - const char *valueChars = env->GetStringUTFChars(value, NULL); + const char *digits = env->GetStringUTFChars(value, NULL); - bool isInteger = (scale == 0); - bool isPositive = (*valueChars != '-'); - - // skip the '-' if the number is negative - const char *digits = (isPositive ? valueChars : valueChars + 1); - int length = strlen(digits); - - // The length of our digit list buffer must be the actual string length + 3, - // because ICU will append some additional characters at the head and at the - // tail of the string, in order to keep strtod() happy: - // - // - The sign "+" or "-" is appended at the head - // - The exponent "e" and the "\0" terminator is appended at the tail - // - // In retrospect, the changes to ICU's DigitList that were necessary for - // big numbers look a bit hacky. It would make sense to rework all this - // once ICU 4.x has been integrated into Android. Ideally, big number - // support would make it into ICU itself, so we don't need our private - // fix anymore. - DigitList digitList(length + 3); - digitList.fCount = length; + // length must be string lengt + 2 because there's an additional + // character in front of the string ("+" or "-") and a \0 at the end + DigitList digitList(strlen(digits) + 2); + digitList.fCount = strlen(digits); strcpy(digitList.fDigits, digits); - env->ReleaseStringUTFChars(value, valueChars); + env->ReleaseStringUTFChars(value, digits); digitList.fDecimalAt = digitList.fCount - scale; - digitList.fIsPositive = isPositive; + digitList.fIsPositive = (*digits != '-'); digitList.fRoundingMode = DecimalFormat::kRoundHalfUp; UChar *result = NULL; @@ -563,9 +548,10 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val UErrorCode status = U_ZERO_ERROR; DecimalFormat::AttributeBuffer *attrBuffer = NULL; - attrBuffer = (DecimalFormat::AttributeBuffer *) calloc(sizeof(DecimalFormat::AttributeBuffer), 1); + attrBuffer = (DecimalFormat::AttributeBuffer *) malloc(sizeof(DecimalFormat::AttributeBuffer)); attrBuffer->bufferSize = 128; - attrBuffer->buffer = (char *) calloc(129 * sizeof(char), 1); + attrBuffer->buffer = (char *) malloc(129 * sizeof(char)); + attrBuffer->buffer[0] = '\0'; DecimalFormat *fmt = (DecimalFormat *)(int)addr; @@ -578,7 +564,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val if(status==U_BUFFER_OVERFLOW_ERROR) { status=U_ZERO_ERROR; - result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); + result = (UChar*)malloc(sizeof(UChar) * (reslenneeded + 1)); res.extract(result, reslenneeded + 1, status); @@ -598,7 +584,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val } free(attrBuffer->buffer); free(attrBuffer); - return NULL; + return NULL; } int attrLength = (strlen(attrBuffer->buffer) + 1 ); @@ -610,7 +596,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val // prepare the classes and method ids const char * stringBufferClassName = "java/lang/StringBuffer"; jclass stringBufferClass = env->FindClass(stringBufferClassName); - jmethodID appendMethodID = env->GetMethodID(stringBufferClass, + jmethodID appendMethodID = env->GetMethodID(stringBufferClass, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); jstring attrString = env->NewStringUTF(attrBuffer->buffer + 1); // cut off the leading ';' @@ -669,7 +655,7 @@ static jstring formatDigitList(JNIEnv *env, jclass clazz, jint addr, jstring val return resulting; } -static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, +static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, jobject position) { const char * textUTF = env->GetStringUTFChars(text, NULL); @@ -694,11 +680,11 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, jclass bigDecimalClass = env->FindClass(bigDecimalClassName); jclass bigIntegerClass = env->FindClass(bigIntegerClassName); - jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, + jmethodID getIndexMethodID = env->GetMethodID(parsePositionClass, "getIndex", "()I"); - jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass, + jmethodID setIndexMethodID = env->GetMethodID(parsePositionClass, "setIndex", "(I)V"); - jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass, + jmethodID setErrorIndexMethodID = env->GetMethodID(parsePositionClass, "setErrorIndex", "(I)V"); jmethodID longInitMethodID = env->GetMethodID(longClass, "<init>", "(J)V"); @@ -710,8 +696,8 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, bool resultAssigned; int parsePos = env->CallIntMethod(position, getIndexMethodID, NULL); - // make sure the ParsePosition is valid. Actually icu4c would parse a number - // correctly even if the parsePosition is set to -1, but since the RI fails + // make sure the ParsePosition is valid. Actually icu4c would parse a number + // correctly even if the parsePosition is set to -1, but since the RI fails // for that case we have to fail too if(parsePos < 0 || parsePos > strlength) { return NULL; @@ -721,9 +707,9 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, const UnicodeString src((UChar*)str, strlength, strlength); ParsePosition pp; - + pp.setIndex(parsePos); - + DigitList digits; ((const DecimalFormat*)fmt)->parse(src, resultAssigned, res, pp, FALSE, digits); @@ -733,8 +719,8 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, if(pp.getErrorIndex() == -1) { parsePos = pp.getIndex(); } else { - env->CallVoidMethod(position, setErrorIndexMethodID, - (jint) pp.getErrorIndex()); + env->CallVoidMethod(position, setErrorIndexMethodID, + (jint) pp.getErrorIndex()); return NULL; } @@ -759,17 +745,17 @@ static jobject parse(JNIEnv *env, jclass clazz, jint addr, jstring text, case Formattable::kDouble: resultDouble = res.getDouble(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); - return env->NewObject(doubleClass, dblInitMethodID, + return env->NewObject(doubleClass, dblInitMethodID, (jdouble) resultDouble); case Formattable::kLong: resultLong = res.getLong(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); - return env->NewObject(longClass, longInitMethodID, + return env->NewObject(longClass, longInitMethodID, (jlong) resultLong); case Formattable::kInt64: resultInt64 = res.getInt64(); env->CallVoidMethod(position, setIndexMethodID, (jint) parsePos); - return env->NewObject(longClass, longInitMethodID, + return env->NewObject(longClass, longInitMethodID, (jlong) resultInt64); default: return NULL; @@ -819,7 +805,7 @@ static jint cloneImpl(JNIEnv *env, jclass clazz, jint addr) { static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - {"openDecimalFormatImpl", "(Ljava/lang/String;Ljava/lang/String;)I", + {"openDecimalFormatImpl", "(Ljava/lang/String;Ljava/lang/String;)I", (void*) openDecimalFormatImpl}, {"closeDecimalFormatImpl", "(I)V", (void*) closeDecimalFormatImpl}, {"setSymbol", "(IILjava/lang/String;)V", (void*) setSymbol}, @@ -830,22 +816,22 @@ static JNINativeMethod gMethods[] = { {"getTextAttribute", "(II)Ljava/lang/String;", (void*) getTextAttribute}, {"applyPatternImpl", "(IZLjava/lang/String;)V", (void*) applyPatternImpl}, {"toPatternImpl", "(IZ)Ljava/lang/String;", (void*) toPatternImpl}, - {"format", - "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", + {"format", + "(IJLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", (void*) formatLong}, - {"format", - "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", + {"format", + "(IDLjava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;)Ljava/lang/String;", (void*) formatDouble}, - {"format", - "(ILjava/lang/String;Ljava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;I)Ljava/lang/String;", + {"format", + "(ILjava/lang/String;Ljava/text/FieldPosition;Ljava/lang/String;Ljava/lang/StringBuffer;I)Ljava/lang/String;", (void*) formatDigitList}, - {"parse", - "(ILjava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;", + {"parse", + "(ILjava/lang/String;Ljava/text/ParsePosition;)Ljava/lang/Number;", (void*) parse}, {"cloneImpl", "(I)I", (void*) cloneImpl} }; int register_com_ibm_icu4jni_text_NativeDecimalFormat(JNIEnv* env) { - return jniRegisterNativeMethods(env, - "com/ibm/icu4jni/text/NativeDecimalFormat", gMethods, + return jniRegisterNativeMethods(env, + "com/ibm/icu4jni/text/NativeDecimalFormat", gMethods, NELEM(gMethods)); } diff --git a/libcore/luni/src/main/java/java/io/BufferedInputStream.java b/libcore/luni/src/main/java/java/io/BufferedInputStream.java index 0b9afc329..1720366dc 100644 --- a/libcore/luni/src/main/java/java/io/BufferedInputStream.java +++ b/libcore/luni/src/main/java/java/io/BufferedInputStream.java @@ -108,7 +108,7 @@ public class BufferedInputStream extends FilterInputStream { Logger.global.info( "Default buffer size used in BufferedInputStream " + "constructor. It would be " + - "better to be explicit if an 8k buffer is required."); + "better to be explicit if a 8k buffer is required."); // END android-added } diff --git a/libcore/luni/src/main/java/java/io/BufferedOutputStream.java b/libcore/luni/src/main/java/java/io/BufferedOutputStream.java index 835d13f15..55419d1ec 100644 --- a/libcore/luni/src/main/java/java/io/BufferedOutputStream.java +++ b/libcore/luni/src/main/java/java/io/BufferedOutputStream.java @@ -80,7 +80,7 @@ public class BufferedOutputStream extends FilterOutputStream { Logger.global.info( "Default buffer size used in BufferedOutputStream " + "constructor. It would be " + - "better to be explicit if an 8k buffer is required."); + "better to be explicit if a 8k buffer is required."); // END android-added } diff --git a/libcore/luni/src/main/java/java/io/BufferedReader.java b/libcore/luni/src/main/java/java/io/BufferedReader.java index e82e53855..c490b89bf 100644 --- a/libcore/luni/src/main/java/java/io/BufferedReader.java +++ b/libcore/luni/src/main/java/java/io/BufferedReader.java @@ -80,7 +80,7 @@ public class BufferedReader extends Reader { Logger.global.info( "Default buffer size used in BufferedReader " + "constructor. It would be " + - "better to be explicit if an 8k-char buffer is required."); + "better to be explicit if a 8k-char buffer is required."); // END android-added } diff --git a/libcore/luni/src/main/java/java/io/BufferedWriter.java b/libcore/luni/src/main/java/java/io/BufferedWriter.java index 761b9c5a4..66bfcc9be 100644 --- a/libcore/luni/src/main/java/java/io/BufferedWriter.java +++ b/libcore/luni/src/main/java/java/io/BufferedWriter.java @@ -80,7 +80,7 @@ public class BufferedWriter extends Writer { Logger.global.info( "Default buffer size used in BufferedWriter " + "constructor. It would be " + - "better to be explicit if an 8k-char buffer is required."); + "better to be explicit if a 8k-char buffer is required."); // END android-added } diff --git a/libcore/luni/src/main/java/java/util/ArrayList.java b/libcore/luni/src/main/java/java/util/ArrayList.java index 3bae372ac..5ea316a23 100644 --- a/libcore/luni/src/main/java/java/util/ArrayList.java +++ b/libcore/luni/src/main/java/java/util/ArrayList.java @@ -112,7 +112,7 @@ public class ArrayList<E> extends AbstractList<E> implements List<E>, Cloneable, * @param object * the object to add. * @throws IndexOutOfBoundsException - * when {@code location < 0 || > size()} + * when {@code location < 0 || >= size()} * @since Android 1.0 */ @Override diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringTest.java index 30510c28e..6c995bb81 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringTest.java @@ -17,14 +17,15 @@ package org.apache.harmony.luni.tests.java.lang; +import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; +import dalvik.annotation.TestTargetClass; import junit.framework.TestCase; import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; +import java.lang.reflect.Constructor; @TestTargetClass(String.class) public class StringTest extends TestCase { @@ -700,40 +701,4 @@ public class StringTest extends TestCase { } catch (IndexOutOfBoundsException e) { } } - - @TestTargetNew( - level = TestLevel.ADDITIONAL, - notes = "Regression test for some existing bugs and crashes", - method = "format", - args = { String.class, Object.class } - ) - public void testProblemCases() { - BigDecimal[] input = new BigDecimal[] { - new BigDecimal("20.00000"), - new BigDecimal("20.000000"), - new BigDecimal(".2"), - new BigDecimal("2"), - new BigDecimal("-2"), - new BigDecimal("200000000000000000000000"), - new BigDecimal("20000000000000000000000000000000000000000000000000") - }; - - String[] output = new String[] { - "20.00", - "20.00", - "0.20", - "2.00", - "-2.00", - "200000000000000000000000.00", - "20000000000000000000000000000000000000000000000000.00" - }; - - for (int i = 0; i < input.length; i++) { - String result = String.format("%.2f", input[i]); - assertEquals("Format test for \"" + input[i] + "\" failed, " + - "expected=" + output[i] + ", " + - "actual=" + result, output[i], result); - } - } - } diff --git a/libcore/run-core-tests b/libcore/run-core-tests index 25e53ee31..3656e2d3c 100755 --- a/libcore/run-core-tests +++ b/libcore/run-core-tests @@ -26,5 +26,6 @@ mkdir $tmp chmod 777 $tmp exec dalvikvm -Duser.language=en -Duser.region=US -Djava.io.tmpdir=$tmp \ - -Xbootclasspath:$BOOTCLASSPATH:/system/framework/core-tests.jar \ + -Xbootclasspath:$BOOTCLASSPATH \ + -classpath /system/framework/core-tests.jar \ com.google.coretests.Main "$@" diff --git a/libcore/security/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java b/libcore/security/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java index 8f0d08bc8..035e92487 100644 --- a/libcore/security/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java +++ b/libcore/security/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java @@ -66,17 +66,6 @@ public class X509NameTokenizer { if (escaped || quoted) { - // BEGIN android-added - // copied from a newer version of BouncyCastle - if (c == '#' && buf.charAt(buf.length() - 1) == '=') - { - buf.append('\\'); - } - else if (c == '+' && seperator != '+') - { - buf.append('\\'); - } - // END android-added buf.append(c); escaped = false; } @@ -99,4 +88,4 @@ public class X509NameTokenizer index = end; return buf.toString().trim(); } -}
\ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java index 2b7c03e53..cb821c2ad 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java @@ -24,7 +24,6 @@ import java.security.Security; // BEGIN android-added import java.lang.reflect.Method; import java.net.UnknownHostException; -import java.util.logging.Logger; // END android-added import javax.net.SocketFactory; @@ -58,56 +57,60 @@ public abstract class SSLSocketFactory extends SocketFactory { * @since Android 1.0 */ public static SocketFactory getDefault() { - synchronized (SSLSocketFactory.class) { - if (defaultSocketFactory != null) { - // BEGIN android-added - log("SSLSocketFactory", "Using factory " + defaultSocketFactory); - // END android-added - return defaultSocketFactory; - } - if (defaultName == null) { - AccessController.doPrivileged(new java.security.PrivilegedAction(){ - public Object run() { - defaultName = Security.getProperty("ssl.SocketFactory.provider"); - if (defaultName != null) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if (cl == null) { - cl = ClassLoader.getSystemClassLoader(); - } - try { - defaultSocketFactory = (SocketFactory) Class.forName( - defaultName, true, cl).newInstance(); - } catch (Exception e) { - return e; - } - } - return null; - } - }); - } - - if (defaultSocketFactory == null) { - // Try to find in providers - SSLContext context = DefaultSSLContext.getContext(); - if (context != null) { - defaultSocketFactory = context.getSocketFactory(); - } - } - if (defaultSocketFactory == null) { - // Use internal implementation - defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed"); - } + if (defaultSocketFactory != null) { // BEGIN android-added log("SSLSocketFactory", "Using factory " + defaultSocketFactory); // END android-added return defaultSocketFactory; } + if (defaultName == null) { + AccessController.doPrivileged(new java.security.PrivilegedAction(){ + public Object run() { + defaultName = Security.getProperty("ssl.SocketFactory.provider"); + if (defaultName != null) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + try { + defaultSocketFactory = (SocketFactory) Class.forName( + defaultName, true, cl).newInstance(); + } catch (Exception e) { + return e; + } + } + return null; + } + }); + } + + if (defaultSocketFactory == null) { + // Try to find in providers + SSLContext context = DefaultSSLContext.getContext(); + if (context != null) { + defaultSocketFactory = context.getSocketFactory(); + } + } + if (defaultSocketFactory == null) { + // Use internal implementation + defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed"); + } + // BEGIN android-added + log("SSLSocketFactory", "Using factory " + defaultSocketFactory); + // END android-added + return defaultSocketFactory; } // BEGIN android-added @SuppressWarnings("unchecked") private static void log(String tag, String msg) { - Logger.getLogger(tag).info(msg); + try { + Class clazz = Class.forName("android.util.Log"); + Method method = clazz.getMethod("d", new Class[] { String.class, String.class }); + method.invoke(null, new Object[] { tag, msg }); + } catch (Exception ex) { + // Silently ignore. + } } // END android-added diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java deleted file mode 100644 index a95d38fbd..000000000 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AbstractSessionContext.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import java.util.*; -import java.util.logging.Level; -import java.io.*; - -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import javax.security.cert.X509Certificate; -import javax.security.cert.CertificateEncodingException; -import javax.security.cert.CertificateException; - -/** - * Supports SSL session caches. - */ -abstract class AbstractSessionContext implements SSLSessionContext { - - volatile int maximumSize; - volatile int timeout; - - final SSLParameters parameters; - - /** Identifies OpenSSL sessions. */ - static final int OPEN_SSL = 1; - - /** - * Constructs a new session context. - * - * @param parameters - * @param maximumSize of cache - * @param timeout for cache entries - */ - AbstractSessionContext(SSLParameters parameters, int maximumSize, - int timeout) { - this.parameters = parameters; - this.maximumSize = maximumSize; - this.timeout = timeout; - } - - /** - * Returns the collection of sessions ordered by least-recently-used first. - */ - abstract Iterator<SSLSession> sessionIterator(); - - public final Enumeration getIds() { - final Iterator<SSLSession> iterator = sessionIterator(); - return new Enumeration<byte[]>() { - public boolean hasMoreElements() { - return iterator.hasNext(); - } - public byte[] nextElement() { - return iterator.next().getId(); - } - }; - } - - public final int getSessionCacheSize() { - return maximumSize; - } - - public final int getSessionTimeout() { - return timeout; - } - - /** - * Makes sure cache size is < maximumSize. - */ - abstract void trimToSize(); - - public final void setSessionCacheSize(int size) - throws IllegalArgumentException { - if (size < 0) { - throw new IllegalArgumentException("size < 0"); - } - - int oldMaximum = maximumSize; - maximumSize = size; - - // Trim cache to size if necessary. - if (size < oldMaximum) { - trimToSize(); - } - } - - /** - * Converts the given session to bytes. - * - * @return session data as bytes or null if the session can't be converted - */ - byte[] toBytes(SSLSession session) { - // TODO: Support SSLSessionImpl, too. - if (!(session instanceof OpenSSLSessionImpl)) { - return null; - } - - OpenSSLSessionImpl sslSession = (OpenSSLSessionImpl) session; - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream daos = new DataOutputStream(baos); - - daos.writeInt(OPEN_SSL); // session type ID - - // Session data. - byte[] data = sslSession.getEncoded(); - daos.writeInt(data.length); - daos.write(data); - - // Certificates. - X509Certificate[] certs = session.getPeerCertificateChain(); - daos.writeInt(certs.length); - - // TODO: Call nativegetpeercertificates() - for (X509Certificate cert : certs) { - data = cert.getEncoded(); - daos.writeInt(data.length); - daos.write(data); - } - - return baos.toByteArray(); - } catch (IOException e) { - log(e); - return null; - } catch (CertificateEncodingException e) { - log(e); - return null; - } - } - - /** - * Creates a session from the given bytes. - * - * @return a session or null if the session can't be converted - */ - SSLSession toSession(byte[] data, String host, int port) { - ByteArrayInputStream bais = new ByteArrayInputStream(data); - DataInputStream dais = new DataInputStream(bais); - try { - int type = dais.readInt(); - if (type != OPEN_SSL) { - log(new AssertionError("Unexpected type ID: " + type)); - return null; - } - - int length = dais.readInt(); - byte[] sessionData = new byte[length]; - dais.readFully(sessionData); - - int count = dais.readInt(); - X509Certificate[] certs = new X509Certificate[count]; - for (int i = 0; i < count; i++) { - length = dais.readInt(); - byte[] certData = new byte[length]; - dais.readFully(certData); - certs[i] = X509Certificate.getInstance(certData); - } - - return new OpenSSLSessionImpl(sessionData, parameters, host, port, - certs, this); - } catch (IOException e) { - log(e); - return null; - } catch (CertificateException e) { - log(e); - return null; - } - } - - static void log(Throwable t) { - java.util.logging.Logger.global.log(Level.WARNING, - "Error converting session.", t); - } - - /** - * Byte array wrapper. Implements equals() and hashCode(). - */ - static class ByteArray { - - private final byte[] bytes; - - ByteArray(byte[] bytes) { - this.bytes = bytes; - } - - @Override - public int hashCode() { - return Arrays.hashCode(bytes); - } - - @Override - @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") - public boolean equals(Object o) { - ByteArray other = (ByteArray) o; - return Arrays.equals(bytes, other.bytes); - } - } -}
\ No newline at end of file diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java index 55a06f5c1..841909643 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java @@ -613,18 +613,22 @@ public class ClientHandshakeImpl extends HandshakeProtocol { host = engineOwner.getPeerHost(); port = engineOwner.getPeerPort(); } + // END android-changed if (host == null || port == -1) { return null; // starts new session } - ClientSessionContext context = parameters.getClientSessionContext(); - SSLSessionImpl session - = (SSLSessionImpl) context.getSession(host, port); - if (session != null) { - session = (SSLSessionImpl) session.clone(); + byte[] id; + SSLSession ses; + SSLSessionContext context = parameters.getClientSessionContext(); + for (Enumeration en = context.getIds(); en.hasMoreElements();) { + id = (byte[])en.nextElement(); + ses = context.getSession(id); + if (host.equals(ses.getPeerHost()) && port == ses.getPeerPort()) { + return (SSLSessionImpl)((SSLSessionImpl)ses).clone(); // resume + } } - return session; - // END android-changed + return null; // starts new session } } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java deleted file mode 100644 index 2c8738f15..000000000 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContext.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.HashMap; -import java.util.ArrayList; -import java.util.Arrays; - -import javax.net.ssl.SSLSession; - -/** - * Caches client sessions. Indexes by host and port. Users are typically - * looking to reuse any session for a given host and port. Users of the - * standard API are forced to iterate over the sessions semi-linearly as - * opposed to in constant time. - */ -public class ClientSessionContext extends AbstractSessionContext { - - /* - * We don't care about timeouts in the client implementation. Trying - * to reuse an expired session and having to start a new one requires no - * more effort than starting a new one, so you might as well try to reuse - * one on the off chance it's still valid. - */ - - /** Sessions indexed by host and port in access order. */ - final Map<HostAndPort, SSLSession> sessions - = new LinkedHashMap<HostAndPort, SSLSession>() { - @Override - protected boolean removeEldestEntry( - Map.Entry<HostAndPort, SSLSession> eldest) { - // Called while lock is held on sessions. - boolean remove = maximumSize > 0 && size() > maximumSize; - if (remove) { - removeById(eldest.getValue()); - } - return remove; - } - }; - - /** - * Sessions indexed by ID. Initialized on demand. Protected from concurrent - * access by holding a lock on sessions. - */ - Map<ByteArray, SSLSession> sessionsById; - - final SSLClientSessionCache persistentCache; - - public ClientSessionContext(SSLParameters parameters, - SSLClientSessionCache persistentCache) { - super(parameters, 10, 0); - this.persistentCache = persistentCache; - } - - public final void setSessionTimeout(int seconds) - throws IllegalArgumentException { - if (seconds < 0) { - throw new IllegalArgumentException("seconds < 0"); - } - timeout = seconds; - } - - Iterator<SSLSession> sessionIterator() { - synchronized (sessions) { - SSLSession[] array = sessions.values().toArray( - new SSLSession[sessions.size()]); - return Arrays.asList(array).iterator(); - } - } - - void trimToSize() { - synchronized (sessions) { - int size = sessions.size(); - if (size > maximumSize) { - int removals = size - maximumSize; - Iterator<SSLSession> i = sessions.values().iterator(); - do { - removeById(i.next()); - i.remove(); - } while (--removals > 0); - } - } - } - - void removeById(SSLSession session) { - if (sessionsById != null) { - sessionsById.remove(new ByteArray(session.getId())); - } - } - - /** - * {@inheritDoc} - * - * @see #getSession(String, int) for an implementation-specific but more - * efficient approach - */ - public SSLSession getSession(byte[] sessionId) { - /* - * This method is typically used in conjunction with getIds() to - * iterate over the sessions linearly, so it doesn't make sense for - * it to impact access order. - * - * It also doesn't load sessions from the persistent cache as doing - * so would likely force every session to load. - */ - - ByteArray id = new ByteArray(sessionId); - synchronized (sessions) { - indexById(); - return sessionsById.get(id); - } - } - - /** - * Ensures that the ID-based index is initialized. - */ - private void indexById() { - if (sessionsById == null) { - sessionsById = new HashMap<ByteArray, SSLSession>(); - for (SSLSession session : sessions.values()) { - sessionsById.put(new ByteArray(session.getId()), session); - } - } - } - - /** - * Adds the given session to the ID-based index if the index has already - * been initialized. - */ - private void indexById(SSLSession session) { - if (sessionsById != null) { - sessionsById.put(new ByteArray(session.getId()), session); - } - } - - /** - * Finds a cached session for the given host name and port. - * - * @param host of server - * @param port of server - * @return cached session or null if none found - */ - public SSLSession getSession(String host, int port) { - synchronized (sessions) { - SSLSession session = sessions.get(new HostAndPort(host, port)); - if (session != null) { - return session; - } - } - - // Look in persistent cache. - if (persistentCache != null) { - byte[] data = persistentCache.getSessionData(host, port); - if (data != null) { - SSLSession session = toSession(data, host, port); - if (session != null) { - synchronized (sessions) { - sessions.put(new HostAndPort(host, port), session); - indexById(session); - } - return session; - } - } - } - - return null; - } - - void putSession(SSLSession session) { - HostAndPort key = new HostAndPort(session.getPeerHost(), - session.getPeerPort()); - synchronized (sessions) { - sessions.put(key, session); - indexById(session); - } - - // TODO: This in a background thread. - if (persistentCache != null) { - byte[] data = toBytes(session); - if (data != null) { - persistentCache.putSessionData(session, data); - } - } - } - - static class HostAndPort { - final String host; - final int port; - - HostAndPort(String host, int port) { - this.host = host; - this.port = port; - } - - @Override - public int hashCode() { - return host.hashCode() * 31 + port; - } - - @Override - @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") - public boolean equals(Object o) { - HostAndPort other = (HostAndPort) o; - return host.equals(other.host) && port == other.port; - } - } -} diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java deleted file mode 100644 index ab097e4af..000000000 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCache.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import javax.net.ssl.SSLSession; -import java.util.Map; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Set; -import java.util.TreeSet; -import java.util.Iterator; -import java.util.Arrays; -import java.util.logging.Level; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - -/** - * File-based cache implementation. Only one process should access the - * underlying directory at a time. - */ -public class FileClientSessionCache { - - static final int MAX_SIZE = 20; - - static final java.util.logging.Logger logger - = java.util.logging.Logger.getLogger( - FileClientSessionCache.class.getName()); - - private FileClientSessionCache() {} - - /** - * This cache creates one file per SSL session using "host.port" for - * the file name. Files are created or replaced when session data is put - * in the cache (see {@link #putSessionData}). Files are read on - * cache hits, but not on cache misses. - * - * <p>When the number of session files exceeds MAX_SIZE, we delete the - * least-recently-used file. We don't current persist the last access time, - * so the ordering actually ends up being least-recently-modified in some - * cases and even just "not accessed in this process" if the filesystem - * doesn't track last modified times. - */ - static class Impl implements SSLClientSessionCache { - - /** Directory to store session files in. */ - final File directory; - - /** - * Map of name -> File. Keeps track of the order files were accessed in. - */ - Map<String, File> accessOrder = newAccessOrder(); - - /** The number of files on disk. */ - int size; - - /** - * The initial set of files. We use this to defer adding information - * about all files to accessOrder until necessary. - */ - String[] initialFiles; - - /** - * Constructs a new cache backed by the given directory. - */ - Impl(File directory) throws IOException { - boolean exists = directory.exists(); - if (exists && !directory.isDirectory()) { - throw new IOException(directory - + " exists but is not a directory."); - } - - if (exists) { - // Read and sort initial list of files. We defer adding - // information about these files to accessOrder until necessary - // (see indexFiles()). Sorting the list enables us to detect - // cache misses in getSessionData(). - // Note: Sorting an array here was faster than creating a - // HashSet on Dalvik. - initialFiles = directory.list(); - Arrays.sort(initialFiles); - size = initialFiles.length; - } else { - // Create directory. - if (!directory.mkdirs()) { - throw new IOException("Creation of " + directory - + " directory failed."); - } - size = 0; - } - - this.directory = directory; - } - - /** - * Creates a new access-ordered linked hash map. - */ - private static Map<String, File> newAccessOrder() { - return new LinkedHashMap<String, File>( - MAX_SIZE, 0.75f, true /* access order */); - } - - /** - * Gets the file name for the given host and port. - */ - private static String fileName(String host, int port) { - if (host == null) { - throw new NullPointerException("host"); - } - return host + "." + port; - } - - public synchronized byte[] getSessionData(String host, int port) { - /* - * Note: This method is only called when the in-memory cache - * in SSLSessionContext misses, so it would be unnecesarily - * rendundant for this cache to store data in memory. - */ - - String name = fileName(host, port); - File file = accessOrder.get(name); - - if (file == null) { - // File wasn't in access order. Check initialFiles... - if (initialFiles == null) { - // All files are in accessOrder, so it doesn't exist. - return null; - } - - // Look in initialFiles. - if (Arrays.binarySearch(initialFiles, name) < 0) { - // Not found. - return null; - } - - // The file is on disk but not in accessOrder yet. - file = new File(directory, name); - accessOrder.put(name, file); - } - - FileInputStream in; - try { - in = new FileInputStream(file); - } catch (FileNotFoundException e) { - logReadError(host, e); - return null; - } - try { - int size = (int) file.length(); - byte[] data = new byte[size]; - new DataInputStream(in).readFully(data); - logger.log(Level.FINE, "Read session for " + host + "."); - return data; - } catch (IOException e) { - logReadError(host, e); - return null; - } finally { - try { - in.close(); - } catch (IOException e) { /* ignore */ } - } - } - - static void logReadError(String host, Throwable t) { - logger.log(Level.INFO, "Error reading session data for " + host - + ".", t); - } - - public synchronized void putSessionData(SSLSession session, - byte[] sessionData) { - String host = session.getPeerHost(); - if (sessionData == null) { - throw new NullPointerException("sessionData"); - } - - String name = fileName(host, session.getPeerPort()); - File file = new File(directory, name); - - // Used to keep track of whether or not we're expanding the cache. - boolean existedBefore = file.exists(); - - FileOutputStream out; - try { - out = new FileOutputStream(file); - } catch (FileNotFoundException e) { - // We can't write to the file. - logWriteError(host, e); - return; - } - - // If we expanded the cache (by creating a new file)... - if (!existedBefore) { - size++; - - // Delete an old file if necessary. - makeRoom(); - } - - boolean writeSuccessful = false; - try { - out.write(sessionData); - writeSuccessful = true; - } catch (IOException e) { - logWriteError(host, e); - } finally { - boolean closeSuccessful = false; - try { - out.close(); - closeSuccessful = true; - } catch (IOException e) { - logWriteError(host, e); - } finally { - if (!writeSuccessful || !closeSuccessful) { - // Storage failed. Clean up. - delete(file); - } else { - // Success! - accessOrder.put(name, file); - logger.log(Level.FINE, "Stored session for " + host - + "."); - } - } - } - } - - /** - * Deletes old files if necessary. - */ - private void makeRoom() { - if (size <= MAX_SIZE) { - return; - } - - indexFiles(); - - // Delete LRUed files. - int removals = size - MAX_SIZE; - Iterator<File> i = accessOrder.values().iterator(); - do { - delete(i.next()); - i.remove(); - } while (--removals > 0); - } - - /** - * Lazily updates accessOrder to know about all files as opposed to - * just the files accessed since this process started. - */ - private void indexFiles() { - String[] initialFiles = this.initialFiles; - if (initialFiles != null) { - this.initialFiles = null; - - // Files on disk only, sorted by last modified time. - // TODO: Use last access time. - Set<CacheFile> diskOnly = new TreeSet<CacheFile>(); - for (String name : initialFiles) { - // If the file hasn't been accessed in this process... - if (!accessOrder.containsKey(name)) { - diskOnly.add(new CacheFile(directory, name)); - } - } - - if (!diskOnly.isEmpty()) { - // Add files not accessed in this process to the beginning - // of accessOrder. - Map<String, File> newOrder = newAccessOrder(); - for (CacheFile cacheFile : diskOnly) { - newOrder.put(cacheFile.name, cacheFile); - } - newOrder.putAll(accessOrder); - - this.accessOrder = newOrder; - } - } - } - - @SuppressWarnings("ThrowableInstanceNeverThrown") - private void delete(File file) { - if (!file.delete()) { - logger.log(Level.INFO, "Failed to delete " + file + ".", - new IOException()); - } - size--; - } - - static void logWriteError(String host, Throwable t) { - logger.log(Level.INFO, "Error writing session data for " - + host + ".", t); - } - } - - /** - * Maps directories to the cache instances that are backed by those - * directories. We synchronize access using the cache instance, so it's - * important that everyone shares the same instance. - */ - static final Map<File, FileClientSessionCache.Impl> caches - = new HashMap<File, FileClientSessionCache.Impl>(); - - /** - * Returns a cache backed by the given directory. Creates the directory - * (including parent directories) if necessary. This cache should have - * exclusive access to the given directory. - * - * @param directory to store files in - * @return a cache backed by the given directory - * @throws IOException if the file exists and is not a directory or if - * creating the directories fails - */ - public static synchronized SSLClientSessionCache usingDirectory( - File directory) throws IOException { - FileClientSessionCache.Impl cache = caches.get(directory); - if (cache == null) { - cache = new FileClientSessionCache.Impl(directory); - caches.put(directory, cache); - } - return cache; - } - - /** For testing. */ - static synchronized void reset() { - caches.clear(); - } - - /** A file containing a piece of cached data. */ - static class CacheFile extends File { - - final String name; - - CacheFile(File dir, String name) { - super(dir, name); - this.name = name; - } - - long lastModified = -1; - - @Override - public long lastModified() { - long lastModified = this.lastModified; - if (lastModified == -1) { - lastModified = this.lastModified = super.lastModified(); - } - return lastModified; - } - - @Override - public int compareTo(File another) { - // Sort by last modified time. - long result = lastModified() - another.lastModified(); - if (result == 0) { - return super.compareTo(another); - } - return result < 0 ? -1 : 1; - } - } -}
\ No newline at end of file diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java index e98590876..4fc6e9930 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLServerSocketImpl.java @@ -302,8 +302,7 @@ public class OpenSSLServerSocketImpl extends javax.net.ssl.SSLServerSocket { @Override public Socket accept() throws IOException { - OpenSSLSocketImpl socket - = new OpenSSLSocketImpl(sslParameters, ssl_op_no); + OpenSSLSocketImpl socket = new OpenSSLSocketImpl(sslParameters, ssl_op_no); implAccept(socket); socket.accept(ssl_ctx, client_mode); diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java index ca7d6f8b0..475d38818 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java @@ -23,7 +23,6 @@ import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Iterator; -import java.util.UnknownFormatConversionException; import java.util.Vector; import javax.net.ssl.SSLPeerUnverifiedException; @@ -56,41 +55,21 @@ public class OpenSSLSessionImpl implements SSLSession { private SSLParameters sslParameters; private String peerHost; private int peerPort; - private final SSLSessionContext sessionContext; /** * Class constructor creates an SSL session context given the appropriate * SSL parameters. - * - * @param session the Identifier for SSL session * @param sslParameters the SSL parameters like ciphers' suites etc. + * @param ssl the Identifier for SSL session */ - protected OpenSSLSessionImpl(int session, SSLParameters sslParameters, - String peerHost, int peerPort, SSLSessionContext sessionContext) { + protected OpenSSLSessionImpl(int session, SSLParameters sslParameters, String peerHost, int peerPort) { this.session = session; this.sslParameters = sslParameters; this.peerHost = peerHost; this.peerPort = peerPort; - this.sessionContext = sessionContext; } /** - * Constructs a session from a byte[]. - */ - OpenSSLSessionImpl(byte[] derData, SSLParameters sslParameters, - String peerHost, int peerPort, - javax.security.cert.X509Certificate[] peerCertificateChain, - SSLSessionContext sessionContext) - throws IOException { - this.sslParameters = sslParameters; - this.peerHost = peerHost; - this.peerPort = peerPort; - this.peerCertificateChain = peerCertificateChain; - this.sessionContext = sessionContext; - initializeNative(derData); - } - - /** * Returns the identifier of the actual OpenSSL session. */ private native byte[] nativegetid(); @@ -111,40 +90,6 @@ public class OpenSSLSessionImpl implements SSLSession { private native long nativegetcreationtime(); /** - * Serialize the native state of the session ( ID, cypher, keys - but - * not certs ), using openSSL i2d_SSL_SESSION() - * - * @return the DER encoding of the session. - */ - private native byte[] nativeserialize(); - - /** - * Create a SSL_SESSION object using d2i_SSL_SESSION. - */ - private native int nativedeserialize(byte[] data, int size); - - /** - * Get the session object in DER format. This allows saving the session - * data or sharing it with other processes. - */ - byte[] getEncoded() { - return nativeserialize(); - } - - /** - * Init the underlying native object from DER data. This - * allows loading the saved session. - * @throws IOException - */ - private void initializeNative(byte[] derData) throws IOException { - this.session = nativedeserialize(derData, derData.length); - if (this.session == 0) { - throw new IOException("Invalid session data"); - } - } - - - /** * Gets the creation time of the SSL session. * @return the session's creation time in milli seconds since 12.00 PM, * January 1st, 1970 @@ -392,7 +337,7 @@ public class OpenSSLSessionImpl implements SSLSession { if (sm != null) { sm.checkPermission(new SSLPermission("getSSLSessionContext")); } - return sessionContext; + return sslParameters.getClientSessionContext(); } /** @@ -402,7 +347,7 @@ public class OpenSSLSessionImpl implements SSLSession { * @return true if this session may be resumed. */ public boolean isValid() { - SSLSessionContext context = sessionContext; + SSLSessionContextImpl context = sslParameters.getClientSessionContext(); if (isValid && context != null && context.getSessionTimeout() != 0 @@ -427,7 +372,7 @@ public class OpenSSLSessionImpl implements SSLSession { * of security, by the full machinery of the <code>AccessController</code> * class. * - * @param name the name of the binding to find. + * @param <code>String name</code> the name of the binding to find. * @return the value bound to that name, or null if the binding does not * exist. * @throws <code>IllegalArgumentException</code> if the argument is null. @@ -471,9 +416,9 @@ public class OpenSSLSessionImpl implements SSLSession { * -data bounds are monitored, as a matter of security, by the full * machinery of the <code>AccessController</code> class. * - * @param name the name of the link (no null are + * @param <code>String name</code> the name of the link (no null are * accepted!) - * @param value data object that shall be bound to + * @param <code>Object value</code> data object that shall be bound to * name. * @throws <code>IllegalArgumentException</code> if one or both * argument(s) is null. @@ -499,7 +444,7 @@ public class OpenSSLSessionImpl implements SSLSession { * monitored, as a matter of security, by the full machinery of the * <code>AccessController</code> class. * - * @param name the name of the link (no null are + * @param <code>String name</code> the name of the link (no null are * accepted!) * @throws <code>IllegalArgumentException</code> if the argument is null. */ diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java index 87797363d..1d38ca9a4 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.lang.reflect.Method; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; @@ -30,6 +31,7 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; +import java.util.Enumeration; import java.util.logging.Level; import java.util.logging.Logger; @@ -56,7 +58,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { private int ssl; private InputStream is; private OutputStream os; - private final Object handshakeLock = new Object(); + private Object handshakeLock = new Object(); private Object readLock = new Object(); private Object writeLock = new Object(); private SSLParameters sslParameters; @@ -64,7 +66,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { private Socket socket; private boolean autoClose; private boolean handshakeStarted = false; - private ArrayList<HandshakeCompletedListener> listeners; + private ArrayList listeners; private long ssl_op_no = 0x00000000L; private int timeout = 0; private InetSocketAddress address; @@ -135,11 +137,11 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Class constructor with 2 parameters * - * @param sslParameters Parameters for the SSL + * @param <code>SSLParameters sslParameters</code> Parameters for the SSL * context - * @param ssl_op_no Parameter to set the enabled + * @param <code>long ssl_op_no</code> Parameter to set the enabled * protocols - * @throws IOException if network fails + * @throws <code>IOException</code> if network fails */ protected OpenSSLSocketImpl(SSLParameters sslParameters, long ssl_op_no) throws IOException { super(); @@ -151,9 +153,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Class constructor with 1 parameter * - * @param sslParameters Parameters for the SSL + * @param <code>SSLParameters sslParameters</code> Parameters for the SSL * context - * @throws IOException if network fails + * @throws <code>IOException</code> if network fails */ protected OpenSSLSocketImpl(SSLParameters sslParameters) throws IOException { super(); @@ -165,8 +167,11 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Class constructor with 3 parameters * - * @throws IOException if network fails - * @throws java.net.UnknownHostException host not defined + * @param <code> String host</code> + * @param <code>int port</code> + * @param <code>SSLParameters sslParameters</code> + * @throws <code>IOException</code> if network fails + * @throws <code>UnknownHostException</code> host not defined */ protected OpenSSLSocketImpl(String host, int port, SSLParameters sslParameters) @@ -181,8 +186,11 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Class constructor with 3 parameters: 1st is InetAddress * - * @throws IOException if network fails - * @throws java.net.UnknownHostException host not defined + * @param <code>InetAddress address</code> + * @param <code>int port</code> + * @param <code>SSLParameters sslParameters</code> + * @throws <code>IOException</code> if network fails + * @throws <code>UnknownHostException</code> host not defined */ protected OpenSSLSocketImpl(InetAddress address, int port, SSLParameters sslParameters) @@ -197,8 +205,13 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Class constructor with 5 parameters: 1st is host * - * @throws IOException if network fails - * @throws java.net.UnknownHostException host not defined + * @param <code>String host</code> + * @param <code>int port</code> + * @param <code>InetAddress localHost</code> + * @param <code>int localPort</code> + * @param <code>SSLParameters sslParameters</code> + * @throws <code>IOException</code> if network fails + * @throws <code>UnknownHostException</code> host not defined */ protected OpenSSLSocketImpl(String host, int port, InetAddress clientAddress, int clientPort, SSLParameters sslParameters) @@ -212,8 +225,13 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Class constructor with 5 parameters: 1st is InetAddress * - * @throws IOException if network fails - * @throws java.net.UnknownHostException host not defined + * @param <code>InetAddress address</code> + * @param <code>int port</code> + * @param <code>InetAddress localAddress</code> + * @param <code>int localPort</code> + * @param <code>SSLParameters sslParameters</code> + * @throws <code>IOException</code> if network fails + * @throws <code>UnknownHostException</code> host not defined */ protected OpenSSLSocketImpl(InetAddress address, int port, InetAddress clientAddress, int clientPort, SSLParameters sslParameters) @@ -228,7 +246,12 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * Constructor with 5 parameters: 1st is socket. Enhances an existing socket * with SSL functionality. * - * @throws IOException if network fails + * @param <code>Socket socket</code> + * @param <code>String host</code> + * @param <code>int port</code> + * @param <code>boolean autoClose</code> + * @param <code>SSLParameters sslParameters</code> + * @throws <code>IOException</code> if network fails */ protected OpenSSLSocketImpl(Socket socket, String host, int port, boolean autoClose, SSLParameters sslParameters) throws IOException { @@ -255,26 +278,26 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * * @return OpenSSLSessionImpl */ - private OpenSSLSessionImpl getCachedClientSession() { - if (super.getInetAddress() == null || - super.getInetAddress().getHostAddress() == null || - super.getInetAddress().getHostName() == null) { - return null; + private OpenSSLSessionImpl getOpenSSLSessionImpl() { + try { + byte[] id; + SSLSession ses; + for (Enumeration<byte[]> en = sslParameters.getClientSessionContext().getIds(); en.hasMoreElements();) { + id = en.nextElement(); + ses = sslParameters.getClientSessionContext().getSession(id); + if (ses instanceof OpenSSLSessionImpl && ses.isValid() && + super.getInetAddress() != null && + super.getInetAddress().getHostAddress() != null && + super.getInetAddress().getHostName().equals(ses.getPeerHost()) && + super.getPort() == ses.getPeerPort()) { + return (OpenSSLSessionImpl) ses; + } + } + } catch (Exception ex) { + // It's not clear to me under what circumstances the above code + // might fail. I also can't reproduce it. } - ClientSessionContext sessionContext - = sslParameters.getClientSessionContext(); - return (OpenSSLSessionImpl) sessionContext.getSession( - super.getInetAddress().getHostName(), - super.getPort()); - } - - /** - * Ensures that logger is lazily loaded. The outer class seems to load - * before logging is ready. - */ - static class LoggerHolder { - static final Logger logger = Logger.getLogger( - OpenSSLSocketImpl.class.getName()); + return null; } /** @@ -294,60 +317,38 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { return; } } - - OpenSSLSessionImpl session = getCachedClientSession(); + + { + // Debug + int size = 0; + for (Enumeration<byte[]> en = sslParameters.getClientSessionContext().getIds(); + en.hasMoreElements(); en.nextElement()) { size++; }; + } + OpenSSLSessionImpl session = getOpenSSLSessionImpl(); // Check if it's allowed to create a new session (default is true) - if (session == null && !sslParameters.getEnableSessionCreation()) { + if (!sslParameters.getEnableSessionCreation() && session == null) { throw new SSLHandshakeException("SSL Session may not be created"); } else { - Socket socket = this.socket != null ? this.socket : this; - int sessionId = session != null ? session.session : 0; - if (nativeconnect(ssl_ctx, socket, sslParameters.getUseClientMode(), - sessionId)) { - // nativeconnect shouldn't return true if the session is not - // done + if (nativeconnect(ssl_ctx, this.socket != null ? + this.socket : this, sslParameters.getUseClientMode(), session != null ? session.session : 0)) { session.lastAccessedTime = System.currentTimeMillis(); sslSession = session; - - LoggerHolder.logger.fine("Reused cached session for " - + getInetAddress().getHostName() + "."); } else { - if (session != null) { - LoggerHolder.logger.fine("Reuse of cached session for " - + getInetAddress().getHostName() + " failed."); - } else { - LoggerHolder.logger.fine("Created new session for " - + getInetAddress().getHostName() + "."); - } - - ClientSessionContext sessionContext - = sslParameters.getClientSessionContext(); - if (address == null) { - sslSession = new OpenSSLSessionImpl( - nativegetsslsession(ssl), sslParameters, - super.getInetAddress().getHostName(), - super.getPort(), sessionContext); - } else { - sslSession = new OpenSSLSessionImpl( - nativegetsslsession(ssl), sslParameters, - address.getHostName(), address.getPort(), - sessionContext); - } - + if (address == null) sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl), + sslParameters, super.getInetAddress().getHostName(), super.getPort()); + else sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl), + sslParameters, address.getHostName(), address.getPort()); try { - X509Certificate[] peerCertificates = (X509Certificate[]) - sslSession.getPeerCertificates(); + X509Certificate[] peerCertificates = (X509Certificate[]) sslSession.getPeerCertificates(); - if (peerCertificates == null - || peerCertificates.length == 0) { + if (peerCertificates == null || peerCertificates.length == 0) { throw new SSLException("Server sends no certificate"); } - sslParameters.getTrustManager().checkServerTrusted( - peerCertificates, - nativecipherauthenticationmethod()); - sessionContext.putSession(sslSession); + sslParameters.getTrustManager().checkServerTrusted(peerCertificates, + nativecipherauthenticationmethod()); + sslParameters.getClientSessionContext().putSession(sslSession); } catch (CertificateException e) { throw new SSLException("Not trusted server certificate", e); } @@ -359,8 +360,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { HandshakeCompletedEvent event = new HandshakeCompletedEvent(this, sslSession); int size = listeners.size(); - for (int i = 0; i < size; i++) { - listeners.get(i).handshakeCompleted(event); + for (int i=0; i<size; i++) { + ((HandshakeCompletedListener)listeners.get(i)) + .handshakeCompleted(event); } } } @@ -379,27 +381,22 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { nativeaccept(this, m_ctx, client_mode); - ServerSessionContext sessionContext - = sslParameters.getServerSessionContext(); sslSession = new OpenSSLSessionImpl(nativegetsslsession(ssl), - sslParameters, super.getInetAddress().getHostName(), - super.getPort(), sessionContext); + sslParameters, super.getInetAddress().getHostName(), super.getPort()); sslSession.lastAccessedTime = System.currentTimeMillis(); - sessionContext.putSession(sslSession); } /** * Callback methode for the OpenSSL native certificate verification process. * - * @param bytes Byte array containing the cert's + * @param <code>byte[][] bytes</code> Byte array containing the cert's * information. * @return 0 if the certificate verification fails or 1 if OK */ @SuppressWarnings("unused") private int verify_callback(byte[][] bytes) { try { - X509Certificate[] peerCertificateChain - = new X509Certificate[bytes.length]; + X509Certificate[] peerCertificateChain = new X509Certificate[bytes.length]; for(int i = 0; i < bytes.length; i++) { peerCertificateChain[i] = new X509CertImpl(javax.security.cert.X509Certificate.getInstance(bytes[i]).getEncoded()); @@ -585,6 +582,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * Registers a listener to be notified that a SSL handshake * was successfully completed on this connection. + * @param <code>HandShakeCompletedListener listener</code> * @throws <code>IllegalArgumentException</code> if listener is null. */ public void addHandshakeCompletedListener( @@ -600,6 +598,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * The method removes a registered listener. + * @param <code>HandShakeCompletedListener listener</code> * @throws IllegalArgumentException if listener is null or not registered */ public void removeHandshakeCompletedListener( @@ -632,7 +631,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * SSL sessions. If the flag is set to false, and there are no actual * sessions to resume, then there will be no successful handshaking. * - * @param flag true if session may be created; false + * @param <code>boolean flag</code> true if session may be created; false * if a session already exists and must be resumed. */ public void setEnableSessionCreation(boolean flag) { @@ -684,9 +683,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * This method enables the cipher suites listed by * getSupportedCipherSuites(). * - * @param suites names of all the cipher suites to + * @param <code> String[] suites</code> names of all the cipher suites to * put on use - * @throws IllegalArgumentException when one or more of the + * @throws <code>IllegalArgumentException</code> when one or more of the * ciphers in array suites are not supported, or when the array * is null. */ @@ -782,9 +781,9 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { /** * This method set the actual SSL socket to client mode. * - * @param mode true if the socket starts in client + * @param <code>boolean mode</code> true if the socket starts in client * mode - * @throws IllegalArgumentException if mode changes during + * @throws <code>IllegalArgumentException</code> if mode changes during * handshake. */ public synchronized void setUseClientMode(boolean mode) { @@ -819,7 +818,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * Sets the SSL socket to use client's authentication. Relevant only for * server sockets! * - * @param need true if client authentication is + * @param <code>boolean need</code> true if client authentication is * desired, false if not. */ public void setNeedClientAuth(boolean need) { @@ -832,7 +831,7 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * method will continue the negotiation if the client decide not to send * authentication credentials. * - * @param want true if client authentication is + * @param <code>boolean want</code> true if client authentication is * desired, false if not. */ public void setWantClientAuth(boolean want) { @@ -879,9 +878,6 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { * socket's closure. */ public void close() throws IOException { - // TODO: Close SSL sockets using a background thread so they close - // gracefully. - synchronized (handshakeLock) { if (!handshakeStarted) { handshakeStarted = true; @@ -991,11 +987,10 @@ public class OpenSSLSocketImpl extends javax.net.ssl.SSLSocket { } /** - * Helper class for a thread that knows how to call - * {@link OpenSSLSocketImpl#close} on behalf of instances being finalized, - * since that call can take arbitrarily long (e.g., due to a slow network), - * and an overly long-running finalizer will cause the process to be - * totally aborted. + * Helper class for a thread that knows how to call {@link #close} on behalf + * of instances being finalized, since that call can take arbitrarily long + * (e.g., due to a slow network), and an overly long-running finalizer will + * cause the process to be totally aborted. */ private class Finalizer extends Thread { public void run() { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLClientSessionCache.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLClientSessionCache.java deleted file mode 100644 index 8a73fa59f..000000000 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLClientSessionCache.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import javax.net.ssl.SSLSession; - -/** - * A persistent {@link javax.net.ssl.SSLSession} cache used by - * {@link javax.net.ssl.SSLSessionContext} to share client-side SSL sessions - * across processes. For example, this cache enables applications to - * persist and reuse sessions across restarts. - * - * <p>The {@code SSLSessionContext} implementation converts - * {@code SSLSession}s into raw bytes and vice versa. The exact makeup of the - * session data is dependent upon the caller's implementation and is opaque to - * the {@code SSLClientSessionCache} implementation. - */ -public interface SSLClientSessionCache { - - /** - * Gets data from a pre-existing session for a given server host and port. - * - * @param host from {@link javax.net.ssl.SSLSession#getPeerHost()} - * @param port from {@link javax.net.ssl.SSLSession#getPeerPort()} - * @return the session data or null if none is cached - * @throws NullPointerException if host is null - */ - public byte[] getSessionData(String host, int port); - - /** - * Stores session data for the given session. - * - * @param session to cache data for - * @param sessionData to cache - * @throws NullPointerException if session, result of - * {@code session.getPeerHost()} or data is null - */ - public void putSessionData(SSLSession session, byte[] sessionData); -}
\ No newline at end of file diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java index 2e4de04a3..3f4ab96d3 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java @@ -22,6 +22,9 @@ package org.apache.harmony.xnet.provider.jsse; +// BEGIN android-removed +// import org.apache.harmony.xnet.provider.jsse.SSLSocketFactoryImpl; +// END android-removed import org.apache.harmony.xnet.provider.jsse.SSLEngineImpl; import org.apache.harmony.xnet.provider.jsse.SSLParameters; // BEGIN android-removed @@ -39,21 +42,19 @@ import javax.net.ssl.SSLSessionContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; -// BEGIN android-note -// Modified heavily during SSLSessionContext refactoring. Added support for -// persistent session caches. -// END android-note - /** * Implementation of SSLContext service provider interface. */ public class SSLContextImpl extends SSLContextSpi { - /** Client session cache. */ - private ClientSessionContext clientSessionContext; - - /** Server session cache. */ - private ServerSessionContext serverSessionContext; + // client session context contains the set of reusable + // client-side SSL sessions + private SSLSessionContextImpl clientSessionContext = + new SSLSessionContextImpl(); + // server session context contains the set of reusable + // server-side SSL sessions + private SSLSessionContextImpl serverSessionContext = + new SSLSessionContextImpl(); protected SSLParameters sslParameters; @@ -63,44 +64,26 @@ public class SSLContextImpl extends SSLContextSpi { public void engineInit(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException { - engineInit(kms, tms, sr, null, null); - } - - /** - * Initializes this {@code SSLContext} instance. All of the arguments are - * optional, and the security providers will be searched for the required - * implementations of the needed algorithms. - * - * @param kms the key sources or {@code null} - * @param tms the trust decision sources or {@code null} - * @param sr the randomness source or {@code null} - * @param clientCache persistent client session cache or {@code null} - * @param serverCache persistent server session cache or {@code null} - * @throws KeyManagementException if initializing this instance fails - * - * @since Android 1.1 - */ - public void engineInit(KeyManager[] kms, TrustManager[] tms, - SecureRandom sr, SSLClientSessionCache clientCache, - SSLServerSessionCache serverCache) throws KeyManagementException { - sslParameters = new SSLParameters(kms, tms, sr, - clientCache, serverCache); - clientSessionContext = sslParameters.getClientSessionContext(); - serverSessionContext = sslParameters.getServerSessionContext(); + sslParameters = new SSLParameters(kms, tms, sr, clientSessionContext, + serverSessionContext); } public SSLSocketFactory engineGetSocketFactory() { if (sslParameters == null) { throw new IllegalStateException("SSLContext is not initiallized."); } + // BEGIN android-changed return new OpenSSLSocketFactoryImpl(sslParameters); + // END android-changed } public SSLServerSocketFactory engineGetServerSocketFactory() { if (sslParameters == null) { throw new IllegalStateException("SSLContext is not initiallized."); } + // BEGIN android-changed return new OpenSSLServerSocketFactoryImpl(sslParameters); + // END android-changed } public SSLEngine engineCreateSSLEngine(String host, int port) { @@ -118,11 +101,12 @@ public class SSLContextImpl extends SSLContextSpi { return new SSLEngineImpl((SSLParameters) sslParameters.clone()); } - public ServerSessionContext engineGetServerSessionContext() { + public SSLSessionContext engineGetServerSessionContext() { return serverSessionContext; } - public ClientSessionContext engineGetClientSessionContext() { + public SSLSessionContext engineGetClientSessionContext() { return clientSessionContext; } -}
\ No newline at end of file +} + diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java index 60a5535be..9a14f466e 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java @@ -22,6 +22,8 @@ package org.apache.harmony.xnet.provider.jsse; +import org.apache.harmony.xnet.provider.jsse.SSLSessionContextImpl; + import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -43,9 +45,7 @@ import javax.net.ssl.X509TrustManager; * and controls whether new SSL sessions may be established by this * socket or not. */ -// BEGIN android-changed -public class SSLParameters implements Cloneable { -// END android-changed +public class SSLParameters { // default source of authentication keys private static X509KeyManager defaultKeyManager; @@ -58,12 +58,10 @@ public class SSLParameters implements Cloneable { // client session context contains the set of reusable // client-side SSL sessions -// BEGIN android-changed - private final ClientSessionContext clientSessionContext; + private SSLSessionContextImpl clientSessionContext; // server session context contains the set of reusable // server-side SSL sessions - private final ServerSessionContext serverSessionContext; -// END android-changed + private SSLSessionContextImpl serverSessionContext; // source of authentication keys private X509KeyManager keyManager; // source of authentication trust decisions @@ -90,7 +88,16 @@ public class SSLParameters implements Cloneable { // if the peer with this parameters allowed to cteate new SSL session private boolean enable_session_creation = true; -// BEGIN android-changed + /** + * Creates an instance of SSLParameters. + */ + private SSLParameters() { + // BEGIN android-removed + // this.enabledCipherSuites = CipherSuite.defaultCipherSuites; + // END android-removed + } + + // BEGIN android-added protected CipherSuite[] getEnabledCipherSuitesMember() { if (enabledCipherSuites == null) this.enabledCipherSuites = CipherSuite.defaultCipherSuites; return enabledCipherSuites; @@ -113,26 +120,23 @@ public class SSLParameters implements Cloneable { if (ssl_ctx == 0) ssl_ctx = nativeinitsslctx(); return ssl_ctx; } -// END android-changed + // END android-added /** * Initializes the parameters. Naturally this constructor is used * in SSLContextImpl.engineInit method which dirrectly passes its * parameters. In other words this constructor holds all * the functionality provided by SSLContext.init method. - * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[], - * SecureRandom)} for more information + * See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],SecureRandom)} + * for more information */ protected SSLParameters(KeyManager[] kms, TrustManager[] tms, -// BEGIN android-changed - SecureRandom sr, SSLClientSessionCache clientCache, - SSLServerSessionCache serverCache) + SecureRandom sr, SSLSessionContextImpl clientSessionContext, + SSLSessionContextImpl serverSessionContext) throws KeyManagementException { - this.serverSessionContext - = new ServerSessionContext(this, serverCache); - this.clientSessionContext - = new ClientSessionContext(this, clientCache); -// END android-changed + this(); + this.serverSessionContext = serverSessionContext; + this.clientSessionContext = clientSessionContext; try { // initialize key manager boolean initialize_default = false; @@ -224,9 +228,8 @@ public class SSLParameters implements Cloneable { protected static SSLParameters getDefault() throws KeyManagementException { if (defaultParameters == null) { -// BEGIN android-changed - defaultParameters = new SSLParameters(null, null, null, null, null); -// END android-changed + defaultParameters = new SSLParameters(null, null, null, + new SSLSessionContextImpl(), new SSLSessionContextImpl()); } return (SSLParameters) defaultParameters.clone(); } @@ -234,18 +237,14 @@ public class SSLParameters implements Cloneable { /** * @return server session context */ -// BEGIN android-changed - protected ServerSessionContext getServerSessionContext() { -// END android-changed + protected SSLSessionContextImpl getServerSessionContext() { return serverSessionContext; } /** * @return client session context */ -// BEGIN android-changed - protected ClientSessionContext getClientSessionContext() { -// END android-changed + protected SSLSessionContextImpl getClientSessionContext() { return clientSessionContext; } @@ -336,7 +335,7 @@ public class SSLParameters implements Cloneable { /** * Sets the set of available protocols for use in SSL connection. - * @param protocols String[] + * @param suites: String[] */ protected void setEnabledProtocols(String[] protocols) { if (protocols == null) { @@ -423,13 +422,23 @@ public class SSLParameters implements Cloneable { * @return the clone. */ protected Object clone() { -// BEGIN android-changed - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(e); - } -// END android-changed + SSLParameters parameters = new SSLParameters(); + + parameters.clientSessionContext = clientSessionContext; + parameters.serverSessionContext = serverSessionContext; + parameters.keyManager = keyManager; + parameters.trustManager = trustManager; + parameters.secureRandom = secureRandom; + + parameters.enabledCipherSuites = enabledCipherSuites; + parameters.enabledProtocols = enabledProtocols; + + parameters.client_mode = client_mode; + parameters.need_client_auth = need_client_auth; + parameters.want_client_auth = want_client_auth; + parameters.enable_session_creation = enable_session_creation; + + return parameters; } } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSessionCache.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSessionCache.java deleted file mode 100644 index 32a0e72c7..000000000 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSessionCache.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import javax.net.ssl.SSLSession; - -/** - * A persistent {@link javax.net.ssl.SSLSession} cache used by - * {@link javax.net.ssl.SSLSessionContext} to share server-side SSL sessions - * across processes. For example, this cache enables one server to resume - * a session started by a different server based on a session ID provided - * by the client. - * - * <p>The {@code SSLSessionContext} implementation converts - * {@code SSLSession}s into raw bytes and vice versa. The exact makeup of the - * session data is dependent upon the caller's implementation and is opaque to - * the {@code SSLServerSessionCache} implementation. - */ -public interface SSLServerSessionCache { - - /** - * Gets the session data for given session ID. - * - * @param id from {@link javax.net.ssl.SSLSession#getId()} - * @return the session data or null if none is cached - * @throws NullPointerException if id is null - */ - public byte[] getSessionData(byte[] id); - - /** - * Stores session data for the given session. - * - * @param session to cache data for - * @param sessionData to cache - * @throws NullPointerException if session or data is null - */ - public void putSessionData(SSLSession session, byte[] sessionData); -}
\ No newline at end of file diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java new file mode 100644 index 000000000..6882aa139 --- /dev/null +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionContextImpl.java @@ -0,0 +1,168 @@ +/* + * 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. + */ + +/** + * @author Boris Kuznetsov + * @version $Revision$ + */ +package org.apache.harmony.xnet.provider.jsse; + +import java.nio.ByteBuffer; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionContext; + +/** + * SSLSessionContext implementation + * + * @see javax.net.ssl.SSLSessionContext + */ +public class SSLSessionContextImpl implements SSLSessionContext { + + private int cacheSize = 20; + private long timeout = 0; + private final LinkedHashMap<ByteBuffer, SSLSession> sessions = + new LinkedHashMap<ByteBuffer, SSLSession>(cacheSize, 0.75f, true) { + @Override + public boolean removeEldestEntry( + Map.Entry<ByteBuffer, SSLSession> eldest) { + return cacheSize > 0 && this.size() > cacheSize; + } + }; + private volatile LinkedHashMap<ByteBuffer, SSLSession> clone = + new LinkedHashMap<ByteBuffer, SSLSession>(); + + /** + * @see javax.net.ssl.SSLSessionContext#getIds() + */ + public Enumeration<byte[]> getIds() { + return new Enumeration<byte[]>() { + Iterator<ByteBuffer> iterator = clone.keySet().iterator(); + public boolean hasMoreElements() { + return iterator.hasNext(); + } + public byte[] nextElement() { + return iterator.next().array(); + } + }; + } + + /** + * @see javax.net.ssl.SSLSessionContext#getSession(byte[] sessionId) + */ + public SSLSession getSession(byte[] sessionId) { + synchronized (sessions) { + return sessions.get(ByteBuffer.wrap(sessionId)); + } + } + + /** + * @see javax.net.ssl.SSLSessionContext#getSessionCacheSize() + */ + public int getSessionCacheSize() { + synchronized (sessions) { + return cacheSize; + } + } + + /** + * @see javax.net.ssl.SSLSessionContext#getSessionTimeout() + */ + public int getSessionTimeout() { + synchronized (sessions) { + return (int) (timeout/1000); + } + } + + /** + * @see javax.net.ssl.SSLSessionContext#setSessionCacheSize(int size) + */ + public void setSessionCacheSize(int size) throws IllegalArgumentException { + if (size < 0) { + throw new IllegalArgumentException("size < 0"); + } + synchronized (sessions) { + cacheSize = size; + if (cacheSize > 0 && cacheSize < sessions.size()) { + int removals = sessions.size() - cacheSize; + Iterator<ByteBuffer> iterator = sessions.keySet().iterator(); + while (removals-- > 0) { + iterator.next(); + iterator.remove(); + } + clone = (LinkedHashMap<ByteBuffer, SSLSession>) + sessions.clone(); + } + } + } + + /** + * @see javax.net.ssl.SSLSessionContext#setSessionTimeout(int seconds) + */ + public void setSessionTimeout(int seconds) throws IllegalArgumentException { + if (seconds < 0) { + throw new IllegalArgumentException("seconds < 0"); + } + synchronized (sessions) { + timeout = seconds*1000; + // Check timeouts and remove expired sessions + SSLSession ses; + Iterator<Map.Entry<ByteBuffer, SSLSession>> iterator = + sessions.entrySet().iterator(); + while (iterator.hasNext()) { + SSLSession session = iterator.next().getValue(); + if (!session.isValid()) { + // safe to remove with this special method since it doesn't + // make the iterator throw a ConcurrentModificationException + iterator.remove(); + } + } + clone = (LinkedHashMap<ByteBuffer, SSLSession>) sessions.clone(); + } + } + + /** + * Adds session to the session cache + * @param ses + */ + void putSession(SSLSession ses) { + synchronized (sessions) { + sessions.put(ByteBuffer.wrap(ses.getId()), ses); + clone = (LinkedHashMap<ByteBuffer, SSLSession>) sessions.clone(); + } + } +} diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java index 4510c96c8..df46094d7 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java @@ -40,6 +40,7 @@ import javax.net.ssl.SSLSessionBindingListener; import javax.net.ssl.SSLSessionContext; import org.apache.harmony.luni.util.TwoKeyHashMap; +import org.apache.harmony.xnet.provider.jsse.SSLSessionContextImpl; /** * @@ -82,9 +83,8 @@ public class SSLSessionImpl implements SSLSession { /** * Context of the session */ -// BEGIN android-changed - SSLSessionContext context; -// END android-changed + SSLSessionContextImpl context; + /** * certificates were sent to the peer diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java index f6eef23bf..93dc9c4b0 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java @@ -81,6 +81,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { /** * Start session negotiation + * @param session */ public void start() { if (session == null) { // initial handshake @@ -298,7 +299,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { clientFinished = new Finished(io_stream, length); verifyFinished(clientFinished.getData()); // BEGIN android-added - session.context = parameters.getServerSessionContext(); + session.context = parameters.getClientSessionContext(); // END android-added parameters.getServerSessionContext().putSession(session); if (!isResuming) { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java deleted file mode 100644 index a3c2c6d0f..000000000 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerSessionContext.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Iterator; -import java.util.ArrayList; -import java.util.Arrays; - -import javax.net.ssl.SSLSession; - -/** - * Caches server sessions. Indexes by session ID. Users typically look up - * sessions using the ID provided by an SSL client. - */ -public class ServerSessionContext extends AbstractSessionContext { - - /* - * TODO: Expire timed-out sessions more pro-actively. - */ - - private final Map<ByteArray, SSLSession> sessions - = new LinkedHashMap<ByteArray, SSLSession>() { - @Override - protected boolean removeEldestEntry( - Map.Entry<ByteArray, SSLSession> eldest) { - return maximumSize > 0 && size() > maximumSize; - } - }; - - private final SSLServerSessionCache persistentCache; - - public ServerSessionContext(SSLParameters parameters, - SSLServerSessionCache persistentCache) { - super(parameters, 100, 0); - this.persistentCache = persistentCache; - } - - Iterator<SSLSession> sessionIterator() { - synchronized (sessions) { - SSLSession[] array = sessions.values().toArray( - new SSLSession[sessions.size()]); - return Arrays.asList(array).iterator(); - } - } - - void trimToSize() { - synchronized (sessions) { - int size = sessions.size(); - if (size > maximumSize) { - int removals = size - maximumSize; - Iterator<SSLSession> i = sessions.values().iterator(); - do { - i.next(); - i.remove(); - } while (--removals > 0); - } - } - } - - public void setSessionTimeout(int seconds) - throws IllegalArgumentException { - if (seconds < 0) { - throw new IllegalArgumentException("seconds < 0"); - } - timeout = seconds; - } - - public SSLSession getSession(byte[] sessionId) { - ByteArray key = new ByteArray(sessionId); - synchronized (sessions) { - SSLSession session = sessions.get(key); - if (session != null) { - return session; - } - } - - // Check persistent cache. - if (persistentCache != null) { - byte[] data = persistentCache.getSessionData(sessionId); - if (data != null) { - SSLSession session = toSession(data, null, -1); - if (session != null) { - synchronized (sessions) { - sessions.put(key, session); - } - return session; - } - } - } - - return null; - } - - void putSession(SSLSession session) { - ByteArray key = new ByteArray(session.getId()); - synchronized (sessions) { - sessions.put(key, session); - } - - // TODO: In background thread. - if (persistentCache != null) { - byte[] data = toBytes(session); - if (data != null) { - persistentCache.putSessionData(session, data); - } - } - } -} diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp index feae690c3..0c0e45599 100644 --- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp +++ b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl.cpp @@ -61,7 +61,7 @@ static void throwIOExceptionStr(JNIEnv* env, const char* message) } /** - * Gets the peer certificate in the chain and fills a byte array with the + * Gets the peer certificate in the chain and fills a byte array with the * information therein. */ static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeercertificates(JNIEnv* env, @@ -91,62 +91,7 @@ static jobjectArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_get } /** - * Serialize the session. - * See apache mod_ssl - */ -static jbyteArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_serialize(JNIEnv* env, jobject object) -{ - SSL_SESSION * ssl_session; - jbyteArray bytes = NULL; - jbyte *tmp; - int size; - unsigned char *ucp; - - ssl_session = getSslSessionPointer(env, object); - if (ssl_session == NULL) { - return NULL; - } - - // Compute the size of the DER data - size = i2d_SSL_SESSION(ssl_session, NULL); - if (size == 0) { - return NULL; - } - - bytes = env->NewByteArray(size); - if (bytes != NULL) { - tmp = env->GetByteArrayElements(bytes, NULL); - ucp = (unsigned char *) tmp; - i2d_SSL_SESSION(ssl_session, &ucp); - env->ReleaseByteArrayElements(bytes, tmp, 0); - } - - return bytes; - -} - -/** - * Deserialize the session. - */ -static jint org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_deserialize(JNIEnv* env, jobject object, jbyteArray bytes, jint size) -{ - const unsigned char *ucp; - jbyte *tmp; - SSL_SESSION *ssl_session; - - if (bytes != NULL) { - tmp = env->GetByteArrayElements(bytes, NULL); - ucp = (const unsigned char *) tmp; - ssl_session = d2i_SSL_SESSION(NULL, &ucp, (long) size); - env->ReleaseByteArrayElements(bytes, tmp, 0); - - return (jint) ssl_session; - } - return 0; -} - -/** - * Gets and returns in a byte array the ID of the actual SSL session. + * Gets and returns in a byte array the ID of the actual SSL session. */ static jbyteArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getid(JNIEnv* env, jobject object) { @@ -167,8 +112,8 @@ static jbyteArray org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getid } /** - * Gets and returns in a long integer the creation's time of the - * actual SSL session. + * Gets and returns in a long integer the creation's time of the + * actual SSL session. */ static jlong org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getcreationtime(JNIEnv* env, jobject object) { @@ -181,7 +126,7 @@ static jlong org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getcreatio } /** - * Gets and returns in a string the peer's host's name. + * Gets and returns in a string the peer's host's name. */ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerhost(JNIEnv* env, jobject object) { @@ -213,7 +158,7 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerh } /** - * Gets and returns in a string the peer's port name (https, ftp, etc.). + * Gets and returns in a string the peer's port name (https, ftp, etc.). */ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerport(JNIEnv* env, jobject object) { @@ -234,7 +179,7 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerp bio = SSL_get_rbio(ssl); port = BIO_get_conn_port(bio); - /* Notice: port name can be NULL */ + /* Notice: port name can be NULL */ result = env->NewStringUTF(port); SSL_free(ssl); @@ -244,7 +189,7 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeerp } /** - * Gets and returns in a string the version of the SSL protocol. If it + * Gets and returns in a string the version of the SSL protocol. If it * returns the string "unknown" it means that no connection is established. */ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getprotocol(JNIEnv* env, jobject object) @@ -273,7 +218,7 @@ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getproto } /** - * Gets and returns in a string the set of ciphers the actual SSL session uses. + * Gets and returns in a string the set of ciphers the actual SSL session uses. */ static jstring org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getciphersuite(JNIEnv* env, jobject object) { @@ -319,9 +264,7 @@ static JNINativeMethod sMethods[] = {"nativegetprotocol", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getprotocol}, {"nativegetciphersuite", "()Ljava/lang/String;", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getciphersuite}, {"nativegetpeercertificates", "()[[B", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_getpeercertificates}, - {"nativefree", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_free}, - {"nativeserialize", "()[B", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_serialize}, - {"nativedeserialize", "([BI)I", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_deserialize} + {"nativefree", "(I)V", (void*)org_apache_harmony_xnet_provider_jsse_OpenSSLSessionImpl_free} }; /** diff --git a/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContextTest.java b/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContextTest.java deleted file mode 100644 index af4490bba..000000000 --- a/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/ClientSessionContextTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import junit.framework.TestCase; - -import javax.net.ssl.SSLSession; -import java.util.Enumeration; -import java.util.Set; -import java.util.HashSet; - -public class ClientSessionContextTest extends TestCase { - - public void testGetSessionById() { - ClientSessionContext context = new ClientSessionContext(null, null); - - SSLSession a = new FakeSession("a"); - SSLSession b = new FakeSession("b"); - - context.putSession(a); - context.putSession(b); - - assertSame(a, context.getSession("a".getBytes())); - assertSame(b, context.getSession("b".getBytes())); - - assertSame(a, context.getSession("a", 443)); - assertSame(b, context.getSession("b", 443)); - - assertEquals(2, context.sessions.size()); - - Set<SSLSession> sessions = new HashSet<SSLSession>(); - Enumeration ids = context.getIds(); - while (ids.hasMoreElements()) { - sessions.add(context.getSession((byte[]) ids.nextElement())); - } - - Set<SSLSession> expected = new HashSet<SSLSession>(); - expected.add(a); - expected.add(b); - - assertEquals(expected, sessions); - } - - public void testTrimToSize() { - ClientSessionContext context = new ClientSessionContext(null, null); - - FakeSession a = new FakeSession("a"); - FakeSession b = new FakeSession("b"); - FakeSession c = new FakeSession("c"); - FakeSession d = new FakeSession("d"); - - context.putSession(a); - context.putSession(b); - context.putSession(c); - context.putSession(d); - - context.setSessionCacheSize(2); - - Set<SSLSession> sessions = new HashSet<SSLSession>(); - Enumeration ids = context.getIds(); - while (ids.hasMoreElements()) { - sessions.add(context.getSession((byte[]) ids.nextElement())); - } - - Set<SSLSession> expected = new HashSet<SSLSession>(); - expected.add(c); - expected.add(d); - - assertEquals(expected, sessions); - } - -} diff --git a/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/FakeSession.java b/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/FakeSession.java deleted file mode 100644 index 4a793ddc7..000000000 --- a/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/FakeSession.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import java.security.cert.Certificate; -import java.security.Principal; - -class FakeSession implements SSLSession { - final String host; - - FakeSession(String host) { - this.host = host; - } - - public int getApplicationBufferSize() { - throw new UnsupportedOperationException(); - } - - public String getCipherSuite() { - throw new UnsupportedOperationException(); - } - - public long getCreationTime() { - throw new UnsupportedOperationException(); - } - - public byte[] getId() { - return host.getBytes(); - } - - public long getLastAccessedTime() { - throw new UnsupportedOperationException(); - } - - public Certificate[] getLocalCertificates() { - throw new UnsupportedOperationException(); - } - - public Principal getLocalPrincipal() { - throw new UnsupportedOperationException(); - } - - public int getPacketBufferSize() { - throw new UnsupportedOperationException(); - } - - public javax.security.cert.X509Certificate[] getPeerCertificateChain() { - throw new UnsupportedOperationException(); - } - - public Certificate[] getPeerCertificates() { - throw new UnsupportedOperationException(); - } - - public String getPeerHost() { - return host; - } - - public int getPeerPort() { - return 443; - } - - public Principal getPeerPrincipal() { - throw new UnsupportedOperationException(); - } - - public String getProtocol() { - throw new UnsupportedOperationException(); - } - - public SSLSessionContext getSessionContext() { - throw new UnsupportedOperationException(); - } - - public Object getValue(String name) { - throw new UnsupportedOperationException(); - } - - public String[] getValueNames() { - throw new UnsupportedOperationException(); - } - - public void invalidate() { - throw new UnsupportedOperationException(); - } - - public boolean isValid() { - throw new UnsupportedOperationException(); - } - - public void putValue(String name, Object value) { - throw new UnsupportedOperationException(); - } - - public void removeValue(String name) { - throw new UnsupportedOperationException(); - } -} diff --git a/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCacheTest.java b/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCacheTest.java deleted file mode 100644 index ee50863a1..000000000 --- a/libcore/x-net/src/test/java/org/apache/harmony/xnet/provider/jsse/FileClientSessionCacheTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.xnet.provider.jsse; - -import junit.framework.TestCase; - -import java.io.File; -import java.io.IOException; - -public class FileClientSessionCacheTest extends TestCase { - - public void testMaxSize() throws IOException, InterruptedException { - String tmpDir = System.getProperty("java.io.tmpdir"); - if (tmpDir == null) { - fail("Please set 'java.io.tmpdir' system property."); - } - File cacheDir = new File(tmpDir - + "/" + FileClientSessionCacheTest.class.getName() + "/cache"); - final SSLClientSessionCache cache - = FileClientSessionCache.usingDirectory(cacheDir); - Thread[] threads = new Thread[10]; - final int iterations = FileClientSessionCache.MAX_SIZE * 10; - for (int i = 0; i < threads.length; i++) { - final int id = i; - threads[i] = new Thread() { - @Override - public void run() { - for (int i = 0; i < iterations; i++) { - cache.putSessionData(new FakeSession(id + "." + i), - new byte[10]); - } - } - }; - } - for (int i = 0; i < threads.length; i++) { - threads[i].start(); - } - for (int i = 0; i < threads.length; i++) { - threads[i].join(); - } - assertEquals(FileClientSessionCache.MAX_SIZE, cacheDir.list().length); - } -} diff --git a/vm/mterp/armv5te/footer.S b/vm/mterp/armv5te/footer.S index 8f7cc41f6..3a725ed2b 100644 --- a/vm/mterp/armv5te/footer.S +++ b/vm/mterp/armv5te/footer.S @@ -379,23 +379,22 @@ common_exceptionThrown: /* call, r0 gets catchRelPc (a code-unit offset) */ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp) + ldr rFP, [sp, #4] @ retrieve the updated rFP + cmp r0, #0 @ is catchRelPc < 0? + add sp, sp, #8 @ restore stack + bmi .LnotCaughtLocally - /* fix earlier stack overflow if necessary; may trash rFP */ + /* fix stack overflow if necessary; must preserve r0 */ ldrb r1, [r10, #offThread_stackOverflowed] cmp r1, #0 @ did we overflow earlier? beq 1f @ no, skip ahead - mov rFP, r0 @ save relPc result in rFP + mov r9, r0 @ r9<- r0 (save it) mov r0, r10 @ r0<- self bl dvmCleanupStackOverflow @ call(self) - mov r0, rFP @ restore result + mov r0, r9 @ r0<- r9 (restore it) + ldr r9, [r10, #offThread_exception] @ r9<- self->exception 1: - /* update frame pointer and check result from dvmFindCatchBlock */ - ldr rFP, [sp, #4] @ retrieve the updated rFP - cmp r0, #0 @ is catchRelPc < 0? - add sp, sp, #8 @ restore stack - bmi .LnotCaughtLocally - /* adjust locals to match self->curFrame and updated PC */ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method diff --git a/vm/mterp/c/gotoTargets.c b/vm/mterp/c/gotoTargets.c index f52e3f020..f97adf5d7 100644 --- a/vm/mterp/c/gotoTargets.c +++ b/vm/mterp/c/gotoTargets.c @@ -636,10 +636,6 @@ GOTO_TARGET(exceptionThrown) * exception this won't happen until some other exception gets * thrown. If the code keeps pushing the stack bounds we'll end * up aborting the VM. - * - * Note we want to do this *after* the call to dvmFindCatchBlock, - * because that may need extra stack space to resolve exception - * classes (e.g. through a class loader). */ if (self->stackOverflowed) dvmCleanupStackOverflow(self); diff --git a/vm/mterp/out/InterpAsm-armv4.S b/vm/mterp/out/InterpAsm-armv4.S index 3de7aea93..07acd91f3 100644 --- a/vm/mterp/out/InterpAsm-armv4.S +++ b/vm/mterp/out/InterpAsm-armv4.S @@ -9667,23 +9667,22 @@ common_exceptionThrown: /* call, r0 gets catchRelPc (a code-unit offset) */ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp) + ldr rFP, [sp, #4] @ retrieve the updated rFP + cmp r0, #0 @ is catchRelPc < 0? + add sp, sp, #8 @ restore stack + bmi .LnotCaughtLocally - /* fix earlier stack overflow if necessary; may trash rFP */ + /* fix stack overflow if necessary; must preserve r0 */ ldrb r1, [r10, #offThread_stackOverflowed] cmp r1, #0 @ did we overflow earlier? beq 1f @ no, skip ahead - mov rFP, r0 @ save relPc result in rFP + mov r9, r0 @ r9<- r0 (save it) mov r0, r10 @ r0<- self bl dvmCleanupStackOverflow @ call(self) - mov r0, rFP @ restore result + mov r0, r9 @ r0<- r9 (restore it) + ldr r9, [r10, #offThread_exception] @ r9<- self->exception 1: - /* update frame pointer and check result from dvmFindCatchBlock */ - ldr rFP, [sp, #4] @ retrieve the updated rFP - cmp r0, #0 @ is catchRelPc < 0? - add sp, sp, #8 @ restore stack - bmi .LnotCaughtLocally - /* adjust locals to match self->curFrame and updated PC */ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S index 9987ff56e..765cdb264 100644 --- a/vm/mterp/out/InterpAsm-armv5te.S +++ b/vm/mterp/out/InterpAsm-armv5te.S @@ -9661,23 +9661,22 @@ common_exceptionThrown: /* call, r0 gets catchRelPc (a code-unit offset) */ bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp) + ldr rFP, [sp, #4] @ retrieve the updated rFP + cmp r0, #0 @ is catchRelPc < 0? + add sp, sp, #8 @ restore stack + bmi .LnotCaughtLocally - /* fix earlier stack overflow if necessary; may trash rFP */ + /* fix stack overflow if necessary; must preserve r0 */ ldrb r1, [r10, #offThread_stackOverflowed] cmp r1, #0 @ did we overflow earlier? beq 1f @ no, skip ahead - mov rFP, r0 @ save relPc result in rFP + mov r9, r0 @ r9<- r0 (save it) mov r0, r10 @ r0<- self bl dvmCleanupStackOverflow @ call(self) - mov r0, rFP @ restore result + mov r0, r9 @ r0<- r9 (restore it) + ldr r9, [r10, #offThread_exception] @ r9<- self->exception 1: - /* update frame pointer and check result from dvmFindCatchBlock */ - ldr rFP, [sp, #4] @ retrieve the updated rFP - cmp r0, #0 @ is catchRelPc < 0? - add sp, sp, #8 @ restore stack - bmi .LnotCaughtLocally - /* adjust locals to match self->curFrame and updated PC */ SAVEAREA_FROM_FP(r1, rFP) @ r1<- new save area ldr r1, [r1, #offStackSaveArea_method] @ r1<- new method diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S index a80e59ec0..749cdde77 100644 --- a/vm/mterp/out/InterpAsm-x86.S +++ b/vm/mterp/out/InterpAsm-x86.S @@ -1,3 +1,4 @@ + /* * This file was generated automatically by gen-mterp.py for 'x86'. * @@ -6556,7 +6557,7 @@ dvmAsmSisterStart: sarl $4,rINST_FULL .endif movl rINST_FULL,OUT_ARG1(%esp) # arg1<- A or AA (length) - call dvmAllocArrayByClass # eax<- call(arrayClass, length, flags) + call dvmAllocArrayByClass # eax<- call(typeCh, length, flags) UNSPILL(rPC) GET_GLUE(%ecx) testl %eax,%eax # alloc successful? @@ -6661,7 +6662,7 @@ dvmAsmSisterStart: sarl $4,rINST_FULL .endif movl rINST_FULL,OUT_ARG1(%esp) # arg1<- A or AA (length) - call dvmAllocArrayByClass # eax<- call(arrayClass, length, flags) + call dvmAllocArrayByClass # eax<- call(typeCh, length, flags) UNSPILL(rPC) GET_GLUE(%ecx) testl %eax,%eax # alloc successful? diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c index 635a87318..8d253e0e9 100644 --- a/vm/mterp/out/InterpC-allstubs.c +++ b/vm/mterp/out/InterpC-allstubs.c @@ -3661,10 +3661,6 @@ GOTO_TARGET(exceptionThrown) * exception this won't happen until some other exception gets * thrown. If the code keeps pushing the stack bounds we'll end * up aborting the VM. - * - * Note we want to do this *after* the call to dvmFindCatchBlock, - * because that may need extra stack space to resolve exception - * classes (e.g. through a class loader). */ if (self->stackOverflowed) dvmCleanupStackOverflow(self); diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c index d527cc043..503425db6 100644 --- a/vm/mterp/out/InterpC-portdbg.c +++ b/vm/mterp/out/InterpC-portdbg.c @@ -3922,10 +3922,6 @@ GOTO_TARGET(exceptionThrown) * exception this won't happen until some other exception gets * thrown. If the code keeps pushing the stack bounds we'll end * up aborting the VM. - * - * Note we want to do this *after* the call to dvmFindCatchBlock, - * because that may need extra stack space to resolve exception - * classes (e.g. through a class loader). */ if (self->stackOverflowed) dvmCleanupStackOverflow(self); diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c index 64e5ccd13..7292c0b19 100644 --- a/vm/mterp/out/InterpC-portstd.c +++ b/vm/mterp/out/InterpC-portstd.c @@ -3642,10 +3642,6 @@ GOTO_TARGET(exceptionThrown) * exception this won't happen until some other exception gets * thrown. If the code keeps pushing the stack bounds we'll end * up aborting the VM. - * - * Note we want to do this *after* the call to dvmFindCatchBlock, - * because that may need extra stack space to resolve exception - * classes (e.g. through a class loader). */ if (self->stackOverflowed) dvmCleanupStackOverflow(self); diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c index cd5fe95ef..fa45730a3 100644 --- a/vm/mterp/out/InterpC-x86.c +++ b/vm/mterp/out/InterpC-x86.c @@ -1811,10 +1811,6 @@ GOTO_TARGET(exceptionThrown) * exception this won't happen until some other exception gets * thrown. If the code keeps pushing the stack bounds we'll end * up aborting the VM. - * - * Note we want to do this *after* the call to dvmFindCatchBlock, - * because that may need extra stack space to resolve exception - * classes (e.g. through a class loader). */ if (self->stackOverflowed) dvmCleanupStackOverflow(self); diff --git a/vm/oo/Class.c b/vm/oo/Class.c index c7ab763c1..151a9e381 100644 --- a/vm/oo/Class.c +++ b/vm/oo/Class.c @@ -177,10 +177,6 @@ static void throwEarlierClassFailure(ClassObject* clazz); #if LOG_CLASS_LOADING /* * Logs information about a class loading with given timestamp. - * - * TODO: In the case where we fail in dvmLinkClass() and log the class as closing (type='<'), - * it would probably be better to use a new type code to indicate the failure. This change would - * require a matching change in the parser and analysis code in frameworks/base/tools/preload. */ static void logClassLoadWithTime(char type, ClassObject* clazz, u8 time) { pid_t ppid = getppid(); @@ -200,7 +196,7 @@ static void logClassLoad(char type, ClassObject* clazz) { } #endif -/* +/* * Some LinearAlloc unit tests. */ static void linearAllocTests() @@ -1099,7 +1095,7 @@ void dvmSetClassSerialNumber(ClassObject* clazz) * * The class will be loaded and initialized if it has not already been. * If necessary, the superclass will be loaded. - * + * * If the class can't be found, returns NULL with an appropriate exception * raised. */ @@ -1395,7 +1391,7 @@ static ClassObject* findClassNoInit(const char* descriptor, Object* loader, goto bail; } - /* + /* * Lock the class while we link it so other threads must wait for us * to finish. Set the "initThreadId" so we can identify recursive * invocation. @@ -1455,17 +1451,6 @@ static ClassObject* findClassNoInit(const char* descriptor, Object* loader, dvmObjectNotifyAll(self, (Object*) clazz); dvmUnlockObject(self, (Object*) clazz); -#if LOG_CLASS_LOADING - LOG(LOG_INFO, "DVMLINK FAILED FOR CLASS ", "%s in %s\n", - clazz->descriptor, get_process_name()); - - /* - * TODO: It would probably be better to use a new type code here (instead of '<') to - * indicate the failure. This change would require a matching change in the parser - * and analysis code in frameworks/base/tools/preload. - */ - logClassLoad('<', clazz); -#endif clazz = NULL; if (gDvm.optimizing) { /* happens with "external" libs */ @@ -1473,6 +1458,9 @@ static ClassObject* findClassNoInit(const char* descriptor, Object* loader, } else { LOGW("Link of class '%s' failed\n", descriptor); } +#if LOG_CLASS_LOADING + logClassLoad('<', clazz); +#endif goto bail; } dvmObjectNotifyAll(self, (Object*) clazz); @@ -2004,11 +1992,11 @@ static void loadMethodFromDex(ClassObject* clazz, const DexMethod* pDexMethod, /* * jniArgInfo (32-bit int) layout: * SRRRHHHH HHHHHHHH HHHHHHHH HHHHHHHH - * + * * S - if set, do things the hard way (scan the signature) * R - return-type enumeration * H - target-specific hints - * + * * This info is used at invocation time by dvmPlatformInvoke. In most * cases, the target-specific hints allow dvmPlatformInvoke to avoid * having to fully parse the signature. @@ -3634,7 +3622,7 @@ static void initSFields(ClassObject* clazz) bool needRelease = false; if (! parsed) { - /* + /* * TODO: Eventually verification should attempt to ensure * that this can't happen at least due to a data integrity * problem. @@ -4501,7 +4489,7 @@ void dvmDumpAllClasses(int flags) */ int dvmGetNumLoadedClasses() { - int count; + int count; dvmHashTableLock(gDvm.loadedClasses); count = dvmHashTableNumEntries(gDvm.loadedClasses); dvmHashTableUnlock(gDvm.loadedClasses); |
