Skip to content

feat(core): Create StaticInjector which does not depend on Reflect polyfill#18496

Closed
mhevery wants to merge 2 commits into
angular:masterfrom
mhevery:no_reflective_injector
Closed

feat(core): Create StaticInjector which does not depend on Reflect polyfill#18496
mhevery wants to merge 2 commits into
angular:masterfrom
mhevery:no_reflective_injector

Conversation

@mhevery
Copy link
Copy Markdown
Contributor

@mhevery mhevery commented Aug 2, 2017

No description provided.

@mhevery mhevery force-pushed the no_reflective_injector branch from fa8f677 to 750f4b7 Compare August 2, 2017 22:39
@vicb vicb added area: core Issues related to the framework runtime action: review The PR is still awaiting reviews from at least one requested reviewer labels Aug 3, 2017
Comment thread packages/core/src/di/injector.ts Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is duplicate signature intended?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@mhevery mhevery force-pushed the no_reflective_injector branch 3 times, most recently from 97fc905 to 5e07e15 Compare August 3, 2017 20:46
@tbosch tbosch added the feature Label used to distinguish feature request from other issues label Aug 3, 2017
@mhevery mhevery force-pushed the no_reflective_injector branch from 5e07e15 to 4158b94 Compare August 3, 2017 23:20
@mhevery mhevery requested a review from tbosch August 3, 2017 23:20
@mhevery mhevery force-pushed the no_reflective_injector branch 2 times, most recently from 85e97a0 to 763a3a9 Compare August 4, 2017 17:47
*
* @stable
*/
export type Provider =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Provider is also used in our decorators (e.g. @NgModule). It seems wrong that it is now defined in a file called reflective_injector_interfaces. I think it should be defined in e.g. di/metadata.ts and then this file can reexport it if we really need this file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

moved back to provider.ts

let value: any = EMPTY;
let useNew: boolean = false;
let provide = resolveForwardRef(provider.provide);
if (USE_VALUE in provider) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why use in here? For all the cases we can just check for e.g. .useFactory !== undefined, and the fall through case is useValue...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would both be faster and remove getClosureSafeProperty

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can't use .useValue since the value could be undefined. So since I started using in with useValue, I am just being consistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed, only USE_VALUE is special

Comment thread packages/core/src/di/injector.ts Outdated
}

interface Records {
[key: string]: Record;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why use an object hash and not a Map directly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed to map

Comment thread packages/core/src/di/injector.ts Outdated

let idCounter = 0;

function getId(obj: any): string {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think using a Map is simpler, and shouldn't be slower AFAIK.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

changed

Comment thread packages/core/src/di/injector.ts Outdated
}
}
if (useNew) {
obj = Object.create(fn.prototype);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This won't work if users run in real ES6 mode.
Consider doing a obj = new fn(...deps).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good catch, fixed

Comment thread packages/core/src/di/injector.ts Outdated
const NG_TOKEN_PATH = 'ngTokenPath';
const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
const ID_EXPANDO = '__symbol_angular_id_' + new Date().getTime() + '__';
const OPT_OPTIONAL = 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider using an enum for the bitmask here and add the suffix Flags (which is what we do everywhere else):

enum OptionFlags {
  Optional = 1 << 0,
  CheckSelf = 1 << 1,
  CheckParent = 1 << 2,
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

if (!(e instanceof Error)) {
e = new Error(e);
}
const path: any[] = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider passing the tokenPathOnError directly to tryResolveToken, so you don't need to patch it unto the error (as it is a private field anyways).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't want to have the cost of tokenPath only in the case of an error. Hence it is patched on Error

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I mean you can just create it as an empty array, and only fill it in case of an error in the various catch clauses. This way, the cost is only to create the empty array, but not to fill it.

Comment thread packages/core/src/di/injector.ts Outdated
if (depRecords.length) {
deps = [];
for (let i = 0; i < depRecords.length; i++) {
const depRecord: DependencyRecord = depRecords[i], options = depRecord.options,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe have separate const statements to clear up the formatting?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment thread packages/core/src/di/injector.ts Outdated
} catch (e) {
const tokenPath: any[] = e[NG_TEMP_TOKEN_PATH];
let message: string = e.message;
message = message && message.charAt(0) === NO_NEW_LINE ? message.substr(1) : '\n' + message;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider adding the logic of adding a newLine or not to staticError

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

Comment thread packages/core/src/di/injector.ts Outdated
let message: string = e.message;
message = message && message.charAt(0) === NO_NEW_LINE ? message.substr(1) : '\n' + message;
const error = staticError(message, tokenPath);
e.message = error.message;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Did you check if this prints nicely in the browser console? AFAIK it does not.
Another solution would be to throw 2 errors: 1 for flow control where it is now, and another one here with the proper message passed to new Error(...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, it looks good in console.


describe('StaticClassProvider', () => {
it('works', () => {
// #docregion StaticClassProvider
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is missing a guide that refers to these new #docregions...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure I understand. It is present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, never mind, I remembered incorrectly how the guides work.

Copy link
Copy Markdown
Contributor

@tbosch tbosch left a comment

Choose a reason for hiding this comment

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

Looks good in general, needs cleanup.

@mhevery mhevery force-pushed the no_reflective_injector branch 3 times, most recently from 5c8714d to fd8649c Compare August 4, 2017 22:27
@mhevery mhevery force-pushed the no_reflective_injector branch from fd8649c to 00f50cb Compare August 4, 2017 23:26
@mhevery mhevery force-pushed the no_reflective_injector branch 2 times, most recently from 86d6f8d to 6fc47e6 Compare August 7, 2017 16:08
@mhevery mhevery added action: merge The PR is ready for merge by the caretaker target: major This PR is targeted for the next major release and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Aug 7, 2017
@vicb
Copy link
Copy Markdown
Contributor

vicb commented Aug 7, 2017

blocked on tap/164481065

@mhevery
Copy link
Copy Markdown
Contributor Author

mhevery commented Aug 7, 2017

@vicb here is the fix http://cl/164500104

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

~~?

This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.

Code savings for HelloWorld using Closure:

Reflective: bundle.js:  105,864(34,190 gzip)
    Static: bundle.js:  154,889(33,555 gzip)
                            645( 2%)

BREAKING CHANGE:

`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.

Example:
Before:
```
[
  MyClass,
  {provide: ClassA, useClass: SubClassA}
]

```

After:
```
[
  {provide: MyClass, deps: [Dep1,...]},
  {provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```

NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides 
declarations.

Benchpress note: Previously Benchpress also supported reflective 
provides, which now require static providers.


DEPRECATION:

- `ReflectiveInjector` is now deprecated as it will be remove. Use
  `Injector.create` as a replacement.
@mhevery mhevery force-pushed the no_reflective_injector branch from 6fc47e6 to 5db475c Compare August 7, 2017 21:55
@vicb vicb closed this in fcadbf4 Aug 7, 2017
vicb added a commit to vicb/angular that referenced this pull request Aug 7, 2017
@trotyl
Copy link
Copy Markdown
Contributor

trotyl commented Aug 8, 2017

Would #13820 be affected by this? Seems StaticInjector does not require @Injectable() anymore.

@sod
Copy link
Copy Markdown
Contributor

sod commented Aug 8, 2017

@Injectable() is just a trick to get typescript to export the typings from the constructor, thanks to emitDecoratorMetadata setting in tsconfig.json. It'll most likley stay.

image

@trotyl
Copy link
Copy Markdown
Contributor

trotyl commented Aug 8, 2017

@sod #13820 is about requiring @Injectable() when there is no dependency, where metadata is not needed here.

@sod
Copy link
Copy Markdown
Contributor

sod commented Aug 8, 2017

Commit message fcadbf4 reads like you are indeed right. They deprecated or even got rid of the reflective resolver altogether.

vicb added a commit that referenced this pull request Aug 8, 2017
asnowwolf pushed a commit to asnowwolf/angular that referenced this pull request Aug 11, 2017
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.

Code savings for HelloWorld using Closure:

Reflective: bundle.js:  105,864(34,190 gzip)
    Static: bundle.js:  154,889(33,555 gzip)
                            645( 2%)

BREAKING CHANGE:

`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.

Example:
Before:
```
[
  MyClass,
  {provide: ClassA, useClass: SubClassA}
]

```

After:
```
[
  {provide: MyClass, deps: [Dep1,...]},
  {provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```

NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.

Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.

DEPRECATION:

- `ReflectiveInjector` is now deprecated as it will be remove. Use
  `Injector.create` as a replacement.

closes angular#18496
asnowwolf pushed a commit to asnowwolf/angular that referenced this pull request Aug 11, 2017
juleskremer pushed a commit to juleskremer/angular that referenced this pull request Aug 26, 2017
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.

Code savings for HelloWorld using Closure:

Reflective: bundle.js:  105,864(34,190 gzip)
    Static: bundle.js:  154,889(33,555 gzip)
                            645( 2%)

BREAKING CHANGE:

`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.

Example:
Before:
```
[
  MyClass,
  {provide: ClassA, useClass: SubClassA}
]

```

After:
```
[
  {provide: MyClass, deps: [Dep1,...]},
  {provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```

NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.

Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.

DEPRECATION:

- `ReflectiveInjector` is now deprecated as it will be remove. Use
  `Injector.create` as a replacement.

closes angular#18496
juleskremer pushed a commit to juleskremer/angular that referenced this pull request Aug 26, 2017
juleskremer pushed a commit to juleskremer/angular that referenced this pull request Aug 28, 2017
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.

Code savings for HelloWorld using Closure:

Reflective: bundle.js:  105,864(34,190 gzip)
    Static: bundle.js:  154,889(33,555 gzip)
                            645( 2%)

BREAKING CHANGE:

`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.

Example:
Before:
```
[
  MyClass,
  {provide: ClassA, useClass: SubClassA}
]

```

After:
```
[
  {provide: MyClass, deps: [Dep1,...]},
  {provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```

NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.

Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.

DEPRECATION:

- `ReflectiveInjector` is now deprecated as it will be remove. Use
  `Injector.create` as a replacement.

closes angular#18496
juleskremer pushed a commit to juleskremer/angular that referenced this pull request Aug 28, 2017
@angular-automatic-lock-bot
Copy link
Copy Markdown

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 Sep 12, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime cla: yes feature Label used to distinguish feature request from other issues target: major This PR is targeted for the next major release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants