Merge "Fix a long standing bug within dvmHeapSourceGetObjectBitmaps. All callers of this function assign the return value to an unsigned value even though this function returns -1 in the error case. This causes the error checks to succeed in cases where it should otherwise fail. Rather than return -1 on error, I have elected to return 0 instead which just happens to be compatible with all current uses."
diff --git a/docs/embedded-vm-control.html b/docs/embedded-vm-control.html
index 0b279e8..1e54453 100644
--- a/docs/embedded-vm-control.html
+++ b/docs/embedded-vm-control.html
@@ -8,7 +8,7 @@
<h1>Controlling the Embedded VM</h1>
<ul>
- <li><a href="#overview">Overview</a>
+ <li><a href="#introduction">Introduction</a> (read this first!)
<li><a href="#checkjni">Extended JNI Checks</a>
<li><a href="#assertions">Assertions</a>
<li><a href="#verifier">Bytecode Verification and Optimization</a>
@@ -18,7 +18,7 @@
<li><a href="#dexcheck">DEX File Checksums</a>
</ul>
-<h2><a name="overview">Overview</a></h2>
+<h2><a name="introduction">Introduction (read this first!)</a></h2>
<p>The Dalvik VM supports a variety of command-line arguments
(use <code>adb shell dalvikvm -help</code> to get a summary), but
@@ -31,12 +31,13 @@
issuing a shell command on the device like this:
<pre>adb shell setprop <name> <value></pre>
-<p>The Android runtime must be restarted before the changes will take
-effect (<code>adb shell stop; adb shell start</code>). This is because the
+<p><strong>The Android runtime must be restarted before the changes will take
+effect</strong> (<code>adb shell stop; adb shell start</code>). This is because the
settings are processed in the "zygote" process, which starts early and stays
around "forever".
-<p>You may not be able to set this as an unprivileged user. You can use
+<p>You may not be able to set <code>dalvik.*</code> properties or restart
+the system as an unprivileged user. You can use
<code>adb root</code> or run the <code>su</code> command from the device
shell on "userdebug" builds to become root first. When in doubt,
<pre>adb shell getprop <name></pre>
@@ -48,7 +49,7 @@
<p>Such changes will survive reboots, but will be lost if the data
partition is wiped. (Hint: create a <code>local.prop</code>
-on your workstation, then <code>adb push local.prop /data</code> . Or,
+on your workstation, then <code>adb push local.prop /data</code>. Or,
use one-liners like
<code>adb shell "echo name = value >> /data/local.prop"</code> -- note
the quotes are important.)
diff --git a/docs/heap-profiling.html b/docs/heap-profiling.html
index 3c80b4a..a9c949e 100644
--- a/docs/heap-profiling.html
+++ b/docs/heap-profiling.html
@@ -11,6 +11,10 @@
of the virtual heap. This is very useful for debugging memory usage
and looking for memory leaks. Getting at the information can be tricky,
but has become easier in recent releases.
+</p><p>
+In what follows, the version number refers to the software release
+running on the phone. To take advantage of the DDMS integration, you will
+also need a sufficiently recent version of DDMS.
<h2>Getting the data</h2>
@@ -148,6 +152,7 @@
permission, which is required to write data to the SD card. If you're
accustomed to writing profile data to <code>/sdcard</code>, you will
need to enable the permission in your application's manifest.
+</p>
<h3>Android 2.0 ("Eclair")</h3>
@@ -157,8 +162,15 @@
and click the "dump HPROF file" button in the top left. This always
writes files to the SD card, so
you must have a card inserted and the permission enabled in your application.
+</p>
+<h3>Android 2.x ("Froyo")</h3>
+<p>
+DDMS heap dump requests are now streamed directly out of the VM, removing
+the external storage requirement.
+</p>
+
<h2>Examining the data</h2>
<p>
The data file format was augmented slightly from the common hprof format,
diff --git a/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java b/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
index cfae706..ce3e95c 100644
--- a/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -311,6 +311,16 @@
public static native void dumpHprofData(String fileName) throws IOException;
/**
+ * Collect "hprof" and send it to DDMS. This will cause a GC.
+ *
+ * @throws UnsupportedOperationException if the VM was built without
+ * HPROF support.
+ *
+ * @hide
+ */
+ public static native void dumpHprofDataDdms();
+
+ /**
* Primes the register map cache.
*
* @hide
diff --git a/libcore/dom/src/test/java/org/w3c/domts/DOMTest.java b/libcore/dom/src/test/java/org/w3c/domts/DOMTest.java
index b39ea67..7dd8f26 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/DOMTest.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/DOMTest.java
@@ -165,8 +165,8 @@
//
// if available use JDK 1.4's File.toURI().toString()
//
- Method method = File.class.getMethod("toURI", null);
- Object uri = method.invoke(tempFile, null);
+ Method method = File.class.getMethod("toURI", (Class<?>) null);
+ Object uri = method.invoke(tempFile, (Class<?>) null);
return uri.toString();
}
catch (NoSuchMethodException ex) {
diff --git a/libcore/dom/src/test/java/org/w3c/domts/LSDocumentBuilderFactory.java b/libcore/dom/src/test/java/org/w3c/domts/LSDocumentBuilderFactory.java
index 7ed0f54..b769602 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/LSDocumentBuilderFactory.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/LSDocumentBuilderFactory.java
@@ -272,8 +272,8 @@
try {
Class domImplRegistryClass = Class.forName(
"org.w3c.dom.bootstrap.DOMImplementationRegistry");
- Method newInstanceMethod = domImplRegistryClass.getMethod("newInstance", null);
- Object domRegistry = newInstanceMethod.invoke(null, null);
+ Method newInstanceMethod = domImplRegistryClass.getMethod("newInstance", (Class<?>) null);
+ Object domRegistry = newInstanceMethod.invoke(null, (Class<?>) null);
Method getDOMImplementationMethod = domImplRegistryClass.getMethod(
"getDOMImplementation", new Class[] {String.class});
impl = (DOMImplementation) getDOMImplementationMethod.invoke(domRegistry,
diff --git a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java
index d2e38ca..f3484da 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue1.java
@@ -69,7 +69,7 @@
attributes = testNode.getAttributes();
titleAttr = (Attr) attributes.getNamedItem("class");
value = titleAttr.getValue();
- assertEquals("attrValue1", "Yα", value);
+ assertEquals("attrValue1", "Y\u03b1", value); // android-changed: GREEK LOWER CASE ALPHA
}
/**
* Gets URI that identifies the test.
diff --git a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java
index fd5b211..814b693 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrgetvalue2.java
@@ -89,7 +89,7 @@
firstChild = titleAttr.getFirstChild();
retval = titleAttr.insertBefore(alphaRef, firstChild);
value = titleAttr.getValue();
- assertEquals("attrValue1", "αYα", value);
+ assertEquals("attrValue1", "\u03b1Y\u03b1", value); // android-changed: GREEK LOWER CASE ALPHA
}
}
diff --git a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java
index c9a2e62..8ba4c57 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_attrspecifiedvaluechanged.java
@@ -71,7 +71,7 @@
doc = (Document) load("hc_staff", true);
addressList = doc.getElementsByTagName("acronym");
testNode = addressList.item(2);
- ((Element) /*Node */testNode).setAttribute("class", "Yα");
+ ((Element) /*Node */testNode).setAttribute("class", "Y\u03b1"); // android-changed: GREEK LOWER CASE ALPHA
attributes = testNode.getAttributes();
streetAttr = (Attr) attributes.getNamedItem("class");
state = streetAttr.getSpecified();
diff --git a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java
index e8d3268..36dc3f8 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_namednodemapinuseattributeerr.java
@@ -75,7 +75,7 @@
elementList = doc.getElementsByTagName("acronym");
firstNode = (Element) elementList.item(0);
domesticAttr = doc.createAttribute("title");
- domesticAttr.setValue("Yα");
+ domesticAttr.setValue("Y\u03b1"); // android-changed: GREEK LOWER CASE ALPHA
setAttr = firstNode.setAttributeNode(domesticAttr);
elementList = doc.getElementsByTagName("acronym");
testNode = elementList.item(2);
diff --git a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java
index 5694a4a..2a10501 100644
--- a/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java
+++ b/libcore/dom/src/test/java/org/w3c/domts/level1/core/hc_textparseintolistofelements.java
@@ -72,13 +72,13 @@
java.util.List result = new java.util.ArrayList();
java.util.List expectedNormal = new java.util.ArrayList();
- expectedNormal.add("β");
+ expectedNormal.add("\u03b2"); // android-changed: GREEK LOWER CASE BETA
expectedNormal.add(" Dallas, ");
- expectedNormal.add("γ");
+ expectedNormal.add("\u03b3"); // android-changed: GREEK LOWER CASE GAMMA
expectedNormal.add("\n 98554");
java.util.List expectedExpanded = new java.util.ArrayList();
- expectedExpanded.add("β Dallas, γ\n 98554");
+ expectedExpanded.add("\u03b2 Dallas, \u03b3\n 98554"); // android-changed: GREEK LOWER CASE BETA, GREEK LOWER CASE GAMMA
doc = (Document) load("hc_staff", false);
elementList = doc.getElementsByTagName("acronym");
diff --git a/libcore/luni/src/main/java/java/io/EmulatedFields.java b/libcore/luni/src/main/java/java/io/EmulatedFields.java
index a947a52..f6dbdfc 100644
--- a/libcore/luni/src/main/java/java/io/EmulatedFields.java
+++ b/libcore/luni/src/main/java/java/io/EmulatedFields.java
@@ -119,7 +119,7 @@
public boolean defaulted(String name) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, null);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no field '" + name + "'");
}
return slot.defaulted;
}
@@ -207,7 +207,7 @@
ObjectSlot slot = findSlot(name, Byte.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no byte field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Byte) slot.fieldValue)
.byteValue();
@@ -233,7 +233,7 @@
ObjectSlot slot = findSlot(name, Character.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no char field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Character) slot.fieldValue)
.charValue();
@@ -259,7 +259,7 @@
ObjectSlot slot = findSlot(name, Double.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no double field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Double) slot.fieldValue)
.doubleValue();
@@ -285,7 +285,7 @@
ObjectSlot slot = findSlot(name, Float.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no float field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Float) slot.fieldValue)
.floatValue();
@@ -311,7 +311,7 @@
ObjectSlot slot = findSlot(name, Integer.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no int field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Integer) slot.fieldValue)
.intValue();
@@ -337,7 +337,7 @@
ObjectSlot slot = findSlot(name, Long.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no long field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Long) slot.fieldValue)
.longValue();
@@ -363,7 +363,7 @@
ObjectSlot slot = findSlot(name, null);
// if not initialized yet, we give the default value
if (slot == null || slot.field.getType().isPrimitive()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no Object field '" + name + "'");
}
return slot.defaulted ? defaultValue : slot.fieldValue;
}
@@ -388,7 +388,7 @@
ObjectSlot slot = findSlot(name, Short.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no short field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Short) slot.fieldValue)
.shortValue();
@@ -414,7 +414,7 @@
ObjectSlot slot = findSlot(name, Boolean.TYPE);
// if not initialized yet, we give the default value
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no boolean field '" + name + "'");
}
return slot.defaulted ? defaultValue : ((Boolean) slot.fieldValue)
.booleanValue();
@@ -435,7 +435,7 @@
public void put(String name, byte value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Byte.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no byte field '" + name + "'");
}
slot.fieldValue = Byte.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -456,7 +456,7 @@
public void put(String name, char value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Character.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no char field '" + name + "'");
}
slot.fieldValue = Character.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -477,7 +477,7 @@
public void put(String name, double value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Double.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no double field '" + name + "'");
}
slot.fieldValue = Double.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -498,7 +498,7 @@
public void put(String name, float value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Float.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no float field '" + name + "'");
}
slot.fieldValue = Float.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -519,7 +519,7 @@
public void put(String name, int value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Integer.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no integer field '" + name + "'");
}
slot.fieldValue = Integer.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -540,7 +540,7 @@
public void put(String name, long value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Long.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no long field '" + name + "'");
}
slot.fieldValue = Long.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -565,7 +565,7 @@
}
ObjectSlot slot = findSlot(name, valueClass);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no Object field '" + name + "'");
}
slot.fieldValue = value;
slot.defaulted = false; // No longer default value
@@ -586,7 +586,7 @@
public void put(String name, short value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Short.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no short field '" + name + "'");
}
slot.fieldValue = Short.valueOf(value);
slot.defaulted = false; // No longer default value
@@ -607,7 +607,7 @@
public void put(String name, boolean value) throws IllegalArgumentException {
ObjectSlot slot = findSlot(name, Boolean.TYPE);
if (slot == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("no boolean field '" + name + "'");
}
slot.fieldValue = Boolean.valueOf(value);
slot.defaulted = false; // No longer default value
diff --git a/libcore/luni/src/main/java/java/net/AddressCache.java b/libcore/luni/src/main/java/java/net/AddressCache.java
index 8388b48..c0d8d97 100644
--- a/libcore/luni/src/main/java/java/net/AddressCache.java
+++ b/libcore/luni/src/main/java/java/net/AddressCache.java
@@ -47,7 +47,7 @@
// The actual cache.
private final Map<String, AddressCacheEntry> map;
- class AddressCacheEntry {
+ static class AddressCacheEntry {
// The addresses. May be the empty array for a negative cache entry.
InetAddress[] addresses;
diff --git a/libcore/luni/src/main/java/java/util/Currency.java b/libcore/luni/src/main/java/java/util/Currency.java
index ed9868f..6aa295a 100644
--- a/libcore/luni/src/main/java/java/util/Currency.java
+++ b/libcore/luni/src/main/java/java/util/Currency.java
@@ -174,7 +174,9 @@
return localeData.currencySymbol;
}
- return Resources.getCurrencySymbolNative(locale.toString(), currencyCode);
+ // Try ICU, and fall back to the currency code if ICU has nothing.
+ String symbol = Resources.getCurrencySymbolNative(locale.toString(), currencyCode);
+ return symbol != null ? symbol : currencyCode;
// END android-changed
}
diff --git a/libcore/luni/src/test/java/java/util/AllTests.java b/libcore/luni/src/test/java/java/util/AllTests.java
index fdeb03a..774d48b 100644
--- a/libcore/luni/src/test/java/java/util/AllTests.java
+++ b/libcore/luni/src/test/java/java/util/AllTests.java
@@ -22,8 +22,10 @@
public class AllTests {
public static final Test suite() {
TestSuite suite = tests.TestSuiteFactory.createTestSuite();
+ suite.addTestSuite(java.util.CurrencyTest.class);
suite.addTestSuite(java.util.DateTest.class);
suite.addTestSuite(java.util.FormatterTest.class);
+ suite.addTestSuite(java.util.TimeZoneTest.class);
return suite;
}
}
diff --git a/libcore/luni/src/test/java/java/util/CurrencyTest.java b/libcore/luni/src/test/java/java/util/CurrencyTest.java
new file mode 100644
index 0000000..16111d5
--- /dev/null
+++ b/libcore/luni/src/test/java/java/util/CurrencyTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class CurrencyTest extends junit.framework.TestCase {
+ // Regression test to ensure that Currency.getSymbol(Locale) returns the
+ // currency code if ICU doesn't have a localization of the symbol. The
+ // harmony Currency tests don't test this, and their DecimalFormat tests
+ // only test it as a side-effect, and in a way that only detected my
+ // specific mistake of returning null (returning "stinky" would have
+ // passed).
+ public void test_getSymbol_fallback() throws Exception {
+ // This assumes that AED never becomes a currency important enough to
+ // Canada that Canadians give it a localized (to Canada) symbol.
+ assertEquals("AED", Currency.getInstance("AED").getSymbol(Locale.CANADA));
+ }
+}
diff --git a/libcore/luni/src/test/java/java/util/FormatterTest.java b/libcore/luni/src/test/java/java/util/FormatterTest.java
index 11d82fb..1722e67 100644
--- a/libcore/luni/src/test/java/java/util/FormatterTest.java
+++ b/libcore/luni/src/test/java/java/util/FormatterTest.java
@@ -37,8 +37,8 @@
public void test_formatNull() throws Exception {
// We fast-path %s and %d (with no configuration) but need to make sure we handle the
// special case of the null argument...
- assertEquals("null", String.format(Locale.US, "%s", null));
- assertEquals("null", String.format(Locale.US, "%d", null));
+ assertEquals("null", String.format(Locale.US, "%s", (String) null));
+ assertEquals("null", String.format(Locale.US, "%d", (Integer) null));
// ...without screwing up conversions that don't take an argument.
assertEquals("%", String.format(Locale.US, "%%"));
}
diff --git a/libcore/luni/src/test/java/java/util/TimeZoneTest.java b/libcore/luni/src/test/java/java/util/TimeZoneTest.java
new file mode 100644
index 0000000..ec4879f
--- /dev/null
+++ b/libcore/luni/src/test/java/java/util/TimeZoneTest.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TimeZoneTest extends junit.framework.TestCase {
+ // http://code.google.com/p/android/issues/detail?id=877
+ public void test_useDaylightTime() {
+ TimeZone asiaTaipei = TimeZone.getTimeZone("Asia/Taipei");
+ assertFalse("Taiwan doesn't use DST", asiaTaipei.useDaylightTime());
+ }
+}
diff --git a/libcore/luni/src/test/java/tests/api/java/util/CalendarTest.java b/libcore/luni/src/test/java/tests/api/java/util/CalendarTest.java
index cc0f91a..4ffe2c6 100644
--- a/libcore/luni/src/test/java/tests/api/java/util/CalendarTest.java
+++ b/libcore/luni/src/test/java/tests/api/java/util/CalendarTest.java
@@ -983,11 +983,12 @@
// locale dependent test, bug 1943269
return;
}
+ Locale.setDefault(Locale.US);
Calendar cal = Calendar.getInstance();
- assertTrue(cal.getMinimalDaysInFirstWeek()==1);
+ assertEquals(1, cal.getMinimalDaysInFirstWeek());
Locale.setDefault(Locale.FRANCE);
cal = Calendar.getInstance();
- assertTrue(cal.getMinimalDaysInFirstWeek()==4);
+ assertEquals(4, cal.getMinimalDaysInFirstWeek());
Locale.setDefault(Locale.US);
}
diff --git a/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java b/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java
index fb03a34..7709b88 100644
--- a/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java
+++ b/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java
@@ -2101,25 +2101,25 @@
china.set(Calendar.MILLISECOND, 609);
final Object[][] lowerCaseGermanTriple = {
- {0L, 'a', "Do"}, //$NON-NLS-2$
- {Long.MAX_VALUE, 'a', "So"}, //$NON-NLS-2$
- {-1000L, 'a', "Do"}, //$NON-NLS-2$
- {new Date(1147327147578L), 'a', "Do"}, //$NON-NLS-2$
- {paris, 'a', "Mo"}, //$NON-NLS-2$
- {china, 'a', "Mo"}, //$NON-NLS-2$
+ {0L, 'a', "Do."}, //$NON-NLS-2$
+ {Long.MAX_VALUE, 'a', "So."}, //$NON-NLS-2$
+ {-1000L, 'a', "Do."}, //$NON-NLS-2$
+ {new Date(1147327147578L), 'a', "Do."}, //$NON-NLS-2$
+ {paris, 'a', "Mo."}, //$NON-NLS-2$
+ {china, 'a', "Mo."}, //$NON-NLS-2$
{0L, 'b', "Jan"}, //$NON-NLS-2$
{Long.MAX_VALUE, 'b', "Aug"}, //$NON-NLS-2$
{-1000L, 'b', "Jan"}, //$NON-NLS-2$
{new Date(1147327147578L), 'b', "Mai"}, //$NON-NLS-2$
{paris, 'b', "Mai"}, //$NON-NLS-2$
{china, 'b', "Mai"}, //$NON-NLS-2$
- {0L, 'c', "Do Jan 01 08:00:00 GMT+08:00 1970"}, //$NON-NLS-2$
- // {Long.MAX_VALUE, 'c', "So Aug 17 15:12:55 GMT+08:00 292278994"}, //$NON-NLS-2$
- {Long.MAX_VALUE, 'c', "So Aug 17 15:18:47 GMT+08:00 292278994"}, //$NON-NLS-2$
- {-1000L, 'c', "Do Jan 01 07:59:59 GMT+08:00 1970"}, //$NON-NLS-2$
- {new Date(1147327147578L), 'c', "Do Mai 11 13:59:07 GMT+08:00 2006"}, //$NON-NLS-2$
- {paris, 'c', "Mo Mai 08 12:00:00 MESZ 2006"}, //$NON-NLS-2$
- {china, 'c', "Mo Mai 08 12:00:00 GMT-08:00 2006"}, //$NON-NLS-2$
+ {0L, 'c', "Do. Jan 01 08:00:00 GMT+08:00 1970"}, //$NON-NLS-2$
+ // {Long.MAX_VALUE, 'c', "So. Aug 17 15:12:55 GMT+08:00 292278994"}, //$NON-NLS-2$
+ {Long.MAX_VALUE, 'c', "So. Aug 17 15:18:47 GMT+08:00 292278994"}, //$NON-NLS-2$
+ {-1000L, 'c', "Do. Jan 01 07:59:59 GMT+08:00 1970"}, //$NON-NLS-2$
+ {new Date(1147327147578L), 'c', "Do. Mai 11 13:59:07 GMT+08:00 2006"}, //$NON-NLS-2$
+ {paris, 'c', "Mo. Mai 08 12:00:00 MESZ 2006"}, //$NON-NLS-2$
+ {china, 'c', "Mo. Mai 08 12:00:00 GMT-08:00 2006"}, //$NON-NLS-2$
{0L, 'd', "01"}, //$NON-NLS-2$
{Long.MAX_VALUE, 'd', "17"}, //$NON-NLS-2$
{-1000L, 'd', "01"}, //$NON-NLS-2$
@@ -2209,13 +2209,13 @@
{new Date(1147327147578L), 'b', "mai"}, //$NON-NLS-2$
{paris, 'b', "mai"}, //$NON-NLS-2$
{china, 'b', "mai"}, //$NON-NLS-2$
- {0L, 'c', "jeu. janv. 01 08:00:00 HMG+08:00 1970"}, //$NON-NLS-2$
- // {Long.MAX_VALUE, 'c', "dim. ao\u00fbt 17 15:12:55 HMG+08:00 292278994"}, //$NON-NLS-2$
- {Long.MAX_VALUE, 'c', "dim. ao\u00fbt 17 15:18:47 HMG+08:00 292278994"}, //$NON-NLS-2$
- {-1000L, 'c', "jeu. janv. 01 07:59:59 HMG+08:00 1970"}, //$NON-NLS-2$
- {new Date(1147327147578L), 'c', "jeu. mai 11 13:59:07 HMG+08:00 2006"}, //$NON-NLS-2$
+ {0L, 'c', "jeu. janv. 01 08:00:00 UTC+08:00 1970"}, //$NON-NLS-2$
+ // {Long.MAX_VALUE, 'c', "dim. ao\u00fbt 17 15:12:55 UTC+08:00 292278994"}, //$NON-NLS-2$
+ {Long.MAX_VALUE, 'c', "dim. ao\u00fbt 17 15:18:47 UTC+08:00 292278994"}, //$NON-NLS-2$
+ {-1000L, 'c', "jeu. janv. 01 07:59:59 UTC+08:00 1970"}, //$NON-NLS-2$
+ {new Date(1147327147578L), 'c', "jeu. mai 11 13:59:07 UTC+08:00 2006"}, //$NON-NLS-2$
{paris, 'c', "lun. mai 08 12:00:00 HAEC 2006"}, //$NON-NLS-2$
- {china, 'c', "lun. mai 08 12:00:00 HMG-08:00 2006"}, //$NON-NLS-2$
+ {china, 'c', "lun. mai 08 12:00:00 UTC-08:00 2006"}, //$NON-NLS-2$
{0L, 'd', "01"}, //$NON-NLS-2$
{Long.MAX_VALUE, 'd', "17"}, //$NON-NLS-2$
{-1000L, 'd', "01"}, //$NON-NLS-2$
@@ -2401,7 +2401,7 @@
f = new Formatter(Locale.GERMAN);
f.format(formatSpecifier, lowerCaseGermanTriple[i][input]);
assertEquals("Format pattern: " + formatSpecifier //$NON-NLS-2$
- + " Argument: " + lowerCaseGermanTriple[i][pattern], //$NON-NLS-2$
+ + " Argument: " + lowerCaseGermanTriple[i][input], //$NON-NLS-2$
lowerCaseGermanTriple[i][output], f.toString());
f = new Formatter(Locale.GERMAN);
@@ -2638,12 +2638,12 @@
{new Date(1147327147578L), 'Y', "2006"}, //$NON-NLS-2$
{paris, 'Y', "2006"}, //$NON-NLS-2$
{china, 'Y', "2006"}, //$NON-NLS-2$
- {0L, 'Z', "HMG+08:00"}, //$NON-NLS-2$
- {Long.MAX_VALUE, 'Z', "HMG+08:00"}, //$NON-NLS-2$
- {-1000L, 'Z', "HMG+08:00"}, //$NON-NLS-2$
- {new Date(1147327147578L), 'Z', "HMG+08:00"}, //$NON-NLS-2$
+ {0L, 'Z', "UTC+08:00"}, //$NON-NLS-2$
+ {Long.MAX_VALUE, 'Z', "UTC+08:00"}, //$NON-NLS-2$
+ {-1000L, 'Z', "UTC+08:00"}, //$NON-NLS-2$
+ {new Date(1147327147578L), 'Z', "UTC+08:00"}, //$NON-NLS-2$
{paris, 'Z', "HAEC"}, //$NON-NLS-2$
- {china, 'Z', "HMG-08:00"}, //$NON-NLS-2$
+ {china, 'Z', "UTC-08:00"}, //$NON-NLS-2$
};
diff --git a/libcore/luni/src/test/java/tests/api/java/util/TimeZoneTest.java b/libcore/luni/src/test/java/tests/api/java/util/TimeZoneTest.java
index 9b1d395..aacf6ce 100644
--- a/libcore/luni/src/test/java/tests/api/java/util/TimeZoneTest.java
+++ b/libcore/luni/src/test/java/tests/api/java/util/TimeZoneTest.java
@@ -315,7 +315,8 @@
}
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
assertEquals("Pacific Standard Time", tz.getDisplayName(new Locale("US")));
- assertEquals("Heure normale du Pacifique", tz.getDisplayName(Locale.FRANCE));
+ // BEGIN android-note: RI has "Heure", CLDR/ICU has "heure".
+ assertEquals("heure normale du Pacifique", tz.getDisplayName(Locale.FRANCE));
}
@TestTargetNew(
@@ -349,9 +350,10 @@
assertEquals("Pacific Daylight Time", tz.getDisplayName(true, 1, Locale.US));
assertEquals("Pacific Standard Time", tz.getDisplayName(false, 1, Locale.UK));
//RI fails on following line. RI always returns short time zone name as "PST"
- assertEquals("HMG-08:00", tz.getDisplayName(false, 0, Locale.FRANCE));
- assertEquals("Heure avanc\u00e9e du Pacifique", tz.getDisplayName(true, 1, Locale.FRANCE));
- assertEquals("Heure normale du Pacifique", tz.getDisplayName(false, 1, Locale.FRANCE));
+ assertEquals("UTC-08:00", tz.getDisplayName(false, 0, Locale.FRANCE));
+ // BEGIN android-note: RI has "Heure", CLDR/ICU has "heure".
+ assertEquals("heure avanc\u00e9e du Pacifique", tz.getDisplayName(true, 1, Locale.FRANCE));
+ assertEquals("heure normale du Pacifique", tz.getDisplayName(false, 1, Locale.FRANCE));
}
@TestTargetNew(
@@ -392,19 +394,6 @@
@TestTargetNew(
level = TestLevel.COMPLETE,
notes = "",
- method = "useDaylightTime",
- args = {}
- )
- public void test_useDaylightTime() {
- // http://code.google.com/p/android/issues/detail?id=877
-
- TimeZone asiaTaipei = TimeZone.getTimeZone("Asia/Taipei");
- assertFalse("Taiwan doesn't use DST", asiaTaipei.useDaylightTime());
- }
-
- @TestTargetNew(
- level = TestLevel.COMPLETE,
- notes = "",
method = "setID",
args = {java.lang.String.class}
)
diff --git a/libcore/math/src/main/java/java/math/BigDecimal.java b/libcore/math/src/main/java/java/math/BigDecimal.java
index 33042ba..4e4875b 100644
--- a/libcore/math/src/main/java/java/math/BigDecimal.java
+++ b/libcore/math/src/main/java/java/math/BigDecimal.java
@@ -2148,7 +2148,10 @@
long newScale = scale;
if (isZero()) {
+ // BEGIN android-changed: preserve RI compatibility, so BigDecimal.equals (which checks
+ // value *and* scale) continues to work.
return this;
+ // END android-changed
}
BigInteger strippedBI = getUnscaledValue();
BigInteger[] quotAndRem;
diff --git a/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java b/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java
index 572f2c1..29d68a2 100644
--- a/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java
+++ b/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java
@@ -1293,7 +1293,6 @@
method = "stripTrailingZeros",
args = {}
)
- @AndroidOnly("Stripping trailing zeroes from 0.000 value doesn't work on RI. See below")
public void test_stripTrailingZero() {
BigDecimal sixhundredtest = new BigDecimal("600.0");
assertTrue("stripTrailingZero failed for 600.0",
@@ -1306,11 +1305,13 @@
((notrailingzerotest.stripTrailingZeros()).scale() == 0)
);
+ // BEGIN android-changed: preserve RI compatibility, so BigDecimal.equals (which checks
+ // value *and* scale) continues to work. https://issues.apache.org/jira/browse/HARMONY-4623
/* Zero */
- //regression for HARMONY-4623, NON-BUG DIFF with RI
BigDecimal zerotest = new BigDecimal("0.0000");
assertEquals("stripTrailingZero failed for 0.0000",
- 0, (zerotest.stripTrailingZeros()).scale() );
+ 4, (zerotest.stripTrailingZeros()).scale() );
+ // END android-changed
}
@TestTargetNew(
diff --git a/libcore/support/src/test/java/tests/util/TestEnvironment.java b/libcore/support/src/test/java/tests/util/TestEnvironment.java
index 69a87c2..088e624 100644
--- a/libcore/support/src/test/java/tests/util/TestEnvironment.java
+++ b/libcore/support/src/test/java/tests/util/TestEnvironment.java
@@ -73,12 +73,21 @@
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
}
- private static void resetSystemProperties() {
- String tmpDir = System.getProperty("java.io.tmpdir");
- if (tmpDir == null) {
- throw new IllegalStateException("Test execution requires the"
- + " system property java.io.tmpdir to be set.");
+ private static String getExistingSystemProperty(String name) {
+ String result = System.getProperty(name);
+ if (result == null) {
+ throw new AssertionError("Tests require the '" + name + "' system property");
}
+ return result;
+ }
+
+ private static void resetSystemProperties() {
+ // There are two system properties we can't live without.
+ // 1. We need somewhere writable to stash our stuff.
+ String tmpDir = getExistingSystemProperty("java.io.tmpdir");
+ // 2. We need to know who we are, specifically "am I root?" because that affects what
+ // the OS lets us do, and that affects test expectations.
+ String userName = getExistingSystemProperty("user.name");
Properties p = new Properties();
@@ -129,7 +138,7 @@
p.put("line.separator", "\n");
p.put("path.separator", ":");
p.put("user.language", "en");
- p.put("user.name", "");
+ p.put("user.name", userName);
p.put("user.region", "US");
System.setProperties(p);
diff --git a/libcore/text/src/main/java/java/text/DecimalFormat.java b/libcore/text/src/main/java/java/text/DecimalFormat.java
index 765bdae..65d4d48 100644
--- a/libcore/text/src/main/java/java/text/DecimalFormat.java
+++ b/libcore/text/src/main/java/java/text/DecimalFormat.java
@@ -946,7 +946,7 @@
// END android-added
if (this.isParseIntegerOnly() && number.equals(NEGATIVE_ZERO_DOUBLE)) {
- return new Long(0);
+ return Long.valueOf(0); // android-changed
}
return number;
@@ -1208,18 +1208,10 @@
fields.put("positiveSuffix", dform.getPositiveSuffix());
fields.put("negativePrefix", dform.getNegativePrefix());
fields.put("negativeSuffix", dform.getNegativeSuffix());
- String posPrefixPattern = (String) Format.getInternalField(
- "posPrefixPattern", dform);
- fields.put("posPrefixPattern", posPrefixPattern);
- String posSuffixPattern = (String) Format.getInternalField(
- "posSuffixPattern", dform);
- fields.put("posSuffixPattern", posSuffixPattern);
- String negPrefixPattern = (String) Format.getInternalField(
- "negPrefixPattern", dform);
- fields.put("negPrefixPattern", negPrefixPattern);
- String negSuffixPattern = (String) Format.getInternalField(
- "negSuffixPattern", dform);
- fields.put("negSuffixPattern", negSuffixPattern);
+ fields.put("posPrefixPattern", (String) null);
+ fields.put("posSuffixPattern", (String) null);
+ fields.put("negPrefixPattern", (String) null);
+ fields.put("negSuffixPattern", (String) null);
fields.put("multiplier", dform.getMultiplier());
fields.put("groupingSize", (byte) dform.getGroupingSize());
// BEGIN android-added
@@ -1230,7 +1222,7 @@
fields.put("parseBigDecimal", parseBigDecimal);
fields.put("symbols", symbols);
fields.put("useExponentialNotation", false);
- fields.put("minExponentDigits", 0);
+ fields.put("minExponentDigits", (byte) 0);
fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits());
fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits());
fields.put("maximumFractionDigits", dform.getMaximumFractionDigits());
@@ -1254,52 +1246,24 @@
private void readObject(ObjectInputStream stream) throws IOException,
ClassNotFoundException {
- ObjectInputStream.GetField fields = stream.readFields();
- String positivePrefix = (String) fields.get("positivePrefix", "");
- String positiveSuffix = (String) fields.get("positiveSuffix", "");
- String negativePrefix = (String) fields.get("negativePrefix", "-");
- String negativeSuffix = (String) fields.get("negativeSuffix", "");
-
- String posPrefixPattern = (String) fields.get("posPrefixPattern", "");
- String posSuffixPattern = (String) fields.get("posSuffixPattern", "");
- String negPrefixPattern = (String) fields.get("negPrefixPattern", "-");
- String negSuffixPattern = (String) fields.get("negSuffixPattern", "");
-
- int multiplier = fields.get("multiplier", 1);
- byte groupingSize = fields.get("groupingSize", (byte) 3);
- // BEGIN android-added
- boolean groupingUsed = fields.get("groupingUsed", true);
- // END android-added
- boolean decimalSeparatorAlwaysShown = fields.get(
- "decimalSeparatorAlwaysShown", false);
- boolean parseBigDecimal = fields.get("parseBigDecimal", false);
- symbols = (DecimalFormatSymbols) fields.get("symbols", null);
-
- int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309);
- int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309);
- int maximumFractionDigits = fields.get("maximumFractionDigits", 340);
- int minimumFractionDigits = fields.get("minimumFractionDigits", 340);
- int serialVersionOnStream = fields.get("serialVersionOnStream", 0);
-
- Locale locale = (Locale) Format.getInternalField("locale", symbols);
// BEGIN android-changed
+ ObjectInputStream.GetField fields = stream.readFields();
+ this.symbols = (DecimalFormatSymbols) fields.get("symbols", null);
+
initNative("");
- // END android-changed
- dform.setPositivePrefix(positivePrefix);
- dform.setPositiveSuffix(positiveSuffix);
- dform.setNegativePrefix(negativePrefix);
- dform.setNegativeSuffix(negativeSuffix);
- setInternalField("posPrefixPattern", dform, posPrefixPattern);
- setInternalField("posSuffixPattern", dform, posSuffixPattern);
- setInternalField("negPrefixPattern", dform, negPrefixPattern);
- setInternalField("negSuffixPattern", dform, negSuffixPattern);
- dform.setMultiplier(multiplier);
- dform.setGroupingSize(groupingSize);
- // BEGIN android-added
- dform.setGroupingUsed(groupingUsed);
- // END android-added
- dform.setDecimalSeparatorAlwaysShown(decimalSeparatorAlwaysShown);
- setMinimumIntegerDigits(minimumIntegerDigits);
+ dform.setPositivePrefix((String) fields.get("positivePrefix", ""));
+ dform.setPositiveSuffix((String) fields.get("positiveSuffix", ""));
+ dform.setNegativePrefix((String) fields.get("negativePrefix", "-"));
+ dform.setNegativeSuffix((String) fields.get("negativeSuffix", ""));
+ dform.setMultiplier(fields.get("multiplier", 1));
+ dform.setGroupingSize(fields.get("groupingSize", (byte) 3));
+ dform.setGroupingUsed(fields.get("groupingUsed", true));
+ dform.setDecimalSeparatorAlwaysShown(fields.get("decimalSeparatorAlwaysShown", false));
+
+ final int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309);
+ final int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309);
+ final int maximumFractionDigits = fields.get("maximumFractionDigits", 340);
+ final int minimumFractionDigits = fields.get("minimumFractionDigits", 340);
// BEGIN android-changed: tell ICU what we want, then ask it what we can have, and then
// set that in our Java object. This isn't RI-compatible, but then very little of our
// behavior in this area is, and it's not obvious how we can second-guess ICU (or tell
@@ -1307,41 +1271,18 @@
// because ICU doesn't seem to have its own ideas about the other options.
dform.setMaximumIntegerDigits(maximumIntegerDigits);
super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits());
- // END android-changed
+
+ setMinimumIntegerDigits(minimumIntegerDigits);
setMinimumFractionDigits(minimumFractionDigits);
setMaximumFractionDigits(maximumFractionDigits);
- setParseBigDecimal(parseBigDecimal);
+ setParseBigDecimal(fields.get("parseBigDecimal", false));
- if (serialVersionOnStream < 3) {
+ if (fields.get("serialVersionOnStream", 0) < 3) {
setMaximumIntegerDigits(super.getMaximumIntegerDigits());
setMinimumIntegerDigits(super.getMinimumIntegerDigits());
setMaximumFractionDigits(super.getMaximumFractionDigits());
setMinimumFractionDigits(super.getMinimumFractionDigits());
}
- }
-
- /*
- * Sets private field value by reflection.
- *
- * @param fieldName the field name to be set @param target the object which
- * field to be set @param value the value to be set
- */
- private void setInternalField(final String fieldName, final Object target,
- final Object value) {
- AccessController
- .doPrivileged(new PrivilegedAction<java.lang.reflect.Field>() {
- public java.lang.reflect.Field run() {
- java.lang.reflect.Field field = null;
- try {
- field = target.getClass().getDeclaredField(
- fieldName);
- field.setAccessible(true);
- field.set(target, value);
- } catch (Exception e) {
- return null;
- }
- return field;
- }
- });
+ // END android-changed
}
}
diff --git a/libcore/text/src/main/java/java/text/Format.java b/libcore/text/src/main/java/java/text/Format.java
index 567b0f6..18b0490 100644
--- a/libcore/text/src/main/java/java/text/Format.java
+++ b/libcore/text/src/main/java/java/text/Format.java
@@ -216,32 +216,6 @@
*/
public abstract Object parseObject(String string, ParsePosition position);
- /*
- * Gets private field value by reflection.
- *
- * @param fieldName the field name to be set @param target the object which
- * field to be gotten
- */
- static Object getInternalField(final String fieldName, final Object target) {
- Object value = AccessController
- .doPrivileged(new PrivilegedAction<Object>() {
- public Object run() {
- Object result = null;
- java.lang.reflect.Field field = null;
- try {
- field = target.getClass().getDeclaredField(
- fieldName);
- field.setAccessible(true);
- result = field.get(target);
- } catch (Exception e1) {
- return null;
- }
- return result;
- }
- });
- return value;
- }
-
static boolean upTo(String string, ParsePosition position,
StringBuffer buffer, char stop) {
int index = position.getIndex(), length = string.length();
diff --git a/libcore/text/src/main/java/java/text/SimpleDateFormat.java b/libcore/text/src/main/java/java/text/SimpleDateFormat.java
index a67c7e6..20fff63 100644
--- a/libcore/text/src/main/java/java/text/SimpleDateFormat.java
+++ b/libcore/text/src/main/java/java/text/SimpleDateFormat.java
@@ -509,11 +509,6 @@
// BEGIN android-removed
// SimpleDateFormat(Locale locale, com.ibm.icu.text.SimpleDateFormat icuFormat){
- // this(locale);
- // this.icuFormat = icuFormat;
- // this.icuFormat.setTimeZone(com.ibm.icu.util.TimeZone.getTimeZone(tzId));
- // pattern = (String)Format.getInternalField("pattern", icuFormat); //$NON-NLS-1$
- // formatData = new DateFormatSymbols(locale);
// }
// END android-removed
diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java
index b92e37b..7a615d5 100644
--- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java
+++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java
@@ -776,17 +776,17 @@
// Values based on Java 1.5 RI DecimalFormatSymbols for Locale.FRANCE
/*
* currency = [EUR]
- * currencySymbol = [€][U+20ac]
+ * currencySymbol = [U+20ac] // EURO SIGN
* decimalSeparator = [,][U+002c]
* digit = [#][U+0023]
- * groupingSeparator = [ ][U+00a0]
- * infinity = [∞][U+221e]
+ * groupingSeparator = [U+00a0] // NON-BREAKING SPACE
+ * infinity = [U+221e] // INFINITY
* internationalCurrencySymbol = [EUR]
* minusSign = [-][U+002d]
* monetaryDecimalSeparator = [,][U+002c]
- * naN = [�][U+fffd]
+ * naN = [U+fffd] // REPLACEMENT CHARACTER
* patternSeparator = [;][U+003b]
- * perMill = [‰][U+2030]
+ * perMill = [U+2030] // PER MILLE
* percent = [%][U+0025]
* zeroDigit = [0][U+0030]
*/
@@ -800,6 +800,8 @@
assertEquals('-', dfs.getMinusSign());
assertEquals(',', dfs.getMonetaryDecimalSeparator());
// RI's default NaN is U+FFFD, Harmony's is based on ICU
+ // This suggests an RI bug, assuming that non-UTF8 bytes are UTF8 and
+ // getting a conversion failure.
assertEquals("\uFFFD", dfs.getNaN());
assertEquals('\u003b', dfs.getPatternSeparator());
assertEquals('\u2030', dfs.getPerMill());
diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java
index 7c4a319..e949f1d 100644
--- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java
+++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java
@@ -148,11 +148,10 @@
// Test for method java.text.NumberFormat
// getIntegerInstance(java.util.Locale)
Locale usLocale = Locale.US;
- // BEGIN android-changed
- // use de_CH instead
- // Locale arLocale = new Locale("ar", "AE");
+ Locale arLocale = new Locale("ar", "AE");
+ // BEGIN android-added: use de_CH too.
Locale chLocale = new Locale("de", "CH");
- // END android-changed
+ // END android-added
Locale[] requiredLocales = {usLocale, chLocale};
if (!Support_Locale.areLocalesAvailable(requiredLocales)) {
@@ -182,9 +181,7 @@
format.isParseIntegerOnly());
// try with a locale that has a different integer pattern
- // BEGIN android-changed
- // use de_CH instead
- // format = (DecimalFormat) NumberFormat.getIntegerInstance(arLocale);
+ // BEGIN android-added: try de_CH too
format = (DecimalFormat) NumberFormat.getIntegerInstance(chLocale);
assertEquals(
"Test7: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).toPattern() returned wrong pattern",
@@ -205,8 +202,9 @@
assertTrue(
"Test12: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).isParseIntegerOnly() returned wrong value",
format.isParseIntegerOnly());
- // use de_CH instead
- /*assertEquals(
+ // END android-added
+ format = (DecimalFormat) NumberFormat.getIntegerInstance(arLocale);
+ assertEquals(
"Test7: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).toPattern() returned wrong pattern",
"#,##0;#,##0-", format.toPattern());
assertEquals(
@@ -224,8 +222,7 @@
0, format.getMaximumFractionDigits());
assertTrue(
"Test12: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).isParseIntegerOnly() returned wrong value",
- format.isParseIntegerOnly());*/
- // END android-changed
+ format.isParseIntegerOnly());
}
/**
@@ -645,19 +642,20 @@
// use de_AT instead
// format = NumberFormat.getCurrencyInstance(mkLocale);
format = NumberFormat.getCurrencyInstance(atLocale);
-
+ // BEGIN android-changed: ICU uses non-breaking space after the euro sign; the RI uses ' '.
assertEquals(
"Test5: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(35.76) returned wrong value",
- "\u20ac 35,76", format.format(35.76));
+ "\u20ac\u00a035,76", format.format(35.76));
assertEquals(
"Test6: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(123456.789) returned wrong value",
- "\u20ac 123.456,79", format.format(123456.789));
+ "\u20ac\u00a0123.456,79", format.format(123456.789));
assertEquals(
"Test7: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(0.1) returned wrong value",
- "\u20ac 0,10", format.format(0.1));
+ "\u20ac\u00a00,10", format.format(0.1));
assertEquals(
"Test8: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(0.999) returned wrong value",
- "\u20ac 1,00", format.format(0.999));
+ "\u20ac\u00a01,00", format.format(0.999));
+ // END android-changed
// use de_AT instead
/*assertEquals(
"Test5: NumberFormat.getCurrencyInstance(new Locale(\"mk\", \"MK\")).format(35.76) returned wrong value",
diff --git a/libcore/tools/runner/Android.mk b/libcore/tools/runner/Android.mk
index ee5c4f1..851214b 100644
--- a/libcore/tools/runner/Android.mk
+++ b/libcore/tools/runner/Android.mk
@@ -32,6 +32,8 @@
java/dalvik/runner/MainFinder.java \
java/dalvik/runner/MainRunner.java \
java/dalvik/runner/NamingPatternCodeFinder.java \
+ java/dalvik/runner/Option.java \
+ java/dalvik/runner/OptionParser.java \
java/dalvik/runner/Result.java \
java/dalvik/runner/Strings.java \
java/dalvik/runner/TestRun.java \
diff --git a/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java b/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java
index 84d54ef..20e722c 100644
--- a/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java
+++ b/libcore/tools/runner/java/dalvik/runner/DalvikRunner.java
@@ -17,6 +17,7 @@
package dalvik.runner;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -34,25 +35,203 @@
*/
public final class DalvikRunner {
- private final File localTemp;
- private File sdkJar;
- private Integer debugPort;
- private long timeoutSeconds;
- private Set<File> expectationFiles = new LinkedHashSet<File>();
- private File xmlReportsDirectory;
- private String javaHome;
- private List<String> vmArgs = new ArrayList<String>();
- private boolean clean = true;
- private String deviceRunnerDir = "/sdcard/dalvikrunner";
- private List<File> testFiles = new ArrayList<File>();
+ private static class Options {
- private DalvikRunner() {
- localTemp = new File("/tmp/" + UUID.randomUUID());
- timeoutSeconds = 10 * 60; // default is ten minutes
- sdkJar = new File("/home/dalvik-prebuild/android-sdk-linux/platforms/android-2.0/android.jar");
- expectationFiles.add(new File("dalvik/libcore/tools/runner/expectations.txt"));
+ private final List<File> testFiles = new ArrayList<File>();
+
+ @Option(names = { "--expectations" })
+ private Set<File> expectationFiles = new LinkedHashSet<File>();
+ {
+ expectationFiles.add(new File("dalvik/libcore/tools/runner/expectations.txt"));
+ }
+
+ private static String MODE_DEVICE = "device";
+ private static String MODE_HOST = "host";
+ private static String MODE_ACTIVITY = "activity";
+ @Option(names = { "--mode" })
+ private String mode = MODE_DEVICE;
+
+ @Option(names = { "--timeout" })
+ private long timeoutSeconds = 10 * 60; // default is ten minutes;
+
+ @Option(names = { "--clean" })
+ private boolean clean = true;
+
+ @Option(names = { "--xml-reports-directory" })
+ private File xmlReportsDirectory;
+
+ @Option(names = { "--verbose" })
+ private boolean verbose;
+
+ @Option(names = { "--debug" })
+ private Integer debugPort;
+
+ @Option(names = { "--device-runner-dir" })
+ private File deviceRunnerDir = new File("/sdcard/dalvikrunner");
+
+ @Option(names = { "--vm-arg" })
+ private List<String> vmArgs = new ArrayList<String>();
+
+ @Option(names = { "--java-home" })
+ private File javaHome;
+
+ @Option(names = { "--sdk" })
+ private File sdkJar = new File("/home/dalvik-prebuild/android-sdk-linux/platforms/android-2.0/android.jar");
+
+ private void printUsage() {
+ System.out.println("Usage: DalvikRunner [options]... <tests>...");
+ System.out.println();
+ System.out.println(" <tests>: a .java file containing a jtreg test, JUnit test,");
+ System.out.println(" Caliper benchmark, or a directory of such tests.");
+ System.out.println();
+ System.out.println("GENERAL OPTIONS");
+ System.out.println();
+ System.out.println(" --expectations <file>: include the specified file when looking for");
+ System.out.println(" test expectations. The file should include qualified test names");
+ System.out.println(" and the corresponding expected output.");
+ System.out.println(" Default is: " + expectationFiles);
+ System.out.println();
+ System.out.println(" --mode <device|host|activity>: specify which environment to run the");
+ System.out.println(" tests in. Options are on the device VM, on the host VM, and on");
+ System.out.println(" device within an android.app.Activity.");
+ System.out.println(" Default is: " + mode);
+ System.out.println();
+ System.out.println(" --clean: remove temporary files (default). Disable with --no-clean");
+ System.out.println(" and use with --verbose if you'd like to manually re-run");
+ System.out.println(" commands afterwards.");
+ System.out.println();
+ System.out.println(" --timeout-seconds <seconds>: maximum execution time of each");
+ System.out.println(" test before the runner aborts it.");
+ System.out.println(" Default is: " + timeoutSeconds);
+ System.out.println();
+ System.out.println(" --xml-reports-directory <path>: directory to emit JUnit-style");
+ System.out.println(" XML test results.");
+ System.out.println();
+ System.out.println(" --verbose: turn on verbose output");
+ System.out.println();
+ System.out.println("DEVICE OPTIONS");
+ System.out.println();
+ System.out.println(" --debug <port>: enable Java debugging on the specified port.");
+ System.out.println(" This port must be free both on the device and on the local");
+ System.out.println(" system.");
+ System.out.println();
+ System.out.println(" --device-runner-dir <directory>: use the specified directory for");
+ System.out.println(" on-device temporary files and code.");
+ System.out.println(" Default is: " + deviceRunnerDir);
+ System.out.println();
+ System.out.println("GENERAL VM OPTIONS");
+ System.out.println();
+ System.out.println(" --vm-arg <argument>: include the specified argument when spawning a");
+ System.out.println(" virtual machine. Examples: -Xint:fast, -ea, -Xmx16M");
+ System.out.println();
+ System.out.println("HOST VM OPTIONS");
+ System.out.println();
+ System.out.println(" --java-home <java_home>: execute the tests on the local workstation");
+ System.out.println(" using the specified java home directory. This does not impact");
+ System.out.println(" which javac gets used. When unset, java is used from the PATH.");
+ System.out.println();
+ System.out.println("COMPILE OPTIONS");
+ System.out.println();
+ System.out.println(" --sdk <android jar>: the API jar file to compile against.");
+ System.out.println(" Usually this is <SDK>/platforms/android-<X.X>/android.jar");
+ System.out.println(" where <SDK> is the path to an Android SDK path and <X.X> is");
+ System.out.println(" a release version like 1.5.");
+ System.out.println(" Default is: " + sdkJar);
+ System.out.println();
+ }
+
+ private boolean parseArgs(String[] args) {
+ final List<String> testFilenames;
+ try {
+ testFilenames = new OptionParser(this).parse(args);
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ return false;
+ }
+
+ //
+ // Semantic error validation
+ //
+
+ boolean device;
+ boolean vm;
+ if (mode.equals(MODE_DEVICE)) {
+ device = true;
+ vm = true;
+ } else if (mode.equals(MODE_HOST)) {
+ device = false;
+ vm = true;
+ } else if (mode.equals(MODE_ACTIVITY)) {
+ device = true;
+ vm = false;
+ } else {
+ System.out.println("Unknown mode: " + mode);
+ return false;
+ }
+
+
+ if (device) { // check device option consistency
+ if (javaHome != null) {
+ System.out.println("java home " + javaHome + " should not be specified for mode " + mode);
+ return false;
+ }
+
+ } else { // check host (!device) option consistency
+ if (javaHome != null && !new File(javaHome, "/bin/java").exists()) {
+ System.out.println("Invalid java home: " + javaHome);
+ return false;
+ }
+ if (debugPort != null) {
+ System.out.println("debug port " + debugPort + " should not be specified for mode " + mode);
+ return false;
+ }
+ }
+
+ // check vm option consistency
+ if (!vm) {
+ if (!vmArgs.isEmpty()) {
+ System.out.println("vm args " + vmArgs + " should not be specified for mode " + mode);
+ return false;
+ }
+ }
+
+ if (!sdkJar.exists()) {
+ System.out.println("Could not find SDK jar: " + sdkJar);
+ return false;
+ }
+
+ if (xmlReportsDirectory != null && !xmlReportsDirectory.isDirectory()) {
+ System.out.println("Invalid XML reports directory: " + xmlReportsDirectory);
+ return false;
+ }
+
+ if (testFilenames.isEmpty()) {
+ System.out.println("No tests provided.");
+ return false;
+ }
+
+ //
+ // Post-processing arguments
+ //
+
+ for (String testFilename : testFilenames) {
+ testFiles.add(new File(testFilename));
+ }
+
+ if (verbose) {
+ Logger.getLogger("dalvik.runner").setLevel(Level.FINE);
+ }
+
+ return true;
+ }
+
}
+ private final Options options = new Options();
+ private final File localTemp = new File("/tmp/" + UUID.randomUUID());
+
+ private DalvikRunner() {}
+
private void prepareLogging() {
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
@@ -66,139 +245,61 @@
logger.setUseParentHandlers(false);
}
- private boolean parseArgs(String[] args) throws Exception {
- for (int i = 0; i < args.length; i++) {
- if ("--debug".equals(args[i])) {
- debugPort = Integer.valueOf(args[++i]);
-
- } else if ("--device-runner-dir".equals(args[i])) {
- deviceRunnerDir = args[++i];
-
- } else if ("--expectations".equals(args[i])) {
- expectationFiles.add(new File(args[++i]));
-
- } else if ("--java-home".equals(args[i])) {
- javaHome = args[++i];
- if (!new File(javaHome, "/bin/java").exists()) {
- System.out.println("Invalid java home: " + javaHome);
- return false;
- }
-
- } else if ("--timeout-seconds".equals(args[i])) {
- timeoutSeconds = Long.valueOf(args[++i]);
-
- } else if ("--sdk".equals(args[i])) {
- sdkJar = new File(args[++i]);
- if (!sdkJar.exists()) {
- System.out.println("Could not find SDK jar: " + sdkJar);
- return false;
- }
-
- } else if ("--skip-clean".equals(args[i])) {
- clean = false;
-
- } else if ("--verbose".equals(args[i])) {
- Logger.getLogger("dalvik.runner").setLevel(Level.FINE);
-
- } else if ("--vm-arg".equals(args[i])) {
- vmArgs.add(args[++i]);
-
- } else if ("--xml-reports-directory".equals(args[i])) {
- xmlReportsDirectory = new File(args[++i]);
- if (!xmlReportsDirectory.isDirectory()) {
- System.out.println("Invalid XML reports directory: " + xmlReportsDirectory);
- return false;
- }
-
- } else if (args[i].startsWith("-")) {
- System.out.println("Unrecognized option: " + args[i]);
- return false;
-
- } else {
- testFiles.add(new File(args[i]));
- }
+ private void run() {
+ Vm vm;
+ if (options.mode.equals(Options.MODE_DEVICE)) {
+ vm = new DeviceDalvikVm(
+ options.debugPort,
+ options.timeoutSeconds,
+ options.sdkJar,
+ localTemp,
+ options.vmArgs,
+ options.clean,
+ options.deviceRunnerDir);
+ } else if (options.mode.equals(Options.MODE_HOST)) {
+ vm = new JavaVm(
+ options.debugPort,
+ options.timeoutSeconds,
+ options.sdkJar,
+ localTemp,
+ options.javaHome,
+ options.vmArgs,
+ options.clean);
+ } else if (options.mode.equals(Options.MODE_ACTIVITY)) {
+ vm = null;
+ System.out.println("Mode " + options.mode + " not currently supported.");
+ return;
+ } else {
+ System.out.println("Unknown mode mode " + options.mode + ".");
+ return;
}
- if (testFiles.isEmpty()) {
- System.out.println("No tests provided.");
- return false;
- }
-
- return true;
- }
-
- private void printUsage() {
- System.out.println("Usage: DalvikRunner [options]... <tests>...");
- System.out.println();
- System.out.println(" <tests>: a .java file containing a jtreg test, JUnit test,");
- System.out.println(" Caliper benchmark, or a directory of such tests.");
- System.out.println();
- System.out.println("OPTIONS");
- System.out.println();
- System.out.println(" --debug <port>: enable Java debugging on the specified port.");
- System.out.println(" This port must be free both on the device and on the local");
- System.out.println(" system.");
- System.out.println();
- System.out.println(" ----device-runner-dir <directory>: use the specified directory for");
- System.out.println(" on-device temporary files and code.");
- System.out.println(" Default is: " + deviceRunnerDir);
- System.out.println();
- System.out.println(" --expectations <file>: include the specified file when looking for");
- System.out.println(" test expectations. The file should include qualified test names");
- System.out.println(" and the corresponding expected output.");
- System.out.println(" Default is: " + expectationFiles);
- System.out.println();
- System.out.println(" --java-home <java_home>: execute the tests on the local workstation");
- System.out.println(" using the specified java home directory. This does not impact");
- System.out.println(" which javac gets used. When unset, tests are run on a device");
- System.out.println(" using adb.");
- System.out.println();
- System.out.println(" --sdk <android jar>: the API jar file to compile against.");
- System.out.println(" Usually this is <SDK>/platforms/android-<X.X>/android.jar");
- System.out.println(" where <SDK> is the path to an Android SDK path and <X.X> is");
- System.out.println(" a release version like 1.5.");
- System.out.println(" Default is: " + sdkJar);
- System.out.println();
- System.out.println(" --skip-clean: leave temporary files in their place. Useful when");
- System.out.println(" coupled with --verbose if you'd like to manually re-run");
- System.out.println(" commands afterwards.");
- System.out.println();
- System.out.println(" --timeout-seconds <seconds>: maximum execution time of each");
- System.out.println(" test before the runner aborts it.");
- System.out.println(" Default is: " + timeoutSeconds);
- System.out.println();
- System.out.println(" --vm-arg <argument>: include the specified argument when spawning a");
- System.out.println(" virtual machine. Examples: -Xint:fast, -ea, -Xmx16M");
- System.out.println();
- System.out.println(" --xml-reports-directory <path>: directory to emit JUnit-style");
- System.out.println(" XML test results.");
- System.out.println();
- System.out.println(" --verbose: turn on verbose output");
- System.out.println();
- }
-
- private void run() throws Exception {
- Vm vm = javaHome != null
- ? new JavaVm(debugPort, timeoutSeconds, sdkJar, localTemp,
- javaHome, vmArgs, clean)
- : new DeviceDalvikVm(debugPort, timeoutSeconds, sdkJar,
- localTemp, vmArgs, clean, deviceRunnerDir);
List<CodeFinder> codeFinders = Arrays.asList(
new JtregFinder(localTemp),
new JUnitFinder(),
new CaliperFinder(),
new MainFinder());
- Driver driver = new Driver(localTemp,
- vm, expectationFiles, xmlReportsDirectory, codeFinders);
- driver.loadExpectations();
- driver.buildAndRunAllTests(testFiles);
+ Driver driver = new Driver(
+ localTemp,
+ vm,
+ options.expectationFiles,
+ options.xmlReportsDirectory,
+ codeFinders);
+ try {
+ driver.loadExpectations();
+ } catch (IOException e) {
+ System.out.println("Problem loading expectations: " + e);
+ return;
+ }
+
+ driver.buildAndRunAllTests(options.testFiles);
vm.shutdown();
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) {
DalvikRunner dalvikRunner = new DalvikRunner();
- if (!dalvikRunner.parseArgs(args)) {
- dalvikRunner.printUsage();
+ if (!dalvikRunner.options.parseArgs(args)) {
+ dalvikRunner.options.printUsage();
return;
}
dalvikRunner.prepareLogging();
diff --git a/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java b/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java
index 47db11f..4388565 100644
--- a/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java
+++ b/libcore/tools/runner/java/dalvik/runner/DeviceDalvikVm.java
@@ -38,10 +38,10 @@
private final Adb adb = new Adb();
DeviceDalvikVm(Integer debugPort, long timeoutSeconds, File sdkJar,
- File localTemp, List<String> additionalVmArgs, boolean clean, String runnerDir) {
+ File localTemp, List<String> additionalVmArgs, boolean clean, File runnerDir) {
super(debugPort, timeoutSeconds, sdkJar, localTemp, additionalVmArgs, clean);
- this.runnerDir = new File(runnerDir);
+ this.runnerDir = runnerDir;
this.testTemp = new File(this.runnerDir, "/tests.tmp");
}
diff --git a/libcore/tools/runner/java/dalvik/runner/Driver.java b/libcore/tools/runner/java/dalvik/runner/Driver.java
index 7a30ab7..81c7d6b 100644
--- a/libcore/tools/runner/java/dalvik/runner/Driver.java
+++ b/libcore/tools/runner/java/dalvik/runner/Driver.java
@@ -72,7 +72,7 @@
/**
* Builds and executes all tests in the test directory.
*/
- public void buildAndRunAllTests(Collection<File> testFiles) throws Exception {
+ public void buildAndRunAllTests(Collection<File> testFiles) {
localTemp.mkdirs();
final BlockingQueue<TestRun> readyToRun = new ArrayBlockingQueue<TestRun>(4);
@@ -136,10 +136,15 @@
+ readyToRun.size() + " are ready to run");
// if it takes 5 minutes for build and install, something is broken
- TestRun testRun = readyToRun.poll(300, TimeUnit.SECONDS);
+ TestRun testRun;
+ try {
+ testRun = readyToRun.poll(5 * 60, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Unexpected interruption waiting for build and install", e);
+ }
+
if (testRun == null) {
- throw new IllegalStateException(
- "Expected " + tests.size() + " tests but found only " + i);
+ throw new IllegalStateException("Expected " + tests.size() + " tests but found only " + i);
}
runs.add(testRun);
diff --git a/libcore/tools/runner/java/dalvik/runner/JavaVm.java b/libcore/tools/runner/java/dalvik/runner/JavaVm.java
index 8b53477..e29b36b 100644
--- a/libcore/tools/runner/java/dalvik/runner/JavaVm.java
+++ b/libcore/tools/runner/java/dalvik/runner/JavaVm.java
@@ -24,18 +24,19 @@
*/
final class JavaVm extends Vm {
- private final String javaHome;
+ private final File javaHome;
JavaVm(Integer debugPort, long timeoutSeconds, File sdkJar, File localTemp,
- String javaHome, List<String> additionalVmArgs, boolean clean) {
+ File javaHome, List<String> additionalVmArgs, boolean clean) {
super(debugPort, timeoutSeconds, sdkJar, localTemp, additionalVmArgs, clean);
this.javaHome = javaHome;
}
@Override protected VmCommandBuilder newVmCommandBuilder(
File workingDirectory) {
+ String java = javaHome == null ? "java" : new File(javaHome, "bin/java").getPath();
return new VmCommandBuilder()
- .vmCommand(javaHome + "/bin/java")
+ .vmCommand(java)
.workingDir(workingDirectory);
}
}
diff --git a/libcore/tools/runner/java/dalvik/runner/Option.java b/libcore/tools/runner/java/dalvik/runner/Option.java
new file mode 100644
index 0000000..779aa63
--- /dev/null
+++ b/libcore/tools/runner/java/dalvik/runner/Option.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.runner;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a field as representing a command-line option for OptionParser.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Option {
+ /**
+ * The names for this option, such as { "-h", "--help" }.
+ * Names must start with one or two '-'s.
+ * An option must have at least one name.
+ */
+ String[] names();
+}
diff --git a/libcore/tools/runner/java/dalvik/runner/OptionParser.java b/libcore/tools/runner/java/dalvik/runner/OptionParser.java
new file mode 100644
index 0000000..64af51c
--- /dev/null
+++ b/libcore/tools/runner/java/dalvik/runner/OptionParser.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.runner;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Parses command line options.
+ *
+ * Strings in the passed-in String[] are parsed left-to-right. Each
+ * String is classified as a short option (such as "-v"), a long
+ * option (such as "--verbose"), an argument to an option (such as
+ * "out.txt" in "-f out.txt"), or a non-option positional argument.
+ *
+ * A simple short option is a "-" followed by a short option
+ * character. If the option requires an argument (which is true of any
+ * non-boolean option), it may be written as a separate parameter, but
+ * need not be. That is, "-f out.txt" and "-fout.txt" are both
+ * acceptable.
+ *
+ * It is possible to specify multiple short options after a single "-"
+ * as long as all (except possibly the last) do not require arguments.
+ *
+ * A long option begins with "--" followed by several characters. If
+ * the option requires an argument, it may be written directly after
+ * the option name, separated by "=", or as the next argument. (That
+ * is, "--file=out.txt" or "--file out.txt".)
+ *
+ * A boolean long option '--name' automatically gets a '--no-name'
+ * companion. Given an option "--flag", then, "--flag", "--no-flag",
+ * "--flag=true" and "--flag=false" are all valid, though neither
+ * "--flag true" nor "--flag false" are allowed (since "--flag" by
+ * itself is sufficient, the following "true" or "false" is
+ * interpreted separately). You can use "yes" and "no" as synonyms for
+ * "true" and "false".
+ *
+ * Each String not starting with a "-" and not a required argument of
+ * a previous option is a non-option positional argument, as are all
+ * successive Strings. Each String after a "--" is a non-option
+ * positional argument.
+ *
+ * Parsing of numeric fields such byte, short, int, long, float, and
+ * double fields is supported. This includes both unboxed and boxed
+ * versions (e.g. int vs Integer). If there is a problem parsing the
+ * argument to match the desired type, a runtime exception is thrown.
+ *
+ * File option fields are supported by simply wrapping the string
+ * argument in a File object without testing for the existance of the
+ * file.
+ *
+ * Parameterized Collection fields such as List<File> and Set<String>
+ * are supported as long as the parameter type is otherwise supported
+ * by the option parser. The collection field should be initialized
+ * with an appropriate collection instance.
+ *
+ * The fields corresponding to options are updated as their options
+ * are processed. Any remaining positional arguments are returned as a
+ * List<String>.
+ *
+ * Here's a simple example:
+ *
+ * // This doesn't need to be a separate class, if your application doesn't warrant it.
+ * // Non-@Option fields will be ignored.
+ * class Options {
+ * @Option(names = { "-q", "--quiet" })
+ * boolean quiet = false;
+ *
+ * // Boolean options require a long name if it's to be possible to explicitly turn them off.
+ * // Here the user can use --no-color.
+ * @Option(names = { "--color" })
+ * boolean color = true;
+ *
+ * @Option(names = { "-m", "--mode" })
+ * String mode = "standard; // Supply a default just by setting the field.
+ *
+ * @Option(names = { "-p", "--port" })
+ * int portNumber = 8888;
+ *
+ * // There's no need to offer a short name for rarely-used options.
+ * @Option(names = { "--timeout" })
+ * double timeout = 1.0;
+ *
+ * @Option(names = { "-o", "--output-file" })
+ * File output;
+ *
+ * // Multiple options are added to the collection.
+ * // The collection field itself must be non-null.
+ * @Option(names = { "-i", "--input-file" })
+ * List<File> inputs = new ArrayList<File>();
+ *
+ * }
+ *
+ * class Main {
+ * public static void main(String[] args) {
+ * Options options = new Options();
+ * List<String> inputFilenames = new OptionParser(options).parse(args);
+ * for (String inputFilename : inputFilenames) {
+ * if (!options.quiet) {
+ * ...
+ * }
+ * ...
+ * }
+ * }
+ * }
+ *
+ * See also:
+ *
+ * the getopt(1) man page
+ * Python's "optparse" module (http://docs.python.org/library/optparse.html)
+ * the POSIX "Utility Syntax Guidelines" (http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap12.html#tag_12_02)
+ * the GNU "Standards for Command Line Interfaces" (http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces)
+ */
+public class OptionParser {
+ private static final HashMap<Class<?>, Handler> handlers = new HashMap<Class<?>, Handler>();
+ static {
+ handlers.put(boolean.class, new BooleanHandler());
+ handlers.put(Boolean.class, new BooleanHandler());
+
+ handlers.put(byte.class, new ByteHandler());
+ handlers.put(Byte.class, new ByteHandler());
+ handlers.put(short.class, new ShortHandler());
+ handlers.put(Short.class, new ShortHandler());
+ handlers.put(int.class, new IntegerHandler());
+ handlers.put(Integer.class, new IntegerHandler());
+ handlers.put(long.class, new LongHandler());
+ handlers.put(Long.class, new LongHandler());
+
+ handlers.put(float.class, new FloatHandler());
+ handlers.put(Float.class, new FloatHandler());
+ handlers.put(double.class, new DoubleHandler());
+ handlers.put(Double.class, new DoubleHandler());
+
+ handlers.put(String.class, new StringHandler());
+ handlers.put(File.class, new FileHandler());
+ }
+ Handler getHandler(Type type) {
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Class rawClass = (Class<?>) parameterizedType.getRawType();
+ if (!Collection.class.isAssignableFrom(rawClass)) {
+ throw new RuntimeException("cannot handle non-collection parameterized type " + type);
+ }
+ Type actualType = parameterizedType.getActualTypeArguments()[0];
+ if (!(actualType instanceof Class)) {
+ throw new RuntimeException("cannot handle nested parameterized type " + type);
+ }
+ return getHandler(actualType);
+ }
+ if (type instanceof Class) {
+ if (Collection.class.isAssignableFrom((Class) type)) {
+ // could handle by just having a default of treating
+ // contents as String but consciously decided this
+ // should be an error
+ throw new RuntimeException(
+ "cannot handle non-parameterized collection " + type + ". " +
+ "use a generic Collection to specify a desired element type");
+ }
+ return handlers.get((Class<?>) type);
+ }
+ throw new RuntimeException("cannot handle unknown field type " + type);
+ }
+
+ private final Object optionSource;
+ private final HashMap<String, Field> optionMap;
+
+ /**
+ * Constructs a new OptionParser for setting the @Option fields of 'optionSource'.
+ */
+ public OptionParser(Object optionSource) {
+ this.optionSource = optionSource;
+ this.optionMap = makeOptionMap();
+ }
+
+ /**
+ * Parses the command-line arguments 'args', setting the @Option fields of the 'optionSource' provided to the constructor.
+ * Returns a list of the positional arguments left over after processing all options.
+ */
+ public List<String> parse(String[] args) {
+ return parseOptions(Arrays.asList(args).iterator());
+ }
+
+ private List<String> parseOptions(Iterator<String> args) {
+ final List<String> leftovers = new ArrayList<String>();
+
+ // Scan 'args'.
+ while (args.hasNext()) {
+ final String arg = args.next();
+ if (arg.equals("--")) {
+ // "--" marks the end of options and the beginning of positional arguments.
+ break;
+ } else if (arg.startsWith("--")) {
+ // A long option.
+ parseLongOption(arg, args);
+ } else if (arg.startsWith("-")) {
+ // A short option.
+ parseGroupedShortOptions(arg, args);
+ } else {
+ // The first non-option marks the end of options.
+ leftovers.add(arg);
+ break;
+ }
+ }
+
+ // Package up the leftovers.
+ while (args.hasNext()) {
+ leftovers.add(args.next());
+ }
+ return leftovers;
+ }
+
+ private Field fieldForArg(String name) {
+ final Field field = optionMap.get(name);
+ if (field == null) {
+ throw new RuntimeException("unrecognized option '" + name + "'");
+ }
+ return field;
+ }
+
+ private void parseLongOption(String arg, Iterator<String> args) {
+ String name = arg.replaceFirst("^--no-", "--");
+ String value = null;
+
+ // Support "--name=value" as well as "--name value".
+ final int equalsIndex = name.indexOf('=');
+ if (equalsIndex != -1) {
+ value = name.substring(equalsIndex + 1);
+ name = name.substring(0, equalsIndex);
+ }
+
+ final Field field = fieldForArg(name);
+ final Handler handler = getHandler(field.getGenericType());
+ if (value == null) {
+ if (handler.isBoolean()) {
+ value = arg.startsWith("--no-") ? "false" : "true";
+ } else {
+ value = grabNextValue(args, name, field);
+ }
+ }
+ setValue(optionSource, field, arg, handler, value);
+ }
+
+ // Given boolean options a and b, and non-boolean option f, we want to allow:
+ // -ab
+ // -abf out.txt
+ // -abfout.txt
+ // (But not -abf=out.txt --- POSIX doesn't mention that either way, but GNU expressly forbids it.)
+ private void parseGroupedShortOptions(String arg, Iterator<String> args) {
+ for (int i = 1; i < arg.length(); ++i) {
+ final String name = "-" + arg.charAt(i);
+ final Field field = fieldForArg(name);
+ final Handler handler = getHandler(field.getGenericType());
+ String value;
+ if (handler.isBoolean()) {
+ value = "true";
+ } else {
+ // We need a value. If there's anything left, we take the rest of this "short option".
+ if (i + 1 < arg.length()) {
+ value = arg.substring(i + 1);
+ i = arg.length() - 1;
+ } else {
+ value = grabNextValue(args, name, field);
+ }
+ }
+ setValue(optionSource, field, arg, handler, value);
+ }
+ }
+
+ private static void setValue(Object object, Field field, String arg, Handler handler, String valueText) {
+
+ Object value = handler.translate(valueText);
+ if (value == null) {
+ final String type = field.getType().getSimpleName().toLowerCase();
+ throw new RuntimeException("couldn't convert '" + valueText + "' to a " + type + " for option '" + arg + "'");
+ }
+ try {
+ field.setAccessible(true);
+ if (Collection.class.isAssignableFrom(field.getType())) {
+ Collection collection = (Collection)field.get(object);
+ collection.add(value);
+ } else {
+ field.set(object, value);
+ }
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException("internal error", ex);
+ }
+ }
+
+ // Returns the next element of 'args' if there is one. Uses 'name' and 'field' to construct a helpful error message.
+ private String grabNextValue(Iterator<String> args, String name, Field field) {
+ if (!args.hasNext()) {
+ final String type = field.getType().getSimpleName().toLowerCase();
+ throw new RuntimeException("option '" + name + "' requires a " + type + " argument");
+ }
+ return args.next();
+ }
+
+ // Cache the available options and report any problems with the options themselves right away.
+ private HashMap<String, Field> makeOptionMap() {
+ final HashMap<String, Field> optionMap = new HashMap<String, Field>();
+ final Class<?> optionClass = optionSource.getClass();
+ for (Field field : optionClass.getDeclaredFields()) {
+ if (field.isAnnotationPresent(Option.class)) {
+ final Option option = field.getAnnotation(Option.class);
+ final String[] names = option.names();
+ if (names.length == 0) {
+ throw new RuntimeException("found an @Option with no name!");
+ }
+ for (String name : names) {
+ if (optionMap.put(name, field) != null) {
+ throw new RuntimeException("found multiple @Options sharing the name '" + name + "'");
+ }
+ }
+ if (getHandler(field.getGenericType()) == null) {
+ throw new RuntimeException("unsupported @Option field type '" + field.getType() + "'");
+ }
+ }
+ }
+ return optionMap;
+ }
+
+ static abstract class Handler {
+ // Only BooleanHandler should ever override this.
+ boolean isBoolean() {
+ return false;
+ }
+
+ /**
+ * Returns an object of appropriate type for the given Handle, corresponding to 'valueText'.
+ * Returns null on failure.
+ */
+ abstract Object translate(String valueText);
+ }
+
+ static class BooleanHandler extends Handler {
+ @Override boolean isBoolean() {
+ return true;
+ }
+
+ Object translate(String valueText) {
+ if (valueText.equalsIgnoreCase("true") || valueText.equalsIgnoreCase("yes")) {
+ return Boolean.TRUE;
+ } else if (valueText.equalsIgnoreCase("false") || valueText.equalsIgnoreCase("no")) {
+ return Boolean.FALSE;
+ }
+ return null;
+ }
+ }
+
+ static class ByteHandler extends Handler {
+ Object translate(String valueText) {
+ try {
+ return Byte.parseByte(valueText);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+ }
+
+ static class ShortHandler extends Handler {
+ Object translate(String valueText) {
+ try {
+ return Short.parseShort(valueText);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+ }
+
+ static class IntegerHandler extends Handler {
+ Object translate(String valueText) {
+ try {
+ return Integer.parseInt(valueText);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+ }
+
+ static class LongHandler extends Handler {
+ Object translate(String valueText) {
+ try {
+ return Long.parseLong(valueText);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+ }
+
+ static class FloatHandler extends Handler {
+ Object translate(String valueText) {
+ try {
+ return Float.parseFloat(valueText);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+ }
+
+ static class DoubleHandler extends Handler {
+ Object translate(String valueText) {
+ try {
+ return Double.parseDouble(valueText);
+ } catch (NumberFormatException ex) {
+ return null;
+ }
+ }
+ }
+
+ static class StringHandler extends Handler {
+ Object translate(String valueText) {
+ return valueText;
+ }
+ }
+
+ static class FileHandler extends Handler {
+ Object translate(String valueText) {
+ return new File(valueText);
+ }
+ }
+}
diff --git a/libcore/tools/runner/java/dalvik/runner/Vm.java b/libcore/tools/runner/java/dalvik/runner/Vm.java
index 3d66fce..3afd7ae 100644
--- a/libcore/tools/runner/java/dalvik/runner/Vm.java
+++ b/libcore/tools/runner/java/dalvik/runner/Vm.java
@@ -51,11 +51,11 @@
private final Pattern JAVA_TEST_PATTERN = Pattern.compile("\\/(\\w)+\\.java$");
static final Classpath COMPILATION_CLASSPATH = Classpath.of(
- new File("out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar"),
- new File("out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar"),
- new File("out/target/common/obj/JAVA_LIBRARIES/jsr305_intermediates/classes.jar"),
- new File("out/target/common/obj/JAVA_LIBRARIES/guava_intermediates/classes.jar"),
- new File("out/target/common/obj/JAVA_LIBRARIES/caliper_intermediates/classes.jar"));
+ new File("out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar").getAbsoluteFile(),
+ new File("out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jar").getAbsoluteFile(),
+ new File("out/target/common/obj/JAVA_LIBRARIES/jsr305_intermediates/classes.jar").getAbsoluteFile(),
+ new File("out/target/common/obj/JAVA_LIBRARIES/guava_intermediates/classes.jar").getAbsoluteFile(),
+ new File("out/target/common/obj/JAVA_LIBRARIES/caliper_intermediates/classes.jar").getAbsoluteFile());
private static final Logger logger = Logger.getLogger(Vm.class.getName());
diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
index eacf0a0..5a3c48c 100644
--- a/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
+++ b/libcore/xml/src/main/java/org/apache/harmony/xml/parsers/DocumentBuilderImpl.java
@@ -17,6 +17,8 @@
package org.apache.harmony.xml.parsers;
import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilder;
@@ -122,10 +124,16 @@
parser.setInput(source.getByteStream(), source.getEncoding());
} else if (source.getCharacterStream() != null) {
parser.setInput(source.getCharacterStream());
+ } else if (source.getSystemId() != null) {
+ URL url = new URL(source.getSystemId());
+ URLConnection urlConnection = url.openConnection();
+ urlConnection.connect();
+ String encoding = source.getEncoding();
+ // TODO: if null, extract the encoding from the Content-Type header?
+ parser.setInput(urlConnection.getInputStream(), encoding);
} else {
- // TODO Accept other sources as well?
throw new SAXParseException(
- "InputSource needs either stream or reader", null);
+ "InputSource needs a stream, reader or URI", null);
}
if(parser.nextToken() == XmlPullParser.END_DOCUMENT) {
diff --git a/libcore/xml/src/main/java/org/apache/xml/dtm/DTMException.java b/libcore/xml/src/main/java/org/apache/xml/dtm/DTMException.java
index ebb3024..030dc1f 100644
--- a/libcore/xml/src/main/java/org/apache/xml/dtm/DTMException.java
+++ b/libcore/xml/src/main/java/org/apache/xml/dtm/DTMException.java
@@ -323,7 +323,7 @@
boolean isJdk14OrHigher = false;
try {
- Throwable.class.getMethod("getCause",null);
+ Throwable.class.getMethod("getCause", (Class<?>) null);
isJdk14OrHigher = true;
} catch (NoSuchMethodException nsme) {
// do nothing
@@ -357,12 +357,12 @@
try {
Method meth =
((Object) exception).getClass().getMethod("getException",
- null);
+ (Class<?>) null);
if (null != meth) {
Throwable prev = exception;
- exception = (Throwable) meth.invoke(exception, null);
+ exception = (Throwable) meth.invoke(exception, (Class<?>) null);
if (prev == exception) {
break;
diff --git a/libcore/xml/src/main/java/org/apache/xpath/XPathException.java b/libcore/xml/src/main/java/org/apache/xpath/XPathException.java
index b5e682a..315a5d6 100644
--- a/libcore/xml/src/main/java/org/apache/xpath/XPathException.java
+++ b/libcore/xml/src/main/java/org/apache/xpath/XPathException.java
@@ -267,7 +267,7 @@
boolean isJdk14OrHigher = false;
try {
- Throwable.class.getMethod("getCause",null);
+ Throwable.class.getMethod("getCause", (Class<?>) null);
isJdk14OrHigher = true;
} catch (NoSuchMethodException nsme) {
// do nothing
diff --git a/libcore/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/libcore/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 1944bdc..4721800 100644
--- a/libcore/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/libcore/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -471,7 +471,9 @@
}
/**
- * Returns the element or attribute local name, like "h1". Never empty.
+ * Returns the element or attribute local name, like "h1". Never empty. When
+ * namespace processing is disabled, this may contain a prefix, yielding a
+ * local name like "html:h1". In such cases, the qName will always be empty.
*/
jstring localName() {
return internString(mEnv, mParsingContext, mLocalName);
@@ -504,13 +506,17 @@
bool matchesQName(const char* qName) {
char* lastColon = strrchr(qName, ':');
- // if the input doesn't have a colon, there's no namespace prefix. Our
- // prefix must be empty and the qName must equal our localName
- if (lastColon == NULL) {
- return strlen(mPrefix) == 0 && strcmp(qName, mLocalName) == 0;
+ // Compare local names only if either:
+ // - the input qualified name doesn't have a colon (like "h1")
+ // - this element doesn't have a prefix. Such is the case when it
+ // doesn't belong to a namespace, or when this parser's namespace
+ // processing is disabled. In the latter case, this element's local
+ // name may still contain a colon (like "html:h1").
+ if (lastColon == NULL || *mPrefix == 0) {
+ return strcmp(qName, mLocalName) == 0;
}
- // otherwise the prefixes must be equal and our localName must equal qName
+ // Otherwise compare both prefix and local name
size_t prefixLength = lastColon - qName;
return strlen(mPrefix) == prefixLength
&& strncmp(qName, mPrefix, prefixLength) == 0
@@ -1004,7 +1010,7 @@
}
/**
- * Creates a new Expat parser. Called from the Java ExpatParser contructor.
+ * Creates a new Expat parser. Called from the Java ExpatParser constructor.
*
* @param object the Java ExpatParser instance
* @param javaEncoding the character encoding name
@@ -1208,11 +1214,6 @@
jint attributePointer, jint index) {
XML_Parser parser = (XML_Parser) pointer;
ParsingContext* context = (ParsingContext*) XML_GetUserData(parser);
-
- if (!context->processNamespaces) {
- return emptyString;
- }
-
return ExpatElementName(env, context, attributePointer, index).uri();
}
@@ -1229,11 +1230,6 @@
jint attributePointer, jint index) {
XML_Parser parser = (XML_Parser) pointer;
ParsingContext* context = (ParsingContext*) XML_GetUserData(parser);
-
- if (!context->processNamespaces) {
- return emptyString;
- }
-
return ExpatElementName(env, context, attributePointer, index).localName();
}
@@ -1250,11 +1246,6 @@
jint attributePointer, jint index) {
XML_Parser parser = (XML_Parser) pointer;
ParsingContext* context = (ParsingContext*) XML_GetUserData(parser);
-
- if (context->processNamespaces) {
- return emptyString;
- }
-
return ExpatElementName(env, context, attributePointer, index).qName();
}
diff --git a/libcore/xml/src/test/java/org/apache/harmony/xml/NamespacedAttributesLookupTest.java b/libcore/xml/src/test/java/org/apache/harmony/xml/NamespacedAttributesLookupTest.java
new file mode 100644
index 0000000..4f58262
--- /dev/null
+++ b/libcore/xml/src/test/java/org/apache/harmony/xml/NamespacedAttributesLookupTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xml;
+
+import junit.framework.TestCase;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.SAXParserFactory;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests that we both report and retrieve attributes using the appropriate
+ * names for different combinations of namespaces and namespace prefixes.
+ */
+public class NamespacedAttributesLookupTest extends TestCase {
+
+ private static final String SAX_PROPERTY_NS =
+ "http://xml.org/sax/features/namespaces";
+ private static final String SAX_PROPERTY_NS_PREFIXES =
+ "http://xml.org/sax/features/namespace-prefixes";
+
+ private static String xml = "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<test xmlns='http://foo' xmlns:bar='http://bar' xmlns:baz='http://baz' baz:c='a'>" +
+ "<b c='w' bar:c='x'/>" +
+ "<bar:e baz:c='y' bar:c='z'/>" +
+ "</test>";
+
+ public void testNamespace() throws Exception {
+ List<String> expected = Arrays.asList(
+ "http://foo,test\n" +
+ " http://baz,c\n" +
+ " http://bar+c=null,\n" +
+ " bar:c=null\n",
+
+ "http://foo,b\n" +
+ " ,c\n" +
+ " http://bar,c\n" +
+ " http://bar+c=x,\n" +
+ " bar:c=x\n",
+
+ "http://bar,e\n" +
+ " http://baz,c\n" +
+ " http://bar,c\n" +
+ " http://bar+c=z,\n" +
+ " bar:c=z\n");
+
+ boolean namespace = true;
+ boolean namespacePrefixes = false;
+ assertEquals(expected, getStartElements(xml, namespace, namespacePrefixes));
+ }
+
+ public void testNamespacePrefixes() throws Exception {
+ List<String> expected = Arrays.asList(
+ "test\n" +
+ " xmlns\n" +
+ " xmlns:bar\n" +
+ " xmlns:baz\n" +
+ " baz:c\n" +
+ " http://bar+c=null,\n" +
+ " bar:c=null\n",
+
+ "b\n" +
+ " c\n" +
+ " bar:c\n" +
+ " http://bar+c=null,\n" +
+ " bar:c=x\n",
+
+ "bar:e\n" +
+ " baz:c\n" +
+ " bar:c\n" +
+ " http://bar+c=null,\n" +
+ " bar:c=z\n");
+
+ boolean namespace = false;
+ boolean namespacePrefixes = true;
+ assertEquals(expected, getStartElements(xml, namespace, namespacePrefixes));
+ }
+
+ public List<String> getStartElements(String xml, final boolean namespace, boolean namespacePrefixes)
+ throws Exception {
+ final List<String> result = new ArrayList<String>();
+ XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+ reader.setFeature(SAX_PROPERTY_NS, namespace);
+ reader.setFeature(SAX_PROPERTY_NS_PREFIXES, namespacePrefixes);
+ reader.setContentHandler(new DefaultHandler() {
+ @Override public final void startElement(
+ String uri, String localName, String qName, Attributes attributes) {
+ StringBuilder serialized = new StringBuilder();
+ /*
+ * Only supply the uri+localName or qname depending on whether namespaces are
+ * enabled. It's an optional parameter and the RI only supplies one or the other.
+ */
+ if (namespace) {
+ serialized.append(uri).append(",");
+ serialized.append(localName);
+ } else {
+ serialized.append(qName);
+ }
+ for (int i = 0; i < attributes.getLength(); i++) {
+ serialized.append("\n ");
+ if (namespace) {
+ serialized.append(attributes.getURI(i)).append(",");
+ serialized.append(attributes.getLocalName(i));
+ } else {
+ serialized.append(attributes.getQName(i));
+ }
+ }
+ serialized.append("\n http://bar+c=")
+ .append(attributes.getValue("http://bar", "c")).append(",")
+ .append("\n bar:c=")
+ .append(attributes.getValue("bar:c"))
+ .append("\n");
+ result.add(serialized.toString());
+ }
+ });
+ reader.parse(new InputSource(new StringReader(xml)));
+ return result;
+ }
+}
diff --git a/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java b/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java
new file mode 100644
index 0000000..7773814
--- /dev/null
+++ b/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xml;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.xml.sax.SAXException;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * The <a href="http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=xslt">OASIS
+ * XSLT conformance test suite</a>, adapted for use by JUnit. To run these tests
+ * on a device:
+ * <ul>
+ * <li>Obtain the <a href="http://www.oasis-open.org/committees/download.php/12171/XSLT-testsuite-04.ZIP">test
+ * suite zip file from the OASIS project site.</li>
+ * <li>Unzip.
+ * <li>Copy the files to a device: <code>adb shell mkdir /data/oasis ;
+ * adb push ./XSLT-Conformance-TC /data/oasis</code>.
+ * <li>Invoke this class' main method, passing the on-device path to the test
+ * suite's <code>catalog.xml</code> file as an argument.
+ * </ul>
+ */
+public class XsltXPathConformanceTestSuite {
+
+ private static final String defaultCatalogFile
+ = "/home/dalvik-prebuild/OASIS/XSLT-Conformance-TC/TESTS/catalog.xml";
+
+ /** Orders element attributes by optional URI and name. */
+ private static final Comparator<Attr> orderByName = new Comparator<Attr>() {
+ public int compare(Attr a, Attr b) {
+ int result = compareNullsFirst(a.getBaseURI(), b.getBaseURI());
+ return result == 0 ? result
+ : compareNullsFirst(a.getName(), b.getName());
+ }
+
+ <T extends Comparable<T>> int compareNullsFirst(T a, T b) {
+ return (a == b) ? 0
+ : (a == null) ? -1
+ : (b == null) ? 1
+ : a.compareTo(b);
+ }
+ };
+
+ private final DocumentBuilder documentBuilder;
+ private final TransformerFactory transformerFactory;
+ private final XmlPullParserFactory xmlPullParserFactory;
+
+ public XsltXPathConformanceTestSuite()
+ throws ParserConfigurationException, XmlPullParserException {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setCoalescing(true);
+ documentBuilder = factory.newDocumentBuilder();
+
+ transformerFactory = TransformerFactory.newInstance();
+ xmlPullParserFactory = XmlPullParserFactory.newInstance();
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 1) {
+ System.out.println("Usage: XsltXPathConformanceTestSuite <catalog-xml>");
+ System.out.println();
+ System.out.println(" catalog-xml: an XML file describing an OASIS test suite");
+ System.out.println(" such as: " + defaultCatalogFile);
+ return;
+ }
+
+ File catalogXml = new File(args[0]);
+ TestRunner.run(suite(catalogXml));
+ }
+
+ public static Test suite() throws Exception {
+ return suite(new File(defaultCatalogFile));
+ }
+
+ /**
+ * Returns a JUnit test suite for the tests described by the given document.
+ */
+ public static Test suite(File catalogXml) throws Exception {
+ XsltXPathConformanceTestSuite suite = new XsltXPathConformanceTestSuite();
+
+ /*
+ * Extract the tests from an XML document with the following structure:
+ *
+ * <test-suite>
+ * <test-catalog submitter="Lotus">
+ * <creator>Lotus/IBM</creator>
+ * <major-path>Xalan_Conformance_Tests</major-path>
+ * <date>2001-11-16</date>
+ * <test-case ...> ... </test-case>
+ * <test-case ...> ... </test-case>
+ * <test-case ...> ... </test-case>
+ * </test-catalog>
+ * </test-suite>
+ */
+
+ Document document = DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder().parse(catalogXml);
+ Element testSuiteElement = document.getDocumentElement();
+ TestSuite result = new TestSuite();
+ for (Element testCatalog : elementsOf(testSuiteElement.getElementsByTagName("test-catalog"))) {
+ Element majorPathElement = (Element) testCatalog.getElementsByTagName("major-path").item(0);
+ String majorPath = majorPathElement.getTextContent();
+ File base = new File(catalogXml.getParentFile(), majorPath);
+
+ for (Element testCaseElement : elementsOf(testCatalog.getElementsByTagName("test-case"))) {
+ result.addTest(suite.create(base, testCaseElement));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a JUnit test for the test described by the given element.
+ */
+ private Test create(File base, Element testCaseElement) {
+
+ /*
+ * Extract the XSLT test from a DOM entity with the following structure:
+ *
+ * <test-case category="XSLT-Result-Tree" id="attribset_attribset01">
+ * <file-path>attribset</file-path>
+ * <creator>Paul Dick</creator>
+ * <date>2001-11-08</date>
+ * <purpose>Set attribute of a LRE from single attribute set.</purpose>
+ * <spec-citation place="7.1.4" type="section" version="1.0" spec="xslt"/>
+ * <scenario operation="standard">
+ * <input-file role="principal-data">attribset01.xml</input-file>
+ * <input-file role="principal-stylesheet">attribset01.xsl</input-file>
+ * <output-file role="principal" compare="XML">attribset01.out</output-file>
+ * </scenario>
+ * </test-case>
+ */
+
+ Element filePathElement = (Element) testCaseElement.getElementsByTagName("file-path").item(0);
+ Element purposeElement = (Element) testCaseElement.getElementsByTagName("purpose").item(0);
+ Element specCitationElement = (Element) testCaseElement.getElementsByTagName("spec-citation").item(0);
+ Element scenarioElement = (Element) testCaseElement.getElementsByTagName("scenario").item(0);
+
+ String category = testCaseElement.getAttribute("category");
+ String id = testCaseElement.getAttribute("id");
+ String name = category + "." + id;
+ String purpose = purposeElement != null ? purposeElement.getTextContent() : "";
+ String spec = "place=" + specCitationElement.getAttribute("place")
+ + " type" + specCitationElement.getAttribute("type")
+ + " version=" + specCitationElement.getAttribute("version")
+ + " spec=" + specCitationElement.getAttribute("spec");
+ String operation = scenarioElement.getAttribute("operation");
+
+ Element principalDataElement = null;
+ Element principalStylesheetElement = null;
+ Element principalElement = null;
+
+ for (Element element : elementsOf(scenarioElement.getChildNodes())) {
+ String role = element.getAttribute("role");
+ if (role.equals("principal-data")) {
+ principalDataElement = element;
+ } else if (role.equals("principal-stylesheet")) {
+ principalStylesheetElement = element;
+ } else if (role.equals("principal")) {
+ principalElement = element;
+ } else if (!role.equals("supplemental-stylesheet")
+ && !role.equals("supplemental-data")) {
+ return new MisspecifiedTest("Unexpected element at " + name);
+ }
+ }
+
+ String testDirectory = filePathElement.getTextContent();
+ File inBase = new File(base, testDirectory);
+ File outBase = new File(new File(base, "REF_OUT"), testDirectory);
+
+ if (principalDataElement == null || principalStylesheetElement == null) {
+ return new MisspecifiedTest("Expected <scenario> to have "
+ + "principal=data and principal-stylesheet elements at " + name);
+ }
+
+ try {
+ File principalData = findFile(inBase, principalDataElement.getTextContent());
+ File principalStylesheet = findFile(inBase, principalStylesheetElement.getTextContent());
+
+ final File principal;
+ final String compareAs;
+ if (!operation.equals("execution-error")) {
+ if (principalElement == null) {
+ return new MisspecifiedTest("Expected <scenario> to have principal element at " + name);
+ }
+
+ principal = findFile(outBase, principalElement.getTextContent());
+ compareAs = principalElement.getAttribute("compare");
+ } else {
+ principal = null;
+ compareAs = null;
+ }
+
+ return new XsltTest(category, id, purpose, spec, principalData,
+ principalStylesheet, principal, operation, compareAs);
+ } catch (FileNotFoundException e) {
+ return new MisspecifiedTest(e.getMessage() + " at " + name);
+ }
+ }
+
+ /**
+ * Finds the named file in the named directory. This tries extra hard to
+ * avoid case-insensitive-naming problems, where the requested file is
+ * available in a different casing.
+ */
+ private File findFile(File directory, String name) throws FileNotFoundException {
+ File file = new File(directory, name);
+ if (file.exists()) {
+ return file;
+ }
+
+ for (String child : directory.list()) {
+ if (child.equalsIgnoreCase(name)) {
+ return new File(directory, child);
+ }
+ }
+
+ throw new FileNotFoundException("Missing file: " + file);
+ }
+
+ /**
+ * Placeholder for a test that couldn't be configured to run properly.
+ */
+ public class MisspecifiedTest extends TestCase {
+ private final String message;
+
+ MisspecifiedTest(String message) {
+ super("test");
+ this.message = message;
+ }
+
+ public void test() {
+ fail(message);
+ }
+ }
+
+ /**
+ * Processes an input XML file with an input XSLT stylesheet and compares
+ * the result to an expected output file.
+ */
+ public class XsltTest extends TestCase {
+ // TODO: include these in toString
+ private final String category;
+ private final String id;
+ private final String purpose;
+ private final String spec;
+
+ private final File principalData;
+ private final File principalStylesheet;
+ private final File principal;
+
+ /** either "standard" or "execution-error" */
+ private final String operation;
+
+ /** the syntax to compare the output file using, such as "XML" or "HTML" */
+ private final String compareAs;
+
+ XsltTest(String category, String id, String purpose, String spec,
+ File principalData, File principalStylesheet, File principal,
+ String operation, String compareAs) {
+ super("test");
+ this.category = category;
+ this.id = id;
+ this.purpose = purpose;
+ this.spec = spec;
+ this.principalData = principalData;
+ this.principalStylesheet = principalStylesheet;
+ this.principal = principal;
+ this.operation = operation;
+ this.compareAs = compareAs;
+ }
+
+ public void test() throws Exception {
+ if (purpose != null) {
+ System.out.println("Purpose: " + purpose);
+ }
+ if (spec != null) {
+ System.out.println("Spec: " + spec);
+ }
+
+ Source xslt = new StreamSource(principalStylesheet);
+ Source in = new StreamSource(principalData);
+
+ Transformer transformer;
+ try {
+ transformer = transformerFactory.newTransformer(xslt);
+ assertEquals("Expected transformer creation to fail",
+ "standard", operation);
+ } catch (TransformerConfigurationException e) {
+ if (operation.equals("execution-error")) {
+ return; // expected, such as in XSLT-Result-Tree.Attributes__78369
+ }
+ AssertionFailedError failure = new AssertionFailedError();
+ failure.initCause(e);
+ throw failure;
+ }
+
+ Result result;
+ if (compareAs.equals("XML")) {
+ result = new DOMResult();
+ } else {
+ // TODO: implement support for comparing HTML etc.
+ throw new UnsupportedOperationException("Cannot compare as " + compareAs);
+ }
+
+ transformer.transform(in, result);
+
+ if (compareAs.equals("XML")) {
+ DOMResult domResult = (DOMResult) result;
+ assertNodesAreEquivalent(principal, domResult.getNode());
+ }
+ }
+
+ @Override public String getName() {
+ return category + "." + id;
+ }
+ }
+
+ /**
+ * Ensures both XML documents represent the same semantic data. Non-semantic
+ * data such as namespace prefixes, comments, and whitespace is ignored.
+ */
+ private void assertNodesAreEquivalent(File expected, Node actual)
+ throws ParserConfigurationException, IOException, SAXException,
+ XmlPullParserException {
+
+ Document expectedDocument = documentBuilder.parse(new FileInputStream(expected));
+ String expectedString = nodeToNormalizedString(expectedDocument);
+ String actualString = nodeToNormalizedString(actual);
+
+ Assert.assertEquals("Expected XML to match file " + expected,
+ expectedString, actualString);
+ }
+
+ private String nodeToNormalizedString(Node node)
+ throws XmlPullParserException, IOException {
+ StringWriter writer = new StringWriter();
+ XmlSerializer xmlSerializer = xmlPullParserFactory.newSerializer();
+ xmlSerializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ xmlSerializer.setOutput(writer);
+ emitNode(xmlSerializer, node);
+ xmlSerializer.flush();
+ return writer.toString();
+ }
+
+ private void emitNode(XmlSerializer serializer, Node node) throws IOException {
+ if (node instanceof Element) {
+ Element element = (Element) node;
+ serializer.startTag(element.getBaseURI(), element.getLocalName());
+ emitAttributes(serializer, element);
+ emitChildren(serializer, element);
+ serializer.endTag(element.getBaseURI(), element.getLocalName());
+
+ } else if (node instanceof Text) {
+ // TODO: is it okay to trim whitespace in general? This may cause
+ // false positives for elements like HTML's <pre> tag
+ String trimmed = node.getTextContent().trim();
+ if (trimmed.length() > 0) {
+ serializer.text(trimmed);
+ }
+
+ } else if (node instanceof Document) {
+ Document document = (Document) node;
+ serializer.startDocument("UTF-8", true);
+ emitNode(serializer, document.getDocumentElement());
+ serializer.endDocument();
+
+ } else if (node instanceof ProcessingInstruction) {
+ ProcessingInstruction processingInstruction = (ProcessingInstruction) node;
+ String data = processingInstruction.getData();
+ String target = processingInstruction.getTarget();
+ serializer.processingInstruction(target + " " + data);
+
+ } else if (node instanceof Comment) {
+ // ignore!
+
+ } else {
+ Object nodeClass = node != null ? node.getClass() : null;
+ throw new UnsupportedOperationException(
+ "Cannot serialize nodes of type " + nodeClass);
+ }
+ }
+
+ private void emitAttributes(XmlSerializer serializer, Node node)
+ throws IOException {
+ NamedNodeMap map = node.getAttributes();
+ if (map == null) {
+ return;
+ }
+
+ List<Attr> attributes = new ArrayList<Attr>();
+ for (int i = 0; i < map.getLength(); i++) {
+ attributes.add((Attr) map.item(i));
+ }
+ Collections.sort(attributes, orderByName);
+
+ for (Attr attr : attributes) {
+ if ("xmlns".equals(attr.getPrefix()) || "xmlns".equals(attr.getLocalName())) {
+ /*
+ * Omit namespace declarations because they aren't considered
+ * data. Ie. <foo:a xmlns:bar="http://google.com"> is semantically
+ * equal to <bar:a xmlns:bar="http://google.com"> since the
+ * prefix doesn't matter, only the URI it points to.
+ *
+ * When we omit the prefix, our XML serializer will still
+ * generate one for us, using a predictable pattern.
+ */
+ } else {
+ serializer.attribute(attr.getBaseURI(), attr.getLocalName(), attr.getValue());
+ }
+ }
+ }
+
+ private void emitChildren(XmlSerializer serializer, Node node)
+ throws IOException {
+ NodeList childNodes = node.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ emitNode(serializer, childNodes.item(i));
+ }
+ }
+
+ private static List<Element> elementsOf(NodeList nodeList) {
+ List<Element> result = new ArrayList<Element>();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node instanceof Element) {
+ result.add((Element) node);
+ }
+ }
+ return result;
+ }
+}
diff --git a/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java b/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java
index 292c2f1..02b6d80 100644
--- a/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java
+++ b/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java
@@ -16,6 +16,28 @@
package tests.api.javax.xml.parsers;
+import dalvik.annotation.KnownFailure;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import junit.framework.TestCase;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.Text;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import tests.api.org.xml.sax.support.MethodLogger;
+import tests.api.org.xml.sax.support.MockHandler;
+import tests.api.org.xml.sax.support.MockResolver;
+import tests.util.TestEnvironment;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -23,33 +45,6 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.logging.Logger;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import junit.framework.TestCase;
-
-import org.w3c.dom.DOMImplementation;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.EntityReference;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Text;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-
-import tests.api.org.xml.sax.support.MethodLogger;
-import tests.api.org.xml.sax.support.MockHandler;
-import tests.api.org.xml.sax.support.MockResolver;
-import dalvik.annotation.KnownFailure;
-import dalvik.annotation.TestLevel;
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargetNew;
@TestTargetClass(DocumentBuilder.class)
public class DocumentBuilderTest extends TestCase {
@@ -133,6 +128,8 @@
DocumentBuilder db;
protected void setUp() throws Exception {
+ TestEnvironment.reset();
+
dbf = DocumentBuilderFactory.newInstance();
dbf.setIgnoringElementContentWhitespace(true);
@@ -266,6 +263,21 @@
}
/**
+ * Tests that the Base URI for the document is populated with the file URI.
+ */
+ @TestTargetNew(
+ level = TestLevel.COMPLETE,
+ notes = "",
+ method = "parse",
+ args = {java.io.File.class}
+ )
+ public void testGetBaseURI() throws IOException, SAXException {
+ File f = resourceToTmpFile("/simple.xml");
+ Document d = db.parse(f);
+ assertTrue(d.getDocumentElement().getBaseURI().startsWith("file://"));
+ }
+
+ /**
* @tests javax.xml.parsers.DocumentBuilder#parse(java.io.File)
* Case 1: Try to parse correct xml document.
* Case 2: Try to call parse() with null argument.
@@ -567,7 +579,6 @@
method = "parse",
args = {java.lang.String.class}
)
- @KnownFailure("Android DocumentBuilder should support File sources")
public void test_parseLjava_lang_String() {
// case 1: Trivial use.
File f = new File(getClass().getResource("/simple.xml").getFile());
diff --git a/libcore/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTest.java b/libcore/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTest.java
index 2b8bb5c..ca7cf71 100644
--- a/libcore/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTest.java
+++ b/libcore/xml/src/test/java/tests/api/javax/xml/parsers/SAXParserTest.java
@@ -347,8 +347,6 @@
method = "parse",
args = {java.io.File.class, org.xml.sax.helpers.DefaultHandler.class}
)
- @KnownFailure("The default handler doesn't get the qName value supplied. " +
- "We either need to change the test, or fix the parser.")
public void test_parseLjava_io_FileLorg_xml_sax_helpers_DefaultHandler()
throws Exception {
@@ -456,8 +454,6 @@
method = "parse",
args = {org.xml.sax.InputSource.class, org.xml.sax.helpers.DefaultHandler.class}
)
- @KnownFailure("The default handler doesn't get the qName value supplied. " +
- "We either need to change the test, or fix the parser.")
public void test_parseLorg_xml_sax_InputSourceLorg_xml_sax_helpers_DefaultHandler()
throws Exception {
@@ -623,8 +619,6 @@
method = "parse",
args = {java.io.InputStream.class, org.xml.sax.helpers.DefaultHandler.class}
)
- @KnownFailure("The default handler doesn't get the qName value supplied. " +
- "We either need to change the test, or fix the parser.")
public void test_parseLjava_io_InputStreamLorg_xml_sax_helpers_DefaultHandler()
throws Exception {
@@ -675,8 +669,6 @@
method = "parse",
args = {java.io.InputStream.class, org.xml.sax.helpers.DefaultHandler.class, java.lang.String.class}
)
- @KnownFailure("The default handler doesn't get the qName value supplied. " +
- "We either need to change the test, or fix the parser.")
public void test_parseLjava_io_InputStreamLorg_xml_sax_helpers_DefaultHandlerLjava_lang_String() {
for(int i = 0; i < list_wf.length; i++) {
try {
@@ -952,8 +944,6 @@
method = "parse",
args = {java.lang.String.class, org.xml.sax.helpers.DefaultHandler.class}
)
- @KnownFailure("The default handler doesn't get the qName value supplied. " +
- "We either need to change the test, or fix the parser.")
public void test_parseLjava_lang_StringLorg_xml_sax_helpers_DefaultHandler()
throws Exception {
@@ -1174,4 +1164,3 @@
}
}
-
diff --git a/vm/AllocTracker.c b/vm/AllocTracker.c
index 9649e68..9fb1c4d 100644
--- a/vm/AllocTracker.c
+++ b/vm/AllocTracker.c
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Allocation tracking and reporting. We maintain a circular buffer with
* the most recent allocations. The data can be viewed through DDMS.
@@ -37,10 +38,12 @@
*
* TODO: consider making the parameters configurable, so DDMS can decide
* how many allocations it wants to see and what the stack depth should be.
+ * Changing the window size is easy, changing the max stack depth is harder
+ * because we go from an array of fixed-size structs to variable-sized data.
*/
#include "Dalvik.h"
-#define kMaxAllocRecordStackDepth 8 /* max 255 */
+#define kMaxAllocRecordStackDepth 16 /* max 255 */
#define kNumAllocRecords 512 /* MUST be power of 2 */
/*
@@ -108,8 +111,9 @@
dvmLockMutex(&gDvm.allocTrackerLock);
if (gDvm.allocRecords == NULL) {
- LOGI("Enabling alloc tracker (%d entries / %d bytes)\n",
- kNumAllocRecords, sizeof(AllocRecord) * kNumAllocRecords);
+ LOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)\n",
+ kNumAllocRecords, kMaxAllocRecordStackDepth,
+ sizeof(AllocRecord) * kNumAllocRecords);
gDvm.allocRecordHead = gDvm.allocRecordCount = 0;
gDvm.allocRecords =
(AllocRecord*) malloc(sizeof(AllocRecord) * kNumAllocRecords);
diff --git a/vm/Debugger.c b/vm/Debugger.c
index be6fa66..2f57046 100644
--- a/vm/Debugger.c
+++ b/vm/Debugger.c
@@ -418,6 +418,9 @@
LOGI("Debugger is active\n");
dvmInitBreakpoints();
gDvm.debuggerActive = true;
+#if defined(WITH_JIT)
+ dvmCompilerStateRefresh();
+#endif
}
/*
@@ -445,6 +448,9 @@
dvmHashTableClear(gDvm.dbgRegistry);
dvmHashTableUnlock(gDvm.dbgRegistry);
+#if defined(WITH_JIT)
+ dvmCompilerStateRefresh();
+#endif
}
/*
@@ -3045,4 +3051,3 @@
dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
}
-
diff --git a/vm/Globals.h b/vm/Globals.h
index bac28f6..f2dd86a 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -80,6 +80,7 @@
bool verboseGc;
bool verboseJni;
bool verboseClass;
+ bool verboseShutdown;
bool jdwpAllowed; // debugging allowed for this process?
bool jdwpConfigured; // has debugging info been provided?
@@ -685,6 +686,9 @@
/* Array of profile threshold counters */
unsigned char *pProfTable;
+ /* Copy of pProfTable used for temporarily disabling the Jit */
+ unsigned char *pProfTableCopy;
+
/* Size of JIT hash table in entries. Must be a power of 2 */
unsigned int jitTableSize;
diff --git a/vm/Init.c b/vm/Init.c
index c65ce65..5d49986 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -795,6 +795,8 @@
gDvm.verboseJni = true;
} else if (strcmp(argv[i], "-verbose:gc") == 0) {
gDvm.verboseGc = true;
+ } else if (strcmp(argv[i], "-verbose:shutdown") == 0) {
+ gDvm.verboseShutdown = true;
} else if (strncmp(argv[i], "-enableassertions", 17) == 0) {
enableAssertions(argv[i] + 17, true);
@@ -954,7 +956,7 @@
dvmFprintf(stderr, "Bad value for -Xgc");
return -1;
}
- LOGD("Precise GC configured %s\n", gDvm.preciseGc ? "ON" : "OFF");
+ LOGV("Precise GC configured %s\n", gDvm.preciseGc ? "ON" : "OFF");
} else if (strcmp(argv[i], "-Xcheckdexsum") == 0) {
gDvm.verifyDexChecksum = true;
@@ -1577,7 +1579,8 @@
*/
dvmSlayDaemons();
- LOGD("VM cleaning up\n");
+ if (gDvm.verboseShutdown)
+ LOGD("VM cleaning up\n");
dvmDebuggerShutdown();
dvmReflectShutdown();
diff --git a/vm/Jni.c b/vm/Jni.c
index 7b68195..6692f3b 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -3874,7 +3874,8 @@
if (ext == NULL)
return JNI_ERR;
- LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
+ if (gDvm.verboseShutdown)
+ LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
/*
* Sleep on a condition variable until it's okay to exit.
@@ -3906,7 +3907,8 @@
// TODO: call System.exit() to run any registered shutdown hooks
// (this may not return -- figure out how this should work)
- LOGD("DestroyJavaVM shutting VM down\n");
+ if (gDvm.verboseShutdown)
+ LOGD("DestroyJavaVM shutting VM down\n");
dvmShutdown();
// TODO - free resources associated with JNI-attached daemon threads
diff --git a/vm/LinearAlloc.c b/vm/LinearAlloc.c
index a8ed3ab..bf89d50 100644
--- a/vm/LinearAlloc.c
+++ b/vm/LinearAlloc.c
@@ -236,10 +236,12 @@
//dvmLinearAllocDump(classLoader);
- LOGV("Unmapping linear allocator base=%p\n", pHdr->mapAddr);
- LOGD("LinearAlloc %p used %d of %d (%d%%)\n",
- classLoader, pHdr->curOffset, pHdr->mapLength,
- (pHdr->curOffset * 100) / pHdr->mapLength);
+ if (gDvm.verboseShutdown) {
+ LOGV("Unmapping linear allocator base=%p\n", pHdr->mapAddr);
+ LOGD("LinearAlloc %p used %d of %d (%d%%)\n",
+ classLoader, pHdr->curOffset, pHdr->mapLength,
+ (pHdr->curOffset * 100) / pHdr->mapLength);
+ }
if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
LOGW("LinearAlloc munmap(%p, %d) failed: %s\n",
diff --git a/vm/Profile.c b/vm/Profile.c
index dcfad71..b079988 100644
--- a/vm/Profile.c
+++ b/vm/Profile.c
@@ -230,6 +230,9 @@
} while (!ATOMIC_CMP_SWAP(&gDvm.activeProfilers, oldValue, newValue));
LOGD("+++ active profiler count now %d\n", newValue);
+#if defined(WITH_JIT)
+ dvmCompilerStateRefresh();
+#endif
}
diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c
index 90211fd..7edbf38 100644
--- a/vm/SignalCatcher.c
+++ b/vm/SignalCatcher.c
@@ -255,7 +255,7 @@
} else if (rcvd == SIGUSR1) {
#if WITH_HPROF
LOGI("SIGUSR1 forcing GC and HPROF dump\n");
- hprofDumpHeap(NULL);
+ hprofDumpHeap(NULL, false);
#else
LOGI("SIGUSR1 forcing GC (no HPROF)\n");
dvmCollectGarbage(false);
diff --git a/vm/Sync.c b/vm/Sync.c
index acd916c..5340d86 100644
--- a/vm/Sync.c
+++ b/vm/Sync.c
@@ -504,7 +504,7 @@
/*
* Converts the given relative waiting time into an absolute time.
*/
-static void absoluteTime(s8 msec, s4 nsec, struct timespec *ts)
+void dvmAbsoluteTime(s8 msec, s4 nsec, struct timespec *ts)
{
s8 endSec;
@@ -589,7 +589,7 @@
if (msec == 0 && nsec == 0) {
timed = false;
} else {
- absoluteTime(msec, nsec, &ts);
+ dvmAbsoluteTime(msec, nsec, &ts);
timed = true;
}
@@ -1987,4 +1987,3 @@
}
#endif /*WITH_DEADLOCK_PREDICTION*/
-
diff --git a/vm/Sync.h b/vm/Sync.h
index 0ce3ebc..0832608 100644
--- a/vm/Sync.h
+++ b/vm/Sync.h
@@ -146,6 +146,11 @@
bool dvmHoldsLock(struct Thread* thread, struct Object* obj);
/*
+ * Converts the given relative time into an absolute time
+ */
+void dvmAbsoluteTime(s8 msec, s4 nsec, struct timespec *ts);
+
+/*
* Debug.
*/
void dvmDumpMonitorInfo(const char* msg);
diff --git a/vm/Thread.c b/vm/Thread.c
index 7505ec5..9e95d88 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -1285,8 +1285,6 @@
ClassObject* nativeStart;
Method* runMeth;
- assert(thread->threadId != 1); // not for main thread
-
nativeStart =
dvmFindSystemClassNoInit("Ldalvik/system/NativeStart;");
if (nativeStart == NULL) {
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index bec30f3..4774819 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -856,7 +856,8 @@
(int) time(NULL), (int) getpid());
gcHeap->hprofFileName = nameBuf;
}
- gcHeap->hprofContext = hprofStartup(gcHeap->hprofFileName);
+ gcHeap->hprofContext = hprofStartup(gcHeap->hprofFileName,
+ gcHeap->hprofDirectToDdms);
if (gcHeap->hprofContext != NULL) {
hprofStartHeapDump(gcHeap->hprofContext);
}
@@ -1071,7 +1072,7 @@
*
* Returns 0 on success, or an error code on failure.
*/
-int hprofDumpHeap(const char* fileName)
+int hprofDumpHeap(const char* fileName, bool directToDdms)
{
int result;
@@ -1079,6 +1080,7 @@
gDvm.gcHeap->hprofDumpOnGc = true;
gDvm.gcHeap->hprofFileName = fileName;
+ gDvm.gcHeap->hprofDirectToDdms = directToDdms;
dvmCollectGarbageInternal(false, GC_HPROF_DUMP_HEAP);
result = gDvm.gcHeap->hprofResult;
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index a2d31fe..9a5071f 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -170,6 +170,7 @@
const char* hprofFileName;
hprof_context_t *hprofContext;
int hprofResult;
+ bool hprofDirectToDdms;
#endif
};
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
index 3e0f669..228be5a 100644
--- a/vm/alloc/HeapWorker.c
+++ b/vm/alloc/HeapWorker.c
@@ -104,7 +104,7 @@
*/
if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0)
LOGW("HeapWorker thread join failed\n");
- else
+ else if (gDvm.verboseShutdown)
LOGD("HeapWorker thread has shut down\n");
gDvm.heapWorkerReady = false;
@@ -373,7 +373,8 @@
}
dvmUnlockMutex(&gDvm.heapWorkerLock);
- LOGD("HeapWorker thread shutting down\n");
+ if (gDvm.verboseShutdown)
+ LOGD("HeapWorker thread shutting down\n");
return NULL;
}
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c
index ceb1da1..9ed3a05 100644
--- a/vm/compiler/Compiler.c
+++ b/vm/compiler/Compiler.c
@@ -295,20 +295,8 @@
goto fail;
}
- dvmInitMutex(&gDvmJit.compilerLock);
- pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
- pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
-
dvmLockMutex(&gDvmJit.compilerLock);
- gDvmJit.haltCompilerThread = false;
-
- /* Reset the work queue */
- memset(gDvmJit.compilerWorkQueue, 0,
- sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
- gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
- gDvmJit.compilerQueueLength = 0;
-
/* Track method-level compilation statistics */
gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
@@ -356,6 +344,7 @@
gDvmJit.compilerHighWater =
COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
gDvmJit.pProfTable = pJitProfTable;
+ gDvmJit.pProfTableCopy = pJitProfTable;
dvmUnlockMutex(&gDvmJit.tableLock);
/* Signal running threads to refresh their cached pJitTable pointers */
@@ -370,13 +359,39 @@
static void *compilerThreadStart(void *arg)
{
+ int ret;
+ struct timespec ts;
+
dvmChangeStatus(NULL, THREAD_VMWAIT);
/*
* Wait a little before recieving translation requests on the assumption
* that process start-up code isn't worth compiling.
*/
- usleep(1 * 1000 * 1000);
+
+ dvmLockMutex(&gDvmJit.compilerLock);
+ /*
+ * TUNING: once framework is calling VMRuntime.startJitCompilation,
+ * experiment with the delay time (and perhaps have target-dependent
+ * values?
+ */
+ dvmAbsoluteTime(1000, 0, &ts);
+#if defined(HAVE_TIMEDWAIT_MONOTONIC)
+ ret = pthread_cond_timedwait_monotonic(&gDvmJit.compilerQueueActivity,
+ &gDvmJit.compilerLock, &ts);
+#else
+ ret = pthread_cond_timedwait(&gDvmJit.compilerQueueActivity,
+ &gDvmJit.compilerLock, &ts);
+#endif
+ assert(ret == 0 || ret == ETIMEDOUT);
+
+ if (gDvmJit.haltCompilerThread) {
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+ return NULL;
+ }
+
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
compilerThreadStartup();
dvmLockMutex(&gDvmJit.compilerLock);
@@ -453,14 +468,26 @@
*/
dvmChangeStatus(NULL, THREAD_RUNNING);
- LOGD("Compiler thread shutting down\n");
+ if (gDvm.verboseShutdown)
+ LOGD("Compiler thread shutting down\n");
return NULL;
}
bool dvmCompilerStartup(void)
{
+
+ dvmInitMutex(&gDvmJit.compilerLock);
+ dvmLockMutex(&gDvmJit.compilerLock);
+ pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
+ pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
+
+ /* Reset the work queue */
+ gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
+ gDvmJit.compilerQueueLength = 0;
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+
/*
- * Defer initialization until we're sure JIT'ng makes sense. Launch
+ * Defer rest of initialization until we're sure JIT'ng makes sense. Launch
* the compiler thread, which will do the real initialization if and
* when it is signalled to do so.
*/
@@ -482,7 +509,27 @@
if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0)
LOGW("Compiler thread join failed\n");
- else
+ else if (gDvm.verboseShutdown)
LOGD("Compiler thread has shut down\n");
}
}
+
+
+void dvmCompilerStateRefresh()
+{
+ bool jitActive;
+ bool jitActivate;
+
+ dvmLockMutex(&gDvmJit.tableLock);
+ jitActive = gDvmJit.pProfTable != NULL;
+ jitActivate = !(gDvm.debuggerActive || (gDvm.activeProfilers > 0));
+
+ if (jitActivate && !jitActive) {
+ gDvmJit.pProfTable = gDvmJit.pProfTableCopy;
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ } else if (!jitActivate && jitActive) {
+ gDvmJit.pProfTable = NULL;
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ dvmJitUnchainAll();
+ }
+}
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
index 71eed5d..6b4d414 100644
--- a/vm/compiler/Compiler.h
+++ b/vm/compiler/Compiler.h
@@ -165,6 +165,7 @@
struct SSARepresentation *ssaRep);
void dvmCompilerDataFlowAnalysisDispatcher(struct CompilationUnit *cUnit,
void (*func)(struct CompilationUnit *, struct BasicBlock *));
+void dvmCompilerStateRefresh(void);
JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc);
#endif /* _DALVIK_VM_COMPILER */
diff --git a/vm/compiler/codegen/arm/Assemble.c b/vm/compiler/codegen/arm/Assemble.c
index 6a59c7e..998c955 100644
--- a/vm/compiler/codegen/arm/Assemble.c
+++ b/vm/compiler/codegen/arm/Assemble.c
@@ -1328,7 +1328,7 @@
u4 newInst;
bool thumbTarget;
- if (gDvm.sumThreadSuspendCount == 0) {
+ if ((gDvmJit.pProfTable != NULL) && gDvm.sumThreadSuspendCount == 0) {
assert((branchOffset >= -(1<<22)) && (branchOffset <= ((1<<22)-2)));
gDvmJit.translationChains++;
diff --git a/vm/hprof/Hprof.c b/vm/hprof/Hprof.c
index 2e6f7c9..8380fd8 100644
--- a/vm/hprof/Hprof.c
+++ b/vm/hprof/Hprof.c
@@ -33,15 +33,13 @@
#define kHeadSuffix "-hptemp"
hprof_context_t *
-hprofStartup(const char *outputFileName)
+hprofStartup(const char *outputFileName, bool directToDdms)
{
- hprof_context_t *ctx;
+ FILE* fp = NULL;
- ctx = malloc(sizeof(*ctx));
- if (ctx != NULL) {
+ if (!directToDdms) {
int len = strlen(outputFileName);
char fileName[len + sizeof(kHeadSuffix)];
- FILE *fp;
/* Construct the temp file name. This wasn't handed to us by the
* application, so we need to be careful about stomping on it.
@@ -49,14 +47,12 @@
sprintf(fileName, "%s" kHeadSuffix, outputFileName);
if (access(fileName, F_OK) == 0) {
LOGE("hprof: temp file %s exists, bailing\n", fileName);
- free(ctx);
return NULL;
}
fp = fopen(fileName, "w+");
if (fp == NULL) {
LOGE("hprof: can't open %s: %s.\n", fileName, strerror(errno));
- free(ctx);
return NULL;
}
if (unlink(fileName) != 0) {
@@ -64,20 +60,28 @@
/* keep going */
}
LOGI("hprof: dumping VM heap to \"%s\".\n", fileName);
+ }
- hprofStartup_String();
- hprofStartup_Class();
+ hprofStartup_String();
+ hprofStartup_Class();
#if WITH_HPROF_STACK
- hprofStartup_StackFrame();
- hprofStartup_Stack();
+ hprofStartup_StackFrame();
+ hprofStartup_Stack();
#endif
- /* pass in "fp" for the temp file, and the name of the output file */
- hprofContextInit(ctx, strdup(outputFileName), fp, false);
- } else {
+ hprof_context_t *ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
LOGE("hprof: can't allocate context.\n");
+ if (fp != NULL)
+ fclose(fp);
+ return NULL;
}
+ /* pass in "fp" for the temp file, and the name of the output file */
+ hprofContextInit(ctx, strdup(outputFileName), fp, false, directToDdms);
+
+ assert(ctx->fp != NULL);
+
return ctx;
}
@@ -115,46 +119,55 @@
* Finish up the hprof dump. Returns true on success.
*/
bool
-hprofShutdown(hprof_context_t *ctx)
+hprofShutdown(hprof_context_t *tailCtx)
{
- FILE *tempFp = ctx->fp;
- FILE *fp;
+ FILE *fp = NULL;
/* flush output to the temp file, then prepare the output file */
- hprofFlushCurrentRecord(ctx);
- free(ctx->curRec.body);
- ctx->curRec.body = NULL;
- ctx->curRec.allocLen = 0;
- ctx->fp = NULL;
+ hprofFlushCurrentRecord(tailCtx);
- LOGI("hprof: dumping heap strings to \"%s\".\n", ctx->fileName);
- fp = fopen(ctx->fileName, "w");
- if (fp == NULL) {
- LOGE("can't open %s: %s\n", ctx->fileName, strerror(errno));
- fclose(tempFp);
- free(ctx->fileName);
- free(ctx);
- return false;
+ LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName);
+ if (!tailCtx->directToDdms) {
+ fp = fopen(tailCtx->fileName, "w");
+ if (fp == NULL) {
+ LOGE("can't open %s: %s\n", tailCtx->fileName, strerror(errno));
+ hprofFreeContext(tailCtx);
+ return false;
+ }
}
- hprofContextInit(ctx, ctx->fileName, fp, true);
- hprofDumpStrings(ctx);
- hprofDumpClasses(ctx);
+ /*
+ * Create a new context struct for the start of the file. We
+ * heap-allocate it so we can share the "free" function.
+ */
+ hprof_context_t *headCtx = malloc(sizeof(*headCtx));
+ if (headCtx == NULL) {
+ LOGE("hprof: can't allocate context.\n");
+ if (fp != NULL)
+ fclose(fp);
+ hprofFreeContext(tailCtx);
+ return NULL;
+ }
+ hprofContextInit(headCtx, strdup(tailCtx->fileName), fp, true,
+ tailCtx->directToDdms);
+
+ hprofDumpStrings(headCtx);
+ hprofDumpClasses(headCtx);
/* Write a dummy stack trace record so the analysis
* tools don't freak out.
*/
- hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
- hprofAddU4ToRecord(&ctx->curRec, HPROF_NULL_STACK_TRACE);
- hprofAddU4ToRecord(&ctx->curRec, HPROF_NULL_THREAD);
- hprofAddU4ToRecord(&ctx->curRec, 0); // no frames
+ hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
+ hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
+ hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
+ hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames
#if WITH_HPROF_STACK
- hprofDumpStackFrames(ctx);
- hprofDumpStacks(ctx);
+ hprofDumpStackFrames(headCtx);
+ hprofDumpStacks(headCtx);
#endif
- hprofFlushCurrentRecord(ctx);
+ hprofFlushCurrentRecord(headCtx);
hprofShutdown_Class();
hprofShutdown_String();
@@ -163,24 +176,52 @@
hprofShutdown_StackFrame();
#endif
- /*
- * Append the contents of the temp file to the output file. The temp
- * file was removed immediately after being opened, so it will vanish
- * when we close it.
- */
- rewind(tempFp);
- if (!copyFileToFile(ctx->fp, tempFp)) {
- LOGW("hprof: file copy failed, hprof data may be incomplete\n");
- /* finish up anyway */
+ if (tailCtx->directToDdms) {
+ /* flush to ensure memstream pointer and size are updated */
+ fflush(headCtx->fp);
+ fflush(tailCtx->fp);
+
+ /* send the data off to DDMS */
+ struct iovec iov[2];
+ iov[0].iov_base = headCtx->fileDataPtr;
+ iov[0].iov_len = headCtx->fileDataSize;
+ iov[1].iov_base = tailCtx->fileDataPtr;
+ iov[1].iov_len = tailCtx->fileDataSize;
+ dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
+ } else {
+ /*
+ * Append the contents of the temp file to the output file. The temp
+ * file was removed immediately after being opened, so it will vanish
+ * when we close it.
+ */
+ rewind(tailCtx->fp);
+ if (!copyFileToFile(headCtx->fp, tailCtx->fp)) {
+ LOGW("hprof: file copy failed, hprof data may be incomplete\n");
+ /* finish up anyway */
+ }
}
- fclose(tempFp);
- fclose(ctx->fp);
- free(ctx->fileName);
- free(ctx->curRec.body);
- free(ctx);
+ hprofFreeContext(headCtx);
+ hprofFreeContext(tailCtx);
/* throw out a log message for the benefit of "runhat" */
LOGI("hprof: heap dump completed, temp file removed\n");
return true;
}
+
+/*
+ * Free any heap-allocated items in "ctx", and then free "ctx" itself.
+ */
+void
+hprofFreeContext(hprof_context_t *ctx)
+{
+ assert(ctx != NULL);
+
+ if (ctx->fp != NULL)
+ fclose(ctx->fp);
+ free(ctx->curRec.body);
+ free(ctx->fileName);
+ free(ctx->fileDataPtr);
+ free(ctx);
+}
+
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
index 696b0a7..db5049f 100644
--- a/vm/hprof/Hprof.h
+++ b/vm/hprof/Hprof.h
@@ -125,13 +125,23 @@
* can cast from a context to a record.
*/
hprof_record_t curRec;
- char *fileName;
- FILE *fp;
+
u4 gcThreadSerialNumber;
u1 gcScanState;
HprofHeapId currentHeap; // which heap we're currently emitting
u4 stackTraceSerialNumber;
size_t objectsInSegment;
+
+ /*
+ * If "directToDdms" is not set, "fileName" is valid, and "fileDataPtr"
+ * and "fileDataSize" are not used. If "directToDdms" is not set,
+ * it's the other way around.
+ */
+ bool directToDdms;
+ char *fileName;
+ char *fileDataPtr; // for open_memstream
+ size_t fileDataSize; // for open_memstream
+ FILE *fp;
} hprof_context_t;
@@ -178,7 +188,7 @@
*/
void hprofContextInit(hprof_context_t *ctx, char *fileName, FILE *fp,
- bool writeHeader);
+ bool writeHeader, bool directToDdms);
int hprofFlushRecord(hprof_record_t *rec, FILE *fp);
int hprofFlushCurrentRecord(hprof_context_t *ctx);
@@ -234,8 +244,9 @@
* Hprof.c functions
*/
-hprof_context_t *hprofStartup(const char *outputFileName);
+hprof_context_t* hprofStartup(const char *outputFileName, bool directToDdms);
bool hprofShutdown(hprof_context_t *ctx);
+void hprofFreeContext(hprof_context_t *ctx);
/*
* Heap.c functions
@@ -244,7 +255,7 @@
* the heap implementation; these functions require heap knowledge,
* so they are implemented in Heap.c.
*/
-int hprofDumpHeap(const char* fileName);
+int hprofDumpHeap(const char* fileName, bool directToDdms);
void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber);
#endif // _DALVIK_HPROF_HPROF
diff --git a/vm/hprof/HprofOutput.c b/vm/hprof/HprofOutput.c
index c6d1cbc..0677c85 100644
--- a/vm/hprof/HprofOutput.c
+++ b/vm/hprof/HprofOutput.c
@@ -14,7 +14,9 @@
* limitations under the License.
*/
#include <sys/time.h>
+#include <cutils/open_memstream.h>
#include <time.h>
+#include <errno.h>
#include "Hprof.h"
#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
@@ -54,11 +56,33 @@
buf_[offset_ + 7] = (unsigned char)(value_ ); \
} while (0)
+/*
+ * Initialize an hprof context struct.
+ *
+ * This will take ownership of "fileName" and "fp".
+ */
void
hprofContextInit(hprof_context_t *ctx, char *fileName, FILE *fp,
- bool writeHeader)
+ bool writeHeader, bool directToDdms)
{
memset(ctx, 0, sizeof (*ctx));
+
+ if (directToDdms) {
+ /*
+ * Have to do this here, because it must happen after we
+ * memset the struct (want to treat fileDataPtr/fileDataSize
+ * as read-only while the file is open).
+ */
+ assert(fp == NULL);
+ fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
+ if (fp == NULL) {
+ /* not expected */
+ LOGE("hprof: open_memstream failed: %s\n", strerror(errno));
+ dvmAbort();
+ }
+ }
+
+ ctx->directToDdms = directToDdms;
ctx->fileName = fileName;
ctx->fp = fp;
diff --git a/vm/interp/Jit.c b/vm/interp/Jit.c
index 34a7736..d536070 100644
--- a/vm/interp/Jit.c
+++ b/vm/interp/Jit.c
@@ -457,7 +457,8 @@
dvmCompilerShutdown();
- dvmCompilerDumpStats();
+ if (gDvm.verboseShutdown)
+ dvmCompilerDumpStats();
dvmDestroyMutex(&gDvmJit.tableLock);
@@ -776,6 +777,7 @@
interpState->entryPoint = kInterpEntryResume;
switchInterp = !debugOrProfile;
break;
+ case kJitTSelectRequest:
case kJitTSelectAbort:
#if defined(SHOW_TRACE)
LOGD("TraceGen: trace abort");
@@ -833,7 +835,8 @@
void* dvmJitGetCodeAddr(const u2* dPC)
{
int idx = dvmJitHash(dPC);
- const u2* npc = gDvmJit.pJitEntryTable[idx].dPC;
+ const u2* npc = (gDvmJit.pProfTable == NULL) ? NULL :
+ gDvmJit.pJitEntryTable[idx].dPC;
if (npc != NULL) {
if (npc == dPC) {
@@ -1130,5 +1133,4 @@
return (s8)f;
}
-
#endif /* WITH_JIT */
diff --git a/vm/jdwp/JdwpAdb.c b/vm/jdwp/JdwpAdb.c
index 6ec9f10..dfc7bd7 100644
--- a/vm/jdwp/JdwpAdb.c
+++ b/vm/jdwp/JdwpAdb.c
@@ -174,7 +174,7 @@
LOGW("receiving file descriptor from ADB failed (socket %d): %s\n",
netState->controlSock, strerror(errno));
} else {
- LOGI("adbd disconnected\n");
+ LOGD("adbd disconnected\n");
}
close(netState->controlSock);
netState->controlSock = -1;
diff --git a/vm/jdwp/JdwpMain.c b/vm/jdwp/JdwpMain.c
index 4166c67..540259d 100644
--- a/vm/jdwp/JdwpMain.c
+++ b/vm/jdwp/JdwpMain.c
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* JDWP initialization.
*/
@@ -185,7 +186,8 @@
/*
* Close down the network to inspire the thread to halt.
*/
- LOGD("JDWP shutting down net...\n");
+ if (gDvm.verboseShutdown)
+ LOGD("JDWP shutting down net...\n");
dvmJdwpNetShutdown(state);
if (state->debugThreadStarted) {
@@ -195,7 +197,8 @@
}
}
- LOGV("JDWP freeing netstate...\n");
+ if (gDvm.verboseShutdown)
+ LOGD("JDWP freeing netstate...\n");
dvmJdwpNetFree(state);
state->netState = NULL;
}
diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c
index 5b984e3..5abdff5 100644
--- a/vm/mterp/out/InterpC-portdbg.c
+++ b/vm/mterp/out/InterpC-portdbg.c
@@ -1477,25 +1477,21 @@
interpState->pc,
interpState->method->name);
#endif
-
#if INTERP_TYPE == INTERP_DBG
- /* Check to see if we've got a trace selection request. If we do,
- * but something is amiss, revert to the fast interpreter.
- */
-#if !defined(WITH_SELF_VERIFICATION)
- if (dvmJitCheckTraceRequest(self,interpState)) {
+ /* Check to see if we've got a trace selection request. */
+ if (
+#if defined(WITH_SELF_VERIFICATION)
+ (interpState->jitState != kJitSelfVerification) &&
+#endif
+ !gDvm.debuggerActive &&
+#if defined(WITH_PROFILER)
+ (gDvm.activeProfilers == 0) &&
+#endif
+ dvmJitCheckTraceRequest(self, interpState)) {
interpState->nextMode = INTERP_STD;
- //LOGD("** something wrong, exiting\n");
+ //LOGD("Invalid trace request, exiting\n");
return true;
}
-#else
- if (interpState->jitState != kJitSelfVerification &&
- dvmJitCheckTraceRequest(self,interpState)) {
- interpState->nextMode = INTERP_STD;
- //LOGD("** something wrong, exiting\n");
- return true;
- }
-#endif /* WITH_SELF_VERIFICATION */
#endif /* INTERP_TYPE == INTERP_DBG */
#endif /* WITH_JIT */
diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c
index bfda671..8b30bd6 100644
--- a/vm/mterp/out/InterpC-portstd.c
+++ b/vm/mterp/out/InterpC-portstd.c
@@ -1217,25 +1217,21 @@
interpState->pc,
interpState->method->name);
#endif
-
#if INTERP_TYPE == INTERP_DBG
- /* Check to see if we've got a trace selection request. If we do,
- * but something is amiss, revert to the fast interpreter.
- */
-#if !defined(WITH_SELF_VERIFICATION)
- if (dvmJitCheckTraceRequest(self,interpState)) {
+ /* Check to see if we've got a trace selection request. */
+ if (
+#if defined(WITH_SELF_VERIFICATION)
+ (interpState->jitState != kJitSelfVerification) &&
+#endif
+ !gDvm.debuggerActive &&
+#if defined(WITH_PROFILER)
+ (gDvm.activeProfilers == 0) &&
+#endif
+ dvmJitCheckTraceRequest(self, interpState)) {
interpState->nextMode = INTERP_STD;
- //LOGD("** something wrong, exiting\n");
+ //LOGD("Invalid trace request, exiting\n");
return true;
}
-#else
- if (interpState->jitState != kJitSelfVerification &&
- dvmJitCheckTraceRequest(self,interpState)) {
- interpState->nextMode = INTERP_STD;
- //LOGD("** something wrong, exiting\n");
- return true;
- }
-#endif /* WITH_SELF_VERIFICATION */
#endif /* INTERP_TYPE == INTERP_DBG */
#endif /* WITH_JIT */
diff --git a/vm/mterp/portable/entry.c b/vm/mterp/portable/entry.c
index 4a6ed4e..8ea4bdc 100644
--- a/vm/mterp/portable/entry.c
+++ b/vm/mterp/portable/entry.c
@@ -42,25 +42,21 @@
interpState->pc,
interpState->method->name);
#endif
-
#if INTERP_TYPE == INTERP_DBG
- /* Check to see if we've got a trace selection request. If we do,
- * but something is amiss, revert to the fast interpreter.
- */
-#if !defined(WITH_SELF_VERIFICATION)
- if (dvmJitCheckTraceRequest(self,interpState)) {
+ /* Check to see if we've got a trace selection request. */
+ if (
+#if defined(WITH_SELF_VERIFICATION)
+ (interpState->jitState != kJitSelfVerification) &&
+#endif
+ !gDvm.debuggerActive &&
+#if defined(WITH_PROFILER)
+ (gDvm.activeProfilers == 0) &&
+#endif
+ dvmJitCheckTraceRequest(self, interpState)) {
interpState->nextMode = INTERP_STD;
- //LOGD("** something wrong, exiting\n");
+ //LOGD("Invalid trace request, exiting\n");
return true;
}
-#else
- if (interpState->jitState != kJitSelfVerification &&
- dvmJitCheckTraceRequest(self,interpState)) {
- interpState->nextMode = INTERP_STD;
- //LOGD("** something wrong, exiting\n");
- return true;
- }
-#endif /* WITH_SELF_VERIFICATION */
#endif /* INTERP_TYPE == INTERP_DBG */
#endif /* WITH_JIT */
diff --git a/vm/native/dalvik_system_VMDebug.c b/vm/native/dalvik_system_VMDebug.c
index 3f0b5fc..1d2f024 100644
--- a/vm/native/dalvik_system_VMDebug.c
+++ b/vm/native/dalvik_system_VMDebug.c
@@ -93,6 +93,7 @@
#ifdef WITH_HPROF
/* VM responds to DDMS heap dump requests */
features[idx++] = "hprof-heap-dump";
+ features[idx++] = "hprof-heap-dump-streaming";
#endif
assert(idx <= MAX_FEATURE_COUNT);
@@ -664,7 +665,7 @@
RETURN_VOID();
}
- result = hprofDumpHeap(fileName);
+ result = hprofDumpHeap(fileName, false);
free(fileName);
if (result != 0) {
@@ -681,6 +682,32 @@
}
/*
+ * static void dumpHprofDataDdms()
+ *
+ * Cause "hprof" data to be computed and sent directly to DDMS.
+ */
+static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args,
+ JValue* pResult)
+{
+#ifdef WITH_HPROF
+ int result;
+
+ result = hprofDumpHeap("[DDMS]", true);
+
+ if (result != 0) {
+ /* ideally we'd throw something more specific based on actual failure */
+ dvmThrowException("Ljava/lang/RuntimeException;",
+ "Failure during heap dump -- check log output for details");
+ RETURN_VOID();
+ }
+#else
+ dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL);
+#endif
+
+ RETURN_VOID();
+}
+
+/*
* static boolean cacheRegisterMap(String classAndMethodDescr)
*
* If the specified class is loaded, and the named method exists, ensure
@@ -876,6 +903,8 @@
Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos },
{ "dumpHprofData", "(Ljava/lang/String;)V",
Dalvik_dalvik_system_VMDebug_dumpHprofData },
+ { "dumpHprofDataDdms", "()V",
+ Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms },
{ "cacheRegisterMap", "(Ljava/lang/String;)Z",
Dalvik_dalvik_system_VMDebug_cacheRegisterMap },
{ "dumpReferenceTables", "()V",
diff --git a/vm/native/dalvik_system_VMRuntime.c b/vm/native/dalvik_system_VMRuntime.c
index 946a427..df0e73b 100644
--- a/vm/native/dalvik_system_VMRuntime.c
+++ b/vm/native/dalvik_system_VMRuntime.c
@@ -188,10 +188,15 @@
JValue* pResult)
{
#if defined(WITH_JIT)
+#if 0
/*
- * TODO - experiment the timing and put code here to start JIT'ing
+ * TODO - experiment with the timing.
* The tentative plan is onResume() will invoke the callback.
*/
+ dvmLockMutex(&gDvmJit.compilerLock);
+ pthread_cond_signal(&gDvmJit.compilerQueueActivity);
+ dvmUnlockMutex(&gDvmJit.compilerLock);
+#endif
#endif
RETURN_VOID();