1 package gnu.expr;
2 import java.io.*;
3 import gnu.bytecode.*;
4 import gnu.kawa.functions.IsEqual;
5 import gnu.kawa.util.GeneralHashTable;
6 import gnu.kawa.util.HashUtils;
7 import java.lang.reflect.Array;
8 import java.util.*;
9 import gnu.mapping.*;
10 /* #ifdef use:java.util.regex */
11 import java.util.regex.*;
12 /* #endif */
13 
14 /** Manages the literals of a Compilation.
15  * Implements ObjectOutput, because we use externalization to determine
16  * how literals get compiled into code that re-creates the literal. */
17 
18 public class LitTable extends GeneralHashTable<Object,Object>
19     implements ObjectOutput
20 {
21   Compilation comp;
22   ClassType mainClass;
23 
24   /** A table mapping objects to public static final field literals.
25    * When we a need a literal for a value that is an instance of some
26    * class we automatically search the class for static fields.
27    * We use a {@code Table2D} primarily to make use of weak references,
28    * but we also use the 2nd argument:
29    * {@code staticTable(value, null, defaultValue)} yields a Literal
30    * if there is a public static final field for {@code value},
31    * and {@code defaultValue} otherwise.
32    * {@code staticTable(class, Boolean.TRUE, null) != null} if and only if
33    * we have scanned {@code class} (a {@code java.lang.Class} object).
34    */
35   static Table2D staticTable =  new Table2D (100);
36 
37   int literalsCount;
38 
39   /** Remembers literals to initialize (in <clinit>). */
40   Literal literalsChain;
41 
LitTable(Compilation comp)42   public LitTable(Compilation comp)
43   {
44     this.comp = comp;
45     this.mainClass = comp.mainClass;
46   }
47 
48     private Object hashKeyCache = null;
49     private int hashCodeCache;
50 
51     @Override
hash(Object key)52     public int hash(Object key) {
53         if (key == hashKeyCache)
54             return hashCodeCache;
55         int h = comp.immediate ? System.identityHashCode(key)
56             : HashUtils.boundedHash(key);
57         hashKeyCache = key;
58         hashCodeCache = h;
59         return h;
60     }
61 
62     @Override
matches(Object key1, Object key2)63     protected boolean matches(Object key1, Object key2) {
64         if (comp.immediate)
65             return key1 == key2;
66         return litEquals.apply(key1, key2, null);
67     }
68 
emit()69   public void emit() throws IOException
70   {
71     // We use two passes.  The first generates the graph of
72     // objects and how they are generated.
73     // The second pass actually emits code.
74     // The reason for using two passes is so we can detect cycles
75     // and sharing using the first pass.  This generates better code:
76     // If an object is only used once, and is not a top-level literal,
77     // they we don't need to allocate a Field for it.  And if an object
78     // does not cyclically depend on itself, we can allocate *and*
79     // initialize using a single call, which generates better code.
80 
81     // Here is the first pass.
82     for (Literal init = literalsChain;  init != null;
83 	 init = init.next)
84       {
85 	writeObject(init.value);
86       }
87 
88     // Here is the second pass.
89     for (Literal init = literalsChain;  init != null;
90 	 init = init.next)
91       {
92 	emit(init, true);
93       }
94 
95     // For speedier garbage collection.
96     clear();
97     literalsCount = 0;
98   }
99 
100   Object[] valueStack = new Object[20];
101   Type[] typeStack = new Type[20];
102   int stackPointer;
103 
push(Object value, Type type)104   void push(Object value, Type type)
105   {
106     if (stackPointer >= valueStack.length)
107       {
108 	Object[] newValues = new Object[2 * valueStack.length];
109 	Type[] newTypes = new Type[2 * typeStack.length];
110 	System.arraycopy(valueStack, 0, newValues, 0, stackPointer);
111 	System.arraycopy(typeStack, 0, newTypes,  0, stackPointer);
112 	valueStack = newValues;
113 	typeStack = newTypes;
114       }
115     valueStack[stackPointer] = value;
116     typeStack[stackPointer] = type;
117     stackPointer++;
118   }
119 
error(String msg)120   void error(String msg)
121   {
122     throw new Error(msg);
123   }
124 
flush()125   public void flush()
126   {
127   }
128 
close()129   public void close()
130   {
131   }
132 
write(int b)133   public void write(int b) throws IOException
134   {
135     error("cannot handle call to write(int) when externalizing literal");
136   }
137 
writeBytes(String s)138   public void writeBytes(String s) throws IOException
139   {
140     error("cannot handle call to writeBytes(String) when externalizing literal");
141   }
142 
write(byte[] b)143   public void write(byte[] b) throws IOException
144   {
145     error("cannot handle call to write(byte[]) when externalizing literal");
146   }
147 
write(byte[] b, int off, int len)148   public void write(byte[] b, int off, int len) throws IOException
149   {
150     error("cannot handle call to write(byte[],int,int) when externalizing literal");
151   }
152 
writeBoolean(boolean v)153   public void writeBoolean(boolean v)
154   {
155     push(new Boolean(v), Type.booleanType);
156   }
157 
writeChar(int v)158   public void writeChar(int v)
159   {
160     push(new Character((char) v), Type.charType);
161   }
162 
writeByte(int v)163   public void writeByte(int v)
164   {
165     push(new Byte((byte) v), Type.byteType);
166   }
167 
writeShort(int v)168   public void writeShort(int v)
169   {
170     push(new Short((short) v), Type.shortType);
171   }
172 
writeInt(int v)173   public void writeInt(int v)
174   {
175     push(new Integer(v), Type.intType);
176   }
177 
writeLong(long v)178   public void writeLong(long v)
179   {
180     push(new Long(v), Type.longType);
181   }
182 
writeFloat(float v)183   public void writeFloat(float v)
184   {
185     push(new Float(v), Type.floatType);
186   }
187 
writeDouble(double v)188   public void writeDouble(double v)
189   {
190     push(new Double(v), Type.doubleType);
191   }
192 
writeUTF(String v)193   public void writeUTF(String v)
194   {
195     push(v, Type.string_type);
196   }
197 
writeChars(String v)198   public void writeChars(String v)
199   {
200     push(v, Type.string_type);
201   }
202 
writeObject(Object obj)203   public void writeObject(Object obj) throws IOException
204   {
205     Literal lit = findLiteral(obj);
206 
207     if ((lit.flags & (Literal.WRITTEN|Literal.WRITING)) != 0)
208       {
209 	// It is referenced more than once, so we we need a Field
210 	// to save the value.
211 	if (lit.field == null
212 	    && obj != null && ! (obj instanceof String))
213 	  lit.assign(this);
214 	if ((lit.flags & Literal.WRITTEN) == 0)
215 	  lit.flags |= Literal.CYCLIC;
216       }
217     else
218       {
219 	lit.flags |= Literal.WRITING;
220 	int oldStack = stackPointer;
221 	if (obj instanceof gnu.lists.FString
222 	    && ((gnu.lists.FString) obj).size() < 65535)
223 	  { // Optimization.
224 	    push(obj.toString(), Type.string_type);
225 	  }
226 	else if (obj instanceof Externalizable)
227 	  {
228 	    ((Externalizable) obj).writeExternal(this);
229 	  }
230 	else if (obj instanceof Object[])
231 	  {
232 	    Object[] arr = (Object[]) obj;
233 	    for (int i = 0;  i < arr.length;  i++)
234 	      {
235 		writeObject(arr[i]);
236 	      }
237 	  }
238 	else if (obj == null
239                  || obj instanceof String || lit.type instanceof ArrayType)
240 	  {
241 	    // nothing to do
242 	  }
243         else if (obj instanceof java.math.BigInteger)
244           {
245             writeChars(obj.toString());
246           }
247         else if (obj instanceof java.math.BigDecimal)
248           {
249             java.math.BigDecimal dec = (java.math.BigDecimal) obj;
250             /* #ifdef JAVA2 */
251             writeObject(dec.unscaledValue());
252             writeInt(dec.scale());
253             /* #else */
254             // writeChars(obj.toString());
255             /* #endif */
256           }
257 	else if (obj instanceof Integer)
258 	  push(obj, Type.intType);
259 	else if (obj instanceof Short)
260 	  push(obj, Type.shortType);
261 	else if (obj instanceof Byte)
262 	  push(obj, Type.byteType);
263 	else if (obj instanceof Long)
264 	  push(obj, Type.longType);
265 	else if (obj instanceof Double)
266 	  push(obj, Type.doubleType);
267 	else if (obj instanceof Float)
268 	  push(obj, Type.floatType);
269 	else if (obj instanceof Character)
270 	  push(obj, Type.charType);
271         else if (obj instanceof Class)
272           push(obj, Type.java_lang_Class_type);
273         /* #ifdef use:java.util.regex */
274         else if (obj instanceof Pattern)
275           {
276             Pattern pat = (Pattern) obj;
277             push(pat.pattern(), Type.string_type);
278             push(Integer.valueOf(pat.flags()), Type.intType);
279           }
280         /* #endif */
281 	else
282 	  error(obj.getClass().getName()+" does not implement Externalizable");
283 	int nargs = stackPointer - oldStack;
284 	if (nargs == 0)
285 	  {
286 	    lit.argValues = gnu.mapping.Values.noArgs;
287 	    lit.argTypes = Type.typeArray0;
288 	  }
289 	else
290 	  {
291 	    lit.argValues = new Object[nargs];
292 	    lit.argTypes = new Type[nargs];
293 	    System.arraycopy(valueStack, oldStack, lit.argValues, 0, nargs);
294 	    System.arraycopy(typeStack, oldStack, lit.argTypes, 0, nargs);
295 	    stackPointer = oldStack;
296 	  }
297 	lit.flags |= Literal.WRITTEN;
298       }
299     push(lit, lit.type);
300   }
301 
findLiteral(Object value)302   public Literal findLiteral (Object value)
303   {
304     if (value == null)
305       return Literal.nullLiteral;
306     Literal literal = (Literal) get(value);
307     int valueHash = hash(value); // gets cached value
308     if (literal != null)
309       return literal;
310     if (comp.immediate)
311       return new Literal (value, this);
312     Class valueClass = value.getClass();
313     Type valueType = Type.make(valueClass);
314 
315     synchronized (staticTable)
316       {
317 	literal = (Literal) staticTable.get(value, null, null);
318 	if ((literal == null || literal.value != value)
319 	    && valueType instanceof ClassType)
320 	  {
321               if (value instanceof HasOwningField) {
322                   Field fld = ((HasOwningField) value).getOwningField();
323                   if (fld != null) {
324                       Literal lit = new Literal (value, fld, this);
325                       staticTable.put(value, null, lit);
326                       return lit;
327                   }
328               }
329 	    // Add all the static final public fields to staticTable.
330 	    int needed_mod = Access.STATIC | Access.FINAL | Access.PUBLIC;
331 	    Class fldClass = valueClass;
332 	    ClassType fldType = (ClassType) valueType;
333 	    while (staticTable.get(fldClass, Boolean.TRUE, null) == null)
334 	      {
335 		// This is a convention to note that we've scanned valueType.
336 		staticTable.put(fldClass, Boolean.TRUE, fldClass);
337 		for (Field fld = fldType.getFields();
338 		     fld != null;  fld = fld.getNext())
339 		  {
340 		    if ((fld.getModifiers() & needed_mod) == needed_mod
341                         && ! (fld.getType() instanceof PrimType))
342 		      {
343 			try
344 			  {
345 			    java.lang.reflect.Field rfld = fld.getReflectField();
346 			    Object litValue = rfld.get(null);
347 			    if (litValue == null
348 				|| ! fldClass.isInstance(litValue))
349 			      continue;
350 			    Literal lit = new Literal (litValue, fld, this);
351 			    staticTable.put(litValue, null, lit);
352                             int litHash = hash(litValue); // gets cached value
353                             if (valueHash == litHash
354                                 && matches(litValue, value))
355 			      literal = lit;
356 			  }
357 			catch (Exception ex)
358 			  {
359 			    error("caught "+ex+" getting static field "+fld);
360 			  }
361 		      }
362 		  }
363 		fldClass = fldClass.getSuperclass();
364 		if (fldClass == null)
365 		  break;
366 		fldType = (ClassType) Type.make(fldClass);
367 	      }
368 	  }
369       }
370 
371     if (literal == null)
372       literal = new Literal (value, valueType, this);
373     return literal;
374   }
375 
getMethod(ClassType type, String name, Literal literal, boolean isStatic)376   Method getMethod (ClassType type, String name,
377 		    Literal literal, boolean isStatic)
378   {
379     Type[] argTypes = literal.argTypes;
380     Method method = type.getDeclaredMethods();
381     int argLength = argTypes.length;
382     Method best = null;
383     long bestArrayArgs = 0;
384     boolean ambiguous = false;
385     Type[] bParameters = null;
386   methodLoop:
387     for (; method != null;  method = method.getNext())
388       {
389 	if (! name.equals(method.getName()))
390 	  continue;
391 	boolean mstatic = method.getStaticFlag();
392 	if (isStatic != mstatic)
393 	  continue;
394 	// One bit set for each array parameter.
395 	long arrayArgs = 0;
396 	Type[] mParameters = method.getParameterTypes();
397 	int iarg = 0;  int iparam = 0;
398 	for (;; iarg++, iparam++)
399 	  {
400 	    if (iarg == argLength && iparam == mParameters.length)
401 	      {
402 		if (best == null || (bestArrayArgs != 0 && arrayArgs == 0))
403 		  {
404 		    best = method;
405 		    bParameters = mParameters;
406 		    bestArrayArgs = arrayArgs;
407 		  }
408 		else if (arrayArgs == 0)
409 		  {
410 		    // Now see which of 'best' and 'method' is more specific.
411 
412 		    // True if we know best cannot be the more specific.
413 		    boolean not1 = false;
414 		    // True if we know new method cannot be the more specific.
415 		    boolean not2 = false;
416 		    for (int j = argLength;  --j >= 0; )
417 		      {
418 			int c = bParameters[j].compare(mParameters[j]);
419 			if (c != 1)
420 			  {
421 			    not2 = true;
422 			    if (not1)
423 			      break;
424 			  }
425 			if (c != -1)
426 			  {
427 			    not1 = true;
428 			    if (not2)
429 			      break;
430 			  }
431 		      }
432 		    if (not1)
433 		      {
434 			best = method;
435 			bParameters = mParameters;
436 		      }
437 		    ambiguous = not1 && not2;
438 		  }
439 		continue methodLoop;  // Look for other matches.
440 	      }
441 	    if (iarg == argLength || iparam == mParameters.length)
442 	      continue methodLoop;  // fail on this method
443 	    Type aType = argTypes[iarg];
444 	    Type pType = mParameters[iparam];
445 	    if (aType.isSubtype(pType))
446 	      ; // OK so far
447 	    else if (pType instanceof ArrayType && iparam < 64
448 		     && (aType == Type.intType || aType == Type.shortType))
449 	      {
450 		int count = ((Number) literal.argValues[iarg]).intValue();
451 		if (count < 0 && type.getName().equals("gnu.math.IntNum"))
452 		  count -= 0x80000000; // special hack for IntNum.
453 		Type elementType = ((ArrayType) pType).getComponentType();
454 		if (count < 0 || iarg + count >= argLength)
455 		  continue methodLoop;  // fail on this method
456 		else
457 		  {
458 		    for (int j = count;  --j >= 0; )
459 		      {
460 			Type t = argTypes[iarg + j + 1];
461 			if (elementType instanceof PrimType
462 			    ? elementType.getSignature() != t.getSignature()
463 			    : ! t.isSubtype(elementType))
464 			  continue methodLoop;  // fail on this method
465 		      }
466 		    iarg += count;
467 		    arrayArgs |= 1 << iparam;
468 		  }
469 	      }
470 	    else
471 	      {
472 	      continue methodLoop;  // fail on this method
473 	      }
474 	  }
475       }
476     if (ambiguous)
477       return null;
478     if (bestArrayArgs != 0)
479       {
480 	Object[] args = new Object[bParameters.length];
481 	Type[] types = new Type[bParameters.length];
482 	int iarg = 0;  int iparam = 0;
483 	for (;; iarg++, iparam++)
484 	  {
485 	    if (iarg == argLength)
486 	      break;
487 	    Type pType = bParameters[iparam];
488 	    if ((bestArrayArgs & (1 << iparam)) == 0)
489 	      {
490 		args[iparam] = literal.argValues[iarg];
491 		types[iparam] = literal.argTypes[iarg];
492 	      }
493 	    else
494 	      {
495 		int count = ((Number) literal.argValues[iarg]).intValue();
496 		boolean isIntNum = type.getName().equals("gnu.math.IntNum");
497 		if (isIntNum)
498 		  count -= 0x80000000; // special hack for IntNum.
499 		Type elementType = ((ArrayType) pType).getComponentType();
500 		types[iparam] = pType;
501 		args[iparam] = Array.newInstance(elementType.getReflectClass(),
502 						 count);
503 		Object[] argValues = literal.argValues;
504 		if (isIntNum)
505 		  {
506 		    // Special kludge for IntNum:  words are Externalized
507 		    // in big-endian (network) order, but the representation
508 		    // is little-endian.
509 		    int[] arr = (int[]) args[iparam];
510 		    for (int j = count;  j > 0;  j--)
511 		      arr[count - j]
512 			= ((Integer) argValues[iarg + j]).intValue();
513 		  }
514 		else
515 		  {
516 		    for (int j = count;  --j >= 0; )
517 		      Array.set(args[iparam], j, argValues[iarg + 1 + j]);
518 		  }
519 		Literal arrayLiteral = new Literal(args[iparam], pType);
520 		if (elementType instanceof ObjectType)
521 		  arrayLiteral.argValues = (Object[]) args[iparam];
522 		args[iparam] = arrayLiteral;
523 		iarg += count;
524 	      }
525 	  }
526 	literal.argValues = args;
527 	literal.argTypes = types;
528       }
529     return best;
530   }
531 
putArgs(Literal literal, CodeAttr code)532   void putArgs(Literal literal, CodeAttr code)
533   {
534     Type[] argTypes = literal.argTypes;
535     int len = argTypes.length;
536     for (int i = 0;  i < len;  i++)
537       {
538 	Object value = literal.argValues[i];
539 	if (value instanceof Literal)
540 	  emit((Literal) value, false);
541 	else
542 	  comp.compileConstant(value, new StackTarget(argTypes[i]));
543       }
544   }
545 
store(Literal literal, boolean ignore, CodeAttr code)546   private void store (Literal literal, boolean ignore, CodeAttr code)
547   {
548     if (literal.field != null)
549       {
550 	if (! ignore)
551 	  code.emitDup(literal.type);
552 	code.emitPutStatic(literal.field);
553       }
554     literal.flags |= Literal.EMITTED;
555   }
556 
emit(Literal literal, boolean ignore)557   void emit(Literal literal, boolean ignore)
558   {
559     CodeAttr code = comp.getCode();
560     if (literal.value == null)
561       {
562 	if (! ignore)
563 	  code.emitPushNull();
564       }
565     else if (literal.value instanceof String)
566       {
567 	if (! ignore)
568 	  code.emitPushString(literal.value.toString ());
569       }
570     else if ((literal.flags & Literal.EMITTED) != 0)
571       {
572 	if (! ignore)
573 	  code.emitGetStatic(literal.field);
574       }
575     else if (literal.value instanceof Object[])
576       {
577 	int len = literal.argValues.length;
578 	Type elementType = ((ArrayType) literal.type).getComponentType();
579 	code.emitPushInt(len);
580 	code.emitNewArray(elementType);
581         int numNonNull = 0;
582         for (int i = 0;  i < len;  i++) {
583 	    if (((Literal) literal.argValues[i]).value != null)
584                 numNonNull++;
585         }
586         if (numNonNull > 0)
587             code.emitDup(literal.type);
588 	store(literal, ignore, code);
589 	for (int i = 0;  i < len;  i++)
590 	  {
591 	    Literal el = (Literal) literal.argValues[i];
592 	    if (el.value == null)
593 	      continue;
594             if (--numNonNull > 0)
595                 code.emitDup(literal.type);
596 	    code.emitPushInt(i);
597 	    emit(el, false);
598 	    code.emitArrayStore(elementType);
599 	  }
600       }
601     else if (literal.type instanceof ArrayType)
602     {
603         int length = java.lang.reflect.Array.getLength(literal.value);
604         int nchunks = 0;
605         int count = length;
606         int chunkSize = 6;
607         // "Optimize" big primitive arrays, to reduce code size.
608         // For now only optimize big int[] literals; maybe more later. FIXME
609         // Otherwise srfi14.class becomes bigger than the 64k limit.
610         if (length >= 8 && literal.value instanceof int[]) {
611             nchunks = length / chunkSize;
612             count -= chunkSize * nchunks;
613         }
614 	code.emitPushPrimArray(literal.value, length, count, (ArrayType) literal.type);
615         for (int ichunk = 0; ichunk < nchunks; ichunk++) {
616             Method chunkMethod = ClassType.make("gnu.kawa.util.PrimArrayUtils")
617                 .getDeclaredMethod("initArray6Int", 2+chunkSize);
618             code.emitPushInt(count);
619             int[] iarr = (int[]) literal.value;
620             for (int j = 0; j < chunkSize; j++) {
621                 code.emitPushInt(iarr[count++]);
622             }
623             code.emitInvokeStatic(chunkMethod);
624         }
625 	store(literal, ignore, code);
626       }
627     else if (literal.value instanceof Class)
628       {
629         Class clas = (Class) literal.value;
630         if (clas.isPrimitive())
631           {
632             String cname = clas.getName();
633             if (cname.equals("int"))
634               cname = "integer";
635             cname = "java.lang."
636               +Character.toUpperCase(cname.charAt(0))
637               +cname.substring(1);
638             code.emitGetStatic(ClassType.make(cname).getDeclaredField("TYPE"));
639           }
640         else
641           comp.loadClassRef((ObjectType)Type.make(clas));
642 	store(literal, ignore, code);
643       }
644     else if (literal.value instanceof ClassType
645 	     && ! ((ClassType) literal.value).isExisting())
646       {
647 	// We need to special case ClassTypes that are (currently)
648 	// non-existing, because the corresponding reflective Class
649 	// needs to be loaded using the correct ClassLoader.
650         ClassType ct = (ClassType) literal.value;
651         boolean isPair = literal.value instanceof PairClassType;
652         ClassType typeType = isPair ? ClassType.make("gnu.expr.PairClassType")
653             : Compilation.typeType;
654         Type[] atypes = new Type[isPair ? 2 : 1];
655         for (int i = atypes.length;  --i >= 0; )
656             atypes[i] = Type.javalangClassType;
657         Method meth = typeType.getDeclaredMethod("make", atypes);
658 	comp.loadClassRef((ClassType) ct);
659         if (isPair)
660             comp.loadClassRef(((PairClassType) ct).instanceType);
661 	code.emitInvokeStatic(meth);
662 	code.emitCheckcast(Compilation.typeClassType);
663 	store(literal, ignore, code);
664       }
665     else
666       {
667 	ClassType type = (ClassType) literal.type;
668 	boolean useDefaultInit = (literal.flags & Literal.CYCLIC) != 0;
669 	Method method = null;
670 	boolean makeStatic = false;
671 	if (! useDefaultInit)
672 	  {
673 	    // Look for matching "valueOf" or "make" method.
674             // (For backward compatibility for we prefer Symbol's 'make'
675             // method over 'valueOf' - they differ in argument order.)
676               if (! (literal.value instanceof Symbol))
677                 method = getMethod(type, "valueOf", literal, true);
678               else if (literal.value instanceof SimpleSymbol)
679                 method = getMethod(Compilation.typeSymbol, "valueOf", literal, true);
680             if (method == null
681                 // Values.make has return type Object, so use the constructor.
682                 && ! (literal.value instanceof Values))
683               {
684                 String mname = "make";
685                 /* #ifdef use:java.util.regex */
686                 if (literal.value instanceof Pattern)
687                   mname = "compile";
688                 /* #endif */
689                 method = getMethod(type, mname, literal, true);
690               }
691 	    // otherwise look for matching constructor;
692 	    if (method != null)
693 	      makeStatic = true;
694 	    else if (literal.argTypes.length > 0)
695 	      method = getMethod(type, "<init>", literal, false);
696 
697 	    if (method == null)
698 	      useDefaultInit = true;
699 	  }
700 	if (useDefaultInit)
701 	  {
702 	    method = getMethod(type, "init", literal, false);
703             if (method == null)
704                 method = getMethod(type, "set", literal, false);
705 	    // otherwise error;
706 	  }
707 	if (method == null && literal.argTypes.length > 0)
708 	  error("no method to construct "+literal.type);
709 	if (makeStatic)
710 	  {
711 	    putArgs(literal, code);
712 	    code.emitInvokeStatic(method);
713 	  }
714 	else if (useDefaultInit)
715 	  {
716 	    code.emitNew(type);
717 	    code.emitDup(type);
718 	    Method init0 = type.getDeclaredMethod("<init>", 0);
719 	    code.emitInvokeSpecial(init0);
720 	  }
721 	else
722 	  {
723 	    code.emitNew(type);
724 	    code.emitDup(type);
725 	    putArgs(literal, code);
726 	    code.emitInvokeSpecial(method);
727 	  }
728 	Method resolveMethod
729 	  = makeStatic || literal.value instanceof Values ? null
730           : type.getDeclaredMethod("readResolve", 0);
731 	if (resolveMethod != null)
732 	  {
733 	    code.emitInvokeVirtual(resolveMethod);
734 	    type.emitCoerceFromObject(code);
735 	  }
736 	store(literal, ignore && ! (useDefaultInit && method != null), code);
737 	if (useDefaultInit && method != null)
738 	  {
739 	    if (! ignore)
740 	      code.emitDup(type);
741 	    putArgs(literal, code);
742 	    code.emitInvokeVirtual(method);
743 	  }
744       }
745   }
746 
747     /** A modified equality predicate.
748      * Mostly same as IsEqual (Scheme equal? predicate),
749      * but the classes have to match.  Also Symbol is handled specially.
750      * This allows combining equivalent literals in the source code.
751      */
752     static class LitEquals extends IsEqual {
LitEquals()753         public LitEquals() { super(null, "(equals-for-literals)"); }
754 
apply(Object arg1, Object arg2, Map<Object,ArrayList<Object>> map)755         public boolean apply (Object arg1, Object arg2,
756                               Map<Object,ArrayList<Object>> map) {
757             if (arg1 == arg2)
758                 return true;
759             if (arg1 == null || arg2 == null
760                 // Symbols can be equals even if not == due to namespaces
761                 || arg1 instanceof Symbol
762                 || arg1.getClass() != arg2.getClass())
763                 return false;
764 
765             return super.apply(arg1, arg2, map);
766         }
767     }
768     static final LitEquals litEquals = new LitEquals();
769 }
770