Path: blob/master/runtime/bcutil/ClassFileWriter.hpp
5985 views
/*******************************************************************************1* Copyright (c) 2001, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21/*22* ClassFileWriter.hpp23*/2425#ifndef CLASSFILEWRITER_HPP_26#define CLASSFILEWRITER_HPP_2728#include "cfr.h"29#include "hashtable_api.h"30#include "j9comp.h"31#include "j9port.h"32#include "ut_j9bcu.h"33#include "util_api.h"3435#include "BuildResult.hpp"36#include "VMHelpers.hpp"3738#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)39J9_DECLARE_CONSTANT_UTF8(injectedInvokerClassname, "InjectedInvoker");40#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */4142class ClassFileWriter {43/*44* Data members45*/46private:47/*48* Hash table assumption: everything that needs to be written to the .class file exists somewhere in:49* 1) the ROM class; or50* 2) program constants.51*52* One possible violation of this assumption is that primitive array names are encoded53* directly in stack map table's Object variable info entries rather than as UTF8s.54*/55struct HashTableEntry {56void * address;57U_16 cpIndex;58U_8 cpType;5960HashTableEntry(void * addr, U_16 index, U_8 type)61: address(addr)62, cpIndex(index)63, cpType(type)64{}65};6667J9JavaVM * _javaVM;68J9PortLibrary * _portLibrary;69J9ROMClass * _romClass;70U_8 * _classFileBuffer;71U_8 * _classFileCursor;72BuildResult _buildResult;73J9HashTable * _cpHashTable;74U_16 _constantPoolCount;75U_32 _bsmAttributeLength;76UDATA _classFileSize;77bool _isAnon;78bool _isInjectedInvoker;79J9UTF8* _anonClassName;80J9UTF8* _originalClassName;81#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)82U_32 _numOfInjectedInterfaces;83#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */8485protected:8687public:8889/*90* Function members91*/92private:9394static UDATA hashFunction(void * entry, void * userData)95{96/* TODO figure out if this is a good hash function - a lot of UTF-8 data may be spread around the address space */97HashTableEntry * e = (HashTableEntry *) entry;98switch (e->cpType) {99case CFR_CONSTANT_Double: /* fall through */100case CFR_CONSTANT_Long: {101U_32 * address = (U_32 *) e->address;102return address[0] ^ address[1] ^ UDATA(e->cpType);103}104case CFR_CONSTANT_Integer: /* fall through */105case CFR_CONSTANT_Float:106return (*(U_32 *) e->address) ^ UDATA(e->cpType);107case CFR_CONSTANT_Class:108case CFR_CONSTANT_Utf8: {109J9UTF8 *utf8 = (J9UTF8 *) e->address;110return VM_VMHelpers::computeHashForUTF8(J9UTF8_DATA(utf8), J9UTF8_LENGTH(utf8));111}112default:113/* Mix cpType into the 4th byte of the address, which is not likely to vary much within the ROM class */114return UDATA(e->address) ^ (UDATA(e->cpType) << 24);115}116}117118static UDATA equalFunction(void * leftEntry, void * rightEntry, void * userData)119{120HashTableEntry * left = (HashTableEntry *) leftEntry;121HashTableEntry * right = (HashTableEntry *) rightEntry;122123if (left->cpType != right->cpType) {124return 0;125}126127switch (left->cpType) {128case CFR_CONSTANT_Double: /* fall through */129case CFR_CONSTANT_Long:130return (*(U_64 *) left->address) == (*(U_64 *) right->address);131case CFR_CONSTANT_Integer: /* fall through */132case CFR_CONSTANT_Float:133return (*(U_32 *) left->address) == (*(U_32 *) right->address);134case CFR_CONSTANT_Class:135case CFR_CONSTANT_Utf8: {136J9UTF8 *utf81 = (J9UTF8 *) left->address;137J9UTF8 *utf82 = (J9UTF8 *) right->address;138return J9UTF8_EQUALS(utf81, utf82);139}140default:141return left->address == right->address;142}143}144145/* canFail is passed 'true' only when recreating StackMapTable attribute. See writeVerificationTypeInfo() */146U_16 indexForType(void * address, U_8 cpType, bool canFail = false)147{148HashTableEntry entry(address, 0, cpType);149HashTableEntry * result = (HashTableEntry *) hashTableFind(_cpHashTable, &entry);150if (NULL != result) {151return result->cpIndex;152}153if (!canFail) {154_buildResult = GenericError;155Trc_BCU_Assert_ShouldNeverHappen();156}157return 0;158}159160U_16 indexForUTF8(J9UTF8 * utf8) { return indexForType(utf8, CFR_CONSTANT_Utf8); }161U_16 indexForClass(J9UTF8 * utf8, bool canFail = false) { return indexForType(utf8, CFR_CONSTANT_Class, canFail); }162U_16 indexForDouble(U_32 * val) { return indexForType(val, CFR_CONSTANT_Double); }163U_16 indexForLong(U_32 * val) { return indexForType(val, CFR_CONSTANT_Long); }164U_16 indexForFloat(U_32 * val) { return indexForType(val, CFR_CONSTANT_Float); }165U_16 indexForInteger(U_32 * val) { return indexForType(val, CFR_CONSTANT_Integer); }166U_16 indexForNAS(J9ROMNameAndSignature * nas) { return indexForType(nas, CFR_CONSTANT_NameAndType); }167U_16 indexForInvokeDynamic(void * val) { return indexForType(val, CFR_CONSTANT_InvokeDynamic); }168169void addEntry(void * address, U_16 cpIndex, U_8 cpType)170{171HashTableEntry entry(address, cpIndex, cpType);172HashTableEntry * result = (HashTableEntry *) hashTableFind(_cpHashTable, &entry);173if (NULL == result) {174result = (HashTableEntry *) hashTableAdd(_cpHashTable, &entry);175if (NULL == result) {176_buildResult = OutOfMemory;177}178} else if ((0 == result->cpIndex) && (0 != cpIndex)) {179/* Update the existing entry to record the cpIndex - only expect this to happen for Annotation UTF8 entries in the ROM constant pool */180Trc_BCU_Assert_True(CFR_CONSTANT_Utf8 == cpType);181result->cpIndex = cpIndex;182}183}184185void addClassEntry(J9UTF8 * utf8, U_16 cpIndex)186{187addEntry(utf8, 0, CFR_CONSTANT_Utf8);188addEntry(utf8, cpIndex, CFR_CONSTANT_Class);189}190191void addNASEntry(J9ROMNameAndSignature * nas)192{193addEntry(J9ROMNAMEANDSIGNATURE_NAME(nas), 0,CFR_CONSTANT_Utf8);194addEntry(J9ROMNAMEANDSIGNATURE_SIGNATURE(nas), 0, CFR_CONSTANT_Utf8);195addEntry(nas, 0, CFR_CONSTANT_NameAndType);196}197198#ifdef J9VM_ENV_LITTLE_ENDIAN199void flip16bit(U_8 * code)200{201U_8 tmp = code[0];202code[0] = code[1];203code[1] = tmp;204}205206void flip32bit(U_8 * code)207{208U_8 tmp = code[0];209code[0] = code[3];210code[3] = tmp;211tmp = code[1];212code[1] = code[2];213code[2] = tmp;214}215#else /* J9VM_ENV_LITTLE_ENDIAN */216void flip16bit(U_8 * code) { /* nothing to do */ }217void flip32bit(U_8 * code) { /* nothing to do */ }218#endif /* J9VM_ENV_LITTLE_ENDIAN */219220void allocateBuffer()221{222PORT_ACCESS_FROM_PORT(_portLibrary);223224_classFileBuffer = (U_8 *) j9mem_allocate_memory(_romClass->classFileSize, OMRMEM_CATEGORY_VM);225if (NULL == _classFileBuffer) {226_buildResult = OutOfMemory;227}228_classFileCursor = _classFileBuffer;229}230231void writeU8(U_8 val)232{233*_classFileCursor = val;234_classFileCursor += sizeof(U_8);235}236237void writeU16(U_16 val)238{239U_16 * u16Addr = (U_16 *) _classFileCursor;240#ifdef J9VM_ENV_LITTLE_ENDIAN241*u16Addr = ((val & 0xff00) >> 8) | ((val & 0x00ff) << 8);242#else /* J9VM_ENV_LITTLE_ENDIAN */243*u16Addr = val;244#endif /* J9VM_ENV_LITTLE_ENDIAN */245_classFileCursor += sizeof(U_16);246}247248void writeU32(U_32 val)249{250U_32 * u32Addr = (U_32 *) _classFileCursor;251#ifdef J9VM_ENV_LITTLE_ENDIAN252*u32Addr = ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24);253#else /* J9VM_ENV_LITTLE_ENDIAN */254*u32Addr = val;255#endif /* J9VM_ENV_LITTLE_ENDIAN */256_classFileCursor += sizeof(U_32);257}258259void writeU8At(U_8 val, U_8 * address)260{261*address = val;262}263264void writeU16At(U_16 val, U_8 * address)265{266U_16 * u16Addr = (U_16 *) address;267#ifdef J9VM_ENV_LITTLE_ENDIAN268*u16Addr = ((val & 0xff00) >> 8) | ((val & 0x00ff) << 8);269#else /* J9VM_ENV_LITTLE_ENDIAN */270*u16Addr = val;271#endif /* J9VM_ENV_LITTLE_ENDIAN */272}273274void writeU32At(U_32 val, U_8 * address)275{276U_32 * u32Addr = (U_32 *) address;277#ifdef J9VM_ENV_LITTLE_ENDIAN278*u32Addr = ((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24);279#else /* J9VM_ENV_LITTLE_ENDIAN */280*u32Addr = val;281#endif /* J9VM_ENV_LITTLE_ENDIAN */282}283284void writeData(U_32 length, void * bytes)285{286memcpy(_classFileCursor, bytes, length);287_classFileCursor += length;288}289290/*291* Walk the ROM class, adding entries to the _cpHashTable for all referenced UTF8s & NASs, and all J9CPTYPE_INT, J9CPTYPE_LONG, J9CPTYPE_DOUBLE CP entries.292* If the hashtable or a hashtable entry can't be allocated, returns with _buildResult = OutOfMemory.293* If a class file CP entry can't be found, returns with _buildResult = GenericError.294*/295void analyzeROMClass();296void analyzeConstantPool();297void analyzeInterfaces();298void analyzeFields();299void analyzeMethods();300void analyzeRecordAttribute();301302void writeClassFile();303void writeConstantPool();304void writeInterfaces();305void writeField(J9ROMFieldShape * fieldShape);306void writeFields();307void writeMethod(J9ROMMethod * method);308void writeMethods();309void writeAttributes();310void writeCodeAttribute(J9ROMMethod * method);311void writeVerificationTypeInfo(U_16 count, U_8 ** typeInfo);312void writeStackMapTableAttribute(J9ROMMethod * romMethod);313void writeSignatureAttribute(J9UTF8 * genericSignature);314void writeAnnotationElement(U_8 **annotationElement);315void writeAnnotation(U_8 **annotation);316void writeAnnotationsAttribute(U_32 * annotationsData);317void writeParameterAnnotationsAttribute(U_32 *parameterAnnotationsData);318void writeTypeAnnotationsAttribute(U_32 *typeAnnotationsData);319void writeAnnotationDefaultAttribute(U_32 *annotationsDefaultData);320void writeAttributeHeader(J9UTF8 * name, U_32 length);321void writeRecordAttribute();322323/* Similar to ClassFileOracle::computeSendSlotCount().324* This is used to get the argument count for the method called using invokeinterface bytecode.325*/326U_8 computeArgsCount(U_16 methodRefIndex);327void rewriteBytecode(J9ROMMethod * method, U_32 length, U_8 * code);328329protected:330331public:332ClassFileWriter(J9JavaVM * javaVM, J9PortLibrary * portLibrary, J9ROMClass * romClass)333: _javaVM(javaVM)334, _portLibrary(portLibrary)335, _romClass(romClass)336, _classFileBuffer(NULL)337, _classFileCursor(NULL)338, _buildResult(OK)339, _cpHashTable(NULL)340, _constantPoolCount(romClass->romConstantPoolCount)341, _bsmAttributeLength(0)342, _classFileSize(0)343, _isAnon(FALSE)344, _isInjectedInvoker(FALSE)345, _anonClassName(NULL)346, _originalClassName(NULL)347#if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)348, _numOfInjectedInterfaces(0)349#endif /* J9VM_OPT_VALHALLA_VALUE_TYPES */350{351/* anonClasses have the following name format: '[originalName]/[ROMSegmentAddress]' */352if (J9_ARE_ANY_BITS_SET(_romClass->extraModifiers, J9AccClassAnonClass | J9AccClassHidden)) {353PORT_ACCESS_FROM_JAVAVM(_javaVM);354_isAnon = true;355_anonClassName = J9ROMCLASS_CLASSNAME(_romClass);356U_16 anonNameLength = J9UTF8_LENGTH(_anonClassName);357U_16 originalNameLength = anonNameLength - ROM_ADDRESS_LENGTH - 1;358U_8 *anonClassNameData = J9UTF8_DATA(_anonClassName);359#if defined(J9VM_OPT_OPENJDK_METHODHANDLE)360/* ROM class format: <HOST_NAME>/InjectedInvoker/<ROM_ADDRESS_LENGTH>.361* Search for InjectedInvoker in _anonClassName using the above format.362* If found, reset the class name to "InjectedInvoker" in the class file.363*/364IDATA startIndex = anonNameLength - J9UTF8_LENGTH(&injectedInvokerClassname) - ROM_ADDRESS_LENGTH - 1;365U_8 *start = anonClassNameData + startIndex;366if ((startIndex >= 0)367&& (0 == memcmp(start, J9UTF8_DATA(&injectedInvokerClassname), J9UTF8_LENGTH(&injectedInvokerClassname)))368) {369_isInjectedInvoker = TRUE;370originalNameLength = J9UTF8_LENGTH(&injectedInvokerClassname);371anonClassNameData = J9UTF8_DATA(&injectedInvokerClassname);372}373#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */374/* nameLength field + nameBytes field + NULL terminator */375_originalClassName = (J9UTF8 *)j9mem_allocate_memory(sizeof(U_16) + originalNameLength + 1, J9MEM_CATEGORY_CLASSES);376if (NULL == _originalClassName) {377_buildResult = OutOfMemory;378} else {379J9UTF8_SET_LENGTH(_originalClassName, originalNameLength);380memcpy(((U_8 *)J9UTF8_DATA(_originalClassName)), anonClassNameData, originalNameLength);381*(((U_8 *)J9UTF8_DATA(_originalClassName)) + originalNameLength) = '\0';382}383}384if (isOK()) {385analyzeROMClass();386}387if (isOK()) {388allocateBuffer();389}390if (isOK()) {391writeClassFile();392_classFileSize = UDATA(_classFileCursor - _classFileBuffer);393/* We should never overshoot _classFileBuffer size */394Trc_BCU_Assert_True(_classFileSize <= _romClass->classFileSize);395}396}397398~ClassFileWriter()399{400if (NULL != _cpHashTable) {401hashTableFree(_cpHashTable);402_cpHashTable = NULL;403}404405if (!isOK()) {406PORT_ACCESS_FROM_PORT(_portLibrary);407408/* If an error occurred during class file recreation, free the classFileBuffer */409j9mem_free_memory(_classFileBuffer);410_classFileBuffer = NULL;411}412/* Don't free if name is InjectedInvoker since it is static */413if (_isAnon && !_isInjectedInvoker) {414PORT_ACCESS_FROM_JAVAVM(_javaVM);415j9mem_free_memory(_originalClassName);416}417}418419UDATA classFileSize() { return isOK() ? _classFileSize : 0; }420U_8 * classFileData() { return isOK() ? _classFileBuffer : NULL; }421bool isOK() { return OK == _buildResult; }422BuildResult getResult() { return _buildResult; }423};424425#endif /* CLASSFILEWRITER_HPP_ */426427428