Skip to content

Generate fast change detector classes in Dart code - #1932

Merged
kegluneq merged 2 commits into
angular:masterfrom
kegluneq:dart_change_detectors_2
May 29, 2015
Merged

kegluneq merged 2 commits into
angular:masterfrom
kegluneq:dart_change_detectors_2

Conversation

@kegluneq

Copy link
Copy Markdown

/cc @tbosch

I haven't addressed unit tests yet, but wanted to get this to you early in case you have cleanup suggestions for me.

Thanks!

@tbosch

tbosch commented May 15, 2015

Copy link
Copy Markdown
Contributor

+1 for reusing the existing code template of JitChangeDetector!

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch from f2df53a to 167e022 Compare May 16, 2015 04:31
@kegluneq

Copy link
Copy Markdown
Author

Whoops, I updated this before seeing you comment to diverge from the pattern in the js change detection, but I'd like to lobby for switching the Js code to a more consolidated template also. What do you think @vsavkin ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm particularly curious if this change to looseIdentical maintains the intended behavior.

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch 2 times, most recently from 219f2cb to 2a72195 Compare May 18, 2015 20:57

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Looks like using looseIdentical inside an eval isn't going to work, which makes sense so I reverted the previous change.

However, I think the original line is equivalent to newValue !== oldValue:

For all values a, a === a is true unless isNaN(a)
So (newValue !== newValue) && (oldValue !== oldValue) is equivalent to isNaN(newValue) && isNaN(oldValue), however
isNaN(newValue) && isNaN(oldValue) implies (newValue !== oldValue)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(newValue !== newValue) && (oldValue !== oldValue) is equivalent to isNaN(newValue) && isNaN(oldValue)

This is not exactly true, because (the global) isNaN() will try to convert the input to number first; e.g.:
isNaN('123') === false, but isNaN('foo') === true.
This will be true for ES6's Number.isNaN(); e.g. (newValue !== newValue) <=> Number.isNaN(newValue) === true
Not that it makes any difference here :) Just saying...

Without having any idea what this code does, I wonder if the previous versions intention was:

// If `newValue` is different than `oldValue` (in terms of !==) AND not both are `NaN`
if (${newValue} !== ${oldValue} && !((${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue}))) {

(If that is the case, then if (${newValue} !== ${oldValue}) { is not a propser fix :))

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 am not sure why it is there, and there is no unit test checking this behavior. Let's remove it.

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch from ae34cf4 to 01a0e64 Compare May 19, 2015 17:30
@vsavkin

vsavkin commented May 19, 2015

Copy link
Copy Markdown
Contributor

Did you figure out a strategy of testing generated detectors? We need to be sure that our change detection system are in sync.

@kegluneq

Copy link
Copy Markdown
Author

Did you have thoughts on the code layout in change_detection_codegen.dart? Would you be okay with me updating the Js version to flatten out the class template in a similar way?

Re: testing, what I'm currently thinking is:

  1. Create a serialization format for ChangeDetectorDefinition
  2. Refactor current tests to extract those definitions
  3. Create a gulp task that generates change detectors from the extracted definitions
  4. Create a test mode that uses those generated classes to run the tests.

What do you think?

@vsavkin

vsavkin commented May 19, 2015

Copy link
Copy Markdown
Contributor

Refactor current tests to extract those definitions

This sounds good. Can you do it for just one simple test? So we can review it asap?

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch from ec50cba to 86affb1 Compare May 19, 2015 23:15
@kegluneq

Copy link
Copy Markdown
Author

Just pushed updates to the JITGenerator & Dart Codegen class to make them parallel each other.

If you're okay with it, I'd like to get this checked in before starting to integrate the unit tests. Since the Dart change detectors aren't actually being used anywhere yet it shouldn't cause any problems, and the refactoring to integrate the two feels like it should be a separate commit to me.

@vicb

vicb commented May 20, 2015

Copy link
Copy Markdown
Contributor

@kegluneq #1983 has been merged into master, it introduces operator === & !==. Should the Dart codegen be updated to translate this to (!)identical(left, right) ?

@kegluneq

Copy link
Copy Markdown
Author

We should probably use looseIdentical (link) to ensure good behavior with strings.

Are there updates that need to be made to this PR or will edits in ChangeDetectionUtil cover this support?

@naomiblack naomiblack added this to the M9: TypeScript and Build Stability milestone May 20, 2015
@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch from f454361 to b1c5305 Compare May 21, 2015 18:03

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is all generated using values from simple_watch_config.json by executing pub run gen_change_detectors.dart <path_to_simple_watch_config.json>.

Ideally we would not check this in, but it would be generated by gulp.

@kegluneq

Copy link
Copy Markdown
Author

The actual change detector classes are being generated manually via a tool and the CREATE_ME gulp task needs to be cleanly integrated, but test.unit.js/ci and test.unit.dart/ci are running the tests and passing now -- we're getting close!

We can either try to automate generating the change detector classes or we can manually re-generate and check them in whenever the change detector tests are updated. I don't particularly like either option, but of them I'd vote for manually running -- automating this seems like it could get a little too clever.

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch from b1c5305 to 58b6dfa Compare May 21, 2015 20:04
@tbosch

tbosch commented May 22, 2015

Copy link
Copy Markdown
Contributor

FYI, we will update clang-format to a new version today (see #2059). So before this PR gets rebaed ontop of master, please do a local clang-format with the newest clang-format locally to minimize merge conflicts.

@kegluneq

Copy link
Copy Markdown
Author

ACK for the clang-format

@vsavkin and I just discussed this and our plan for landing is

  1. Code generation will not be part of the standard gulp pipeline, it will be run manually.
  2. Generated code will be checked into a generated/ subdirectory.
  3. Add a test to gulp test.unit.dart/ci that will fail if the generated/ files are out of date and output the command necessary to re-generate (like gulp check-format)
  4. profit

This requires the least amount of magic and should allow anybody not updating the change_detector code to completely ignore the generated code.

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch 2 times, most recently from 69e3ce8 to c1036ae Compare May 25, 2015 19:10
@IgorMinar

Copy link
Copy Markdown
Contributor

I spoke with @vsavkin to get a bit more context and we agreed that it would be good to make the following changes:

  • don't generate tests via a template, but instead use the json file to refer to the expression from hand written tests. This way, we don't need to worry about misgenerating tests (or not generating them at all) and we'd improve the debugging experience as well. Additionally, since many other tests can't be simply generated, we'll need to do this anyway, so we might as well do it for all tests rather than have two different code paths.
  • don't store generated change detectors in the repo, but instead generate them during each build. This will ensure that we don't have stale code in the repo and that the codegen build tasks don't bit rot.

@kegluneq

Copy link
Copy Markdown
Author

Okay, after discussing with @IgorMinar and @vsavkin this morning, here is the approach I'm thinking of:

Remove test generation via template & the JSON file. Test configuration is stored in a .ts file whose responsibility is to take a changeDetectorName: String and return a ChangeDetectorDescription object. For our simple watch checks, the name will simply be the constant expression we are evaluating. For more complex scenarios this will describe the feature we are testing.

Add a file defining a top-level function for each of dynamic, JIT, and PreGenerated that takes a ChangeDetectorDefinition and returns a ProtoChangeDetector. For dynamic and JIT these will be static and straightforward. For PreGenerated this will essentially map ChangeDetectorDescription#id to the appropriate generated ChangeDetector class.

In the tests, it will look like:

import {getDefinition} from './config';
import {createProtoChangeDetector} as dynamic from './dynamic';
import {createProtoChangeDetector} as jit from './jit';
import {createProtoChangeDetector} as pregen from './generated/simple_watch_classes';

function getProtoChangeDetector(cdType: string, def: ChangeDetectorDefinition) {
  switch(cdType) {
    case 'dynamic':
      return dynamic.createProtoChangeDetector(changeDetectorDef);
    // same for 'jit' and 'pregen'
  }
}

['dynamic', 'jit', 'pregen'].forEach((cdType) => {
  describe(`${cdType} Change Detector'), () => {
    it('should work for addition', () => {
      var changeDetectorDef = getDefinition('10 + 5');
      var changeDetector = getProtoChangeDetector(cdType, changeDetectorDef)
        .instantiate(dispatcher);
      expect(testAction(changeDetector)).toEqual('15');
    });
  });
});

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch 2 times, most recently from 384431c to e4244ec Compare May 28, 2015 01:08
@kegluneq

Copy link
Copy Markdown
Author

@vsavkin , @IgorMinar I'm still not auto-generating the change detectors on each build but the test changes are done.

Please let me know if you have any issues with the approach.

Comment thread modules/angular2/src/test_lib/utils.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.

Can you move this into the change detector module?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ah, good catch - this is no longer needed. I'll remove it.

@IgorMinar

Copy link
Copy Markdown
Contributor

I looked through the PR, the test changes look good to me. We still need to regenerate the detectors within the build. I'm not an expert on dart generators so I skipped that part.

I noticed that the expression definition is now in the TS file. how do you plan to extract the generators from there?

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch 2 times, most recently from 5d880ef to 7ea6f8c Compare May 28, 2015 21:47
@kegluneq

Copy link
Copy Markdown
Author

@IgorMinar , @vsavkin , the change detector build is now integrated into build/packages.dart and all tests are passing (locally, anyway).

PTAL

@IgorMinar

Copy link
Copy Markdown
Contributor

the gulp related bits lgtm. we'll clean it up more as we cleanup the gulpfile but this is good enough for now. thanks!

@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch 2 times, most recently from b902195 to b3f8d76 Compare May 29, 2015 20:25
@mhevery mhevery modified the milestone: M9: TypeScript and Build Stability May 29, 2015
@kegluneq

Copy link
Copy Markdown
Author

fyi, I have @IgorMinar signing off on gulp above & @vsavkin verbally gave the lgtm on the change detector code.

35f0ee5 introduced an issue that was causing transformer tests to fail -- fixing now & i'll submit once Travis is happy.

Tim Blasi added 2 commits May 29, 2015 14:48
Use the `ProtoViewDto` created by the render `Compiler` to create a
`ChangeDetectorDefinition`.

From there, generate a subclass of `AbstractChangeDetector` for each
`ChangeDetectorDefinition`.

Run some basic unit tests for the dynamic and JIT change detectors on
pre-generated change detectors.
@kegluneq
kegluneq force-pushed the dart_change_detectors_2 branch from 3159c7b to 000a8e2 Compare May 29, 2015 21:57
@kegluneq
kegluneq merged commit 000a8e2 into angular:master May 29, 2015
@kegluneq
kegluneq deleted the dart_change_detectors_2 branch May 29, 2015 22:03
@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 6, 2019
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.

9 participants