/* -*- 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::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 &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 &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 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(static_cast (&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(static_cast(&PrimitiveType::obtain(tk)))); next = s+1; return type; }