[go: nahoru, domu]

Skip to content

Commit

Permalink
feat: invalidate method dependencies (enso-org#1516)
Browse files Browse the repository at this point in the history
When the edit changes the method body, invalidate 
all the method usages, and the corresponding 
dependencies
  • Loading branch information
4e6 committed Feb 26, 2021
1 parent 6544c24 commit 3bf566f
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,44 @@ final class ChangesetBuilder[A: TextEditor: IndexedSource](
DataflowAnalysis,
"Empty dataflow analysis metadata during changeset calculation."
)
val direct = invalidated(edits)
val transitive = direct
.flatMap(ChangesetBuilder.toDataflowDependencyTypes)
.flatMap(metadata.getExternal)
.flatten

@scala.annotation.tailrec
def go(
queue: mutable.Queue[DataflowAnalysis.DependencyInfo.Type],
visited: mutable.Set[DataflowAnalysis.DependencyInfo.Type]
): Set[IR.ExternalId] =
if (queue.isEmpty) visited.flatMap(_.externalId).toSet
else {
val elem = queue.dequeue()
val transitive = metadata.get(elem).getOrElse(Set())
val dynamic = transitive
.flatMap {
case DataflowAnalysis.DependencyInfo.Type.Static(int, ext) =>
ChangesetBuilder
.getExpressionName(ir, int)
.map(DataflowAnalysis.DependencyInfo.Type.Dynamic(_, ext))
case dyn: DataflowAnalysis.DependencyInfo.Type.Dynamic =>
Some(dyn)
case _ =>
None
}
.flatMap(metadata.get)
.flatten
val combined = transitive.union(dynamic)

go(
queue ++= combined.diff(visited),
visited ++= combined
)
}

val direct =
invalidated(edits).flatMap(ChangesetBuilder.toDataflowDependencyTypes)
val transitive =
go(
mutable.Queue().addAll(direct),
mutable.Set()
)
direct.flatMap(_.externalId) ++ transitive
}

Expand Down Expand Up @@ -353,4 +386,19 @@ object ChangesetBuilder {
}
static +: dynamic.toSeq
}

/** Get expression name by the given id.
*
* @param ir the IR tree
* @param id the node identifier
* @return the node name
*/
private def getExpressionName(ir: IR, id: IR.Identifier): Option[String] =
ir.preorder.find(_.getId == id).collect {
case name: IR.Name =>
name.name
case method: IR.Module.Scope.Definition.Method =>
method.methodName.name
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,7 @@ object IR {
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
) extends Method
with IRKind.Primitive {
override protected var id: Identifier = _
override protected var id: Identifier = randomId

/** Creates a copy of `this`.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,121 @@ class RuntimeServerTest
)
}

it should "send updates when function body is changed" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
val moduleName = "Test.Main"
val metadata = new Metadata

val fooX = metadata.addItem(40, 1)
val fooRes = metadata.addItem(46, 1)
val mainFoo = metadata.addItem(64, 8)
val mainRes = metadata.addItem(77, 12)

val code =
"""from Builtins import all
|
|foo =
| x = 4
| x
|
|main =
| y = here.foo
| IO.println y
|""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code)
val mainFile = context.writeMain(contents)

// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
context.receive shouldEqual Some(
Api.Response(requestId, Api.CreateContextResponse(contextId))
)

// Open the new file
context.send(
Api.Request(Api.OpenFileNotification(mainFile, contents, true))
)
context.receiveNone shouldEqual None

// push main
context.send(
Api.Request(
requestId,
Api.PushContextRequest(
contextId,
Api.StackItem.ExplicitCall(
Api.MethodPointer(moduleName, "Test.Main", "main"),
None,
Vector()
)
)
)
)
context.receive(4) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PushContextResponse(contextId)),
TestMessages
.update(
contextId,
mainFoo,
Constants.INTEGER,
Api.MethodPointer("Test.Main", "Test.Main", "foo")
),
TestMessages.update(contextId, mainRes, Constants.NOTHING),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("4")

// push foo call
context.send(
Api.Request(
requestId,
Api.PushContextRequest(contextId, Api.StackItem.LocalCall(mainFoo))
)
)
context.receive(4) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PushContextResponse(contextId)),
TestMessages.update(contextId, fooX, Constants.INTEGER),
TestMessages.update(contextId, fooRes, Constants.INTEGER),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("4")

// Modify the foo method
context.send(
Api.Request(
Api.EditFileNotification(
mainFile,
Seq(
TextEdit(
model.Range(model.Position(3, 8), model.Position(3, 9)),
"5"
)
)
)
)
)
context.receive(1) should contain theSameElementsAs Seq(
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("5")

// pop the foo call
context.send(Api.Request(requestId, Api.PopContextRequest(contextId)))
context.receive(3) should contain theSameElementsAs Seq(
Api.Response(requestId, Api.PopContextResponse(contextId)),
TestMessages
.update(
contextId,
mainFoo,
Constants.INTEGER,
Api.MethodPointer("Test.Main", "Test.Main", "foo")
),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("5")
}

it should "not send updates when the type is not changed" in {
val contextId = UUID.randomUUID()
val requestId = UUID.randomUUID()
Expand Down

0 comments on commit 3bf566f

Please sign in to comment.