summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala27
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala17
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala6
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala11
-rw-r--r--src/library/scala/Array.scala8
-rw-r--r--src/library/scala/collection/mutable/WrappedArray.scala4
-rw-r--r--src/library/scala/runtime/ArrayRuntime.java16
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala14
-rw-r--r--test/files/run/arrayclone.scala106
11 files changed, 186 insertions, 29 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 2f66f672d8..7f351293c5 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -730,16 +730,25 @@ abstract class GenICode extends SubComponent {
else ctx
ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1)
-
- val hostClass = fun match {
- case Select(qualifier, _)
- if (qualifier.tpe.typeSymbol != ArrayClass) =>
- qualifier.tpe.typeSymbol
- case _ => sym.owner
+ val cm = CALL_METHOD(sym, invokeStyle)
+
+ /** In a couple cases, squirrel away a little extra information in the
+ * CALL_METHOD for use by GenJVM.
+ */
+ fun match {
+ case Select(qual, _) =>
+ val qualSym = qual.tpe.typeSymbol
+ if (qualSym == ArrayClass) cm setTargetTypeKind toTypeKind(qual.tpe)
+ else cm setHostClass qualSym
+
+ if (settings.debug.value) log(
+ if (qualSym == ArrayClass) "Stored target type kind " + toTypeKind(qual.tpe) + " for " + sym.fullNameString
+ else "Set more precise host class for " + sym.fullNameString + " host: " + qualSym
+ )
+ case _ =>
}
- if (settings.debug.value && hostClass != sym.owner)
- log("Set more precise host class for " + sym.fullNameString + " host: " + hostClass);
- ctx1.bb.emit(CALL_METHOD(sym, invokeStyle) setHostClass hostClass, tree.pos)
+ ctx1.bb.emit(cm, tree.pos)
+
if (sym == ctx1.method.symbol) {
ctx1.method.recursive = true
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
index 9785279e80..f7baab286b 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -325,17 +325,20 @@ trait Opcodes { self: ICodes =>
var hostClass: Symbol = method.owner;
def setHostClass(cls: Symbol): this.type = { hostClass = cls; this }
- override def consumed = {
- var result = method.tpe.paramTypes.length;
- result = result + (style match {
+ /** This is specifically for preserving the target native Array type long
+ * enough that clone() can generate the right call.
+ */
+ var targetTypeKind: TypeKind = UNIT // the default should never be used, so UNIT should fail fast.
+ def setTargetTypeKind(tk: TypeKind) = targetTypeKind = tk
+
+ override def consumed = method.tpe.paramTypes.length + (
+ style match {
case Dynamic | InvokeDynamic => 1
case Static(true) => 1
case Static(false) => 0
case SuperCall(_) => 1
- });
-
- result;
- }
+ }
+ )
override def consumedTypes = {
val args = method.tpe.paramTypes map toTypeKind
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index d622edc8ab..7496107798 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -82,6 +82,7 @@ abstract class GenJVM extends SubComponent {
val StringBuilderType = new JObjectType(StringBuilderClass)
val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY)
+ val arrayCloneType = new JMethodType(JObjectType.JAVA_LANG_OBJECT, JType.EMPTY_ARRAY)
val MethodTypeType = new JObjectType("java.dyn.MethodType")
val JavaLangClassType = new JObjectType("java.lang.Class")
val MethodHandleType = new JObjectType("java.dyn.MethodHandle")
@@ -1087,6 +1088,11 @@ abstract class GenJVM extends SubComponent {
case CALL_PRIMITIVE(primitive) =>
genPrimitive(primitive, instr.pos)
+ /** Special handling to access native Array.clone() */
+ case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
+ val target: String = javaType(call.targetTypeKind).getSignature()
+ jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType)
+
case call @ CALL_METHOD(method, style) =>
val owner: String = javaName(method.owner)
// reference the type of the receiver instead of the method owner (if not an interface!)
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index e14a7d796c..155f5332d8 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -154,6 +154,7 @@ trait Definitions {
def arrayApplyMethod = getMember(ScalaRunTimeModule, "array_apply")
def arrayUpdateMethod = getMember(ScalaRunTimeModule, "array_update")
def arrayLengthMethod = getMember(ScalaRunTimeModule, "array_length")
+ def arrayCloneMethod = getMember(ScalaRunTimeModule, "array_clone")
// classes with special meanings
lazy val NotNullClass = getClass("scala.NotNull")
@@ -215,6 +216,7 @@ trait Definitions {
def Array_apply = getMember(ArrayClass, nme.apply)
def Array_update = getMember(ArrayClass, nme.update)
def Array_length = getMember(ArrayClass, nme.length)
+ lazy val Array_clone = getMember(ArrayClass, nme.clone_)
lazy val ArrayModule = getModule("scala.Array")
def ArrayModule_apply = getMember(ArrayModule, nme.apply)
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index c508400711..81fe911328 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -365,7 +365,8 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
def isArrayMethodSignature =
(methSym.name == nme.length && params.isEmpty) ||
(methSym.name == nme.update && (structResType.typeSymbol eq UnitClass)) ||
- (methSym.name == nme.apply && params.size == 1)
+ (methSym.name == nme.apply && params.size == 1) ||
+ (methSym.name == nme.clone_ && params.isEmpty)
def isDefinitelyArray = isArrayMethodSignature && (qualSym == ArrayClass)
def isMaybeArray = isArrayMethodSignature && (qualSym == ObjectClass) // precondition: !isDefinitelyArray
@@ -374,6 +375,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
case nme.length => REF(boxMethod(IntClass)) APPLY (REF(arrayLengthMethod) APPLY args)
case nme.update => REF(arrayUpdateMethod) APPLY List(args(0), (REF(unboxMethod(IntClass)) APPLY args(1)), args(2))
case nme.apply => REF(arrayApplyMethod) APPLY List(args(0), (REF(unboxMethod(IntClass)) APPLY args(1)))
+ case nme.clone_ => REF(arrayCloneMethod) APPLY List(args(0))
}
def genArrayTest = {
def oneTest(s: Symbol) = qual IS_OBJ arrayType(s.tpe)
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index e92ba64469..7722939aaf 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -477,17 +477,6 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
private def cast(tree: Tree, pt: Type): Tree =
tree AS_ATTR pt
- /** Is symbol a member of unboxed arrays (which will be expanded directly
- * later)?
- *
- * @param sym ..
- * @return <code>true</code> if ..
- */
- private def isUnboxedArrayMember(sym: Symbol) = sym.name match {
- case nme.apply | nme.length | nme.update => true
- case _ => sym.owner == ObjectClass
- }
-
private def isUnboxedValueMember(sym: Symbol) =
sym != NoSymbol && isValueClass(sym.owner)
diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala
index fd171c5a5e..afaaed7c7c 100644
--- a/src/library/scala/Array.scala
+++ b/src/library/scala/Array.scala
@@ -538,4 +538,12 @@ final class Array[T](_length: Int) {
* <code>length <= i</code>
*/
def update(i: Int, x: T) { throw new Error() }
+
+ /** <p>
+ * Clone the Array.
+ * </p>
+ *
+ * @return A clone of the Array.
+ */
+ override def clone: Array[T] = throw new Error()
}
diff --git a/src/library/scala/collection/mutable/WrappedArray.scala b/src/library/scala/collection/mutable/WrappedArray.scala
index cedeb673e0..6652f5e40a 100644
--- a/src/library/scala/collection/mutable/WrappedArray.scala
+++ b/src/library/scala/collection/mutable/WrappedArray.scala
@@ -43,6 +43,9 @@ abstract class WrappedArray[T] extends IndexedSeq[T] with ArrayLike[T, WrappedAr
def array: Array[T]
override def stringPrefix = "WrappedArray"
+ /** Clones this object, including the underlying Array. */
+ override def clone: WrappedArray[T] = WrappedArray make array.clone()
+
/** Creates new builder for this collection ==> move to subclasses
*/
override protected[this] def newBuilder: Builder[T, WrappedArray[T]] =
@@ -50,7 +53,6 @@ abstract class WrappedArray[T] extends IndexedSeq[T] with ArrayLike[T, WrappedAr
}
object WrappedArray {
-
def make[T](x: AnyRef): WrappedArray[T] = x match {
case x: Array[AnyRef] => wrapRefArray[AnyRef](x).asInstanceOf[WrappedArray[T]]
case x: Array[Int] => wrapIntArray(x).asInstanceOf[WrappedArray[T]]
diff --git a/src/library/scala/runtime/ArrayRuntime.java b/src/library/scala/runtime/ArrayRuntime.java
new file mode 100644
index 0000000000..b382fdf8f6
--- /dev/null
+++ b/src/library/scala/runtime/ArrayRuntime.java
@@ -0,0 +1,16 @@
+package scala.runtime;
+
+/**
+ * Methods on Java arrays
+ */
+class ArrayRuntime {
+ static boolean[] cloneArray(boolean[] array) { return array.clone(); }
+ static byte[] cloneArray(byte[] array) { return array.clone(); }
+ static short[] cloneArray(short[] array) { return array.clone(); }
+ static char[] cloneArray(char[] array) { return array.clone(); }
+ static int[] cloneArray(int[] array) { return array.clone(); }
+ static long[] cloneArray(long[] array) { return array.clone(); }
+ static float[] cloneArray(float[] array) { return array.clone(); }
+ static double[] cloneArray(double[] array) { return array.clone(); }
+ static Object[] cloneArray(Object[] array) { return array.clone(); }
+}
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index ecc81c074e..2f6ffb5535 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -74,6 +74,20 @@ object ScalaRunTime {
case null => throw new NullPointerException
}
+ def array_clone(xs: AnyRef): AnyRef = xs match {
+ case x: Array[AnyRef] => ArrayRuntime.cloneArray(x)
+ case x: Array[Int] => ArrayRuntime.cloneArray(x)
+ case x: Array[Double] => ArrayRuntime.cloneArray(x)
+ case x: Array[Long] => ArrayRuntime.cloneArray(x)
+ case x: Array[Float] => ArrayRuntime.cloneArray(x)
+ case x: Array[Char] => ArrayRuntime.cloneArray(x)
+ case x: Array[Byte] => ArrayRuntime.cloneArray(x)
+ case x: Array[Short] => ArrayRuntime.cloneArray(x)
+ case x: Array[Boolean] => ArrayRuntime.cloneArray(x)
+ case x: Array[Unit] => x
+ case null => throw new NullPointerException
+ }
+
/** Convert a numeric value array to an object array.
* Needed to deal with vararg arguments of primtive types that are passed
* to a generic Java vararg parameter T ...
diff --git a/test/files/run/arrayclone.scala b/test/files/run/arrayclone.scala
new file mode 100644
index 0000000000..2b56c53469
--- /dev/null
+++ b/test/files/run/arrayclone.scala
@@ -0,0 +1,106 @@
+object Test extends Application{
+ BooleanArrayClone;
+ ByteArrayClone;
+ ShortArrayClone;
+ CharArrayClone;
+ IntArrayClone;
+ LongArrayClone;
+ FloatArrayClone;
+ DoubleArrayClone;
+ ObjectArrayClone;
+ PolymorphicArrayClone;
+}
+
+object BooleanArrayClone{
+ val it : Array[Boolean] = Array(true, false);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = false;
+ assert(it(0) == true)
+}
+
+object ByteArrayClone{
+ val it : Array[Byte] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object ShortArrayClone{
+ val it : Array[Short] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object CharArrayClone{
+ val it : Array[Char] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object IntArrayClone{
+ val it : Array[Int] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object LongArrayClone{
+ val it : Array[Long] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object FloatArrayClone{
+ val it : Array[Float] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object DoubleArrayClone{
+ val it : Array[Double] = Array(1, 0);
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = 0;
+ assert(it(0) == 1)
+}
+
+object ObjectArrayClone{
+ val it : Array[String] = Array("1", "0");
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = "0";
+ assert(it(0) == "1")
+}
+
+object PolymorphicArrayClone{
+ def testIt[T](it : Array[T], one : T, zero : T) = {
+ val cloned = it.clone();
+ assert(cloned.sameElements(it));
+ cloned(0) = zero;
+ assert(it(0) == one)
+ }
+
+ testIt(Array("one", "two"), "one", "two");
+
+ class Mangler[T: Manifest](ts : T*){
+ // this will always be a BoxedAnyArray even after we've unboxed its contents.
+ val it = ts.toArray[T];
+ }
+
+ val mangled = new Mangler[Int](0, 1);
+
+ val y : Array[Int] = mangled.it; // make sure it's unboxed
+
+ testIt(mangled.it, 0, 1);
+}