001: package gnu.expr;
002:
003: import java.io.*;
004: import gnu.bytecode.*;
005: import java.lang.reflect.Array;
006: import java.util.*;
007:
008: /** Manages the literals of a Compilation.
009: * Implements ObjectOutput, because we use externalization to determine
010: * how literals get compiled into code that re-creates the literal. */
011:
012: public class LitTable implements ObjectOutput {
013: Compilation comp;
014: ClassType mainClass;
015:
016: /* #ifdef use:java.util.IdentityHashMap */
017: IdentityHashMap literalTable = new IdentityHashMap(100);
018: /* #else */
019: // Hashtable literalTable = new Hashtable(100);
020: /* #endif */
021:
022: static Hashtable staticTable = new Hashtable(100);
023:
024: int literalsCount;
025:
026: /** Rembembers literals to initialize (in <clinit>). */
027: Literal literalsChain;
028:
029: public LitTable(Compilation comp) {
030: this .comp = comp;
031: this .mainClass = comp.mainClass;
032: }
033:
034: public void emit() throws IOException {
035: // We use two passes. The first generates the graph of
036: // objects and how they are generated.
037: // The second pass actually emits code.
038: // The reason for using two passes is so we can detect cycles
039: // and sharing using the first pass. This generates better code:
040: // If an object is only used once, and is not a top-level literal,
041: // they we don't need to allocate a Field for it. And if an object
042: // does not cyclically depend on itself, we can allocate *and*
043: // initialize using a single call, which generates better code.
044:
045: // Here is the first pass.
046: for (Literal init = literalsChain; init != null; init = init.next) {
047: writeObject(init.value);
048: }
049:
050: // Here is the second pass.
051: for (Literal init = literalsChain; init != null; init = init.next) {
052: emit(init, true);
053: }
054:
055: // For speedier garbage collection.
056: literalTable = null;
057: literalsCount = 0;
058: }
059:
060: Object[] valueStack = new Object[20];
061: Type[] typeStack = new Type[20];
062: int stackPointer;
063:
064: void push(Object value, Type type) {
065: if (stackPointer >= valueStack.length) {
066: Object[] newValues = new Object[2 * valueStack.length];
067: Type[] newTypes = new Type[2 * typeStack.length];
068: System.arraycopy(valueStack, 0, newValues, 0, stackPointer);
069: System.arraycopy(typeStack, 0, newTypes, 0, stackPointer);
070: valueStack = newValues;
071: typeStack = newTypes;
072: }
073: valueStack[stackPointer] = value;
074: typeStack[stackPointer] = type;
075: stackPointer++;
076: }
077:
078: void error(String msg) {
079: throw new Error(msg);
080: }
081:
082: public void flush() {
083: }
084:
085: public void close() {
086: }
087:
088: public void write(int b) throws IOException {
089: error("cannot handle call to write(int) when externalizing literal");
090: }
091:
092: public void writeBytes(String s) throws IOException {
093: error("cannot handle call to writeBytes(String) when externalizing literal");
094: }
095:
096: public void write(byte[] b) throws IOException {
097: error("cannot handle call to write(byte[]) when externalizing literal");
098: }
099:
100: public void write(byte[] b, int off, int len) throws IOException {
101: error("cannot handle call to write(byte[],int,int) when externalizing literal");
102: }
103:
104: public void writeBoolean(boolean v) {
105: push(new Boolean(v), Type.boolean_type);
106: }
107:
108: public void writeChar(int v) {
109: push(new Character((char) v), Type.char_type);
110: }
111:
112: public void writeByte(int v) {
113: push(new Byte((byte) v), Type.byte_type);
114: }
115:
116: public void writeShort(int v) {
117: push(new Short((short) v), Type.short_type);
118: }
119:
120: public void writeInt(int v) {
121: push(new Integer(v), Type.int_type);
122: }
123:
124: public void writeLong(long v) {
125: push(new Long(v), Type.long_type);
126: }
127:
128: public void writeFloat(float v) {
129: push(new Float(v), Type.float_type);
130: }
131:
132: public void writeDouble(double v) {
133: push(new Double(v), Type.double_type);
134: }
135:
136: public void writeUTF(String v) {
137: push(v, Type.string_type);
138: }
139:
140: public void writeChars(String v) {
141: push(v, Type.string_type);
142: }
143:
144: public void writeObject(Object obj) throws IOException {
145: Literal lit = findLiteral(obj);
146:
147: // Usually a no-op, but if the literalTable is a Hashtable (rather
148: // than an IdentityHashMap) then we might find a literal whose
149: // value is equals to obj, but not identical. This can lead to trouble,
150: // e.g. if one is a Pair and the other is a PairWithPosition.
151: /* #ifndef use:java.util.IdentityHashMap */
152: // obj = lit.value;
153: /* #endif */
154:
155: if ((lit.flags & (Literal.WRITTEN | Literal.WRITING)) != 0) {
156: // It is referenced more than once, so we we need a Field
157: // to save the value.
158: if (lit.field == null && obj != null
159: && !(obj instanceof String))
160: lit.assign(this );
161: if ((lit.flags & Literal.WRITTEN) == 0)
162: lit.flags |= Literal.CYCLIC;
163: } else {
164: lit.flags |= Literal.WRITING;
165: int oldStack = stackPointer;
166: if (obj instanceof gnu.lists.FString
167: && ((gnu.lists.FString) obj).size() < 65535) { // Optimization.
168: push(obj.toString(), Type.string_type);
169: } else if (obj instanceof Externalizable) {
170: ((Externalizable) obj).writeExternal(this );
171: } else if (obj instanceof Object[]) {
172: Object[] arr = (Object[]) obj;
173: for (int i = 0; i < arr.length; i++) {
174: writeObject(arr[i]);
175: }
176: } else if (obj == null || obj instanceof String
177: || lit.type instanceof ArrayType) {
178: // nothing to do
179: } else if (obj instanceof java.math.BigInteger) {
180: writeChars(obj.toString());
181: } else if (obj instanceof java.math.BigDecimal) {
182: java.math.BigDecimal dec = (java.math.BigDecimal) obj;
183: /* #ifdef JAVA2 */
184: writeObject(dec.unscaledValue());
185: writeInt(dec.scale());
186: /* #else */
187: // writeChars(obj.toString());
188: /* #endif */
189: } else if (obj instanceof Integer)
190: push(obj, Type.int_type);
191: else if (obj instanceof Short)
192: push(obj, Type.short_type);
193: else if (obj instanceof Byte)
194: push(obj, Type.byte_type);
195: else if (obj instanceof Long)
196: push(obj, Type.long_type);
197: else if (obj instanceof Double)
198: push(obj, Type.double_type);
199: else if (obj instanceof Float)
200: push(obj, Type.float_type);
201: else if (obj instanceof Character)
202: push(obj, Type.char_type);
203: else if (obj instanceof Class)
204: push(obj, Type.java_lang_Class_type);
205: else
206: error(obj.getClass().getName()
207: + " does not implement Externalizable");
208: int nargs = stackPointer - oldStack;
209: if (nargs == 0) {
210: lit.argValues = gnu.mapping.Values.noArgs;
211: lit.argTypes = Type.typeArray0;
212: } else {
213: lit.argValues = new Object[nargs];
214: lit.argTypes = new Type[nargs];
215: System.arraycopy(valueStack, oldStack, lit.argValues,
216: 0, nargs);
217: System.arraycopy(typeStack, oldStack, lit.argTypes, 0,
218: nargs);
219: stackPointer = oldStack;
220: }
221: lit.flags |= Literal.WRITTEN;
222: }
223: push(lit, lit.type);
224: }
225:
226: public Literal findLiteral(Object value) {
227: if (value == null)
228: return Literal.nullLiteral;
229: Literal literal = (Literal) literalTable.get(value);
230: if (literal != null)
231: return literal;
232: if (comp.immediate)
233: return new Literal(value, this );
234: Class valueClass = value.getClass();
235: Type valueType = Type.make(valueClass);
236:
237: synchronized (staticTable) {
238: literal = (Literal) staticTable.get(value);
239: if ((literal == null || literal.value != value)
240: && valueType instanceof ClassType) {
241: // Add all the static final public fields to staticTable.
242: int needed_mod = Access.STATIC | Access.FINAL
243: | Access.PUBLIC;
244: Class fldClass = valueClass;
245: ClassType fldType = (ClassType) valueType;
246: while (staticTable.get(fldClass) == null) {
247: // This is a convention to note that we've scanned valueType.
248: staticTable.put(fldClass, fldClass);
249: for (Field fld = fldType.getFields(); fld != null; fld = fld
250: .getNext()) {
251: if ((fld.getModifiers() & needed_mod) == needed_mod) {
252: try {
253: java.lang.reflect.Field rfld = fld
254: .getReflectField();
255: Object litValue = rfld.get(null);
256: if (litValue == null
257: || !fldClass
258: .isInstance(litValue))
259: continue;
260: Literal lit = new Literal(litValue,
261: fld, this );
262: staticTable.put(litValue, lit);
263: staticTable.put(lit, litValue);
264: if (value == litValue)
265: literal = lit;
266: } catch (Throwable ex) {
267: error("caught " + ex
268: + " getting static field "
269: + fld);
270: }
271: }
272: }
273: fldClass = fldClass.getSuperclass();
274: if (fldClass == null)
275: break;
276: fldType = (ClassType) Type.make(fldClass);
277: }
278: }
279: }
280:
281: if (literal != null)
282: literalTable.put(value, literal);
283: else
284: literal = new Literal(value, valueType, this );
285: return literal;
286: }
287:
288: Method getMethod(ClassType type, String name, Literal literal,
289: boolean isStatic) {
290: Type[] argTypes = literal.argTypes;
291: Method method = type.getDeclaredMethods();
292: int argLength = argTypes.length;
293: Method best = null;
294: long bestArrayArgs = 0;
295: boolean ambiguous = false;
296: Type[] bParameters = null;
297: methodLoop: for (; method != null; method = method.getNext()) {
298: if (!name.equals(method.getName()))
299: continue;
300: boolean mstatic = method.getStaticFlag();
301: if (isStatic != mstatic)
302: continue;
303: // One bit set for each array parameter.
304: long arrayArgs = 0;
305: Type[] mParameters = method.getParameterTypes();
306: int iarg = 0;
307: int iparam = 0;
308: for (;; iarg++, iparam++) {
309: if (iarg == argLength && iparam == mParameters.length) {
310: if (best == null
311: || (bestArrayArgs != 0 && arrayArgs == 0)) {
312: best = method;
313: bParameters = mParameters;
314: bestArrayArgs = arrayArgs;
315: } else if (arrayArgs == 0) {
316: // Now see which of 'best' and 'method' is more specific.
317:
318: // True if we know best cannot be the more specific.
319: boolean not1 = false;
320: // True if we know new method cannot be the more specific.
321: boolean not2 = false;
322: for (int j = argLength; --j >= 0;) {
323: int c = bParameters[j]
324: .compare(mParameters[j]);
325: if (c != 1) {
326: not2 = true;
327: if (not1)
328: break;
329: }
330: if (c != -1) {
331: not1 = true;
332: if (not2)
333: break;
334: }
335: }
336: if (not1) {
337: best = method;
338: bParameters = mParameters;
339: }
340: ambiguous = not1 && not2;
341: }
342: continue methodLoop; // Look for other matches.
343: }
344: if (iarg == argLength || iparam == mParameters.length)
345: continue methodLoop; // fail on this method
346: Type aType = argTypes[iarg];
347: Type pType = mParameters[iparam];
348: if (aType.isSubtype(pType))
349: ; // OK so far
350: else if (pType instanceof ArrayType
351: && iparam < 64
352: && (aType == Type.int_type || aType == Type.short_type)) {
353: int count = ((Number) literal.argValues[iarg])
354: .intValue();
355: if (count < 0
356: && type.getName().equals("gnu.math.IntNum"))
357: count -= 0x80000000; // special hack for IntNum.
358: Type elementType = ((ArrayType) pType)
359: .getComponentType();
360: if (count < 0 || iarg + count >= argLength)
361: continue methodLoop; // fail on this method
362: else {
363: for (int j = count; --j >= 0;) {
364: Type t = argTypes[iarg + j + 1];
365: if (elementType instanceof PrimType ? elementType
366: .getSignature() != t.getSignature()
367: : !t.isSubtype(elementType))
368: continue methodLoop; // fail on this method
369: }
370: iarg += count;
371: arrayArgs |= 1 << iparam;
372: }
373: } else {
374: continue methodLoop; // fail on this method
375: }
376: }
377: }
378: if (ambiguous)
379: return null;
380: if (bestArrayArgs != 0) {
381: Object[] args = new Object[bParameters.length];
382: Type[] types = new Type[bParameters.length];
383: int iarg = 0;
384: int iparam = 0;
385: for (;; iarg++, iparam++) {
386: if (iarg == argLength)
387: break;
388: Type pType = bParameters[iparam];
389: if ((bestArrayArgs & (1 << iparam)) == 0) {
390: args[iparam] = literal.argValues[iarg];
391: types[iparam] = literal.argTypes[iarg];
392: } else {
393: int count = ((Number) literal.argValues[iarg])
394: .intValue();
395: boolean isIntNum = type.getName().equals(
396: "gnu.math.IntNum");
397: if (isIntNum)
398: count -= 0x80000000; // special hack for IntNum.
399: Type elementType = ((ArrayType) pType)
400: .getComponentType();
401: types[iparam] = pType;
402: args[iparam] = Array.newInstance(elementType
403: .getReflectClass(), count);
404: Object[] argValues = literal.argValues;
405: if (isIntNum) {
406: // Special kludge for IntNum: words are Externalized
407: // in big-endian (network) order, but the representation
408: // is little-endian.
409: int[] arr = (int[]) args[iparam];
410: for (int j = count; j > 0; j--)
411: arr[count - j] = ((Integer) argValues[iarg
412: + j]).intValue();
413: } else {
414: for (int j = count; --j >= 0;)
415: Array.set(args[iparam], j, argValues[iarg
416: + 1 + j]);
417: }
418: Literal arrayLiteral = new Literal(args[iparam],
419: pType);
420: if (elementType instanceof ObjectType)
421: arrayLiteral.argValues = (Object[]) args[iparam];
422: args[iparam] = arrayLiteral;
423: iarg += count;
424: }
425: }
426: literal.argValues = args;
427: literal.argTypes = types;
428: }
429: return best;
430: }
431:
432: void putArgs(Literal literal, CodeAttr code) {
433: Type[] argTypes = literal.argTypes;
434: int len = argTypes.length;
435: for (int i = 0; i < len; i++) {
436: Object value = literal.argValues[i];
437: if (value instanceof Literal)
438: emit((Literal) value, false);
439: else
440: comp.compileConstant(value,
441: new StackTarget(argTypes[i]));
442: }
443: }
444:
445: private void store(Literal literal, boolean ignore, CodeAttr code) {
446: if (literal.field != null) {
447: if (!ignore)
448: code.emitDup(literal.type);
449: code.emitPutStatic(literal.field);
450: }
451: literal.flags |= Literal.EMITTED;
452: }
453:
454: void emit(Literal literal, boolean ignore) {
455: CodeAttr code = comp.getCode();
456: if (literal.value == null) {
457: if (!ignore)
458: code.emitPushNull();
459: } else if (literal.value instanceof String) {
460: if (!ignore)
461: code.emitPushString(literal.value.toString());
462: } else if ((literal.flags & Literal.EMITTED) != 0) {
463: if (!ignore)
464: code.emitGetStatic(literal.field);
465: } else if (literal.value instanceof Object[]) {
466: int len = literal.argValues.length;
467: Type elementType = ((ArrayType) literal.type)
468: .getComponentType();
469: code.emitPushInt(len);
470: code.emitNewArray(elementType);
471: store(literal, ignore, code);
472: for (int i = 0; i < len; i++) {
473: Literal el = (Literal) literal.argValues[i];
474: if (el.value == null)
475: continue;
476: code.emitDup(elementType);
477: code.emitPushInt(i);
478: emit(el, false);
479: code.emitArrayStore(elementType);
480: }
481: } else if (literal.type instanceof ArrayType) {
482: code.emitPushPrimArray(literal.value,
483: (ArrayType) literal.type);
484: store(literal, ignore, code);
485: } else if (literal.value instanceof Class) {
486: comp.loadClassRef(((Class) literal.value).getName());
487: store(literal, ignore, code);
488: } else if (literal.value instanceof ClassType
489: && !((ClassType) literal.value).isExisting()) {
490: // We need to special case ClassTypes that are (currently)
491: // non-existing, because the corresponding reflective Class
492: // needs to be loaded using the correct ClassLoader.
493: comp.loadClassRef((ClassType) literal.value);
494: Method meth = Compilation.typeType.getDeclaredMethod(
495: "valueOf", 1);
496: if (meth == null)
497: meth = Compilation.typeType
498: .getDeclaredMethod("make", 1);
499: code.emitInvokeStatic(meth);
500: code.emitCheckcast(Compilation.typeClassType);
501: store(literal, ignore, code);
502: } else {
503: ClassType type = (ClassType) literal.type;
504: boolean useDefaultInit = (literal.flags & Literal.CYCLIC) != 0;
505: Method method = null;
506: boolean makeStatic = false;
507: if (!useDefaultInit) {
508: // look for matching "valueOf" or "make" method;
509: method = getMethod(type, "valueOf", literal, true);
510: if (method == null)
511: method = getMethod(type, "make", literal, true);
512: // otherwise look for matching constructor;
513: if (method != null)
514: makeStatic = true;
515: else if (literal.argTypes.length > 0)
516: method = getMethod(type, "<init>", literal, false);
517: if (method == null)
518: useDefaultInit = true;
519: }
520: if (useDefaultInit) {
521: method = getMethod(type, "set", literal, false);
522: // otherwise error;
523: }
524: if (method == null && literal.argTypes.length > 0)
525: error("no method to construct " + literal.type);
526: if (makeStatic) {
527: putArgs(literal, code);
528: code.emitInvokeStatic(method);
529: } else if (useDefaultInit) {
530: code.emitNew(type);
531: code.emitDup(type);
532: Method init0 = type.getDeclaredMethod("<init>", 0);
533: code.emitInvokeSpecial(init0);
534: } else {
535: code.emitNew(type);
536: code.emitDup(type);
537: putArgs(literal, code);
538: code.emitInvokeSpecial(method);
539: }
540: Method resolveMethod = makeStatic ? null : type
541: .getDeclaredMethod("readResolve", 0);
542: if (resolveMethod != null) {
543: code.emitInvokeVirtual(resolveMethod);
544: type.emitCoerceFromObject(code);
545: }
546: store(literal, ignore
547: && !(useDefaultInit && method != null), code);
548: if (useDefaultInit && method != null) {
549: if (!ignore)
550: code.emitDup(type);
551: putArgs(literal, code);
552: code.emitInvokeVirtual(method);
553: }
554: }
555: }
556:
557: }
|