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