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

fix: "Maximum call stack size exceeded" error in fast components V2 under minify JS. #4969

Closed
gf4t47 opened this issue Jul 20, 2021 · 5 comments · Fixed by #4999
Closed

fix: "Maximum call stack size exceeded" error in fast components V2 under minify JS. #4969

gf4t47 opened this issue Jul 20, 2021 · 5 comments · Fixed by #4999
Assignees
Labels
area:fast-element Pertains to fast-element bug A bug status:needs-investigation Needs additional investigation

Comments

@gf4t47
Copy link

gf4t47 commented Jul 20, 2021

🐛 Bug Report

With the new FAST V2 version, loading FAST components with minify JS causes the below error.
Uncaught RangeError: Maximum call stack size exceeded
Screenshot 2021-07-19 191528

💻 Repro or Code Sample

https://stackblitz.com/edit/angular-ivy-q9599c?file=src%2Fapp%2Fapp.module.ts
I made an Angular repo here to reproduce the issue. I CAN'T make stackblitz to serve on a production build online.
But if you download that package to local, after npm install:

  1. npx ng serve --configuration=production would produce the error.
  2. npx ng serve --configuration=development would load FAST components successfully.

🤔 Expected Behavior

Be able to load FAST components under minification build.

😯 Current Behavior

Only able to load FAST components without JS minification.

main.js:1 Uncaught RangeError: Maximum call stack size exceeded
    at e.get rawValue [as rawValue] (main.js:1)
    at e.resolveRawValue (main.js:1)
    at e.resolveRealValue (main.js:1)
    at e.get value [as value] (main.js:1)
    at Za.watch (main.js:1)
    at Ra.getValue (main.js:1)
    at e.get [as _rawValue] (main.js:1)
    at e.get rawValue [as rawValue] (main.js:1)
    at e.resolveRawValue (main.js:1)
    at e.resolveRealValue (main.js:1)

💁 Possible Solution

  1. FAST components V1 version doesn't have this issue.
  2. Only appear on minifying js.
  3. Not just in Angular project, pure Stenciljs host also has the same behavior.
  4. The last event name that is recursively called a lot is "neutral-palette", seen related to this particular component: https://www.fast.design/docs/api/fast-components.fastdesignsystemprovider.neutralpalette/#fastdesignsystemproviderneutralpalette-property

🔦 Context

We are currently downgrading to FAST V1.

🌍 Your Environment

  • OS & Device: Windows
  • Browser: Google Chrome[91.0.4472.164] && Mozilla FireFox[91.0.4472.164]
@gf4t47 gf4t47 added the status:triage New Issue - needs triage label Jul 20, 2021
@nicholasrice nicholasrice added area:fast-foundation Pertains to fast-foundation bug A bug status:needs-investigation Needs additional investigation labels Jul 26, 2021
@nicholasrice nicholasrice added this to Triage in Architecture via automation Jul 26, 2021
@nicholasrice nicholasrice removed the status:triage New Issue - needs triage label Jul 26, 2021
@EisenbergEffect EisenbergEffect moved this from Triage to To Do in Architecture Jul 26, 2021
@nicholasrice
Copy link
Contributor

nicholasrice commented Jul 26, 2021

This seems to be an issue with fast-element's Observable code. I was above to reduce the above repro to the following:

class Foo {
  @observable foo: number = 1;

  @observable bar: number = 2;
}

const binding = Observable.binding((source: Foo) => source.bar + source.foo);
binding.observe(new Foo(), defaultExecutionContext)

@NgModule({
  imports: [BrowserModule, FormsModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}

@nicholasrice nicholasrice added area:fast-element Pertains to fast-element and removed area:fast-foundation Pertains to fast-foundation labels Jul 26, 2021
@nicholasrice
Copy link
Contributor

This is a bug in Angular 12's code-minification.

The BindingObserver.watch() method is getting converted

from:

watcher = void 0;
const prevValue = prev.propertySource[prev.propertyName];
watcher = this;

to:

(wa = void 0,
wa = this,
e === n.propertySource[n.propertyName] && (this.needsRefresh = !0)

In non-minified code, the observable property prev.propertySource[prev.propertyName] is accessed but no watcher code is executed because the watcher is assigned void 0.

In the minified code, the watcher is assigned void 0 and then immediately re-assigned to this. This causes the prev.propertySource[prev.propertyName] expression to get tracked by Observable, infinitely recursing the BindingObserver.watch() method.

Architecture automation moved this from To Do to Done Jul 27, 2021
@gf4t47
Copy link
Author

gf4t47 commented Jul 27, 2021

Thanks for pushing a fix, even it's a bundle's issue. Appreciated.
It's also interesting the issue is not just on Angular v12 and v11. I also see it on stencils dev host, which uses rollup as its bundle tool, I believe.

@tgoze
Copy link
Contributor

tgoze commented Aug 6, 2021

@gf4t47 I've dealt with this exact issue in Stencil and Angular. This is NOT an Angular or Stencil code minifcation bug, but has to do with Terser JS. Terser JS is used in in both Angular and Stencil prod builds, and both of them have configured Terser to use pure_getters = true. This configuration in Terser makes the assumption that getters produce no side effects - which is inherently untrue in the FAST framework. This assumption changes the way that Terser minifies code and caused the built code that @nicholasrice described. I personally confirmed that pure_getters = false does not cause the issue. I've had to create custom build processes using that config in Terser.

More specifics on the projects:

Angular: put in an issue to Angular back at the end of last year - see angular/angular-cli#19705. One of the devs responded clarifying that they are currently trying to open up the Terser config for Angular builds - see angular/angular-cli#15761.

Stencil: I noticed that prod builds in Stencil have a hard config for Terser as well. Check out this line - https://github.com/ionic-team/stencil/blob/bf5f197910daab7f822a6e4c56f4f40a81c2ce7e/src/compiler/optimize/optimize-module.ts#L103. We've had to do custom builds in Stencil to get this working, and I have no knowledge of them attempting to open up configuration here.

@nicholasrice I really appreciate this fix since it will make our build processes much simpler now. We've been having to work around this for months, so thank you! It's interesting how your fix prevents Terser from doing what it does with pure_getters = true... Not sure how that changes the way Terser makes assumptions, but glad it works!

@nicholasrice
Copy link
Contributor

Thanks for adding that context @tgoze! That makes sense and will be super helpful if this ever regresses in the future.

rajsite added a commit to ni/nimble that referenced this issue Aug 6, 2021
# Pull Request

## 🤨 Rationale

Updated to fast-element 1.4.1 which includes the following change to work with angular optimized builds: microsoft/fast#4969

Also updates the jasmine-core version which was conflicting with peer dependency resolutions. Regenerated the lockfile for latest versions of listed packages. Fixes #18.

## 👩‍💻 Implementation

See above.

## 🧪 Testing

Tested built angular application with optimizations locally in chrome, firefox, and safari.

## ✅ Checklist

- [x] I have updated the project documentation to reflect my changes or determined no changes are needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:fast-element Pertains to fast-element bug A bug status:needs-investigation Needs additional investigation
Projects
Architecture
  
Done
Development

Successfully merging a pull request may close this issue.

3 participants