/******************************************************************************* * Copyright IBM Corp. and others 2001 * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ * or the Apache License, Version 2.0 which accompanies this distribution and * is available at https://www.apache.org/licenses/LICENSE-2.0. * * This Source Code may also be made available under the following * Secondary Licenses when the conditions for such availability set * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU * General Public License, version 2 with the GNU Classpath * Exception [1] and GNU General Public License, version 2 with the * OpenJDK Assembly Exception [2]. * * [1] https://www.gnu.org/software/classpath/license.html * [2] https://openjdk.org/legal/assembly-exception.html * * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 *******************************************************************************/ #include "ClasspathItem.hpp" #include "sharedconsts.h" #include "ut_j9shr.h" #include "j9shrnls.h" #include <stdlib.h> #include <string.h> #define CLASSPATHITEM_ERR_TRACE(var) j9nls_printf(PORTLIB, J9NLS_ERROR, var); #define HEADER_SIZE sizeof(ClasspathItem) #define CPEI_ARRAY_PTR_FROM_CPI(cpiPtr) (IDATA*)((BlockPtr)cpiPtr + HEADER_SIZE) #define UPDATE_CPEI_ARRAY_PTR_FROM_CPI(cpiPtr) (IDATA*)(cpiPtr += HEADER_SIZE) ClasspathEntryItem* ClasspathEntryItem::newInstance(const char* path, U_16 pathLen, UDATA protocol, ClasspathEntryItem* memForConstructor) { ClasspathEntryItem* newCPEI = (ClasspathEntryItem*)memForConstructor; new(newCPEI) ClasspathEntryItem(); if (newCPEI->initialize(path, pathLen, protocol)==0) { return newCPEI; } else { return NULL; } } IDATA ClasspathEntryItem::initialize(const char* path_, U_16 pathLen_, UDATA protocol_) { flags = 0; protocol = protocol_; /* important to initialize timestamp to -1 */ timestamp = -1; hashValue = 0; path = (char*)path_; pathLen = pathLen_; locationPathLen = pathLen_; if (protocol == PROTO_JAR) { if (NULL != path) { char* jarPath = strstr(path,"!/"); if (NULL == jarPath) { jarPath = strstr(path,"!\\"); } if (NULL != jarPath) { locationPathLen = jarPath - path; } } } return 0; } /* Assume we have enough space */ ClasspathEntryItem::BlockPtr ClasspathEntryItem::writeToAddress(BlockPtr block) { ClasspathEntryItem* cpeiInCache = (ClasspathEntryItem*)block; IDATA paddedPathSize = SHC_PAD(pathLen, SHC_WORDALIGN); BlockPtr pathInCache = block + sizeof(ClasspathEntryItem); memcpy(block, this, sizeof(ClasspathEntryItem)); strncpy(pathInCache, path, pathLen); /* Mark cache entry so that we know how to read it */ cpeiInCache->flags |= IS_IN_CACHE_FLAG; return (pathInCache + paddedPathSize); } /* If this is view on cache, return correct ptr. Otherwise, return known ptr. */ const char* ClasspathEntryItem::getPath(U_16* pathLen_) const { if (pathLen_) { *pathLen_ = (U_16)pathLen; } if ((flags & IS_IN_CACHE_FLAG)==0) { return path; } else { return (((BlockPtr)this) + sizeof(ClasspathEntryItem)); } } U_32 ClasspathEntryItem::getSizeNeeded(void) const { return (sizeof(ClasspathEntryItem) + SHC_PAD((U_32)pathLen, SHC_WORDALIGN)); } UDATA ClasspathEntryItem::hash(J9InternalVMFunctions* functionTable) { U_16 pathLen = 0; const char* path = getPath(&pathLen); if (!hashValue) { return (hashValue = (functionTable->computeHashForUTF8((U_8*)path, pathLen) + protocol)); } return hashValue; } const char* ClasspathEntryItem::getLocation(U_16* locationPathLen_) const { if (locationPathLen_) { *locationPathLen_ = (U_16)locationPathLen; } if ((flags & IS_IN_CACHE_FLAG)==0) { return path; } else { return (((BlockPtr)this) + sizeof(ClasspathEntryItem)); } } void ClasspathEntryItem::cleanup(void) { /* Currently no cleanup */ } ClasspathItem* ClasspathItem::newInstance(J9JavaVM* vm, IDATA entries_, IDATA helperID_, U_16 cpType_, ClasspathItem* memForConstructor) { ClasspathItem* newCPI = (ClasspathItem*)memForConstructor; new(newCPI) ClasspathItem(); newCPI->initialize(vm, entries_, helperID_, cpType_, ((BlockPtr)memForConstructor + sizeof(ClasspathItem))); return newCPI; } UDATA ClasspathItem::getRequiredConstrBytes(UDATA entries) { UDATA reqBytes = 0; reqBytes += sizeof(ClasspathItem); reqBytes += (entries * (sizeof(ClasspathEntryItem) + sizeof(ClasspathEntryItem*))); return reqBytes; } void ClasspathItem::initialize(J9JavaVM* vm, IDATA entries_, IDATA helperID_, U_16 cpType_, BlockPtr memForConstructor) { type = cpType_; flags = 0; entries = entries_; portlib = vm->portLibrary; helperID = helperID_; itemsAdded = 0; firstDirIndex = -1; hashValue = 0; jarsLockedToIndex = -1; Trc_SHR_CPI_initialize_Entry(helperID_, entries_, cpType_); items = (ClasspathEntryItem**)memForConstructor; for (IDATA i=0; i<entries_; i++) { items[i] = (ClasspathEntryItem*)(((UDATA)items) + ((entries_ * sizeof(ClasspathEntryItem*)) + (sizeof(ClasspathEntryItem) * i))); } Trc_SHR_CPI_initialize_Exit(); } /* Returns -1 if addItem fails otherwise total number of items added */ IDATA ClasspathItem::addItem(J9InternalVMFunctions* functionTable, const char* path, U_16 pathLen, UDATA protocol) { ClasspathEntryItem* current = NULL; Trc_SHR_CPI_addItem_Entry(pathLen, path, protocol); if (entries==itemsAdded) { /* Cannot access verbose level, so this is not suppressed by "silent". However, it's a "should never happen" message. */ PORT_ACCESS_FROM_PORT(portlib); CLASSPATHITEM_ERR_TRACE(J9NLS_SHRC_CPI_TOO_MANY_ITEMS); Trc_SHR_CPI_addItem_ExitTooMany(); Trc_SHR_Assert_ShouldNeverHappen(); return -1; } current = ClasspathEntryItem::newInstance(path, pathLen, protocol, items[itemsAdded]); if (!current) { Trc_SHR_CPI_addItem_ExitError(); return -1; } if (protocol==PROTO_DIR && firstDirIndex==-1) { firstDirIndex = itemsAdded; } hashValue += current->hash(functionTable); ++itemsAdded; Trc_SHR_CPI_addItem_Exit(itemsAdded); return itemsAdded; } /* Either knows directly about its items, or calculates using offsets in the cache */ ClasspathEntryItem* ClasspathItem::itemAt(I_16 i) const { Trc_SHR_CPI_itemAt_Entry(i); if (i>=itemsAdded) { Trc_SHR_CPI_itemAt_ExitError(itemsAdded); Trc_SHR_Assert_ShouldNeverHappen(); return NULL; } if (!(flags & IS_IN_CACHE_FLAG)) { if (!items) { Trc_SHR_CPI_itemAt_NotInitialized(); return NULL; /* Not initialized yet */ } else { Trc_SHR_CPI_itemAt_ExitLocal(); return items[i]; } } else { IDATA* offsets = CPEI_ARRAY_PTR_FROM_CPI(this); Trc_SHR_CPI_itemAt_ExitInCache(); return (ClasspathEntryItem*)((BlockPtr)this + offsets[i]); } } I_16 ClasspathItem::find(J9InternalVMFunctions* functionTable, ClasspathEntryItem* test) const { return find(functionTable, test, -1); } /* Does not compare timestamps or flags */ bool /* static */ ClasspathItem::compare(J9InternalVMFunctions* functionTable, ClasspathEntryItem* test, ClasspathEntryItem* compareTo) { U_16 testPathLen = 0; U_16 comparePathLen = 0; U_8* testPath = NULL; U_8* comparePath = NULL; UDATA hash1, hash2; Trc_SHR_CPI_compare_CPEI_Entry(test, compareTo); if (test==compareTo) { Trc_SHR_CPI_compare_CPEI_ExitSame(); return true; } if (test==NULL || compareTo==NULL) { Trc_SHR_CPI_compare_CPEI_ExitNull(); Trc_SHR_Assert_ShouldNeverHappen(); return false; } testPath = (U_8*)test->getPath(&testPathLen); comparePath = (U_8*)compareTo->getPath(&comparePathLen); Trc_SHR_CPI_compare_CPEI_Paths(testPathLen, testPath, comparePathLen, comparePath); hash1 = test->hash(functionTable); hash2 = compareTo->hash(functionTable); if (hash1 != hash2) { Trc_SHR_CPI_compare_CPEI_ExitHash(hash1, hash2); return false; } if (test->protocol != compareTo->protocol) { Trc_SHR_CPI_compare_CPEI_ExitProtocol(test->protocol, compareTo->protocol); return false; } if (!J9UTF8_DATA_EQUALS(testPath, testPathLen, comparePath, comparePathLen)) { Trc_SHR_CPI_compare_CPEI_ExitCompare(); return false; } Trc_SHR_CPI_compare_CPEI_ExitSuccess(); return true; } /* NOTE: Should not compare getType(). A 1-element URL should be the same as a 1-element Classpath. */ bool /* static */ ClasspathItem::compare(J9InternalVMFunctions* functionTable, ClasspathItem* test, ClasspathItem* compareTo) { Trc_SHR_CPI_compare_Entry(test, compareTo); if (test==compareTo) { Trc_SHR_CPI_compare_ExitTrue1(); return true; } if (test==NULL || compareTo==NULL) { Trc_SHR_CPI_compare_ExitNullError(); Trc_SHR_Assert_ShouldNeverHappen(); return false; } if (test->getItemsAdded()!=compareTo->getItemsAdded()) { Trc_SHR_CPI_compare_ExitFalse1(); return false; } if (test->getHashCode()!=compareTo->getHashCode()) { Trc_SHR_CPI_compare_ExitFalse2(); return false; } for (I_16 i=0; i<(I_16)test->itemsAdded; i++) { if (!compare(functionTable, test->itemAt(i), compareTo->itemAt(i))) { Trc_SHR_CPI_compare_ExitFalse4(i); return false; } } Trc_SHR_CPI_compare_ExitTrue2(); return true; } /* PERFORMANCE: Since most classpath matching will be done comparing the same classpath, * the quickest patch to compare is to work right to left, only starting at the index * we're interested in. */ I_16 ClasspathItem::find(J9InternalVMFunctions* functionTable, ClasspathEntryItem* test, I_16 stopAtIndex) const { I_16 countFrom = 0; Trc_SHR_CPI_find_Entry(test, stopAtIndex); /* stopAtIndex should hopefully never be greater than itemsAdded! */ if ((stopAtIndex==-1) || (stopAtIndex >= (I_16)itemsAdded)) { countFrom = (I_16)itemsAdded - 1; } else { countFrom = stopAtIndex; } for (I_16 i=countFrom; i>=0; i--) { if (compare(functionTable, itemAt(i), test)) { Trc_SHR_CPI_find_ExitFound(i); return i; } } Trc_SHR_CPI_find_ExitNotFound(); return -1; } IDATA ClasspathItem::getMaxItems(void) const { return entries; } I_16 ClasspathItem::getItemsAdded(void) const { return (I_16)itemsAdded; } /* Assume we have enough memory at block to write whole item */ IDATA ClasspathItem::writeToAddress(BlockPtr block) { BlockPtr cursor = block; IDATA* arrayPtr = NULL; /* Array of offsets */ ClasspathItem* cpiInCache = (ClasspathItem*)block; Trc_SHR_CPI_writeToAddress_Entry(block); memcpy(cpiInCache, this, sizeof(ClasspathItem)); arrayPtr = UPDATE_CPEI_ARRAY_PTR_FROM_CPI(cursor); cursor += itemsAdded * (sizeof(IDATA)); for (I_16 i=0; i<(I_16)itemsAdded; i++) { /* store the offset */ *(arrayPtr++) = (cursor - block); cursor = itemAt(i)->writeToAddress(cursor); } /* Indicate that this is stored in cache */ cpiInCache->flags |= IS_IN_CACHE_FLAG; Trc_SHR_CPI_writeToAddress_Exit(); return 0; } /* Returns amount of size needed in cache to write out complete ClasspathItem */ U_32 ClasspathItem::getSizeNeeded(void) const { U_32 total = HEADER_SIZE + ((U_32)itemsAdded*sizeof(IDATA)); for (I_16 i=0; i<(I_16)itemsAdded; i++) { total += itemAt(i)->getSizeNeeded(); } return total; } /* Only valid/interesting for ClasspathItems created locally (not in cache) */ IDATA ClasspathItem::getHelperID(void) const { if (!(flags & IS_IN_CACHE_FLAG)) { return helperID; } else { return HELPERID_NOT_APPLICABLE; } } IDATA ClasspathItem::getFirstDirIndex(void) const { return firstDirIndex; } UDATA ClasspathItem::getHashCode(void) const { return hashValue; } UDATA ClasspathItem::getType(void) const { return type; } bool ClasspathItem::isInCache(void) const { return ((flags & IS_IN_CACHE_FLAG) != 0); } void ClasspathItem::setJarsLockedToIndex(I_16 i) { if (!(flags & IS_IN_CACHE_FLAG)) { jarsLockedToIndex = i; } } I_16 ClasspathItem::getJarsLockedToIndex(void) const { if (!(flags & IS_IN_CACHE_FLAG)) { return (I_16)jarsLockedToIndex; } else { return -1; } } void ClasspathItem::cleanup(void) { if (!(flags & IS_IN_CACHE_FLAG)) { if (items) { for (int i=0; i<itemsAdded; i++) { items[i]->cleanup(); } } } }