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

Investigate why pure_getters had to be re-added #14316

Closed
filipesilva opened this issue Apr 30, 2019 · 5 comments · Fixed by #15607
Closed

Investigate why pure_getters had to be re-added #14316

filipesilva opened this issue Apr 30, 2019 · 5 comments · Fixed by #15607
Assignees
Labels
area: devkit/build-angular freq1: low Only reported by a handful of users who observe it rarely severity3: broken type: bug/fix
Milestone

Comments

@filipesilva
Copy link
Contributor

Followup on #14287

@filipesilva filipesilva added area: devkit/build-angular freq1: low Only reported by a handful of users who observe it rarely severity3: broken type: bug/fix labels Apr 30, 2019
@ngbot ngbot bot added this to the Backlog milestone Apr 30, 2019
@filipesilva filipesilva modified the milestones: Backlog, 8.0 Apr 30, 2019
@kzc
Copy link

kzc commented May 1, 2019

Related issue: angular/angular#21880 (comment)

I'm not an Angular user but I remember looking at this pure_getters issue at the time using the original pre-uglify rollup output from angular/angular#21880 (comment) (which doesn't exist now). Using:

$ terser -V
terser 3.11.0

The minified size with pure_getters enabled:

$ cat ivy-bundle.js | terser -mc pure_getters,passes=3 --define ngDevMode=false | wc -c
    9872

The minified size with pure_getters disabled:

$ cat ivy-bundle.js | terser -mc passes=3 --define ngDevMode=false | wc -c
   24030

Property reads are considered to be side effects with pure_getters disabled. Declaring property reads of IIFE objects at module scope will retain those IIFEs even if they are not otherwise used/referenced.

Here's a workaround to obtain similar minified size with pure_getters disabled. For illustrative purposes and convenience I'm just patching the generated unminified bundle directly. The IIFE property reads are moved into functions to make DCE more effective with pure call annotations:

$ diff -u ivy-bundle.js ivy-bundle-patched.js
--- ivy-bundle.js	2019-05-01 16:22:51.000000000 -0400
+++ ivy-bundle-patched.js	2019-05-01 16:23:02.000000000 -0400
@@ -1957,18 +1957,21 @@
     };
     return ConnectableObservable;
 }(Observable));
-var connectableProto = ConnectableObservable.prototype;
-var connectableObservableDescriptor = {
-    operator: { value: null },
-    _refCount: { value: 0, writable: true },
-    _subject: { value: null, writable: true },
-    _connection: { value: null, writable: true },
-    _subscribe: { value: connectableProto._subscribe },
-    _isComplete: { value: connectableProto._isComplete, writable: true },
-    getSubject: { value: connectableProto.getSubject },
-    connect: { value: connectableProto.connect },
-    refCount: { value: connectableProto.refCount }
-};
+var connectableObservableDescriptor = /*@__PURE__*/ (function(){
+    var connectableProto = ConnectableObservable.prototype;
+    var connectableObservableDescriptor = {
+        operator: { value: null },
+        _refCount: { value: 0, writable: true },
+        _subject: { value: null, writable: true },
+        _connection: { value: null, writable: true },
+        _subscribe: { value: connectableProto._subscribe },
+        _isComplete: { value: connectableProto._isComplete, writable: true },
+        getSubject: { value: connectableProto.getSubject },
+        connect: { value: connectableProto.connect },
+        refCount: { value: connectableProto.refCount }
+    };
+    return connectableObservableDescriptor;
+}());
 var ConnectableSubscriber = /*@__PURE__*/ (function (_super) {
     __extends(ConnectableSubscriber, _super);
     function ConnectableSubscriber(destination, connectable) {
@@ -3070,13 +3073,12 @@
 var USE_VALUE = /*@__PURE__*/ getClosureSafeProperty({ provide: String, useValue: ɵ2 });
 var NG_TOKEN_PATH = 'ngTokenPath';
 var NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
-var NULL_INJECTOR = Injector.NULL;
 var NEW_LINE = /\n/gm;
 var NO_NEW_LINE = 'ɵ';
 var StaticInjector = /*@__PURE__*/ (/*@__PURE__*/ function () {
     function StaticInjector(providers, parent, source) {
         if (parent === void 0) {
-            parent = NULL_INJECTOR;
+            parent = Injector.NULL;
         }
         if (source === void 0) {
             source = null;
@@ -3283,7 +3285,7 @@
                     depRecord.token, childRecord, records, 
                     // If we don't know how to resolve dependency and we should not check parent for it,
                     // than pass in Null injector.
-                    !childRecord && !(options & 4 /* CheckParent */) ? NULL_INJECTOR : parent, options & 1 /* Optional */ ? null : Injector.THROW_IF_NOT_FOUND));
+                    !childRecord && !(options & 4 /* CheckParent */) ? Injector.NULL : parent, options & 1 /* Optional */ ? null : Injector.THROW_IF_NOT_FOUND));
                 }
             }
             record.value = value = useNew ? new ((_a = ((fn))).bind.apply(_a, [void 0].concat(deps)))() : fn.apply(obj, deps);

The size of the minified patched bundle with pure_getters disabled compares favorably to the original pure_getters version:

$ cat ivy-bundle-patched.js | terser -mc passes=3 --define ngDevMode=false | wc -c
    9979

This might be the source code in question:

https://github.com/ReactiveX/rxjs/blob/8c5d8319105415a015e36a58c5c6a34a6dafbf2b/src/internal/observable/ConnectableObservable.ts#L58-L70

https://github.com/angular/angular/blob/7d6f4885b25f36ab5262d868b0a4d762dea27a62/packages/core/src/di/injector.ts#L120

@filipesilva
Copy link
Contributor Author

filipesilva commented May 2, 2019

Whoa @kzc that's amazing work, thank you so much for looking into this!

I was toying with the idea of adding some linter rules to prevent property access on toplevel modules and your analysis further cements that idea really.

@filipesilva
Copy link
Contributor Author

Tried removing all property accesses in the Angular packages and it doesn't look like anything was being retained by accident: angular/angular#30239

@filipesilva
Copy link
Contributor Author

Removing this from the 8.0 milestone. We're removing the toplevel property access in Angular (angular/angular#29329) and RxJs (ReactiveX/rxjs#4769), then checking the pure_getters impact on a few projects, then trying to remove it altogether post 8.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: devkit/build-angular freq1: low Only reported by a handful of users who observe it rarely severity3: broken type: bug/fix
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants