UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

144 lines 6.3 kB
import { isPresent, isBlank, looseIdentical } from 'angular2/src/facade/lang'; import { ListWrapper, Map } from 'angular2/src/facade/collection'; import { RecordType, ProtoRecord } from './proto_record'; /** * Removes "duplicate" records. It assumes that record evaluation does not have side-effects. * * Records that are not last in bindings are removed and all the indices of the records that depend * on them are updated. * * Records that are last in bindings CANNOT be removed, and instead are replaced with very cheap * SELF records. * * @internal */ export function coalesce(srcRecords) { let dstRecords = []; let excludedIdxs = []; let indexMap = new Map(); let skipDepth = 0; let skipSources = ListWrapper.createFixedSize(srcRecords.length); for (let protoIndex = 0; protoIndex < srcRecords.length; protoIndex++) { let skipRecord = skipSources[protoIndex]; if (isPresent(skipRecord)) { skipDepth--; skipRecord.fixedArgs[0] = dstRecords.length; } let src = srcRecords[protoIndex]; let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap); if (dst.isSkipRecord()) { dstRecords.push(dst); skipDepth++; skipSources[dst.fixedArgs[0]] = dst; } else { let record = _mayBeAddRecord(dst, dstRecords, excludedIdxs, skipDepth > 0); indexMap.set(src.selfIndex, record.selfIndex); } } return _optimizeSkips(dstRecords); } /** * - Conditional skip of 1 record followed by an unconditional skip of N are replaced by a * conditional skip of N with the negated condition, * - Skips of 0 records are removed */ function _optimizeSkips(srcRecords) { let dstRecords = []; let skipSources = ListWrapper.createFixedSize(srcRecords.length); let indexMap = new Map(); for (let protoIndex = 0; protoIndex < srcRecords.length; protoIndex++) { let skipRecord = skipSources[protoIndex]; if (isPresent(skipRecord)) { skipRecord.fixedArgs[0] = dstRecords.length; } let src = srcRecords[protoIndex]; if (src.isSkipRecord()) { if (src.isConditionalSkipRecord() && src.fixedArgs[0] === protoIndex + 2 && protoIndex < srcRecords.length - 1 && srcRecords[protoIndex + 1].mode === RecordType.SkipRecords) { src.mode = src.mode === RecordType.SkipRecordsIf ? RecordType.SkipRecordsIfNot : RecordType.SkipRecordsIf; src.fixedArgs[0] = srcRecords[protoIndex + 1].fixedArgs[0]; protoIndex++; } if (src.fixedArgs[0] > protoIndex + 1) { let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap); dstRecords.push(dst); skipSources[dst.fixedArgs[0]] = dst; } } else { let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap); dstRecords.push(dst); indexMap.set(src.selfIndex, dst.selfIndex); } } return dstRecords; } /** * Add a new record or re-use one of the existing records. */ function _mayBeAddRecord(record, dstRecords, excludedIdxs, excluded) { let match = _findFirstMatch(record, dstRecords, excludedIdxs); if (isPresent(match)) { if (record.lastInBinding) { dstRecords.push(_createSelfRecord(record, match.selfIndex, dstRecords.length + 1)); match.referencedBySelf = true; } else { if (record.argumentToPureFunction) { match.argumentToPureFunction = true; } } return match; } if (excluded) { excludedIdxs.push(record.selfIndex); } dstRecords.push(record); return record; } /** * Returns the first `ProtoRecord` that matches the record. */ function _findFirstMatch(record, dstRecords, excludedIdxs) { return dstRecords.find( // TODO(vicb): optimize excludedIdxs.indexOf (sorted array) rr => excludedIdxs.indexOf(rr.selfIndex) == -1 && rr.mode !== RecordType.DirectiveLifecycle && _haveSameDirIndex(rr, record) && rr.mode === record.mode && looseIdentical(rr.funcOrValue, record.funcOrValue) && rr.contextIndex === record.contextIndex && looseIdentical(rr.name, record.name) && ListWrapper.equals(rr.args, record.args)); } /** * Clone the `ProtoRecord` and changes the indexes for the ones in the destination array for: * - the arguments, * - the context, * - self */ function _cloneAndUpdateIndexes(record, dstRecords, indexMap) { let args = record.args.map(src => _srcToDstSelfIndex(indexMap, src)); let contextIndex = _srcToDstSelfIndex(indexMap, record.contextIndex); let selfIndex = dstRecords.length + 1; return new ProtoRecord(record.mode, record.name, record.funcOrValue, args, record.fixedArgs, contextIndex, record.directiveIndex, selfIndex, record.bindingRecord, record.lastInBinding, record.lastInDirective, record.argumentToPureFunction, record.referencedBySelf, record.propertyBindingIndex); } /** * Returns the index in the destination array corresponding to the index in the src array. * When the element is not present in the destination array, return the source index. */ function _srcToDstSelfIndex(indexMap, srcIdx) { var dstIdx = indexMap.get(srcIdx); return isPresent(dstIdx) ? dstIdx : srcIdx; } function _createSelfRecord(r, contextIndex, selfIndex) { return new ProtoRecord(RecordType.Self, "self", null, [], r.fixedArgs, contextIndex, r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding, r.lastInDirective, false, false, r.propertyBindingIndex); } function _haveSameDirIndex(a, b) { var di1 = isBlank(a.directiveIndex) ? null : a.directiveIndex.directiveIndex; var ei1 = isBlank(a.directiveIndex) ? null : a.directiveIndex.elementIndex; var di2 = isBlank(b.directiveIndex) ? null : b.directiveIndex.directiveIndex; var ei2 = isBlank(b.directiveIndex) ? null : b.directiveIndex.elementIndex; return di1 === di2 && ei1 === ei2; } //# sourceMappingURL=coalesce.js.map