mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 16:46:26 +00:00
545 lines
15 KiB
C++
545 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (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.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
#include "prenv.h"
|
|
#include "prio.h"
|
|
#include "ClassCentral.h"
|
|
#include "ErrorHandling.h"
|
|
#include "DiskFileReader.h"
|
|
|
|
#include "CUtils.h"
|
|
#include "StringUtils.h"
|
|
|
|
#include "plstr.h"
|
|
#include "prprf.h"
|
|
|
|
|
|
UT_DEFINE_LOG_MODULE(ClassCentral);
|
|
|
|
|
|
ClassCentral::ClassCentral(Pool &p, ClassWorld &world, StringPool &sp,
|
|
const char *cp) :
|
|
classPool(staticPool), world(world), sp(sp), dynamicPool(p)
|
|
{
|
|
classPath = 0;
|
|
setClassPath(cp);
|
|
}
|
|
|
|
|
|
/* Returns a new path string formed by concatenating the
|
|
* path string t to the path string s. A path string is a
|
|
* colon-separated list of (canonical) directory names.
|
|
*/
|
|
char *ClassCentral::concatPath(const char *s, const char *t)
|
|
{
|
|
char *newStr = new (staticPool) char[PL_strlen(s)+PL_strlen(t)+1];
|
|
|
|
PL_strcpy(newStr, s);
|
|
|
|
if (newStr[PL_strlen(s)-1] != ':')
|
|
PL_strcat(newStr, ":");
|
|
|
|
PL_strcat(newStr, t);
|
|
|
|
return newStr;
|
|
}
|
|
|
|
char *ClassCentral::getEnvPath()
|
|
{
|
|
char *env;
|
|
if ((env = PR_GetEnv("CLASSPATH")) != 0)
|
|
return dupString(env, staticPool);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NO_NSPR
|
|
#ifdef EF_WINDOWS
|
|
#define PATH_SEPARATOR '\\'
|
|
#endif
|
|
#else
|
|
#define PATH_SEPARATOR '/'
|
|
#endif
|
|
|
|
|
|
static bool fileExists(const char *fn)
|
|
{
|
|
#ifdef NO_NSPR
|
|
fn = 0; // Dummy
|
|
return true;
|
|
#else
|
|
PRFileInfo info;
|
|
return (PR_GetFileInfo(fn, &info) >= 0);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Look for a file name that matches maxFileNameLen characters
|
|
* of className. This is to look for truncated files on the
|
|
* MAC.
|
|
*/
|
|
FileReader *ClassCentral::lookForClassFile(const char* className,
|
|
size_t maxFileNameLen)
|
|
{
|
|
for (DoublyLinkedList<PathNode>::iterator i = pathList.begin();
|
|
!pathList.done(i); i = pathList.advance(i)) {
|
|
PathComponent *component = pathList.get(i).component;
|
|
FileReader *fileReader = component->getFile(className, maxFileNameLen);
|
|
|
|
if (fileReader)
|
|
return fileReader;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Returns the canonical filename of the class from the fully
|
|
* qualified className. The current class path is searched to
|
|
* find the file and the first available match is returned.
|
|
* Returns NULL if no matching file was found.
|
|
*/
|
|
FileReader *ClassCentral::resolveClassName(const char *className)
|
|
{
|
|
FileReader *reader;
|
|
|
|
#ifdef XP_MAC
|
|
/* first look for the class file with a maximum length of 31
|
|
* for the name
|
|
*/
|
|
if ((reader = lookForClassFile(className, 30)) != 0)
|
|
return reader;
|
|
#endif
|
|
|
|
/* now look for the class file with its whole name */
|
|
if ((reader = lookForClassFile(className)) != 0)
|
|
return reader;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Adds the paths represented in cpath either to the
|
|
* end of, or at the beginning of, the current classPath
|
|
* depending on whether end is true or false. cpath is
|
|
* a colon-separated list of canonical pathnames. cpath
|
|
* is altered as a result of this routine.
|
|
*/
|
|
void ClassCentral::addPath(char *cpath, bool end)
|
|
{
|
|
Strtok tok(cpath);
|
|
char *s = tok.get(":");
|
|
|
|
if (end) {
|
|
while (s) {
|
|
PathNode *pathNode = new (staticPool) PathNode;
|
|
pathNode->component = new (staticPool) PathComponent(s, staticPool);
|
|
|
|
pathList.addLast(*pathNode);
|
|
s = tok.get(":");
|
|
}
|
|
} else {
|
|
if (s) {
|
|
/* Add the first component to the beginning */
|
|
PathNode *firstNode = new (staticPool) PathNode;
|
|
firstNode->component = new (staticPool) PathComponent(s, staticPool);
|
|
|
|
pathList.addFirst(*firstNode);
|
|
s = tok.get(":");
|
|
|
|
/* Now add the rest of the components */
|
|
while (s) {
|
|
PathNode *pathNode = new (staticPool) PathNode;
|
|
pathNode->component = new (staticPool) PathComponent(s, staticPool);
|
|
|
|
pathList.insertAfter(*pathNode, pathList.location(*firstNode));
|
|
s = tok.get(":");
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassCentral::setClassPath(const char *cp)
|
|
{
|
|
pathList.clear();
|
|
|
|
classPath = (cp) ? dupString(cp, staticPool) : getEnvPath();
|
|
|
|
if (classPath) {
|
|
char *cpath = dupString(classPath, staticPool);
|
|
|
|
addPath(cpath, true);
|
|
}
|
|
|
|
/* Always add the current directory to the CLASSPATH; this might result
|
|
* in the current directory being searched twice if it's already in the
|
|
* CLASSPATH, but that's OK.
|
|
*/
|
|
addPath(dupString(".", staticPool), true);
|
|
}
|
|
|
|
void ClassCentral::prefixClassPath(const char *path)
|
|
{
|
|
classPath = concatPath(path, classPath);
|
|
char *cpath = dupString(path, staticPool);
|
|
|
|
addPath(cpath, false);
|
|
}
|
|
|
|
void ClassCentral::suffixClassPath(const char *path)
|
|
{
|
|
classPath = concatPath(classPath, path);
|
|
|
|
char *cpath = dupString(path, staticPool);
|
|
|
|
addPath(cpath, true);
|
|
}
|
|
|
|
/* Create a new ClassFileSummary object with the given parameters without
|
|
* adding it to our repository (classPool). Verify that the name of the
|
|
* class matches className. See the documentation for
|
|
* ClassFileSummary::ClassFileSummary for the interpretation of the
|
|
* className, fileName, arrayType, numDimensions, primitiveElementType,
|
|
* tk, and component parameters.
|
|
*/
|
|
ClassFileSummary &ClassCentral::newSummary(const char *className,
|
|
FileReader *fileReader,
|
|
bool arrayType,
|
|
Uint32 numDimensions,
|
|
bool primitiveElementType,
|
|
TypeKind tk,
|
|
ClassFileSummary *component)
|
|
{
|
|
ClassFileSummary *info;
|
|
|
|
info = new (staticPool) ClassFileSummary(staticPool,
|
|
dynamicPool,
|
|
sp,
|
|
*this,
|
|
world,
|
|
className,
|
|
fileReader,
|
|
arrayType,
|
|
numDimensions,
|
|
primitiveElementType,
|
|
tk,
|
|
component);
|
|
|
|
/* XXX Will this result in calling of the correct destructor? */
|
|
if (fileReader)
|
|
fileReader->FileReader::~FileReader();
|
|
|
|
if (*className != '[') {
|
|
const char *classNameInFile =
|
|
info->getClassName();
|
|
|
|
|
|
/* Check to see if the class defined in this class file is indeed
|
|
* the classname we're looking for
|
|
*/
|
|
if (PL_strcmp(classNameInFile, className) != 0)
|
|
verifyError(VerifyError::classNotFound);
|
|
}
|
|
|
|
return *info;
|
|
}
|
|
|
|
/* Loads (but does not fully resolve) the class whose fully qualified name is
|
|
* className and stores it in ClassCentral's
|
|
* classPool. If fileName is non-NULL, it is the canonical name of the
|
|
* file in which to find the class; if it is NULL, classPath is used to
|
|
* construct the name of the file. If the class has already been loaded
|
|
* or if the class is actually an array or an interface,
|
|
* loadParents is set to false; otherwise, loadParents is set to true,
|
|
* indicating that the parent classes of this class must be loaded
|
|
* by the caller.
|
|
* components is a vector of ClassFileSummary objects that have already been
|
|
* loaded in recursive calls to Classcentral::addClass; this is passed on
|
|
* to recursive calls, if any, to ClassCentral::addClass().
|
|
*/
|
|
ClassFileSummary &ClassCentral::loadClass(Vector<ClassFileSummary *> &components,
|
|
const char *className,
|
|
const char *fileName,
|
|
bool &alreadyLoaded,
|
|
bool &loadParents)
|
|
{
|
|
ClassFileSummary *hashNodeData;
|
|
loadParents = false;
|
|
|
|
/* Has Class already been loaded? */
|
|
if (classPool.get(className, &hashNodeData)) {
|
|
alreadyLoaded = true;
|
|
return *hashNodeData;
|
|
}
|
|
alreadyLoaded = false;
|
|
|
|
/* Array classes */
|
|
if (*className == '[') {
|
|
const char *cname = className;
|
|
|
|
Uint32 numDimensions = 1;
|
|
while (*++cname == '[')
|
|
numDimensions++;
|
|
|
|
if (!*cname)
|
|
verifyError(VerifyError::noClassDefFound);
|
|
|
|
TypeKind tk;
|
|
switch (*cname) {
|
|
case 'L':
|
|
if (!*++cname)
|
|
verifyError(VerifyError::noClassDefFound);
|
|
else {
|
|
ClassFileSummary *elementSummary;
|
|
|
|
TemporaryStringCopy copy(cname);
|
|
char *cnameCopy = copy;
|
|
|
|
/* Over-write the semi-colon, if we can find it */
|
|
char *t;
|
|
for (t = cnameCopy; *t && *t != ';' ; t++)
|
|
;
|
|
|
|
if (*t != ';' || *(t+1))
|
|
verifyError(VerifyError::badClassFormat);
|
|
|
|
*t = 0;
|
|
|
|
if (classPool.get(cnameCopy, &hashNodeData))
|
|
elementSummary = hashNodeData;
|
|
else
|
|
elementSummary = &addClass(components, cnameCopy);
|
|
|
|
return newSummary(className,
|
|
(FileReader *) 0,
|
|
true, numDimensions,
|
|
false,
|
|
tkVoid,
|
|
elementSummary);
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (!getTypeKindFromDescriptor(*cname, tk) || *(cname+1))
|
|
verifyError(VerifyError::badClassFormat);
|
|
|
|
break;
|
|
}
|
|
|
|
/* If we're here, we have an array of a primitive type */
|
|
return newSummary(className,
|
|
(FileReader *) 0,
|
|
true, numDimensions,
|
|
true,
|
|
tk,
|
|
0);
|
|
}
|
|
|
|
|
|
UT_LOG(ClassCentral, PR_LOG_DEBUG, ("\tLoading class %s\n", className));
|
|
|
|
|
|
/* If we're here, we're a non-array class or interface that must be loaded from
|
|
* a class file.
|
|
*/
|
|
|
|
FileReader *fileReader;
|
|
loadParents = true;
|
|
|
|
if (fileName && fileExists(fileName))
|
|
fileReader = new DiskFileReader(staticPool, fileName);
|
|
else {
|
|
if (!(fileReader = resolveClassName(className)))
|
|
verifyError(VerifyError::noClassDefFound);
|
|
}
|
|
|
|
return newSummary(className,
|
|
fileReader,
|
|
false, 0, false, tkVoid,
|
|
0);
|
|
}
|
|
|
|
/* Add a class, ensuring that none of the summaries in the Vector
|
|
* component represent the class to be resolved.
|
|
*/
|
|
ClassFileSummary &ClassCentral::addClass(Vector<ClassFileSummary *> &components,
|
|
const char *className,
|
|
const char *fileName)
|
|
{
|
|
bool loadParents, alreadyLoaded;
|
|
ClassFileSummary *pParentInfo;
|
|
|
|
/* Make sure we're not already on the component vector. If we are,
|
|
* we've detected a circularity
|
|
*/
|
|
Uint32 size = components.size();
|
|
for (Uint32 i = 0; i < size; i++) {
|
|
ClassFileSummary *summ = components[i];
|
|
|
|
/* Note that this has to be a strcmp since className may or may
|
|
* not be an interned string
|
|
*/
|
|
if (!strcmp(className, summ->getClassName()))
|
|
verifyError(VerifyError::classCircularity);
|
|
}
|
|
|
|
ClassFileSummary &info = loadClass(components,
|
|
className, fileName, alreadyLoaded, loadParents);
|
|
|
|
if (alreadyLoaded)
|
|
return info;
|
|
|
|
components.append(&info);
|
|
|
|
/* All interfaces have a null superclass, despite the fact that the .class file
|
|
lists Object as the superclass of all interfaces. */
|
|
if ((className[0] != '[') && // Ensure non-array class
|
|
(info.getAccessFlags() & CR_ACC_INTERFACE)) {
|
|
pParentInfo = NULL;
|
|
} else if (loadParents) {
|
|
const char *superclassName = info.getSuperclassName();
|
|
|
|
if (superclassName) {
|
|
ClassFileSummary &parentInfo = addClass(components, superclassName, 0);
|
|
|
|
/* Our parent must be a class; it cannot be a final class */
|
|
Uint16 accessFlags = parentInfo.getAccessFlags();
|
|
if ((accessFlags & CR_ACC_INTERFACE) ||
|
|
(parentInfo.getAccessFlags() & CR_ACC_FINAL)) {
|
|
verifyError(VerifyError::badClassFormat);
|
|
}
|
|
|
|
/* Our parent shouldn't be an array class */
|
|
Type *parentType = parentInfo.getThisClass();
|
|
if (parentType->typeKind != tkObject)
|
|
verifyError(VerifyError::badClassFormat);
|
|
|
|
pParentInfo = &parentInfo;
|
|
} else {
|
|
|
|
/* This must be the Object class */
|
|
pParentInfo = NULL;
|
|
}
|
|
} else {
|
|
/* This must be an array class. Arrays always have Object as the superclass */
|
|
pParentInfo = Standard::get(cObject).summary;
|
|
}
|
|
|
|
classPool.add(className, &info);
|
|
info.setParent(pParentInfo);
|
|
|
|
return info;
|
|
}
|
|
|
|
/* This routine enforces a series of constraints on the className
|
|
* and returns true if the className satisfies all the constraints.
|
|
* Otherwise, returns false
|
|
*/
|
|
bool ClassCentral::validateName(const char *className) const
|
|
{
|
|
|
|
if (!className)
|
|
return false;
|
|
|
|
|
|
/* Make sure that the className is not relative. Currently,
|
|
* this means that it does not have a relative path (.) in it.
|
|
*/
|
|
for (const char *s = className; *s; s++)
|
|
if (*s == '.')
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ClassFileSummary &ClassCentral::addClass(const char *className,
|
|
const char *fileName)
|
|
{
|
|
if (!validateName(className))
|
|
verifyError(VerifyError::illegalAccess);
|
|
|
|
Vector<ClassFileSummary *> vec(5);
|
|
|
|
return addClass(vec, className, fileName);
|
|
}
|
|
|
|
void ClassCentral::removeClass(const char * /* className */)
|
|
{
|
|
|
|
}
|
|
|
|
void ClassCentral::removeClass(ClassFileSummary * /* info */)
|
|
{
|
|
|
|
}
|
|
|
|
Type &ClassCentral::parseFieldDescriptor(const char *s, const char *&next)
|
|
{
|
|
TypeKind tk;
|
|
|
|
switch (*s) {
|
|
case '[': {
|
|
Type &type = parseFieldDescriptor(s+1, next);
|
|
return *(const_cast<Type *>(static_cast<const Type *> (&type.getArrayType())));
|
|
}
|
|
|
|
case 'L': {
|
|
char *t = PL_strchr(++s, ';');
|
|
|
|
if (!t)
|
|
verifyError(VerifyError::badClassFormat);
|
|
|
|
Int32 len = t-s+1;
|
|
char *instanceClass = new char [len];
|
|
|
|
if (!instanceClass)
|
|
verifyError(VerifyError::badClassFormat);
|
|
|
|
PL_strncpy(instanceClass, s, len-1);
|
|
instanceClass[len-1] = 0;
|
|
next = t+1;
|
|
|
|
ClassFileSummary &summ = addClass(instanceClass);
|
|
delete [] instanceClass;
|
|
|
|
return *summ.getThisClass();
|
|
}
|
|
|
|
default:
|
|
if (!(getTypeKindFromDescriptor(*s, tk)))
|
|
verifyError(VerifyError::badClassFormat);
|
|
break;
|
|
}
|
|
|
|
/* If we're here, we're a primitive type */
|
|
Type &type = *(const_cast<Type *>(static_cast<const Type *>(&PrimitiveType::obtain(tk))));
|
|
next = s+1;
|
|
return type;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|