001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.object.bytecode.hook.impl;
006:
007: import com.tc.object.TCObject;
008: import com.tc.object.util.IdentityWeakHashMap;
009: import com.tc.util.Assert;
010:
011: import java.util.HashMap;
012: import java.util.Map;
013:
014: /**
015: * Global array manager. The basic purpose of this class to maintain the relationship to DSO managed arrays to their
016: * respective TCObject
017: */
018: public class ArrayManager {
019:
020: // some day we might want to make this stuff externally configurable
021: private final static int CACHE_DEPTH = 2;
022: private final static int NUM_MAPS = 128;
023: private final static int INITIAL_CAPACITY = 500;
024: private final static float LOAD_FACTOR = 0.75F;
025:
026: private static final Map[] maps = new Map[NUM_MAPS];
027: private static final Map primClasses = new HashMap();
028: private static final Object[] keys = new Object[NUM_MAPS
029: * CACHE_DEPTH];
030: private static final TCObject[] values = new TCObject[NUM_MAPS
031: * CACHE_DEPTH];
032:
033: static {
034: for (int i = 0; i < maps.length; i++) {
035: maps[i] = new IdentityWeakHashMap(INITIAL_CAPACITY,
036: LOAD_FACTOR);
037: }
038: }
039:
040: private ArrayManager() {
041: // not to be instantiated
042: }
043:
044: /**
045: * Register an array with its TCO. It is an error to register an array that has already
046: * been registered.
047: * @param array Array
048: * @param tco TCObject
049: * @throws NullPointerException if array or tco are null
050: */
051: public static void register(Object array, TCObject tco) {
052: if ((array == null) || (tco == null)) {
053: throw new NullPointerException();
054: }
055:
056: final int index = array.hashCode() % NUM_MAPS;
057: final Map map = maps[index];
058: final int start = index * CACHE_DEPTH;
059: final int end = start + CACHE_DEPTH;
060: final Object prev;
061:
062: synchronized (map) {
063: for (int i = start; i < end; i++) {
064: if (keys[i] == array) {
065: values[i] = tco;
066: break;
067: }
068: }
069:
070: prev = map.put(array, tco);
071: }
072: if (prev != null) {
073: throw new AssertionError("replaced mapping for " + array);
074: }
075: }
076:
077: /**
078: * Get the TCO for an array
079: * @param array The array instance
080: * @return The TCObject
081: */
082: public static TCObject getObject(Object array) {
083: final int hash = array.hashCode();
084: final int index = hash % NUM_MAPS;
085: final Map map = maps[index];
086: final int start = index * CACHE_DEPTH;
087: final int end = start + CACHE_DEPTH;
088:
089: synchronized (map) {
090: for (int i = start; i < end; i++) {
091: if (keys[i] == array) {
092: return values[i];
093: }
094: }
095:
096: int evict = start + (hash % CACHE_DEPTH);
097: TCObject rv = (TCObject) map.get(array);
098: keys[evict] = array;
099: values[evict] = rv;
100: return rv;
101: }
102: }
103:
104: /**
105: * Get TCObject for a cloned array
106: * @param array Array
107: * @return TCObject
108: */
109: public static TCObject getCloneObject(Object array) {
110: return getObject(array);
111: }
112:
113: /**
114: * For java.lang.reflect.Array.get()
115: * @param array The array
116: * @param index Index into the array
117: * @return Item in array at index, boxed to Object if primitive array
118: * @throws NullPointerException If array is null
119: * @throws IllegalArgumentException If array is not an array type
120: */
121: public static Object get(Object array, int index) {
122: if (array == null)
123: throw new NullPointerException();
124:
125: if (array instanceof boolean[])
126: return ((boolean[]) array)[index] ? Boolean.TRUE
127: : Boolean.FALSE;
128: if (array instanceof byte[])
129: return new Byte(((byte[]) array)[index]);
130: if (array instanceof char[])
131: return new Character(((char[]) array)[index]);
132: if (array instanceof short[])
133: return new Short(((short[]) array)[index]);
134: if (array instanceof int[])
135: return new Integer(((int[]) array)[index]);
136: if (array instanceof long[])
137: return new Long(((long[]) array)[index]);
138: if (array instanceof float[])
139: return new Float(((float[]) array)[index]);
140: if (array instanceof double[])
141: return new Double(((double[]) array)[index]);
142:
143: if (array instanceof Object[]) {
144: TCObject tco = getObject(array);
145: if (tco != null) {
146: synchronized (tco.getResolveLock()) {
147: tco.resolveArrayReference(index);
148: return ((Object[]) array)[index];
149: }
150: } else {
151: return ((Object[]) array)[index];
152: }
153: }
154:
155: throw new IllegalArgumentException("Not an array type: "
156: + array.getClass().getName());
157:
158: }
159:
160: /**
161: * Indicate that object in array changed
162: * @param array The array
163: * @param index The index into array
164: * @param value The new value
165: */
166: public static void objectArrayChanged(Object[] array, int index,
167: Object value) {
168: Object existingObj = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
169: if (false && existingObj != existingObj)
170: Assert.fail(); // silence compiler warning
171:
172: TCObject tco = getObject(array);
173: if (tco != null) {
174: tco.objectFieldChanged(array.getClass().getName(), null,
175: value, index);
176: }
177: array[index] = value;
178: }
179:
180: /**
181: * Indicate that short in array changed
182: * @param array The array
183: * @param index The index into array
184: * @param value The new value
185: */
186: public static void shortArrayChanged(short[] array, int index,
187: short value) {
188: short existingVal = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
189: if (false && existingVal != existingVal)
190: Assert.fail(); // silence compiler warning
191:
192: TCObject tco = getObject(array);
193: if (tco != null) {
194: tco.shortFieldChanged(null, null, value, index);
195: }
196: array[index] = value;
197: }
198:
199: /**
200: * Indicate that long in array changed
201: * @param array The array
202: * @param index The index into array
203: * @param value The new value
204: */
205: public static void longArrayChanged(long[] array, int index,
206: long value) {
207: long existingVal = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
208: if (false && existingVal != existingVal)
209: Assert.fail(); // silence compiler warning
210:
211: TCObject tco = getObject(array);
212: if (tco != null) {
213: tco.longFieldChanged(null, null, value, index);
214: }
215: array[index] = value;
216: }
217:
218: /**
219: * Indicate that int in array changed
220: * @param array The array
221: * @param index The index into array
222: * @param value The new value
223: */
224: public static void intArrayChanged(int[] array, int index, int value) {
225: int existingVal = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
226: if (false && existingVal != existingVal)
227: Assert.fail(); // silence compiler warning
228:
229: TCObject tco = getObject(array);
230: if (tco != null) {
231: tco.intFieldChanged(null, null, value, index);
232: }
233: array[index] = value;
234: }
235:
236: /**
237: * Indicate that float in array changed
238: * @param array The array
239: * @param index The index into array
240: * @param value The new value
241: */
242: public static void floatArrayChanged(float[] array, int index,
243: float value) {
244: float existingVal = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
245: if (false && existingVal != existingVal)
246: Assert.fail(); // silence compiler warning
247:
248: TCObject tco = getObject(array);
249: if (tco != null) {
250: tco.floatFieldChanged(null, null, value, index);
251: }
252: array[index] = value;
253: }
254:
255: /**
256: * Indicate that double in array changed
257: * @param array The array
258: * @param index The index into array
259: * @param value The new value
260: */
261: public static void doubleArrayChanged(double[] array, int index,
262: double value) {
263: double existingVal = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
264: if (false && existingVal != existingVal)
265: Assert.fail(); // silence compiler warning
266:
267: TCObject tco = getObject(array);
268: if (tco != null) {
269: tco.doubleFieldChanged(null, null, value, index);
270: }
271: array[index] = value;
272: }
273:
274: /**
275: * Indicate that char in array changed
276: * @param array The array
277: * @param index The index into array
278: * @param value The new value
279: */
280: public static void charArrayChanged(char[] array, int index,
281: char value) {
282: char existingVal = array[index]; // do array operation first (fail fast, NPE and array index out of bounds)
283: if (false && existingVal != existingVal)
284: Assert.fail(); // silence compiler warning
285:
286: TCObject tco = getObject(array);
287: if (tco != null) {
288: tco.charFieldChanged(null, null, value, index);
289: }
290: array[index] = value;
291: }
292:
293: /**
294: * Indicate that byte or boolean in array changed
295: * @param array The array
296: * @param index The index into array
297: * @param value The new value
298: */
299: public static void byteOrBooleanArrayChanged(Object array,
300: int index, byte value) {
301: if (array == null) {
302: throw new NullPointerException();
303: }
304:
305: // Hack to deal with the fact that booleans and bytes are treated the same in bytecode
306: if (array.getClass().getComponentType().equals(Boolean.TYPE)) {
307: boolean[] booleanArray = (boolean[]) array;
308: boolean existingBooleanVal = booleanArray[index]; // do array operation first (fail fast, NPE and array index out
309: // of bounds)
310: if (false && existingBooleanVal != existingBooleanVal)
311: Assert.fail(); // silence compiler warning
312: boolean booleanValue = value == 1;
313:
314: TCObject tco = getObject(array);
315: if (tco != null) {
316: tco
317: .booleanFieldChanged(null, null, booleanValue,
318: index);
319: }
320: booleanArray[index] = booleanValue;
321: } else {
322: byte[] byteArray = (byte[]) array;
323: byte existingByteVal = byteArray[index]; // do array operation first (fail fast, NPE and array index out of
324: // bounds)
325: if (false && existingByteVal != existingByteVal)
326: Assert.fail(); // silence compiler warning
327:
328: TCObject tco = getObject(array);
329: if (tco != null) {
330: tco.byteFieldChanged(null, null, value, index);
331: }
332: byteArray[index] = value;
333: }
334: }
335:
336: /**
337: * Handle System.arraycopy() semantics with managed arrays
338: * @param src Source array
339: * @param srcPos Start index in source
340: * @param dest Destination array
341: * @param destPos Destination start index
342: * @param length Number of items to copy
343: * @throws NullPointerException If src or dest is null
344: */
345: public static void arraycopy(final Object src, final int srcPos,
346: final Object dest, final int destPos, final int length) {
347: // preserve behavior of System.arraycopy()
348: if ((src == null) || (dest == null)) {
349: throw new NullPointerException();
350: }
351:
352: TCObject tcDest = getObject(dest);
353: Class destType = dest.getClass().getComponentType();
354: if (destType == null) {
355: throw new ArrayStoreException();
356: }
357:
358: boolean isDestPrimitive = destType.isPrimitive();
359:
360: // copying into a primitive non-managed array doesn't need any special treatment (even if the source array is
361: // managed. If the source array is managed and non-primitive, you'll get an ArrayStoreException as expected)
362: if (isDestPrimitive && tcDest == null) {
363: System.arraycopy(src, srcPos, dest, destPos, length);
364: return;
365: }
366:
367: // avoid this lookup if we returned above
368: TCObject tcSrc = getObject(src);
369:
370: if ((tcDest != null) || (tcSrc != null)) {
371:
372: Class srcType = src.getClass().getComponentType();
373: if (srcType == null) {
374: throw new ArrayStoreException();
375: }
376: boolean isSrcPrimitive = srcType.isPrimitive();
377:
378: if (isDestPrimitive) {
379: int destCode = getCodeForType(destType);
380:
381: // Check if both arrays have the same primitive types. If not, throw an ArrayStoreException.
382: if (isSrcPrimitive) {
383: int srcCode = getCodeForType(srcType);
384: if (srcCode != destCode) {
385: throw new ArrayStoreException();
386: }
387: } else {
388: throw new ArrayStoreException();
389: }
390:
391: switch (destCode) {
392: case BOOLEAN:
393: booleanArrayCopy((boolean[]) src, srcPos,
394: (boolean[]) dest, destPos, length, tcDest);
395: break;
396: case BYTE:
397: byteArrayCopy((byte[]) src, srcPos, (byte[]) dest,
398: destPos, length, tcDest);
399: break;
400: case CHAR:
401: charArrayCopy((char[]) src, srcPos, (char[]) dest,
402: destPos, length, tcDest);
403: break;
404: case DOUBLE:
405: doubleArrayCopy((double[]) src, srcPos,
406: (double[]) dest, destPos, length, tcDest);
407: break;
408: case FLOAT:
409: floatArrayCopy((float[]) src, srcPos,
410: (float[]) dest, destPos, length, tcDest);
411: break;
412: case INT:
413: intArrayCopy((int[]) src, srcPos, (int[]) dest,
414: destPos, length, tcDest);
415: break;
416: case LONG:
417: longArrayCopy((long[]) src, srcPos, (long[]) dest,
418: destPos, length, tcDest);
419: break;
420: case SHORT:
421: shortArrayCopy((short[]) src, srcPos,
422: (short[]) dest, destPos, length, tcDest);
423: break;
424: default:
425: throw Assert.failure("unexpected type code: "
426: + destCode);
427: }
428: } else {
429: if (isSrcPrimitive) {
430: throw new ArrayStoreException();
431: }
432:
433: Object[] destArray = (Object[]) dest;
434: Object[] srcArray = (Object[]) src;
435:
436: if ((srcPos < 0) || (destPos < 0) || (length < 0)
437: || (srcPos + length > srcArray.length)
438: || (destPos + length > destArray.length))
439: throw new ArrayIndexOutOfBoundsException();
440:
441: Object[] l2subset = new Object[length];
442:
443: if (tcSrc != null) {
444: synchronized (tcSrc.getResolveLock()) {
445: for (int i = 0; i < length; i++) {
446: tcSrc.resolveArrayReference(srcPos + i);
447: // this read of the source array MUST be performed under the resolve lock
448: l2subset[i] = srcArray[srcPos + i];
449: }
450: }
451: } else {
452: System.arraycopy(src, srcPos, l2subset, 0, length);
453: }
454:
455: int actualLength = length;
456: // validate the assignments if necessary, potentially discovering that only a partial copy is legel
457: if (!destType.isAssignableFrom(srcType)) {
458: for (int i = 0, n = l2subset.length; i < n; i++) {
459: Object srcVal = l2subset[i];
460: if (srcVal != null) {
461: if (!destType.isAssignableFrom(srcVal
462: .getClass())) {
463: actualLength = i;
464: break;
465: }
466: }
467: }
468: }
469:
470: // make a second copy because the l2subset can be mutated (refs --> ObjectID)
471: Object[] localSubset = new Object[actualLength];
472: System.arraycopy(l2subset, 0, localSubset, 0,
473: actualLength);
474:
475: if (tcDest != null && actualLength > 0) {
476: tcDest.objectArrayChanged(destPos, l2subset,
477: actualLength);
478: }
479:
480: // only mutate the local array once DSO has a chance to throw portability and TXN exceptions (above)
481: System.arraycopy(localSubset, 0, dest, destPos,
482: actualLength);
483:
484: if (actualLength != length) {
485: throw new ArrayStoreException();
486: }
487:
488: }
489: } else {
490: // no managed arrays in the mix, just do a regular arraycopy
491: System.arraycopy(src, srcPos, dest, destPos, length);
492: }
493: }
494:
495: private static int getCodeForType(Class type) {
496: Integer code = (Integer) primClasses.get(type);
497: if (code == null) {
498: throw new RuntimeException("No code for type " + type);
499: }
500: return code.intValue();
501: }
502:
503: private static void booleanArrayCopy(boolean[] src, int srcPos,
504: boolean[] dest, int destPos, int length, TCObject tcDest) {
505: if ((srcPos < 0) || (destPos < 0) || (length < 0)
506: || (srcPos + length > src.length)
507: || (destPos + length > dest.length))
508: throw new ArrayIndexOutOfBoundsException();
509:
510: boolean[] l2subset = new boolean[length];
511: System.arraycopy(src, srcPos, l2subset, 0, length);
512: tcDest.primitiveArrayChanged(destPos, l2subset, length);
513:
514: // don't mutate local objects until DSO accepts/records the change (above)
515: System.arraycopy(l2subset, 0, dest, destPos, length);
516: }
517:
518: private static void byteArrayCopy(byte[] src, int srcPos,
519: byte[] dest, int destPos, int length, TCObject tcDest) {
520: if ((srcPos < 0) || (destPos < 0) || (length < 0)
521: || (srcPos + length > src.length)
522: || (destPos + length > dest.length))
523: throw new ArrayIndexOutOfBoundsException();
524:
525: byte[] l2subset = new byte[length];
526: System.arraycopy(src, srcPos, l2subset, 0, length);
527: tcDest.primitiveArrayChanged(destPos, l2subset, length);
528:
529: // don't mutate local objects until DSO accepts/records the change (above)
530: System.arraycopy(l2subset, 0, dest, destPos, length);
531: }
532:
533: /**
534: * Copy char[]
535: * @param src Source array
536: * @param srcPos Start in src
537: * @param dest Destination array
538: * @param destPos Start in dest
539: * @param length Number of items to copy
540: * @param tcDest TCObject for dest array
541: *
542: */
543: public static void charArrayCopy(char[] src, int srcPos,
544: char[] dest, int destPos, int length, TCObject tcDest) {
545: if ((srcPos < 0) || (destPos < 0) || (length < 0)
546: || (srcPos + length > src.length)
547: || (destPos + length > dest.length))
548: throw new ArrayIndexOutOfBoundsException();
549:
550: char[] l2subset = new char[length];
551: System.arraycopy(src, srcPos, l2subset, 0, length);
552: tcDest.primitiveArrayChanged(destPos, l2subset, length);
553:
554: // don't mutate local objects until DSO accepts/records the change (above)
555: System.arraycopy(l2subset, 0, dest, destPos, length);
556: }
557:
558: private static void doubleArrayCopy(double[] src, int srcPos,
559: double[] dest, int destPos, int length, TCObject tcDest) {
560: if ((srcPos < 0) || (destPos < 0) || (length < 0)
561: || (srcPos + length > src.length)
562: || (destPos + length > dest.length))
563: throw new ArrayIndexOutOfBoundsException();
564:
565: double[] l2subset = new double[length];
566: System.arraycopy(src, srcPos, l2subset, 0, length);
567: tcDest.primitiveArrayChanged(destPos, l2subset, length);
568:
569: // don't mutate local objects until DSO accepts/records the change (above)
570: System.arraycopy(l2subset, 0, dest, destPos, length);
571: }
572:
573: private static void floatArrayCopy(float[] src, int srcPos,
574: float[] dest, int destPos, int length, TCObject tcDest) {
575: if ((srcPos < 0) || (destPos < 0) || (length < 0)
576: || (srcPos + length > src.length)
577: || (destPos + length > dest.length))
578: throw new ArrayIndexOutOfBoundsException();
579:
580: float[] l2subset = new float[length];
581: System.arraycopy(src, srcPos, l2subset, 0, length);
582: tcDest.primitiveArrayChanged(destPos, l2subset, length);
583:
584: // don't mutate local objects until DSO accepts/records the change (above)
585: System.arraycopy(l2subset, 0, dest, destPos, length);
586: }
587:
588: private static void intArrayCopy(int[] src, int srcPos, int[] dest,
589: int destPos, int length, TCObject tcDest) {
590: if ((srcPos < 0) || (destPos < 0) || (length < 0)
591: || (srcPos + length > src.length)
592: || (destPos + length > dest.length))
593: throw new ArrayIndexOutOfBoundsException();
594:
595: int[] l2subset = new int[length];
596: System.arraycopy(src, srcPos, l2subset, 0, length);
597: tcDest.primitiveArrayChanged(destPos, l2subset, length);
598:
599: // don't mutate local objects until DSO accepts/records the change (above)
600: System.arraycopy(l2subset, 0, dest, destPos, length);
601: }
602:
603: private static void longArrayCopy(long[] src, int srcPos,
604: long[] dest, int destPos, int length, TCObject tcDest) {
605: if ((srcPos < 0) || (destPos < 0) || (length < 0)
606: || (srcPos + length > src.length)
607: || (destPos + length > dest.length))
608: throw new ArrayIndexOutOfBoundsException();
609:
610: long[] l2subset = new long[length];
611: System.arraycopy(src, srcPos, l2subset, 0, length);
612: tcDest.primitiveArrayChanged(destPos, l2subset, length);
613:
614: // don't mutate local objects until DSO accepts/records the change (above)
615: System.arraycopy(l2subset, 0, dest, destPos, length);
616: }
617:
618: private static void shortArrayCopy(short[] src, int srcPos,
619: short[] dest, int destPos, int length, TCObject tcDest) {
620: if ((srcPos < 0) || (destPos < 0) || (length < 0)
621: || (srcPos + length > src.length)
622: || (destPos + length > dest.length))
623: throw new ArrayIndexOutOfBoundsException();
624:
625: short[] l2subset = new short[length];
626: System.arraycopy(src, srcPos, l2subset, 0, length);
627: tcDest.primitiveArrayChanged(destPos, l2subset, length);
628:
629: // don't mutate local objects until DSO accepts/records the change (above)
630: System.arraycopy(l2subset, 0, dest, destPos, length);
631: }
632:
633: private static final int BOOLEAN = 1;
634: private static final int BYTE = 2;
635: private static final int CHAR = 3;
636: private static final int DOUBLE = 4;
637: private static final int FLOAT = 5;
638: private static final int INT = 6;
639: private static final int LONG = 7;
640: private static final int SHORT = 8;
641:
642: static {
643: primClasses.put(Boolean.TYPE, new Integer(BOOLEAN));
644: primClasses.put(Byte.TYPE, new Integer(BYTE));
645: primClasses.put(Character.TYPE, new Integer(CHAR));
646: primClasses.put(Double.TYPE, new Integer(DOUBLE));
647: primClasses.put(Float.TYPE, new Integer(FLOAT));
648: primClasses.put(Integer.TYPE, new Integer(INT));
649: primClasses.put(Long.TYPE, new Integer(LONG));
650: primClasses.put(Short.TYPE, new Integer(SHORT));
651: }
652:
653: }
|