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.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(types);
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(Types types) {
282             super(types);
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 }