Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access non-self arguments when widget annotations are computed #9410

Merged
merged 42 commits into from
Apr 24, 2024

Conversation

JaroslavTulach
Copy link
Member

@JaroslavTulach JaroslavTulach commented Mar 14, 2024

Pull Request Description

Fixes #5629 by adding access to arguments with UUIDs when computing widget annotations.

Implementation Nodes

There are three parts to change:

  • the IDE has to be changed to provide some information about arguments when requesting widget
  • such information then allows the on annotation to access cache based on names of function arguments
  • everything then gets glued together in ExecutionService

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • All code follows the
    Scala,
    Java,
    style guides.
  • All code has been tested:
    • Unit tests that send the cache argument in and check the behavior of make_join_condition_selector: 6942ef5
    • Unit test the IDE behavior

@JaroslavTulach
Copy link
Member Author

Rejected: Send argument expressions, not values

Once @Frizi suggested to

(Good) idea of the map, though what we would send is probably the argument expressions, not values.
Would it be possible to use the lazy evaluation here?

Turns out that lazy evaluation of arguments isn't possible in this case. To evaluate arbitrary text in place one needs to run the whole program first and only when it reaches the requested point (and all local variables are available in stack frames) one can evaluate the expression.

Looks like this is not how widgets get evaluated, right @4e6? The evaluation is executed outside of any program stack and only obtains self value (identified by UUID) from a cache. This PR proposes to make also other variables available, but they still need to be identified by UUID and be available in the cache, as there is no local stack to extract the values from.

We could probably evaluate the widgets and visualizations in place while executing the whole program. However that would require bigger changes and possibly contradicts

@hubertp is currently working on.

@JaroslavTulach JaroslavTulach changed the title PoC: Access arguments when widgets annotations are computed Access non-self arguments when widget annotations are computed Apr 22, 2024
@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Apr 22, 2024

There is 15 failures in:

Fixed by 9c31cd4

_ -> annotation
return_target err = err.payload.target
Panic.catch Not_Invokable handler=return_target
annotation value cache=cache
Copy link
Member Author

@JaroslavTulach JaroslavTulach Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache argument is "optional". If the function doesn't accept it, Not_Invokable panic is raised. It is caught and the value that refused to accept cache argument is returned instead. As a result it is not necessary to accept the cache argument in dynamic dropdown functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is very subtle!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subtle, but tested!

Copy link
Member

@jdunkerley jdunkerley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to have this. Can't wait to try building it out.

_ -> annotation
return_target err = err.payload.target
Panic.catch Not_Invokable handler=return_target
annotation value cache=cache
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is very subtle!

private final Map<UUID, SoftReference<Object>> cache = new HashMap<>();
private final Map<UUID, Reference<Object>> cache = new HashMap<>();
private final Map<Object, UUID> valuesToKeys = new WeakHashMap<>();
private final Map<UUID, Reference<Object>> expressions = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned that these new caches are not invalidated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invalidation is happening in the compile job

Seq(
CacheInvalidation(
CacheInvalidation.StackSelector.All,
invalidateExpressionsCommand,
Set(CacheInvalidation.IndexSelector.Weights)
),
CacheInvalidation(
CacheInvalidation.StackSelector.All,
invalidateStaleCommand,
Set(CacheInvalidation.IndexSelector.All)
)
)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, these two new caches aren't invalidated. However that shouldn't matter much - both are using WeakReference and as such they don't hold the referenced objects in memory more than cache which uses SoftReference - e.g. it is necessary to clean the cache only, the valuesToKeys and expressions are going to be cleaned occasionally then by GC.

Btw. valuesToKeys is a WeakHashMap - e.g. the keys are hold by WeakReference automatically. expressions is using WeakReference explicitly to hold the values.

I can write some tests, if necessary to prove the system is behaving correctly. Other than that there shall be no need to change the code.

Copy link
Member Author

@JaroslavTulach JaroslavTulach Apr 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test written in 63c0b20- we don't have an assertGC test in the code base yet, we'll see how flaky it'll be. However the assertGC concept has been sufficiently used and tested already.

@JaroslavTulach JaroslavTulach requested a review from 4e6 April 23, 2024 09:01
Copy link
Contributor

@Frizi Frizi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good enough for a merge, but ideally I'd like the collectArgumentNamesAndUuids logic to be cleaned up a little. GUI team can do that after merging this PR though.

if (a instanceof ArgumentPlaceholder) {
// pass thru
} else {
process(a.ast, a.argInfo?.name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calculating index yourself, it should be passed here as a.index. No need to pass MethodCallInfo to this function.

Copy link
Member Author

@JaroslavTulach JaroslavTulach Apr 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alas a.index isn't defined in the callTree.test.ts as far as I can say. Maybe it is defined during runtime, but it is missing in the mocked environment. In any case, I can't change the code unless we want to remove the test. Probably better to have worse code with a test than better code without a test...

Comment on lines 435 to 455
let index = 'self' === mci?.suggestion.arguments[0]?.name ? 1 : 0
for (const e of arr) {
const notApplied = mci?.methodCall.notAppliedArguments ?? []
while (notApplied.indexOf(index) != -1) {
index++
}
if (e.uuid) {
m['' + index] = e.uuid
}
const n: string | undefined = mci?.suggestion.arguments[index]?.name
if (n && e.uuid) {
m[n] = e.uuid
}
index++
}
for (const e of arr) {
if (e.name && e.uuid) {
m[e.name] = e.uuid
}
}
return m
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, those separate loops are unnecessary. This whole logic can easily be moved into the process function, directly writing to the m output object. We have uuid, argument name and index all available there.

Copy link
Contributor

@farmaazon farmaazon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments about names. @Frizi is doing main review.

app/gui2/src/util/callTree.ts Outdated Show resolved Hide resolved
app/gui2/src/util/callTree.ts Outdated Show resolved Hide resolved
}
}
} else {
// ignore: process(args)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean? Why do we ignore? Should the process(args) be called normally?

This else looks like it bears some important information, but I cannot decipher it here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither I. I don't know how to write a test for this case. What code one has to parse for FromInterpreterWithInfo to not be ArgumentApplication?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK for example the call foo.bar, where bar takes only self argument.

app/gui2/src/util/callTree.ts Outdated Show resolved Hide resolved
@JaroslavTulach
Copy link
Member Author

Let's move on, give libs guys access to Table.join right argument and fix problems as they appear. Thank you for your patience.

@JaroslavTulach JaroslavTulach merged commit ff62c1e into develop Apr 24, 2024
36 checks passed
@JaroslavTulach JaroslavTulach deleted the wip/jtulach/TableJoin5629 branch April 24, 2024 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generate completion of Table.join join criteria using data from both joined tables
9 participants