deep-c-rsc/JCGO/goclsp/vm/java/lang/VMClassLoader.java
2021-07-16 17:12:20 -05:00

724 lines
20 KiB
Java

/*
* @(#) $(JCGO)/goclsp/vm/java/lang/VMClassLoader.java --
* VM specific methods for Java "ClassLoader" class.
**
* Project: JCGO (http://www.ivmaisoft.com/jcgo/)
* Copyright (C) 2001-2010 Ivan Maidanski <ivmai@ivmaisoft.com>
* All rights reserved.
**
* Class specification origin: GNU Classpath v0.93 vm/reference
*/
/*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
**
* This software is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License (GPL) for more details.
**
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
**
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from
* or based on this library. If you modify this library, you may extend
* this exception to your version of the library, but you are not
* obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package java.lang;
import gnu.classpath.Configuration;
import gnu.classpath.SystemProperties;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.security.ProtectionDomain;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
final class VMClassLoader /* hard-coded class name */
{
static final class ClassParser /* hard-coded class name */
{ /* used by VM classes only */
private static final int USHORT_SIZE = 2;
private static final int INT_SIZE = 4;
private static final int LONG_SIZE = 8;
private static final int MIN_MAJOR_VER = 45;
private static final int MAJOR_VER_OFFSET = INT_SIZE + USHORT_SIZE;
private static final int CONSTPOOL_COUNT_OFFSET =
MAJOR_VER_OFFSET + USHORT_SIZE;
private static final int CONST_POOL_OFFSET =
CONSTPOOL_COUNT_OFFSET + USHORT_SIZE;
private static final int BODY_BYTES_MINLEN = USHORT_SIZE * 7;
private static final int CONST_TAG_CLASS = 7;
private static final int CONST_TAG_FIELDREF = 9;
private static final int CONST_TAG_METHODREF = 10;
private static final int CONST_TAG_INTERFACEMETHODREF = 11;
private static final int CONST_TAG_STRING = 8;
private static final int CONST_TAG_INTEGER = 3;
private static final int CONST_TAG_FLOAT = 4;
private static final int CONST_TAG_LONG = 5;
private static final int CONST_TAG_DOUBLE = 6;
private static final int CONST_TAG_NAMEANDTYPE = 12;
private static final int CONST_TAG_UTF8 = 1;
static
{
if (!"".equals("")) /* hack */
defineClass0X(null, null, new byte[0]); /* hack */
}
private ClassParser() {}
static final Class defineClass0X(String name, Object loader,
byte[] bytes) /* hard-coded method signature */
throws ClassCastException
{ /* called from native code */
return defineClass((ClassLoader) loader, name, bytes, 0, bytes.length,
null);
}
static final String decodeJavaClassName(byte[] bytes, int offset, int len)
{ /* used by VM classes only */
if (bytes.length - offset < len || (offset | len) < 0)
throw new ArrayIndexOutOfBoundsException();
if (len < CONST_POOL_OFFSET || bytes[offset] != (byte) 0xca ||
bytes[offset + 1] != (byte) 0xfe || bytes[offset + 2] != (byte) 0xba ||
bytes[offset + 3] != (byte) 0xbe ||
getUShortAt(bytes, offset + MAJOR_VER_OFFSET) < MIN_MAJOR_VER)
throw new ClassFormatError();
int cpEndOfs = getJavaClassConstPoolEntryOfs(getUShortAt(bytes,
offset + CONSTPOOL_COUNT_OFFSET), bytes, offset);
if (offset + len - cpEndOfs < BODY_BYTES_MINLEN)
throw new ClassFormatError();
int thisClassEntryOfs = getJavaClassConstPoolEntryOfs(getUShortAt(bytes,
cpEndOfs + USHORT_SIZE), bytes, offset);
if (thisClassEntryOfs >= cpEndOfs ||
bytes[thisClassEntryOfs] != CONST_TAG_CLASS)
throw new ClassFormatError();
int nameEntryOfs = getJavaClassConstPoolEntryOfs(getUShortAt(bytes,
thisClassEntryOfs + 1), bytes, offset);
if (nameEntryOfs >= cpEndOfs || bytes[nameEntryOfs] != CONST_TAG_UTF8)
throw new ClassFormatError();
String name = new String(bytes, 0, nameEntryOfs + (1 + USHORT_SIZE),
getUShortAt(bytes, nameEntryOfs + 1));
if (name.length() == 0 || name.indexOf('.') >= 0)
throw new ClassFormatError();
return name.replace('/', '.');
}
private static int getJavaClassConstPoolEntryOfs(int index, byte[] bytes,
int offset)
{
offset += CONST_POOL_OFFSET;
int bytesLen = bytes.length;
while (--index > 0 && bytesLen - offset > USHORT_SIZE)
switch (bytes[offset])
{
case CONST_TAG_UTF8:
offset += getUShortAt(bytes, offset + 1) + (1 + USHORT_SIZE);
break;
case CONST_TAG_CLASS:
case CONST_TAG_STRING:
offset += 1 + USHORT_SIZE;
break;
case CONST_TAG_INTEGER:
case CONST_TAG_FLOAT:
case CONST_TAG_FIELDREF:
case CONST_TAG_METHODREF:
case CONST_TAG_INTERFACEMETHODREF:
case CONST_TAG_NAMEANDTYPE:
offset += 1 + INT_SIZE;
break;
case CONST_TAG_LONG:
case CONST_TAG_DOUBLE:
offset += 1 + LONG_SIZE;
index--;
break;
default:
throw new ClassFormatError();
}
if (index != 0 || offset < 0)
throw new ClassFormatError();
return offset;
}
private static int getUShortAt(byte[] bytes, int offset)
{
return ((bytes[offset] & 0xff) << 8) | (bytes[offset + 1] & 0xff);
}
}
private static final class StaticData
{
static final HashMap bootJars = new HashMap();
static final String JAVA_BOOT_CLASS_PATH = removeNonExistingPaths(
SystemProperties.getProperty("java.boot.class.path", "."));
static final HashMap definedPackages = getBootDefinedPackages();
private static HashMap getBootDefinedPackages()
{
HashMap map = new HashMap();
String[] packages = getBootPackages();
if (packages != null)
{
String specName = SystemProperties.getProperty("java.specification.name");
String vendor = SystemProperties.getProperty("java.specification.vendor");
String version =
SystemProperties.getProperty("java.specification.version");
for (int i = 0; i < packages.length; i++)
{
String name = packages[i];
map.put(name, new Package(name, specName, vendor, version,
"GNU Classpath", "GNU", Configuration.CLASSPATH_VERSION, null, null));
}
}
return map;
}
}
private static final class AppClassLoader extends ClassLoader
{
AppClassLoader()
{
super(null);
}
protected URL findResource(String name)
{
Vector list = new Vector(1);
findResourcesInner(list, name, true,
SystemProperties.getProperty("java.class.path", "."));
return list.size() > 0 ? (URL) list.elementAt(0) : null;
}
protected Enumeration findResources(String name)
throws IOException
{
Vector list = new Vector();
findResourcesInner(list, name, false,
SystemProperties.getProperty("java.class.path", "."));
return list.elements();
}
protected Package getPackage(String name)
{
Package pkg = super.getPackage(name);
if (pkg == null)
{
pkg = new Package(name, null, null, null, null, null, null, null, this);
synchronized (this.definedPackages)
{
this.definedPackages.put(name, pkg);
}
}
return pkg;
}
public String toString()
{
return getClass().getName();
}
}
private static final class JarUrlStreamHandler extends URLStreamHandler
{
private SimpleDateFormat dateFormat;
JarUrlStreamHandler() {}
protected URLConnection openConnection(URL url)
{
return
new URLConnection(url)
{
private ZipFile zip;
private ZipEntry entry;
public synchronized void connect()
throws MalformedURLException
{
if (!connected)
{
URL url = getURL();
String spec = url.getFile();
int pos;
if (!"jar".equals(url.getProtocol()) || (pos = spec.indexOf("!/")) < 0)
throw new MalformedURLException("not jar: " + url.toString());
ZipFile zip = openUrlZipFile(spec.substring(0, pos), null);
if (zip == null || (entry =
zip.getEntry(unquoteUrl(spec.substring(pos + 2)))) == null)
throw new MalformedURLException("not found: " + url.toString());
this.zip = zip;
connected = true;
}
}
public InputStream getInputStream()
throws IOException
{
if (!connected)
connect();
return zip.getInputStream(entry);
}
public String getHeaderField(String name)
{
if (!connected)
{
try
{
connect();
}
catch (IOException e)
{
return null;
}
}
if (name.equals("content-type"))
return guessContentTypeFromName(entry.getName());
if (name.equals("content-length"))
return Long.toString(entry.getSize());
if (name.equals("last-modified"))
synchronized (JarUrlStreamHandler.this)
{
if (dateFormat == null)
dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'",
Locale.US);
return dateFormat.format(new Date(entry.getTime()));
}
return null;
}
};
}
}
private static volatile URLStreamHandler jarUrlStreamHandler;
private static final VMClass.IdentityHashMap defnClassToLoader =
new VMClass.IdentityHashMap();
private static final VMClass.IdentityHashMap definedClassToPDomain =
new VMClass.IdentityHashMap();
static
{
compareClassNames0X("", boolean.class); /* hack */
}
private VMClassLoader() {}
static final Class defineClass(ClassLoader cl, String name, byte[] data,
int offset, int len, ProtectionDomain pd)
{
/* implemented for the pre-compiled classes only */
String className = ClassParser.decodeJavaClassName(data, offset, len);
if (name != null && !name.equals(className))
throw new NoClassDefFoundError("invalid class name: " + name);
if (className.startsWith("java."))
throw new SecurityException("prohibited package name: " +
className.substring(0, className.lastIndexOf('.')));
Class aclass = loadClass0(className, 0);
if (aclass != null)
{
if (cl != null)
synchronized (defnClassToLoader)
{
Object old = defnClassToLoader.put(aclass, cl);
if (old != null && old != cl)
defnClassToLoader.put(aclass, old);
}
if (pd != null)
synchronized (definedClassToPDomain)
{
Object old = definedClassToPDomain.put(aclass, pd);
if (old != null && old != pd)
definedClassToPDomain.put(aclass, old);
}
return aclass;
}
throw new InternalError(
"VMClassLoader.defineClass() not implemented, class " + className);
}
static final Class defineClassWithTransformers(ClassLoader cl, String name,
byte[] data, int offset, int len, ProtectionDomain pd)
{
/* not implemented */
return defineClass(cl, name, data, offset, len, pd);
}
static final void resolveClass(Class aclass)
{
/* dummy */
}
static final Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
if (name.length() == 0)
throw new ClassNotFoundException(name);
Class aclass = loadClass0(name, 0);
if (resolve && aclass != null)
resolveClass(aclass);
return aclass;
}
static URL getResource(String name)
{
Vector list = new Vector(1);
findResourcesInner(list, name, true, StaticData.JAVA_BOOT_CLASS_PATH);
return list.size() > 0 ? (URL) list.elementAt(0) : null;
}
static Enumeration getResources(String name)
{
Vector list = new Vector();
findResourcesInner(list, name, false, StaticData.JAVA_BOOT_CLASS_PATH);
return list.elements();
}
static Package getPackage(String name)
{
return (Package) StaticData.definedPackages.get(name);
}
static Package[] getPackages()
{
int size = StaticData.definedPackages.size();
Package[] packages = new Package[size];
Iterator itr = StaticData.definedPackages.values().iterator();
for (int i = 0; i < size; i++)
packages[i] = (Package) itr.next();
return packages;
}
static String[] getBootPackages()
{
return new String[0];
}
static final Class getPrimitiveClass(char ch)
{
int type = "VZBCSIJFD".indexOf(ch, 0);
return type >= 0 ? getPrimitiveClass0(type) : null;
}
static final boolean defaultAssertionStatus()
{
/* not implemented */
return true;
}
static final Map packageAssertionStatus()
{
/* not implemented */
return new HashMap();
}
static final Map classAssertionStatus()
{
/* not implemented */
return new HashMap();
}
static ClassLoader getSystemClassLoader()
{
return "gnu.java.net.content.text.plain".equals( /* hack */
"gnu.java.net.protocol.file.Handler") ? null : /* hack */
new AppClassLoader();
}
static Class findLoadedClass(ClassLoader cl, String name)
{
Class aclass = loadClass0(name, 0);
if (aclass != null && getLoaderOfDefinedClass(aclass) == cl)
return aclass;
return null;
}
static final ClassLoader getLoaderOfDefinedClass(Class klass)
{ /* used by VM classes only */
synchronized (defnClassToLoader)
{
return (ClassLoader) defnClassToLoader.get(klass);
}
}
static final ProtectionDomain getProtectionDomain(Class klass)
{ /* used by VM classes only */
Class aclass;
while ((aclass = klass.getComponentType()) != null)
klass = aclass;
synchronized (definedClassToPDomain)
{
return (ProtectionDomain) definedClassToPDomain.get(klass);
}
}
static final Class getNextInnerClass(String name)
{ /* used by VM classes only */
return loadClass0(name, 1);
}
private static int unescapeUrlChar(String spec, int pos)
throws MalformedURLException
{
if (pos + 2 >= spec.length() || spec.charAt(pos) != '%')
throw new MalformedURLException(spec);
int high = Character.digit(spec.charAt(pos + 1), 16);
int low = Character.digit(spec.charAt(pos + 2), 16);
if ((high | low) < 0)
throw new MalformedURLException(spec);
return (high << 4) | low;
}
static String unquoteUrl(String spec)
throws MalformedURLException
{ /* used by VM classes only */
int pos = spec.indexOf('%');
if (pos < 0)
return spec;
StringBuilder sb = new StringBuilder(spec.length());
int prev = 0;
do
{
sb.append(spec.substring(prev, pos));
int c = unescapeUrlChar(spec, pos);
if ((c & 0x80) != 0)
{
pos += 3;
int c2 = unescapeUrlChar(spec, pos);
if ((c2 & 0xc0) != 0x80)
throw new MalformedURLException(spec);
if ((c & 0xe0) == 0xc0)
c = ((c & 0x1f) << 6) | (c2 & 0x3f);
else
{
pos += 3;
int c3 = unescapeUrlChar(spec, pos);
if ((c3 & 0xc0) != 0x80 || (c & 0xf0) != 0xe0)
throw new MalformedURLException(spec);
c = ((c & 0xf) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f);
}
}
sb.append((char) c);
prev = pos + 3;
} while ((pos = spec.indexOf('%', prev)) >= 0);
return sb.append(spec.substring(prev)).toString();
}
static final ZipFile openUrlZipFile(String baseSpec, File file)
{ /* used by VM classes only */
SoftReference ref;
synchronized (StaticData.bootJars)
{
ref = (SoftReference) StaticData.bootJars.get(baseSpec);
}
ZipFile zip;
if (ref == null || (zip = (ZipFile) ref.get()) == null)
{
if (file == null)
{
if (baseSpec.length() <= 6 || !baseSpec.startsWith("file:"))
return null;
String path;
try
{
path = unquoteUrl(baseSpec.substring(5));
}
catch (MalformedURLException e)
{
return null;
}
if (path.charAt(0) != '/' ||
!(file = new File(path.substring(1))).isAbsolute())
file = new File(path);
}
zip = null;
try
{
zip = new ZipFile(file);
}
catch (IOException e) {}
catch (SecurityException e) {}
if (zip != null)
{
ref = new SoftReference(zip);
synchronized (StaticData.bootJars)
{
StaticData.bootJars.put(baseSpec, ref);
}
}
}
return zip;
}
static final String removeNonExistingPaths(String pathList)
{ /* used by VM classes only */
StringBuilder sb = new StringBuilder(pathList.length());
int pos = 0;
int nextPos;
do
{
nextPos = pathList.indexOf(File.pathSeparatorChar, pos);
if (nextPos < 0)
nextPos = pathList.length();
File file = new File(pos < nextPos ?
pathList.substring(pos, nextPos) : ".");
try
{
if (!file.exists())
file = null;
}
catch (SecurityException e) {}
if (file != null)
{
if (sb.length() > 0)
sb.append(File.pathSeparatorChar);
sb.append(file.getPath());
}
pos = nextPos + 1;
} while (pathList.length() > nextPos);
return sb.toString();
}
static final void findResourcesInner(Vector list, String name,
boolean findSingle, String pathList)
{ /* used by VM classes only */
int pos;
int nextPos = 0;
do
{
if (name.length() <= nextPos)
return;
pos = nextPos;
char ch;
while ((ch = name.charAt(nextPos++)) == '.')
if (name.length() <= nextPos)
return;
if (ch != '/' && ch != File.separatorChar)
break;
} while (true);
if (pos > 0)
{
name = name.substring(pos);
pos = 0;
}
do
{
nextPos = pathList.indexOf(File.pathSeparatorChar, pos);
if (nextPos < 0)
nextPos = pathList.length();
File file = new File(pos < nextPos ?
pathList.substring(pos, nextPos) : ".");
try
{
String baseSpec = file.toURL().toString();
if (baseSpec.endsWith("/"))
{
File f = new File(file, name);
if (f.exists())
{
list.addElement(f.toURL());
if (findSingle)
break;
}
}
else
{
ZipFile zip = openUrlZipFile(baseSpec, file);
if (zip != null && zip.getEntry(name) != null)
{
String spec = baseSpec + "!/" + name;
try
{
list.addElement(new URL("jar", null, spec));
}
catch (MalformedURLException x)
{
URLStreamHandler handler = jarUrlStreamHandler;
if (handler == null)
jarUrlStreamHandler = handler = new JarUrlStreamHandler();
list.addElement(new URL("jar", null, -1, spec, handler));
}
if (findSingle)
break;
}
}
}
catch (SecurityException e) {}
catch (MalformedURLException e)
{
try
{
if (file.exists())
throw (Error) (new InternalError("findResources")).initCause(e);
}
catch (SecurityException x) {}
}
pos = nextPos + 1;
} while (pathList.length() > nextPos);
}
static final int compareClassNames0X(String name, Class aclass)
{ /* called from native code */
return name.compareTo(aclass.getName());
}
private static native Class getPrimitiveClass0(int type); /* JVM-core */
private static native Class loadClass0(String name,
int nextInner); /* JVM-core */
}