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