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