diff --git a/project/Acyclic.scala b/project/Acyclic.scala new file mode 100644 index 00000000..2beafd4c --- /dev/null +++ b/project/Acyclic.scala @@ -0,0 +1,18 @@ +import sbt.Keys._ +import sbt._ + +import scala.util.Try + +object Acyclic { + def settings: Seq[Setting[_]] = Seq( + libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.1.7" % "provided", + libraryDependencies ++= ( + if (scalaVersion.value.startsWith("2.10")) Seq( + "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided" + ) else Nil + ), + autoCompilerPlugins := true, + addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.7") + ) +} + diff --git a/project/Common.scala b/project/Common.scala index 9a7db24a..18f768b4 100644 --- a/project/Common.scala +++ b/project/Common.scala @@ -21,7 +21,7 @@ object Common { homepage := Some(url("https://scala-debugger.org")), // Default version when not cross-compiling - scalaVersion := "2.10.6", + scalaVersion := "2.11.8", crossScalaVersions := Seq("2.10.6", "2.11.8", "2.12.1"), @@ -135,6 +135,6 @@ object Common { else Some("releases" at nexus + "service/local/staging/deploy/maven2") } - ) ++ Macros.pluginSettings + ) ++ Macros.pluginSettings ++ Acyclic.settings } diff --git a/project/Macros.scala b/project/Macros.scala index d04994a1..7186341a 100644 --- a/project/Macros.scala +++ b/project/Macros.scala @@ -13,10 +13,14 @@ object Macros { ) /** Macro-specific project settings. */ - val settings = pluginSettings ++ Seq( - libraryDependencies += { - scalaVersion("org.scala-lang" % "scala-reflect" % _).value - }, + val settings: Seq[Setting[_]] = pluginSettings ++ Seq( + libraryDependencies ++= Seq( + "org.typelevel" %% "macro-compat" % "1.1.1", + "com.lihaoyi" %% "scalaparse" % "0.4.4", + "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided", + scalaVersion("org.scala-lang" % "scala-reflect" % _).value, + "org.scalatest" %% "scalatest" % "3.0.0" % "test,it" + ), libraryDependencies ++= ( if (scalaVersion.value.startsWith("2.10")) Seq( diff --git a/scala-debugger-api/project/build.properties b/scala-debugger-api/project/build.properties new file mode 100644 index 00000000..c091b86c --- /dev/null +++ b/scala-debugger-api/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.16 diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaInfoProducer.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaInfoProducer.scala index 932a5914..06eef3cb 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaInfoProducer.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaInfoProducer.scala @@ -27,7 +27,7 @@ class JavaInfoProducer extends InfoProducer { * @return The profile instance providing an implementation corresponding * to Java */ - override def toJavaInfo: InfoProducer = new JavaInfoProducer + override def toJavaInfo: InfoProducer = this /** * Retrieves the event info producer tied to this info producer. diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaThreadInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaThreadInfo.scala index ee341075..427365e6 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaThreadInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaThreadInfo.scala @@ -147,6 +147,9 @@ class JavaThreadInfo( */ override def totalFrames: Int = _threadReference.frameCount() + protected def newValueConverter(value: Value): ValueConverter = + JavaValueConverter.from() + protected def newFrameProfile( stackFrame: StackFrame, index: Int diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeConverter.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeConverter.scala new file mode 100644 index 00000000..cffcd683 --- /dev/null +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeConverter.scala @@ -0,0 +1,146 @@ +package org.scaladebugger.api.profiles.java.info + +import com.sun.jdi._ +import org.scaladebugger.api.profiles.traits.info._ +import JavaTypeConverter._ + +object JavaTypeConverter { + val DefaultNewReferenceTypeProfile: JavaTypeInfo => ReferenceType => ReferenceTypeInfo = + `type` => `type`.infoProducer.newReferenceTypeInfo( + `type`.scalaVirtualMachine, + _: ReferenceType + ) + val DefaultNewArrayTypeProfile: JavaTypeInfo => ArrayType => ArrayTypeInfo = + `type` => `type`.infoProducer.newArrayTypeInfo( + `type`.scalaVirtualMachine, + _: ArrayType + ) + val DefaultNewClassTypeProfile: JavaTypeInfo => ClassType => ClassTypeInfo = + `type` => `type`.infoProducer.newClassTypeInfo( + `type`.scalaVirtualMachine, + _: ClassType + ) + val DefaultNewInterfaceTypeProfile: JavaTypeInfo => InterfaceType => InterfaceTypeInfo = + `type` => `type`.infoProducer.newInterfaceTypeInfo( + `type`.scalaVirtualMachine, + _: InterfaceType + ) + val DefaultNewPrimitiveTypeProfile: JavaTypeInfo => PrimitiveType => PrimitiveTypeInfo = + `type` => `type`.infoProducer.newPrimitiveTypeInfo( + `type`.scalaVirtualMachine, + _: PrimitiveType + ) + val DefaultNewVoidTypeProfile: JavaTypeInfo => VoidType => PrimitiveTypeInfo = + `type` => `type`.infoProducer.newPrimitiveTypeInfo( + `type`.scalaVirtualMachine, + _: VoidType + ) + + /** + * Creates a new converter from the provided type. + * @param `type` The type to convert + * @return The converter capable of converting the provided type + */ + def from(`type`: JavaTypeInfo): JavaTypeConverter = + new JavaTypeConverter(`type`)() +} + +class JavaTypeConverter private(private val `type`: JavaTypeInfo)( + private val newReferenceTypeProfile: ReferenceType => ReferenceTypeInfo = + DefaultNewReferenceTypeProfile(`type`), + private val newArrayTypeProfile: ArrayType => ArrayTypeInfo = + DefaultNewArrayTypeProfile(`type`), + private val newClassTypeProfile: ClassType => ClassTypeInfo = + DefaultNewClassTypeProfile(`type`), + private val newInterfaceTypeProfile: InterfaceType => InterfaceTypeInfo = + DefaultNewInterfaceTypeProfile(`type`), + private val newPrimitiveTypeProfile: PrimitiveType => PrimitiveTypeInfo = + DefaultNewPrimitiveTypeProfile(`type`), + private val newVoidTypeProfile: VoidType => PrimitiveTypeInfo = + DefaultNewVoidTypeProfile(`type`) +) extends TypeConverter { + /** + * Creates a copy of the converter that will use the specified converter + * functions over the current instances. + * @param newReferenceTypeProfile + * @param newArrayTypeProfile + * @param newClassTypeProfile + * @param newInterfaceTypeProfile + * @param newPrimitiveTypeProfile + * @param newVoidTypeProfile + * @return The new converter instance + */ + def using( + newReferenceTypeProfile: ReferenceType => ReferenceTypeInfo = newReferenceTypeProfile, + newArrayTypeProfile: ArrayType => ArrayTypeInfo = newArrayTypeProfile, + newClassTypeProfile: ClassType => ClassTypeInfo = newClassTypeProfile, + newInterfaceTypeProfile: InterfaceType => InterfaceTypeInfo = newInterfaceTypeProfile, + newPrimitiveTypeProfile: PrimitiveType => PrimitiveTypeInfo = newPrimitiveTypeProfile, + newVoidTypeProfile: VoidType => PrimitiveTypeInfo = newVoidTypeProfile + ): JavaTypeConverter = new JavaTypeConverter(`type`)( + newReferenceTypeProfile = newReferenceTypeProfile, + newArrayTypeProfile = newArrayTypeProfile, + newClassTypeProfile = newClassTypeProfile, + newInterfaceTypeProfile = newInterfaceTypeProfile, + newPrimitiveTypeProfile = newPrimitiveTypeProfile, + newVoidTypeProfile = newVoidTypeProfile + ) + + /** + * Returns the type as an array type (profile). + * + * @return The array type profile wrapping this type + */ + @throws[AssertionError] + override def toArrayType: ArrayTypeInfo = { + assert(`type`.isArrayType, "Type must be an array type!") + newArrayTypeProfile(`type`._type.asInstanceOf[ArrayType]) + } + + /** + * Returns the type as an class type (profile). + * + * @return The class type profile wrapping this type + */ + @throws[AssertionError] + override def toClassType: ClassTypeInfo = { + assert(`type`.isClassType, "Type must be a class type!") + newClassTypeProfile(`type`._type.asInstanceOf[ClassType]) + } + + /** + * Returns the type as an interface type (profile). + * + * @return The interface type profile wrapping this type + */ + @throws[AssertionError] + override def toInterfaceType: InterfaceTypeInfo = { + assert(`type`.isInterfaceType, "Type must be an interface type!") + newInterfaceTypeProfile(`type`._type.asInstanceOf[InterfaceType]) + } + + /** + * Returns the type as an reference type (profile). + * + * @return The reference type profile wrapping this type + */ + @throws[AssertionError] + override def toReferenceType: ReferenceTypeInfo = { + assert(`type`.isReferenceType, "Type must be a reference type!") + newReferenceTypeProfile(`type`._type.asInstanceOf[ReferenceType]) + } + + /** + * Returns the type as an primitive type (profile). + * + * @return The primitive type profile wrapping this type + */ + @throws[AssertionError] + override def toPrimitiveType: PrimitiveTypeInfo = { + assert(`type`.isPrimitiveType, "Type must be a primitive type!") + `type`._type match { + case p: PrimitiveType => newPrimitiveTypeProfile(p) + case v: VoidType => newPrimitiveTypeProfile(v) + } + } +} diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeInfo.scala index caf8c95c..7fd9bbd1 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaTypeInfo.scala @@ -22,8 +22,8 @@ object JavaTypeInfo { */ class JavaTypeInfo( val scalaVirtualMachine: ScalaVirtualMachine, - protected val infoProducer: InfoProducer, - private val _type: Type + protected[info] val infoProducer: InfoProducer, + private[info] val _type: Type ) extends TypeInfo { /** * Returns whether or not this info profile represents the low-level Java @@ -121,106 +121,6 @@ class JavaTypeInfo( */ override def isNullType: Boolean = _type == null - /** - * Returns the type as an array type (profile). - * - * @return The array type profile wrapping this type - */ - @throws[AssertionError] - override def toArrayType: ArrayTypeInfo = { - assert(isArrayType, "Type must be an array type!") - newArrayTypeProfile(_type.asInstanceOf[ArrayType]) - } - - /** - * Returns the type as an class type (profile). - * - * @return The class type profile wrapping this type - */ - @throws[AssertionError] - override def toClassType: ClassTypeInfo = { - assert(isClassType, "Type must be a class type!") - newClassTypeProfile(_type.asInstanceOf[ClassType]) - } - - /** - * Returns the type as an interface type (profile). - * - * @return The interface type profile wrapping this type - */ - @throws[AssertionError] - override def toInterfaceType: InterfaceTypeInfo = { - assert(isInterfaceType, "Type must be an interface type!") - newInterfaceTypeProfile(_type.asInstanceOf[InterfaceType]) - } - - /** - * Returns the type as an reference type (profile). - * - * @return The reference type profile wrapping this type - */ - @throws[AssertionError] - override def toReferenceType: ReferenceTypeInfo = { - assert(isReferenceType, "Type must be a reference type!") - newReferenceTypeProfile(_type.asInstanceOf[ReferenceType]) - } - - /** - * Returns the type as an primitive type (profile). - * - * @return The primitive type profile wrapping this type - */ - @throws[AssertionError] - override def toPrimitiveType: PrimitiveTypeInfo = { - assert(isPrimitiveType, "Type must be a primitive type!") - _type match { - case p: PrimitiveType => newPrimitiveTypeProfile(p) - case v: VoidType => newPrimitiveTypeProfile(v) - } - } - protected def newTypeProfile(_type: Type): TypeInfo = infoProducer.newTypeInfo(scalaVirtualMachine, _type) - - protected def newReferenceTypeProfile( - referenceType: ReferenceType - ): ReferenceTypeInfo = infoProducer.newReferenceTypeInfo( - scalaVirtualMachine, - referenceType - ) - - protected def newArrayTypeProfile( - arrayType: ArrayType - ): ArrayTypeInfo = infoProducer.newArrayTypeInfo( - scalaVirtualMachine, - arrayType - ) - - protected def newClassTypeProfile( - classType: ClassType - ): ClassTypeInfo = infoProducer.newClassTypeInfo( - scalaVirtualMachine, - classType - ) - - protected def newInterfaceTypeProfile( - interfaceType: InterfaceType - ): InterfaceTypeInfo = infoProducer.newInterfaceTypeInfo( - scalaVirtualMachine, - interfaceType - ) - - protected def newPrimitiveTypeProfile( - primitiveType: PrimitiveType - ): PrimitiveTypeInfo = infoProducer.newPrimitiveTypeInfo( - scalaVirtualMachine, - primitiveType - ) - - protected def newPrimitiveTypeProfile( - voidType: VoidType - ): PrimitiveTypeInfo = infoProducer.newPrimitiveTypeInfo( - scalaVirtualMachine, - voidType - ) } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueConverter.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueConverter.scala new file mode 100644 index 00000000..57347fc8 --- /dev/null +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueConverter.scala @@ -0,0 +1,181 @@ +package org.scaladebugger.api.profiles.java.info + +import com.sun.jdi._ +import org.scaladebugger.api.profiles.traits.info._ +import JavaValueConverter._ + +object JavaValueConverter { + val DefaultNewPrimitiveInfo: JavaValueInfo => PrimitiveValue => PrimitiveInfo = + value => value.infoProducer.newPrimitiveInfo(value.scalaVirtualMachine, _: PrimitiveValue) + val DefaultNewVoidInfo: JavaValueInfo => VoidValue => PrimitiveInfo = + value => value.infoProducer.newPrimitiveInfo(value.scalaVirtualMachine, _: VoidValue) + val DefaultNewObjectInfo: JavaValueInfo => ObjectReference => ObjectInfo = + value => value.infoProducer.newObjectInfo(value.scalaVirtualMachine, _: ObjectReference)() + val DefaultNewStringInfo: JavaValueInfo => StringReference => StringInfo = + value => value.infoProducer.newStringInfo(value.scalaVirtualMachine, _: StringReference)() + val DefaultNewArrayInfo: JavaValueInfo => ArrayReference => ArrayInfo = + value => value.infoProducer.newArrayInfo(value.scalaVirtualMachine, _: ArrayReference)() + val DefaultNewClassLoaderInfo: JavaValueInfo => ClassLoaderReference => ClassLoaderInfo = + value => value.infoProducer.newClassLoaderInfo(value.scalaVirtualMachine, _: ClassLoaderReference)() + val DefaultNewClassObjectInfo: JavaValueInfo => ClassObjectReference => ClassObjectInfo = + value => value.infoProducer.newClassObjectInfo(value.scalaVirtualMachine, _: ClassObjectReference)() + val DefaultNewThreadGroupInfo: JavaValueInfo => ThreadGroupReference => ThreadGroupInfo = + value => value.infoProducer.newThreadGroupInfo(value.scalaVirtualMachine, _: ThreadGroupReference)() + val DefaultNewThreadInfo: JavaValueInfo => ThreadReference => ThreadInfo = + value => value.infoProducer.newThreadInfo(value.scalaVirtualMachine, _: ThreadReference)() + + /** + * Creates a new converter from the provided value. + * @param value The value to convert + * @return The converter capable of converting the provided value + */ + def from(value: JavaValueInfo): JavaValueConverter = + new JavaValueConverter(value)() +} + +class JavaValueConverter private(private val value: JavaValueInfo)( + private val newPrimitiveInfo: PrimitiveValue => PrimitiveInfo = + DefaultNewPrimitiveInfo(value), + private val newVoidInfo: VoidValue => PrimitiveInfo = + DefaultNewVoidInfo(value), + private val newObjectInfo: ObjectReference => ObjectInfo = + DefaultNewObjectInfo(value), + private val newStringInfo: StringReference => StringInfo = + DefaultNewStringInfo(value), + private val newArrayInfo: ArrayReference => ArrayInfo = + DefaultNewArrayInfo(value), + private val newClassLoaderInfo: ClassLoaderReference => ClassLoaderInfo = + DefaultNewClassLoaderInfo(value), + private val newClassObjectInfo: ClassObjectReference => ClassObjectInfo = + DefaultNewClassObjectInfo(value), + private val newThreadGroupInfo: ThreadGroupReference => ThreadGroupInfo = + DefaultNewThreadGroupInfo(value), + private val newThreadInfo: ThreadReference => ThreadInfo = + DefaultNewThreadInfo(value) +) extends ValueConverter { + /** + * Creates a copy of the converter that will use the specified converter + * functions over the current instances. + * @param newPrimitiveInfo + * @param newVoidInfo + * @param newObjectInfo + * @param newStringInfo + * @param newArrayInfo + * @param newClassLoaderInfo + * @param newClassObjectInfo + * @param newThreadGroupInfo + * @param newThreadInfo + * @return The new converter instance + */ + def using( + newPrimitiveInfo: PrimitiveValue => PrimitiveInfo = newPrimitiveInfo, + newVoidInfo: VoidValue => PrimitiveInfo = newVoidInfo, + newObjectInfo: ObjectReference => ObjectInfo = newObjectInfo, + newStringInfo: StringReference => StringInfo = newStringInfo, + newArrayInfo: ArrayReference => ArrayInfo = newArrayInfo, + newClassLoaderInfo: ClassLoaderReference => ClassLoaderInfo = newClassLoaderInfo, + newClassObjectInfo: ClassObjectReference => ClassObjectInfo = newClassObjectInfo, + newThreadGroupInfo: ThreadGroupReference => ThreadGroupInfo = newThreadGroupInfo, + newThreadInfo: ThreadReference => ThreadInfo = newThreadInfo + ): JavaValueConverter = new JavaValueConverter(value)( + newPrimitiveInfo = newPrimitiveInfo, + newVoidInfo = newVoidInfo, + newObjectInfo = newObjectInfo, + newStringInfo = newStringInfo, + newArrayInfo = newArrayInfo, + newClassLoaderInfo = newClassLoaderInfo, + newClassObjectInfo = newClassObjectInfo, + newThreadGroupInfo = newThreadGroupInfo + ) + + /** + * Returns the value as an array (profile). + * + * @return The array profile wrapping this value + */ + @throws[AssertionError] + override def toArrayInfo: ArrayInfo = { + assert(value.isArray, "Value must be an array!") + newArrayInfo(value._value.asInstanceOf[ArrayReference]) + } + + /** + * Returns the value as a class loader (profile). + * + * @return The class loader profile wrapping this value + */ + @throws[AssertionError] + override def toClassLoaderInfo: ClassLoaderInfo = { + assert(value.isClassLoader, "Value must be a class loader!") + newClassLoaderInfo(value._value.asInstanceOf[ClassLoaderReference]) + } + + /** + * Returns the value as a class object (profile). + * + * @return The class object profile wrapping this value + */ + @throws[AssertionError] + override def toClassObjectInfo: ClassObjectInfo = { + assert(value.isClassObject, "Value must be a class object!") + newClassObjectInfo(value._value.asInstanceOf[ClassObjectReference]) + } + + /** + * Returns the value as a thread (profile). + * + * @return The thread profile wrapping this value + */ + @throws[AssertionError] + override def toThreadInfo: ThreadInfo = { + assert(value.isThread, "Value must be a thread!") + newThreadInfo(value._value.asInstanceOf[ThreadReference]) + } + + /** + * Returns the value as a thread group (profile). + * + * @return The thread group profile wrapping this value + */ + @throws[AssertionError] + override def toThreadGroupInfo: ThreadGroupInfo = { + assert(value.isThreadGroup, "Value must be a thread group!") + newThreadGroupInfo(value._value.asInstanceOf[ThreadGroupReference]) + } + + /** + * Returns the value as an object (profile). + * + * @return The object profile wrapping this value + */ + @throws[AssertionError] + override def toObjectInfo: ObjectInfo = { + assert(value.isObject, "Value must be an object!") + newObjectInfo(value._value.asInstanceOf[ObjectReference]) + } + + /** + * Returns the value as a string (profile). + * + * @return The string profile wrapping this value + */ + @throws[AssertionError] + override def toStringInfo: StringInfo = { + assert(value.isString, "Value must be a string!") + newStringInfo(value._value.asInstanceOf[StringReference]) + } + + /** + * Returns the value as a primitive (profile). + * + * @return The primitive profile wrapping this value + */ + @throws[AssertionError] + override def toPrimitiveInfo: PrimitiveInfo = { + assert(value.isPrimitive, "Value must be a primitive!") + value._value match { + case p: PrimitiveValue => newPrimitiveInfo(p) + case v: VoidValue => newVoidInfo(v) + } + } +} diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueInfo.scala index bb3758f5..249da893 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/java/info/JavaValueInfo.scala @@ -21,8 +21,8 @@ object JavaValueInfo { */ class JavaValueInfo( val scalaVirtualMachine: ScalaVirtualMachine, - protected val infoProducer: InfoProducer, - private val _value: Value + protected[info] val infoProducer: InfoProducer, + private[info] val _value: Value ) extends ValueInfo { /** * Returns whether or not this info profile represents the low-level Java @@ -74,97 +74,6 @@ class JavaValueInfo( else null } - /** - * Returns the value as an array (profile). - * - * @return The array profile wrapping this value - */ - @throws[AssertionError] - override def toArrayInfo: ArrayInfo = { - assert(isArray, "Value must be an array!") - newArrayProfile(_value.asInstanceOf[ArrayReference]) - } - - /** - * Returns the value as a class loader (profile). - * - * @return The class loader profile wrapping this value - */ - @throws[AssertionError] - override def toClassLoaderInfo: ClassLoaderInfo = { - assert(isClassLoader, "Value must be a class loader!") - newClassLoaderProfile(_value.asInstanceOf[ClassLoaderReference]) - } - - /** - * Returns the value as a class object (profile). - * - * @return The class object profile wrapping this value - */ - @throws[AssertionError] - override def toClassObjectInfo: ClassObjectInfo = { - assert(isClassObject, "Value must be a class object!") - newClassObjectProfile(_value.asInstanceOf[ClassObjectReference]) - } - - /** - * Returns the value as a thread (profile). - * - * @return The thread profile wrapping this value - */ - @throws[AssertionError] - override def toThreadInfo: ThreadInfo = { - assert(isThread, "Value must be a thread!") - newThreadProfile(_value.asInstanceOf[ThreadReference]) - } - - /** - * Returns the value as a thread group (profile). - * - * @return The thread group profile wrapping this value - */ - @throws[AssertionError] - override def toThreadGroupInfo: ThreadGroupInfo = { - assert(isThreadGroup, "Value must be a thread group!") - newThreadGroupProfile(_value.asInstanceOf[ThreadGroupReference]) - } - - /** - * Returns the value as an object (profile). - * - * @return The object profile wrapping this value - */ - @throws[AssertionError] - override def toObjectInfo: ObjectInfo = { - assert(isObject, "Value must be an object!") - newObjectProfile(_value.asInstanceOf[ObjectReference]) - } - - /** - * Returns the value as a string (profile). - * - * @return The string profile wrapping this value - */ - @throws[AssertionError] - override def toStringInfo: StringInfo = { - assert(isString, "Value must be a string!") - newStringProfile(_value.asInstanceOf[StringReference]) - } - - /** - * Returns the value as a primitive (profile). - * - * @return The primitive profile wrapping this value - */ - @throws[AssertionError] - override def toPrimitiveInfo: PrimitiveInfo = { - assert(isPrimitive, "Value must be a primitive!") - _value match { - case p: PrimitiveValue => newPrimitiveProfile(p) - case v: VoidValue => newPrimitiveProfile(v) - } - } - /** * Returns whether or not this value represents a primitive. * @@ -243,33 +152,6 @@ class JavaValueInfo( */ override def isNull: Boolean = _value == null - protected def newPrimitiveProfile(primitiveValue: PrimitiveValue): PrimitiveInfo = - infoProducer.newPrimitiveInfo(scalaVirtualMachine, primitiveValue) - - protected def newPrimitiveProfile(voidValue: VoidValue): PrimitiveInfo = - infoProducer.newPrimitiveInfo(scalaVirtualMachine, voidValue) - - protected def newObjectProfile(objectReference: ObjectReference): ObjectInfo = - infoProducer.newObjectInfo(scalaVirtualMachine, objectReference)() - - protected def newStringProfile(stringReference: StringReference): StringInfo = - infoProducer.newStringInfo(scalaVirtualMachine, stringReference)() - - protected def newArrayProfile(arrayReference: ArrayReference): ArrayInfo = - infoProducer.newArrayInfo(scalaVirtualMachine, arrayReference)() - - protected def newClassLoaderProfile(classLoaderReference: ClassLoaderReference): ClassLoaderInfo = - infoProducer.newClassLoaderInfo(scalaVirtualMachine, classLoaderReference)() - - protected def newClassObjectProfile(classObjectReference: ClassObjectReference): ClassObjectInfo = - infoProducer.newClassObjectInfo(scalaVirtualMachine, classObjectReference)() - - protected def newThreadGroupProfile(threadGroupReference: ThreadGroupReference): ThreadGroupInfo = - infoProducer.newThreadGroupInfo(scalaVirtualMachine, threadGroupReference)() - - protected def newThreadProfile(threadReference: ThreadReference): ThreadInfo = - infoProducer.newThreadInfo(scalaVirtualMachine, threadReference)() - protected def newTypeProfile(_type: Type): TypeInfo = infoProducer.newTypeInfo(scalaVirtualMachine, _type) } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayInfo.scala index 65da1990..5ff8b5ad 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayInfo.scala @@ -1,9 +1,12 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ArrayReference +import org.scaladebugger.api.profiles.traits.info.ArrayInfo._ +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try -import ArrayInfo._ /** * Contains constants available to all array-focused information profiles. @@ -19,6 +22,7 @@ object ArrayInfo { /** * Represents the interface for array-based interaction. */ +//@Freezable trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { import scala.reflect.runtime.universe.{TypeTag, typeOf} @@ -29,6 +33,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ArrayInfo /** @@ -36,6 +41,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ArrayReference /** @@ -43,6 +49,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) override def `type`: ArrayTypeInfo /** @@ -50,6 +57,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * * @return The length of the array */ + @CanFreeze def length: Int /** @@ -66,6 +74,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * @param index The location in the array to retrieve a value * @return The retrieved value */ + @CannotFreeze def value(index: Int): ValueInfo /** @@ -97,6 +106,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * all remaining values to the end of the array * @return The retrieved values */ + @CannotFreeze def values(index: Int, length: Int): Seq[ValueInfo] /** @@ -123,6 +133,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * * @return The retrieved values */ + @CanFreeze(ReturnType.FreezeCollection) def values: Seq[ValueInfo] /** @@ -190,6 +201,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * @param value The new value to place in the array * @return The updated remote value */ + @CannotFreeze def setValueFromInfo(index: Int, value: ValueInfo): ValueInfo /** @@ -278,6 +290,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * beginning of the index * @return The updated remote values */ + @CannotFreeze def setValuesFromInfo( index: Int, values: Seq[ValueInfo], @@ -324,6 +337,7 @@ trait ArrayInfo extends ObjectInfo with CreateInfo with CommonInfo { * @param values The new values to use when overwriting elements in the array * @return The updated remote values */ + @CannotFreeze def setValuesFromInfo(values: Seq[ValueInfo]): Seq[ValueInfo] /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayTypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayTypeInfo.scala index 98e0cc6f..4eafb320 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayTypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ArrayTypeInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ArrayType +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for retrieving interface type-based information. */ +//@Freezable trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ArrayTypeInfo /** @@ -22,6 +27,7 @@ trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ArrayType /** @@ -30,6 +36,7 @@ trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { * @return The declared type of the elements (the runtime type may be a * subclass of this type) */ + @CanFreeze def elementSignature: String /** @@ -37,6 +44,7 @@ trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The type name as a string */ + @CanFreeze def elementTypeName: String /** @@ -44,6 +52,7 @@ trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) def elementType: TypeInfo /** @@ -60,6 +69,7 @@ trait ArrayTypeInfo extends ReferenceTypeInfo with TypeInfo { * @param length The total length of the array * @return The profile representing the new instance */ + @CannotFreeze def newInstance(length: Int): ArrayInfo /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CastNotPossibleException.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CastNotPossibleException.scala index 6933f5b5..b5ce0cc3 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CastNotPossibleException.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CastNotPossibleException.scala @@ -1,4 +1,5 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file /** * Represents an exception that occurs when a local value is trying to be diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassLoaderInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassLoaderInfo.scala index 517cbcde..e6427587 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassLoaderInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassLoaderInfo.scala @@ -1,10 +1,14 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ClassLoaderReference +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} /** * Represents the interface for "class loader"-based interaction. */ +//@Freezable trait ClassLoaderInfo extends ObjectInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -13,6 +17,7 @@ trait ClassLoaderInfo extends ObjectInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ClassLoaderInfo /** @@ -20,6 +25,7 @@ trait ClassLoaderInfo extends ObjectInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ClassLoaderReference /** @@ -27,6 +33,7 @@ trait ClassLoaderInfo extends ObjectInfo with CommonInfo { * * @return The collection of reference types for the loaded classes */ + @CanFreeze(ReturnType.FreezeCollection) def definedClasses: Seq[ReferenceTypeInfo] /** @@ -35,5 +42,6 @@ trait ClassLoaderInfo extends ObjectInfo with CommonInfo { * * @return The collection of reference types for the initiated classes */ + @CanFreeze(ReturnType.FreezeCollection) def visibleClasses: Seq[ReferenceTypeInfo] } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassObjectInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassObjectInfo.scala index 4cb9bc76..157bdee1 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassObjectInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassObjectInfo.scala @@ -1,10 +1,14 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ClassObjectReference +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} /** * Represents the interface for "class object"-based interaction. */ +//@Freezable trait ClassObjectInfo extends ObjectInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -13,6 +17,7 @@ trait ClassObjectInfo extends ObjectInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ClassObjectInfo /** @@ -20,6 +25,7 @@ trait ClassObjectInfo extends ObjectInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ClassObjectReference /** @@ -28,5 +34,6 @@ trait ClassObjectInfo extends ObjectInfo with CommonInfo { * * @return The reference type information */ + @CanFreeze(ReturnType.FreezeObject) def reflectedType: ReferenceTypeInfo } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassTypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassTypeInfo.scala index e5a57eeb..491321dc 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassTypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ClassTypeInfo.scala @@ -1,13 +1,17 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ClassType import org.scaladebugger.api.lowlevel.JDIArgument +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for retrieving class type-based information. */ +//@Freezable trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { /** * Converts the current profile instance to a representation of @@ -16,6 +20,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ClassTypeInfo /** @@ -23,6 +28,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ClassType /** @@ -31,6 +37,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The collection of interface type info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def allInterfaces: Seq[InterfaceTypeInfo] /** @@ -47,6 +54,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The collection of interface type info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def interfaces: Seq[InterfaceTypeInfo] /** @@ -62,6 +70,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The collection of class type info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def subclasses: Seq[ClassTypeInfo] /** @@ -69,6 +78,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return Some class type info if the super class exists, otherwise None */ + @CanFreeze(ReturnType.FreezeOption) def superclassOption: Option[ClassTypeInfo] /** @@ -76,6 +86,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return True if it is an enumeration, otherwise false */ + @CanFreeze def isEnumeration: Boolean /** @@ -86,6 +97,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * @param signature The JNI signature of the method * @return Some method if found, otherwise None */ + @CannotFreeze def methodOption(name: String, signature: String): Option[MethodInfo] /** @@ -131,6 +143,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * the method invocation * @return The resulting value of the invocation */ + @CannotFreeze def invokeStaticMethod( thread: ThreadInfo, method: MethodInfo, @@ -175,6 +188,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * method invocation * @return The resulting value of the invocation */ + @CannotFreeze def invokeStaticMethod( thread: ThreadInfo, methodName: String, @@ -220,6 +234,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * the constructor invocation * @return The instantiated object */ + @CannotFreeze def newInstance( thread: ThreadInfo, constructorName: String, @@ -238,6 +253,7 @@ trait ClassTypeInfo extends ReferenceTypeInfo with TypeInfo { * the constructor invocation * @return The instantiated object */ + @CannotFreeze def newInstance( thread: ThreadInfo, constructor: MethodInfo, diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CommonInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CommonInfo.scala index 0c762109..378b6a43 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CommonInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CommonInfo.scala @@ -1,17 +1,21 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Mirror import org.scaladebugger.api.virtualmachines.ScalaVirtualMachine +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} /** * Represents common methods between information-gathering profiles. */ +//@Freezable trait CommonInfo extends JavaInfo { /** * Returns the Scala virtual machine containing this instance. * * @return The Scala virtual machine instance */ + @CannotFreeze def scalaVirtualMachine: ScalaVirtualMachine /** @@ -19,6 +23,7 @@ trait CommonInfo extends JavaInfo { * * @return The JDI instance */ + @CannotFreeze def toJdiInstance: Mirror /** @@ -27,6 +32,7 @@ trait CommonInfo extends JavaInfo { * * @return The human-readable description */ + @CanFreeze def toPrettyString: String /** @@ -36,5 +42,6 @@ trait CommonInfo extends JavaInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: CommonInfo } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CreateInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CreateInfo.scala index 720e6258..21b7e926 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CreateInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/CreateInfo.scala @@ -1,11 +1,15 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file +import org.scaladebugger.macros.freeze.{CannotFreeze, Freezable} + import scala.util.Try /** * Represents the interface that needs to be implemented to provide * ability to create data using a specific debug profile. */ +//@Freezable trait CreateInfo { /** * Creates the provided value on the remote JVM. @@ -13,6 +17,7 @@ trait CreateInfo { * @param value The value to create (mirror) on the remote JVM * @return The information about the remote value */ + @CannotFreeze def createRemotely(value: AnyVal): ValueInfo /** @@ -31,6 +36,7 @@ trait CreateInfo { * @param value The value to create (mirror) on the remote JVM * @return The information about the remote value */ + @CannotFreeze def createRemotely(value: String): ValueInfo /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FieldVariableInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FieldVariableInfo.scala index a99197d5..dd0db463 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FieldVariableInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FieldVariableInfo.scala @@ -1,6 +1,9 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Field +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try @@ -9,6 +12,7 @@ import scala.util.Try * Represents the interface for variable-based interaction with field-specific * information. */ +//@Freezable trait FieldVariableInfo extends VariableInfo with CreateInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -17,6 +21,7 @@ trait FieldVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: FieldVariableInfo /** @@ -24,6 +29,7 @@ trait FieldVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Field /** @@ -32,6 +38,7 @@ trait FieldVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * @return The reference type information (if a static field) or object * information (if a non-static field) */ + @CanFreeze(ReturnType.FreezeEither) def parent: Either[ObjectInfo, ReferenceTypeInfo] /** @@ -39,6 +46,7 @@ trait FieldVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * * @return The reference type information that declared this field */ + @CanFreeze(ReturnType.FreezeObject) def declaringType: ReferenceTypeInfo /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FrameInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FrameInfo.scala index 690746c0..3cb94105 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FrameInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/FrameInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.StackFrame +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for frame-based interaction. */ +//@Freezable trait FrameInfo extends CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait FrameInfo extends CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: FrameInfo /** @@ -22,6 +27,7 @@ trait FrameInfo extends CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: StackFrame /** @@ -29,6 +35,7 @@ trait FrameInfo extends CommonInfo { * * @return The index with 0 being the top frame */ + @CanFreeze def index: Int /** @@ -58,6 +65,7 @@ trait FrameInfo extends CommonInfo { * * @return Some profile of this object, or None if not available */ + @CanFreeze(ReturnType.FreezeOption) def thisObjectOption: Option[ObjectInfo] /** @@ -72,6 +80,7 @@ trait FrameInfo extends CommonInfo { * * @return The profile of the thread */ + @CanFreeze(ReturnType.FreezeObject) def currentThread: ThreadInfo /** @@ -86,6 +95,7 @@ trait FrameInfo extends CommonInfo { * * @return The profile of the location */ + @CanFreeze(ReturnType.FreezeObject) def location: LocationInfo /** @@ -103,6 +113,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of argument values in order as provided to the frame */ + @CanFreeze(ReturnType.FreezeCollection) def argumentValues: Seq[ValueInfo] /** @@ -130,6 +141,7 @@ trait FrameInfo extends CommonInfo { * @param name The name of the variable to retrieve * @return Some profile of the variable, or None if it doesn't exist */ + @CannotFreeze def variableOption(name: String): Option[VariableInfo] /** @@ -161,6 +173,7 @@ trait FrameInfo extends CommonInfo { * @param name The name of the variable to retrieve * @return Some profile of the variable, or None if it doesn't exist */ + @CannotFreeze def indexedVariableOption(name: String): Option[VariableInfo] /** @@ -262,6 +275,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def allVariables: Seq[VariableInfo] /** @@ -269,6 +283,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def argumentLocalVariables: Seq[IndexedVariableInfo] /** @@ -276,6 +291,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def nonArgumentLocalVariables: Seq[IndexedVariableInfo] /** @@ -283,6 +299,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def localVariables: Seq[IndexedVariableInfo] /** @@ -290,6 +307,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def fieldVariables: Seq[FieldVariableInfo] /** @@ -297,6 +315,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def indexedAllVariables: Seq[VariableInfo] /** @@ -305,6 +324,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def indexedArgumentLocalVariables: Seq[IndexedVariableInfo] /** @@ -313,6 +333,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def indexedNonArgumentLocalVariables: Seq[IndexedVariableInfo] /** @@ -321,6 +342,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def indexedLocalVariables: Seq[IndexedVariableInfo] /** @@ -329,6 +351,7 @@ trait FrameInfo extends CommonInfo { * * @return The collection of variables as their profile equivalents */ + @CanFreeze(ReturnType.FreezeCollection) def indexedFieldVariables: Seq[FieldVariableInfo] /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/GrabInfoProfile.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/GrabInfoProfile.scala index b16eebfb..c201b985 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/GrabInfoProfile.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/GrabInfoProfile.scala @@ -1,8 +1,10 @@ package org.scaladebugger.api.profiles.traits.info -import com.sun.jdi._ +import acyclic.file import java.util.NoSuchElementException +import com.sun.jdi._ + import scala.annotation.tailrec import scala.util.Try diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/IndexedVariableInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/IndexedVariableInfo.scala index 1a9eb4c1..0e2a1045 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/IndexedVariableInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/IndexedVariableInfo.scala @@ -1,10 +1,15 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} + /** * Represents the interface for variable-based interaction with indexed * location information. */ +//@Freezable trait IndexedVariableInfo extends VariableInfo with CreateInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -13,6 +18,7 @@ trait IndexedVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: IndexedVariableInfo /** @@ -20,6 +26,7 @@ trait IndexedVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * * @return The profile of the frame */ + @CanFreeze(ReturnType.FreezeObject) def frame: FrameInfo /** @@ -27,5 +34,6 @@ trait IndexedVariableInfo extends VariableInfo with CreateInfo with CommonInfo { * * @return The frame starting from 0 (top of the stack) */ + @CanFreeze def frameIndex: Int } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InfoProducer.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InfoProducer.scala index 41918453..2f05bf7c 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InfoProducer.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InfoProducer.scala @@ -1,5 +1,6 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi._ import org.scaladebugger.api.profiles.traits.info.events.EventInfoProducer import org.scaladebugger.api.virtualmachines.ScalaVirtualMachine diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InterfaceTypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InterfaceTypeInfo.scala index e8751a0a..c9753983 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InterfaceTypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/InterfaceTypeInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.InterfaceType +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for retrieving interface type-based information. */ +//@Freezable trait InterfaceTypeInfo extends ReferenceTypeInfo with TypeInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait InterfaceTypeInfo extends ReferenceTypeInfo with TypeInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: InterfaceTypeInfo /** @@ -22,6 +27,7 @@ trait InterfaceTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: InterfaceType /** @@ -29,6 +35,7 @@ trait InterfaceTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The collection of class type info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def implementors: Seq[ClassTypeInfo] /** @@ -36,6 +43,7 @@ trait InterfaceTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The collection of interface type info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def subinterfaces: Seq[InterfaceTypeInfo] /** @@ -43,6 +51,7 @@ trait InterfaceTypeInfo extends ReferenceTypeInfo with TypeInfo { * * @return The collection of interface type info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def superinterfaces: Seq[InterfaceTypeInfo] /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/JavaInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/JavaInfo.scala index 7d2b08b1..24b438f2 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/JavaInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/JavaInfo.scala @@ -1,10 +1,14 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} + /** * Represents a profile that provides common methods to convert info profiles * to their low-level Java equivalents. */ -trait JavaInfo { +//@Freezable +trait JavaInfo extends java.io.Serializable { /** * Returns whether or not this info profile represents the low-level Java * implementation. @@ -13,6 +17,7 @@ trait JavaInfo { * otherwise this profile represents something higher-level like * Scala, Jython, or JRuby */ + @CanFreeze def isJavaInfo: Boolean /** @@ -22,5 +27,6 @@ trait JavaInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze def toJavaInfo: AnyRef } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/LocationInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/LocationInfo.scala index 040f8d14..12e292a0 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/LocationInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/LocationInfo.scala @@ -1,12 +1,17 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Location +import org.scaladebugger.api.virtualmachines.ScalaVirtualMachine +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for location-based interaction. */ +//@Freezable trait LocationInfo extends CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +20,7 @@ trait LocationInfo extends CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: LocationInfo /** @@ -22,6 +28,7 @@ trait LocationInfo extends CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Location /** @@ -30,6 +37,7 @@ trait LocationInfo extends CommonInfo { * * @return The reference type information */ + @CanFreeze(ReturnType.FreezeObject) def declaringType: ReferenceTypeInfo /** @@ -37,6 +45,7 @@ trait LocationInfo extends CommonInfo { * * @return The method information */ + @CanFreeze(ReturnType.FreezeObject) def method: MethodInfo /** @@ -44,6 +53,7 @@ trait LocationInfo extends CommonInfo { * * @return The code position, or -1 if not available */ + @CanFreeze def codeIndex: Long /** @@ -58,6 +68,7 @@ trait LocationInfo extends CommonInfo { * * @return The line number, or -1 if not available */ + @CanFreeze def lineNumber: Int /** @@ -73,6 +84,7 @@ trait LocationInfo extends CommonInfo { * * @return The identifying name */ + @CanFreeze def sourceName: String /** @@ -88,6 +100,7 @@ trait LocationInfo extends CommonInfo { * * @return The source path */ + @CanFreeze def sourcePath: String /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MethodInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MethodInfo.scala index b0bc6cb0..82beb870 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MethodInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MethodInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Method +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for method-based interaction. */ +//@Freezable trait MethodInfo extends CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait MethodInfo extends CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: MethodInfo /** @@ -22,6 +27,7 @@ trait MethodInfo extends CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Method /** @@ -29,6 +35,7 @@ trait MethodInfo extends CommonInfo { * * @return The name of the method */ + @CanFreeze def name: String /** @@ -46,6 +53,7 @@ trait MethodInfo extends CommonInfo { * * @return The collection of parameter type names */ + @CanFreeze def parameterTypeNames: Seq[String] /** @@ -53,6 +61,7 @@ trait MethodInfo extends CommonInfo { * * @return The collection of profiles containing type information */ + @CanFreeze(ReturnType.FreezeCollection) def parameterTypes: Seq[TypeInfo] /** @@ -77,6 +86,7 @@ trait MethodInfo extends CommonInfo { * * @return The return type name */ + @CanFreeze def returnTypeName: String /** @@ -84,6 +94,7 @@ trait MethodInfo extends CommonInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) def returnType: TypeInfo /** @@ -98,6 +109,7 @@ trait MethodInfo extends CommonInfo { * * @return The reference type information that declared this method */ + @CanFreeze(ReturnType.FreezeObject) def declaringType: ReferenceTypeInfo /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MiscInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MiscInfo.scala index 1b3ca160..1cfc51e9 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MiscInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/MiscInfo.scala @@ -1,5 +1,6 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import org.scaladebugger.api.virtualmachines.ScalaVirtualMachine /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ObjectInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ObjectInfo.scala index 08845aef..34d26676 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ObjectInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ObjectInfo.scala @@ -1,13 +1,17 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ObjectReference import org.scaladebugger.api.lowlevel.JDIArgument +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for object-based interaction. */ +//@Freezable trait ObjectInfo extends ValueInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -16,6 +20,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ObjectInfo /** @@ -23,6 +28,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ObjectReference /** @@ -30,6 +36,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) override def `type`: ReferenceTypeInfo /** @@ -37,6 +44,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * * @return The unique id as a long */ + @CanFreeze def uniqueId: Long /** @@ -55,6 +63,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * yield the reference type for String, not AnyRef. * @return The reference type information */ + @CanFreeze(ReturnType.FreezeObject) def referenceType: ReferenceTypeInfo /** @@ -197,6 +206,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * method invocation * @return The resulting value of the invocation */ + @CannotFreeze def invoke( thread: ThreadInfo, method: MethodInfo, @@ -217,6 +227,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * * @return The profiles wrapping the visible fields in this object */ + @CanFreeze(ReturnType.FreezeCollection) def fields: Seq[FieldVariableInfo] /** @@ -232,6 +243,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * * @return The profiles wrapping the visible fields in this object */ + @CanFreeze(ReturnType.FreezeCollection) def indexedFields: Seq[FieldVariableInfo] /** @@ -263,6 +275,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * @param name The name of the field * @return Some profile wrapping the field, or None if doesn't exist */ + @CannotFreeze def indexedFieldOption(name: String): Option[FieldVariableInfo] /** @@ -289,6 +302,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * @param name The name of the field * @return Some profile wrapping the field, or None if doesn't exist */ + @CannotFreeze def fieldOption(name: String): Option[FieldVariableInfo] /** @@ -304,6 +318,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * * @return The profiles wrapping the visible methods in this object */ + @CanFreeze(ReturnType.FreezeCollection) def methods: Seq[MethodInfo] /** @@ -342,6 +357,7 @@ trait ObjectInfo extends ValueInfo with CommonInfo { * of the method to find * @return Some profile wrapping the method, otherwise None if doesn't exist */ + @CannotFreeze def methodOption( name: String, parameterTypeNames: String* diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveInfo.scala index 793eaac9..eac8f021 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveInfo.scala @@ -1,13 +1,17 @@ package org.scaladebugger.api.profiles.traits.info -import com.sun.jdi.{PrimitiveValue, Value} +import acyclic.file +import com.sun.jdi.Value +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.{Failure, Success, Try} /** * Represents information about a primitive value. */ +//@Freezable trait PrimitiveInfo extends ValueInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -16,6 +20,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: PrimitiveInfo /** @@ -23,6 +28,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Value /** @@ -30,6 +36,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) override def `type`: PrimitiveTypeInfo /** @@ -45,6 +52,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return The value as a local instance */ + @CanFreeze override def toLocalValue: AnyVal /** @@ -52,6 +60,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a boolean, otherwise false */ + @CanFreeze def isBoolean: Boolean /** @@ -59,6 +68,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a byte, otherwise false */ + @CanFreeze def isByte: Boolean /** @@ -66,6 +76,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a char, otherwise false */ + @CanFreeze def isChar: Boolean /** @@ -73,6 +84,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a double, otherwise false */ + @CanFreeze def isDouble: Boolean /** @@ -80,6 +92,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a float, otherwise false */ + @CanFreeze def isFloat: Boolean /** @@ -87,6 +100,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a integer, otherwise false */ + @CanFreeze def isInteger: Boolean /** @@ -94,6 +108,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a long, otherwise false */ + @CanFreeze def isLong: Boolean /** @@ -101,6 +116,7 @@ trait PrimitiveInfo extends ValueInfo with CommonInfo { * * @return True if the primitive is a short, otherwise false */ + @CanFreeze def isShort: Boolean /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveTypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveTypeInfo.scala index 4b9f0ae0..645b8f15 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveTypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/PrimitiveTypeInfo.scala @@ -1,8 +1,12 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file +import org.scaladebugger.macros.freeze.{CannotFreeze, Freezable} + /** * Represents the interface for retrieving primitive type-based information. */ +//@Freezable trait PrimitiveTypeInfo extends TypeInfo { /** * Converts the current profile instance to a representation of @@ -11,6 +15,7 @@ trait PrimitiveTypeInfo extends TypeInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: PrimitiveTypeInfo /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ReferenceTypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ReferenceTypeInfo.scala index 149b2ce9..855a9462 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ReferenceTypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ReferenceTypeInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ReferenceType +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for "reference type"-based interaction. */ +//@Freezable trait ReferenceTypeInfo extends CommonInfo with TypeInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ReferenceTypeInfo /** @@ -22,6 +27,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ReferenceType /** @@ -30,6 +36,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of fields as variable info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def allFields: Seq[FieldVariableInfo] /** @@ -49,6 +56,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of fields as variable info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def visibleFields: Seq[FieldVariableInfo] /** @@ -71,6 +79,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of fields as variable info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def indexedVisibleFields: Seq[FieldVariableInfo] /** @@ -104,6 +113,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * @param name The name of the field to retrieve * @return Some field as a variable info profile, or None if doesn't exist */ + @CannotFreeze def indexedFieldOption(name: String): Option[FieldVariableInfo] /** @@ -123,6 +133,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * @param name The name of the field to retrieve * @return Some field as a variable info profile, or None if doesn't exist */ + @CannotFreeze def fieldOption(name: String): Option[FieldVariableInfo] /** @@ -149,6 +160,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of methods as method info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def allMethods: Seq[MethodInfo] /** @@ -168,6 +180,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of methods as method info profiles */ + @CanFreeze(ReturnType.FreezeCollection) def visibleMethods: Seq[MethodInfo] /** @@ -187,6 +200,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * @param name The name of the method to retrieve * @return The collection of method info profiles */ + @CannotFreeze def methods(name: String): Seq[MethodInfo] /** @@ -215,6 +229,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * @return Some profile representing the classloader, * otherwise None if loaded through the bootstrap classloader */ + @CanFreeze(ReturnType.FreezeOption) def classLoaderOption: Option[ClassLoaderInfo] /** @@ -222,6 +237,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The profile representing the class */ + @CanFreeze(ReturnType.FreezeObject) def classObject: ClassObjectInfo /** @@ -229,6 +245,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return Some signature if it exists, otherwise None */ + @CanFreeze def genericSignature: Option[String] /** @@ -238,6 +255,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * to get all reachable instances * @return The collection of object instances */ + @CannotFreeze def instances(maxInstances: Long): Seq[ObjectInfo] /** @@ -271,6 +289,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return True if abstract, otherwise false */ + @CanFreeze def isAbstract: Boolean /** @@ -278,6 +297,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return True if final, otherwise false */ + @CanFreeze def isFinal: Boolean /** @@ -287,6 +307,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return True if initialized, otherwise false */ + @CanFreeze def isInitialized: Boolean /** @@ -294,6 +315,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return True if prepared, otherwise false */ + @CanFreeze def isPrepared: Boolean /** @@ -301,6 +323,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return True if static, otherwise false */ + @CanFreeze def isStatic: Boolean /** @@ -310,6 +333,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return True if verified, otherwise false */ + @CanFreeze def isVerified: Boolean /** @@ -318,6 +342,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of location information */ + @CanFreeze(ReturnType.FreezeCollection) def allLineLocations: Seq[LocationInfo] /** @@ -336,6 +361,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of location information */ + @CannotFreeze def locationsOfLine(line: Int): Seq[LocationInfo] /** @@ -354,6 +380,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The major version number */ + @CanFreeze def majorVersion: Int /** @@ -370,6 +397,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The minor version number */ + @CanFreeze def minorVersion: Int /** @@ -385,6 +413,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The fully-qualified class name */ + @CanFreeze def name: String /** @@ -393,6 +422,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of reference type information */ + @CanFreeze(ReturnType.FreezeCollection) def nestedTypes: Seq[ReferenceTypeInfo] /** @@ -400,6 +430,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The source debug extension */ + @CanFreeze def sourceDebugExtension: String /** @@ -415,6 +446,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of identifying names */ + @CanFreeze def sourceNames: Seq[String] /** @@ -431,6 +463,7 @@ trait ReferenceTypeInfo extends CommonInfo with TypeInfo { * * @return The collection of source paths */ + @CanFreeze def sourcePaths: Seq[String] /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/StringInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/StringInfo.scala index b1ef8b37..b72b751a 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/StringInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/StringInfo.scala @@ -1,15 +1,13 @@ package org.scaladebugger.api.profiles.traits.info - -import com.sun.jdi.{ArrayReference, StringReference} -import org.scaladebugger.api.profiles.traits.info.ArrayInfo._ - -import scala.util.Try - +import acyclic.file +import com.sun.jdi.StringReference +import org.scaladebugger.macros.freeze.{CannotFreeze, Freezable} /** * Represents the interface for string-based interaction. */ +//@Freezable trait StringInfo extends ObjectInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -18,6 +16,7 @@ trait StringInfo extends ObjectInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: StringInfo /** @@ -25,6 +24,7 @@ trait StringInfo extends ObjectInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: StringReference /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadGroupInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadGroupInfo.scala index 11335ff8..66850ed4 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadGroupInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadGroupInfo.scala @@ -1,13 +1,15 @@ package org.scaladebugger.api.profiles.traits.info -import com.sun.jdi.{ThreadGroupReference, ThreadReference} - -import scala.util.Try +import acyclic.file +import com.sun.jdi.ThreadGroupReference +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} /** * Represents the interface for thread-based interaction. */ +//@Freezable trait ThreadGroupInfo extends ObjectInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -16,6 +18,7 @@ trait ThreadGroupInfo extends ObjectInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ThreadGroupInfo /** @@ -23,6 +26,7 @@ trait ThreadGroupInfo extends ObjectInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ThreadGroupReference /** @@ -30,6 +34,7 @@ trait ThreadGroupInfo extends ObjectInfo with CommonInfo { * * @return The thread group name as a string */ + @CanFreeze def name: String /** @@ -37,18 +42,21 @@ trait ThreadGroupInfo extends ObjectInfo with CommonInfo { * * @return Some thread group if a parent exists, otherwise None if top-level */ + @CanFreeze(ReturnType.FreezeOption) def parent: Option[ThreadGroupInfo] /** * Resumes all threads in the thread group and subgroups. This is not an * atomic operation, so new threads added to a group will be unaffected. */ + @CannotFreeze def resume(): Unit /** * Suspends all threads in the thread group and subgroups. This is not an * atomic operation, so new threads added to a group will be unaffected. */ + @CannotFreeze def suspend(): Unit /** @@ -57,6 +65,7 @@ trait ThreadGroupInfo extends ObjectInfo with CommonInfo { * * @return The collection of threads */ + @CanFreeze(ReturnType.FreezeCollection) def threads: Seq[ThreadInfo] /** @@ -65,6 +74,7 @@ trait ThreadGroupInfo extends ObjectInfo with CommonInfo { * * @return The collection of thread groups */ + @CanFreeze(ReturnType.FreezeCollection) def threadGroups: Seq[ThreadGroupInfo] /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadInfo.scala index 6c2e894e..0b907eb6 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.ThreadReference +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for thread-based interaction. */ +//@Freezable trait ThreadInfo extends ObjectInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ThreadInfo /** @@ -22,6 +27,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: ThreadReference /** @@ -29,6 +35,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * * @return The thread name as a string */ + @CanFreeze def name: String /** @@ -36,6 +43,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * * @return The thread's status as a profile */ + @CanFreeze(ReturnType.FreezeObject) def status: ThreadStatusInfo /** @@ -43,6 +51,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * * @return The profile of the thread group */ + @CanFreeze(ReturnType.FreezeObject) def threadGroup: ThreadGroupInfo /** @@ -50,11 +59,13 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * counter. If the counter remains greater than zero, the thread remains * suspended. */ + @CannotFreeze def resume(): Unit /** * Suspends the thread by incrementing the pending suspension counter. */ + @CannotFreeze def suspend(): Unit /** @@ -65,6 +76,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * @tparam T The return type of the block of code * @return Success containing the result of the thunk, or a failure */ + @CannotFreeze def suspendAndExecute[T](thunk: => T): Try[T] = { suspend() @@ -87,6 +99,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * * @return The collection of frame profiles */ + @CanFreeze(ReturnType.FreezeCollection) def frames: Seq[FrameInfo] /** @@ -136,6 +149,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * at index * @return The collection of frame profiles */ + @CannotFreeze protected def rawFrames(index: Int, length: Int): Seq[FrameInfo] /** @@ -150,6 +164,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * * @return The total number of frames */ + @CanFreeze def totalFrames: Int /** @@ -169,6 +184,7 @@ trait ThreadInfo extends ObjectInfo with CommonInfo { * profile to retrieve * @return The new frame profile instance */ + @CannotFreeze def frame(index: Int): FrameInfo /** diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadStatusInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadStatusInfo.scala index d5bac47d..012f52d9 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadStatusInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ThreadStatusInfo.scala @@ -1,14 +1,19 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file +import org.scaladebugger.macros.freeze.{CanFreeze, Freezable} + /** * Represents information about a thread's status. */ +//@Freezable trait ThreadStatusInfo { /** * Represents the status code for the thread. * * @return The status code as a number */ + @CanFreeze def statusCode: Int /** @@ -34,6 +39,7 @@ trait ThreadStatusInfo { * * @return False if the status is known, otherwise true */ + @CanFreeze def isUnknown: Boolean /** @@ -41,6 +47,7 @@ trait ThreadStatusInfo { * * @return True if a zombie, otherwise false */ + @CanFreeze def isZombie: Boolean /** @@ -48,6 +55,7 @@ trait ThreadStatusInfo { * * @return True if running, otherwise false */ + @CanFreeze def isRunning: Boolean /** @@ -55,6 +63,7 @@ trait ThreadStatusInfo { * * @return True if sleeping, otherwise false */ + @CanFreeze def isSleeping: Boolean /** @@ -62,6 +71,7 @@ trait ThreadStatusInfo { * * @return True if monitoring, otherwise false */ + @CanFreeze def isMonitor: Boolean /** @@ -69,6 +79,7 @@ trait ThreadStatusInfo { * * @return True if waiting, otherwise false */ + @CanFreeze def isWait: Boolean /** @@ -76,6 +87,7 @@ trait ThreadStatusInfo { * * @return True if has started, otherwise false */ + @CanFreeze def isNotStarted: Boolean /** @@ -83,6 +95,7 @@ trait ThreadStatusInfo { * * @return True if suspended at a breakpoint, otherwise false */ + @CanFreeze def isAtBreakpoint: Boolean /** @@ -90,6 +103,7 @@ trait ThreadStatusInfo { * * @return True if suspended, otherwise false */ + @CanFreeze def isSuspended: Boolean /** @@ -97,5 +111,6 @@ trait ThreadStatusInfo { * * @return The total number of pending suspensions */ + @CanFreeze def suspendCount: Int } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeChecker.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeChecker.scala index 6e84b03a..adb2b398 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeChecker.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeChecker.scala @@ -1,4 +1,5 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file /** * Represents the interface for type-checking based interactions. diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeConverter.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeConverter.scala new file mode 100644 index 00000000..316b3730 --- /dev/null +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeConverter.scala @@ -0,0 +1,88 @@ +package org.scaladebugger.api.profiles.traits.info + +import org.scaladebugger.macros.freeze.CanFreeze +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType + +import scala.util.Try + +trait TypeConverter { + /** + * Returns the type as an array type (profile). + * + * @return The array type profile wrapping this type + */ + @CanFreeze(ReturnType.FreezeObject) + def toArrayType: ArrayTypeInfo + + /** + * Returns the type as an array type (profile). + * + * @return Success containing the array type profile wrapping this type, + * otherwise a failure + */ + def tryToArrayType: Try[ArrayTypeInfo] = Try(toArrayType) + + /** + * Returns the type as an class type (profile). + * + * @return The class type profile wrapping this type + */ + @CanFreeze(ReturnType.FreezeObject) + def toClassType: ClassTypeInfo + + /** + * Returns the type as an class type (profile). + * + * @return Success containing the class type profile wrapping this type, + * otherwise a failure + */ + def tryToClassType: Try[ClassTypeInfo] = Try(toClassType) + + /** + * Returns the type as an interface type (profile). + * + * @return The interface type profile wrapping this type + */ + @CanFreeze(ReturnType.FreezeObject) + def toInterfaceType: InterfaceTypeInfo + + /** + * Returns the type as an interface type (profile). + * + * @return Success containing the interface type profile wrapping this type, + * otherwise a failure + */ + def tryToInterfaceType: Try[InterfaceTypeInfo] = Try(toInterfaceType) + + /** + * Returns the type as an reference type (profile). + * + * @return The reference type profile wrapping this type + */ + @CanFreeze(ReturnType.FreezeObject) + def toReferenceType: ReferenceTypeInfo + + /** + * Returns the type as an reference type (profile). + * + * @return Success containing the reference type profile wrapping this type, + * otherwise a failure + */ + def tryToReferenceType: Try[ReferenceTypeInfo] = Try(toReferenceType) + + /** + * Returns the type as an primitive type (profile). + * + * @return The primitive type profile wrapping this type + */ + @CanFreeze(ReturnType.FreezeObject) + def toPrimitiveType: PrimitiveTypeInfo + + /** + * Returns the type as an primitive type (profile). + * + * @return Success containing the primitive type profile wrapping this type, + * otherwise a failure + */ + def tryToPrimitiveType: Try[PrimitiveTypeInfo] = Try(toPrimitiveType) +} diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeInfo.scala index 469508db..0f1b2b4b 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/TypeInfo.scala @@ -1,12 +1,15 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Type +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for retrieving type-based information. */ +//@Freezable trait TypeInfo extends CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +18,7 @@ trait TypeInfo extends CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: TypeInfo /** @@ -22,6 +26,7 @@ trait TypeInfo extends CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Type /** @@ -29,6 +34,7 @@ trait TypeInfo extends CommonInfo { * * @return The text representation of the type */ + @CanFreeze def name: String /** @@ -38,6 +44,7 @@ trait TypeInfo extends CommonInfo { * * @return The JNI-style signature */ + @CanFreeze def signature: String /** @@ -108,6 +115,7 @@ trait TypeInfo extends CommonInfo { * * @return True if an array type, otherwise false */ + @CanFreeze def isArrayType: Boolean /** @@ -115,6 +123,7 @@ trait TypeInfo extends CommonInfo { * * @return True if a class type, otherwise false */ + @CanFreeze def isClassType: Boolean /** @@ -122,6 +131,7 @@ trait TypeInfo extends CommonInfo { * * @return True if an interface type, otherwise false */ + @CanFreeze def isInterfaceType: Boolean /** @@ -129,6 +139,7 @@ trait TypeInfo extends CommonInfo { * * @return True if a reference type, otherwise false */ + @CanFreeze def isReferenceType: Boolean /** @@ -136,6 +147,7 @@ trait TypeInfo extends CommonInfo { * * @return True if a primitive type, otherwise false */ + @CanFreeze def isPrimitiveType: Boolean /** @@ -143,83 +155,9 @@ trait TypeInfo extends CommonInfo { * * @return True if representing the type of a null value, otherwise false */ + @CanFreeze def isNullType: Boolean - /** - * Returns the type as an array type (profile). - * - * @return The array type profile wrapping this type - */ - def toArrayType: ArrayTypeInfo - - /** - * Returns the type as an array type (profile). - * - * @return Success containing the array type profile wrapping this type, - * otherwise a failure - */ - def tryToArrayType: Try[ArrayTypeInfo] = Try(toArrayType) - - /** - * Returns the type as an class type (profile). - * - * @return The class type profile wrapping this type - */ - def toClassType: ClassTypeInfo - - /** - * Returns the type as an class type (profile). - * - * @return Success containing the class type profile wrapping this type, - * otherwise a failure - */ - def tryToClassType: Try[ClassTypeInfo] = Try(toClassType) - - /** - * Returns the type as an interface type (profile). - * - * @return The interface type profile wrapping this type - */ - def toInterfaceType: InterfaceTypeInfo - - /** - * Returns the type as an interface type (profile). - * - * @return Success containing the interface type profile wrapping this type, - * otherwise a failure - */ - def tryToInterfaceType: Try[InterfaceTypeInfo] = Try(toInterfaceType) - - /** - * Returns the type as an reference type (profile). - * - * @return The reference type profile wrapping this type - */ - def toReferenceType: ReferenceTypeInfo - - /** - * Returns the type as an reference type (profile). - * - * @return Success containing the reference type profile wrapping this type, - * otherwise a failure - */ - def tryToReferenceType: Try[ReferenceTypeInfo] = Try(toReferenceType) - - /** - * Returns the type as an primitive type (profile). - * - * @return The primitive type profile wrapping this type - */ - def toPrimitiveType: PrimitiveTypeInfo - - /** - * Returns the type as an primitive type (profile). - * - * @return Success containing the primitive type profile wrapping this type, - * otherwise a failure - */ - def tryToPrimitiveType: Try[PrimitiveTypeInfo] = Try(toPrimitiveType) - /** * Attempts to cast the provided primitive to this type, performing any * necessary data conversions. diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/UnsupportedTypeException.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/UnsupportedTypeException.scala index 1a0289e8..702ac879 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/UnsupportedTypeException.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/UnsupportedTypeException.scala @@ -1,4 +1,5 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file /** * Represents an exception that occurs when a local value is trying to be diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueConverter.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueConverter.scala new file mode 100644 index 00000000..6f8f0498 --- /dev/null +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueConverter.scala @@ -0,0 +1,144 @@ +package org.scaladebugger.api.profiles.traits.info + +import org.scaladebugger.macros.freeze.CanFreeze +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType + +import scala.util.Try + +trait ValueConverter { + /** + * Returns the value as a primitive (profile). + * + * @return Success containing the primitive profile wrapping this value, + * otherwise a failure + */ + def tryToPrimitiveInfo: Try[PrimitiveInfo] = Try(toPrimitiveInfo) + + /** + * Returns the value as a primitive (profile). + * + * @return The primitive profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toPrimitiveInfo: PrimitiveInfo + + /** + * Returns the value as a class loader (profile). + * + * @return Success containing the class loader profile wrapping this value, + * otherwise a failure + */ + def tryToClassLoaderInfo: Try[ClassLoaderInfo] = Try(toClassLoaderInfo) + + /** + * Returns the value as a class loader (profile). + * + * @return The class loader profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toClassLoaderInfo: ClassLoaderInfo + + /** + * Returns the value as a class object (profile). + * + * @return Success containing the class object profile wrapping this value, + * otherwise a failure + */ + def tryToClassObjectInfo: Try[ClassObjectInfo] = Try(toClassObjectInfo) + + /** + * Returns the value as a class object (profile). + * + * @return The class object profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toClassObjectInfo: ClassObjectInfo + + /** + * Returns the value as a thread group (profile). + * + * @return Success containing the thread group profile wrapping this value, + * otherwise a failure + */ + def tryToThreadGroupInfo: Try[ThreadGroupInfo] = Try(toThreadGroupInfo) + + /** + * Returns the value as a thread group (profile). + * + * @return The thread group profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toThreadGroupInfo: ThreadGroupInfo + + /** + * Returns the value as a thread (profile). + * + * @return Success containing the thread profile wrapping this value, + * otherwise a failure + */ + def tryToThreadInfo: Try[ThreadInfo] = Try(toThreadInfo) + + /** + * Returns the value as a thread (profile). + * + * @return The thread profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toThreadInfo: ThreadInfo + + /** + * Returns the value as an object (profile). + * + * @return Success containing the object profile wrapping this value, + * otherwise a failure + */ + def tryToObjectInfo: Try[ObjectInfo] = Try(toObjectInfo) + + /** + * Returns the value as an object (profile). + * + * @return The object profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toObjectInfo: ObjectInfo + + /** + * Returns the value as a string (profile). + * + * @return Success containing the string profile wrapping this value, + * otherwise a failure + */ + def tryToStringInfo: Try[StringInfo] = Try(toStringInfo) + + /** + * Returns the value as an string (profile). + * + * @return The string profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toStringInfo: StringInfo + + /** + * Returns the value as an array (profile). + * + * @return Success containing the array profile wrapping this value, + * otherwise a failure + */ + def tryToArrayInfo: Try[ArrayInfo] = Try(toArrayInfo) + + /** + * Returns the value as an array (profile). + * + * @return The array profile wrapping this value + */ + @throws[AssertionError] + @CanFreeze(ReturnType.FreezeObject) + def toArrayInfo: ArrayInfo +} diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueInfo.scala index 421f1267..d10826d2 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/ValueInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Value +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents information about a value. */ +//@Freezable trait ValueInfo extends CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait ValueInfo extends CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: ValueInfo /** @@ -22,6 +27,7 @@ trait ValueInfo extends CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Value /** @@ -29,6 +35,7 @@ trait ValueInfo extends CommonInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) def `type`: TypeInfo /** @@ -52,6 +59,7 @@ trait ValueInfo extends CommonInfo { * * @return The value as a local instance */ + @CannotFreeze def toLocalValue: Any /** @@ -59,6 +67,7 @@ trait ValueInfo extends CommonInfo { * * @return True if a primitive, otherwise false */ + @CanFreeze def isPrimitive: Boolean /** @@ -66,6 +75,7 @@ trait ValueInfo extends CommonInfo { * * @return True if an array, otherwise false */ + @CanFreeze def isArray: Boolean /** @@ -73,6 +83,7 @@ trait ValueInfo extends CommonInfo { * * @return True if a class loader, otherwise false */ + @CanFreeze def isClassLoader: Boolean /** @@ -80,6 +91,7 @@ trait ValueInfo extends CommonInfo { * * @return True if a class object, otherwise false */ + @CanFreeze def isClassObject: Boolean /** @@ -87,6 +99,7 @@ trait ValueInfo extends CommonInfo { * * @return True if a thread group, otherwise false */ + @CanFreeze def isThreadGroup: Boolean /** @@ -94,6 +107,7 @@ trait ValueInfo extends CommonInfo { * * @return True if a thread, otherwise false */ + @CanFreeze def isThread: Boolean /** @@ -101,6 +115,7 @@ trait ValueInfo extends CommonInfo { * * @return True if an object, otherwise false */ + @CanFreeze def isObject: Boolean /** @@ -108,6 +123,7 @@ trait ValueInfo extends CommonInfo { * * @return True if a string, otherwise false */ + @CanFreeze def isString: Boolean /** @@ -115,6 +131,7 @@ trait ValueInfo extends CommonInfo { * * @return True if null, otherwise false */ + @CanFreeze def isNull: Boolean /** @@ -122,136 +139,9 @@ trait ValueInfo extends CommonInfo { * * @return True if void, otherwise false */ + @CanFreeze def isVoid: Boolean - /** - * Returns the value as a primitive (profile). - * - * @return Success containing the primitive profile wrapping this value, - * otherwise a failure - */ - def tryToPrimitiveInfo: Try[PrimitiveInfo] = Try(toPrimitiveInfo) - - /** - * Returns the value as a primitive (profile). - * - * @return The primitive profile wrapping this value - */ - @throws[AssertionError] - def toPrimitiveInfo: PrimitiveInfo - - /** - * Returns the value as a class loader (profile). - * - * @return Success containing the class loader profile wrapping this value, - * otherwise a failure - */ - def tryToClassLoaderInfo: Try[ClassLoaderInfo] = Try(toClassLoaderInfo) - - /** - * Returns the value as a class loader (profile). - * - * @return The class loader profile wrapping this value - */ - @throws[AssertionError] - def toClassLoaderInfo: ClassLoaderInfo - - /** - * Returns the value as a class object (profile). - * - * @return Success containing the class object profile wrapping this value, - * otherwise a failure - */ - def tryToClassObjectInfo: Try[ClassObjectInfo] = Try(toClassObjectInfo) - - /** - * Returns the value as a class object (profile). - * - * @return The class object profile wrapping this value - */ - @throws[AssertionError] - def toClassObjectInfo: ClassObjectInfo - - /** - * Returns the value as a thread group (profile). - * - * @return Success containing the thread group profile wrapping this value, - * otherwise a failure - */ - def tryToThreadGroupInfo: Try[ThreadGroupInfo] = Try(toThreadGroupInfo) - - /** - * Returns the value as a thread group (profile). - * - * @return The thread group profile wrapping this value - */ - @throws[AssertionError] - def toThreadGroupInfo: ThreadGroupInfo - - /** - * Returns the value as a thread (profile). - * - * @return Success containing the thread profile wrapping this value, - * otherwise a failure - */ - def tryToThreadInfo: Try[ThreadInfo] = Try(toThreadInfo) - - /** - * Returns the value as a thread (profile). - * - * @return The thread profile wrapping this value - */ - @throws[AssertionError] - def toThreadInfo: ThreadInfo - - /** - * Returns the value as an object (profile). - * - * @return Success containing the object profile wrapping this value, - * otherwise a failure - */ - def tryToObjectInfo: Try[ObjectInfo] = Try(toObjectInfo) - - /** - * Returns the value as an object (profile). - * - * @return The object profile wrapping this value - */ - @throws[AssertionError] - def toObjectInfo: ObjectInfo - - /** - * Returns the value as a string (profile). - * - * @return Success containing the string profile wrapping this value, - * otherwise a failure - */ - def tryToStringInfo: Try[StringInfo] = Try(toStringInfo) - - /** - * Returns the value as an string (profile). - * - * @return The string profile wrapping this value - */ - @throws[AssertionError] - def toStringInfo: StringInfo - - /** - * Returns the value as an array (profile). - * - * @return Success containing the array profile wrapping this value, - * otherwise a failure - */ - def tryToArrayInfo: Try[ArrayInfo] = Try(toArrayInfo) - - /** - * Returns the value as an array (profile). - * - * @return The array profile wrapping this value - */ - @throws[AssertionError] - def toArrayInfo: ArrayInfo - /** * Returns a string presenting a better human-readable description of * the JDI instance. @@ -262,14 +152,14 @@ trait ValueInfo extends CommonInfo { Try { if (this.isNull) "null" else if (this.isVoid) "void" - else if (this.isArray) this.toArrayInfo.toPrettyString - else if (this.isString) this.toStringInfo.toPrettyString - else if (this.isClassLoader) this.toClassLoaderInfo.toPrettyString - else if (this.isClassObject) this.toClassObjectInfo.toPrettyString - else if (this.isThreadGroup) this.toThreadGroupInfo.toPrettyString - else if (this.isThread) this.toThreadInfo.toPrettyString - else if (this.isObject) this.toObjectInfo.toPrettyString - else if (this.isPrimitive) this.toPrimitiveInfo.toPrettyString + else if (this.isArray) "ARRAY TODO"//this.toArrayInfo.toPrettyString + else if (this.isString) "STRING TODO"//this.toStringInfo.toPrettyString + else if (this.isClassLoader) "CLASS LOADER TODO"//this.toClassLoaderInfo.toPrettyString + else if (this.isClassObject) "CLASS OBJECT TODO"//this.toClassObjectInfo.toPrettyString + else if (this.isThreadGroup) "THREAD GROUP TODO"//this.toThreadGroupInfo.toPrettyString + else if (this.isThread) "THREAD TODO"//this.toThreadInfo.toPrettyString + else if (this.isObject) "OBJECT TODO"//this.toObjectInfo.toPrettyString + else if (this.isPrimitive) "PRIMITIVE TODO"//this.toPrimitiveInfo.toPrettyString else "???" }.getOrElse("") } diff --git a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/VariableInfo.scala b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/VariableInfo.scala index 5a668e3a..b6a11648 100644 --- a/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/VariableInfo.scala +++ b/scala-debugger-api/src/main/scala/org/scaladebugger/api/profiles/traits/info/VariableInfo.scala @@ -1,12 +1,16 @@ package org.scaladebugger.api.profiles.traits.info +import acyclic.file import com.sun.jdi.Mirror +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.{CanFreeze, CannotFreeze, Freezable} import scala.util.Try /** * Represents the interface for variable-based interaction. */ +//@Freezable trait VariableInfo extends CreateInfo with CommonInfo { /** * Converts the current profile instance to a representation of @@ -15,6 +19,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * @return The profile instance providing an implementation corresponding * to Java */ + @CannotFreeze override def toJavaInfo: VariableInfo /** @@ -22,6 +27,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return The JDI instance */ + @CannotFreeze override def toJdiInstance: Mirror /** @@ -29,6 +35,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return The name of the variable */ + @CanFreeze def name: String /** @@ -44,6 +51,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return Non-negative number if provided with an index, otherwise -1 */ + @CanFreeze def offsetIndex: Int /** @@ -51,6 +59,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return The type name as a string */ + @CanFreeze def typeName: String /** @@ -58,6 +67,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return The profile containing type information */ + @CanFreeze(ReturnType.FreezeObject) def `type`: TypeInfo /** @@ -73,6 +83,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return True if a field, otherwise false */ + @CanFreeze def isField: Boolean /** @@ -80,6 +91,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return True if a local variable, otherwise false */ + @CanFreeze def isLocal: Boolean /** @@ -87,6 +99,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return True if an argument, otherwise false */ + @CanFreeze def isArgument: Boolean /** @@ -102,6 +115,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * * @return The profile representing the value */ + @CanFreeze(ReturnType.FreezeObject) def toValueInfo: ValueInfo /** @@ -154,6 +168,7 @@ trait VariableInfo extends CreateInfo with CommonInfo { * @param valueInfo The remote value to set for the variable * @return The info for the variable's new value */ + @CannotFreeze def setValueFromInfo(valueInfo: ValueInfo): ValueInfo /** diff --git a/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/.placeholder b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/.placeholder deleted file mode 100644 index e69de29b..00000000 diff --git a/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/MacroUtils.scala b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/MacroUtils.scala new file mode 100644 index 00000000..cbd00513 --- /dev/null +++ b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/MacroUtils.scala @@ -0,0 +1,189 @@ +package org.scaladebugger.macros + +import scala.language.experimental.macros +import scala.annotation.tailrec +import scala.reflect.macros.whitebox +import macrocompat.bundle + +@bundle class MacroUtils[C <: whitebox.Context](val c: C) { + import c.universe._ + + def containsAnnotation(d: DefDef, aName: String): Boolean = + findAnnotation(d, aName).nonEmpty + + def findAnnotation(d: DefDef, aName: String): Option[Tree] = + d.mods.annotations.find(a => isAnnotation(a, aName)) + + def isAnnotation(t: Tree, aName: String): Boolean = t match { + case Apply(f, args) => f match { + case Select(qual, name) => qual match { + case New(tpt) => tpt match { + case Ident(iName) => iName.decodedName.toString == aName + case _ => false + } + case _ => false + } + } + case _ => false + } + + def isTrait(classDef: ClassDef): Boolean = { + try { + val q""" + $mods trait $tpname[..$tparams] extends { + ..$earlydefns + } with ..$parents { $self => + ..$body + } + """ = classDef + true + } catch { + case _: Throwable => false + } + } + + def extractInheritedMethods(parents: List[Type]): List[MethodSymbol] = { + val anyTypeSymbol = typeOf[Any].typeSymbol + val objectTypeSymbol = typeOf[Object].typeSymbol + + def stripDuplicateMethods(methods: List[MethodSymbol]): List[MethodSymbol] = { + val overrides = methods.flatMap(_.overrides) + methods.filterNot(overrides.contains) + } + + @tailrec def extract(types: List[Type], methods: List[MethodSymbol]): List[MethodSymbol] = { + val parentMethods = types.flatMap(pt => { + pt.members + .filter(_.isMethod) + .filter(_.owner == pt.typeSymbol) + .map(_.asMethod) + }) + val parentTypes = types + .flatMap(t => { + t.baseClasses.filterNot(c => + c == t.typeSymbol || + c == anyTypeSymbol || + c == objectTypeSymbol + ) + }) + .filter(_.isClass) + .map(_.asClass.toType) + + val allMethods = stripDuplicateMethods(methods ++ parentMethods) + + if (parentTypes.nonEmpty) extract(parentTypes, allMethods) + else allMethods + } + + // TODO: Getting paramLists and materializing seems to trigger the + // appearance of our static annotations; figure out why and fix + extract(parents, Nil).map(m => { + m.paramLists.toString + m + }) + } + + def extractTypes(trees: List[Tree]): List[Option[Type]] = + trees.map(extractType) + + def extractType(tree: Tree): Option[Type] = { + def isBaseType(n1: Name, n2: Name = null): Boolean = { + val name = if (n2 != null) { + n1.toString + "." + n2.toString + } else { + n1.toString + } + + val baseTypeNames = Seq( + "Any", "scala.Any", "AnyRef", "scala.AnyRef", + "AnyVal", "scala.AnyVal", "java.lang.Object" + ) + + baseTypeNames.contains(name) + } + + tree match { + case Ident(name) if !isBaseType(name) => + val typeName = name.toTypeName + val typedTree = scala.util.Try(c.typecheck( + q"null.asInstanceOf[$typeName]", + withMacrosDisabled = true + )) + typedTree.toOption.flatMap(tt => Option(tt.tpe)) + case Select(Ident(name), typeName) if !isBaseType(name, typeName) => + val t1 = name.toTermName + val t2 = typeName.toTypeName + val typedTree = scala.util.Try(c.typecheck( + q"null.asInstanceOf[$t1.$t2]", + withMacrosDisabled = true + )) + typedTree.toOption.flatMap(tt => Option(tt.tpe)) + case a => + None + } + } + + def currentPackage(): String = { + val typeName = TypeName("Dummy" + c.freshName()) + val dummyType = c.typecheck(q"class $typeName").tpe + dummyType.typeConstructor + val ownerSymbol = dummyType.typeSymbol.owner + println("TYPE SYMBOL OF " + typeName + " IS " + dummyType.typeSymbol) + println("TERM SYMBOL OF " + typeName + " IS " + dummyType.termSymbol) + println("OWNER OF " + typeName + " IS " + ownerSymbol) + + @tailrec def buildPackage(owner: Symbol, tokens: List[String]): String = { + if (owner == NoSymbol) tokens.mkString(".") + else buildPackage(owner.owner, owner.name.decodedName.toString +: tokens) + } + + buildPackage(ownerSymbol, Nil) + } + + def newEmptyObject(name: String): ModuleDef = { + val oName = TermName(name) + val moduleDef: ModuleDef = q"object $oName {}" match { + case m: ModuleDef => m + } + moduleDef + } + + def classNameToTree(fqcn: String): Tree = { + classNameToTree(fqcn, fallback = None) + } + + def classNameToTree(fqcn: String, fallback: Option[String]): Tree = { + if (fqcn == "..." || fqcn == "" || fqcn == "") { + fallback.map(f => classNameToTree(f)).getOrElse(tq"") + } else { + val classNames = fqcn.split("\\[|\\]").filter(_.nonEmpty) + if (classNames.length <= 1) singleClassNameToTree(classNames.head) + else { + // NOTE: Assuming type param is only one, not more than one + AppliedTypeTree( + classNameToTree(classNames.head, fallback), + classNames.tail.map(n => classNameToTree(n, fallback)).toList + ) + } + } + } + + // Only converts my.class.Name, not my.class.Name[my.TypeA, my.TypeB] + private def singleClassNameToTree(fqcn: String): Tree = { + val tokens = fqcn.split('.').filter(_.nonEmpty) + + val packageName = tokens.take(tokens.length - 1).toList + val pTermNames = packageName.map(TermName.apply) + + val className = tokens.last + val cTypeName = TypeName(className) + + if (tokens.length <= 1) Ident(cTypeName) + else { + val pRef = pTermNames.tail.foldLeft(Ident(pTermNames.head): RefTree) { + case (s, n) => Select(s, n) + } + Select(pRef, cTypeName) + } + } +} diff --git a/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/CanFreeze.scala b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/CanFreeze.scala new file mode 100644 index 00000000..f6daa895 --- /dev/null +++ b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/CanFreeze.scala @@ -0,0 +1,28 @@ +package org.scaladebugger.macros.freeze + +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType +import org.scaladebugger.macros.freeze.CanFreeze.ReturnType.ReturnType + +import scala.annotation.StaticAnnotation +import scala.language.experimental.macros + +/** + * Marks an internal method of a [[org.scaladebugger.macros.freeze.Freezable]] + * component as able to be frozen, which means it will be swapped with the + * result of accessing the live data when frozen. + * + * @param returnType Freeze-related information about the method's return type + * (defaults to normal freezing with no additional work) + */ +case class CanFreeze( + returnType: ReturnType = ReturnType.Normal +) extends StaticAnnotation + +object CanFreeze { + /** Return type of method being frozen. */ + object ReturnType extends Enumeration { + type ReturnType = Value + val Normal, FreezeObject, FreezeCollection, + FreezeOption, FreezeEither = Value + } +} diff --git a/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/CannotFreeze.scala b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/CannotFreeze.scala new file mode 100644 index 00000000..1de7fadb --- /dev/null +++ b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/CannotFreeze.scala @@ -0,0 +1,10 @@ +package org.scaladebugger.macros.freeze +import scala.language.experimental.macros +import scala.annotation.StaticAnnotation + +/** + * Marks an internal method of a [[org.scaladebugger.macros.freeze.Freezable]] + * component as unable to be frozen, which means it will be swapped with an + * exception when frozen. + */ +class CannotFreeze extends StaticAnnotation diff --git a/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/Freezable.scala b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/Freezable.scala new file mode 100644 index 00000000..cc958667 --- /dev/null +++ b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/Freezable.scala @@ -0,0 +1,26 @@ +package org.scaladebugger.macros.freeze + +import scala.language.experimental.macros +import scala.annotation.{StaticAnnotation, compileTimeOnly} + +/** + * Represents a freezable bit of information. + * + *

Injects a `freeze()` method along with a `Frozen` + * class implementing the trait.

+ * + *

Any internal method marked with + * [[org.scaladebugger.macros.freeze.CanFreeze]] will have its live value + * from an implementation stored at freeze time.

+ * + *

Any internal method marked with the + * [[org.scaladebugger.macros.freeze.CannotFreeze]] annotation will + * be marked to always throw an exception when accessed.

+ * + *

Any internal method not marked with either annotation will keep its + * current implementation from the trait itself.

+ */ +@compileTimeOnly("enable macro paradise to expand macro annotations") +class Freezable extends StaticAnnotation { + def macroTransform(annottees: Any*): Any = macro FreezableMacro.impl +} diff --git a/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/FreezableMacro.scala b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/FreezableMacro.scala new file mode 100644 index 00000000..031ae951 --- /dev/null +++ b/scala-debugger-macros/src/main/scala/org/scaladebugger/macros/freeze/FreezableMacro.scala @@ -0,0 +1,439 @@ +package org.scaladebugger.macros.freeze + +import scala.language.experimental.macros +import scala.reflect.macros.whitebox +import macrocompat.bundle +import org.scaladebugger.macros.MacroUtils + +@bundle class FreezableMacro(val c: whitebox.Context) { + import c.universe._ + + val InternalConstructorName = "$init$" + val FreezeMethodName = TermName("freeze") + val FrozenTraitName = TypeName("Frozen") + val FrozenClassName = TypeName("FrozenImpl") + + val M = new MacroUtils[c.type](c) + + // NOTE: Stuck with c.Expr[Any] instead of c.Tree for now; + // blows up in Scala 2.10 even with @bundle otherwise + def impl(annottees: c.Expr[Any]*): c.Expr[Any] = { + val id = java.util.UUID.randomUUID().toString + val (annottee, expandees) = annottees.map(_.tree) match { + case (classDef: ClassDef) :: (moduleDef: ModuleDef) :: Nil if M.isTrait(classDef) => + val results = processTraitAndObj(classDef, moduleDef) + (EmptyTree, results) + case (classDef: ClassDef) :: Nil if M.isTrait(classDef) => + val results = processTrait(classDef) + (EmptyTree, results) + case _ => + c.abort( + c.enclosingPosition, + "Freezable can only be placed on traits!" + ) + } + + val outputs = expandees + println(outputs) + //println(showRaw(q"val x: scala.util.Try[Seq[java.lang.String]]")) + //println(showRaw(q"val x: scala.util.Try[Seq[_]]")) + + c.Expr[Any]( + Block(outputs, Literal(Constant(()))) + ) + } + + private def processTrait(classDef: ClassDef): List[Tree] = { + val q""" + $mods trait $tpname[..$tparams] extends { + ..$earlydefns + } with ..$parents { $self => + ..$body + } + """ = classDef + + // Represents name of companion object + val traitTypeName: TypeName = tpname match { + case t: TypeName => t + } + + // Generate a new class containing the helper methods and + // object containing "Frozen" class representation + val moduleDef = M.newEmptyObject(traitTypeName.toString) + + processTraitAndObj(classDef, moduleDef) + } + + private def processTraitAndObj( + classDef: ClassDef, + moduleDef: ModuleDef + ): List[Tree] = { + val q""" + $mods trait $tpname[..$tparams] extends { + ..$earlydefns + } with ..$parents { $self => + ..$body + } + """ = classDef + + val parentTypes = M.extractTypes(parents.asInstanceOf[List[Tree]]).flatten + val inheritedMethods = M.extractInheritedMethods(parentTypes) + + val freezeTrees = generateTrees( + body match { case l: List[Tree] => l }, + tpname match { case t: TypeName => t }, + parentTypes, + inheritedMethods + ) + + // TODO: Inject freeze method directly into trait rather than + // relying on object implicit + val newClassDef: ClassDef = { + val bodyTrees: List[Tree] = body match { + case l: List[Tree] => l + } + + // TODO: Find better way to know when to mark as overridden + val o = TermName(tpname.toString()) + val freezeMethod = + if (inheritedMethods.exists(_.name.toString == FreezeMethodName.toString)) + q"override def $FreezeMethodName(): $tpname = $o.$FreezeMethodName(this)" + else + q"def $FreezeMethodName(): $tpname = $o.$FreezeMethodName(this)" + + val tree = q""" + $mods trait $tpname[..$tparams] extends { + ..$earlydefns + } with ..$parents { $self => + ..$bodyTrees + $freezeMethod + } + """ + + tree match { + case c: ClassDef => c + } + } + + val newModuleDef: ModuleDef = { + val q""" + $mods object $tname extends { + ..$earlydefns + } with ..$parents { $self => + ..$body + } + """ = moduleDef + + val newObjDef = q""" + $mods object $tname extends { + ..$earlydefns + } with ..$parents { $self => + ..$body + ..$freezeTrees + } + """ + + newObjDef match { case m: ModuleDef => m } + } + + List(newClassDef, newModuleDef) + } + + private def markModifiersOverride(modifiers: Modifiers): Modifiers = { + // TODO: Is there a way to filter out just the DEFERRED flag? + val newFlags = Flag.OVERRIDE | + (if (modifiers.hasFlag(Flag.FINAL)) Flag.FINAL else NoFlags) + + c.universe.Modifiers( + flags = newFlags, + privateWithin = modifiers.privateWithin, + annotations = modifiers.annotations.filterNot(a => { + M.isAnnotation(a, "CanFreeze") || + M.isAnnotation(a, "CannotFreeze") + }) + ) + } + + private def generateFreezeMethodBody( + r: org.scaladebugger.macros.freeze.CanFreeze.ReturnType.ReturnType, + freezeObjName: TermName, + methodName: TermName, + returnType: Tree + ): Tree = { + import org.scaladebugger.macros.freeze.CanFreeze.ReturnType + r match { + case ReturnType.Normal => + q"""scala.util.Try.apply($freezeObjName.$methodName) + .asInstanceOf[scala.util.Try[$returnType]]""" + case ReturnType.FreezeObject => + q"""scala.util.Try.apply($freezeObjName.$methodName).map(_.$FreezeMethodName()) + .asInstanceOf[scala.util.Try[$returnType]]""" + case ReturnType.FreezeCollection => + q"""scala.util.Try.apply($freezeObjName.$methodName).map(_.map(_.$FreezeMethodName())) + .asInstanceOf[scala.util.Try[$returnType]]""" + case ReturnType.FreezeOption => + q"""scala.util.Try.apply($freezeObjName.$methodName).map(_.map(_.$FreezeMethodName())) + .asInstanceOf[scala.util.Try[$returnType]]""" + case ReturnType.FreezeEither => + q""" + scala.util.Try.apply($freezeObjName.$methodName).map(r => r match { + case scala.util.Left(o) => scala.util.Left(o.$FreezeMethodName()) + case scala.util.Right(o) => scala.util.Right(o.$FreezeMethodName()) + }).asInstanceOf[scala.util.Try[$returnType]] + """ + } + } + + private def generateTrees( + body: List[Tree], + traitTypeName: TypeName, + parentTypes: List[Type], + inheritedMethods: List[MethodSymbol] + ): List[Tree] = { + val freezeObjName = TermName("valueToFreeze") + + val internalMethodTrees = body.collect { case d: DefDef => d } + val internalGroups = internalMethodTrees.filterNot { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val name = tname.toString() + name == InternalConstructorName.toString + }.groupBy(d => { + if (M.containsAnnotation(d, "CanFreeze")) "CanFreeze" + else if (M.containsAnnotation(d, "CannotFreeze")) "CannotFreeze" + else "Other" + }) + val freezableMethods = internalGroups.getOrElse("CanFreeze", Nil) + val unfreezableMethods = internalGroups.getOrElse("CannotFreeze", Nil) + val otherMethods = internalGroups.getOrElse("Other", Nil) + + // Validate that we don't have a method left behind without an implementation + otherMethods.foreach { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + if (expr.isEmpty) c.abort( + c.enclosingPosition, + s"$tname has no implementation and is not marked as @CanFreeze or @CannotFreeze!" + ) + } + + // TODO: Figure out why multiple of method symbol show up here, + // resulting in us using distinct + val filteredInheritedMethods = inheritedMethods.distinct.filterNot(m => { + m.name.decodedName.toString == InternalConstructorName.toString || + m.name.decodedName.toString == FreezeMethodName.toString || + internalMethodTrees.exists(t => + t.name.decodedName.toString == m.name.decodedName.toString && + t.vparamss.size == m.paramLists.size && + t.vparamss.zip(m.paramLists).forall { case (valDefList, paramList) => + valDefList.size == paramList.size && + valDefList.zip(paramList).forall { case (valDef, param) => + // TODO: Verify this actually works -- can we encounter + // a scenario where tpt is not the full name? + valDef.tpt.toString() == param.typeSignature.toString + } + } + ) + }) + val iFreezableMethods = filteredInheritedMethods.filter( + _.annotations.exists(_.tree.tpe =:= typeOf[CanFreeze]) + ) + val iOtherMethods = filteredInheritedMethods.filterNot( + _.annotations.map(_.tree.tpe).exists(t => + t =:= typeOf[CanFreeze] || t =:= typeOf[CannotFreeze]) + ) + + // Build up dependencies for frozen trait + val cDeps = freezableMethods.map { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val name = TermName(s"$$$tname") + q"protected val $name: scala.util.Try[$tpt]" + } ++ iFreezableMethods.map(m => { + val name = TermName(s"$$${m.name.decodedName.toString}") + + val retTypeStr = m.returnType.toString + val tpt = M.classNameToTree(retTypeStr, Some(traitTypeName.toString)) + + println("FOR METHOD: " + name + " TYPE IS " + tpt) + q"protected val $name: scala.util.Try[$tpt]" + }) ++ otherMethods.flatMap { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val name = TermName(s"$$$tname") + val m = mods match { case mm: Modifiers => mm } + + if (paramss.nonEmpty) None + else if (m.hasFlag(Flag.OVERRIDE)) + Some(q"override protected val $name: scala.util.Try[$tpt] = scala.util.Try.apply(this.$tname)") + else + Some(q"protected val $name: scala.util.Try[$tpt] = scala.util.Try.apply(this.$tname)") + } ++ iOtherMethods.flatMap(m => { + val mname = TermName(m.name.decodedName.toString) + val name = TermName(s"$$${m.name.decodedName.toString}") + + val retTypeStr = m.returnType.toString + val tpt = M.classNameToTree(retTypeStr, Some(traitTypeName.toString)) + + if (m.paramLists.nonEmpty) None + else Some(q"override protected val $name: scala.util.Try[$tpt] = scala.util.Try.apply(this.$mname)") + + }) + + // Build up constructor parameters for frozen class + val cParams = freezableMethods.map { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val name = TermName(s"$$$tname") + q"protected val $name: scala.util.Try[$tpt]" + } ++ iFreezableMethods.map(m => { + val name = TermName(s"$$${m.name.decodedName.toString}") + + val retTypeStr = m.returnType.toString + val tpt = M.classNameToTree(retTypeStr, Some(traitTypeName.toString)) + + q"protected val $name: scala.util.Try[$tpt]" + }) + + // TODO: Remove runtime checks by getting better understanding of + // return type, if it contains a freeze method, etc. + // + // Build up constructor arguments for freeze method instantiating + // the frozen class + val cArgs = freezableMethods.map { d => + val q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" = d + + // TODO: Find better way to match against the input + val r = M.findAnnotation(d, "CanFreeze").get match { + case q"new CanFreeze()" => + import org.scaladebugger.macros.freeze.CanFreeze.ReturnType + ReturnType.Normal + case q"new CanFreeze($returnType)" => + import org.scaladebugger.macros.freeze.CanFreeze.ReturnType + ReturnType.withName(returnType.toString().split('.').last) + } + + generateFreezeMethodBody(r, freezeObjName, tname, tpt) + } ++ iFreezableMethods.map(m => { + val tname = TermName(m.name.decodedName.toString) + + val retTypeStr = m.returnType.toString + val tpt = M.classNameToTree(retTypeStr, Some(traitTypeName.toString)) + + val a = m.annotations.find(_.tree.tpe =:= typeOf[CanFreeze]) + if (a.isEmpty) c.abort( + c.enclosingPosition, + s"Invalid state while processing inherited method ${m.name}" + ) + + // TODO: Figure out better way to handle this than some random hack + import org.scaladebugger.macros.freeze.CanFreeze.ReturnType + val fullArgs = a.get.tree.children.map(_.toString()) + val r = scala.util.Try( + ReturnType.withName(fullArgs.last.split('.').last) + ).getOrElse(ReturnType.Normal) + + generateFreezeMethodBody(r, freezeObjName, tname, tpt) + }) + + val newFreezableMethods = freezableMethods.map { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val name = TermName(s"$$$tname") + + if (paramss.nonEmpty) c.abort( + c.enclosingPosition, + s"Unable to freeze $tname! Must have zero arguments!" + ) + + val newMods: Modifiers = markModifiersOverride(mods match { + case m: Modifiers => m + }) + + q"$newMods def $tname[..$tparams](...$paramss): $tpt = this.$name.get" + } + + val newUnfreezableMethods = unfreezableMethods.map { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val newMods: Modifiers = markModifiersOverride(mods match { + case m: Modifiers => m + }) + + q"""$newMods def $tname[..$tparams](...$paramss): $tpt = + throw new IllegalStateException("Method not frozen!")""" + } + + val newSuperMethods = otherMethods.map { + case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => + val newMods: Modifiers = markModifiersOverride(mods match { + case m: Modifiers => m + }) + + q"""$newMods def $tname[..$tparams](...$paramss): $tpt = + super.$tname[..$tparams](...$paramss)""" + } + + val newInheritedMethods = filteredInheritedMethods.flatMap(m => { + val methodName = m.name.decodedName.toString + val mods = m.annotations.map(_.tree) + val tname = TermName(methodName) + val tparams = m.typeParams.map(tp => TypeName(tp.fullName)) + val paramss = m.paramLists.map(_.map(p => { + val name = TermName(p.name.decodedName.toString) + val typeName = TypeName(p.typeSignature.toString) + + // TODO: Support default values? + // TODO: Support annotations? + q"val $name: $typeName" + })) + + val retTypeStr = m.returnType.toString + val tpt = M.classNameToTree(retTypeStr, Some(traitTypeName.toString)) + + val aFreezable = m.annotations.find(_.tree.tpe =:= typeOf[CanFreeze]) + val aUnfreezable = m.annotations.find(_.tree.tpe =:= typeOf[CannotFreeze]) + + val hasAnnotation = aFreezable.nonEmpty || aUnfreezable.nonEmpty + if (hasAnnotation && m.isFinal) c.abort( + c.enclosingPosition, + s"Inherited $methodName is marked as final and cannot be overridden!" + ) + if (!hasAnnotation && m.isAbstract) c.abort( + c.enclosingPosition, + Seq( + s"Inherited $methodName has no implementation and", + "is not marked as @CanFreeze or @CannotFreeze!" + ).mkString(" ") + ) + + val methodTree = if (aFreezable.nonEmpty) { + val name = TermName(s"$$$tname") + q"override def $tname[..$tparams](...$paramss): $tpt = this.$name.get" + } else if (aUnfreezable.nonEmpty) { + q"""override def $tname[..$tparams](...$paramss): $tpt = + throw new IllegalStateException("Method not frozen!")""" + } else { + null + } + + Option(methodTree) + }) + + val parents = parentTypes + .map(_.typeSymbol.fullName + "." + FrozenTraitName) + .map(M.classNameToTree) + + val interface = q""" + trait $FrozenTraitName extends $traitTypeName with ..$parents { + ..$cDeps + ..$newFreezableMethods + ..$newUnfreezableMethods + } + """ + + val klass = q""" + final class $FrozenClassName private[$traitTypeName](..$cParams) extends $FrozenTraitName + """ + + val method = q""" + def $FreezeMethodName($freezeObjName: $traitTypeName): $traitTypeName = new $FrozenClassName(..$cArgs) + """ + + List(interface, klass, method) + } +} + diff --git a/scala-debugger-macros/src/test/scala/org/scaladebugger/macros/freeze/FreezableSpec.scala b/scala-debugger-macros/src/test/scala/org/scaladebugger/macros/freeze/FreezableSpec.scala new file mode 100644 index 00000000..09200040 --- /dev/null +++ b/scala-debugger-macros/src/test/scala/org/scaladebugger/macros/freeze/FreezableSpec.scala @@ -0,0 +1,23 @@ +package org.scaladebugger.macros.freeze + +import org.scalatest.{FunSpec, Matchers} + +class FreezableSpec extends FunSpec with Matchers { + describe("@Freezable") { + it("should generate a companion object with a freeze method") { + fail() + } + + it("should modify an existing companion object with a freeze method") { + fail() + } + + it("should convert @CannotFreeze methods to throw an exception") { + fail() + } + + it("should convert @CanFreeze(Normal) methods to store ") { + fail() + } + } +}