Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@yjbanov
Copy link
Contributor

@yjbanov yjbanov commented Jun 19, 2020

Description

This upgrades the dart:_engine code to null-safe Dart.

Notable changes:

  • Apply the new syntax (?, !, etc)
  • Implicit downcasts become explicit (required by the new language)
  • Disable dart2js inlining for tests for better stack traces
  • CSS attributes are no longer nullable => changed to assigning empty string instead of null
  • Changed html.window.navigator.clipboard?.writeText != null and html.window.navigator.clipboard?.readText != null to just html.window.navigator.clipboard != null because the tear-offs are never null.
  • Unimplemented non-null API that used to return null now throws UnsupportedError.
  • PathMetrics iterators throw RangeError instead of returning null (matches mobile behavior).
  • Refactor compositor/rasterizer.dart and compositor/surface.dart to initialize HtmlViewEmbedder upon construction so that it can be final and non-null.
  • Throw errors when CanvasKit fails to construct a GL surface, instead of just printing to window.console (this removes many null situations).
  • Replace List<Conic> with _ConicPair as temporary storage of results (NNBD does not support fixed-capacity lists).
  • Converted some constructors to factories so that final fields can be immediately initialized to non-null values as opposed to lazy initializing them (e.g. WriteBuffer, SurfaceCanvas)
  • EngineLineMetrics.withText uses bogus values for unsupported fields
  • Factories no longer return null (e.g. factory AutofillInfo.fromFrameworkMessage). This is a new language requirement.
  • Fixes an issue with getBoxesForRange in RTL mode (see paragraph_test.dart)

How to review this PR

  • This PR touches every file in the dart:_engine. It is not practical for one person to carefully review all of it. Instead, each reviewer is assigned a subset of files (although do feel free to comment on anything). The assignments are tracked in this spreadsheet: https://docs.google.com/spreadsheets/d/1Efar1J6eDARoeDlDI4qXdoldJuxUcMUeLpXpu3gQDMA/edit?pli=1#gid=180643056
  • The PR does not fix all nullability issues. It is not practical to address them all in a PR of this size. If you see places where something should be non-null but isn't, please file an issue so we remember to fix this in a smaller follow-up PR. The reverse is not true, however. If something has been make non-null where it should be nullable could lead to a bug, so please comment on that.

Related Issues

Fixes flutter/flutter#58811

Copy link
Contributor

@ferhatb ferhatb left a comment

Choose a reason for hiding this comment

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

First batch of comments.

Copy link
Contributor

@ferhatb ferhatb left a comment

Choose a reason for hiding this comment

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

Comments Batch2

///
/// Painting outside the bounds of this rectangle is cropped.
final ui.Rect bounds;
final ui.Rect? bounds;
Copy link
Contributor

Choose a reason for hiding this comment

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

why nullable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should we kill HoudiniCanvas until we're ready for a proper investigation? Not sure if it's worth maintaining it with no tests and users.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please go ahead.

Copy link
Contributor

@mdebbar mdebbar left a comment

Choose a reason for hiding this comment

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

This is a great effort! Thank you so much for working on it.

I'm still not done with the review, but wanted to submit the comments I have so far. All my comments are things we can iterate on later, not blockers.

/// This is used to implement operating system specific behavior such as
/// soft keyboards.
OperatingSystem get operatingSystem {
OperatingSystem? get operatingSystem {
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC, this should never return null. debugOperatingSystemOverride is returned only if it's non-null. And _detectOperatingSystem() returns a non-nullable.

Maybe we need the same trick you did in line 45?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

return debugBrowserEngineOverride;
}
return _browserEngine ??= _detectBrowserEngine();
return debugBrowserEngineOverride ?? (_browserEngine ??= _detectBrowserEngine());
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this required to make the analyzer happy? I think it's possible to infer from the old code that the result is always non-nullable. Is this a known issue or should we file one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is to satisfy the type inference. It's a known issue, but not easily solvable. Maybe sealed classes will help one day.


/// The active path in the browser history.
String get path;
String? get path;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason this is nullable? In the HashLocationStrategy it always returns a string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


@override
void pushState(dynamic state, String title, String url) {
void pushState(dynamic state, String title, String? url) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The nullable url here is probably what's causing multiple other things to be nullable (e.g. String? get path, etc). Let's make it non-nullable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


@override
void replaceState(dynamic state, String title, String url) {
void replaceState(dynamic state, String title, String? url) {
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

static PointerBinding? _instance;

static void initInstance(html.Element glassPaneElement) {
static void initInstance(html.Element? glassPaneElement) {
Copy link
Contributor

Choose a reason for hiding this comment

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

glassPaneElement should never be null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

final html.Element? glassPaneElement;
_PointerDataCallback _callback;
PointerDataConverter _pointerDataConverter;
PointerDataConverter? _pointerDataConverter;
Copy link
Contributor

Choose a reason for hiding this comment

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

I couldn't find a situation where this can be null. It's always initialized in the constructor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

static Profiler get instance {
static Profiler? get instance {
Copy link
Contributor

Choose a reason for hiding this comment

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

This never returns null. If _instance is null, it throws.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@visibleForTesting
ParagraphRuler findOrCreateRuler(ParagraphGeometricStyle style) {
ParagraphRuler ruler = _rulers[style];
ParagraphRuler? findOrCreateRuler(ParagraphGeometricStyle style) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In all cases, this function returns a ruler.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

final String? _ellipsis;
final ui.Locale? _locale;

String? get _effectiveFontFamily {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is never null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@yjbanov yjbanov force-pushed the nnbd-migrate-web-engine branch from d9771b6 to 56ea6bb Compare June 22, 2020 16:56

/// Given a [domElement], set attributes that are specific to this input type.
void configureInputMode(html.HtmlElement domElement) {
void configureInputMode(html.HtmlElement? domElement) {
Copy link
Contributor

Choose a reason for hiding this comment

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

When calling this method, dom_element should not be null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

if (operatingSystem == OperatingSystem.iOs ||
operatingSystem == OperatingSystem.android) {
domElement.setAttribute('inputmode', inputmodeAttribute);
domElement!.setAttribute('inputmode', inputmodeAttribute!);
Copy link
Contributor

Choose a reason for hiding this comment

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

dom_element should not be null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

const EngineInputType();

static EngineInputType fromName(String name) {
static EngineInputType fromName(String? name) {
Copy link
Contributor

Choose a reason for hiding this comment

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

name shouldn't be null. It always get defaulted to TextInputType.text on the framework side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@yjbanov
Copy link
Contributor Author

yjbanov commented Jun 23, 2020

I think responded to all the comments (at least those I was able to find). PTAL! This is no longer a draft. While I'm still testing manually, this is good to go.

@yjbanov yjbanov force-pushed the nnbd-migrate-web-engine branch from 231bb98 to a21cf7e Compare June 23, 2020 23:52
}

html.Element prepareAccesibilityPlaceholder() {
html.Element? prepareAccesibilityPlaceholder() {
Copy link
Contributor

Choose a reason for hiding this comment

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

prepareAccesibilityPlaceholder always returns a value so this is never null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@override
html.Element prepareAccesibilityPlaceholder() {
_semanticsPlaceholder = html.Element.tag('flt-semantics-placeholder');
html.Element? prepareAccesibilityPlaceholder() {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is never null (related to the previous comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

AutofillInfo.fromFrameworkMessage(focusedElementAutofill);
final Map<String, html.HtmlElement> elements = <String, html.HtmlElement>{};
final Map<String, AutofillInfo> items = <String, AutofillInfo>{};
final Map<String?, html.HtmlElement> elements = <String?, html.HtmlElement>{};
Copy link
Contributor

Choose a reason for hiding this comment

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

I might be missing something, I don't understand the usage of String? as a map key.

Does that mean the key is nullable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Most of the conversion is syntactic. Prior to null safety everything was nullable. The goal of this PR is to convert to the new syntax (i.e. add ? to what's nullable). Converting to non-null is nice-to-have, but it's not realistic to clean up everything in one PR. I expect most of the actual refactoring will happen in many small PRs following this one.

<StreamSubscription<html.Event>>[];
keys.forEach((String key) {
final html.Element element = elements[key];
keys.forEach((String? key) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same question. Why map can have null keys?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

///
/// Used as id of the text field.
final String uniqueIdentifier;
final String? uniqueIdentifier;
Copy link
Contributor

Choose a reason for hiding this comment

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

If there is autofill info, it means this value is always sent. Therefore this should not be null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

/// in the field.
/// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
final String hint;
final String? hint;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above comment: "If there is autofill info, it means this value is always sent. Therefore this should not be null."

@yjbanov yjbanov force-pushed the nnbd-migrate-web-engine branch from a21cf7e to 8bb4e61 Compare June 24, 2020 18:11
@override
set strokeMiterLimit(double value) {
assert(value != null);
throw UnsupportedError('SurfacePaint.strokeMiterLimit');
Copy link
Contributor

Choose a reason for hiding this comment

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

Please keeps assert and add TODO, there is already an issue i believe. Otherwise it would be breaking change (some apps using it today would start failing).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

(t1 * t1 * y2);
// Expand bounds.
minX = math.min(minX, extremaX);
minX = math.min(minX, extremaX as double);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion change tprime/extremaX/Y to final double and remove as. Here and below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, although it seems as double is a noop in dart2js. It compiles to:

  extremaX = t13 * curX + t14 * cpX + t12 * x2;
  extremaY = t13 * curY + t14 * cpY + t12 * y2;
  minX = Math.min(minX, extremaX);
  maxX = Math.max(maxX, extremaX);
  minY = Math.min(minY, extremaY);
  maxY = Math.max(maxY, extremaY);

);

if (frameSize.isEmpty) {
if (layerTree.frameSize.isEmpty) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this never sets the frameSize on layerTree

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In order to de-null frameSize I changed the initialization sequence a little. frameSize is now final non-null field on LayerTree, so it's guaranteed to be available.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also removed frameSize from Rasterizer as it is available in the LayerTree, which Rasterizer has access to already.

}

@override
double get devicePixelRatio => _debugDevicePixelRatio != null
Copy link
Contributor

Choose a reason for hiding this comment

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

could this be double get devicePixelRatio => _debugDevicePixelRatio ?? browserDevicePixelRatio;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@yjbanov yjbanov force-pushed the nnbd-migrate-web-engine branch from d78ee38 to 43882d0 Compare June 26, 2020 16:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make dart:_engine (web engine implementation) null safe

7 participants