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