auto import from //depot/cupcake/@132589
diff --git a/docs/dalvik-bytecode.html b/docs/dalvik-bytecode.html
index 4945d60..2bbffe6 100644
--- a/docs/dalvik-bytecode.html
+++ b/docs/dalvik-bytecode.html
@@ -1170,7 +1170,7 @@
<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 6e23f0d..0000000
--- 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 d7bf690..941689e 100644
--- a/docs/instruction-formats.html
+++ b/docs/instruction-formats.html
@@ -366,15 +366,15 @@
<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 e1c3035..9fd199c 100644
--- a/docs/libraries.html
+++ b/docs/libraries.html
@@ -34,7 +34,7 @@
<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 c19a8dc..87934db 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 @@
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 4614fc4..d963fca 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.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 @@
/** 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,6 +227,18 @@
* Sets the entry at the given index.
*
* @param n >= 0, < size(); which index
+ * @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, int start, int end, RegisterSpec spec) {
+ set0(n, new Entry(start, end, spec));
+ }
+
+ /**
+ * 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>
*/
public void set(int n, Entry entry) {
@@ -71,79 +246,35 @@
}
/**
- * Does a human-friendly dump of this instance.
- *
- * @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.
- */
- 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;
- }
-
- /**
* 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 @@
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;
+ 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 (compareTo((Entry) other) == 0);
}
/**
- * Compares by (in priority order) address, end then start
- * disposition (variants of end are all consistered
- * equivalent), and spec.
+ * Gets the start address.
*
- * @param other non-null; entry to compare to
- * @return {@code -1..1}; standard result of comparison
+ * @return >= 0; the start address
*/
- 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;
- }
-
- return spec.compareTo(other.spec);
+ public int getStart() {
+ return start;
}
/**
- * Gets the address.
+ * Gets the end address (exclusive).
*
- * @return >= 0; the address
+ * @return > start; the end address (exclusive)
*/
- public int getAddress() {
- return address;
- }
-
- /**
- * Gets the disposition.
- *
- * @return non-null; the disposition
- */
- 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 @@
* @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 @@
* <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);
+ public Entry withEnd(int newEnd) {
+ return new Entry(start, newEnd, 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);
- }
-
- 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 3ffd276..519452b 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 @@
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 @@
* @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 @@
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 @@
/**
* An entry in the resulting locals table
*/
- static private class LocalEntry {
- /** address of event */
- public int address;
-
- /** {@code true} iff it's a local start */
- public boolean isStart;
-
- /** register number */
- public int reg;
-
- /** index of name in strings table */
- public int nameIndex;
-
- /** index of type in types table */
- public int typeIndex;
-
- /** index of type signature in strings table */
- public int signatureIndex;
-
- public LocalEntry(int address, boolean isStart, int reg, int nameIndex,
- int typeIndex, int signatureIndex) {
- this.address = address;
- this.isStart = isStart;
+ 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;
}
- public String toString() {
- return String.format("[%x %s v%d %04x %04x %04x]",
- address, isStart ? "start" : "end", reg,
- nameIndex, typeIndex, signatureIndex);
- }
+ /** 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 */
+ int reg;
+
+ /** index of name in strings table */
+ int nameIndex;
+
+ /** index of type in types table */
+ int typeIndex;
+
+ /** index of type signature in strings table */
+ int signatureIndex;
+
}
/**
@@ -195,6 +166,13 @@
* @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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
+ 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 @@
}
}
- /*
- * Go through the original local list, in order, matching up
- * with decoded entries.
- */
+ List<LocalEntry> decodedLocals;
- List<LocalEntry> decodedLocals = decoder.getLocals();
- int thisStringIdx = decoder.thisStringIdx;
- int decodedSz = decodedLocals.size();
+ decodedLocals = decoder.getLocals();
+
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;
+ int matchedLocalsEntries = 0;
- 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;
+ for (LocalEntry entry: decodedLocals) {
+ boolean found = false;
+ for (int i = ll.size() - 1; i >= 0; i--) {
+ LocalList.Entry le = ll.get(i);
- for (int i = 0; i < origSz; i++) {
- LocalList.Entry origEntry = 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 780e18d..49781bd 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 @@
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 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 @@
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 @@
}
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 (curl < szl) {
- nextAddrL = locals.get(curl).getAddress();
+ if (curls < szl) {
+ nextAddrLS = sortedLocalsStart.get(curls).getStart();
+ }
+
+ 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 @@
* 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 @@
}
/**
- * 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 @@
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 @@
* a LOCAL_RESTART_EXTENDED
*/
- for (LocalList.Entry arg : lastEntryForReg) {
+ for (LocalList.Entry arg: lastEntryForReg) {
if (arg == null) {
continue;
}
@@ -484,11 +543,11 @@
* @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 @@
}
/**
+ * 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 @@
* 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 argBase = getParamBase();
+
+ BitSet seen = new BitSet(regSize - argBase);
+
+ 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 @@
}
// 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 0e4329b..b074593 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 @@
*/
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 @@
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 bac6ce2..b1e1a4b 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 @@
* 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 @@
* @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 @@
* @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 @@
}
/** {@inheritDoc} */
- public int compareTo(LocalItem local) {
+ public int compareTo (LocalItem local) {
int ret;
ret = compareHandlesNulls(name, local.name);
@@ -105,7 +106,7 @@
/** {@inheritDoc} */
@Override
- public int hashCode() {
+ public int hashCode () {
return (name == null ? 0 : name.hashCode()) * 31
+ (signature == null ? 0 : signature.hashCode());
}
@@ -120,7 +121,7 @@
}
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 73af91f..09f7f18 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 @@
* 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 @@
* @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 @@
}
/**
- * 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 @@
|| ((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 @@
* 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 @@
}
/**
- * 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 @@
* 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 @@
}
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 adc77c3..4eb2f65 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 @@
}
/**
- * 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 @@
}
/**
- * 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 6221826..fb5cf9f 100644
--- a/libcore/icu/src/main/native/DecimalFormatInterface.cpp
+++ b/libcore/icu/src/main/native/DecimalFormatInterface.cpp
@@ -50,13 +50,13 @@
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 @@
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 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
free(attrBuffer->buffer);
free(attrBuffer);
- return NULL;
+ return NULL;
}
int attrLength = (strlen(attrBuffer->buffer) + 1 );
@@ -610,7 +596,7 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
{"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 0b9afc3..1720366 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 @@
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 835d13f..55419d1 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 @@
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 e82e538..c490b89 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 @@
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 761b9c5..66bfcc9 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 @@
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 3bae372..5ea316a 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 @@
* @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 30510c2..6c995bb 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 @@
} 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 25e53ee..3656e2d 100755
--- a/libcore/run-core-tests
+++ b/libcore/run-core-tests
@@ -26,5 +26,6 @@
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 8f0d08b..035e924 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 @@
{
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 @@
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 2b7c03e..cb821c2 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 @@
// 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 @@
* @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 a95d38f..0000000
--- 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 55a06f5..8419096 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 @@
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 2c8738f..0000000
--- 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 ab097e4..0000000
--- 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 e985908..4fc6e99 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 @@
@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 ca7d6f8..475d388 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.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 @@
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 @@
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 @@
if (sm != null) {
sm.checkPermission(new SSLPermission("getSSLSessionContext"));
}
- return sessionContext;
+ return sslParameters.getClientSessionContext();
}
/**
@@ -402,7 +347,7 @@
* @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 @@
* 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 @@
* -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 @@
* 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 8779736..1d38ca9 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.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.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 @@
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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
*
* @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 @@
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 @@
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 @@
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 @@
/**
* 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 @@
/**
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
* 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 @@
* 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 @@
* 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 @@
}
/**
- * 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 8a73fa5..0000000
--- 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 2e4de04..3f4ab96 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.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 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 @@
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 60a5535..9a14f46 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 @@
* 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
/**
* @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 @@
/**
* 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 @@
* @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 32a0e72..0000000
--- 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 0000000..6882aa1
--- /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 4510c96..df46094 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.SSLSessionContext;
import org.apache.harmony.luni.util.TwoKeyHashMap;
+import org.apache.harmony.xnet.provider.jsse.SSLSessionContextImpl;
/**
*
@@ -82,9 +83,8 @@
/**
* 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 f6eef23..93dc9c4 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 @@
/**
* Start session negotiation
+ * @param session
*/
public void start() {
if (session == null) { // initial handshake
@@ -298,7 +299,7 @@
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 a3c2c6d..0000000
--- 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 feae690..0c0e455 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
{"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 af4490b..0000000
--- 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 4a793dd..0000000
--- 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 ee50863..0000000
--- 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 8f7cc41..3a725ed 100644
--- a/vm/mterp/armv5te/footer.S
+++ b/vm/mterp/armv5te/footer.S
@@ -379,23 +379,22 @@
/* call, r0 gets catchRelPc (a code-unit offset) */
bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
-
- /* fix earlier stack overflow if necessary; may trash rFP */
- 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 r0, r10 @ r0<- self
- bl dvmCleanupStackOverflow @ call(self)
- mov r0, rFP @ restore result
-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
+ /* 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 r9, r0 @ r9<- r0 (save it)
+ mov r0, r10 @ r0<- self
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, r9 @ r0<- r9 (restore it)
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+1:
+
/* 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 f52e3f0..f97adf5 100644
--- a/vm/mterp/c/gotoTargets.c
+++ b/vm/mterp/c/gotoTargets.c
@@ -636,10 +636,6 @@
* 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 3de7aea..07acd91 100644
--- a/vm/mterp/out/InterpAsm-armv4.S
+++ b/vm/mterp/out/InterpAsm-armv4.S
@@ -9667,23 +9667,22 @@
/* call, r0 gets catchRelPc (a code-unit offset) */
bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
-
- /* fix earlier stack overflow if necessary; may trash rFP */
- 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 r0, r10 @ r0<- self
- bl dvmCleanupStackOverflow @ call(self)
- mov r0, rFP @ restore result
-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
+ /* 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 r9, r0 @ r9<- r0 (save it)
+ mov r0, r10 @ r0<- self
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, r9 @ r0<- r9 (restore it)
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+1:
+
/* 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 9987ff5..765cdb2 100644
--- a/vm/mterp/out/InterpAsm-armv5te.S
+++ b/vm/mterp/out/InterpAsm-armv5te.S
@@ -9661,23 +9661,22 @@
/* call, r0 gets catchRelPc (a code-unit offset) */
bl dvmFindCatchBlock @ call(self, relPc, exc, scan?, &fp)
-
- /* fix earlier stack overflow if necessary; may trash rFP */
- 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 r0, r10 @ r0<- self
- bl dvmCleanupStackOverflow @ call(self)
- mov r0, rFP @ restore result
-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
+ /* 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 r9, r0 @ r9<- r0 (save it)
+ mov r0, r10 @ r0<- self
+ bl dvmCleanupStackOverflow @ call(self)
+ mov r0, r9 @ r0<- r9 (restore it)
+ ldr r9, [r10, #offThread_exception] @ r9<- self->exception
+1:
+
/* 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 a80e59e..749cdde 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 @@
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 @@
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 635a873..8d253e0 100644
--- a/vm/mterp/out/InterpC-allstubs.c
+++ b/vm/mterp/out/InterpC-allstubs.c
@@ -3661,10 +3661,6 @@
* 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 d527cc0..503425d 100644
--- a/vm/mterp/out/InterpC-portdbg.c
+++ b/vm/mterp/out/InterpC-portdbg.c
@@ -3922,10 +3922,6 @@
* 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 64e5ccd..7292c0b 100644
--- a/vm/mterp/out/InterpC-portstd.c
+++ b/vm/mterp/out/InterpC-portstd.c
@@ -3642,10 +3642,6 @@
* 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 cd5fe95..fa45730 100644
--- a/vm/mterp/out/InterpC-x86.c
+++ b/vm/mterp/out/InterpC-x86.c
@@ -1811,10 +1811,6 @@
* 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 c7ab763..151a9e3 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -177,10 +177,6 @@
#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 @@
}
#endif
-/*
+/*
* Some LinearAlloc unit tests.
*/
static void linearAllocTests()
@@ -1099,7 +1095,7 @@
*
* 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 @@
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 @@
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 @@
} 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 @@
/*
* 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 @@
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 @@
*/
int dvmGetNumLoadedClasses()
{
- int count;
+ int count;
dvmHashTableLock(gDvm.loadedClasses);
count = dvmHashTableNumEntries(gDvm.loadedClasses);
dvmHashTableUnlock(gDvm.loadedClasses);