/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.classloader;
import static org.jikesrvm.VM.NOT_REACHED;
import static org.jikesrvm.classloader.ClassLoaderConstants.*;
import static org.jikesrvm.mm.mminterface.Barriers.*;
import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_BOOLEAN;
import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_CHAR;
import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_DOUBLE;
import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_SHORT;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_BOOLEAN;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_CHAR;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_DOUBLE;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_FLOAT;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_INT;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_LONG;
import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_SHORT;
import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
import org.jikesrvm.VM;
import org.jikesrvm.architecture.ArchConstants;
import org.jikesrvm.mm.mminterface.Barriers;
import org.jikesrvm.mm.mminterface.HandInlinedScanning;
import org.jikesrvm.mm.mminterface.MemoryManager;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.objectmodel.TIB;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.Memory;
import org.jikesrvm.runtime.RuntimeEntrypoints;
import org.jikesrvm.runtime.Statics;
import org.vmmagic.pragma.Entrypoint;
import org.vmmagic.pragma.Inline;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.NonMoving;
import org.vmmagic.pragma.Pure;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Offset;
/**
* Description of a java "array" type. <p>
*
* This description is not read from a ".class" file, but rather
* is manufactured by the VM as execution proceeds.
*
* @see RVMType
* @see RVMClass
* @see Primitive
* @see UnboxedType
*/
@NonMoving
public final class RVMArray extends RVMType {
/*
* We hold on to a number of commonly used arrays for easy access.
*/
public static final RVMArray BooleanArray;
public static final RVMArray ByteArray;
public static final RVMArray CharArray;
public static final RVMArray ShortArray;
public static final RVMArray IntArray;
public static final RVMArray LongArray;
public static final RVMArray FloatArray;
public static final RVMArray DoubleArray;
public static final RVMArray JavaLangObjectArray;
static {
BooleanArray = (RVMArray) TypeReference.BooleanArray.resolve();
CharArray = (RVMArray) TypeReference.CharArray.resolve();
FloatArray = (RVMArray) TypeReference.FloatArray.resolve();
DoubleArray = (RVMArray) TypeReference.DoubleArray.resolve();
ByteArray = (RVMArray) TypeReference.ByteArray.resolve();
ShortArray = (RVMArray) TypeReference.ShortArray.resolve();
IntArray = (RVMArray) TypeReference.IntArray.resolve();
LongArray = (RVMArray) TypeReference.LongArray.resolve();
JavaLangObjectArray = (RVMArray) TypeReference.JavaLangObjectArray.resolve();
}
/**
* The RVMType object for elements of this array type.
*/
private final RVMType elementType;
/**
* The log of the element size for this array type.
*/
private final int logElementSize;
/**
* The RVMType object for the innermost element of this array type.
*/
private final RVMType innermostElementType;
/**
* The dimension of the innermost element of this array type.
*/
@Entrypoint(fieldMayBeFinal = true)
@SuppressWarnings({"unused"})
private final int innermostElementTypeDimension;
/**
* The desired alignment for instances of this type.
* Cached rather than computed because this is a frequently
* asked question
*/
private final int alignment;
/**
* Reference Count GC: is this type acyclic?
*/
private final boolean acyclic;
/**
* The TIB for this type, created when the array is resolved.
*/
private TIB typeInformationBlock;
/**
* current class-loading stage (loaded, resolved or initialized)
*/
private byte state;
/**
* Is this array type in the bootimage?
*/
private boolean inBootImage;
/**
* Name - something like "[I" or "[Ljava.lang.String;"
*/
@Override
@Pure
public String toString() {
return getDescriptor().toString().replace('/', '.');
}
/**
* @return 1
*/
@Override
@Pure
@Uninterruptible
public int getStackWords() {
return 1;
}
@Override
@Pure
@Uninterruptible
public int getMemoryBytes() {
return BYTES_IN_ADDRESS;
}
/**
* @return element type.
*/
@Uninterruptible
public RVMType getElementType() {
return elementType;
}
/**
* @return innermost element type
*/
@Uninterruptible
public RVMType getInnermostElementType() {
return innermostElementType;
}
/**
* @return alignment for instances of this array type
*/
@Uninterruptible
public int getAlignment() {
return alignment;
}
/**
* Size, in bytes, of an array element, log base 2.
* @return log base 2 of array element size
*/
@Uninterruptible
public int getLogElementSize() {
return logElementSize;
}
/**
* Calculate the size, in bytes, of an array element, log base 2.
* @return log base 2 of array element size
*/
private int computeLogElementSize() {
if (elementType.getTypeRef().equals(TypeReference.Code)) {
return ArchConstants.getLogInstructionWidth();
}
switch (getDescriptor().parseForArrayElementTypeCode()) {
case ClassTypeCode:
return LOG_BYTES_IN_ADDRESS;
case ArrayTypeCode:
return LOG_BYTES_IN_ADDRESS;
case BooleanTypeCode:
return LOG_BYTES_IN_BOOLEAN;
case ByteTypeCode:
return 0;
case ShortTypeCode:
return LOG_BYTES_IN_SHORT;
case IntTypeCode:
return LOG_BYTES_IN_INT;
case LongTypeCode:
return LOG_BYTES_IN_LONG;
case FloatTypeCode:
return LOG_BYTES_IN_FLOAT;
case DoubleTypeCode:
return LOG_BYTES_IN_DOUBLE;
case CharTypeCode:
return LOG_BYTES_IN_CHAR;
}
if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
return -1;
}
/**
* Total size, in bytes, of an instance of this array type (including object header).
* @param numelts number of array elements in the instance
* @return size in bytes
*/
@Inline
@Pure
@Uninterruptible
public int getInstanceSize(int numelts) {
return ObjectModel.computeArrayHeaderSize(this) + (numelts << getLogElementSize());
}
/**
* @return false
*/
@Override
@Pure
@Uninterruptible
public boolean hasFinalizer() {
return false;
}
/**
* Static fields of this array type.
*/
@Override
@Pure
public RVMField[] getStaticFields() {
return RVMType.JavaLangObjectType.getStaticFields();
}
/**
* Non-static fields of this array type.
*/
@Override
@Pure
public RVMField[] getInstanceFields() {
return RVMType.JavaLangObjectType.getInstanceFields();
}
/**
* Statically dispatched methods of this array type.
*/
@Override
@Pure
public RVMMethod[] getStaticMethods() {
return RVMType.JavaLangObjectType.getStaticMethods();
}
/**
* Virtually dispatched methods of this array type.
*/
@Override
@Pure
public RVMMethod[] getVirtualMethods() {
return RVMType.JavaLangObjectType.getVirtualMethods();
}
/**
* Runtime type information for this array type.
*/
@Override
@Pure
@Uninterruptible
public TIB getTypeInformationBlock() {
if (VM.VerifyAssertions) VM._assert(isResolved());
return typeInformationBlock;
}
/**
* @return 1
*/
@Override
@Pure
@Uninterruptible
public int getTypeDepth() {
return 1;
}
@Override
@Pure
@Uninterruptible
public boolean isAcyclicReference() {
return acyclic;
}
/**
* Number of [ in descriptor for arrays
*/
@Override
@Pure
@Uninterruptible
public int getDimensionality() {
return dimension;
}
@Override
@Uninterruptible
public boolean isResolved() {
return state >= CLASS_RESOLVED;
}
@Override
@Uninterruptible
public boolean isInstantiated() {
return state >= CLASS_INSTANTIATED;
}
@Override
@Uninterruptible
public boolean isInitialized() {
return state == CLASS_INITIALIZED;
}
@Override
public void markAsBootImageClass() {
inBootImage = true;
}
@Override
@Uninterruptible
public boolean isInBootImage() {
return inBootImage;
}
/**
* Get the offset in instances of this type assigned to the thin lock word.
*/
@Override
@Uninterruptible
public Offset getThinLockOffset() {
return ObjectModel.defaultThinLockOffset();
}
/**
* @return <code>false</code>
*/
@Override
@Pure
@Uninterruptible
public boolean isClassType() {
return false;
}
/**
* @return <code>true</code>
*/
@Override
@Pure
@Uninterruptible
public boolean isArrayType() {
return true;
}
/**
* @return <code>false</code>
*/
@Override
@Pure
@Uninterruptible
public boolean isPrimitiveType() {
return false;
}
/**
* @return <code>true</code>
*/
@Override
@Pure
@Uninterruptible
public boolean isReferenceType() {
return true;
}
/**
* @return <code>false</code>
*/
@Override
@Pure
@Uninterruptible
public boolean isUnboxedType() {
return false;
}
RVMArray(TypeReference typeRef, RVMType elementType) {
super(typeRef, typeRef.getDimensionality(), null);
this.elementType = elementType;
this.logElementSize = computeLogElementSize();
depth = 1;
if (elementType.isArrayType()) {
innermostElementType = elementType.asArray().getInnermostElementType();
} else {
innermostElementType = elementType;
}
innermostElementTypeDimension = innermostElementType.dimension;
if (VM.BuildForIA32 && typeRef == TypeReference.CodeArray) {
this.alignment = 16;
} else if (BYTES_IN_DOUBLE != BYTES_IN_ADDRESS) {
// Desired alignment on 32bit architectures
if (elementType.isDoubleType() || elementType.isLongType()) {
this.alignment = BYTES_IN_DOUBLE;
} else {
this.alignment = BYTES_IN_ADDRESS;
}
} else {
this.alignment = BYTES_IN_DOUBLE;
}
// RCGC: Array is acyclic if its references are acyclic
acyclic = elementType.isAcyclicReference();
/* Set GC metadata for this type */
boolean isRefArray = elementType.isReferenceType();
referenceOffsets = isRefArray ? REFARRAY_OFFSET_ARRAY : NOREFS_OFFSET_ARRAY;
state = CLASS_LOADED;
if (VM.verboseClassLoading) VM.sysWriteln("[Loaded " + this.getDescriptor() + "]");
if (VM.verboseClassLoading) VM.sysWriteln("[Loaded superclasses of " + this.getDescriptor() + "]");
}
/**
* Resolve an array.
* Also forces the resolution of the element type.
*/
@Override
public void resolve() {
synchronized (this) {
if (isResolved()) return;
if (VM.VerifyAssertions) VM._assert(state == CLASS_LOADED);
}
// Resolving the element type requires a lock on the RVMType object that
// represents the element type. It does *not* require the lock of this object.
// It is thus safe to release the lock on this object while the element type is
// being resolved. This helps to to prevent deadlocks when compiling with multiple
// threads.
elementType.resolve();
synchronized (this) {
if (isResolved()) return;
// Using the type information block for java.lang.Object as a template,
// build a type information block for this new array type by copying the
// virtual method fields and substituting an appropriate type field.
//
TIB javaLangObjectTIB = RVMType.JavaLangObjectType.getTypeInformationBlock();
int alignCode = elementType.isReferenceType() ? HandInlinedScanning.referenceArray() : HandInlinedScanning.primitiveArray();
TIB allocatedTib = MemoryManager.newTIB(javaLangObjectTIB.numVirtualMethods(), alignCode);
superclassIds = DynamicTypeCheck.buildSuperclassIds(this);
doesImplement = DynamicTypeCheck.buildDoesImplement(this);
publishResolved(allocatedTib, superclassIds, doesImplement);
MemoryManager.notifyClassResolved(this);
}
}
/**
* Atomically initialize the important parts of the TIB and let the world know this type is
* resolved.
*
* @param allocatedTib The TIB that has been allocated for this type
* @param superclassIds The calculated superclass ids array
* @param doesImplement The calculated does implement array
*/
@Uninterruptible
private void publishResolved(TIB allocatedTib, short[] superclassIds, int[] doesImplement) {
Statics.setSlotContents(getTibOffset(), allocatedTib);
allocatedTib.setType(this);
allocatedTib.setSuperclassIds(superclassIds);
allocatedTib.setDoesImplement(doesImplement);
if (!(elementType.isPrimitiveType() || elementType.isUnboxedType())) {
allocatedTib.setArrayElementTib(elementType.getTypeInformationBlock());
}
typeInformationBlock = allocatedTib;
state = CLASS_RESOLVED;
}
@Override
public void allBootImageTypesResolved() {
// nothing to do
}
/**
* Instantiate an array.
* Main result is to copy the virtual methods from JavaLangObject's TIB.
*/
@Override
public synchronized void instantiate() {
if (isInstantiated()) return;
if (VM.VerifyAssertions) VM._assert(state == CLASS_RESOLVED);
if (VM.TraceClassLoading && VM.runningVM) {
VM.sysWriteln("RVMArray: instantiate " + this);
}
// Initialize TIB slots for virtual methods (copy from superclass == Object)
RVMType objectType = RVMType.JavaLangObjectType;
if (VM.VerifyAssertions) VM._assert(objectType.isInstantiated());
TIB javaLangObjectTIB = objectType.getTypeInformationBlock();
for (int i = 0; i < javaLangObjectTIB.numVirtualMethods(); i++) {
typeInformationBlock.setVirtualMethod(i, javaLangObjectTIB.getVirtualMethod(i));
}
SpecializedMethodManager.notifyTypeInstantiated(this);
state = CLASS_INITIALIZED; // arrays have no "initialize" phase
}
/**
* Initialization is a no-op (arrays have no {@code <clinit>} method).
*/
@Override
public void initialize() { }
@Override
public void prepareForFirstUse() {
resolve();
instantiate();
}
//-------------------------------------------------------------------------------------------------//
// Misc static methods. //
//-------------------------------------------------------------------------------------------------//
/**
* Get description of specified primitive array.
* @param atype array type number (see "newarray" bytecode description in Java VM Specification)
* @return array description
*/
@Pure
public static RVMArray getPrimitiveArrayType(int atype) {
switch (atype) {
case 4:
return BooleanArray;
case 5:
return CharArray;
case 6:
return FloatArray;
case 7:
return DoubleArray;
case 8:
return ByteArray;
case 9:
return ShortArray;
case 10:
return IntArray;
case 11:
return LongArray;
}
if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
return null;
}
//--------------------------------------------------------------------------------------------------//
// Support for array copy //
//--------------------------------------------------------------------------------------------------//
/**
* Perform an array copy for arrays of bytes.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(byte[] src, int srcIdx, byte[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx >= (dstIdx + BYTES_IN_ADDRESS)) && BYTE_BULK_COPY_SUPPORTED) {
if (NEEDS_BYTE_ASTORE_BARRIER || NEEDS_BYTE_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx);
Barriers.byteBulkCopy(src, srcOffset, dst, dstOffset, len);
} else {
Memory.arraycopy8Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of bytes. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(byte[] src, int srcIdx, byte[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of booleans.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(boolean[] src, int srcIdx, boolean[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx >= (dstIdx + BYTES_IN_ADDRESS / BYTES_IN_BOOLEAN)) && BOOLEAN_BULK_COPY_SUPPORTED) {
if (NEEDS_BOOLEAN_ASTORE_BARRIER || NEEDS_BOOLEAN_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_BOOLEAN);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_BOOLEAN);
Barriers.booleanBulkCopy(src, srcOffset, dst, dstOffset, len);
} else {
Memory.arraycopy8Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of booleans. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(boolean[] src, int srcIdx, boolean[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of shorts.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(short[] src, int srcIdx, short[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx >= (dstIdx + BYTES_IN_ADDRESS / BYTES_IN_SHORT)) && SHORT_BULK_COPY_SUPPORTED) {
if (NEEDS_SHORT_ASTORE_BARRIER || NEEDS_SHORT_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_SHORT);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_SHORT);
Barriers.shortBulkCopy(src, srcOffset, dst, dstOffset, len << LOG_BYTES_IN_SHORT);
} else {
Memory.arraycopy16Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of shorts. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(short[] src, int srcIdx, short[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of chars.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(char[] src, int srcIdx, char[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx >= (dstIdx + BYTES_IN_ADDRESS / BYTES_IN_CHAR)) && CHAR_BULK_COPY_SUPPORTED) {
if (NEEDS_CHAR_ASTORE_BARRIER || NEEDS_CHAR_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_CHAR);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_CHAR);
Barriers.charBulkCopy(src, srcOffset, dst, dstOffset, len << LOG_BYTES_IN_CHAR);
} else {
Memory.arraycopy16Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of chars. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(char[] src, int srcIdx, char[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of ints.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(int[] src, int srcIdx, int[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx >= dstIdx) && INT_BULK_COPY_SUPPORTED) {
if (NEEDS_INT_ASTORE_BARRIER || NEEDS_INT_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_INT);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_INT);
Barriers.intBulkCopy(src, srcOffset, dst, dstOffset, len << LOG_BYTES_IN_INT);
} else {
Memory.arraycopy32Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of ints. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(int[] src, int srcIdx, int[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of floats.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(float[] src, int srcIdx, float[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx > dstIdx) && FLOAT_BULK_COPY_SUPPORTED) {
if (NEEDS_FLOAT_ASTORE_BARRIER || NEEDS_FLOAT_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_FLOAT);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_FLOAT);
Barriers.floatBulkCopy(src, srcOffset, dst, dstOffset, len << LOG_BYTES_IN_FLOAT);
} else {
Memory.arraycopy32Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of floats. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(float[] src, int srcIdx, float[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of longs.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(long[] src, int srcIdx, long[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx > dstIdx) && LONG_BULK_COPY_SUPPORTED) {
if (NEEDS_LONG_ASTORE_BARRIER || NEEDS_LONG_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_LONG);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_LONG);
Barriers.longBulkCopy(src, srcOffset, dst, dstOffset, len << LOG_BYTES_IN_LONG);
} else {
Memory.arraycopy64Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of longs. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(long[] src, int srcIdx, long[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of doubles.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,3,4})
public static void arraycopy(double[] src, int srcIdx, double[] dst, int dstIdx, int len) {
// Don't do any of the assignments if the offsets and lengths
// are in error
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
if ((src != dst || srcIdx > dstIdx) && DOUBLE_BULK_COPY_SUPPORTED) {
if (NEEDS_DOUBLE_ASTORE_BARRIER || NEEDS_DOUBLE_ALOAD_BARRIER) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_DOUBLE);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_DOUBLE);
Barriers.doubleBulkCopy(src, srcOffset, dst, dstOffset, len << LOG_BYTES_IN_DOUBLE);
} else {
Memory.arraycopy64Bit(src, srcIdx, dst, dstIdx, len);
}
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform element-by-element arraycopy for array of doubles. Used
* when bulk copy is not possible.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
@NoInline // unlikely case, so reduce code space costs
private static void arraycopyPiecemeal(double[] src, int srcIdx, double[] dst, int dstIdx, int len) {
if (srcIdx < dstIdx) {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
} else {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
}
}
/**
* Perform an array copy for arrays of objects. This code must
* ensure that write barriers are invoked as if the copy were
* performed element-by-element.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
public static void arraycopy(Object[] src, int srcIdx, Object[] dst, int dstIdx, int len) {
// Check offsets and lengths before doing anything
if (srcIdx >= 0 &&
dstIdx >= 0 &&
len >= 0 &&
(srcIdx + len) >= 0 &&
(srcIdx + len) <= src.length &&
(dstIdx + len) >= 0 &&
(dstIdx + len) <= dst.length) {
RVMType lhs = Magic.getObjectType(dst).asArray().getElementType();
RVMType rhs = Magic.getObjectType(src).asArray().getElementType();
if ((lhs == rhs) || (lhs == RVMType.JavaLangObjectType) || RuntimeEntrypoints.isAssignableWith(lhs, rhs)) {
arraycopyNoCheckcast(src, srcIdx, dst, dstIdx, len);
} else {
arraycopyPiecemeal(src, srcIdx, dst, dstIdx, len);
}
} else {
failWithIndexOutOfBoundsException();
}
}
/**
* Perform an array copy for arrays of objects where the possibility
* of an ArrayStoreException being thrown <i>does not</i> exist.
* This may be done using direct byte copies, <i>however</i>, write
* barriers must be explicitly invoked (if required by the GC) since
* the write barrier associated with an explicit array store
* (aastore) will be bypassed.
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting source index
* @param len The number of array elements to be copied
*/
private static void arraycopyNoCheckcast(Object[] src, int srcIdx, Object[] dst, int dstIdx, int len) {
Offset srcOffset = Offset.fromIntZeroExtend(srcIdx << LOG_BYTES_IN_ADDRESS);
Offset dstOffset = Offset.fromIntZeroExtend(dstIdx << LOG_BYTES_IN_ADDRESS);
int bytes = len << LOG_BYTES_IN_ADDRESS;
if (((src != dst) || (srcIdx > dstIdx)) && OBJECT_BULK_COPY_SUPPORTED) {
if (NEEDS_OBJECT_ASTORE_BARRIER || NEEDS_OBJECT_ALOAD_BARRIER) {
Barriers.objectBulkCopy(src, srcOffset, dst, dstOffset, bytes);
} else {
Memory.alignedWordCopy(Magic.objectAsAddress(dst).plus(dstOffset), Magic.objectAsAddress(src).plus(srcOffset), bytes);
}
} else {
arraycopyPiecemealNoCheckcast(src, dst, len, srcOffset, dstOffset, bytes);
}
}
/**
* Perform element-by-element arraycopy for array of objects without
* performing checkcast. Used when bulk copy is not possible, but
* checkcast is still not necessary. If barriers are required they
* must be explicitly invoked.
*
* @param src The source array
* @param dst The destination array
* @param len The number of array elements to be copied
* @param srcOffset The starting offset in the source array
* @param dstOffset The starting offset in the destination array.
* @param bytes the number of bytes to copy
*/
private static void arraycopyPiecemealNoCheckcast(Object[] src, Object[] dst, int len,
Offset srcOffset, Offset dstOffset, int bytes) {
// set up things according to the direction of the copy
int increment;
if (srcOffset.sGT(dstOffset)) { // direction of copy
increment = BYTES_IN_ADDRESS;
} else {
srcOffset = srcOffset.plus(bytes - BYTES_IN_ADDRESS);
dstOffset = dstOffset.plus(bytes - BYTES_IN_ADDRESS);
increment = -BYTES_IN_ADDRESS;
}
// perform the copy
while (len-- != 0) {
Object value;
if (NEEDS_OBJECT_ALOAD_BARRIER) {
value = Barriers.objectArrayRead(src, srcOffset.toInt() >> LOG_BYTES_IN_ADDRESS);
} else {
value = Magic.getObjectAtOffset(src, srcOffset);
}
if (NEEDS_OBJECT_ASTORE_BARRIER) {
Barriers.objectArrayWrite(dst, dstOffset.toInt() >> LOG_BYTES_IN_ADDRESS, value);
} else {
Magic.setObjectAtOffset(dst, dstOffset, value);
}
srcOffset = srcOffset.plus(increment);
dstOffset = dstOffset.plus(increment);
}
}
/**
* Perform an array copy for arrays of objects where the possibility
* of an ArrayStoreException being thrown exists. This must be done
* with element by element assignments in the correct order.
* <i>Since write barriers are implicitly performed on explicit
* array stores, there is no need to explicitly invoke a write
* barrier in this code.</i>
*
* @param src The source array
* @param srcIdx The starting source index
* @param dst The destination array
* @param dstIdx The starting destination index
* @param len The number of array elements to be copied
*/
private static void arraycopyPiecemeal(Object[] src, int srcIdx, Object[] dst, int dstIdx, int len) {
if ((src != dst) || srcIdx >= dstIdx) {
while (len-- != 0) {
dst[dstIdx++] = src[srcIdx++];
}
} else {
srcIdx += len;
dstIdx += len;
while (len-- != 0) {
dst[--dstIdx] = src[--srcIdx];
}
}
}
@NoInline
private static void failWithIndexOutOfBoundsException() {
throw new ArrayIndexOutOfBoundsException();
}
}