-
Notifications
You must be signed in to change notification settings - Fork 344
Extract evalService from debuggerController that serves console. #4992
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
Conversation
@@ -1154,7 +1154,7 @@ class _LineItemState extends State<LineItem> | |||
|
|||
if (word != '') { | |||
try { | |||
final response = await controller.evalAtCurrentFrame(word); | |||
final response = await controller.evalService.evalAtCurrentFrame(word); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case is tricky as you wouldn't want any injected variables from the console to show up when evaluating while hovering over source code. Maybe this case should take an extra named parameter like includeConsoleVariables: false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed and agreed evalAtCurrentFrame
will never be invoked for global scope.
required this.service, | ||
}); | ||
|
||
final IsolateRef? Function() isolateRef; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would seem more consistent to use ValueListenable for all of these rather than callbacks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ValueListenable is too powerful. I want it to be clear that EvalService does not need to listen for the changes. It just needs up-to-date value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite buy this. The autocomplete cache is bogus if the isolate changed, for example. ValueListenable does not force you to always listen for changes. As appropriate you can always just use .value to get the current value. On the other end of the spectrum, VmServiceWrapper needs to just be a fixed instance because there is no reason anyone writing this code should worry it can changed.,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My goal is clarity of contract for the method.
I want the constructor parameters to be smallest needed set of what the object needs to function. Of course, not at cost of convenience.
About ValueListenable vs function. I remember a story: when I passed ValueListenable, I got comments in code review like 'why this object need to listen to every change? it is performance overhead'. And then I needed to explain that the object just needs values.
So, it affects readability of code too.
Does it change something for you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About VmServiceWrapper: as I understand when reconnect happens, DebuggerController is not recreated, but it resets some variables.
So, EvalService will not recreate, but just reset.
Yes, it may be not the best implementation, but it is what we currently have. And it will take more refactoring to change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And, I did not understand you point about consistency. In Flutter framework there is ton of methods and constructors that take functions, not listeners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each time an app connects you get a new DebuggerController. How it happens flows back to how the provider for the DebuggerController is initialized.
The private getter for the service in debuggerController is really just calling
serviceManager.service!
Would be clearer to just call that global from EvalService rather than making it look like it is injected in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I think ValueListenable is almost always the right choice if something can actually change. If it can't change then pass in the actual value when initializing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converted service to variable, isPaused and variables to listenable.
|
||
String get _isolateRefId { | ||
final id = isolateRef()?.id; | ||
if (id == null) return ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is returning '' for the isolateRefId ever ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not know. I copied it from previous code. Added 'TODO'.
/// The return value can be one of [Obj] or [Sentinel]. | ||
Future<Obj> getObject(ObjRef objRef) { | ||
return _service.getObject(_isolateRefId, objRef.id!); | ||
evalService.cache.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is badly coupled. Instead EvalService should listen for isolate changes itself instead of requiring the debuggerControl to call this method at the appropriate time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks. fixed.
late final EvalService evalService = EvalService( | ||
isolateRef: isolateRef, | ||
variables: variables, | ||
frameForEval: () => _frameForEval, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_frameForEval should also be made a ValueListenable for consistency. That would probably cleanup some of the debugger UI that should be listening for changes to the frameForEval.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not seem to make sense for me to recalculate and update frameForEval
every time _selectedStackFrame
or _stackFramesWithLocation
change, from both code readability/simplicity and performance perspective.
Deleted getter and moved the logic to the function, as the getter is not used in other places..
} | ||
} | ||
|
||
final codeViewController = CodeViewController(); | ||
|
||
late final EvalService evalService = EvalService( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make this final instead of late final
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Move the initializer to the constructor to avoid this issue. In general, I would avoid initializing fields inline except for very trivial cases. Otherwise the initialization order semantics can trip some people up when reviewing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even after the move it is still late, because it cannot be initialized in constructor initializers, but only in the body. Did i miss something? Let's follow up then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be out tomorrow. Would be nice to remove the late final moving it to a constructor.
RELEASE_NOTE_EXCEPTION=[no functional changes]