1 /*
2 * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.javac.jvm;
27
28 import com.sun.tools.javac.code.Kinds.Kind;
29 import com.sun.tools.javac.code.Symbol;
30 import com.sun.tools.javac.code.Symbol.ClassSymbol;
31 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
32 import com.sun.tools.javac.code.Symbol.MethodHandleSymbol;
33 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
34 import com.sun.tools.javac.code.Symbol.PackageSymbol;
35 import com.sun.tools.javac.code.Type;
36 import com.sun.tools.javac.code.Types;
37 import com.sun.tools.javac.jvm.ClassWriter.PoolOverflow;
38 import com.sun.tools.javac.jvm.ClassWriter.StringOverflow;
39 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant;
40 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant.BasicConstant;
41 import com.sun.tools.javac.jvm.PoolConstant.Dynamic;
42 import com.sun.tools.javac.jvm.PoolConstant.Dynamic.BsmKey;
43 import com.sun.tools.javac.jvm.PoolConstant.NameAndType;
44 import com.sun.tools.javac.util.ByteBuffer;
45 import com.sun.tools.javac.util.InvalidUtfException;
46 import com.sun.tools.javac.util.List;
47 import com.sun.tools.javac.util.Name;
48 import com.sun.tools.javac.util.Names;
49
50 import java.io.IOException;
51 import java.io.OutputStream;
52 import java.util.ArrayDeque;
53 import java.util.HashMap;
54 import java.util.HashSet;
55 import java.util.LinkedHashMap;
56 import java.util.LinkedHashSet;
57 import java.util.Map;
58 import java.util.Set;
59
60 import static com.sun.tools.javac.code.Kinds.Kind.TYP;
61 import static com.sun.tools.javac.code.TypeTag.ARRAY;
62 import static com.sun.tools.javac.code.TypeTag.CLASS;
63 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Class;
64 import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_MethodType;
65 import static com.sun.tools.javac.jvm.ClassFile.externalize;
66
67 /**
68 * Pool interface towards {@code ClassWriter}. Exposes methods to encode and write javac entities
69 * into the constant pool.
70 *
71 * <p><b>This is NOT part of any supported API.
72 * If you write code that depends on this, you do so at your own risk.
73 * This code and its internal interfaces are subject to change or
74 * deletion without notice.</b>
75 */
76 public class PoolWriter {
77
78 /** Max number of constant pool entries. */
79 public static final int MAX_ENTRIES = 0xFFFF;
80
81 /** Max number of char in a string constant. */
82 public static final int MAX_STRING_LENGTH = 0xFFFF;
83
84 private static final int POOL_BUF_SIZE = 0x7fff;
85
86 private final Types types;
87
88 private final Names names;
89
90 /** Pool helper **/
91 final WriteablePoolHelper pool;
92
93 /** Sole signature generator */
94 final SharedSignatureGenerator signatureGen;
95
96 /** The inner classes to be written, as an ordered set (enclosing first). */
97 LinkedHashSet<ClassSymbol> innerClasses = new LinkedHashSet<>();
98
99 Set<Symbol> loadableDescriptors = new HashSet<>();
100
101 /** The list of entries in the BootstrapMethods attribute. */
102 Map<BsmKey, Integer> bootstrapMethods = new LinkedHashMap<>();
103
104 public PoolWriter(Types types, Names names) {
105 this.types = types;
106 this.names = names;
107 this.signatureGen = new SharedSignatureGenerator();
108 this.pool = new WriteablePoolHelper();
109 }
110
111 /**
112 * Puts a class symbol into the pool and return its index.
113 */
114 int putClass(ClassSymbol csym) {
115 return putClass(csym.type);
116 }
117
118 /**
119 * Puts a type into the pool and return its index. The type could be either a class, a type variable
120 * or an array type.
121 */
122 int putClass(Type t) {
123 return pool.writeIfNeeded(types.erasure(t));
124 }
125
126 /**
127 * Puts a member reference into the constant pool. Valid members are either field or method symbols.
128 */
129 int putMember(Symbol s) {
130 return pool.writeIfNeeded(s);
131 }
132
133 /**
134 * Puts a dynamic reference into the constant pool and return its index.
135 */
136 int putDynamic(Dynamic d) {
137 return pool.writeIfNeeded(d);
138 }
139
140 /**
141 * Puts a field or method descriptor into the constant pool and return its index.
142 */
143 int putDescriptor(Type t) {
144 return putName(typeSig(types.erasure(t)));
145 }
146
147 /**
148 * Puts a field or method descriptor into the constant pool and return its index.
149 */
150 int putDescriptor(Symbol s) {
151 return putDescriptor(descriptorType(s));
152 }
153
154 /**
155 * Puts a signature (see {@code Signature} attribute in JVMS 4.4) into the constant pool and
156 * return its index.
157 */
158 int putSignature(Symbol s) {
159 if (s.kind == TYP) {
160 return putName(classSig(s.type));
161 } else {
162 return putName(typeSig(s.type));
163 }
164 }
165
166 /**
167 * Puts a constant value into the pool and return its index. Supported values are int, float, long,
168 * double and String.
169 */
170 int putConstant(Object o) {
171 if (o instanceof Integer intVal) {
172 return putConstant(LoadableConstant.Int(intVal));
173 } else if (o instanceof Float floatVal) {
174 return putConstant(LoadableConstant.Float(floatVal));
175 } else if (o instanceof Long longVal) {
176 return putConstant(LoadableConstant.Long(longVal));
177 } else if (o instanceof Double doubleVal) {
178 return putConstant(LoadableConstant.Double(doubleVal));
179 } else if (o instanceof String strVal) {
180 return putConstant(LoadableConstant.String(strVal));
181 } else {
182 throw new AssertionError("unexpected constant: " + o);
183 }
184 }
185
186 /**
187 * Puts a constant into the pool and return its index.
188 */
189 int putConstant(LoadableConstant c) {
190 switch (c.poolTag()) {
191 case CONSTANT_Class:
192 return putClass((Type)c);
193 case CONSTANT_MethodType:
194 return pool.writeIfNeeded(types.erasure((Type)c));
195 default:
196 return pool.writeIfNeeded(c);
197 }
198 }
199
200 int putName(Name name) {
201 return pool.writeIfNeeded(name);
202 }
203
204 /**
205 * Puts a name and type pair into the pool and returns its index.
206 */
207 int putNameAndType(Symbol s) {
208 return pool.writeIfNeeded(new NameAndType(s.name, descriptorType(s)));
209 }
210
211 /**
212 * Puts a package entry into the pool and returns its index.
213 */
214 int putPackage(PackageSymbol pkg) {
215 return pool.writeIfNeeded(pkg);
216 }
217
218 /**
219 * Puts a module entry into the pool and returns its index.
220 */
221 int putModule(ModuleSymbol mod) {
222 return pool.writeIfNeeded(mod);
223 }
224
225 /**
226 * Enter an inner class into the `innerClasses' set.
227 */
228 void enterInner(ClassSymbol c) {
229 if (c.type.isCompound()) {
230 throw new AssertionError("Unexpected intersection type: " + c.type);
231 }
232 c.complete();
233 if (c.owner.enclClass() != null && !innerClasses.contains(c)) {
234 enterInner(c.owner.enclClass());
235 innerClasses.add(c);
236 }
237 }
238
239 /** Enter a value class into the `loadableDescriptorsClasses' set.
240 */
241 void enterLoadableDescriptorsClass(Symbol c) {
242 if (c.type.isCompound()) {
243 throw new AssertionError("Unexpected intersection type: " + c.type);
244 }
245 c.complete();
246 loadableDescriptors.add(c);
247 }
248
249 /**
250 * Create a new Utf8 entry representing a descriptor for given (member) symbol.
251 */
252 private Type descriptorType(Symbol s) {
253 return s.kind == Kind.MTH ? s.externalType(types) : s.erasure(types);
254 }
255
256 private int makeBootstrapEntry(Dynamic dynamic) {
257 BsmKey bsmKey = dynamic.bsmKey(types);
258
259 // Figure out the index for existing BSM; create a new BSM if no key
260 Integer index = bootstrapMethods.get(bsmKey);
261 if (index == null) {
262 index = bootstrapMethods.size();
263 bootstrapMethods.put(bsmKey, index);
264 }
265
266 return index;
267 }
268
269 /**
270 * Write pool contents into given byte buffer.
271 */
272 void writePool(OutputStream out) throws IOException, PoolOverflow {
273 if (pool.overflowString != null) {
274 throw new StringOverflow(pool.overflowString);
275 }
276 int size = size();
277 if (size > MAX_ENTRIES) {
278 throw new PoolOverflow();
279 }
280 out.write(size >> 8);
281 out.write(size);
282 out.write(pool.poolbuf.elems, 0, pool.poolbuf.length);
283 }
284
285 /**
286 * Signature Generation
287 */
288 class SharedSignatureGenerator extends Types.SignatureGenerator {
289
290 /**
291 * An output buffer for type signatures.
292 */
293 ByteBuffer sigbuf = new ByteBuffer();
294
295 SharedSignatureGenerator() {
296 types.super();
297 }
298
299 /**
300 * Assemble signature of given type in string buffer.
301 * Check for uninitialized types before calling the general case.
302 */
303 @Override
304 public void assembleSig(Type type) {
305 switch (type.getTag()) {
306 case UNINITIALIZED_THIS:
307 case UNINITIALIZED_OBJECT:
308 // we don't yet have a spec for uninitialized types in the
309 // local variable table
310 assembleSig(types.erasure(((UninitializedType)type).qtype));
311 break;
312 default:
313 super.assembleSig(type);
314 }
315 }
316
317 @Override
318 protected void append(char ch) {
319 sigbuf.appendByte(ch);
320 }
321
322 @Override
323 protected void append(byte[] ba) {
324 sigbuf.appendBytes(ba);
325 }
326
327 @Override
328 protected void append(Name name) {
329 sigbuf.appendName(name);
330 }
331
332 @Override
333 protected void classReference(ClassSymbol c) {
334 enterInner(c);
335 }
336
337 protected void reset() {
338 sigbuf.reset();
339 }
340
341 protected Name toName() {
342 try {
343 return sigbuf.toName(names);
344 } catch (InvalidUtfException e) {
345 throw new AssertionError(e);
346 }
347 }
348 }
349
350 class WriteablePoolHelper {
351
352 /** Pool entries. */
353 private final Map<Object, Integer> keysToPos = new HashMap<>(64);
354
355 final ByteBuffer poolbuf = new ByteBuffer(POOL_BUF_SIZE);
356
357 int currentIndex = 1;
358
359 ArrayDeque<PoolConstant> todo = new ArrayDeque<>();
360
361 String overflowString = null;
362
363 private <P extends PoolConstant> int writeIfNeeded(P p) {
364 Object key = p.poolKey(types);
365 Integer index = keysToPos.get(key);
366 if (index == null) {
367 keysToPos.put(key, index = currentIndex++);
368 boolean first = todo.isEmpty();
369 todo.addLast(p);
370 if (first) {
371 while (!todo.isEmpty()) {
372 writeConstant(todo.peekFirst());
373 todo.removeFirst();
374 }
375 }
376 }
377 return index;
378 }
379
380 void writeConstant(PoolConstant c) {
381 int tag = c.poolTag();
382 switch (tag) {
383 case ClassFile.CONSTANT_Class: {
384 Type ct = (Type)c;
385 Name name = ct.hasTag(ARRAY) ?
386 typeSig(ct) :
387 externalize(ct.tsym.flatName());
388 poolbuf.appendByte(tag);
389 poolbuf.appendChar(putName(name));
390 if (ct.hasTag(CLASS)) {
391 enterInner((ClassSymbol)ct.tsym);
392 }
393 break;
394 }
395 case ClassFile.CONSTANT_Utf8: {
396 Name name = (Name)c;
397 poolbuf.appendByte(tag);
398 byte[] bs = name.toUtf();
399 poolbuf.appendChar(bs.length);
400 poolbuf.appendBytes(bs, 0, bs.length);
401 if (overflowString == null && bs.length > MAX_STRING_LENGTH) {
402 //report error only once
403 overflowString = new String(bs);
404 }
405 break;
406 }
407 case ClassFile.CONSTANT_InterfaceMethodref:
408 case ClassFile.CONSTANT_Methodref:
409 case ClassFile.CONSTANT_Fieldref: {
410 Symbol sym = (Symbol)c;
411 poolbuf.appendByte(tag);
412 poolbuf.appendChar(putClass((ClassSymbol)sym.owner));
413 poolbuf.appendChar(putNameAndType(sym));
414 break;
415 }
416 case ClassFile.CONSTANT_Package: {
417 PackageSymbol pkg = (PackageSymbol)c;
418 Name pkgName = externalize(pkg.flatName());
419 poolbuf.appendByte(tag);
420 poolbuf.appendChar(putName(pkgName));
421 break;
422 }
423 case ClassFile.CONSTANT_Module: {
424 ModuleSymbol mod = (ModuleSymbol)c;
425 int modName = putName(mod.name);
426 poolbuf.appendByte(mod.poolTag());
427 poolbuf.appendChar(modName);
428 break;
429 }
430 case ClassFile.CONSTANT_Integer:
431 poolbuf.appendByte(tag);
432 poolbuf.appendInt((int)((BasicConstant)c).data);
433 break;
434 case ClassFile.CONSTANT_Float:
435 poolbuf.appendByte(tag);
436 poolbuf.appendFloat((float)((BasicConstant)c).data);
437 break;
438 case ClassFile.CONSTANT_Long:
439 currentIndex++;
440 poolbuf.appendByte(tag);
441 poolbuf.appendLong((long)((BasicConstant)c).data);
442 break;
443 case ClassFile.CONSTANT_Double:
444 currentIndex++;
445 poolbuf.appendByte(tag);
446 poolbuf.appendDouble((double)((BasicConstant)c).data);
447 break;
448 case ClassFile.CONSTANT_MethodHandle: {
449 MethodHandleSymbol h = (MethodHandleSymbol)c;
450 poolbuf.appendByte(tag);
451 poolbuf.appendByte(h.referenceKind());
452 poolbuf.appendChar(putMember(h.baseSymbol()));
453 break;
454 }
455 case ClassFile.CONSTANT_MethodType: {
456 Type.MethodType mt = (Type.MethodType)c;
457 poolbuf.appendByte(tag);
458 poolbuf.appendChar(putDescriptor(mt.baseType()));
459 break;
460 }
461 case ClassFile.CONSTANT_String: {
462 Name utf = names.fromString((String)((BasicConstant)c).data);
463 poolbuf.appendByte(tag);
464 poolbuf.appendChar(putName(utf));
465 break;
466 }
467 case ClassFile.CONSTANT_NameandType: {
468 NameAndType nt = (NameAndType)c;
469 poolbuf.appendByte(tag);
470 poolbuf.appendChar(putName(nt.name));
471 poolbuf.appendChar(putDescriptor(nt.type));
472 break;
473 }
474 case ClassFile.CONSTANT_InvokeDynamic: {
475 DynamicMethodSymbol d = (DynamicMethodSymbol)c;
476 poolbuf.appendByte(tag);
477 poolbuf.appendChar(makeBootstrapEntry(d));
478 poolbuf.appendChar(putNameAndType(d));
479 break;
480 }
481 case ClassFile.CONSTANT_Dynamic: {
482 Symbol.DynamicVarSymbol d = (Symbol.DynamicVarSymbol)c;
483 poolbuf.appendByte(tag);
484 poolbuf.appendChar(makeBootstrapEntry(d));
485 poolbuf.appendChar(putNameAndType(d));
486 break;
487 }
488 default:
489 throw new AssertionError("Unexpected constant tag: " + tag);
490 }
491 }
492
493 void reset() {
494 keysToPos.clear();
495 currentIndex = 1;
496 todo.clear();
497 overflowString = null;
498 poolbuf.reset();
499 }
500 }
501
502 int size() {
503 return pool.currentIndex;
504 }
505
506 /**
507 * Return signature of given type
508 */
509 private Name typeSig(Type type) {
510 signatureGen.reset();
511 signatureGen.assembleSig(type);
512 return signatureGen.toName();
513 }
514
515 private Name classSig(Type t) {
516 signatureGen.reset();
517 List<Type> typarams = t.getTypeArguments();
518 if (typarams.nonEmpty()) {
519 signatureGen.assembleParamsSig(typarams);
520 }
521 signatureGen.assembleSig(types.supertype(t));
522 for (Type i : types.interfaces(t))
523 signatureGen.assembleSig(i);
524 return signatureGen.toName();
525 }
526
527 void reset() {
528 innerClasses.clear();
529 loadableDescriptors.clear();
530 bootstrapMethods.clear();
531 pool.reset();
532 }
533 }