26
26
27
27
package com .oracle .objectfile .debugentry ;
28
28
29
+ import java .util .ArrayList ;
29
30
import java .util .Comparator ;
30
- import java .util .HashMap ;
31
31
import java .util .List ;
32
32
import java .util .Map ;
33
- import java .util .concurrent .ConcurrentSkipListSet ;
33
+ import java .util .stream .Collectors ;
34
+ import java .util .stream .IntStream ;
34
35
35
36
import com .oracle .objectfile .debugentry .range .Range ;
36
37
@@ -53,26 +54,32 @@ public sealed class ClassEntry extends StructureTypeEntry permits EnumClassEntry
53
54
/**
54
55
* Details of methods located in this instance.
55
56
*/
56
- private final ConcurrentSkipListSet <MethodEntry > methods ;
57
+ private List <MethodEntry > methods ;
57
58
/**
58
59
* A list recording details of all normal compiled methods included in this class sorted by
59
60
* ascending address range. Note that the associated address ranges are disjoint and contiguous.
60
61
*/
61
- private final ConcurrentSkipListSet <CompiledMethodEntry > compiledMethods ;
62
+ private List <CompiledMethodEntry > compiledMethods ;
62
63
63
64
/**
64
- * A list of all files referenced from info associated with this class, including info detailing
65
- * inline method ranges.
65
+ * A map of all files referenced from info associated with this class, including info detailing
66
+ * inline method ranges. Each unique file is mapped to its 1-based index in this class.
66
67
*/
67
- private final ConcurrentSkipListSet <FileEntry > files ;
68
- private final Map <FileEntry , Integer > indexedFiles = new HashMap <>();
68
+ private Map <FileEntry , Integer > indexedFiles = null ;
69
+ /**
70
+ * The list of all files referenced from this class.
71
+ */
72
+ private List <FileEntry > files ;
69
73
70
74
/**
71
- * A list of all directories referenced from info associated with this class, including info
72
- * detailing inline method ranges.
75
+ * A map of all directories referenced from info associated with this class, including info
76
+ * detailing inline method ranges. Each unique dir is mapped to its 1-based index in this class.
77
+ */
78
+ private Map <DirEntry , Integer > indexedDirs = null ;
79
+ /**
80
+ * The list of all dirs referenced from this class.
73
81
*/
74
- private final ConcurrentSkipListSet <DirEntry > dirs ;
75
- private final Map <DirEntry , Integer > indexedDirs = new HashMap <>();
82
+ private List <DirEntry > dirs ;
76
83
77
84
public ClassEntry (String typeName , int size , long classOffset , long typeSignature ,
78
85
long compressedTypeSignature , long layoutTypeSignature ,
@@ -81,20 +88,60 @@ public ClassEntry(String typeName, int size, long classOffset, long typeSignatur
81
88
this .superClass = superClass ;
82
89
this .fileEntry = fileEntry ;
83
90
this .loader = loader ;
84
- this .methods = new ConcurrentSkipListSet <>(Comparator . comparingInt ( MethodEntry :: getModifiers ). thenComparingInt ( MethodEntry :: getLine ). thenComparing ( MethodEntry :: getSymbolName ) );
85
- this .compiledMethods = new ConcurrentSkipListSet <>(Comparator . comparing ( CompiledMethodEntry :: primary ) );
86
- this .files = new ConcurrentSkipListSet <>(Comparator . comparing ( FileEntry :: fileName ). thenComparing ( file -> file . dirEntry (). path ()) );
87
- this .dirs = new ConcurrentSkipListSet <>(Comparator . comparing ( DirEntry :: path ) );
91
+ this .methods = new ArrayList <>();
92
+ this .compiledMethods = new ArrayList <>();
93
+ this .files = new ArrayList <>();
94
+ this .dirs = new ArrayList <>();
88
95
89
96
addFile (fileEntry );
90
97
}
91
98
99
+ @ Override
100
+ public void seal () {
101
+ super .seal ();
102
+ assert methods instanceof ArrayList <MethodEntry > && compiledMethods instanceof ArrayList <CompiledMethodEntry > &&
103
+ files instanceof ArrayList <FileEntry > && indexedFiles == null && dirs instanceof ArrayList <DirEntry > &&
104
+ indexedDirs == null : "ClassEntry should only be sealed once" ;
105
+ methods = List .copyOf (methods );
106
+ methods .forEach (MethodEntry ::seal );
107
+
108
+ compiledMethods = compiledMethods .stream ().sorted (Comparator .comparing (CompiledMethodEntry ::primary )).toList ();
109
+ compiledMethods .forEach (CompiledMethodEntry ::seal );
110
+ compiledMethods .stream ()
111
+ .flatMap (cm -> cm .topDownRangeStream (true ).map (Range ::getFileEntry ))
112
+ .distinct ()
113
+ .forEach (this ::addFile );
114
+
115
+ files = files .stream ().distinct ().toList ();
116
+ indexedFiles = IntStream .range (0 , files .size ())
117
+ .boxed ()
118
+ .collect (Collectors .toUnmodifiableMap (i -> files .get (i ), i -> i + 1 ));
119
+
120
+ dirs = dirs .stream ().distinct ().toList ();
121
+ indexedDirs = IntStream .range (0 , dirs .size ())
122
+ .boxed ()
123
+ .collect (Collectors .toUnmodifiableMap (i -> dirs .get (i ), i -> i + 1 ));
124
+ }
125
+
126
+ /**
127
+ * Adds and indexes a file entry and the corresponding dir entry for this class entry.
128
+ * <p>
129
+ * This is only called during debug info generation. No more files are added to this
130
+ * {@code ClassEntry} when writing debug info to the object file.
131
+ *
132
+ * @param addFileEntry the file entry to add
133
+ */
92
134
private void addFile (FileEntry addFileEntry ) {
135
+ assert files instanceof ArrayList <FileEntry > && dirs instanceof ArrayList <DirEntry > : "Can only add files and dirs before a ClassEntry is sealed." ;
93
136
if (addFileEntry != null && !addFileEntry .fileName ().isEmpty ()) {
94
- files .add (addFileEntry );
137
+ synchronized (files ) {
138
+ files .add (addFileEntry );
139
+ }
95
140
DirEntry addDirEntry = addFileEntry .dirEntry ();
96
141
if (addDirEntry != null && !addDirEntry .getPathString ().isEmpty ()) {
97
- dirs .add (addDirEntry );
142
+ synchronized (dirs ) {
143
+ dirs .add (addDirEntry );
144
+ }
98
145
}
99
146
}
100
147
}
@@ -112,26 +159,34 @@ public void addField(FieldEntry field) {
112
159
113
160
/**
114
161
* Add a method to the class entry and store its file entry.
162
+ * <p>
163
+ * This is only called during debug info generation. No more methods are added to this
164
+ * {@code ClassEntry} when writing debug info to the object file.
115
165
*
116
166
* @param methodEntry the {@code MethodEntry} to add
117
167
*/
118
168
public void addMethod (MethodEntry methodEntry ) {
169
+ assert methods instanceof ArrayList <MethodEntry > : "Can only add methods before a ClassEntry is sealed." ;
119
170
addFile (methodEntry .getFileEntry ());
120
- methods .add (methodEntry );
171
+ synchronized (methods ) {
172
+ methods .add (methodEntry );
173
+ }
121
174
}
122
175
123
176
/**
124
177
* Add a compiled method to the class entry and store its file entry and the file entries of
125
178
* inlined methods.
179
+ * <p>
180
+ * This is only called during debug info generation. No more compiled methods are added to this
181
+ * {@code ClassEntry} when writing debug info to the object file.
126
182
*
127
183
* @param compiledMethodEntry the {@code CompiledMethodEntry} to add
128
184
*/
129
185
public void addCompiledMethod (CompiledMethodEntry compiledMethodEntry ) {
130
- addFile ( compiledMethodEntry . primary (). getFileEntry ()) ;
131
- for ( Range range : compiledMethodEntry . topDownRangeStream (). toList () ) {
132
- addFile ( range . getFileEntry () );
186
+ assert compiledMethods instanceof ArrayList < CompiledMethodEntry > : "Can only add compiled methods before a ClassEntry is sealed." ;
187
+ synchronized ( compiledMethods ) {
188
+ compiledMethods . add ( compiledMethodEntry );
133
189
}
134
- compiledMethods .add (compiledMethodEntry );
135
190
}
136
191
137
192
public String getFileName () {
@@ -173,30 +228,19 @@ public int getDirIdx() {
173
228
174
229
/**
175
230
* Returns the file index of a given file entry within this class entry.
176
- *
177
231
* <p>
178
- * The first time a file entry is fetched, this produces a file index that is used for further
179
- * index lookups. The file index is only created once. Therefore, this method must be used only
180
- * after debug info generation is finished and no more file entries can be added to this class
181
- * entry.
182
- *
232
+ * This method is only called once all debug info entries are produced, the class entry and the
233
+ * file index was generated.
234
+ *
183
235
* @param file the given file entry
184
236
* @return the index of the file entry
185
237
*/
186
238
public int getFileIdx (FileEntry file ) {
187
- if (file == null || files .isEmpty () || !files .contains (file )) {
239
+ assert indexedFiles != null : "Can only request file index after a ClassEntry is sealed." ;
240
+ if (file == null || !indexedFiles .containsKey (file )) {
188
241
return 0 ;
189
242
}
190
243
191
- // Create a file index for all files in this class entry
192
- if (indexedFiles .isEmpty ()) {
193
- int index = 1 ;
194
- for (FileEntry f : getFiles ()) {
195
- indexedFiles .put (f , index );
196
- index ++;
197
- }
198
- }
199
-
200
244
return indexedFiles .get (file );
201
245
}
202
246
@@ -214,30 +258,19 @@ public int getDirIdx(FileEntry file) {
214
258
215
259
/**
216
260
* Returns the dir index of a given dir entry within this class entry.
217
- *
218
261
* <p>
219
- * The first time a dir entry is fetched, this produces a dir index that is used for further
220
- * index lookups. The dir index is only created once. Therefore, this method must be used only
221
- * after debug info generation is finished and no more dir entries can be added to this class
222
- * entry.
262
+ * This method is only called once all debug info entries are produced, the class entry and the
263
+ * dir index was generated.
223
264
*
224
265
* @param dir the given dir entry
225
266
* @return the index of the dir entry
226
267
*/
227
268
public int getDirIdx (DirEntry dir ) {
228
- if (dir == null || dir .getPathString ().isEmpty () || dirs .isEmpty () || !dirs .contains (dir )) {
269
+ assert indexedDirs != null : "Can only request dir index after a ClassEntry is sealed." ;
270
+ if (dir == null || !indexedDirs .containsKey (dir )) {
229
271
return 0 ;
230
272
}
231
273
232
- // Create a dir index for all dirs in this class entry
233
- if (indexedDirs .isEmpty ()) {
234
- int index = 1 ;
235
- for (DirEntry d : getDirs ()) {
236
- indexedDirs .put (d , index );
237
- index ++;
238
- }
239
- }
240
-
241
274
return indexedDirs .get (dir );
242
275
}
243
276
@@ -246,15 +279,17 @@ public String getLoaderId() {
246
279
}
247
280
248
281
/**
249
- * Retrieve a list of all compiled method entries for this class.
282
+ * Retrieve a list of all compiled method entries for this class, sorted by start address .
250
283
*
251
- * @return a list of all compiled method entries for this class.
284
+ * @return a {@code List} of all compiled method entries for this class
252
285
*/
253
286
public List <CompiledMethodEntry > compiledMethods () {
254
- return List .copyOf (compiledMethods );
287
+ assert !(compiledMethods instanceof ArrayList <CompiledMethodEntry >) : "Can only access compiled methods after a ClassEntry is sealed." ;
288
+ return compiledMethods ;
255
289
}
256
290
257
291
public boolean hasCompiledMethods () {
292
+ assert !(compiledMethods instanceof ArrayList <CompiledMethodEntry >) : "Can only access compiled methods after a ClassEntry is sealed." ;
258
293
return !compiledMethods .isEmpty ();
259
294
}
260
295
@@ -263,7 +298,8 @@ public ClassEntry getSuperClass() {
263
298
}
264
299
265
300
public List <MethodEntry > getMethods () {
266
- return List .copyOf (methods );
301
+ assert !(methods instanceof ArrayList <MethodEntry >) : "Can only access methods after a ClassEntry is sealed." ;
302
+ return methods ;
267
303
}
268
304
269
305
/**
@@ -274,7 +310,7 @@ public List<MethodEntry> getMethods() {
274
310
*/
275
311
public long lowpc () {
276
312
assert hasCompiledMethods ();
277
- return compiledMethods .first ().primary ().getLo ();
313
+ return compiledMethods .getFirst ().primary ().getLo ();
278
314
}
279
315
280
316
/**
@@ -284,9 +320,10 @@ public long lowpc() {
284
320
*
285
321
* @return the highest code section offset for compiled method code belonging to this class
286
322
*/
323
+ @ SuppressWarnings ("unused" )
287
324
public long hipc () {
288
325
assert hasCompiledMethods ();
289
- return compiledMethods .last ().primary ().getHi ();
326
+ return compiledMethods .getLast ().primary ().getHi ();
290
327
}
291
328
292
329
/**
@@ -296,7 +333,8 @@ public long hipc() {
296
333
* @return a list of all referenced files
297
334
*/
298
335
public List <FileEntry > getFiles () {
299
- return List .copyOf (files );
336
+ assert !(files instanceof ArrayList <FileEntry >) : "Can only access files after a ClassEntry is sealed." ;
337
+ return files ;
300
338
}
301
339
302
340
/**
@@ -306,6 +344,7 @@ public List<FileEntry> getFiles() {
306
344
* @return a list of all referenced directories
307
345
*/
308
346
public List <DirEntry > getDirs () {
309
- return List .copyOf (dirs );
347
+ assert !(dirs instanceof ArrayList <DirEntry >) : "Can only access dir after a ClassEntry is sealed." ;
348
+ return dirs ;
310
349
}
311
350
}
0 commit comments