1- import { isPresent , isBlank , looseIdentical , StringWrapper } from 'angular2/src/core/facade/lang' ;
1+ import { isPresent , isBlank , looseIdentical } from 'angular2/src/core/facade/lang' ;
22import { ListWrapper , Map } from 'angular2/src/core/facade/collection' ;
33import { RecordType , ProtoRecord } from './proto_record' ;
44
@@ -13,70 +13,163 @@ import {RecordType, ProtoRecord} from './proto_record';
1313 *
1414 * @internal
1515 */
16- export function coalesce ( records : ProtoRecord [ ] ) : ProtoRecord [ ] {
17- var res : ProtoRecord [ ] = [ ] ;
18- var indexMap : Map < number , number > = new Map < number , number > ( ) ;
16+ export function coalesce ( srcRecords : ProtoRecord [ ] ) : ProtoRecord [ ] {
17+ let dstRecords = [ ] ;
18+ let excludedIdxs = [ ] ;
19+ let indexMap : Map < number , number > = new Map < number , number > ( ) ;
20+ let skipDepth = 0 ;
21+ let skipSources : ProtoRecord [ ] = ListWrapper . createFixedSize ( srcRecords . length ) ;
1922
20- for ( var i = 0 ; i < records . length ; ++ i ) {
21- var r = records [ i ] ;
22- var record = _replaceIndices ( r , res . length + 1 , indexMap ) ;
23- var matchingRecord = _findMatching ( record , res ) ;
23+ for ( let protoIndex = 0 ; protoIndex < srcRecords . length ; protoIndex ++ ) {
24+ let skipRecord = skipSources [ protoIndex ] ;
25+ if ( isPresent ( skipRecord ) ) {
26+ skipDepth -- ;
27+ skipRecord . fixedArgs [ 0 ] = dstRecords . length ;
28+ }
2429
25- if ( isPresent ( matchingRecord ) && record . lastInBinding ) {
26- res . push ( _selfRecord ( record , matchingRecord . selfIndex , res . length + 1 ) ) ;
27- indexMap . set ( r . selfIndex , matchingRecord . selfIndex ) ;
28- matchingRecord . referencedBySelf = true ;
30+ let src = srcRecords [ protoIndex ] ;
31+ let dst = _cloneAndUpdateIndexes ( src , dstRecords , indexMap ) ;
2932
30- } else if ( isPresent ( matchingRecord ) && ! record . lastInBinding ) {
31- if ( record . argumentToPureFunction ) {
32- matchingRecord . argumentToPureFunction = true ;
33+ if ( dst . isSkipRecord ( ) ) {
34+ dstRecords . push ( dst ) ;
35+ skipDepth ++ ;
36+ skipSources [ dst . fixedArgs [ 0 ] ] = dst ;
37+ } else {
38+ let record = _mayBeAddRecord ( dst , dstRecords , excludedIdxs , skipDepth > 0 ) ;
39+ indexMap . set ( src . selfIndex , record . selfIndex ) ;
40+ }
41+ }
42+
43+ return _optimizeSkips ( dstRecords ) ;
44+ }
45+
46+ /**
47+ * - Conditional skip of 1 record followed by an unconditional skip of N are replaced by a
48+ * conditional skip of N with the negated condition,
49+ * - Skips of 0 records are removed
50+ */
51+ function _optimizeSkips ( srcRecords : ProtoRecord [ ] ) : ProtoRecord [ ] {
52+ let dstRecords = [ ] ;
53+ let skipSources = ListWrapper . createFixedSize ( srcRecords . length ) ;
54+ let indexMap : Map < number , number > = new Map < number , number > ( ) ;
55+
56+ for ( let protoIndex = 0 ; protoIndex < srcRecords . length ; protoIndex ++ ) {
57+ let skipRecord = skipSources [ protoIndex ] ;
58+ if ( isPresent ( skipRecord ) ) {
59+ skipRecord . fixedArgs [ 0 ] = dstRecords . length ;
60+ }
61+
62+ let src = srcRecords [ protoIndex ] ;
63+
64+ if ( src . isSkipRecord ( ) ) {
65+ if ( src . isConditionalSkipRecord ( ) && src . fixedArgs [ 0 ] === protoIndex + 2 &&
66+ protoIndex < srcRecords . length - 1 &&
67+ srcRecords [ protoIndex + 1 ] . mode === RecordType . SkipRecords ) {
68+ src . mode = src . mode === RecordType . SkipRecordsIf ? RecordType . SkipRecordsIfNot :
69+ RecordType . SkipRecordsIf ;
70+ src . fixedArgs [ 0 ] = srcRecords [ protoIndex + 1 ] . fixedArgs [ 0 ] ;
71+ protoIndex ++ ;
3372 }
3473
35- indexMap . set ( r . selfIndex , matchingRecord . selfIndex ) ;
74+ if ( src . fixedArgs [ 0 ] > protoIndex + 1 ) {
75+ let dst = _cloneAndUpdateIndexes ( src , dstRecords , indexMap ) ;
76+ dstRecords . push ( dst ) ;
77+ skipSources [ dst . fixedArgs [ 0 ] ] = dst ;
78+ }
3679
3780 } else {
38- res . push ( record ) ;
39- indexMap . set ( r . selfIndex , record . selfIndex ) ;
81+ let dst = _cloneAndUpdateIndexes ( src , dstRecords , indexMap ) ;
82+ dstRecords . push ( dst ) ;
83+ indexMap . set ( src . selfIndex , dst . selfIndex ) ;
4084 }
4185 }
4286
43- return res ;
87+ return dstRecords ;
4488}
4589
46- function _selfRecord ( r : ProtoRecord , contextIndex : number , selfIndex : number ) : ProtoRecord {
47- return new ProtoRecord ( RecordType . Self , "self" , null , [ ] , r . fixedArgs , contextIndex ,
48- r . directiveIndex , selfIndex , r . bindingRecord , r . lastInBinding ,
49- r . lastInDirective , false , false , r . propertyBindingIndex ) ;
90+ /**
91+ * Add a new record or re-use one of the existing records.
92+ */
93+ function _mayBeAddRecord ( record : ProtoRecord , dstRecords : ProtoRecord [ ] , excludedIdxs : number [ ] ,
94+ excluded : boolean ) : ProtoRecord {
95+ let match = _findFirstMatch ( record , dstRecords , excludedIdxs ) ;
96+
97+ if ( isPresent ( match ) ) {
98+ if ( record . lastInBinding ) {
99+ dstRecords . push ( _createSelfRecord ( record , match . selfIndex , dstRecords . length + 1 ) ) ;
100+ match . referencedBySelf = true ;
101+ } else {
102+ if ( record . argumentToPureFunction ) {
103+ match . argumentToPureFunction = true ;
104+ }
105+ }
106+
107+ return match ;
108+ }
109+
110+ if ( excluded ) {
111+ excludedIdxs . push ( record . selfIndex ) ;
112+ }
113+
114+ dstRecords . push ( record ) ;
115+ return record ;
50116}
51117
52- function _findMatching ( r : ProtoRecord , rs : ProtoRecord [ ] ) {
118+ /**
119+ * Returns the first `ProtoRecord` that matches the record.
120+ */
121+ function _findFirstMatch ( record : ProtoRecord , dstRecords : ProtoRecord [ ] ,
122+ excludedIdxs : number [ ] ) : ProtoRecord {
53123 return ListWrapper . find (
54- rs , ( rr ) => rr . mode !== RecordType . DirectiveLifecycle && _sameDirIndex ( rr , r ) &&
55- rr . mode === r . mode && looseIdentical ( rr . funcOrValue , r . funcOrValue ) &&
56- rr . contextIndex === r . contextIndex && StringWrapper . equals ( rr . name , r . name ) &&
57- ListWrapper . equals ( rr . args , r . args ) ) ;
124+ dstRecords ,
125+ // TODO(vicb): optimize notReusableIndexes.indexOf (sorted array)
126+ rr => excludedIdxs . indexOf ( rr . selfIndex ) == - 1 && rr . mode !== RecordType . DirectiveLifecycle &&
127+ _haveSameDirIndex ( rr , record ) && rr . mode === record . mode &&
128+ looseIdentical ( rr . funcOrValue , record . funcOrValue ) &&
129+ rr . contextIndex === record . contextIndex && looseIdentical ( rr . name , record . name ) &&
130+ ListWrapper . equals ( rr . args , record . args ) ) ;
58131}
59132
60- function _sameDirIndex ( a : ProtoRecord , b : ProtoRecord ) : boolean {
61- var di1 = isBlank ( a . directiveIndex ) ? null : a . directiveIndex . directiveIndex ;
62- var ei1 = isBlank ( a . directiveIndex ) ? null : a . directiveIndex . elementIndex ;
133+ /**
134+ * Clone the `ProtoRecord` and changes the indexes for the ones in the destination array for:
135+ * - the arguments,
136+ * - the context,
137+ * - self
138+ */
139+ function _cloneAndUpdateIndexes ( record : ProtoRecord , dstRecords : ProtoRecord [ ] ,
140+ indexMap : Map < number , number > ) : ProtoRecord {
141+ let args = record . args . map ( src => _srcToDstSelfIndex ( indexMap , src ) ) ;
142+ let contextIndex = _srcToDstSelfIndex ( indexMap , record . contextIndex ) ;
143+ let selfIndex = dstRecords . length + 1 ;
63144
64- var di2 = isBlank ( b . directiveIndex ) ? null : b . directiveIndex . directiveIndex ;
65- var ei2 = isBlank ( b . directiveIndex ) ? null : b . directiveIndex . elementIndex ;
145+ return new ProtoRecord ( record . mode , record . name , record . funcOrValue , args , record . fixedArgs ,
146+ contextIndex , record . directiveIndex , selfIndex , record . bindingRecord ,
147+ record . lastInBinding , record . lastInDirective ,
148+ record . argumentToPureFunction , record . referencedBySelf ,
149+ record . propertyBindingIndex ) ;
150+ }
66151
67- return di1 === di2 && ei1 === ei2 ;
152+ /**
153+ * Returns the index in the destination array corresponding to the index in the src array.
154+ * When the element is not present in the destination array, return the source index.
155+ */
156+ function _srcToDstSelfIndex ( indexMap : Map < number , number > , srcIdx : number ) : number {
157+ var dstIdx = indexMap . get ( srcIdx ) ;
158+ return isPresent ( dstIdx ) ? dstIdx : srcIdx ;
68159}
69160
70- function _replaceIndices ( r : ProtoRecord , selfIndex : number , indexMap : Map < any , any > ) {
71- var args = r . args . map ( a => _map ( indexMap , a ) ) ;
72- var contextIndex = _map ( indexMap , r . contextIndex ) ;
73- return new ProtoRecord ( r . mode , r . name , r . funcOrValue , args , r . fixedArgs , contextIndex ,
161+ function _createSelfRecord ( r : ProtoRecord , contextIndex : number , selfIndex : number ) : ProtoRecord {
162+ return new ProtoRecord ( RecordType . Self , "self" , null , [ ] , r . fixedArgs , contextIndex ,
74163 r . directiveIndex , selfIndex , r . bindingRecord , r . lastInBinding ,
75- r . lastInDirective , r . argumentToPureFunction , r . referencedBySelf ,
76- r . propertyBindingIndex ) ;
164+ r . lastInDirective , false , false , r . propertyBindingIndex ) ;
77165}
78166
79- function _map ( indexMap : Map < any , any > , value : number ) {
80- var r = indexMap . get ( value ) ;
81- return isPresent ( r ) ? r : value ;
167+ function _haveSameDirIndex ( a : ProtoRecord , b : ProtoRecord ) : boolean {
168+ var di1 = isBlank ( a . directiveIndex ) ? null : a . directiveIndex . directiveIndex ;
169+ var ei1 = isBlank ( a . directiveIndex ) ? null : a . directiveIndex . elementIndex ;
170+
171+ var di2 = isBlank ( b . directiveIndex ) ? null : b . directiveIndex . directiveIndex ;
172+ var ei2 = isBlank ( b . directiveIndex ) ? null : b . directiveIndex . elementIndex ;
173+
174+ return di1 === di2 && ei1 === ei2 ;
82175}
0 commit comments