1 /*
  2  * Copyright (c) 2019, 2021, 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.Type.ConstantPoolQType;
 37 import com.sun.tools.javac.code.Types;
 38 import com.sun.tools.javac.jvm.ClassWriter.PoolOverflow;
 39 import com.sun.tools.javac.jvm.ClassWriter.StringOverflow;
 40 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant;
 41 import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant.BasicConstant;
 42 import com.sun.tools.javac.jvm.PoolConstant.Dynamic;
 43 import com.sun.tools.javac.jvm.PoolConstant.Dynamic.BsmKey;
 44 import com.sun.tools.javac.jvm.PoolConstant.NameAndType;
 45 import com.sun.tools.javac.util.ByteBuffer;
 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<ClassSymbol> valueClasses = 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(types);
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         /* Their is nothing to be gained by having the pair of class types Foo.ref and Foo.val
124            result in two different CONSTANT_Class_info strucures in the pool. These are
125            indistinguishable at the class file level. Hence we coalesce them here.
126         */
127         if (t.isReferenceProjection())
128             t = t.valueProjection();
129         return pool.writeIfNeeded(types.erasure(t));
130     }
131 
132     /**
133      * Puts a type into the pool and return its index. The type could be either a class, a type variable
134      * or an array type.
135      */
136     int putClass(ConstantPoolQType t) {
137         return pool.writeIfNeeded(t);
138     }
139 
140     /**
141      * Puts a member reference into the constant pool. Valid members are either field or method symbols.
142      */
143     int putMember(Symbol s) {
144         return pool.writeIfNeeded(s);
145     }
146 
147     /**
148      * Puts a dynamic reference into the constant pool and return its index.
149      */
150     int putDynamic(Dynamic d) {
151         return pool.writeIfNeeded(d);
152     }
153 
154     /**
155      * Puts a field or method descriptor into the constant pool and return its index.
156      */
157     int putDescriptor(Type t) {
158         return putName(typeSig(types.erasure(t)));
159     }
160 
161     /**
162      * Puts a field or method descriptor into the constant pool and return its index.
163      */
164     int putDescriptor(Symbol s) {
165         return putDescriptor(descriptorType(s));
166     }
167 
168     /**
169      * Puts a signature (see {@code Signature} attribute in JVMS 4.4) into the constant pool and
170      * return its index.
171      */
172     int putSignature(Symbol s) {
173         if (s.kind == TYP) {
174             return putName(classSig(s.type));
175         } else {
176             return putName(typeSig(s.type));
177         }
178     }
179 
180     /**
181      * Puts a constant value into the pool and return its index. Supported values are int, float, long,
182      * double and String.
183      */
184     int putConstant(Object o) {
185         if (o instanceof Integer intVal) {
186             return putConstant(LoadableConstant.Int(intVal));
187         } else if (o instanceof Float floatVal) {
188             return putConstant(LoadableConstant.Float(floatVal));
189         } else if (o instanceof Long longVal) {
190             return putConstant(LoadableConstant.Long(longVal));
191         } else if (o instanceof Double doubleVal) {
192             return putConstant(LoadableConstant.Double(doubleVal));
193         } else if (o instanceof String strVal) {
194             return putConstant(LoadableConstant.String(strVal));
195         } else {
196             throw new AssertionError("unexpected constant: " + o);
197         }
198     }
199 
200     /**
201      * Puts a constant into the pool and return its index.
202      */
203     int putConstant(LoadableConstant c) {
204         switch (c.poolTag()) {
205             case CONSTANT_Class:
206                 return putClass((Type)c);
207             case CONSTANT_MethodType:
208                 return pool.writeIfNeeded(types.erasure((Type)c));
209             default:
210                 return pool.writeIfNeeded(c);
211         }
212     }
213 
214     int putName(Name name) {
215         return pool.writeIfNeeded(name);
216     }
217 
218     /**
219      * Puts a name and type pair into the pool and returns its index.
220      */
221     int putNameAndType(Symbol s) {
222         return pool.writeIfNeeded(new NameAndType(s.name, descriptorType(s)));
223     }
224 
225     /**
226      * Puts a package entry into the pool and returns its index.
227      */
228     int putPackage(PackageSymbol pkg) {
229         return pool.writeIfNeeded(pkg);
230     }
231 
232     /**
233      * Puts a module entry into the pool and returns its index.
234      */
235     int putModule(ModuleSymbol mod) {
236         return pool.writeIfNeeded(mod);
237     }
238 
239     /**
240      * Enter an inner class into the `innerClasses' set.
241      */
242     void enterInnerClass(ClassSymbol c) {
243         if (c.type.isCompound()) {
244             throw new AssertionError("Unexpected intersection type: " + c.type);
245         }
246         c.complete();
247         if (c.owner.enclClass() != null && !innerClasses.contains(c)) {
248             enterInnerClass(c.owner.enclClass());
249             innerClasses.add(c);
250         }
251     }
252 
253     /** Enter a value class into the `valueClasses' set.
254      */
255     void enterValueClass(ClassSymbol c) {
256         if (c.type.isCompound()) {
257             throw new AssertionError("Unexpected intersection type: " + c.type);
258         }
259         c.complete();
260         if (c.isValueClass() && !c.isPrimitiveClass()) {
261             valueClasses.add(c);
262         }
263         if (c.owner.enclClass() != null) {
264             enterValueClass(c.owner.enclClass());
265         }
266     }
267 
268 
269     void enterInnerAndValueClass(ClassSymbol c) {
270         enterInnerClass(c);
271         enterValueClass(c);
272     }
273 
274     /**
275      * Create a new Utf8 entry representing a descriptor for given (member) symbol.
276      */
277     private Type descriptorType(Symbol s) {
278         return s.kind == Kind.MTH ? s.externalType(types) : s.erasure(types);
279     }
280 
281     private int makeBootstrapEntry(Dynamic dynamic) {
282         BsmKey bsmKey = dynamic.bsmKey(types);
283 
284         // Figure out the index for existing BSM; create a new BSM if no key
285         Integer index = bootstrapMethods.get(bsmKey);
286         if (index == null) {
287             index = bootstrapMethods.size();
288             bootstrapMethods.put(bsmKey, index);
289         }
290 
291         return index;
292     }
293 
294     /**
295      * Write pool contents into given byte buffer.
296      */
297     void writePool(OutputStream out) throws IOException, PoolOverflow {
298         if (pool.overflowString != null) {
299             throw new StringOverflow(pool.overflowString);
300         }
301         int size = size();
302         if (size > MAX_ENTRIES) {
303             throw new PoolOverflow();
304         }
305         out.write(size >> 8);
306         out.write(size);
307         out.write(pool.poolbuf.elems, 0, pool.poolbuf.length);
308     }
309 
310     /**
311      * Signature Generation
312      */
313     class SharedSignatureGenerator extends Types.SignatureGenerator {
314 
315         /**
316          * An output buffer for type signatures.
317          */
318         ByteBuffer sigbuf = new ByteBuffer();
319 
320         SharedSignatureGenerator(Types types) {
321             super(types);
322         }
323 
324         /**
325          * Assemble signature of given type in string buffer.
326          * Check for uninitialized types before calling the general case.
327          */
328         @Override
329         public void assembleSig(Type type) {
330             switch (type.getTag()) {
331                 case UNINITIALIZED_THIS:
332                 case UNINITIALIZED_OBJECT:
333                     // we don't yet have a spec for uninitialized types in the
334                     // local variable table
335                     assembleSig(types.erasure(((UninitializedType)type).qtype));
336                     break;
337                 default:
338                     super.assembleSig(type);
339             }
340         }
341 
342         @Override
343         protected void append(char ch) {
344             sigbuf.appendByte(ch);
345         }
346 
347         @Override
348         protected void append(byte[] ba) {
349             sigbuf.appendBytes(ba);
350         }
351 
352         @Override
353         protected void append(Name name) {
354             sigbuf.appendName(name);
355         }
356 
357         @Override
358         protected void classReference(ClassSymbol c) {
359             enterInnerAndValueClass(c);
360         }
361 
362         protected void reset() {
363             sigbuf.reset();
364         }
365 
366         protected Name toName() {
367             return sigbuf.toName(names);
368         }
369     }
370 
371     class WriteablePoolHelper {
372 
373         /** Pool entries. */
374         private final Map<Object, Integer> keysToPos = new HashMap<>(64);
375 
376         final ByteBuffer poolbuf = new ByteBuffer(POOL_BUF_SIZE);
377 
378         int currentIndex = 1;
379 
380         ArrayDeque<PoolConstant> todo = new ArrayDeque<>();
381 
382         String overflowString = null;
383 
384         private <P extends PoolConstant> int writeIfNeeded(P p) {
385             Object key = p.poolKey(types);
386             Integer index = keysToPos.get(key);
387             if (index == null) {
388                 keysToPos.put(key, index = currentIndex++);
389                 boolean first = todo.isEmpty();
390                 todo.addLast(p);
391                 if (first) {
392                     while (!todo.isEmpty()) {
393                         writeConstant(todo.peekFirst());
394                         todo.removeFirst();
395                     }
396                 }
397             }
398             return index;
399         }
400 
401         void writeConstant(PoolConstant c) {
402             int tag = c.poolTag();
403             switch (tag) {
404                 case ClassFile.CONSTANT_Class: {
405                     Type ct = c instanceof ConstantPoolQType ? ((ConstantPoolQType)c).type : (Type)c;
406                     Name name = ct.hasTag(ARRAY) ?
407                             typeSig(ct) :
408                             c instanceof ConstantPoolQType ? names.fromString("Q" + new String(externalize(ct.tsym.flatName())) + ";") : names.fromUtf(externalize(ct.tsym.flatName()));
409                     poolbuf.appendByte(tag);
410                     poolbuf.appendChar(putName(name));
411                     if (ct.hasTag(CLASS)) {
412                         enterInnerAndValueClass((ClassSymbol)ct.tsym);
413                     }
414                     break;
415                 }
416                 case ClassFile.CONSTANT_Utf8: {
417                     Name name = (Name)c;
418                     poolbuf.appendByte(tag);
419                     byte[] bs = name.toUtf();
420                     poolbuf.appendChar(bs.length);
421                     poolbuf.appendBytes(bs, 0, bs.length);
422                     if (overflowString == null && bs.length > MAX_STRING_LENGTH) {
423                         //report error only once
424                         overflowString = new String(bs);
425                     }
426                     break;
427                 }
428                 case ClassFile.CONSTANT_InterfaceMethodref:
429                 case ClassFile.CONSTANT_Methodref:
430                 case ClassFile.CONSTANT_Fieldref: {
431                     Symbol sym = (Symbol)c;
432                     poolbuf.appendByte(tag);
433                     poolbuf.appendChar(putClass((ClassSymbol)sym.owner));
434                     poolbuf.appendChar(putNameAndType(sym));
435                     break;
436                 }
437                 case ClassFile.CONSTANT_Package: {
438                     PackageSymbol pkg = (PackageSymbol)c;
439                     Name pkgName = names.fromUtf(externalize(pkg.flatName()));
440                     poolbuf.appendByte(tag);
441                     poolbuf.appendChar(putName(pkgName));
442                     break;
443                 }
444                 case ClassFile.CONSTANT_Module: {
445                     ModuleSymbol mod = (ModuleSymbol)c;
446                     int modName = putName(mod.name);
447                     poolbuf.appendByte(mod.poolTag());
448                     poolbuf.appendChar(modName);
449                     break;
450                 }
451                 case ClassFile.CONSTANT_Integer:
452                     poolbuf.appendByte(tag);
453                     poolbuf.appendInt((int)((BasicConstant)c).data);
454                     break;
455                 case ClassFile.CONSTANT_Float:
456                     poolbuf.appendByte(tag);
457                     poolbuf.appendFloat((float)((BasicConstant)c).data);
458                     break;
459                 case ClassFile.CONSTANT_Long:
460                     currentIndex++;
461                     poolbuf.appendByte(tag);
462                     poolbuf.appendLong((long)((BasicConstant)c).data);
463                     break;
464                 case ClassFile.CONSTANT_Double:
465                     currentIndex++;
466                     poolbuf.appendByte(tag);
467                     poolbuf.appendDouble((double)((BasicConstant)c).data);
468                     break;
469                 case ClassFile.CONSTANT_MethodHandle: {
470                     MethodHandleSymbol h = (MethodHandleSymbol)c;
471                     poolbuf.appendByte(tag);
472                     poolbuf.appendByte(h.referenceKind());
473                     poolbuf.appendChar(putMember(h.baseSymbol()));
474                     break;
475                 }
476                 case ClassFile.CONSTANT_MethodType: {
477                     Type.MethodType mt = (Type.MethodType)c;
478                     poolbuf.appendByte(tag);
479                     poolbuf.appendChar(putDescriptor(mt.baseType()));
480                     break;
481                 }
482                 case ClassFile.CONSTANT_String: {
483                     Name utf = names.fromString((String)((BasicConstant)c).data);
484                     poolbuf.appendByte(tag);
485                     poolbuf.appendChar(putName(utf));
486                     break;
487                 }
488                 case ClassFile.CONSTANT_NameandType: {
489                     NameAndType nt = (NameAndType)c;
490                     poolbuf.appendByte(tag);
491                     poolbuf.appendChar(putName(nt.name));
492                     poolbuf.appendChar(putDescriptor(nt.type));
493                     break;
494                 }
495                 case ClassFile.CONSTANT_InvokeDynamic: {
496                     DynamicMethodSymbol d = (DynamicMethodSymbol)c;
497                     poolbuf.appendByte(tag);
498                     poolbuf.appendChar(makeBootstrapEntry(d));
499                     poolbuf.appendChar(putNameAndType(d));
500                     break;
501                 }
502                 case ClassFile.CONSTANT_Dynamic: {
503                     Symbol.DynamicVarSymbol d = (Symbol.DynamicVarSymbol)c;
504                     poolbuf.appendByte(tag);
505                     poolbuf.appendChar(makeBootstrapEntry(d));
506                     poolbuf.appendChar(putNameAndType(d));
507                     break;
508                 }
509                 default:
510                     throw new AssertionError("Unexpected constant tag: " + tag);
511             }
512         }
513 
514         void reset() {
515             keysToPos.clear();
516             currentIndex = 1;
517             todo.clear();
518             overflowString = null;
519             poolbuf.reset();
520         }
521     }
522 
523     int size() {
524         return pool.currentIndex;
525     }
526 
527     /**
528      * Return signature of given type
529      */
530     private Name typeSig(Type type) {
531         signatureGen.reset();
532         signatureGen.assembleSig(type);
533         return signatureGen.toName();
534     }
535 
536     private Name classSig(Type t) {
537         signatureGen.reset();
538         List<Type> typarams = t.getTypeArguments();
539         if (typarams.nonEmpty()) {
540             signatureGen.assembleParamsSig(typarams);
541         }
542         signatureGen.assembleSig(types.supertype(t));
543         for (Type i : types.interfaces(t))
544             signatureGen.assembleSig(i);
545 
546         return signatureGen.toName();
547     }
548 
549     void reset() {
550         innerClasses.clear();
551         valueClasses.clear();
552         bootstrapMethods.clear();
553         pool.reset();
554     }
555 }