001: package nice.tools.code;
002:
003: import gnu.bytecode.*;
004: import gnu.expr.*;
005:
006: import bossa.util.Debug;
007:
008: /**
009: Arrays that are wrapped on the fly into objects implementing java.util.List
010: when needed.
011: */
012:
013: public class SpecialArray extends gnu.bytecode.ArrayType {
014: /**
015: Return a SpecialArray holding elements of type <tt>elements</tt>.
016: */
017: public static SpecialArray create(Type elements) {
018: String prefix;
019: if (elements instanceof PrimType)
020: prefix = elements.getName();
021: else
022: prefix = null;
023: Type res = Type.lookupType("[" + elements.getSignature());
024: if (res != null && res instanceof SpecialArray)
025: return (SpecialArray) res;
026:
027: return new SpecialArray(elements, prefix, false, true);
028: }
029:
030: public static SpecialArray unknownTypeArray() {
031: if (unknownTypeArray == null)
032: unknownTypeArray = new SpecialArray(objectType, null, true,
033: true);
034: return unknownTypeArray;
035: }
036:
037: private static SpecialArray unknownTypeArray;
038:
039: /** true if the elements have a primitive type. */
040: private boolean primitive;
041:
042: /**
043: If true, this type denote array of elements with any type
044: (could be primitive or not).
045: So the signature for this is Object.
046: */
047: private boolean unknown;
048:
049: protected SpecialArray(Type elements) {
050: this (elements, elements instanceof PrimType ? elements
051: .getName() : null, false, false);
052: }
053:
054: private SpecialArray(Type elements, String prefix, boolean unknown,
055: boolean register) {
056: super (elements);
057:
058: this .unknown = unknown;
059: this .primitive = prefix != null;
060: this .prefix = prefix;
061:
062: if (unknown)
063: // pretend we are java.lang.Object (for casts, method signatures...)
064: setSignature("Ljava/lang/Object;");
065:
066: field = new Field(wrappedType);
067: field.setName("value");
068: field.setType(objectType);
069:
070: if (register && !unknown) {
071: Class c = elements.getReflectClass();
072: if (c == null)
073: bossa.util.Internal.warning("Null refclass for "
074: + elements.getName());
075: else {
076: if (c == Void.TYPE)
077: // It is illegal to construct an array of void values.
078: // This situation most probably originates in a bug
079: // in the source program, so let's notify it.
080: bossa.util.User.error(null,
081: "Arrays cannot contain void values");
082:
083: c = java.lang.reflect.Array.newInstance(c, 0)
084: .getClass();
085: Type.registerTypeForClass(c, this );
086: }
087:
088: Type.registerTypeForName("[" + elements.getSignature(),
089: this );
090: }
091: }
092:
093: public String getInternalName() {
094: if (unknown)
095: return "java.lang.Object";
096: else
097: return getSignature();
098: }
099:
100: /****************************************************************
101: * Conversions
102: ****************************************************************/
103:
104: // Only valid for primitive types.
105: private void callConvert(CodeAttr code) {
106: if (convertMethod == null)
107: convertMethod = wrappedType.getDeclaredMethod("convert_"
108: + prefix, 1);
109:
110: code.emitInvokeStatic(convertMethod);
111: }
112:
113: private void callGenericConvert(CodeAttr code) {
114: if (genericConvertMethod == null)
115: if (primitive)
116: genericConvertMethod = wrappedType.getDeclaredMethod(
117: "gconvert_" + prefix, 1);
118: else
119: genericConvertMethod = wrappedType.getDeclaredMethod(
120: "gconvert", 2);
121:
122: code.emitInvokeStatic(genericConvertMethod);
123: }
124:
125: public void emitCoerceFrom(Type fromType, CodeAttr code) {
126: if (fromType instanceof ArrayType) {
127: // Any array can fit in the unknownTypeArray.
128: if (unknown)
129: return;
130:
131: ArrayType from = (ArrayType) fromType;
132:
133: // Do nothing either if both arrays are the same.
134: if (elements.getSignature().equals(
135: from.elements.getSignature()))
136: return;
137:
138: code.emitCheckcast(this );
139: } else if (isCollectionArray(fromType))
140: emitCoerceFromCollection(code);
141: else
142: // The only possibility left is that the value is some kind of array.
143: emitCoerceFromArray(code);
144: }
145:
146: /** @return true if the given type is one of the types an array can have
147: when considered as a collection.
148: */
149: private boolean isCollectionArray(Type t) {
150: /* We list explicitely the type between Collection and Array.
151: If the hierarchy was changed, this code should be modified.
152: An alternative is to use
153: <code>t.isSubType(ClassType.make("java.util.Collection")) &&
154: wrappedType.isSubType(t)
155: </code>.
156: However it would only work if we ensured that the class hierarchy
157: at the gnu.bytecode level is correctly set up before any call
158: to this code. (Additionally it is less efficient)
159: */
160: String name = t.getName();
161: return "java.util.Collection".equals(name)
162: || "java.util.List".equals(name);
163: }
164:
165: public void emitCoerceFromObject(CodeAttr code) {
166: bossa.util.Internal
167: .warning("SpecialArray.coerceFrom should probably be called instead of this");
168: emitCoerceFromArray(code);
169: }
170:
171: private void emitCoerceFromCollection(CodeAttr code) {
172: code.emitCheckcast(wrappedType);
173: code.emitGetField(field);
174:
175: emitCoerceFromArray(code);
176: }
177:
178: private void emitCoerceFromArray(CodeAttr code) {
179: // If this array type is the unknown, we have nothing to do.
180: if (unknown)
181: return;
182:
183: Label convert = new Label(code), end = new Label(code);
184:
185: code.emitDup();
186: code.emitInstanceof(this );
187: code.emitGotoIfIntEqZero(convert);
188:
189: code.emitCheckcast(this );
190: code.emitGoto(end);
191:
192: convert.define(code);
193: code.emitCheckcast(objectArray);
194:
195: if (primitive)
196: callConvert(code);
197: else
198: convertReferenceArray(code, this , elements);
199:
200: end.define(code);
201: }
202:
203: private static void convertReferenceArray(CodeAttr code,
204: ArrayType toType, Type elements) {
205: code.emitDup();
206: code.emitIfNotNull();
207:
208: code.emitDup();
209: code.emitArrayLength();
210:
211: code.pushScope();
212: Variable len = code.addLocal(Type.int_type);
213: code.emitStore(len);
214:
215: code.emitPushInt(0);
216:
217: code.emitLoad(len);
218: code.emitNewArray(elements);
219:
220: Variable dest = code.addLocal(toType);
221: code.emitDup();
222: code.emitStore(dest);
223:
224: code.emitPushInt(0);
225:
226: code.emitLoad(len);
227:
228: code.emitInvokeStatic(arraycopy);
229: code.emitLoad(dest);
230: code.emitCheckcast(toType);
231:
232: code.popScope();
233:
234: code.emitElse();
235:
236: code.emitPop(1);
237: code.emitPushNull(toType);
238:
239: code.emitFi();
240: }
241:
242: public static final Method arraycopy = ClassType.make(
243: "java.lang.System").getDeclaredMethod("arraycopy", 5);
244:
245: public void emitCoerceTo(Type toType, CodeAttr code) {
246: if (toType instanceof ArrayType)
247: coerce(code, this , (ArrayType) toType);
248: else if (toType != objectType)
249: emitCoerceToObject(code);
250: }
251:
252: public void emitCoerceToObject(CodeAttr code) {
253: emitCoerceToCollection(code);
254: }
255:
256: public static void emitCoerceToCollection(CodeAttr code) {
257: code.emitInvokeStatic(makeMethod);
258: }
259:
260: private static void coerce(CodeAttr code, ArrayType from,
261: ArrayType to) {
262: // Any array can fit in the unknownTypeArray
263: if (to == unknownTypeArray)
264: return;
265:
266: Type elements = to.getComponentType();
267: if (elements instanceof PrimType) {
268: ((SpecialArray) SpecialArray.create(elements))
269: .callGenericConvert(code);
270: } else {
271: boolean generic = !from.getSignature().startsWith("[L");
272:
273: if (generic) {
274: // For generic arrays, we pass a string that represents
275: // the type of the elements, so that the correct array type
276: // can be created
277: code.emitPushString(((ObjectType) elements)
278: .getInternalName().replace('/', '.'));
279:
280: unknownTypeArray.callGenericConvert(code);
281:
282: // Assert what we now guarantee.
283: code.emitCheckcast(to);
284: } else
285: convertReferenceArray(code, to, elements);
286: }
287: }
288:
289: /****************************************************************
290: * Typing
291: ****************************************************************/
292:
293: public Type getImplementationType() {
294: if (unknown)
295: return objectType;
296: else
297: return this ;
298: }
299:
300: public boolean isSubtype(Type other) {
301: return other instanceof ArrayType
302: && (other == unknownTypeArray || elements
303: .isSubtype(((ArrayType) other)
304: .getComponentType()));
305: }
306:
307: public boolean isAssignableTo(Type other) {
308: if (this == other)
309: return true;
310:
311: if (unknown)
312: return other == objectType;
313:
314: return other == objectType
315: || other instanceof ArrayType
316: && (other == unknownTypeArray || elements
317: .isAssignableTo(((ArrayType) other)
318: .getComponentType()));
319: }
320:
321: /****************************************************************
322: * Fields
323: ****************************************************************/
324:
325: private static ClassType wrappedType = ClassType
326: .make("nice.lang.rawArray");
327:
328: public static ClassType wrappedType() {
329: return wrappedType;
330: }
331:
332: private Field field;
333:
334: private static ClassType objectType = ClassType
335: .make("java.lang.Object");
336: private static ArrayType objectArray = new ArrayType(objectType);
337:
338: private Method convertMethod;
339: private Method genericConvertMethod;
340: private static Method makeMethod = wrappedType.getDeclaredMethod(
341: "make", 1);
342: private String prefix;
343:
344: public String toString() {
345: if (unknown)
346: return "Array with unknown element type";
347: else
348: return "SpecialArray(" + elements + ")";
349: }
350: }
|