Skip to content

Conversation

@greglittlefield-wf
Copy link
Contributor

@greglittlefield-wf greglittlefield-wf commented Oct 8, 2025

Motivation

The amount of code generated by the over_react builder is non-negligible, and has some opportunities to be optimized, ultimately reducing the size of compiled dart2js in apps with many components.

Changes

Perform low-hanging optimizations. Note: there are some more potential opportunities for optimization that require more investigation and may involve breakages, which we may revisit later.

Since they affect generated code, changes are easiest to to review commit-by-commit, and the main ones are described below:

  1. 4668279
    1. Remove specialized $PlainMap and $JsMap generated classes, reducing the number of generated concrete props classes from 3 to 1, which translates to similar reductions in the count of dart2js-compiled class representations
      • Note: this does not include generated mixin classes
      • The $JsMap class was originally included as a potential performance optimization when reading/writing props, but there's no evidence that it's helpful, and it's unlikely to be worth the code size increase
    2. Remove workaround for an old DDC bug that required an additional field and constructor body (added in CPLAT-4674: Add workaround for ddc bug via props/state impl constructor #256)
      • This also has the side effect of allowing get$props calls to get inlined, and their results to be reused when reading props
  2. 235add1 Move $isClassGenerated check into an assert so it can be compiled out
  3. edd6bc5 Change aria/dom to getters to optimize dart2js output size
    This way, they don't get compiled as fields, which emit extra code in each props class's dart2js constructor.
  4. 235add1 Move $isClassGenerated check into an assert so it can be compiled out

Effects on build output

This saves ~577 bytes per component (when using -03 --csp --minify), which was calculated by comparing a benchmark component via the benchmark added in #991:

dart run benchmark/dart2js_output.dart compare-code --head='{"git":{"url":"https://github.com/Workiva/over_react.git/","ref":"clean-up-and-optimize-generated-props"}}'

dart2js output changes, obtained by running:

dart run benchmark/dart2js_output.dart compare-code --head='{"git":{"url":"https://github.com/Workiva/over_react.git/","ref":"clean-up-and-optimize-generated-props"}}'

This diff is compares optimized, non-minified dart2js (-03 --csp --no-minify), which has been normalized to allow for better diffing.

Full dart2js diff:
@ -459,3 +459,3 @@
       t#.names = "";
-      if (namedArguments != null && namedArguments.__js_helper$_length !== ###)
+      if (namedArguments != null && namedArguments._length !== ###)
         namedArguments.forEach$###(###, new A.Primitives_functionNoSuchMethod_closure(t#, namedArgumentList, $arguments));
@@ -466,3 +466,3 @@
       if (Array.isArray(positionalArguments))
-        t# = namedArguments == null || namedArguments.__js_helper$_length === ###;
+        t# = namedArguments == null || namedArguments._length === ###;
       else
@@ -510,3 +510,3 @@
       if (t#) {
-        if (namedArguments != null && namedArguments.__js_helper$_length !== ###)
+        if (namedArguments != null && namedArguments._length !== ###)
           return A.Primitives_functionNoSuchMethod($function, $arguments, namedArguments);
@@ -517,3 +517,3 @@
       if (Array.isArray(defaultValues)) {
-        if (namedArguments != null && namedArguments.__js_helper$_length !== ###)
+        if (namedArguments != null && namedArguments._length !== ###)
           return A.Primitives_functionNoSuchMethod($function, $arguments, namedArguments);
@@ -555,3 +555,3 @@
           }
-          if (used !== namedArguments.__js_helper$_length)
+          if (used !== namedArguments._length)
             return A.Primitives_functionNoSuchMethod($function, $arguments, namedArguments);
@@ -1213,3 +1213,3 @@
       var _ = this;
-      _.__js_helper$_length = t#;
+      _._length = t#;
       _._jsObject = t#;
@@ -1288,3 +1288,3 @@
       var _ = this;
-      _.__js_helper$_length = ###;
+      _._length = ###;
       _._last = _._first = _.__js_helper$_rest = _._nums = _._strings = null;
@@ -3266,3 +3266,3 @@
       var _ = this;
-      _.__js_helper$_length = ###;
+      _._length = ###;
       _._last = _._first = _.__js_helper$_rest = _._nums = _._strings = null;
@@ -3276,3 +3276,3 @@
       _._validKey = t#;
-      _.__js_helper$_length = ###;
+      _._length = ###;
       _._last = _._first = _.__js_helper$_rest = _._nums = _._strings = null;
@@ -3672,3 +3672,3 @@
       _._array = t#;
-      _._length = t#;
+      _._html$_length = t#;
       _._position = -###;
@@ -3746,21 +3746,6 @@
     _$ResizeSensor(backingProps) {
-      return backingProps == null ? A._$$ResizeSensorProps$JsMap$(new A.JsBackedMap({})) : A._$$ResizeSensorProps__$$ResizeSensorProps(backingProps);
+      return A._$$ResizeSensorProps$(backingProps);
     },
-    _$$ResizeSensorProps__$$ResizeSensorProps(backingMap) {
-      var t#;
-      if (backingMap instanceof A.JsBackedMap)
-        return A._$$ResizeSensorProps$JsMap$(type$.nullable_JsBackedMap._as(backingMap));
-      else {
-        t# = type$.dynamic;
-        t# = new A._$$ResizeSensorProps$PlainMap(A.LinkedHashMap_LinkedHashMap$_empty(t#, t#), $, $);
-        t#.get$$$isClassGenerated();
-        t#._props = backingMap;
-        return t#;
-      }
-    },
-    _$$ResizeSensorProps$JsMap$(backingMap) {
-      var t# = new A._$$ResizeSensorProps$JsMap(new A.JsBackedMap({}), $, $);
-      t#.get$$$isClassGenerated();
-      t#._props = backingMap == null ? new A.JsBackedMap({}) : backingMap;
-      return t#;
+    _$$ResizeSensorProps$(backingMap) {
+      return new A._$$ResizeSensorProps(backingMap == null ? new A.JsBackedMap({}) : backingMap);
     },
@@ -3776,18 +3761,7 @@
     _$ResizeSensorPropsAccessorsMixin: function _$ResizeSensorPropsAccessorsMixin() {},
-    _$$ResizeSensorProps: function _$$ResizeSensorProps() {},
-    _$$ResizeSensorProps_$getPropKey_closure: function _$$ResizeSensorProps_$getPropKey_closure() {},
-    _$$ResizeSensorProps$PlainMap: function _$$ResizeSensorProps$PlainMap(t#, t#, t#) {
-      var _ = this;
-      _._props = t#;
-      _.componentFactory = null;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_aria_FI = t#;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_dom_FI = t#;
-    },
-    _$$ResizeSensorProps$JsMap: function _$$ResizeSensorProps$JsMap(t#, t#, t#) {
-      var _ = this;
-      _._props = t#;
-      _.componentFactory = null;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_aria_FI = t#;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_dom_FI = t#;
+    _$$ResizeSensorProps: function _$$ResizeSensorProps(t#) {
+      this.props = t#;
+      this.componentFactory = null;
     },
+    _$$ResizeSensorProps_$getPropKey_closure: function _$$ResizeSensorProps_$getPropKey_closure() {},
     _$ResizeSensorComponent: function _$ResizeSensorComponent(t#, t#, t#, t#) {
@@ -3808,13 +3782,8 @@
     DomProps$(componentFactory, props) {
-      var t# = new A.DomProps(componentFactory, props == null ? new A.JsBackedMap({}) : props, $, $);
-      t#.get$$$isClassGenerated();
-      return t#;
+      return new A.DomProps(componentFactory, props == null ? new A.JsBackedMap({}) : props);
     },
-    DomProps: function DomProps(t#, t#, t#, t#) {
-      var _ = this;
-      _.DomProps_componentFactory = t#;
-      _.props = t#;
-      _.componentFactory = null;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_aria_FI = t#;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_dom_FI = t#;
+    DomProps: function DomProps(t#, t#) {
+      this.DomProps_componentFactory = t#;
+      this.props = t#;
+      this.componentFactory = null;
     },
@@ -3873,3 +3842,3 @@
       t# = consumedProps;
-      A.forwardUnconsumedPropsV2(_this.get$props(_this), new A.MappedListIterable(t#, new A.PropsToForward__propsToForward_closure(), A.instanceType(t#)._eval$###("MappedListIterable<###,List<String>>")), false, propsToUpdate);
+      A.forwardUnconsumedPropsV2(_this.props, new A.MappedListIterable(t#, new A.PropsToForward__propsToForward_closure(), A.instanceType(t#)._eval$###("MappedListIterable<###,List<String>>")), false, propsToUpdate);
       return propsToUpdate;
@@ -3969,8 +3938,5 @@
     },
-    GenericUiProps: function GenericUiProps(t#, t#, t#) {
-      var _ = this;
-      _.props = t#;
-      _.componentFactory = null;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_aria_FI = t#;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_dom_FI = t#;
+    GenericUiProps: function GenericUiProps(t#) {
+      this.props = t#;
+      this.componentFactory = null;
     },
@@ -4695,3 +4661,3 @@
     main() {
-      var t#,
+      var t#, t#,
         t# = A.DomProps$($.$get$div(), null);
@@ -4702,27 +4668,13 @@
       t# = $.$get$Foo0().call$###();
-      t# = J.getInterceptor$x(t#);
-      J.$indexSet$ax(t#.get$props(t#), "Foo0Props.foo0", "");
-      J.$indexSet$ax(t#.get$props(t#), "Foo0Props.foo1", "");
-      J.$indexSet$ax(t#.get$props(t#), "Foo0Props.foo2", "");
-      J.$indexSet$ax(t#.get$props(t#), "Foo0Props.foo3", "");
-      J.$indexSet$ax(t#.get$props(t#), "Foo0Props.foo4", "");
+      t# = t#.props;
+      t# = J.getInterceptor$ax(t#);
+      t#.$indexSet(t#, "Foo0Props.foo0", "");
+      t#.$indexSet(t#, "Foo0Props.foo1", "");
+      t#.$indexSet(t#, "Foo0Props.foo2", "");
+      t#.$indexSet(t#, "Foo0Props.foo3", "");
+      t#.$indexSet(t#, "Foo0Props.foo4", "");
       t#.call$###();
     },
-    _$$Foo0Props__$$Foo0Props(backingMap) {
-      var t#;
-      if (backingMap instanceof A.JsBackedMap)
-        return A._$$Foo0Props$JsMap$(type$.nullable_JsBackedMap._as(backingMap));
-      else {
-        t# = type$.dynamic;
-        t# = new A._$$Foo0Props$PlainMap(A.LinkedHashMap_LinkedHashMap$_empty(t#, t#), $, $, $, $, $, $, $);
-        t#.get$$$isClassGenerated();
-        t#._main$_props = backingMap;
-        return t#;
-      }
-    },
-    _$$Foo0Props$JsMap$(backingMap) {
-      var t# = new A._$$Foo0Props$JsMap(new A.JsBackedMap({}), $, $, $, $, $, $, $);
-      t#.get$$$isClassGenerated();
-      t#._main$_props = backingMap == null ? new A.JsBackedMap({}) : backingMap;
-      return t#;
+    _$$Foo0Props$(backingMap) {
+      return new A._$$Foo0Props(backingMap, $, $, $, $, $);
     },
@@ -4734,18 +4686,5 @@
     _$Foo0Config_closure0: function _$Foo0Config_closure0() {},
-    _$$Foo0Props: function _$$Foo0Props() {},
-    _$$Foo0Props$PlainMap: function _$$Foo0Props$PlainMap(t#, t#, t#, t#, t#, t#, t#, t#) {
+    _$$Foo0Props: function _$$Foo0Props(t#, t#, t#, t#, t#, t#) {
       var _ = this;
-      _._main$_props = t#;
-      _.Foo0Props___Foo0Props_foo0_A = t#;
-      _.Foo0Props___Foo0Props_foo1_A = t#;
-      _.Foo0Props___Foo0Props_foo2_A = t#;
-      _.Foo0Props___Foo0Props_foo3_A = t#;
-      _.Foo0Props___Foo0Props_foo4_A = t#;
-      _.componentFactory = null;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_aria_FI = t#;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_dom_FI = t#;
-    },
-    _$$Foo0Props$JsMap: function _$$Foo0Props$JsMap(t#, t#, t#, t#, t#, t#, t#, t#) {
-      var _ = this;
-      _._main$_props = t#;
+      _.props = t#;
       _.Foo0Props___Foo0Props_foo0_A = t#;
@@ -4756,4 +4695,2 @@
       _.componentFactory = null;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_aria_FI = t#;
-      _.UbiquitousDomPropsMixin___UbiquitousDomPropsMixin_dom_FI = t#;
     },
@@ -4858,4 +4795,4 @@
     forwardUnconsumedProps(props, keySetsToOmit, onlyCopyDomProps, propsToUpdate) {
-      var t#, key, t#;
-      for (t# = props.get$props(props), t# = J.get$iterator$ax(t#.get$keys(t#)); t#.moveNext$###();) {
+      var t#, t#, t#, key, t#;
+      for (t# = props.props, t# = J.getInterceptor$x(t#), t# = J.get$iterator$ax(t#.get$keys(t#)); t#.moveNext$###();) {
         key = t#.get$current(t#);
@@ -4866,3 +4803,3 @@
         if (t# || $.$get$_validDomProps().contains$###(###, key)) {
-          t# = props.get$props(props).$index(###, key);
+          t# = t#.$index(t#, key);
           J.$indexSet$ax(propsToUpdate.get$props(propsToUpdate), key, t#);
@@ -6035,3 +5972,3 @@
     get$length(_) {
-      return this.__js_helper$_length;
+      return this._length;
     },
@@ -6114,3 +6051,3 @@
     get$length(_) {
-      return this._getMap$###().__js_helper$_length;
+      return this._getMap$###()._length;
     }
@@ -6299,6 +6236,6 @@
     get$length(_) {
-      return this.__js_helper$_length;
+      return this._length;
     },
     get$isEmpty(_) {
-      return this.__js_helper$_length === ###;
+      return this._length === ###;
     },
@@ -6428,3 +6365,3 @@
         _this._last = _this._last._next = cell;
-      ++_this.__js_helper$_length;
+      ++_this._length;
       _this._modifications = _this._modifications + ### & ###;
@@ -6476,6 +6413,6 @@
     get$length(_) {
-      return this._map.__js_helper$_length;
+      return this._map._length;
     },
     get$isEmpty(_) {
-      return this._map.__js_helper$_length === ###;
+      return this._map._length === ###;
     },
@@ -8920,3 +8857,3 @@
         nextPosition = _this._position + ###,
-        t# = _this._length;
+        t# = _this._html$_length;
       if (nextPosition < t#) {
@@ -9265,3 +9202,3 @@
     get$defaultProps(_) {
-      var t# = A._$$ResizeSensorProps$JsMap$(new A.JsBackedMap({}));
+      var t# = A._$$ResizeSensorProps$(new A.JsBackedMap({}));
       t#.addProps$###($.$get$ResizeSensorPropsMixin_defaultProps());
@@ -9280,3 +9217,3 @@
       t# === $ && A.throwUnnamedLateFieldNI();
-      t# = t#.get$props(t#).$index(###, "ResizeSensorPropsMixin.quickMount");
+      t# = J.$index$asx(t#.props, "ResizeSensorPropsMixin.quickMount");
       t# = A._asBoolQ(t# == null ? null : t#);
@@ -9323,4 +9260,3 @@
       resizeSensor = t#.call$###(expandSensor, collapseSensor);
-      t# = _this.___$ResizeSensorComponent__cachedTypedProps_A;
-      t# = t#.get$props(t#).$index(###, "ResizeSensorPropsMixin.isFlexChild");
+      t# = J.$index$asx(_this.___$ResizeSensorComponent__cachedTypedProps_A.props, "ResizeSensorPropsMixin.isFlexChild");
       t# = A._asBoolQ(t# == null ? _null : t#);
@@ -9330,4 +9266,3 @@
       else {
-        t# = _this.___$ResizeSensorComponent__cachedTypedProps_A;
-        t# = t#.get$props(t#).$index(###, "ResizeSensorPropsMixin.isFlexContainer");
+        t# = J.$index$asx(_this.___$ResizeSensorComponent__cachedTypedProps_A.props, "ResizeSensorPropsMixin.isFlexContainer");
         t# = A._asBoolQ(t# == null ? _null : t#);
@@ -9396,3 +9331,3 @@
       t# === $ && A.throwUnnamedLateFieldNI();
-      t# = t#.get$props(t#).$index(###, "ResizeSensorPropsMixin.onDidReset");
+      t# = J.$index$asx(t#.props, "ResizeSensorPropsMixin.onDidReset");
       if (t# == null)
@@ -9446,5 +9381,2 @@
   A._$$ResizeSensorProps.prototype = {
-    get$$$isClassGenerated() {
-      return true;
-    },
     get$componentFactory() {
@@ -9456,3 +9388,6 @@
     },
-    $isResizeSensorProps: ###
+    $isResizeSensorProps: ###,
+    get$props(receiver) {
+      return this.props;
+    }
   };
@@ -9460,3 +9395,3 @@
     call$###(map) {
-      return A._$$ResizeSensorProps__$$ResizeSensorProps(map);
+      return A._$$ResizeSensorProps$(map);
     },
@@ -9464,12 +9399,2 @@
   };
-  A._$$ResizeSensorProps$PlainMap.prototype = {
-    get$props(_) {
-      return this._props;
-    }
-  };
-  A._$$ResizeSensorProps$JsMap.prototype = {
-    get$props(_) {
-      return this._props;
-    }
-  };
   A._$ResizeSensorComponent.prototype = {
@@ -9477,3 +9402,3 @@
       this.super$Component2$props(###, value);
-      this.___$ResizeSensorComponent__cachedTypedProps_A = A._$$ResizeSensorProps$JsMap$(A.getBackingMap(value));
+      this.___$ResizeSensorComponent__cachedTypedProps_A = A._$$ResizeSensorProps$(A.getBackingMap(value));
     },
@@ -9512,5 +9437,2 @@
   A.DomProps.prototype = {
-    get$$$isClassGenerated() {
-      return true;
-    },
     get$componentFactory() {
@@ -9778,4 +9700,3 @@
     call$###(backingMap) {
-      var t# = new A.GenericUiProps(backingMap == null ? new A.JsBackedMap({}) : backingMap, $, $);
-      t#.get$$$isClassGenerated();
+      var t# = new A.GenericUiProps(backingMap == null ? new A.JsBackedMap({}) : backingMap);
       t#.componentFactory = this.factory;
@@ -9818,5 +9739,2 @@
   A.GenericUiProps.prototype = {
-    get$$$isClassGenerated() {
-      return true;
-    },
     get$props(receiver) {
@@ -10410,12 +10328,14 @@
     call$###(props) {
-      var t#, t#, t#, t#, _null = null,
-        t# = J.$index$asx(props.get$props(props), "Foo0Props.foo0");
+      var t#, t#, t#, _null = null,
+        t# = props.props,
+        t# = J.getInterceptor$asx(t#),
+        t# = t#.$index(t#, "Foo0Props.foo0");
       t# = A._asString(t# == null ? _null : t#);
-      t# = J.$index$asx(props.get$props(props), "Foo0Props.foo1");
+      t# = t#.$index(t#, "Foo0Props.foo1");
       t# = A._asString(t# == null ? _null : t#);
-      t# = J.$index$asx(props.get$props(props), "Foo0Props.foo2");
+      t# = t#.$index(t#, "Foo0Props.foo2");
       t# = A._asString(t# == null ? _null : t#);
-      t# = J.$index$asx(props.get$props(props), "Foo0Props.foo3");
+      t# = t#.$index(t#, "Foo0Props.foo3");
       t# = A._asString(t# == null ? _null : t#);
-      t# = J.$index$asx(props.get$props(props), "Foo0Props.foo4");
+      t# = t#.$index(t#, "Foo0Props.foo4");
       A.print(A._setArrayType([t#, t#, t#, t#, A._asString(t# == null ? _null : t#)], type$.JSArray_String));
@@ -10431,3 +10351,3 @@
     call$###(p) {
-      var t# = J.$index$asx(p.get$props(p), "id");
+      var t# = J.$index$asx(p.props, "id");
       return A._asStringQ(t# == null ? null : t#);
@@ -10439,3 +10359,3 @@
     call$###(map) {
-      return A._$$Foo0Props__$$Foo0Props(map);
+      return A._$$Foo0Props$(map);
     },
@@ -10445,3 +10365,3 @@
     call$###(map) {
-      return A._$$Foo0Props$JsMap$(map);
+      return A._$$Foo0Props$(map);
     },
@@ -10450,14 +10370,4 @@
   A._$$Foo0Props.prototype = {
-    get$$$isClassGenerated() {
-      return true;
-    }
-  };
-  A._$$Foo0Props$PlainMap.prototype = {
-    get$props(_) {
-      return this._main$_props;
-    }
-  };
-  A._$$Foo0Props$JsMap.prototype = {
-    get$props(_) {
-      return this._main$_props;
+    get$props(receiver) {
+      return this.props;
     }
@@ -11053,6 +10963,2 @@
     _inherit(A._$$ResizeSensorProps, A.__$$ResizeSensorProps__$ResizeSensorProps__$ResizeSensorPropsAccessorsMixin);
-    _inheritMany(A._$$ResizeSensorProps,
-      [A._$$ResizeSensorProps$PlainMap,
-      A._$$ResizeSensorProps$JsMap,
-    ]);
     _inherit(A._$ResizeSensorComponent, A.ResizeSensorComponent);
@@ -11094,6 +11000,2 @@
     _inherit(A._$$Foo0Props, A.__$$Foo0Props_UiProps_Foo0Props_$Foo0Props);
-    _inheritMany(A._$$Foo0Props,
-      [A._$$Foo0Props$PlainMap,
-      A._$$Foo0Props$JsMap,
-    ]);
     _mixin(A._NativeTypedArrayOfDouble_NativeTypedArray_ListMixin, A.ListMixin);
@@ -11216,3 +11118,3 @@
   "Version(NavigatorProvider{namePrefix:String})",
-  "_$$Foo0Props$JsMap(JsBackedMap)",
+  "_$$Foo0Props(JsBackedMap)",
   "_$$Foo0Props(Map<@,@>)",
@@ -12265,26 +12167,2 @@
  "_$$Foo0Props": {
-  "Foo0Props": [],
-  "Map": [
-   "@",
-   "@"
-  ],
-  "MapMixin": [
-   "@",
-   "@"
-  ]
- },
- "_$$Foo0Props$JsMap": {
-  "Foo0Props": [],
-  "Map": [
-   "@",
-   "@"
-  ],
-  "MapMixin": [
-   "@",
-   "@"
-  ],
-  "MapMixin.K": "@",
-  "MapMixin.V": "@"
- },
- "_$$Foo0Props$PlainMap": {
   "Foo0Props": [],
@@ -12302,26 +12180,2 @@
  "_$$ResizeSensorProps": {
-  "Map": [
-   "@",
-   "@"
-  ],
-  "MapMixin": [
-   "@",
-   "@"
-  ],
-  "ResizeSensorProps": []
- },
- "_$$ResizeSensorProps$JsMap": {
-  "Map": [
-   "@",
-   "@"
-  ],
-  "MapMixin": [
-   "@",
-   "@"
-  ],
-  "MapMixin.K": "@",
-  "MapMixin.V": "@",
-  "ResizeSensorProps": []
- },
- "_$$ResizeSensorProps$PlainMap": {
   "Map": [
@@ -12692,3 +12546,2 @@
       nullable_Future_Null: findType("Future<Null>?"),
-      nullable_JsBackedMap: findType("JsBackedMap?"),
       nullable_Object: findType("Object?"),
@@ -13044,6 +12897,6 @@
   };
-  Function.prototype.call$###$### = function(a) {
+  Function.prototype.call$### = function(a) {
     return this(a);
   };
-  Function.prototype.call$### = function(a) {
+  Function.prototype.call$###$### = function(a) {
     return this(a);

Release Notes

Review

See CONTRIBUTING.md for more details on review types (+1 / QA +1 / +10) and code review process.

Please review:

QA Checklist

  • Tests were updated and provide good coverage of the changeset and other affected code
  • Manual testing was performed if needed
    • Steps from PR author:
      • CI passes (testing in this repo should be sufficient to guard against potential regressions)
      • As an extra precaution, verify passing internal consumer test run (hit me up for a link)
    • Anything falling under manual testing criteria outlined in CONTRIBUTING.md

Merge Checklist

While we perform many automated checks before auto-merging, some manual checks are needed:

  • A Frontend Frameworks Design member has reviewed these changes
  • There are no unaddressed comments - this check can be automated if reviewers use the "Request Changes" feature
  • For release PRs - Version metadata in Rosie comment is correct

The JsBackedMap specialized subclass was implemented to potentially improve performance
by allowing dart2js to inline/optimize backing map operations, but from inspecting
compiled code, it doesn't seem to have much of an effect.

These two specialized subclasses result in a significant amount of extra compiled code,
so removing it at this time is worth the tradeoff.

This commit also removes workarounds for an old DDC bug.
This way, they don't get compiled as fields, which emit extra code
in each props class's constructor.

In the future, if we wanted to reuse these objects for successive
calls in builders, we could use an expando instead.
@greglittlefield-wf greglittlefield-wf changed the title Optimize generated code size FED-4215 Low-hanging dart2js code side optimizations Dec 3, 2025
@greglittlefield-wf greglittlefield-wf marked this pull request as ready for review December 3, 2025 19:28
Copy link
Contributor

@kealjones-wk kealjones-wk left a comment

Choose a reason for hiding this comment

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

one comment for posterity

Comment on lines +151 to 155
_$$ErrorBoundaryState typedStateFactoryJs(JsBackedMap? backingMap) =>
_$$ErrorBoundaryState(backingMap);

@override
_$$ErrorBoundaryState typedStateFactory(Map? backingMap) =>
Copy link
Contributor

@kealjones-wk kealjones-wk Dec 8, 2025

Choose a reason for hiding this comment

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

For posterity sake:

The only thing that stuck out to me was another possible optimization maybe? typed[State|Props]Factory and typed[State|Props]FactoryJs are identical implementations.

After talking with greg in person about this he validated that the generated JS code does in fact double generate this in js as well, we were hoping it might optimize it out but no luck. However @greglittlefield-wf believes he might be able to do something to remove the need for both.

All other changes seem really good and very beneficial. I am still reviewing a bit more but so far looking good.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another thought came to mind when reviewing the generated JS thats semi related to the above.

return A.UiFactoryConfig$("Foo2", A.PropsFactory$(new A._$Foo2Config_closure(), new A._$Foo2Config_closure0(), t#), t#);

Because tyepedPropsFactory is used within PropsFactory (used twice, once for map and once for jsMap) another possible gain could be making the jsMap argument optional allowing us to remove the second instantiation of the same props class.

@greglittlefield-wf if this comment and the one above are actually a big lift. these should not hold up this PR. This is good work already as is so we could add these to another follow up PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ooh good ideas! I took a look and these won't be too bad, but will be non-trivial to implement, so I'll make a ticket to follow up

kealjones-wk
kealjones-wk previously approved these changes Dec 8, 2025
Copy link
Contributor

@kealjones-wk kealjones-wk left a comment

Choose a reason for hiding this comment

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

+1
Generation changes look good and dart2js generation looks good. Waiting on wdesk branch using these changes CI to pass for QA.

@greglittlefield-wf greglittlefield-wf force-pushed the clean-up-and-optimize-generated-props branch from 66c17c0 to de8bcce Compare December 8, 2025 22:22
Copy link
Contributor

@kealjones-wk kealjones-wk left a comment

Choose a reason for hiding this comment

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

+10

@greglittlefield-wf
Copy link
Contributor Author

@Workiva/release-management-p

Copy link

@rmconsole-wf rmconsole-wf left a comment

Choose a reason for hiding this comment

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

+1 from RM

@btr-rmconsole-1 btr-rmconsole-1 bot merged commit 57004a7 into master Dec 9, 2025
17 checks passed
@btr-rmconsole-1 btr-rmconsole-1 bot deleted the clean-up-and-optimize-generated-props branch December 9, 2025 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants