Permalink
Browse files

Initial commit of a Scala debugger.

It is enable using a checkbox in the preferences.
It replaces the Java based debug elements by the Scala ones.
Only terminate and improved step over actions are supported right now.
None of the labels are fine.
There are likely a lot of bug and other missing things.
I'll create some doc about it on the website.
  • Loading branch information...
1 parent 09fd528 commit 811096357aee7f542e3f2243e5266fac2451fbef @skyluc skyluc committed Mar 5, 2012
Showing with 2,231 additions and 9 deletions.
  1. +2 −0 org.scala-ide.build/pom.xml
  2. +2 −0 org.scala-ide.sdt.core.tests/META-INF/MANIFEST.MF
  3. +4 −4 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/testsetup/SDTTestUtils.scala
  4. +7 −4 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/testsetup/TestProjectSetup.scala
  5. +5 −1 org.scala-ide.sdt.core/META-INF/MANIFEST.MF
  6. +10 −0 org.scala-ide.sdt.debug.tests/.classpath
  7. +2 −0 org.scala-ide.sdt.debug.tests/.gitignore
  8. +29 −0 org.scala-ide.sdt.debug.tests/.project
  9. +11 −0 org.scala-ide.sdt.debug.tests/META-INF/MANIFEST.MF
  10. +6 −0 org.scala-ide.sdt.debug.tests/build.properties
  11. +160 −0 org.scala-ide.sdt.debug.tests/pom.xml
  12. +319 −0 org.scala-ide.sdt.debug.tests/src/scala/tools/eclipse/debug/ScalaDebugSteppingTest.scala
  13. +148 −0 org.scala-ide.sdt.debug.tests/src/scala/tools/eclipse/debug/ScalaDebugTestSession.scala
  14. +7 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/.classpath
  15. +18 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/.project
  16. +12 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/.settings/org.eclipse.jdt.core.prefs
  17. +12 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/AnonFunOnListString.launch
  18. +12 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListInt.launch
  19. +12 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListObject.launch
  20. +12 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListString.launch
  21. +12 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListString2.launch
  22. +23 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/AnonFunOnListString.scala
  23. +43 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListInt.scala
  24. +41 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListObject.scala
  25. +41 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListString.scala
  26. +21 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListString2.scala
  27. +9 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/Helper.scala
  28. +21 −0 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/Variables.scala
  29. +10 −0 org.scala-ide.sdt.debug/.classpath
  30. +2 −0 org.scala-ide.sdt.debug/.gitignore
  31. +29 −0 org.scala-ide.sdt.debug/.project
  32. +71 −0 org.scala-ide.sdt.debug/META-INF/MANIFEST.MF
  33. +5 −0 org.scala-ide.sdt.debug/build.properties
  34. +32 −0 org.scala-ide.sdt.debug/plugin.xml
  35. +112 −0 org.scala-ide.sdt.debug/pom.xml
  36. +38 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/DebugPreferencePage.scala
  37. +23 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/JDIUtil.scala
  38. +27 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/ScalaDebugPlugin.scala
  39. +85 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/ScalaDebugger.scala
  40. +35 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/ScalaSourceLocator.scala
  41. +15 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/command/ScalaStep.scala
  42. +164 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/command/ScalaStepOver.scala
  43. +36 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaDebugElement.scala
  44. +82 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaDebugModelPresentation.scala
  45. +173 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaDebugTarget.scala
  46. +55 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaStackFrame.scala
  47. +78 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaThread.scala
  48. +96 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaValue.scala
  49. +48 −0 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaVariable.scala
  50. +7 −0 org.scala-ide.sdt.feature/feature.xml
  51. +7 −0 org.scala-ide.sdt.source.feature/feature.xml
View
2 org.scala-ide.build/pom.xml
@@ -31,6 +31,8 @@
<module>../org.scala-ide.sdt.aspects</module>
<module>../org.scala-ide.sdt.core</module>
<module>../org.scala-ide.sdt.core.tests</module>
+ <module>../org.scala-ide.sdt.debug</module>
+ <module>../org.scala-ide.sdt.debug.tests</module>
<module>../org.scala-ide.sdt.feature</module>
<module>../org.scala-ide.sdt.source.feature</module>
<module>../org.scala-ide.sdt.update-site</module>
View
2 org.scala-ide.sdt.core.tests/META-INF/MANIFEST.MF
@@ -10,3 +10,5 @@ Bundle-ClassPath: .,
lib/mockito-all-1.8.5.jar
Require-Bundle: org.scala-ide.scala.library,
org.eclipse.equinox.weaving.aspectj
+Export-Package:
+ scala.tools.eclipse.testsetup
View
8 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/testsetup/SDTTestUtils.scala
@@ -26,8 +26,8 @@ import org.eclipse.jdt.core.IClasspathEntry
*/
object SDTTestUtils {
- lazy val sourceWorkspaceLoc = {
- val bundle = Platform.getBundle("org.scala-ide.sdt.core.tests")
+ def sourceWorkspaceLoc(bundleName: String) = {
+ val bundle = Platform.getBundle(bundleName) //"org.scala-ide.sdt.core.tests"
OSGiUtils.pathInBundle(bundle, File.separatorChar + "test-workspace").get
}
@@ -50,10 +50,10 @@ object SDTTestUtils {
/** Setup the project in the target workspace. The 'name' project should
* exist in the source workspace.
*/
- def setupProject(name: String): ScalaProject = {
+ def setupProject(name: String, bundleName: String): ScalaProject = {
EclipseUtils.workspaceRunnableIn(workspace) { monitor =>
val wspaceLoc = workspace.getRoot.getLocation
- val src = new File(sourceWorkspaceLoc.toFile().getAbsolutePath + File.separatorChar + name)
+ val src = new File(sourceWorkspaceLoc(bundleName).toFile().getAbsolutePath + File.separatorChar + name)
val dst = new File(wspaceLoc.toFile().getAbsolutePath + File.separatorChar + name)
println("copying %s to %s".format(src, dst))
FileUtils.copyDirectory(src, dst)
View
11 org.scala-ide.sdt.core.tests/src/scala/tools/eclipse/testsetup/TestProjectSetup.scala
@@ -11,9 +11,8 @@ import scala.tools.eclipse.javaelements.ScalaCompilationUnit
import org.eclipse.jdt.core.IProblemRequestor
import org.eclipse.jdt.core.WorkingCopyOwner
import org.eclipse.core.runtime.NullProgressMonitor
-
-
import org.mockito.Mockito.{mock, when}
+import org.eclipse.core.resources.IFile
/** Base class for setting up tests that depend on a project found in the test-workspace.
*
@@ -27,11 +26,11 @@ import org.mockito.Mockito.{mock, when}
* Example: `object HyperlinkDetectorTests extends TestProjectSetup("hyperlinks")'
*
*/
-class TestProjectSetup(projectName: String, srcRoot: String = "/%s/src/") extends ProjectBuilder {
+class TestProjectSetup(projectName: String, srcRoot: String = "/%s/src/", bundleName: String = "org.scala-ide.sdt.core.tests") extends ProjectBuilder {
type ScalaUnit = ScalaCompilationUnit with ICompilationUnit
/** The ScalaProject corresponding to projectName, after copying to the test workspace. */
- lazy val project: ScalaProject = SDTTestUtils.setupProject(projectName)
+ lazy val project: ScalaProject = SDTTestUtils.setupProject(projectName, bundleName)
/** The package root corresponding to /src inside the project. */
lazy val srcPackageRoot: IPackageFragmentRoot = {
@@ -45,6 +44,10 @@ class TestProjectSetup(projectName: String, srcRoot: String = "/%s/src/") extend
srcPackageRoot.open(null)
+ def file(path: String): IFile = {
+ project.underlying.getFile(path)
+ }
+
/** Return the compilation unit corresponding to the given path, relative to the src folder.
* for example: "scala/collection/Map.scala"
*/
View
6 org.scala-ide.sdt.core/META-INF/MANIFEST.MF
@@ -87,9 +87,13 @@ Export-Package:
scala.tools.eclipse.templates,
scala.tools.eclipse.ui,
scala.tools.eclipse.util,
- scala.tools.eclipse.wizards
+ scala.tools.eclipse.wizards,
+ scala.tools.eclipse.logging
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-ClassPath: .,
lib/miglayout-3.7.4.jar,
lib/log4j-1.2.16.jar
+Eclipse-ExtensibleAPI:
+ true
+
View
10 org.scala-ide.sdt.debug.tests/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
+ <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
View
2 org.scala-ide.sdt.debug.tests/.gitignore
@@ -0,0 +1,2 @@
+.cache
+target
View
29 org.scala-ide.sdt.debug.tests/.project
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.scala-ide.sdt.debug.tests</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.scala-ide.sdt.core.scalabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.scala-ide.sdt.core.scalanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
11 org.scala-ide.sdt.debug.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Scala Plugin (Test)
+Bundle-SymbolicName: org.scala-ide.sdt.debug.tests
+Bundle-Version: 2.1.0.qualifier
+Bundle-Vendor: scala-ide.org
+Fragment-Host: org.scala-ide.sdt.debug
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Require-Bundle: org.scala-ide.scala.library,
+ org.eclipse.equinox.weaving.aspectj,
+ org.scala-ide.sdt.core
View
6 org.scala-ide.sdt.debug.tests/build.properties
@@ -0,0 +1,6 @@
+source.. = src/
+output.. = target/classes/
+bin.includes = META-INF/,\
+ test-workspace/,\
+ lib/,\
+ .
View
160 org.scala-ide.sdt.debug.tests/pom.xml
@@ -0,0 +1,160 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.scala-ide</groupId>
+ <artifactId>scala-ide-for-eclipse</artifactId>
+ <version>2.1.0-SNAPSHOT</version>
+ <relativePath>../org.scala-ide.build/pom.xml</relativePath>
+ </parent>
+ <artifactId>org.scala-ide.sdt.debug.tests</artifactId>
+ <packaging>eclipse-test-plugin</packaging>
+
+ <profiles>
+ <profile>
+ <id>Mac OS</id>
+ <activation>
+ <os>
+ <family>mac</family>
+ </os>
+ </activation>
+ <properties>
+ <tycho.test.OSspecific>-Dosgi.ws=cocoa -XstartOnFirstThread</tycho.test.OSspecific>
+ </properties>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-surefire-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <dependencies>
+ <dependency>
+ <artifactId>org.eclipse.jdt.launching.macosx</artifactId>
+ <type>eclipse-plugin</type>
+ </dependency>
+
+ </dependencies>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <profile>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <properties>
+ <tycho.test.OSspecific></tycho.test.OSspecific>
+ </properties>
+ </profile>
+
+ </profiles>
+
+ <properties>
+ <!-- Partial workaround against JDT Weaving deadlocks. See #1000317 and the original ticket on https://issuetracker.springsource.com/browse/STS-1445 -->
+ <tycho.test.weaving>-XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass -Dosgi.classloader.lock=classname</tycho.test.weaving>
+ <tycho.test.jvmArgs>-Xmx800m -XX:MaxPermSize=256m -Dsdtcore.headless ${tycho.test.weaving} ${tycho.test.OSspecific}</tycho.test.jvmArgs>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.8.5</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.scala-ide</groupId>
+ <artifactId>org.scala-ide.sdt.core.tests</artifactId>
+ <version>${version}</version>
+ <type>eclipse-plugin</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.scala-tools</groupId>
+ <artifactId>maven-scala-plugin</artifactId>
+ <version>2.15.0</version>
+ <executions>
+ <execution>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <jvmArgs>
+ <jvmArg>-Xms512m</jvmArg>
+ <jvmArg>-Xmx1024m</jvmArg>
+ </jvmArgs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-surefire-plugin</artifactId>
+ <version>${tycho.version}</version>
+ <configuration>
+ <useUIHarness>false</useUIHarness>
+ <useUIThread>false</useUIThread>
+
+ <!-- Enable JDT weaving -->
+ <systemProperties combine.children="append">
+ <aj.weaving.verbose>true</aj.weaving.verbose>
+ <org.aspectj.weaver.showWeaveInfo>true</org.aspectj.weaver.showWeaveInfo>
+ <org.aspectj.osgi.verbose>true</org.aspectj.osgi.verbose>
+ </systemProperties>
+ <frameworkExtensions>
+ <frameworkExtension>
+ <groupId>p2.osgi.bundle</groupId>
+ <artifactId>org.eclipse.equinox.weaving.hook</artifactId>
+ <version>1.0.100.v20110502</version>
+ </frameworkExtension>
+ <frameworkExtension>
+ <groupId>org.scala-ide</groupId>
+ <artifactId>org.scala-ide.sdt.core.tests</artifactId>
+ <version>${version}</version>
+ <type>eclipse-plugin</type>
+ </frameworkExtension>
+ </frameworkExtensions>
+ <bundleStartLevel>
+ <bundle>
+ <id>org.eclipse.equinox.weaving.aspectj</id>
+ <level>2</level>
+ <autoStart>true</autoStart>
+ </bundle>
+ </bundleStartLevel>
+ <!-- <includes> -->
+ <!-- <include>scala/tools/eclipse/*Test.class</include> -->
+ <!-- </includes> -->
+ <argLine>${tycho.test.jvmArgs}</argLine>
+ <testSuite>${project.artifactId}</testSuite>
+ <testClass>scala.tools.eclipse.debug.ScalaDebugSteppingTest</testClass>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho.version}</version>
+ <configuration>
+ <resolver>p2</resolver>
+ <pomDependencies>consider</pomDependencies>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+</project>
+
View
319 org.scala-ide.sdt.debug.tests/src/scala/tools/eclipse/debug/ScalaDebugSteppingTest.scala
@@ -0,0 +1,319 @@
+package scala.tools.eclipse.debug
+
+import scala.tools.eclipse.testsetup.TestProjectSetup
+import org.junit.{Test, Before, After}
+import org.eclipse.core.resources.IncrementalProjectBuilder
+import org.eclipse.core.runtime.NullProgressMonitor
+
+object ScalaDebugSteppingTest extends TestProjectSetup("debug", bundleName= "org.scala-ide.sdt.debug.tests") {
+
+ val TYPENAME_FC_LS = "stepping.ForComprehensionListString"
+ val TYPENAME_FC_LS2 = "stepping.ForComprehensionListString2"
+ val TYPENAME_FC_LO = "stepping.ForComprehensionListObject"
+ val TYPENAME_FC_LI = "stepping.ForComprehensionListInt"
+ val TYPENAME_AF_LS = "stepping.AnonFunOnListString"
+
+}
+
+class ScalaDebugSteppingTest {
+
+ import ScalaDebugSteppingTest._
+
+ var session: ScalaDebugTestSession = null
+
+ @Before
+ def setScalaDebugMode() {
+ ScalaDebugPlugin.plugin.getPreferenceStore.setValue(DebugPreferencePage.P_ENABLE, true)
+ }
+
+ @Before
+ def refreshBinaryFiles() {
+ project.underlying.build(IncrementalProjectBuilder.CLEAN_BUILD, new NullProgressMonitor)
+ project.underlying.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new NullProgressMonitor)
+ }
+
+ @After
+ def cleanDebugSession() {
+ if (session ne null) {
+ session.terminate()
+ session = null
+ }
+ }
+
+ /*
+ * Testing step over/in for comprehension through List[String]
+ */
+
+ @Test
+ def StepOverIntoForComprehensionListStringInObjectMain() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListString.launch"))
+
+ session.runToLine(TYPENAME_FC_LS + "$", 9)
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$", "main([Ljava/lang/String;)V", 9)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$$anonfun$main$1", "apply(Ljava/lang/String;)I", 10)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListStringInObjectFoo() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListString.launch"))
+
+ session.runToLine(TYPENAME_FC_LS + "$", 19)
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$", "foo(Lscala/collection/immutable/List;)V", 19)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$$anonfun$foo$1", "apply(Ljava/lang/String;)I", 20)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListStringInClassConstructor() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListString.launch"))
+
+ session.runToLine(TYPENAME_FC_LS, 29)
+
+ session.checkStackFrame(TYPENAME_FC_LS, "<init>(Lscala/collection/immutable/List;)V", 29)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$$anonfun$1", "apply(Ljava/lang/String;)I", 30)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListStringInClassBar() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListString.launch"))
+
+ session.runToLine(TYPENAME_FC_LS, 35)
+
+ session.checkStackFrame(TYPENAME_FC_LS, "bar()V", 35)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$$anonfun$bar$1", "apply(Ljava/lang/String;)I", 36)
+ }
+
+ /*
+ * Testing step over/back in for comprehension through List[String]
+ */
+
+ @Test
+ def StepOverBackInForComprehentionListString() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListString.launch"))
+
+ session.runToLine(TYPENAME_FC_LS + "$", 10)
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$$anonfun$main$1", "apply(Ljava/lang/String;)I", 10)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LS + "$$anonfun$main$1", "apply(Ljava/lang/String;)I", 10)
+ }
+
+ /*
+ * Testing step over/out for comprehension through List[String]
+ */
+
+ @Test
+ def StepOverOutForComprehentionListString() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListString2.launch"))
+
+ session.runToLine(TYPENAME_FC_LS2 + "$", 12)
+
+ session.checkStackFrame(TYPENAME_FC_LS2 + "$$anonfun$main$1", "apply(Ljava/lang/String;)I", 12)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LS2 + "$", "main([Ljava/lang/String;)V", 15)
+ }
+
+ /*
+ * Testing step over/in for comprehension through List[Object]
+ */
+
+ @Test
+ def StepOverIntoForComprehensionListObjectInObjectMain() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListObject.launch"))
+
+ session.runToLine(TYPENAME_FC_LO + "$", 9)
+
+ session.checkStackFrame(TYPENAME_FC_LO + "$", "main([Ljava/lang/String;)V", 9)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LO + "$$anonfun$main$1", "apply(Ljava/lang/Object;)Ljava/lang/Object;", 10)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListObjectInObjectFoo() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListObject.launch"))
+
+ session.runToLine(TYPENAME_FC_LO + "$", 19)
+
+ session.checkStackFrame(TYPENAME_FC_LO + "$", "foo(Lscala/collection/immutable/List;)V", 19)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LO + "$$anonfun$foo$1", "apply(Ljava/lang/Object;)Ljava/lang/Object;", 20)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListObjectInClassConstructor() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListObject.launch"))
+
+ session.runToLine(TYPENAME_FC_LO, 29)
+
+ session.checkStackFrame(TYPENAME_FC_LO, "<init>(Lscala/collection/immutable/List;)V", 29)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LO + "$$anonfun$1", "apply(Ljava/lang/Object;)Ljava/lang/Object;", 30)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListObjectInClassBar() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListObject.launch"))
+
+ session.runToLine(TYPENAME_FC_LO, 35)
+
+ session.checkStackFrame(TYPENAME_FC_LO, "bar()V", 35)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LO + "$$anonfun$bar$1", "apply(Ljava/lang/Object;)Ljava/lang/Object;", 36)
+ }
+
+ /*
+ * Testing step over/in for comprehension through List[Int]
+ */
+
+ @Test
+ def StepOverIntoForComprehensionListIntInObjectMain() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListInt.launch"))
+
+ session.runToLine(TYPENAME_FC_LI + "$", 11)
+
+ session.checkStackFrame(TYPENAME_FC_LI + "$", "main([Ljava/lang/String;)V", 11)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LI + "$$anonfun$main$1", "apply$mcVI$sp(I)V", 12)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListIntInObjectFoo() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListInt.launch"))
+
+ session.runToLine(TYPENAME_FC_LI + "$", 21)
+
+ session.checkStackFrame(TYPENAME_FC_LI + "$", "foo(Lscala/collection/immutable/List;)V", 21)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LI + "$$anonfun$foo$1", "apply$mcVI$sp(I)V", 22)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListIntInClassConstructor() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListInt.launch"))
+
+ session.runToLine(TYPENAME_FC_LI, 31)
+
+ session.checkStackFrame(TYPENAME_FC_LI, "<init>(Lscala/collection/immutable/List;)V", 31)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LI + "$$anonfun$1", "apply$mcVI$sp(I)V", 32)
+ }
+
+ @Test
+ def StepOverIntoForComprehensionListIntInClassBar() {
+
+ session = new ScalaDebugTestSession(file("ForComprehensionListInt.launch"))
+
+ session.runToLine(TYPENAME_FC_LI, 37)
+
+ session.checkStackFrame(TYPENAME_FC_LI, "bar()V", 37)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_FC_LI + "$$anonfun$bar$1", "apply$mcVI$sp(I)V", 38)
+ }
+
+ /*
+ * Testing step over/in List[String] methods
+ */
+
+ @Test
+ def StepOverIntoListStringForEach() {
+
+ session = new ScalaDebugTestSession(file("AnonFunOnListString.launch"))
+
+ session.runToLine(TYPENAME_AF_LS + "$", 11)
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$", "main([Ljava/lang/String;)V", 11)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$$anonfun$main$1", "apply(Ljava/lang/String;)V", 11)
+ }
+
+ @Test
+ def StepOverIntoListStringFind() {
+
+ session = new ScalaDebugTestSession(file("AnonFunOnListString.launch"))
+
+ session.runToLine(TYPENAME_AF_LS + "$", 13)
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$", "main([Ljava/lang/String;)V", 13)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$$anonfun$main$2", "apply(Ljava/lang/String;)Z", 13)
+ }
+
+ @Test
+ def StepOverIntoListStringMap() {
+
+ session = new ScalaDebugTestSession(file("AnonFunOnListString.launch"))
+
+ session.runToLine(TYPENAME_AF_LS + "$", 15)
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$", "main([Ljava/lang/String;)V", 15)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$$anonfun$main$3", "apply(Ljava/lang/String;)I", 15)
+ }
+
+ @Test
+ def StepOverIntoListStringFoldLeft() {
+
+ session = new ScalaDebugTestSession(file("AnonFunOnListString.launch"))
+
+ session.runToLine(TYPENAME_AF_LS + "$", 17)
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$", "main([Ljava/lang/String;)V", 17)
+
+ session.stepOver()
+
+ session.checkStackFrame(TYPENAME_AF_LS + "$$anonfun$main$4", "apply(ILjava/lang/String;)I", 17)
+ }
+
+}
View
148 org.scala-ide.sdt.debug.tests/src/scala/tools/eclipse/debug/ScalaDebugTestSession.scala
@@ -0,0 +1,148 @@
+package scala.tools.eclipse.debug
+
+import scala.tools.eclipse.debug.model.{ScalaThread, ScalaStackFrame, ScalaDebugTarget}
+
+import org.eclipse.core.resources.{ResourcesPlugin, IFile}
+import org.eclipse.debug.core.{ILaunchManager, IDebugEventSetListener, DebugPlugin, DebugEvent}
+import org.eclipse.jdt.debug.core.JDIDebugModel
+import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget
+import org.hamcrest.CoreMatchers._
+import org.junit.Assert._
+
+class ScalaDebugTestSession(launchConfigurationFile: IFile) extends IDebugEventSetListener {
+
+ object State extends Enumeration {
+ type State = Value
+ val NOT_LAUNCHED, RUNNING, SUSPENDED, TERMINATED = Value
+ }
+ import State._
+
+ // from IDebugEventSetListener
+
+ DebugPlugin.getDefault.addDebugEventListener(this)
+
+ def handleDebugEvents(events: Array[DebugEvent]) {
+ events.foreach(event =>
+ event.getKind match {
+ case DebugEvent.CREATE =>
+ event.getSource match {
+ case target: ScalaDebugTarget =>
+ setLaunched(target)
+ case _ =>
+ }
+ case DebugEvent.RESUME =>
+ setRunning
+ case DebugEvent.SUSPEND =>
+ event.getSource match {
+ case thread: ScalaThread =>
+ setSuspended(thread.getTopStackFrame.asInstanceOf[ScalaStackFrame])
+ case target: ScalaDebugTarget =>
+ setSuspended(null)
+ case _ =>
+ }
+ case DebugEvent.TERMINATE =>
+ event.getSource match {
+ case target: ScalaDebugTarget =>
+ setTerminated
+ case _ =>
+ }
+ case _ =>
+ })
+ }
+
+ // ----
+
+ def setLaunched(target: ScalaDebugTarget) {
+ this.synchronized {
+ debugTarget = target
+ setRunning
+ }
+ }
+
+ def setRunning() {
+ this.synchronized {
+ state = RUNNING
+ currentStackFrame = null
+ }
+ }
+
+ def setSuspended(stackFrame: ScalaStackFrame) {
+ this.synchronized {
+ currentStackFrame = stackFrame
+ state = SUSPENDED
+ this.notify
+ }
+ }
+
+ def setTerminated() {
+ this.synchronized {
+ state = TERMINATED
+ debugTarget = null
+ this.notify
+ }
+ }
+
+ def waitUntilSuspended() {
+ this.synchronized {
+ if (state != SUSPENDED && state != TERMINATED)
+ this.wait
+ }
+ }
+
+ // ----
+
+ var state = NOT_LAUNCHED
+ var debugTarget: ScalaDebugTarget = null
+ var currentStackFrame: ScalaStackFrame = null
+
+ def runToLine(typeName: String, breakpointLine: Int) {
+ assertThat("Bad state before runToBreakpoint", state, anyOf(is(NOT_LAUNCHED), is(SUSPENDED)))
+
+ val breakpoint = JDIDebugModel.createLineBreakpoint(ResourcesPlugin.getWorkspace.getRoot, typeName, breakpointLine, -1, -1, -1, true, null)
+
+ if (state eq NOT_LAUNCHED) {
+ launch()
+ } else {
+ currentStackFrame.resume
+ }
+
+ waitUntilSuspended
+ breakpoint.delete
+
+ assertEquals("Bad state after runToBreakpoint", SUSPENDED, state)
+ }
+
+ def stepOver() {
+ assertEquals("Bad state before stepOver", SUSPENDED, state)
+
+ currentStackFrame.stepOver
+
+ waitUntilSuspended
+
+ assertEquals("Bad state after stepOver", SUSPENDED, state)
+ }
+
+ def terminate() {
+ if ((state ne NOT_LAUNCHED) && (state ne TERMINATED)) {
+ debugTarget.terminate
+ waitUntilSuspended
+ assertEquals("Bad state after terminate", TERMINATED, state)
+ }
+ }
+
+ private def launch() {
+ val launchConfiguration = DebugPlugin.getDefault.getLaunchManager.getLaunchConfiguration(launchConfigurationFile)
+ launchConfiguration.launch(ILaunchManager.DEBUG_MODE, null).getDebugTarget.asInstanceOf[JDIDebugTarget]
+ }
+
+ // -----
+
+ def checkStackFrame(typeName: String, methodFullSignature: String, line: Int) {
+ assertEquals("Bad state before checkStackFrame", SUSPENDED, state)
+
+ assertEquals("Wrong typeName", typeName, currentStackFrame.stackFrame.location.declaringType.name)
+ assertEquals("Wrong method", methodFullSignature, currentStackFrame.stackFrame.location.method.name + currentStackFrame.stackFrame.location.method.signature)
+ assertEquals("Wrong line", line, currentStackFrame.getLineNumber)
+ }
+
+}
View
7 org.scala-ide.sdt.debug.tests/test-workspace/debug/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
View
18 org.scala-ide.sdt.debug.tests/test-workspace/debug/.project
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>debug</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.scala-ide.sdt.core.scalabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.scala-ide.sdt.core.scalanature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
12 org.scala-ide.sdt.debug.tests/test-workspace/debug/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Fri Jul 08 15:43:04 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
View
12 org.scala-ide.sdt.debug.tests/test-workspace/debug/AnonFunOnListString.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="scala.application">
+<stringAttribute key="bad_container_name" value="/debug/f"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/debug/src/stepping/AnonFunOnListString.scala"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="stepping.AnonFunOnListString"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="debug"/>
+</launchConfiguration>
View
12 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListInt.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="scala.application">
+<stringAttribute key="bad_container_name" value="/debug/f"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/debug/src/stepping/ForComprehensionListInt.scala"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="stepping.ForComprehensionListInt"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="debug"/>
+</launchConfiguration>
View
12 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListObject.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="scala.application">
+<stringAttribute key="bad_container_name" value="/debug/f"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/debug/src/stepping/ForComprehensionListObject.scala"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="stepping.ForComprehensionListObject"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="debug"/>
+</launchConfiguration>
View
12 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListString.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="scala.application">
+<stringAttribute key="bad_container_name" value="/debug/f"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/debug/src/stepping/ForComprehensionListString.scala"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="stepping.ForComprehensionListString"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="debug"/>
+</launchConfiguration>
View
12 org.scala-ide.sdt.debug.tests/test-workspace/debug/ForComprehensionListString2.launch
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="scala.application">
+<stringAttribute key="bad_container_name" value="/debug/f"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/debug/src/stepping/ForComprehensionListString2.scala"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="stepping.ForComprehensionListString2"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="debug"/>
+</launchConfiguration>
View
23 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/AnonFunOnListString.scala
@@ -0,0 +1,23 @@
+package stepping
+
+import Helper._
+
+object AnonFunOnListString {
+
+ def main(args: Array[String]) {
+
+ val l = List("un", "deux", "quatre", "huit")
+
+ l.foreach(noop(_))
+
+ l.find(_.isEmpty)
+
+ l.map(_.size)
+
+ l.foldLeft(0)(_ + _.size)
+
+ }
+
+}
+
+class AnonFunOnListString {}
View
43 ...scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListInt.scala
@@ -0,0 +1,43 @@
+package stepping
+
+import Helper._
+
+object ForComprehensionListInt {
+
+ def main(args: Array[String]) {
+
+ val l = List(1, 2, 3, 4)
+
+ for (n <- l) {
+ noop(n)
+ }
+
+ foo(l)
+ new ForComprehensionListInt(l).bar
+ }
+
+ def foo(l: List[Int]) {
+
+ for (n <- l) {
+ noop(n)
+ }
+
+ }
+
+}
+
+class ForComprehensionListInt(l: List[Int]) {
+
+ for (n <- l) {
+ noop(n)
+ }
+
+ def bar() {
+
+ for (n <- l) {
+ noop(n)
+ }
+
+ }
+
+}
View
41 ...la-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListObject.scala
@@ -0,0 +1,41 @@
+package stepping
+
+object ForComprehensionListObject {
+
+ def main(args: Array[String]) {
+
+ val l= List(new Object(), "deux", "quatre", "huit")
+
+ for (n <- l) {
+ n
+ }
+
+ foo(l)
+ new ForComprehensionListObject(l).bar
+ }
+
+ def foo(l: List[Object]) {
+
+ for (n <- l) {
+ n
+ }
+
+ }
+
+}
+
+class ForComprehensionListObject (l: List[Object]) {
+
+ for (n <- l) {
+ n
+ }
+
+ def bar() {
+
+ for (n <- l) {
+ n
+ }
+
+ }
+
+}
View
41 ...la-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListString.scala
@@ -0,0 +1,41 @@
+package stepping
+
+object ForComprehensionListString {
+
+ def main(args: Array[String]) {
+
+ val l = List("un", "deux", "quatre", "huit")
+
+ for (n <- l) {
+ n.size
+ }
+
+ foo(l)
+ new ForComprehensionListString(l).bar
+ }
+
+ def foo(l: List[String]) {
+
+ for (n <- l) {
+ n.size
+ }
+
+ }
+
+}
+
+class ForComprehensionListString(l: List[String]) {
+
+ for (n <- l) {
+ n.size
+ }
+
+ def bar() {
+
+ for (n <- l) {
+ n.size
+ }
+
+ }
+
+}
View
21 ...a-ide.sdt.debug.tests/test-workspace/debug/src/stepping/ForComprehensionListString2.scala
@@ -0,0 +1,21 @@
+package stepping
+
+import Helper._
+
+object ForComprehensionListString2 {
+
+ def main(args: Array[String]) {
+
+ val l = List("un")
+
+ for (n <- l) {
+ n.size
+ }
+
+ noop(None)
+ }
+}
+
+class ForComprehensionListString2{
+
+}
View
9 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/Helper.scala
@@ -0,0 +1,9 @@
+package stepping
+
+object Helper {
+
+ def noop(a: Any) {
+
+ }
+
+}
View
21 org.scala-ide.sdt.debug.tests/test-workspace/debug/src/stepping/Variables.scala
@@ -0,0 +1,21 @@
+package stepping
+
+import Helper._
+
+object Variables {
+
+ def main(args: Array[String]) {
+ val a = true
+ val b = 'c'
+ val c = 3.asInstanceOf[Short]
+ val d = 4
+ val e = 5L
+ val f = 1.0f
+ val g = 2.0
+ val h = "test"
+ val i = Array(1, 2, 3)
+ val j = List(4, 5, 6)
+ noop(None)
+ }
+
+}
View
10 org.scala-ide.sdt.debug/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="target/classes" path="src"/>
+ <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/>
+ <classpathentry kind="var" path="JAVA_HOME/lib/tools.jar"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
View
2 org.scala-ide.sdt.debug/.gitignore
@@ -0,0 +1,2 @@
+.cache
+target
View
29 org.scala-ide.sdt.debug/.project
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.scala-ide.sdt.debug</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.scala-ide.sdt.core.scalabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.scala-ide.sdt.core.scalanature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
71 org.scala-ide.sdt.debug/META-INF/MANIFEST.MF
@@ -0,0 +1,71 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Scala Debug Plugin
+Bundle-SymbolicName: org.scala-ide.sdt.debug;singleton:=true
+Bundle-Version: 2.1.0.qualifier
+Bundle-Vendor: scala-ide.org
+Bundle-ActivationPolicy: lazy
+Bundle-Localization: plugin
+Bundle-Activator:
+ scala.tools.eclipse.debug.ScalaDebugPlugin
+Require-Bundle:
+ org.eclipse.core.expressions,
+ org.eclipse.core.filesystem,
+ org.eclipse.core.resources,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.core,
+ org.eclipse.debug.ui,
+ org.eclipse.help,
+ org.eclipse.jdt.core;bundle-version="[3.6.0,3.7.10)",
+ org.eclipse.jdt.core.manipulation,
+ org.eclipse.jdt.debug,
+ org.eclipse.jdt.debug.ui,
+ org.eclipse.jdt.junit,
+ org.eclipse.jdt.launching,
+ org.eclipse.jdt.ui,
+ org.eclipse.jface.text,
+ org.eclipse.ltk.core.refactoring,
+ org.eclipse.ltk.ui.refactoring,
+ org.eclipse.search,
+ org.eclipse.text,
+ org.eclipse.ui,
+ org.eclipse.ui.console,
+ org.eclipse.ui.editors,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.ide,
+ org.eclipse.ui.navigator,
+ org.eclipse.ui.navigator.resources,
+ org.eclipse.ui.views,
+ org.eclipse.ui.workbench.texteditor,
+ org.eclipse.pde.core;resolution:=optional,
+ org.eclipse.pde.ui;resolution:=optional,
+ org.scala-ide.scala.library,
+ org.scala-ide.scala.compiler,
+ org.scala-refactoring.library,
+ org.scala-ide.sbt.full.library;bundle-version="[0.11.2,0.11.3)",
+ scalariform,
+ org.junit4;bundle-version="4.5.0",
+ org.eclipse.ui.browser;bundle-version="3.3.0",
+ org.scala-ide.sdt.core;bundle-version="[2.1.0, 2.2.0)"
+Import-Package:
+ com.ibm.icu.text;apply-aspects:=false;org.eclipse.swt.graphics;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.builderoptions;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.cfprovider;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.configuration;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.core;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.cuprovider;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.hierarchy;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.imagedescriptor;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.indexerprovider;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.jcompiler;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.launching;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.search;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.spellingengineprovider;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.ui;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.ui.javaeditor;apply-aspects:=false,
+ scala.tools.eclipse.contribution.weaving.jdt.ui.javaeditor.formatter;apply-aspects:=false
+Export-Package:
+ scala.tools.eclipse.debug
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+
View
5 org.scala-ide.sdt.debug/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = target/classes/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml
View
32 org.scala-ide.sdt.debug/plugin.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ category="org.scala-ide.sdt.core.preferences"
+ class="scala.tools.eclipse.debug.DebugPreferencePage"
+ id="org.scala-ide.sdt.debug.preferences"
+ name="Debug">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.preferences">
+ <initializer
+ class="scala.tools.eclipse.debug.DebugPreferenceInitializer">
+ </initializer>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.debugModelPresentations">
+ <debugModelPresentation
+ class="scala.tools.eclipse.debug.model.ScalaDebugModelPresentation"
+ id="org.scala-ide.sdt.debug">
+ </debugModelPresentation>
+ </extension>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup
+ class="scala.tools.eclipse.debug.ScalaDebugPlugin">
+ </startup>
+ </extension>
+</plugin>
View
112 org.scala-ide.sdt.debug/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.scala-ide</groupId>
+ <artifactId>scala-ide-for-eclipse</artifactId>
+ <version>2.1.0-SNAPSHOT</version>
+ <relativePath>../org.scala-ide.build/pom.xml</relativePath>
+ </parent>
+ <artifactId>org.scala-ide.sdt.debug</artifactId>
+ <packaging>eclipse-plugin</packaging>
+
+ <properties>
+ </properties>
+
+ <dependencies>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho.version}</version>
+ <configuration>
+ <resolver>p2</resolver>
+ <pomDependencies>consider</pomDependencies>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-compiler-plugin</artifactId>
+ <version>${tycho.version}</version>
+ <configuration>
+ <excludeResources>
+ <excludeResource>**/*.scala</excludeResource>
+ </excludeResources>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.scala-tools</groupId>
+ <artifactId>maven-scala-plugin</artifactId>
+ <version>2.14</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <jvmArgs>
+ <jvmArg>-Xms512m</jvmArg>
+ <jvmArg>-Xmx1024m</jvmArg>
+ </jvmArgs>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.4.1</version>
+ <configuration>
+ <excludeDefaultDirectories>true</excludeDefaultDirectories>
+ <filesets>
+ <fileset>
+ <directory>./</directory>
+ <includes>
+ <include>lib/**/*</include>
+ <include>lib</include>
+ <include>target/**/*</include>
+ <include>target</include>
+ </includes>
+ <followSymlinks>false</followSymlinks>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+<profiles>
+ <profile>
+ <!-- This is only for non MAC OS X builds, hence the property below -->
+ <id>default-tools.jar</id>
+ <activation>
+ <file>
+ <exists>${java.home}/../lib/tools.jar</exists>
+ </file>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>com.sun</groupId>
+ <artifactId>tools</artifactId>
+ <version>1.5.0</version>
+ <scope>system</scope>
+ <systemPath>${java.home}/../lib/tools.jar</systemPath>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+</project>
View
38 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/DebugPreferencePage.scala
@@ -0,0 +1,38 @@
+package scala.tools.eclipse.debug
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer
+import org.eclipse.jface.preference.{FieldEditorPreferencePage, BooleanFieldEditor}
+import org.eclipse.ui.{IWorkbenchPreferencePage, IWorkbench}
+
+import DebugPreferencePage.P_ENABLE
+
+class DebugPreferencePage extends FieldEditorPreferencePage with IWorkbenchPreferencePage {
+ import DebugPreferencePage._
+
+ setPreferenceStore(ScalaDebugPlugin.plugin.getPreferenceStore)
+ setDescription("""Experimental debugger for Scala.
+To use it, launch your Scala application as usual.""")
+
+ override def createFieldEditors() {
+ addField(new BooleanFieldEditor(P_ENABLE, "Enable (change will be applied to new debug sessions only)", getFieldEditorParent))
+ }
+
+ def init(workbench: IWorkbench) {}
+
+}
+
+object DebugPreferencePage {
+ val BASE = "scala.tools.eclipse.debug."
+ val P_ENABLE = BASE + "enabled"
+}
+
+class DebugPreferenceInitializer extends AbstractPreferenceInitializer {
+
+ import DebugPreferencePage._
+
+ override def initializeDefaultPreferences() {
+ val store = ScalaDebugPlugin.plugin.getPreferenceStore
+ store.setDefault(P_ENABLE, false)
+ }
+
+}
View
23 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/JDIUtil.scala
@@ -0,0 +1,23 @@
+package scala.tools.eclipse.debug
+
+import com.sun.jdi.{Method, AbsentInformationException}
+
+object JDIUtil {
+
+ /**
+ * Return the list of executable lines of the given method.
+ */
+ def methodToLines(m: Method) = {
+ import scala.collection.JavaConverters._
+
+ try {
+ m.allLineLocations.asScala.map(_.lineNumber)
+ } catch {
+ case e: AbsentInformationException =>
+ Nil
+ case e =>
+ throw e
+ }
+ }
+
+}
View
27 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/ScalaDebugPlugin.scala
@@ -0,0 +1,27 @@
+package scala.tools.eclipse.debug
+
+import org.eclipse.ui.plugin.AbstractUIPlugin
+import org.eclipse.ui.IStartup
+import org.osgi.framework.BundleContext
+
+object ScalaDebugPlugin {
+ var plugin: ScalaDebugPlugin = _
+
+}
+
+class ScalaDebugPlugin extends AbstractUIPlugin with IStartup {
+
+ override def start(context: BundleContext) {
+ super.start(context)
+ ScalaDebugPlugin.plugin = this
+ ScalaDebugger.init
+ }
+
+ /*
+ * TODO: to move in start when launching a Scala application trigger the activation of this plugin.
+ */
+ def earlyStartup() {
+ ScalaDebugger.init
+ }
+
+}
View
85 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/ScalaDebugger.scala
@@ -0,0 +1,85 @@
+package scala.tools.eclipse.debug
+
+import scala.collection.mutable.Buffer
+
+import org.eclipse.debug.core.model.IDebugModelProvider
+import org.eclipse.debug.core.{IDebugEventSetListener, DebugPlugin, DebugEvent}
+import org.eclipse.jdt.debug.core.{IJavaStackFrame, IJavaDebugTarget}
+import org.eclipse.jdt.internal.debug.core.model.{JDIThread, JDIDebugTarget}
+
+import model.ScalaDebugTarget
+
+object ScalaDebugger extends IDebugEventSetListener {
+
+ val classIDebugModelProvider = classOf[IDebugModelProvider]
+ val classIJavaDebugTarget = classOf[IJavaDebugTarget]
+ val classIJavaStackFrame = classOf[IJavaStackFrame]
+
+ val modelProvider = new IDebugModelProvider {
+ def getModelIdentifiers() = {
+ Array(modelId)
+ }
+ }
+
+ val modelId = "org.scala-ide.sdt.debug"
+
+ // Members declared in org.eclipse.debug.core.IDebugEventSetListener
+
+ def handleDebugEvents(events: Array[DebugEvent]) {
+ events.foreach(event => {
+ event.getKind match {
+ case DebugEvent.CREATE =>
+ event.getSource match {
+ case target: JDIDebugTarget =>
+ if (ScalaDebugPlugin.plugin.getPreferenceStore.getBoolean(DebugPreferencePage.P_ENABLE)) {
+ javaDebugTargetCreated(target)
+ }
+ case _ =>
+ }
+ case DebugEvent.SUSPEND =>
+ event.getSource match {
+ case thread: JDIThread =>
+ javaThreadSuspended(thread, event.getDetail)
+ case _ =>
+ }
+ case DebugEvent.TERMINATE =>
+ event.getSource match {
+ case target: JDIDebugTarget =>
+ javaDebugTargetTerminated(target)
+ case _ =>
+ }
+ case _ =>
+ }
+ })
+ }
+
+ // ----
+
+ val debugTargets = Buffer[ScalaDebugTarget]()
+
+ def init() {
+ DebugPlugin.getDefault.addDebugEventListener(this)
+ }
+
+ private def javaDebugTargetCreated(target: JDIDebugTarget) {
+ val scalaTarget = ScalaDebugTarget(target)
+ debugTargets += scalaTarget
+ val launch = target.getLaunch
+ launch.removeDebugTarget(target)
+ launch.addDebugTarget(scalaTarget)
+
+ // TODO: do that in a better place
+ launch.setSourceLocator(new ScalaSourceLocator(launch))
+ }
+
+ private def javaDebugTargetTerminated(target: JDIDebugTarget) {
+ debugTargets.find(target == _.javaTarget).foreach(_.terminatedFromJava())
+ }
+
+ private def javaThreadSuspended(thread: JDIThread, eventDetail: Int) {
+ debugTargets.find(thread.getDebugTarget == _.javaTarget).foreach(_.javaThreadSuspended(thread, eventDetail))
+ }
+
+}
+
+class ScalaDebugger
View
35 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/ScalaSourceLocator.scala
@@ -0,0 +1,35 @@
+package scala.tools.eclipse.debug
+
+import scala.tools.eclipse.ScalaPlugin
+
+import org.eclipse.core.resources.ResourcesPlugin
+import org.eclipse.debug.core.model.{IStackFrame, ISourceLocator}
+import org.eclipse.debug.core.ILaunch
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants
+
+import model.ScalaStackFrame
+
+class ScalaSourceLocator(launch: ILaunch) extends ISourceLocator {
+
+ def getSourceElement(stackFrame: IStackFrame): AnyRef = {
+ stackFrame match {
+ case scalaStackFrame: ScalaStackFrame =>
+ getSourceElement(scalaStackFrame)
+ case _ =>
+ null
+ }
+ }
+
+ def getSourceElement(stackFrame: ScalaStackFrame): AnyRef = {
+ val sourceName = stackFrame.getSourceName
+
+ val projectName = launch.getLaunchConfiguration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, "") // TODO: should be null here
+
+ val project = ResourcesPlugin.getWorkspace.getRoot.getProject(projectName)
+
+ val scalaProject = ScalaPlugin.plugin.asScalaProject(project)
+
+ scalaProject.flatMap(_.allSourceFiles.find(_.getName == sourceName)).getOrElse(null)
+ }
+
+}
View
15 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/command/ScalaStep.scala
@@ -0,0 +1,15 @@
+package scala.tools.eclipse.debug.command
+
+trait ScalaStep {
+
+ /**
+ * Initiate the step action
+ */
+ def step()
+
+ /**
+ * Terminates the step action
+ */
+ def stop()
+
+}
View
164 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/command/ScalaStepOver.scala
@@ -0,0 +1,164 @@
+package scala.tools.eclipse.debug.command
+
+import scala.Option.option2Iterable
+import scala.collection.JavaConverters.asScalaBufferConverter
+import scala.collection.mutable.Buffer
+import scala.tools.eclipse.debug.JDIUtil.methodToLines
+import scala.tools.eclipse.debug.model.{ScalaThread, ScalaStackFrame, ScalaDebugTarget}
+
+import org.eclipse.debug.core.DebugEvent
+import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget
+import org.eclipse.jdt.internal.debug.core.IJDIEventListener
+
+import com.sun.jdi.event.{StepEvent, EventSet, Event, ClassPrepareEvent, BreakpointEvent}
+import com.sun.jdi.request.{StepRequest, EventRequest, ClassPrepareRequest, BreakpointRequest}
+import com.sun.jdi.{ThreadReference, ReferenceType, Method}
+
+import ScalaStepOver.{createMethodEntryBreakpoint, anonFunctionsInRange}
+
+object ScalaStepOver {
+
+ def apply(scalaStackFrame: ScalaStackFrame): ScalaStepOver = {
+
+ // TODO : two step process is weird and might not be needed and dangerous
+ import scala.collection.JavaConverters._
+
+ val eventRequestManager = scalaStackFrame.stackFrame.virtualMachine.eventRequestManager
+
+ val classPrepareRequest = eventRequestManager.createClassPrepareRequest
+ val location = scalaStackFrame.stackFrame.location
+ classPrepareRequest.addClassFilter(location.declaringType.name + "$*")
+
+ val stepOverRequest = eventRequestManager.createStepRequest(scalaStackFrame.stackFrame.thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER)
+ stepOverRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD)
+
+ // find anonFunction in range
+ val currentMethodLastLine = methodToLines(location.method).max
+
+ val range = Range(location.lineNumber, (location.method.declaringType.methods.asScala.flatten(methodToLines(_)).filter(_ > currentMethodLastLine) :+ Int.MaxValue).min)
+
+ val loadedAnonFunctionsInRange = location.method.declaringType.nestedTypes.asScala.flatMap(anonFunctionsInRange(_, range))
+
+ // if we are in an anonymous function, add the method
+ if (location.declaringType.name.contains("$$anonfun$")) {
+ loadedAnonFunctionsInRange ++= scalaStackFrame.getScalaDebugTarget.findAnonFunction(location.declaringType)
+ }
+
+ val entryBreakpoints = loadedAnonFunctionsInRange.map(createMethodEntryBreakpoint(_, scalaStackFrame.stackFrame.thread))
+
+ new ScalaStepOver(scalaStackFrame.getScalaDebugTarget, range, scalaStackFrame.thread, classPrepareRequest, stepOverRequest, entryBreakpoints)
+ }
+
+ def createMethodEntryBreakpoint(method: Method, thread: ThreadReference) = {
+ import scala.collection.JavaConverters._
+
+ val breakpointRequest = thread.virtualMachine.eventRequestManager.createBreakpointRequest(method.location)
+ breakpointRequest.addThreadFilter(thread)
+
+ breakpointRequest
+ }
+
+ // TODO: use ScalaDebugTarget#findAnonFunction
+ def anonFunctionsInRange(refType: ReferenceType, range: Range) = {
+ import scala.collection.JavaConverters._
+ val methods = refType.methods.asScala.filter(method =>
+ range.contains(method.location.lineNumber) && method.name.startsWith("apply"))
+
+ // TODO: using isBridge was not working with List[Int]. Should check if we can use it by default with some extra checks when it fails.
+ // methods.find(!_.isBridge)
+
+ methods.size match {
+ case 3 =>
+ // method with primitive parameter
+ methods.find(_.name.startsWith("apply$")).orElse({
+ // method with primitive return type (with specialization in 2.10.0)
+ methods.find(!_.signature.startsWith("(Ljava/lang/Object;)"))
+ })
+ case 2 =>
+ methods.find(_.signature != "(Ljava/lang/Object;)Ljava/lang/Object;")
+ case 1 =>
+ methods.headOption
+ case _ =>
+ None
+ }
+ }
+}
+
+class ScalaStepOver(target: ScalaDebugTarget, range: Range, thread: ScalaThread, classPrepareRequest: ClassPrepareRequest, stepOverRequest: StepRequest, entryBreakpoints: Buffer[BreakpointRequest]) extends IJDIEventListener with ScalaStep {
+ import ScalaStepOver._
+ /* from IJDIEventListener */
+
+ def eventSetComplete(event: Event, target: JDIDebugTarget, suspend: Boolean, eventSet: EventSet): Unit = {
+ // nothing to do
+ }
+
+ def handleEvent(event: Event, javaTarget: JDIDebugTarget, suspendVote: Boolean, eventSet: EventSet): Boolean = {
+ event match {
+ case classPrepareEvent: ClassPrepareEvent =>
+ anonFunctionsInRange(classPrepareEvent.referenceType, range).foreach(method => {
+ val breakpoint = createMethodEntryBreakpoint(method, thread.thread)
+ entryBreakpoints += breakpoint
+ javaTarget.getEventDispatcher.addJDIEventListener(this, breakpoint)
+ breakpoint.enable
+ })
+ true
+ case stepEvent: StepEvent =>
+ if (target.isValidLocation(stepEvent.location)) {
+ stop
+ thread.suspendedFromScala(DebugEvent.STEP_OVER)
+ false
+ } else {
+ true
+ }
+ case breakpointEvent: BreakpointEvent =>
+ stop
+ thread.suspendedFromScala(DebugEvent.STEP_OVER)
+ false
+ case _ =>
+ suspendVote
+ }
+ }
+
+ // ----
+
+ def step() {
+ val eventDispatcher = target.javaTarget.getEventDispatcher
+
+ // TODO: they are all request, and have the same life cycle, they can be combined in one collection
+
+ eventDispatcher.addJDIEventListener(this, classPrepareRequest)
+ classPrepareRequest.enable
+
+ entryBreakpoints.foreach(breakpoint => {
+ eventDispatcher.addJDIEventListener(this, breakpoint)
+ breakpoint.enable
+ })
+
+ eventDispatcher.addJDIEventListener(this, stepOverRequest)
+ stepOverRequest.enable
+ thread.resumedFromScala(DebugEvent.STEP_OVER)
+ thread.thread.resume
+ }
+
+ def stop() {
+ val eventDispatcher = target.javaTarget.getEventDispatcher
+
+ val eventRequestManager = thread.thread.virtualMachine.eventRequestManager
+
+ classPrepareRequest.disable
+ eventDispatcher.removeJDIEventListener(this, classPrepareRequest)
+ eventRequestManager.deleteEventRequest(classPrepareRequest)
+
+ entryBreakpoints.foreach(breakpoint => {
+ breakpoint.disable
+ eventDispatcher.removeJDIEventListener(this, breakpoint)
+ eventRequestManager.deleteEventRequest(breakpoint)
+ })
+
+ stepOverRequest.disable
+ eventDispatcher.removeJDIEventListener(this, stepOverRequest)
+ eventRequestManager.deleteEventRequest(stepOverRequest)
+
+ }
+
+}
View
36 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaDebugElement.scala
@@ -0,0 +1,36 @@
+package scala.tools.eclipse.debug.model
+
+import scala.tools.eclipse.debug.ScalaDebugger.{modelProvider, modelId}
+import scala.tools.eclipse.debug.ScalaDebugger
+import scala.tools.eclipse.logging.HasLogger
+
+import org.eclipse.debug.core.model.{ITerminate, DebugElement}
+
+class ScalaDebugElement(target: ScalaDebugTarget) extends DebugElement(target) with ITerminate with HasLogger {
+
+ // Members declared in org.eclipse.core.runtime.IAdaptable
+
+ override def getAdapter(adapter: Class[_]): Object = {
+ adapter match {
+ case ScalaDebugger.classIDebugModelProvider =>
+ modelProvider
+ case _ =>
+ super.getAdapter(adapter)
+ }
+ }
+
+ // Members declared in org.eclipse.debug.core.model.IDebugElement
+
+ def getModelIdentifier(): String = modelId
+
+ // Members declared in org.eclipse.debug.core.model.ITerminate
+
+ def canTerminate(): Boolean = target.canTerminate
+ def isTerminated(): Boolean = target.isTerminated
+ def terminate(): Unit = target.terminate
+
+ // ----
+
+ def getScalaDebugTarget(): ScalaDebugTarget = target
+
+}
View
82 ...scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaDebugModelPresentation.scala
@@ -0,0 +1,82 @@
+package scala.tools.eclipse.debug.model
+
+import org.eclipse.core.resources.IFile
+import org.eclipse.debug.core.model.IValue
+import org.eclipse.debug.ui.{IValueDetailListener, IDebugUIConstants, IDebugModelPresentation, DebugUITools}
+import org.eclipse.ui.ide.IDE
+import org.eclipse.ui.part.FileEditorInput
+import org.eclipse.ui.{IFileEditorInput, IEditorInput}
+
+class ScalaDebugModelPresentation extends IDebugModelPresentation {
+
+ // Members declared in org.eclipse.jface.viewers.IBaseLabelProvider
+
+ def addListener(x$1: org.eclipse.jface.viewers.ILabelProviderListener): Unit = ???
+ def dispose(): Unit = {} // TODO: need real logic
+ def isLabelProperty(x$1: Any, x$2: String): Boolean = ???
+ def removeListener(x$1: org.eclipse.jface.viewers.ILabelProviderListener): Unit = ???
+
+ // Members declared in org.eclipse.debug.ui.IDebugModelPresentation
+
+ def computeDetail(value: IValue, listener: IValueDetailListener): Unit = {
+ // TODO: the real work
+ listener.detailComputed(value, null)
+ }
+
+ def getImage(element: Any): org.eclipse.swt.graphics.Image = {
+ element match {
+ case target: ScalaDebugTarget =>
+ // TODO: right image depending of state
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_DEBUG_TARGET)
+ case thread: ScalaThread =>
+ // TODO: right image depending of state
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING)
+ case stackFrame: ScalaStackFrame =>
+ // TODO: right image depending of state
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_STACKFRAME)
+ case variable: ScalaVariable =>
+ // TODO: right image depending on ?
+ DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_VARIABLE)
+ case _ =>
+ ???
+ }
+ }
+
+ def getText(element: Any): String = {
+ element match {
+ case target: ScalaDebugTarget =>
+ target.getName // TODO: everything
+ case thread: ScalaThread =>
+ thread.getName // TODO: everything
+ case stackFrame: ScalaStackFrame =>
+ stackFrame.getName // TODO: everything
+ case _ =>
+ ???
+ }
+ }
+
+ def setAttribute(x$1: String, x$2: Any): Unit = ???
+
+ // Members declared in org.eclipse.debug.ui.ISourcePresentation
+
+ def getEditorId(input: IEditorInput, element: Any): String = {
+ input match {
+ case fileInput: IFileEditorInput =>
+ IDE.getEditorDescriptor(fileInput.getFile).getId
+ case _ =>
+ null
+ }
+ }
+
+ def getEditorInput(input: Any): IEditorInput = {
+ input match {
+ case file: IFile =>
+ new FileEditorInput(file)
+ case _ =>
+ ???
+ }
+ }
+
+ // ----
+
+}
View
173 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaDebugTarget.scala
@@ -0,0 +1,173 @@
+package scala.tools.eclipse.debug.model
+
+import scala.Option.option2Iterable
+import scala.collection.JavaConverters.asScalaBufferConverter
+import scala.tools.eclipse.debug.ScalaDebugger
+
+import org.eclipse.debug.core.model.IDebugTarget
+import org.eclipse.jdt.internal.debug.core.model.{JDIThread, JDIDebugTarget}
+import org.eclipse.jdt.internal.debug.core.IJDIEventListener
+
+import com.sun.jdi.event.{ThreadStartEvent, ThreadDeathEvent, EventSet, Event}
+import com.sun.jdi.request.{ThreadStartRequest, ThreadDeathRequest, EventRequest}
+import com.sun.jdi.{ReferenceType, Method, Location}
+
+object ScalaDebugTarget {
+
+ def apply(javaTarget: JDIDebugTarget): ScalaDebugTarget = {
+
+ val virtualMachine = javaTarget.getVM
+
+ val threadStartRequest = virtualMachine.eventRequestManager.createThreadStartRequest
+ threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE)
+
+ val threadDeathRequest = virtualMachine.eventRequestManager.createThreadDeathRequest
+ threadDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE)
+
+ val target = new ScalaDebugTarget(javaTarget, threadStartRequest, threadDeathRequest)
+
+ // enable the requests
+ javaTarget.addJDIEventListener(target, threadStartRequest)
+ threadStartRequest.enable
+ javaTarget.addJDIEventListener(target, threadDeathRequest)
+ threadDeathRequest.enable
+
+ target
+ }
+
+}
+
+class ScalaDebugTarget(val javaTarget: JDIDebugTarget, threadStartRequest: ThreadStartRequest, threadDeathRequest: ThreadDeathRequest) extends ScalaDebugElement(null) with IDebugTarget with IJDIEventListener {
+
+ // Members declared in org.eclipse.core.runtime.IAdaptable
+
+ override def getAdapter(adapter: Class[_]): Object = {
+ adapter match {
+ case ScalaDebugger.classIJavaDebugTarget =>
+ null
+ case ScalaDebugger.classIJavaStackFrame =>
+ null
+ case _ =>
+ super.getAdapter(adapter)
+ }
+ }
+
+ // Members declared in org.eclipse.debug.core.IBreakpointListener
+
+ def breakpointAdded(x$1: org.eclipse.debug.core.model.IBreakpoint): Unit = ???
+ def breakpointChanged(x$1: org.eclipse.debug.core.model.IBreakpoint, x$2: org.eclipse.core.resources.IMarkerDelta): Unit = ???
+ def breakpointRemoved(x$1: org.eclipse.debug.core.model.IBreakpoint, x$2: org.eclipse.core.resources.IMarkerDelta): Unit = ???
+
+ // Members declared in org.eclipse.debug.core.model.IDebugElement
+
+ override def getDebugTarget(): org.eclipse.debug.core.model.IDebugTarget = this
+ override def getLaunch(): org.eclipse.debug.core.ILaunch = javaTarget.getLaunch
+
+ // Members declared in org.eclipse.debug.core.model.IDebugTarget
+
+ def getName(): String = "Scala Debug Target" // TODO: need better name
+ def getProcess(): org.eclipse.debug.core.model.IProcess = javaTarget.getProcess
+ def getThreads(): Array[org.eclipse.debug.core.model.IThread] = threads.toArray
+ def hasThreads(): Boolean = !threads.isEmpty
+ def supportsBreakpoint(x$1: org.eclipse.debug.core.model.IBreakpoint): Boolean = ???
+
+ // Members declared in org.eclipse.debug.core.model.IDisconnect
+
+ def canDisconnect(): Boolean = false // TODO: need real logic
+ def disconnect(): Unit = ???
+ def isDisconnected(): Boolean = false // TODO: need real logic
+
+ // Members declared in org.eclipse.debug.core.model.IMemoryBlockRetrieval
+
+ def getMemoryBlock(x$1: Long, x$2: Long): org.eclipse.debug.core.model.IMemoryBlock = ???
+ def supportsStorageRetrieval(): Boolean = ???
+
+ // Members declared in org.eclipse.debug.core.model.ISuspendResume
+
+ def canResume(): Boolean = false // TODO: need real logic
+ def canSuspend(): Boolean = false // TODO: need real logic
+ def isSuspended(): Boolean = false // TODO: need real logic
+ def resume(): Unit = ???
+ def suspend(): Unit = ???
+
+ // Members declared in org.eclipse.debug.core.model.ITerminate
+
+ override def canTerminate(): Boolean = running // TODO: need real logic
+ override def isTerminated(): Boolean = !running // TODO: need real logic
+ override def terminate(): Unit = javaTarget.terminate
+
+ // Members declared in org.eclipse.jdt.internal.debug.core.IJDIEventListener
+
+ def eventSetComplete(event: Event, target: JDIDebugTarget, suspend: Boolean, eventSet: EventSet): Unit = {
+ // nothing to do
+ }
+
+ def handleEvent(event: Event, target: JDIDebugTarget, suspendVote: Boolean, eventSet: EventSet): Boolean = {
+ event match {
+ case threadStartEvent: ThreadStartEvent =>
+ threads += new ScalaThread(this, threadStartEvent.thread)
+ case threadDeathEvent: ThreadDeathEvent =>
+ threads --= threads.find(_.thread == threadDeathEvent.thread)
+ case _ =>
+ ???
+ }
+ suspendVote
+ }
+
+ // ---
+
+ var running: Boolean = true
+
+ val threads = {
+ import scala.collection.JavaConverters._
+ javaTarget.getVM.allThreads.asScala.map(new ScalaThread(this, _))
+ }
+
+ fireCreationEvent
+
+ def javaThreadSuspended(thread: JDIThread, eventDetail: Int) {
+ threads.find(_.thread == thread.getUnderlyingThread).get.suspendedFromJava(eventDetail)
+ }
+
+ def terminatedFromJava() {
+ threads.clear
+ running = false
+ fireTerminateEvent
+ }
+
+ def findAnonFunction(refType: ReferenceType): Option[Method] = {
+ import scala.collection.JavaConverters._
+ val methods = refType.methods.asScala.filter(method => method.name.startsWith("apply"))
+
+ // TODO: using isBridge was not working with List[Int]. Should check if we can use it by default with some extra checks when it fails.
+ // methods.find(!_.isBridge)
+
+ methods.size match {
+ case 3 =>
+ // method with primitive parameter
+ methods.find(_.name.startsWith("apply$")).orElse({
+ // method with primitive return type (with specialization in 2.10.0)
+ methods.find(!_.signature.startsWith("(Ljava/lang/Object;)"))
+ })
+ case 2 =>
+ methods.find(_.signature != "(Ljava/lang/Object;)Ljava/lang/Object;")
+ case 1 =>
+ methods.headOption
+ case _ =>
+ None
+ }
+ }
+
+ def isValidLocation(location: Location): Boolean = {
+ val typeName = location.declaringType.name
+ // TODO: use better pattern matching
+ // TODO: check for bridge methods?
+ if (typeName.startsWith("scala.collection"))
+ false
+ else if (typeName.contains("$$anonfun$")) {
+ findAnonFunction(location.declaringType).exists(_ == location.method)
+ } else
+ true
+ }
+
+}
View
55 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaStackFrame.scala
@@ -0,0 +1,55 @@
+package scala.tools.eclipse.debug.model
+
+import scala.collection.JavaConverters.asScalaBufferConverter
+import org.eclipse.debug.core.model.IStackFrame
+import com.sun.jdi.StackFrame
+import com.sun.jdi.AbsentInformationException
+
+class ScalaStackFrame(val thread: ScalaThread, val stackFrame: StackFrame) extends ScalaDebugElement(thread.getScalaDebugTarget) with IStackFrame {
+
+ // Members declared in org.eclipse.debug.core.model.IStackFrame
+
+ def getCharEnd(): Int = -1
+ def getCharStart(): Int = -1
+ def getLineNumber(): Int = stackFrame.location.lineNumber // TODO: cache data ?
+ def getName(): String = stackFrame.location.declaringType.name // TODO: cache data ?
+ def getRegisterGroups(): Array[org.eclipse.debug.core.model.IRegisterGroup] = ???
+ def getThread(): org.eclipse.debug.core.model.IThread = thread
+ def getVariables(): Array[org.eclipse.debug.core.model.IVariable] = variables.toArray // TODO: need real logic
+ def hasRegisterGroups(): Boolean = ???
+ def hasVariables(): Boolean = ???
+
+ // Members declared in org.eclipse.debug.core.model.IStep
+
+ def canStepInto(): Boolean = false // TODO: need real logic
+ def canStepOver(): Boolean = true // TODO: need real logic
+ def canStepReturn(): Boolean = false // TODO: need real logic
+ def isStepping(): Boolean = ???
+ def stepInto(): Unit = ???
+ def stepOver(): Unit = thread.stepOver
+ def stepReturn(): Unit = ???
+
+ // Members declared in org.eclipse.debug.core.model.ISuspendResume
+
+ def canResume(): Boolean = false // TODO: need real logic
+ def canSuspend(): Boolean = false // TODO: need real logic
+ def isSuspended(): Boolean = true // TODO: need real logic
+ def resume(): Unit = ???
+ def suspend(): Unit = ???
+
+ // ---
+
+ fireCreationEvent
+
+ val variables: Seq[ScalaLocalVariable] = {
+ import scala.collection.JavaConverters._
+ try {
+ stackFrame.visibleVariables.asScala.map(new ScalaLocalVariable(_, this))
+ } catch {
+ case e: AbsentInformationException => Seq()
+ }
+ }
+
+ def getSourceName(): String = stackFrame.location.sourceName
+
+}
View
78 org.scala-ide.sdt.debug/src/scala/tools/eclipse/debug/model/ScalaThread.scala
@@ -0,0 +1,78 @@
+package scala.tools.eclipse.debug.model
+
+import scala.collection.JavaConverters.asScalaBufferConverter
+import scala.tools.eclipse.debug.command.{ScalaStepOver, ScalaStep}
+
+import org.eclipse.debug.core.model.{IThread, IBreakpoint}
+
+import com.sun.jdi.ThreadReference
+
+class ScalaThread(target: ScalaDebugTarget, val thread: ThreadReference) extends ScalaDebugElement(target) with IThread {
+
+ // Members declared in org.eclipse.debug.core.model.IStep
+
+ def canStepInto(): Boolean = false // TODO: need real logic
+ def canStepOver(): Boolean = suspended // TODO: need real logic
+ def canStepReturn(): Boolean = false // TODO: need real logic
+ def isStepping(): Boolean = ???
+ def stepInto(): Unit = ???
+
+ def stepOver(): Unit = {
+ // top stack frame
+ currentStep = Some(ScalaStepOver(stackFrames.find(sf => true).get))
+ currentStep.get.step
+ }
+