From 36ef60e68c03bc1c7fd2e910ae7d70d4ec32d3bf Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 16 Jan 2010 03:54:39 +0000 Subject: Exposed native Array clone() method. Review by dragos. --- .../scala/tools/nsc/backend/icode/GenICode.scala | 27 ++++++++++++++-------- .../scala/tools/nsc/backend/icode/Opcodes.scala | 17 ++++++++------ .../scala/tools/nsc/backend/jvm/GenJVM.scala | 6 +++++ .../scala/tools/nsc/symtab/Definitions.scala | 2 ++ .../scala/tools/nsc/transform/CleanUp.scala | 4 +++- .../scala/tools/nsc/transform/Erasure.scala | 11 --------- src/library/scala/Array.scala | 8 +++++++ .../scala/collection/mutable/WrappedArray.scala | 4 +++- src/library/scala/runtime/ArrayRuntime.java | 16 +++++++++++++ src/library/scala/runtime/ScalaRunTime.scala | 14 +++++++++++ 10 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 src/library/scala/runtime/ArrayRuntime.java (limited to 'src') 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 true 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) { * length <= i */ def update(i: Int, x: T) { throw new Error() } + + /**

+ * Clone the Array. + *

+ * + * @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 ... -- cgit v1.2.3