ensure ClassLoader.getPackage works with all class libraries

There's more work to do to derive all the properties of a given class
from its code source (e.g. JAR file), but this at least ensures that
ClassLoader.getPackage will actually return something non-null when
appropriate.
This commit is contained in:
Joel Dice 2014-03-19 11:21:26 -06:00
parent e9e365d698
commit 8740d76154
10 changed files with 163 additions and 76 deletions

View File

@ -74,6 +74,21 @@ public class SystemClassLoader extends ClassLoader {
return null;
}
protected Package getPackage(String name) {
Package p = super.getPackage(name);
if (p == null) {
String source = getPackageSource(name);
if (source != null) {
// todo: load attributes from JAR manifest
definePackage(name, null, null, null, null, null, null, null);
}
}
return super.getPackage(name);
}
protected static native String getPackageSource(String name);
// OpenJDK's java.lang.ClassLoader.getResource makes use of
// sun.misc.Launcher to load bootstrap resources, which is not
// appropriate for the Avian build, so we override it to ensure we

View File

@ -559,8 +559,7 @@ public final class Class <T> implements Type, AnnotatedElement {
String name = getCanonicalName();
int index = name.lastIndexOf('.');
if (index >= 0) {
return new Package(name.substring(0, index),
null, null, null, null, null, null, null, null);
return getClassLoader().getPackage(name.substring(0, index));
} else {
return null;
}

View File

@ -17,9 +17,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.HashMap;
public abstract class ClassLoader {
private final ClassLoader parent;
private Map<String, Package> packages;
protected ClassLoader(ClassLoader parent) {
if (parent == null) {
@ -33,6 +36,45 @@ public abstract class ClassLoader {
this(getSystemClassLoader());
}
private Map<String, Package> packages() {
if (packages == null) {
packages = new HashMap();
}
return packages;
}
protected Package getPackage(String name) {
synchronized (this) {
return packages().get(name);
}
}
protected Package[] getPackages() {
synchronized (this) {
return packages().values().toArray(new Package[packages().size()]);
}
}
protected Package definePackage(String name,
String specificationTitle,
String specificationVersion,
String specificationVendor,
String implementationTitle,
String implementationVersion,
String implementationVendor,
URL sealBase)
{
Package p = new Package
(name, implementationTitle, implementationVersion,
implementationVendor, specificationTitle, specificationVersion,
specificationVendor, sealBase, this);
synchronized (this) {
packages().put(name, p);
return p;
}
}
public static ClassLoader getSystemClassLoader() {
return ClassLoader.class.getClassLoader();
}

View File

@ -1577,9 +1577,6 @@ class Classpath {
virtual const char*
bootClasspath() = 0;
virtual void
updatePackageMap(Thread* t, object class_) = 0;
virtual object
makeDirectByteBuffer(Thread* t, void* p, jlong capacity) = 0;

View File

@ -238,6 +238,36 @@ Avian_avian_SystemClassLoader_getClass
(getJClass(t, reinterpret_cast<object>(arguments[0])));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_avian_SystemClassLoader_getPackageSource
(Thread* t, object, uintptr_t* arguments)
{
object name = reinterpret_cast<object>(arguments[0]);
PROTECT(t, name);
ACQUIRE(t, t->m->classLock);
THREAD_RUNTIME_ARRAY(t, char, chars, stringLength(t, name) + 2);
stringChars(t, name, RUNTIME_ARRAY_BODY(chars));
replace('.', '/', RUNTIME_ARRAY_BODY(chars));
RUNTIME_ARRAY_BODY(chars)[stringLength(t, name)] = '/';
RUNTIME_ARRAY_BODY(chars)[stringLength(t, name) + 1] = 0;
object key = makeByteArray(t, RUNTIME_ARRAY_BODY(chars));
object array = hashMapFind
(t, root(t, Machine::PackageMap), key, byteArrayHash, byteArrayEqual);
if (array) {
return reinterpret_cast<uintptr_t>
(makeLocalReference
(t, t->m->classpath->makeString
(t, array, 0, byteArrayLength(t, array))));
} else {
return 0;
}
}
#ifdef AVIAN_HEAPDUMP
extern "C" AVIAN_EXPORT void JNICALL

View File

@ -22,6 +22,7 @@ extern "C" int JNI_OnLoad(JavaVM*, void*);
#include "avian/machine.h"
#include "avian/classpath-common.h"
#include "avian/process.h"
#include "avian/util.h"
#ifdef PLATFORM_WINDOWS
const char* getErrnoDescription(int err); // This function is defined in mingw-extensions.cpp
@ -488,9 +489,22 @@ class MyClasspath : public Classpath {
}
virtual void
boot(Thread*)
boot(Thread* t)
{
// ignore
object c = resolveClass
(t, root(t, Machine::BootLoader), "java/lang/ClassLoader");
PROTECT(t, c);
object constructor = resolveMethod
(t, c, "<init>", "(Ljava/lang/ClassLoader;Z)V");
PROTECT(t, constructor);
t->m->processor->invoke
(t, constructor, root(t, Machine::BootLoader), 0, true);
t->m->processor->invoke
(t, constructor, root(t, Machine::AppLoader),
root(t, Machine::BootLoader), false);
}
virtual const char*
@ -499,12 +513,6 @@ class MyClasspath : public Classpath {
return AVIAN_CLASSPATH;
}
virtual void
updatePackageMap(Thread*, object)
{
// ignore
}
virtual object
makeDirectByteBuffer(Thread* t, void* p, jlong capacity)
{

View File

@ -133,12 +133,6 @@ class MyClasspath : public Classpath {
return AVIAN_CLASSPATH;
}
virtual void
updatePackageMap(Thread*, object)
{
// ignore
}
virtual object
makeDirectByteBuffer(Thread* t, void* p, jlong capacity)
{

View File

@ -774,62 +774,6 @@ class MyClasspath : public Classpath {
return classpath;
}
virtual void
updatePackageMap(Thread* t, object class_)
{
PROTECT(t, class_);
if (root(t, Machine::PackageMap) == 0) {
setRoot(t, Machine::PackageMap, makeHashMap(t, 0, 0));
}
object className = vm::className(t, class_);
if ('[' != byteArrayBody(t, className, 0)) {
THREAD_RUNTIME_ARRAY
(t, char, packageName, byteArrayLength(t, className));
char* s = reinterpret_cast<char*>(&byteArrayBody(t, className, 0));
char* p = strrchr(s, '/');
if (p) {
int length = (p - s) + 1;
memcpy(RUNTIME_ARRAY_BODY(packageName),
&byteArrayBody(t, className, 0),
length);
RUNTIME_ARRAY_BODY(packageName)[length] = 0;
object key = vm::makeByteArray
(t, "%s", RUNTIME_ARRAY_BODY(packageName));
PROTECT(t, key);
hashMapRemove
(t, root(t, Machine::PackageMap), key, byteArrayHash,
byteArrayEqual);
object source = classSource(t, class_);
if (source) {
// note that we strip the "file:" prefix, since
// Package.defineSystemPackage expects an unadorned
// filename:
const unsigned PrefixLength = 5;
unsigned sourceNameLength = byteArrayLength(t, source)
- PrefixLength;
THREAD_RUNTIME_ARRAY(t, char, sourceName, sourceNameLength);
memcpy(RUNTIME_ARRAY_BODY(sourceName),
&byteArrayBody(t, source, PrefixLength),
sourceNameLength);
source = vm::makeByteArray(t, "%s", RUNTIME_ARRAY_BODY(sourceName));
} else {
source = vm::makeByteArray(t, "avian-dummy-package-source");
}
hashMapInsert
(t, root(t, Machine::PackageMap), key, source, byteArrayHash);
}
}
}
virtual object
makeDirectByteBuffer(Thread* t, void* p, jlong capacity)
{

View File

@ -3042,6 +3042,61 @@ findInTable(Thread* t, object table, object name, object spec,
return 0;
}
void
updatePackageMap(Thread* t, object class_)
{
PROTECT(t, class_);
if (root(t, Machine::PackageMap) == 0) {
setRoot(t, Machine::PackageMap, makeHashMap(t, 0, 0));
}
object className = vm::className(t, class_);
if ('[' != byteArrayBody(t, className, 0)) {
THREAD_RUNTIME_ARRAY
(t, char, packageName, byteArrayLength(t, className));
char* s = reinterpret_cast<char*>(&byteArrayBody(t, className, 0));
char* p = strrchr(s, '/');
if (p) {
int length = (p - s) + 1;
memcpy(RUNTIME_ARRAY_BODY(packageName),
&byteArrayBody(t, className, 0),
length);
RUNTIME_ARRAY_BODY(packageName)[length] = 0;
object key = vm::makeByteArray
(t, "%s", RUNTIME_ARRAY_BODY(packageName));
PROTECT(t, key);
hashMapRemove
(t, root(t, Machine::PackageMap), key, byteArrayHash,
byteArrayEqual);
object source = classSource(t, class_);
if (source) {
// note that we strip the "file:" prefix, since OpenJDK's
// Package.defineSystemPackage expects an unadorned filename:
const unsigned PrefixLength = 5;
unsigned sourceNameLength = byteArrayLength(t, source)
- PrefixLength;
THREAD_RUNTIME_ARRAY(t, char, sourceName, sourceNameLength);
memcpy(RUNTIME_ARRAY_BODY(sourceName),
&byteArrayBody(t, source, PrefixLength),
sourceNameLength);
source = vm::makeByteArray(t, "%s", RUNTIME_ARRAY_BODY(sourceName));
} else {
source = vm::makeByteArray(t, "avian-dummy-package-source");
}
hashMapInsert
(t, root(t, Machine::PackageMap), key, source, byteArrayHash);
}
}
}
} // namespace
namespace vm {
@ -4295,7 +4350,7 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_,
if (class_) {
hashMapInsert(t, classLoaderMap(t, loader), spec, class_, byteArrayHash);
t->m->classpath->updatePackageMap(t, class_);
updatePackageMap(t, class_);
} else if (throw_) {
throwNew(t, throwType, "%s", &byteArrayBody(t, spec, 0));
}

View File

@ -249,6 +249,9 @@ public class Reflection {
expect(getClass().getDeclaringClass() == null);
}
}.run();
expect(avian.testing.annotations.Test.class.getPackage().getName().equals
("avian.testing.annotations"));
}
protected static class Baz {