build(typescript): Migrated change detection to typescript

This commit is contained in:
vsavkin 2015-05-01 14:05:19 -07:00
parent f0ef72d6cc
commit fa28b28d0a
45 changed files with 2343 additions and 2576 deletions

View File

@ -6,17 +6,40 @@
*/
export {
ASTWithSource, AST, AstTransformer, AccessMember, LiteralArray, ImplicitReceiver
ASTWithSource,
AST,
AstTransformer,
AccessMember,
LiteralArray,
ImplicitReceiver
} from './src/change_detection/parser/ast';
export {Lexer} from './src/change_detection/parser/lexer';
export {Parser} from './src/change_detection/parser/parser';
export {Locals} from './src/change_detection/parser/locals';
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/change_detection/exceptions';
export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection} from './src/change_detection/interfaces';
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
export {DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/change_detection/proto_change_detector';
export {
ExpressionChangedAfterItHasBeenChecked,
ChangeDetectionError
} from './src/change_detection/exceptions';
export {
ProtoChangeDetector,
ChangeDispatcher,
ChangeDetector,
ChangeDetection
} from './src/change_detection/interfaces';
export {
CHECK_ONCE,
CHECK_ALWAYS,
DETACHED,
CHECKED,
ON_PUSH,
DEFAULT
} from './src/change_detection/constants';
export {
DynamicProtoChangeDetector,
JitProtoChangeDetector
} from './src/change_detection/proto_change_detector';
export {BindingRecord} from './src/change_detection/binding_record';
export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';
@ -25,5 +48,13 @@ export {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
export {uninitialized} from './src/change_detection/change_detection_util';
export {WrappedValue, Pipe} from './src/change_detection/pipes/pipe';
export {
defaultPipes, DynamicChangeDetection, JitChangeDetection, defaultPipeRegistry
defaultPipes,
DynamicChangeDetection,
JitChangeDetection,
defaultPipeRegistry
} from './src/change_detection/change_detection';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;

View File

@ -4,12 +4,17 @@ import {ChangeDetectorRef} from './change_detector_ref';
import {ChangeDetector} from './interfaces';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class AbstractChangeDetector extends ChangeDetector {
lightDomChildren:List;
shadowDomChildren:List;
parent:ChangeDetector;
mode:string;
ref:ChangeDetectorRef;
lightDomChildren: List<any>;
shadowDomChildren: List<any>;
parent: ChangeDetector;
mode: string;
ref: ChangeDetectorRef;
constructor() {
super();
@ -19,37 +24,27 @@ export class AbstractChangeDetector extends ChangeDetector {
this.mode = null;
}
addChild(cd:ChangeDetector) {
addChild(cd: ChangeDetector) {
ListWrapper.push(this.lightDomChildren, cd);
cd.parent = this;
}
removeChild(cd:ChangeDetector) {
ListWrapper.remove(this.lightDomChildren, cd);
}
removeChild(cd: ChangeDetector) { ListWrapper.remove(this.lightDomChildren, cd); }
addShadowDomChild(cd:ChangeDetector) {
addShadowDomChild(cd: ChangeDetector) {
ListWrapper.push(this.shadowDomChildren, cd);
cd.parent = this;
}
removeShadowDomChild(cd:ChangeDetector) {
ListWrapper.remove(this.shadowDomChildren, cd);
}
removeShadowDomChild(cd: ChangeDetector) { ListWrapper.remove(this.shadowDomChildren, cd); }
remove() {
this.parent.removeChild(this);
}
remove() { this.parent.removeChild(this); }
detectChanges() {
this._detectChanges(false);
}
detectChanges() { this._detectChanges(false); }
checkNoChanges() {
this._detectChanges(true);
}
checkNoChanges() { this._detectChanges(true); }
_detectChanges(throwOnChange:boolean) {
_detectChanges(throwOnChange: boolean) {
if (this.mode === DETACHED || this.mode === CHECKED) return;
this.detectChangesInRecords(throwOnChange);
@ -63,30 +58,28 @@ export class AbstractChangeDetector extends ChangeDetector {
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
}
detectChangesInRecords(throwOnChange:boolean){}
callOnAllChangesDone(){}
detectChangesInRecords(throwOnChange: boolean) {}
callOnAllChangesDone() {}
_detectChangesInLightDomChildren(throwOnChange:boolean) {
_detectChangesInLightDomChildren(throwOnChange: boolean) {
var c = this.lightDomChildren;
for(var i = 0; i < c.length; ++i) {
for (var i = 0; i < c.length; ++i) {
c[i]._detectChanges(throwOnChange);
}
}
_detectChangesInShadowDomChildren(throwOnChange:boolean) {
_detectChangesInShadowDomChildren(throwOnChange: boolean) {
var c = this.shadowDomChildren;
for(var i = 0; i < c.length; ++i) {
for (var i = 0; i < c.length; ++i) {
c[i]._detectChanges(throwOnChange);
}
}
markAsCheckOnce() {
this.mode = CHECK_ONCE;
}
markAsCheckOnce() { this.mode = CHECK_ONCE; }
markPathToRootAsCheckOnce() {
var c = this;
while(isPresent(c) && c.mode != DETACHED) {
var c: ChangeDetector = this;
while (isPresent(c) && c.mode != DETACHED) {
if (c.mode === CHECKED) c.mode = CHECK_ONCE;
c = c.parent;
}

View File

@ -1,68 +0,0 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {SetterFn} from 'angular2/src/reflection/types';
import {AST} from './parser/ast';
import {DirectiveIndex, DirectiveRecord} from './directive_record';
const DIRECTIVE="directive";
const ELEMENT="element";
const TEXT_NODE="textNode";
export class BindingRecord {
mode:string;
ast:AST;
implicitReceiver:any; //number | DirectiveIndex
elementIndex:number;
propertyName:string;
setter:SetterFn;
directiveRecord:DirectiveRecord;
constructor(mode:string, implicitReceiver:any, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
this.mode = mode;
this.implicitReceiver = implicitReceiver;
this.ast = ast;
this.elementIndex = elementIndex;
this.propertyName = propertyName;
this.setter = setter;
this.directiveRecord = directiveRecord;
}
callOnChange() {
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
}
isOnPushChangeDetection() {
return isPresent(this.directiveRecord) && this.directiveRecord.isOnPushChangeDetection();
}
isDirective() {
return this.mode === DIRECTIVE;
}
isElement() {
return this.mode === ELEMENT;
}
isTextNode() {
return this.mode === TEXT_NODE;
}
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, setter, directiveRecord);
}
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
return new BindingRecord(ELEMENT, 0, ast, elementIndex, propertyName, null, null);
}
static createForHostProperty(directiveIndex:DirectiveIndex, ast:AST, propertyName:string) {
return new BindingRecord(ELEMENT, directiveIndex, ast, directiveIndex.elementIndex, propertyName, null, null);
}
static createForTextNode(ast:AST, elementIndex:number) {
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null);
}
}

View File

@ -0,0 +1,49 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {SetterFn} from 'angular2/src/reflection/types';
import {AST} from './parser/ast';
import {DirectiveIndex, DirectiveRecord} from './directive_record';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
const DIRECTIVE = "directive";
const ELEMENT = "element";
const TEXT_NODE = "textNode";
export class BindingRecord {
constructor(public mode: string, public implicitReceiver: any, public ast: AST,
public elementIndex: number, public propertyName: string, public setter: SetterFn,
public directiveRecord: DirectiveRecord) {}
callOnChange() { return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange; }
isOnPushChangeDetection() {
return isPresent(this.directiveRecord) && this.directiveRecord.isOnPushChangeDetection();
}
isDirective() { return this.mode === DIRECTIVE; }
isElement() { return this.mode === ELEMENT; }
isTextNode() { return this.mode === TEXT_NODE; }
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
directiveRecord: DirectiveRecord) {
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, setter, directiveRecord);
}
static createForElement(ast: AST, elementIndex: number, propertyName: string) {
return new BindingRecord(ELEMENT, 0, ast, elementIndex, propertyName, null, null);
}
static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST, propertyName: string) {
return new BindingRecord(ELEMENT, directiveIndex, ast, directiveIndex.elementIndex,
propertyName, null, null);
}
static createForTextNode(ast: AST, elementIndex: number) {
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null);
}
}

View File

@ -1,96 +0,0 @@
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
import {PipeFactory} from './pipes/pipe';
import {PipeRegistry} from './pipes/pipe_registry';
import {IterableChangesFactory} from './pipes/iterable_changes';
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
import {AsyncPipeFactory} from './pipes/async_pipe';
import {NullPipeFactory} from './pipes/null_pipe';
import {BindingRecord} from './binding_record';
import {DirectiveRecord} from './directive_record';
import {DEFAULT} from './constants';
import {ChangeDetection, ProtoChangeDetector} from './interfaces';
import {Injectable} from 'angular2/src/di/annotations_impl';
import {List} from 'angular2/src/facade/collection';
/**
* Structural diffing for `Object`s and `Map`s.
*
* @exportedAs angular2/pipes
*/
export var keyValDiff:List<PipeFactory> = [
new KeyValueChangesFactory(),
new NullPipeFactory()
];
/**
* Structural diffing for `Iterable` types such as `Array`s.
*
* @exportedAs angular2/pipes
*/
export var iterableDiff:List<PipeFactory> = [
new IterableChangesFactory(),
new NullPipeFactory()
];
/**
* Async binding to such types as Observable.
*
* @exportedAs angular2/pipes
*/
export var async:List<PipeFactory> = [
new AsyncPipeFactory(),
new NullPipeFactory()
];
export var defaultPipes:Map<String, List<PipeFactory>> = {
"iterableDiff" : iterableDiff,
"keyValDiff" : keyValDiff,
"async" : async
};
/**
* Implements change detection that does not require `eval()`.
*
* This is slower than {@link JitChangeDetection}.
*
* @exportedAs angular2/change_detection
*/
@Injectable()
export class DynamicChangeDetection extends ChangeDetection {
registry:PipeRegistry;
constructor(registry:PipeRegistry) {
super();
this.registry = registry;
}
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new DynamicProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
}
}
/**
* Implements faster change detection, by generating source code.
*
* This requires `eval()`. For change detection that does not require `eval()`, see {@link DynamicChangeDetection}.
*
* @exportedAs angular2/change_detection
*/
@Injectable()
export class JitChangeDetection extends ChangeDetection {
registry:PipeRegistry;
constructor(registry:PipeRegistry) {
super();
this.registry = registry;
}
createProtoChangeDetector(name:string, bindingRecords:List<BindingRecord>, variableBindings:List<string>,
directiveRecords:List<DirectiveRecord>, changeControlStrategy:string = DEFAULT):ProtoChangeDetector{
return new JitProtoChangeDetector(this.registry, bindingRecords, variableBindings, directiveRecords, changeControlStrategy);
}
}
export var defaultPipeRegistry:PipeRegistry = new PipeRegistry(defaultPipes);

View File

@ -0,0 +1,88 @@
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
import {PipeFactory} from './pipes/pipe';
import {PipeRegistry} from './pipes/pipe_registry';
import {IterableChangesFactory} from './pipes/iterable_changes';
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
import {AsyncPipeFactory} from './pipes/async_pipe';
import {NullPipeFactory} from './pipes/null_pipe';
import {BindingRecord} from './binding_record';
import {DirectiveRecord} from './directive_record';
import {DEFAULT} from './constants';
import {ChangeDetection, ProtoChangeDetector} from './interfaces';
import {Injectable} from 'angular2/src/di/decorators';
import {List} from 'angular2/src/facade/collection';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* Structural diffing for `Object`s and `Map`s.
*
* @exportedAs angular2/pipes
*/
export var keyValDiff: List < PipeFactory >= [new KeyValueChangesFactory(), new NullPipeFactory()];
/**
* Structural diffing for `Iterable` types such as `Array`s.
*
* @exportedAs angular2/pipes
*/
export var iterableDiff: List <
PipeFactory >= [new IterableChangesFactory(), new NullPipeFactory()];
/**
* Async binding to such types as Observable.
*
* @exportedAs angular2/pipes
*/
export var async: List < PipeFactory >= [new AsyncPipeFactory(), new NullPipeFactory()];
export var defaultPipes = {
"iterableDiff": iterableDiff,
"keyValDiff": keyValDiff,
"async": async
};
/**
* Implements change detection that does not require `eval()`.
*
* This is slower than {@link JitChangeDetection}.
*
* @exportedAs angular2/change_detection
*/
@Injectable()
export class DynamicChangeDetection extends ChangeDetection {
constructor(public registry: PipeRegistry) { super(); }
createProtoChangeDetector(name: string, bindingRecords: List<BindingRecord>,
variableBindings: List<string>, directiveRecords: List<DirectiveRecord>,
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
return new DynamicProtoChangeDetector(this.registry, bindingRecords, variableBindings,
directiveRecords, changeControlStrategy);
}
}
/**
* Implements faster change detection, by generating source code.
*
* This requires `eval()`. For change detection that does not require `eval()`, see {@link
*DynamicChangeDetection}.
*
* @exportedAs angular2/change_detection
*/
@Injectable()
export class JitChangeDetection extends ChangeDetection {
constructor(public registry: PipeRegistry) { super(); }
createProtoChangeDetector(name: string, bindingRecords: List<BindingRecord>,
variableBindings: List<string>, directiveRecords: List<DirectiveRecord>,
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
return new JitProtoChangeDetector(this.registry, bindingRecords, variableBindings,
directiveRecords, changeControlStrategy);
}
}
export var defaultPipeRegistry: PipeRegistry = new PipeRegistry(defaultPipes);

View File

@ -18,7 +18,13 @@ import {
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
} from './proto_record';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* The code generator takes a list of proto records and creates a function/class
@ -43,8 +49,8 @@ var MODE_ACCESSOR = "this.mode";
var TEMP_LOCAL = "temp";
var CURRENT_PROTO = "currentProto";
function typeTemplate(type:string, cons:string, detectChanges:string,
notifyOnAllChangesDone:string, setContext:string):string {
function typeTemplate(type: string, cons: string, detectChanges: string,
notifyOnAllChangesDone: string, setContext: string): string {
return `
${cons}
${detectChanges}
@ -57,7 +63,7 @@ return function(dispatcher, pipeRegistry) {
`;
}
function constructorTemplate(type:string, fieldsDefinitions:string):string {
function constructorTemplate(type: string, fieldsDefinitions: string): string {
return `
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
${ABSTRACT_CHANGE_DETECTOR}.call(this);
@ -73,20 +79,23 @@ ${type}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
`;
}
function pipeOnDestroyTemplate(pipeNames:List) {
function pipeOnDestroyTemplate(pipeNames: List<any>) {
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
}
function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipeOnDestroy:string,
directiveFieldNames:List<String>, detectorFieldNames:List<String>):string {
function hydrateTemplate(type: string, mode: string, fieldDefinitions: string,
pipeOnDestroy: string, directiveFieldNames: List<String>,
detectorFieldNames: List<String>): string {
var directiveInit = "";
for(var i = 0; i < directiveFieldNames.length; ++i) {
directiveInit += `${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
for (var i = 0; i < directiveFieldNames.length; ++i) {
directiveInit +=
`${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
}
var detectorInit = "";
for(var i = 0; i < detectorFieldNames.length; ++i) {
detectorInit += `${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
for (var i = 0; i < detectorFieldNames.length; ++i) {
detectorInit +=
`${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
}
return `
@ -108,7 +117,7 @@ ${type}.prototype.hydrated = function() {
`;
}
function detectChangesTemplate(type:string, body:string):string {
function detectChangesTemplate(type: string, body: string): string {
return `
${type}.prototype.detectChangesInRecords = function(throwOnChange) {
${body}
@ -116,7 +125,7 @@ ${type}.prototype.detectChangesInRecords = function(throwOnChange) {
`;
}
function callOnAllChangesDoneTemplate(type:string, body:string):string {
function callOnAllChangesDoneTemplate(type: string, body: string): string {
return `
${type}.prototype.callOnAllChangesDone = function() {
${body}
@ -124,12 +133,13 @@ ${type}.prototype.callOnAllChangesDone = function() {
`;
}
function onAllChangesDoneTemplate(directive:string):string {
function onAllChangesDoneTemplate(directive: string): string {
return `${directive}.onAllChangesDone();`;
}
function detectChangesBodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
function detectChangesBodyTemplate(localDefinitions: string, changeDefinitions: string,
records: string): string {
return `
${localDefinitions}
${changeDefinitions}
@ -143,9 +153,10 @@ ${records}
`;
}
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
oldValue:string, newValue:string, change:string, update:string,
addToChanges, lastInDirective:string):string{
function pipeCheckTemplate(protoIndex: number, context: string, bindingPropagationConfig: string,
pipe: string, pipeType: string, oldValue: string, newValue: string,
change: string, update: string, addToChanges,
lastInDirective: string): string {
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
if (${pipe} === ${UTIL}.unitialized()) {
@ -167,8 +178,9 @@ ${lastInDirective}
`;
}
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
update:string, addToChanges:string, lastInDirective:string):string {
function referenceCheckTemplate(protoIndex: number, assignment: string, oldValue: string,
newValue: string, change: string, update: string,
addToChanges: string, lastInDirective: string): string {
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
${assignment}
@ -182,23 +194,23 @@ ${lastInDirective}
`;
}
function assignmentTemplate(field:string, value:string) {
function assignmentTemplate(field: string, value: string) {
return `${field} = ${value};`;
}
function localDefinitionsTemplate(names:List):string {
function localDefinitionsTemplate(names: List<any>): string {
return names.map((n) => `var ${n};`).join("\n");
}
function changeDefinitionsTemplate(names:List):string {
function changeDefinitionsTemplate(names: List<any>): string {
return names.map((n) => `var ${n} = false;`).join("\n");
}
function fieldDefinitionsTemplate(names:List):string {
function fieldDefinitionsTemplate(names: List<any>): string {
return names.map((n) => `${n} = ${UTIL}.unitialized();`).join("\n");
}
function ifChangedGuardTemplate(changeNames:List, body:string):string {
function ifChangedGuardTemplate(changeNames: List<any>, body: string): string {
var cond = changeNames.join(" || ");
return `
if (${cond}) {
@ -207,11 +219,12 @@ if (${cond}) {
`;
}
function addToChangesTemplate(oldValue:string, newValue:string):string {
function addToChangesTemplate(oldValue: string, newValue: string): string {
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
}
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
function updateDirectiveTemplate(oldValue: string, newValue: string,
directiveProperty: string): string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${directiveProperty} = ${newValue};
@ -219,15 +232,15 @@ ${IS_CHANGED_LOCAL} = true;
`;
}
function updateElementTemplate(oldValue:string, newValue:string):string {
function updateElementTemplate(oldValue: string, newValue: string): string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
`;
}
function notifyOnChangesTemplate(directive:string):string{
return `
function notifyOnChangesTemplate(directive: string): string {
return `
if(${CHANGES_LOCAL}) {
${directive}.onChange(${CHANGES_LOCAL});
${CHANGES_LOCAL} = null;
@ -235,16 +248,16 @@ if(${CHANGES_LOCAL}) {
`;
}
function notifyOnPushDetectorsTemplate(detector:string):string{
return `
function notifyOnPushDetectorsTemplate(detector: string): string {
return `
if(${IS_CHANGED_LOCAL}) {
${detector}.markAsCheckOnce();
}
`;
}
function lastInDirectiveTemplate(notifyOnChanges:string, notifyOnPush:string):string{
return `
function lastInDirectiveTemplate(notifyOnChanges: string, notifyOnPush: string): string {
return `
${notifyOnChanges}
${notifyOnPush}
${IS_CHANGED_LOCAL} = false;
@ -253,28 +266,20 @@ ${IS_CHANGED_LOCAL} = false;
export class ChangeDetectorJITGenerator {
typeName:string;
records:List<ProtoRecord>;
directiveRecords:List;
localNames:List<string>;
changeNames:List<string>;
fieldNames:List<string>;
pipeNames:List<string>;
changeDetectionStrategy:stirng;
constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveRecords:List) {
this.typeName = typeName;
this.changeDetectionStrategy = changeDetectionStrategy;
this.records = records;
this.directiveRecords = directiveRecords;
localNames: List<string>;
changeNames: List<string>;
fieldNames: List<string>;
pipeNames: List<string>;
constructor(public typeName: string, public changeDetectionStrategy: string,
public records: List<ProtoRecord>, public directiveRecords: List<any>) {
this.localNames = this.getLocalNames(records);
this.changeNames = this.getChangeNames(this.localNames);
this.fieldNames = this.getFieldNames(this.localNames);
this.pipeNames = this.getPipeNames(this.localNames);
}
getLocalNames(records:List<ProtoRecord>):List<string> {
getLocalNames(records: List<ProtoRecord>): List<string> {
var index = 0;
var names = records.map((r) => {
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
@ -283,51 +288,49 @@ export class ChangeDetectorJITGenerator {
return ["context"].concat(names);
}
getChangeNames(localNames:List<string>):List<string> {
getChangeNames(localNames: List<string>): List<string> {
return localNames.map((n) => `change_${n}`);
}
getFieldNames(localNames:List<string>):List<string> {
getFieldNames(localNames: List<string>): List<string> {
return localNames.map((n) => `this.${n}`);
}
getPipeNames(localNames:List<string>):List<string> {
getPipeNames(localNames: List<string>): List<string> {
return localNames.map((n) => `this.${n}_pipe`);
}
generate():Function {
generate(): Function {
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
this.genCallOnAllChangesDone(), this.genHydrate());
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', text)
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
this.genCallOnAllChangesDone(), this.genHydrate());
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
'directiveRecords', text)(AbstractChangeDetector, ChangeDetectionUtil,
this.records, this.directiveRecords);
}
genConstructor():string {
genConstructor(): string {
return constructorTemplate(this.typeName, this.genFieldDefinitions());
}
genHydrate():string {
genHydrate(): string {
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
}
getDirectiveFieldNames():List<string> {
getDirectiveFieldNames(): List<string> {
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
}
getDetectorFieldNames():List<string> {
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d.directiveIndex));
getDetectorFieldNames(): List<string> {
return this.directiveRecords.filter(r => r.isOnPushChangeDetection())
.map((d) => this.getDetector(d.directiveIndex));
}
getDirective(d:DirectiveIndex) {
return `this.directive_${d.name}`;
}
getDirective(d: DirectiveIndex) { return `this.directive_${d.name}`; }
getDetector(d:DirectiveIndex) {
return `this.detector_${d.name}`;
}
getDetector(d: DirectiveIndex) { return `this.detector_${d.name}`; }
genFieldDefinitions() {
var fields = [];
@ -338,7 +341,7 @@ export class ChangeDetectorJITGenerator {
return fieldDefinitionsTemplate(fields);
}
getNonNullPipeNames():List<string> {
getNonNullPipeNames(): List<string> {
var pipes = [];
this.records.forEach((r) => {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
@ -348,12 +351,12 @@ export class ChangeDetectorJITGenerator {
return pipes;
}
genDetectChanges():string {
genDetectChanges(): string {
var body = this.genDetectChangesBody();
return detectChangesTemplate(this.typeName, body);
}
genCallOnAllChangesDone():string {
genCallOnAllChangesDone(): string {
var notifications = [];
var dirs = this.directiveRecords;
@ -368,28 +371,24 @@ export class ChangeDetectorJITGenerator {
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
}
genDetectChangesBody():string {
genDetectChangesBody(): string {
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
}
genLocalDefinitions():string {
return localDefinitionsTemplate(this.localNames);
}
genLocalDefinitions(): string { return localDefinitionsTemplate(this.localNames); }
genChangeDefinitions():string {
return changeDefinitionsTemplate(this.changeNames);
}
genChangeDefinitions(): string { return changeDefinitionsTemplate(this.changeNames); }
genRecord(r:ProtoRecord):string {
genRecord(r: ProtoRecord): string {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
return this.genPipeCheck (r);
return this.genPipeCheck(r);
} else {
return this.genReferenceCheck(r);
}
}
genPipeCheck(r:ProtoRecord):string {
genPipeCheck(r: ProtoRecord): string {
var context = this.localNames[r.contextIndex];
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
@ -402,11 +401,11 @@ export class ChangeDetectorJITGenerator {
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genLastInDirective(r);
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue, change,
update, addToChanges, lastInDirective);
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue,
change, update, addToChanges, lastInDirective);
}
genReferenceCheck(r:ProtoRecord):string {
genReferenceCheck(r: ProtoRecord): string {
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
var change = this.changeNames[r.selfIndex];
@ -417,7 +416,7 @@ export class ChangeDetectorJITGenerator {
var lastInDirective = this.genLastInDirective(r);
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
update, addToChanges, lastInDirective);
update, addToChanges, lastInDirective);
if (r.isPureFunction()) {
return this.ifChangedGuard(r, check);
} else {
@ -425,7 +424,7 @@ export class ChangeDetectorJITGenerator {
}
}
genUpdateCurrentValue(r:ProtoRecord):string {
genUpdateCurrentValue(r: ProtoRecord): string {
var context = this.getContext(r);
var newValue = this.localNames[r.selfIndex];
var args = this.genArgs(r);
@ -464,7 +463,7 @@ export class ChangeDetectorJITGenerator {
}
}
getContext(r:ProtoRecord):string {
getContext(r: ProtoRecord): string {
if (r.contextIndex == -1) {
return this.getDirective(r.directiveIndex);
} else {
@ -472,11 +471,11 @@ export class ChangeDetectorJITGenerator {
}
}
ifChangedGuard(r:ProtoRecord, body:string):string {
ifChangedGuard(r: ProtoRecord, body: string): string {
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
}
genInterpolation(r:ProtoRecord):string{
genInterpolation(r: ProtoRecord): string {
var res = "";
for (var i = 0; i < r.args.length; ++i) {
res += this.genLiteral(r.fixedArgs[i]);
@ -488,38 +487,37 @@ export class ChangeDetectorJITGenerator {
return res;
}
genLiteral(value):string {
return JSON.stringify(value);
}
genLiteral(value): string { return JSON.stringify(value); }
genUpdateDirectiveOrElement(r:ProtoRecord):string {
if (! r.lastInBinding) return "";
genUpdateDirectiveOrElement(r: ProtoRecord): string {
if (!r.lastInBinding) return "";
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
var br = r.bindingRecord;
if (br.isDirective()) {
var directiveProperty = `${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
var directiveProperty =
`${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
} else {
return updateElementTemplate(oldValue, newValue);
}
}
genAddToChanges(r:ProtoRecord):string {
genAddToChanges(r: ProtoRecord): string {
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
}
genLastInDirective(r:ProtoRecord):string{
genLastInDirective(r: ProtoRecord): string {
var onChanges = this.genNotifyOnChanges(r);
var onPush = this.genNotifyOnPushDetectors(r);
return lastInDirectiveTemplate(onChanges, onPush);
}
genNotifyOnChanges(r:ProtoRecord):string{
genNotifyOnChanges(r: ProtoRecord): string {
var br = r.bindingRecord;
if (r.lastInDirective && br.callOnChange()) {
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
@ -528,7 +526,7 @@ export class ChangeDetectorJITGenerator {
}
}
genNotifyOnPushDetectors(r:ProtoRecord):string{
genNotifyOnPushDetectors(r: ProtoRecord): string {
var br = r.bindingRecord;
if (r.lastInDirective && br.isOnPushChangeDetection()) {
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
@ -537,11 +535,5 @@ export class ChangeDetectorJITGenerator {
}
}
genArgs(r:ProtoRecord):string {
return r.args.map((arg) => this.localNames[arg]).join(", ");
}
genArgs(r: ProtoRecord): string { return r.args.map((arg) => this.localNames[arg]).join(", "); }
}

View File

@ -1,139 +0,0 @@
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {ProtoRecord} from './proto_record';
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
import {WrappedValue} from './pipes/pipe';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
export var uninitialized = new Object();
export class SimpleChange {
previousValue:any;
currentValue:any;
constructor(previousValue:any, currentValue:any) {
this.previousValue = previousValue;
this.currentValue = currentValue;
}
}
var _simpleChangesIndex = 0;
var _simpleChanges = [
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null)
];
function _simpleChange(previousValue, currentValue) {
var index = _simpleChangesIndex++ % 20;
var s = _simpleChanges[index];
s.previousValue = previousValue;
s.currentValue = currentValue;
return s;
}
export class ChangeDetectionUtil {
static unitialized() {
return uninitialized;
}
static arrayFn0() { return []; }
static arrayFn1(a1) { return [a1]; }
static arrayFn2(a1, a2) { return [a1, a2]; }
static arrayFn3(a1, a2, a3) { return [a1, a2, a3]; }
static arrayFn4(a1, a2, a3, a4) { return [a1, a2, a3, a4]; }
static arrayFn5(a1, a2, a3, a4, a5) { return [a1, a2, a3, a4, a5]; }
static arrayFn6(a1, a2, a3, a4, a5, a6) { return [a1, a2, a3, a4, a5, a6]; }
static arrayFn7(a1, a2, a3, a4, a5, a6, a7) { return [a1, a2, a3, a4, a5, a6, a7]; }
static arrayFn8(a1, a2, a3, a4, a5, a6, a7, a8) { return [a1, a2, a3, a4, a5, a6, a7, a8]; }
static arrayFn9(a1, a2, a3, a4, a5, a6, a7, a8, a9) { return [a1, a2, a3, a4, a5, a6, a7, a8, a9]; }
static operation_negate(value) {return !value;}
static operation_add(left, right) {return left + right;}
static operation_subtract(left, right) {return left - right;}
static operation_multiply(left, right) {return left * right;}
static operation_divide(left, right) {return left / right;}
static operation_remainder(left, right) {return left % right;}
static operation_equals(left, right) {return left == right;}
static operation_not_equals(left, right) {return left != right;}
static operation_less_then(left, right) {return left < right;}
static operation_greater_then(left, right) {return left > right;}
static operation_less_or_equals_then(left, right) {return left <= right;}
static operation_greater_or_equals_then(left, right) {return left >= right;}
static operation_logical_and(left, right) {return left && right;}
static operation_logical_or(left, right) {return left || right;}
static cond(cond, trueVal, falseVal) {return cond ? trueVal : falseVal;}
static mapFn(keys:List) {
function buildMap(values) {
var res = StringMapWrapper.create();
for(var i = 0; i < keys.length; ++i) {
StringMapWrapper.set(res, keys[i], values[i]);
}
return res;
}
switch (keys.length) {
case 0: return () => [];
case 1: return (a1) => buildMap([a1]);
case 2: return (a1, a2) => buildMap([a1, a2]);
case 3: return (a1, a2, a3) => buildMap([a1, a2, a3]);
case 4: return (a1, a2, a3, a4) => buildMap([a1, a2, a3, a4]);
case 5: return (a1, a2, a3, a4, a5) => buildMap([a1, a2, a3, a4, a5]);
case 6: return (a1, a2, a3, a4, a5, a6) => buildMap([a1, a2, a3, a4, a5, a6]);
case 7: return (a1, a2, a3, a4, a5, a6, a7) => buildMap([a1, a2, a3, a4, a5, a6, a7]);
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8]);
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8, a9]);
default: throw new BaseException(`Does not support literal maps with more than 9 elements`);
}
}
static keyedAccess(obj, args) {
return obj[args[0]];
}
static unwrapValue(value:any):any {
if (value instanceof WrappedValue) {
return value.wrapped;
} else {
return value;
}
}
static throwOnChange(proto:ProtoRecord, change) {
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
}
static changeDetectionMode(strategy:string) {
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
}
static simpleChange(previousValue:any, currentValue:any):SimpleChange {
return _simpleChange(previousValue, currentValue);
}
static addChange(changes, propertyName:string, change){
if (isBlank(changes)) {
changes = {};
}
changes[propertyName] = change;
return changes;
}
}

View File

@ -0,0 +1,148 @@
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {ProtoRecord} from './proto_record';
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
import {WrappedValue} from './pipes/pipe';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export var uninitialized = new Object();
export class SimpleChange {
constructor(public previousValue: any, public currentValue: any) {}
}
var _simpleChangesIndex = 0;
var _simpleChanges = [
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null),
new SimpleChange(null, null)
];
function _simpleChange(previousValue, currentValue) {
var index = _simpleChangesIndex++ % 20;
var s = _simpleChanges[index];
s.previousValue = previousValue;
s.currentValue = currentValue;
return s;
}
export class ChangeDetectionUtil {
static unitialized() { return uninitialized; }
static arrayFn0() { return []; }
static arrayFn1(a1) { return [a1]; }
static arrayFn2(a1, a2) { return [a1, a2]; }
static arrayFn3(a1, a2, a3) { return [a1, a2, a3]; }
static arrayFn4(a1, a2, a3, a4) { return [a1, a2, a3, a4]; }
static arrayFn5(a1, a2, a3, a4, a5) { return [a1, a2, a3, a4, a5]; }
static arrayFn6(a1, a2, a3, a4, a5, a6) { return [a1, a2, a3, a4, a5, a6]; }
static arrayFn7(a1, a2, a3, a4, a5, a6, a7) { return [a1, a2, a3, a4, a5, a6, a7]; }
static arrayFn8(a1, a2, a3, a4, a5, a6, a7, a8) { return [a1, a2, a3, a4, a5, a6, a7, a8]; }
static arrayFn9(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
return [a1, a2, a3, a4, a5, a6, a7, a8, a9];
}
static operation_negate(value) { return !value; }
static operation_add(left, right) { return left + right; }
static operation_subtract(left, right) { return left - right; }
static operation_multiply(left, right) { return left * right; }
static operation_divide(left, right) { return left / right; }
static operation_remainder(left, right) { return left % right; }
static operation_equals(left, right) { return left == right; }
static operation_not_equals(left, right) { return left != right; }
static operation_less_then(left, right) { return left < right; }
static operation_greater_then(left, right) { return left > right; }
static operation_less_or_equals_then(left, right) { return left <= right; }
static operation_greater_or_equals_then(left, right) { return left >= right; }
static operation_logical_and(left, right) { return left && right; }
static operation_logical_or(left, right) { return left || right; }
static cond(cond, trueVal, falseVal) { return cond ? trueVal : falseVal; }
static mapFn(keys: List<any>) {
function buildMap(values) {
var res = StringMapWrapper.create();
for (var i = 0; i < keys.length; ++i) {
StringMapWrapper.set(res, keys[i], values[i]);
}
return res;
}
switch (keys.length) {
case 0:
return () => [];
case 1:
return (a1) => buildMap([a1]);
case 2:
return (a1, a2) => buildMap([a1, a2]);
case 3:
return (a1, a2, a3) => buildMap([a1, a2, a3]);
case 4:
return (a1, a2, a3, a4) => buildMap([a1, a2, a3, a4]);
case 5:
return (a1, a2, a3, a4, a5) => buildMap([a1, a2, a3, a4, a5]);
case 6:
return (a1, a2, a3, a4, a5, a6) => buildMap([a1, a2, a3, a4, a5, a6]);
case 7:
return (a1, a2, a3, a4, a5, a6, a7) => buildMap([a1, a2, a3, a4, a5, a6, a7]);
case 8:
return (a1, a2, a3, a4, a5, a6, a7, a8) => buildMap([a1, a2, a3, a4, a5, a6, a7, a8]);
case 9:
return (a1, a2, a3, a4, a5, a6, a7, a8, a9) =>
buildMap([a1, a2, a3, a4, a5, a6, a7, a8, a9]);
default:
throw new BaseException(`Does not support literal maps with more than 9 elements`);
}
}
static keyedAccess(obj, args) { return obj[args[0]]; }
static unwrapValue(value: any): any {
if (value instanceof WrappedValue) {
return value.wrapped;
} else {
return value;
}
}
static throwOnChange(proto: ProtoRecord, change) {
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
}
static changeDetectionMode(strategy: string) {
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
}
static simpleChange(previousValue: any, currentValue: any): SimpleChange {
return _simpleChange(previousValue, currentValue);
}
static addChange(changes, propertyName: string, change) {
if (isBlank(changes)) {
changes = {};
}
changes[propertyName] = change;
return changes;
}
}

View File

@ -1,41 +1,40 @@
import {ChangeDetector} from './interfaces';
import {CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './constants';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* Controls change detection.
*
* {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It also allows detaching and
* {@link ChangeDetectorRef} allows requesting checks for detectors that rely on observables. It
*also allows detaching and
* attaching change detector subtrees.
*
* @exportedAs angular2/change_detection
*/
export class ChangeDetectorRef {
_cd:ChangeDetector;
constructor(cd:ChangeDetector) {
this._cd = cd;
}
constructor(private _cd: ChangeDetector) {}
/**
* Request to check all ON_PUSH ancestors.
*/
requestCheck() {
this._cd.markPathToRootAsCheckOnce();
}
requestCheck() { this._cd.markPathToRootAsCheckOnce(); }
/**
* Detaches the change detector from the change detector tree.
*
* The detached change detector will not be checked until it is reattached.
*/
detach() {
this._cd.mode = DETACHED;
}
detach() { this._cd.mode = DETACHED; }
/**
* Reattach the change detector to the change detector tree.
*
* This also requests a check of this change detector. This reattached change detector will be checked during the
* This also requests a check of this change detector. This reattached change detector will be
*checked during the
* next change detection run.
*/
reattach() {

View File

@ -2,6 +2,11 @@ import {isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
import {RECORD_TYPE_SELF, ProtoRecord} from './proto_record';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* Removes "duplicate" records. It assuming that record evaluation does not
* have side-effects.
@ -12,7 +17,7 @@ import {RECORD_TYPE_SELF, ProtoRecord} from './proto_record';
* Records that are last in bindings CANNOT be removed, and instead are
* replaced with very cheap SELF records.
*/
export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
export function coalesce(records: List<ProtoRecord>): List<ProtoRecord> {
var res = ListWrapper.create();
var indexMap = MapWrapper.create();
@ -37,52 +42,27 @@ export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
return res;
}
function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):ProtoRecord {
return new ProtoRecord(
RECORD_TYPE_SELF,
"self",
null,
[],
r.fixedArgs,
contextIndex,
r.directiveIndex,
selfIndex,
r.bindingRecord,
r.expressionAsString,
r.lastInBinding,
r.lastInDirective
);
function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord {
return new ProtoRecord(RECORD_TYPE_SELF, "self", null, [], r.fixedArgs, contextIndex,
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
r.lastInBinding, r.lastInDirective);
}
function _findMatching(r:ProtoRecord, rs:List<ProtoRecord>){
return ListWrapper.find(rs, (rr) =>
rr.mode === r.mode &&
rr.funcOrValue === r.funcOrValue &&
rr.contextIndex === r.contextIndex &&
ListWrapper.equals(rr.args, r.args)
);
function _findMatching(r: ProtoRecord, rs: List<ProtoRecord>) {
return ListWrapper.find(rs, (rr) => rr.mode === r.mode && rr.funcOrValue === r.funcOrValue &&
rr.contextIndex === r.contextIndex &&
ListWrapper.equals(rr.args, r.args));
}
function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
function _replaceIndices(r: ProtoRecord, selfIndex: number, indexMap: Map<any, any>) {
var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
var contextIndex = _map(indexMap, r.contextIndex);
return new ProtoRecord(
r.mode,
r.name,
r.funcOrValue,
args,
r.fixedArgs,
contextIndex,
r.directiveIndex,
selfIndex,
r.bindingRecord,
r.expressionAsString,
r.lastInBinding,
r.lastInDirective
);
return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex,
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
r.lastInBinding, r.lastInDirective);
}
function _map(indexMap:Map, value:number) {
var r = MapWrapper.get(indexMap, value)
function _map(indexMap: Map<any, any>, value: number) {
var r = MapWrapper.get(indexMap, value);
return isPresent(r) ? r : value;
}

View File

@ -1,28 +1,33 @@
//TODO:vsavkin Use enums after switching to TypeScript
// TODO:vsavkin Use enums after switching to TypeScript
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
* will become CHECKED.
*/
export const CHECK_ONCE="CHECK_ONCE";
export const CHECK_ONCE = "CHECK_ONCE";
/**
* CHECKED means that the change detector should be skipped until its mode changes to
* CHECK_ONCE or CHECK_ALWAYS.
*/
export const CHECKED="CHECKED";
export const CHECKED = "CHECKED";
/**
* CHECK_ALWAYS means that after calling detectChanges the mode of the change detector
* will remain CHECK_ALWAYS.
*/
export const CHECK_ALWAYS="ALWAYS_CHECK";
export const CHECK_ALWAYS = "ALWAYS_CHECK";
/**
* DETACHED means that the change detector sub tree is not a part of the main tree and
* should be skipped.
*/
export const DETACHED="DETACHED";
export const DETACHED = "DETACHED";
/**
* ON_PUSH means that the change detector's mode will be set to CHECK_ONCE during hydration.

View File

@ -1,34 +0,0 @@
import {ON_PUSH} from './constants';
import {StringWrapper} from 'angular2/src/facade/lang';
export class DirectiveIndex {
elementIndex:number;
directiveIndex:number;
constructor(elementIndex:number, directiveIndex:number) {
this.elementIndex = elementIndex;
this.directiveIndex = directiveIndex;
}
get name() {
return `${this.elementIndex}_${this.directiveIndex}`;
}
}
export class DirectiveRecord {
directiveIndex:DirectiveIndex;
callOnAllChangesDone:boolean;
callOnChange:boolean;
changeDetection:string;
constructor(directiveIndex:DirectiveIndex, callOnAllChangesDone:boolean, callOnChange:boolean, changeDetection:string) {
this.directiveIndex = directiveIndex;
this.callOnAllChangesDone = callOnAllChangesDone;
this.callOnChange = callOnChange;
this.changeDetection = changeDetection;
}
isOnPushChangeDetection():boolean {
return StringWrapper.equals(this.changeDetection, ON_PUSH);
}
}

View File

@ -0,0 +1,20 @@
import {ON_PUSH} from './constants';
import {StringWrapper} from 'angular2/src/facade/lang';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class DirectiveIndex {
constructor(public elementIndex: number, public directiveIndex: number) {}
get name() { return `${this.elementIndex}_${this.directiveIndex}`; }
}
export class DirectiveRecord {
constructor(public directiveIndex: DirectiveIndex, public callOnAllChangesDone: boolean,
public callOnChange: boolean, public changeDetection: string) {}
isOnPushChangeDetection(): boolean { return StringWrapper.equals(this.changeDetection, ON_PUSH); }
}

View File

@ -4,7 +4,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
import {AbstractChangeDetector} from './abstract_change_detector';
import {BindingRecord} from './binding_record';
import {PipeRegistry} from './pipes/pipe_registry';
import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
import {
@ -20,35 +20,31 @@ import {
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
} from './proto_record';
import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './exceptions';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class DynamicChangeDetector extends AbstractChangeDetector {
dispatcher:any;
pipeRegistry;
locals: any;
values: List<any>;
changes: List<any>;
pipes: List<any>;
prevContexts: List<any>;
directives: any;
locals:any;
values:List;
changes:List;
pipes:List;
prevContexts:List;
protos:List<ProtoRecord>;
directives:any;
directiveRecords:List;
changeControlStrategy:string;
constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry,
protoRecords:List<ProtoRecord>, directiveRecords:List) {
constructor(private changeControlStrategy: string, private dispatcher: any,
private pipeRegistry: PipeRegistry, private protos: List<ProtoRecord>,
private directiveRecords: List<any>) {
super();
this.dispatcher = dispatcher;
this.pipeRegistry = pipeRegistry;
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1);
this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
this.values = ListWrapper.createFixedSize(protos.length + 1);
this.pipes = ListWrapper.createFixedSize(protos.length + 1);
this.prevContexts = ListWrapper.createFixedSize(protos.length + 1);
this.changes = ListWrapper.createFixedSize(protos.length + 1);
ListWrapper.fill(this.values, uninitialized);
ListWrapper.fill(this.pipes, null);
@ -56,13 +52,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
ListWrapper.fill(this.changes, false);
this.locals = null;
this.directives = null;
this.protos = protoRecords;
this.directiveRecords = directiveRecords;
this.changeControlStrategy = changeControlStrategy;
}
hydrate(context:any, locals:any, directives:any) {
hydrate(context: any, locals: any, directives: any) {
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
this.values[0] = context;
this.locals = locals;
@ -79,24 +71,22 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
_destroyPipes() {
for(var i = 0; i < this.pipes.length; ++i) {
for (var i = 0; i < this.pipes.length; ++i) {
if (isPresent(this.pipes[i])) {
this.pipes[i].onDestroy();
}
}
}
hydrated():boolean {
return this.values[0] !== uninitialized;
}
hydrated(): boolean { return this.values[0] !== uninitialized; }
detectChangesInRecords(throwOnChange:boolean) {
var protos:List<ProtoRecord> = this.protos;
detectChangesInRecords(throwOnChange: boolean) {
var protos: List < ProtoRecord >= this.protos;
var changes = null;
var isChanged = false;
for (var i = 0; i < protos.length; ++i) {
var proto:ProtoRecord = protos[i];
var proto: ProtoRecord = protos[i];
var bindingRecord = proto.bindingRecord;
var directiveRecord = bindingRecord.directiveRecord;
@ -142,7 +132,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
_addChange(bindingRecord:BindingRecord, change, changes) {
_addChange(bindingRecord: BindingRecord, change, changes) {
if (bindingRecord.callOnChange()) {
return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
} else {
@ -150,15 +140,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
_getDirectiveFor(directiveIndex) {
return this.directives.getDirectiveFor(directiveIndex);
}
_getDirectiveFor(directiveIndex) { return this.directives.getDirectiveFor(directiveIndex); }
_getDetectorFor(directiveIndex) {
return this.directives.getDetectorFor(directiveIndex);
}
_getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
_check(proto:ProtoRecord) {
_check(proto: ProtoRecord): SimpleChange {
try {
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
return this._pipeCheck(proto);
@ -170,7 +156,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
_referenceCheck(proto:ProtoRecord) {
_referenceCheck(proto: ProtoRecord) {
if (this._pureFuncAndArgsDidNotChange(proto)) {
this._setChanged(proto, false);
return null;
@ -194,7 +180,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
_calculateCurrValue(proto:ProtoRecord) {
_calculateCurrValue(proto: ProtoRecord) {
switch (proto.mode) {
case RECORD_TYPE_SELF:
return this._readContext(proto);
@ -230,7 +216,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
_pipeCheck(proto:ProtoRecord) {
_pipeCheck(proto: ProtoRecord) {
var context = this._readContext(proto);
var pipe = this._pipeFor(proto, context);
var prevValue = this._readSelf(proto);
@ -254,7 +240,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
_pipeFor(proto:ProtoRecord, context) {
_pipeFor(proto: ProtoRecord, context) {
var storedPipe = this._readPipe(proto);
if (isPresent(storedPipe) && storedPipe.supports(context)) {
return storedPipe;
@ -274,7 +260,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
return pipe;
}
_readContext(proto:ProtoRecord) {
_readContext(proto: ProtoRecord) {
if (proto.contextIndex == -1) {
return this._getDirectiveFor(proto.directiveIndex);
} else {
@ -284,33 +270,23 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
return this.values[proto.contextIndex];
}
_readSelf(proto:ProtoRecord) {
return this.values[proto.selfIndex];
}
_readSelf(proto: ProtoRecord) { return this.values[proto.selfIndex]; }
_writeSelf(proto:ProtoRecord, value) {
this.values[proto.selfIndex] = value;
}
_writeSelf(proto: ProtoRecord, value) { this.values[proto.selfIndex] = value; }
_readPipe(proto:ProtoRecord) {
return this.pipes[proto.selfIndex];
}
_readPipe(proto: ProtoRecord) { return this.pipes[proto.selfIndex]; }
_writePipe(proto:ProtoRecord, value) {
this.pipes[proto.selfIndex] = value;
}
_writePipe(proto: ProtoRecord, value) { this.pipes[proto.selfIndex] = value; }
_setChanged(proto:ProtoRecord, value:boolean) {
this.changes[proto.selfIndex] = value;
}
_setChanged(proto: ProtoRecord, value: boolean) { this.changes[proto.selfIndex] = value; }
_pureFuncAndArgsDidNotChange(proto:ProtoRecord):boolean {
_pureFuncAndArgsDidNotChange(proto: ProtoRecord): boolean {
return proto.isPureFunction() && !this._argsChanged(proto);
}
_argsChanged(proto:ProtoRecord):boolean {
_argsChanged(proto: ProtoRecord): boolean {
var args = proto.args;
for(var i = 0; i < args.length; ++i) {
for (var i = 0; i < args.length; ++i) {
if (this.changes[args[i]]) {
return true;
}
@ -318,7 +294,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
return false;
}
_readArgs(proto:ProtoRecord) {
_readArgs(proto: ProtoRecord) {
var res = ListWrapper.createFixedSize(proto.args.length);
var args = proto.args;
for (var i = 0; i < args.length; ++i) {
@ -334,6 +310,3 @@ function isSame(a, b) {
if ((a !== a) && (b !== b)) return true;
return false;
}

View File

@ -1,33 +0,0 @@
import {ProtoRecord} from './proto_record';
import {BaseException} from "angular2/src/facade/lang";
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
message:string;
constructor(proto:ProtoRecord, change:any) {
super();
this.message = `Expression '${proto.expressionAsString}' has changed after it was checked. ` +
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`;
}
toString():string {
return this.message;
}
}
export class ChangeDetectionError extends BaseException {
message:string;
originalException:any;
location:string;
constructor(proto:ProtoRecord, originalException:any) {
super();
this.originalException = originalException;
this.location = proto.expressionAsString;
this.message = `${this.originalException} in [${this.location}]`;
}
toString():string {
return this.message;
}
}

View File

@ -0,0 +1,36 @@
import {ProtoRecord} from './proto_record';
import {BaseException} from "angular2/src/facade/lang";
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class ExpressionChangedAfterItHasBeenChecked extends BaseException {
message: string;
constructor(proto: ProtoRecord, change: any) {
super();
this.message =
`Expression '${proto.expressionAsString}' has changed after it was checked. ` +
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`;
}
toString(): string { return this.message; }
}
export class ChangeDetectionError extends BaseException {
message: string;
originalException: any;
location: string;
constructor(proto: ProtoRecord, originalException: any) {
super();
this.originalException = originalException;
this.location = proto.expressionAsString;
this.message = `${this.originalException} in [${this.location}]`;
}
toString(): string { return this.message; }
}

View File

@ -3,10 +3,13 @@ import {Locals} from './parser/locals';
import {DEFAULT} from './constants';
import {BindingRecord} from './binding_record';
export class ProtoChangeDetector {
instantiate(dispatcher:any):ChangeDetector{
return null;
}
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class ProtoChangeDetector {
instantiate(dispatcher: any): ChangeDetector { return null; }
}
/**
@ -17,10 +20,12 @@ export class ProtoChangeDetector {
* - {@link DynamicChangeDetection}: slower, but does not require `eval()`.
* - {@link JitChangeDetection}: faster, but requires `eval()`.
*
* In JavaScript, you should always use `JitChangeDetection`, unless you are in an environment that has
* In JavaScript, you should always use `JitChangeDetection`, unless you are in an environment that
*has
* [CSP](https://developer.mozilla.org/en-US/docs/Web/Security/CSP), such as a Chrome Extension.
*
* In Dart, use `DynamicChangeDetection` during development. The Angular transformer generates an analog to the
* In Dart, use `DynamicChangeDetection` during development. The Angular transformer generates an
*analog to the
* `JitChangeDetection` strategy at compile time.
*
*
@ -33,26 +38,27 @@ export class ProtoChangeDetector {
* @exportedAs angular2/change_detection
*/
export class ChangeDetection {
createProtoChangeDetector(name:string, bindingRecords:List, variableBindings:List, directiveRecords:List,
changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
createProtoChangeDetector(name: string, bindingRecords: List<any>, variableBindings: List<any>,
directiveRecords: List<any>,
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
return null;
}
}
export class ChangeDispatcher {
notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
notifyOnBinding(bindingRecord: BindingRecord, value: any) {}
}
export class ChangeDetector {
parent:ChangeDetector;
mode:string;
parent: ChangeDetector;
mode: string;
addChild(cd:ChangeDetector) {}
addShadowDomChild(cd:ChangeDetector) {}
removeChild(cd:ChangeDetector) {}
removeShadowDomChild(cd:ChangeDetector) {}
addChild(cd: ChangeDetector) {}
addShadowDomChild(cd: ChangeDetector) {}
removeChild(cd: ChangeDetector) {}
removeShadowDomChild(cd: ChangeDetector) {}
remove() {}
hydrate(context:any, locals:Locals, directives:any) {}
hydrate(context: any, locals: Locals, directives: any) {}
dehydrate() {}
markPathToRootAsCheckOnce() {}

View File

@ -1,532 +0,0 @@
import {autoConvertAdd, isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
export class AST {
eval(context, locals) {
throw new BaseException("Not supported");
}
get isAssignable():boolean {
return false;
}
assign(context, locals, value) {
throw new BaseException("Not supported");
}
visit(visitor) {
}
toString():string {
return "AST";
}
}
export class EmptyExpr extends AST {
eval(context, locals) {
return null;
}
visit(visitor) {
//do nothing
}
}
export class ImplicitReceiver extends AST {
eval(context, locals) {
return context;
}
visit(visitor) {
return visitor.visitImplicitReceiver(this);
}
}
/**
* Multiple expressions separated by a semicolon.
*/
export class Chain extends AST {
expressions:List;
constructor(expressions:List) {
super();
this.expressions = expressions;
}
eval(context, locals) {
var result;
for (var i = 0; i < this.expressions.length; i++) {
var last = this.expressions[i].eval(context, locals);
if (isPresent(last)) result = last;
}
return result;
}
visit(visitor) {
return visitor.visitChain(this);
}
}
export class Conditional extends AST {
condition:AST;
trueExp:AST;
falseExp:AST;
constructor(condition:AST, trueExp:AST, falseExp:AST){
super();
this.condition = condition;
this.trueExp = trueExp;
this.falseExp = falseExp;
}
eval(context, locals) {
if(this.condition.eval(context, locals)) {
return this.trueExp.eval(context, locals);
} else {
return this.falseExp.eval(context, locals);
}
}
visit(visitor) {
return visitor.visitConditional(this);
}
}
export class AccessMember extends AST {
receiver:AST;
name:string;
getter:Function;
setter:Function;
constructor(receiver:AST, name:string, getter:Function, setter:Function) {
super();
this.receiver = receiver;
this.name = name;
this.getter = getter;
this.setter = setter;
}
eval(context, locals) {
if (this.receiver instanceof ImplicitReceiver &&
isPresent(locals) && locals.contains(this.name)) {
return locals.get(this.name);
} else {
var evaluatedReceiver = this.receiver.eval(context, locals);
return this.getter(evaluatedReceiver);
}
}
get isAssignable():boolean {
return true;
}
assign(context, locals, value) {
var evaluatedContext = this.receiver.eval(context, locals);
if (this.receiver instanceof ImplicitReceiver &&
isPresent(locals) && locals.contains(this.name)) {
throw new BaseException(`Cannot reassign a variable binding ${this.name}`);
} else {
return this.setter(evaluatedContext, value);
}
}
visit(visitor) {
return visitor.visitAccessMember(this);
}
}
export class KeyedAccess extends AST {
obj:AST;
key:AST;
constructor(obj:AST, key:AST) {
super();
this.obj = obj;
this.key = key;
}
eval(context, locals) {
var obj = this.obj.eval(context, locals);
var key = this.key.eval(context, locals);
return obj[key];
}
get isAssignable():boolean {
return true;
}
assign(context, locals, value) {
var obj = this.obj.eval(context, locals);
var key = this.key.eval(context, locals);
obj[key] = value;
return value;
}
visit(visitor) {
return visitor.visitKeyedAccess(this);
}
}
export class Pipe extends AST {
exp:AST;
name:string;
args:List<AST>;
inBinding:boolean;
constructor(exp:AST, name:string, args:List, inBinding:boolean) {
super();
this.exp = exp;
this.name = name;
this.args = args;
this.inBinding = inBinding;
}
visit(visitor) {
return visitor.visitPipe(this);
}
}
export class LiteralPrimitive extends AST {
value;
constructor(value) {
super();
this.value = value;
}
eval(context, locals) {
return this.value;
}
visit(visitor) {
return visitor.visitLiteralPrimitive(this);
}
}
export class LiteralArray extends AST {
expressions:List;
constructor(expressions:List) {
super();
this.expressions = expressions;
}
eval(context, locals) {
return ListWrapper.map(this.expressions, (e) => e.eval(context, locals));
}
visit(visitor) {
return visitor.visitLiteralArray(this);
}
}
export class LiteralMap extends AST {
keys:List;
values:List;
constructor(keys:List, values:List) {
super();
this.keys = keys;
this.values = values;
}
eval(context, locals) {
var res = StringMapWrapper.create();
for(var i = 0; i < this.keys.length; ++i) {
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context, locals));
}
return res;
}
visit(visitor) {
return visitor.visitLiteralMap(this);
}
}
export class Interpolation extends AST {
strings:List;
expressions:List;
constructor(strings:List, expressions:List) {
super();
this.strings = strings;
this.expressions = expressions;
}
eval(context, locals) {
throw new BaseException("evaluating an Interpolation is not supported");
}
visit(visitor) {
visitor.visitInterpolation(this);
}
}
export class Binary extends AST {
operation:string;
left:AST;
right:AST;
constructor(operation:string, left:AST, right:AST) {
super();
this.operation = operation;
this.left = left;
this.right = right;
}
eval(context, locals) {
var left = this.left.eval(context, locals);
switch (this.operation) {
case '&&': return left && this.right.eval(context, locals);
case '||': return left || this.right.eval(context, locals);
}
var right = this.right.eval(context, locals);
switch (this.operation) {
case '+' : return left + right;
case '-' : return left - right;
case '*' : return left * right;
case '/' : return left / right;
case '%' : return left % right;
case '==' : return left == right;
case '!=' : return left != right;
case '===' : return left === right;
case '!==' : return left !== right;
case '<' : return left < right;
case '>' : return left > right;
case '<=' : return left <= right;
case '>=' : return left >= right;
case '^' : return left ^ right;
case '&' : return left & right;
}
throw 'Internal error [$operation] not handled';
}
visit(visitor) {
return visitor.visitBinary(this);
}
}
export class PrefixNot extends AST {
expression:AST;
constructor(expression:AST) {
super();
this.expression = expression;
}
eval(context, locals) {
return !this.expression.eval(context, locals);
}
visit(visitor) {
return visitor.visitPrefixNot(this);
}
}
export class Assignment extends AST {
target:AST;
value:AST;
constructor(target:AST, value:AST) {
super();
this.target = target;
this.value = value;
}
eval(context, locals) {
return this.target.assign(context, locals, this.value.eval(context, locals));
}
visit(visitor) {
return visitor.visitAssignment(this);
}
}
export class MethodCall extends AST {
receiver:AST;
fn:Function;
args:List;
name:string;
constructor(receiver:AST, name:string, fn:Function, args:List) {
super();
this.receiver = receiver;
this.fn = fn;
this.args = args;
this.name = name;
}
eval(context, locals) {
var evaluatedArgs = evalList(context, locals, this.args);
if (this.receiver instanceof ImplicitReceiver &&
isPresent(locals) && locals.contains(this.name)) {
var fn = locals.get(this.name);
return FunctionWrapper.apply(fn, evaluatedArgs);
} else {
var evaluatedReceiver = this.receiver.eval(context, locals);
return this.fn(evaluatedReceiver, evaluatedArgs);
}
}
visit(visitor) {
return visitor.visitMethodCall(this);
}
}
export class FunctionCall extends AST {
target:AST;
args:List;
constructor(target:AST, args:List) {
super();
this.target = target;
this.args = args;
}
eval(context, locals) {
var obj = this.target.eval(context, locals);
if (! (obj instanceof Function)) {
throw new BaseException(`${obj} is not a function`);
}
return FunctionWrapper.apply(obj, evalList(context, locals, this.args));
}
visit(visitor) {
return visitor.visitFunctionCall(this);
}
}
export class ASTWithSource extends AST {
ast:AST;
source:string;
location:string;
constructor(ast:AST, source:string, location:string) {
super();
this.source = source;
this.location = location;
this.ast = ast;
}
eval(context, locals) {
return this.ast.eval(context, locals);
}
get isAssignable():boolean {
return this.ast.isAssignable;
}
assign(context, locals, value) {
return this.ast.assign(context, locals, value);
}
visit(visitor) {
return this.ast.visit(visitor);
}
toString():string {
return `${this.source} in ${this.location}`;
}
}
export class TemplateBinding {
key:string;
keyIsVar:boolean;
name:string;
expression:ASTWithSource;
constructor(key:string, keyIsVar:boolean, name:string, expression:ASTWithSource) {
this.key = key;
this.keyIsVar = keyIsVar;
// only either name or expression will be filled.
this.name = name;
this.expression = expression;
}
}
//INTERFACE
export class AstVisitor {
visitAccessMember(ast:AccessMember) {}
visitAssignment(ast:Assignment) {}
visitBinary(ast:Binary) {}
visitChain(ast:Chain){}
visitConditional(ast:Conditional) {}
visitPipe(ast:Pipe) {}
visitFunctionCall(ast:FunctionCall) {}
visitImplicitReceiver(ast:ImplicitReceiver) {}
visitKeyedAccess(ast:KeyedAccess) {}
visitLiteralArray(ast:LiteralArray) {}
visitLiteralMap(ast:LiteralMap) {}
visitLiteralPrimitive(ast:LiteralPrimitive) {}
visitMethodCall(ast:MethodCall) {}
visitPrefixNot(ast:PrefixNot) {}
}
export class AstTransformer {
visitImplicitReceiver(ast:ImplicitReceiver) {
return ast;
}
visitInterpolation(ast:Interpolation) {
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
}
visitLiteralPrimitive(ast:LiteralPrimitive) {
return new LiteralPrimitive(ast.value);
}
visitAccessMember(ast:AccessMember) {
return new AccessMember(ast.receiver.visit(this), ast.name, ast.getter, ast.setter);
}
visitMethodCall(ast:MethodCall) {
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
}
visitFunctionCall(ast:FunctionCall) {
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
}
visitLiteralArray(ast:LiteralArray) {
return new LiteralArray(this.visitAll(ast.expressions));
}
visitLiteralMap(ast:LiteralMap) {
return new LiteralMap(ast.keys, this.visitAll(ast.values));
}
visitBinary(ast:Binary) {
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
}
visitPrefixNot(ast:PrefixNot) {
return new PrefixNot(ast.expression.visit(this));
}
visitConditional(ast:Conditional) {
return new Conditional(
ast.condition.visit(this),
ast.trueExp.visit(this),
ast.falseExp.visit(this)
);
}
visitPipe(ast:Pipe) {
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
}
visitKeyedAccess(ast:KeyedAccess) {
return new KeyedAccess(ast.obj.visit(this), ast.key.visit(this));
}
visitAll(asts:List) {
var res = ListWrapper.createFixedSize(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
return res;
}
}
var _evalListCache = [[],[0],[0,0],[0,0,0],[0,0,0,0],[0,0,0,0,0],
[0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0]];
function evalList(context, locals, exps:List){
var length = exps.length;
if (length > 10) {
throw new BaseException("Cannot have more than 10 argument");
}
var result = _evalListCache[length];
for (var i = 0; i < length; i++) {
result[i] = exps[i].eval(context, locals);
}
return result;
}

View File

@ -0,0 +1,388 @@
import {isBlank, isPresent, FunctionWrapper, BaseException} from "angular2/src/facade/lang";
import {List, Map, ListWrapper, StringMapWrapper} from "angular2/src/facade/collection";
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class AST {
eval(context, locals) { throw new BaseException("Not supported"); }
get isAssignable(): boolean { return false; }
assign(context, locals, value) { throw new BaseException("Not supported"); }
visit(visitor): any { return null; }
toString(): string { return "AST"; }
}
export class EmptyExpr extends AST {
eval(context, locals) { return null; }
visit(visitor) {
// do nothing
}
}
export class ImplicitReceiver extends AST {
eval(context, locals) { return context; }
visit(visitor) { return visitor.visitImplicitReceiver(this); }
}
/**
* Multiple expressions separated by a semicolon.
*/
export class Chain extends AST {
constructor(public expressions: List<any>) { super(); }
eval(context, locals) {
var result;
for (var i = 0; i < this.expressions.length; i++) {
var last = this.expressions[i].eval(context, locals);
if (isPresent(last)) result = last;
}
return result;
}
visit(visitor) { return visitor.visitChain(this); }
}
export class Conditional extends AST {
constructor(public condition: AST, public trueExp: AST, public falseExp: AST) { super(); }
eval(context, locals) {
if (this.condition.eval(context, locals)) {
return this.trueExp.eval(context, locals);
} else {
return this.falseExp.eval(context, locals);
}
}
visit(visitor) { return visitor.visitConditional(this); }
}
export class AccessMember extends AST {
constructor(public receiver: AST, public name: string, public getter: Function,
public setter: Function) {
super();
}
eval(context, locals) {
if (this.receiver instanceof ImplicitReceiver && isPresent(locals) &&
locals.contains(this.name)) {
return locals.get(this.name);
} else {
var evaluatedReceiver = this.receiver.eval(context, locals);
return this.getter(evaluatedReceiver);
}
}
get isAssignable(): boolean { return true; }
assign(context, locals, value) {
var evaluatedContext = this.receiver.eval(context, locals);
if (this.receiver instanceof ImplicitReceiver && isPresent(locals) &&
locals.contains(this.name)) {
throw new BaseException(`Cannot reassign a variable binding ${this.name}`);
} else {
return this.setter(evaluatedContext, value);
}
}
visit(visitor) { return visitor.visitAccessMember(this); }
}
export class KeyedAccess extends AST {
constructor(public obj: AST, public key: AST) { super(); }
eval(context, locals) {
var obj: any = this.obj.eval(context, locals);
var key: any = this.key.eval(context, locals);
return obj[key];
}
get isAssignable(): boolean { return true; }
assign(context, locals, value) {
var obj: any = this.obj.eval(context, locals);
var key: any = this.key.eval(context, locals);
obj[key] = value;
return value;
}
visit(visitor) { return visitor.visitKeyedAccess(this); }
}
export class Pipe extends AST {
constructor(public exp: AST, public name: string, public args: List<any>,
public inBinding: boolean) {
super();
}
visit(visitor) { return visitor.visitPipe(this); }
}
export class LiteralPrimitive extends AST {
constructor(public value) { super(); }
eval(context, locals) { return this.value; }
visit(visitor) { return visitor.visitLiteralPrimitive(this); }
}
export class LiteralArray extends AST {
constructor(public expressions: List<any>) { super(); }
eval(context, locals) {
return ListWrapper.map(this.expressions, (e) => e.eval(context, locals));
}
visit(visitor) { return visitor.visitLiteralArray(this); }
}
export class LiteralMap extends AST {
constructor(public keys: List<any>, public values: List<any>) { super(); }
eval(context, locals) {
var res = StringMapWrapper.create();
for (var i = 0; i < this.keys.length; ++i) {
StringMapWrapper.set(res, this.keys[i], this.values[i].eval(context, locals));
}
return res;
}
visit(visitor) { return visitor.visitLiteralMap(this); }
}
export class Interpolation extends AST {
constructor(public strings: List<any>, public expressions: List<any>) { super(); }
eval(context, locals) { throw new BaseException("evaluating an Interpolation is not supported"); }
visit(visitor) { visitor.visitInterpolation(this); }
}
export class Binary extends AST {
constructor(public operation: string, public left: AST, public right: AST) { super(); }
eval(context, locals) {
var left: any = this.left.eval(context, locals);
switch (this.operation) {
case '&&':
return left && this.right.eval(context, locals);
case '||':
return left || this.right.eval(context, locals);
}
var right: any = this.right.eval(context, locals);
switch (this.operation) {
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
case '%':
return left % right;
case '==':
return left == right;
case '!=':
return left != right;
case '===':
return left === right;
case '!==':
return left !== right;
case '<':
return left < right;
case '>':
return left > right;
case '<=':
return left <= right;
case '>=':
return left >= right;
case '^':
return left ^ right;
case '&':
return left & right;
}
throw 'Internal error [$operation] not handled';
}
visit(visitor) { return visitor.visitBinary(this); }
}
export class PrefixNot extends AST {
constructor(public expression: AST) { super(); }
eval(context, locals) { return !this.expression.eval(context, locals); }
visit(visitor) { return visitor.visitPrefixNot(this); }
}
export class Assignment extends AST {
constructor(public target: AST, public value: AST) { super(); }
eval(context, locals) {
return this.target.assign(context, locals, this.value.eval(context, locals));
}
visit(visitor) { return visitor.visitAssignment(this); }
}
export class MethodCall extends AST {
constructor(public receiver: AST, public name: string, public fn: Function,
public args: List<any>) {
super();
}
eval(context, locals) {
var evaluatedArgs = evalList(context, locals, this.args);
if (this.receiver instanceof ImplicitReceiver && isPresent(locals) &&
locals.contains(this.name)) {
var fn = locals.get(this.name);
return FunctionWrapper.apply(fn, evaluatedArgs);
} else {
var evaluatedReceiver = this.receiver.eval(context, locals);
return this.fn(evaluatedReceiver, evaluatedArgs);
}
}
visit(visitor) { return visitor.visitMethodCall(this); }
}
export class FunctionCall extends AST {
constructor(public target: AST, public args: List<any>) { super(); }
eval(context, locals) {
var obj: any = this.target.eval(context, locals);
if (!(obj instanceof Function)) {
throw new BaseException(`${obj} is not a function`);
}
return FunctionWrapper.apply(obj, evalList(context, locals, this.args));
}
visit(visitor) { return visitor.visitFunctionCall(this); }
}
export class ASTWithSource extends AST {
constructor(public ast: AST, public source: string, public location: string) { super(); }
eval(context, locals) { return this.ast.eval(context, locals); }
get isAssignable(): boolean { return this.ast.isAssignable; }
assign(context, locals, value) { return this.ast.assign(context, locals, value); }
visit(visitor) { return this.ast.visit(visitor); }
toString(): string { return `${this.source} in ${this.location}`; }
}
export class TemplateBinding {
constructor(public key: string, public keyIsVar: boolean, public name: string,
public expression: ASTWithSource) {}
}
// INTERFACE
export class AstVisitor {
visitAccessMember(ast: AccessMember) {}
visitAssignment(ast: Assignment) {}
visitBinary(ast: Binary) {}
visitChain(ast: Chain) {}
visitConditional(ast: Conditional) {}
visitPipe(ast: Pipe) {}
visitFunctionCall(ast: FunctionCall) {}
visitImplicitReceiver(ast: ImplicitReceiver) {}
visitKeyedAccess(ast: KeyedAccess) {}
visitLiteralArray(ast: LiteralArray) {}
visitLiteralMap(ast: LiteralMap) {}
visitLiteralPrimitive(ast: LiteralPrimitive) {}
visitMethodCall(ast: MethodCall) {}
visitPrefixNot(ast: PrefixNot) {}
}
export class AstTransformer {
visitImplicitReceiver(ast: ImplicitReceiver) { return ast; }
visitInterpolation(ast: Interpolation) {
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
}
visitLiteralPrimitive(ast: LiteralPrimitive) { return new LiteralPrimitive(ast.value); }
visitAccessMember(ast: AccessMember) {
return new AccessMember(ast.receiver.visit(this), ast.name, ast.getter, ast.setter);
}
visitMethodCall(ast: MethodCall) {
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
}
visitFunctionCall(ast: FunctionCall) {
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
}
visitLiteralArray(ast: LiteralArray) { return new LiteralArray(this.visitAll(ast.expressions)); }
visitLiteralMap(ast: LiteralMap) { return new LiteralMap(ast.keys, this.visitAll(ast.values)); }
visitBinary(ast: Binary) {
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
}
visitPrefixNot(ast: PrefixNot) { return new PrefixNot(ast.expression.visit(this)); }
visitConditional(ast: Conditional) {
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
ast.falseExp.visit(this));
}
visitPipe(ast: Pipe) {
return new Pipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.inBinding);
}
visitKeyedAccess(ast: KeyedAccess) {
return new KeyedAccess(ast.obj.visit(this), ast.key.visit(this));
}
visitAll(asts: List<any>) {
var res = ListWrapper.createFixedSize(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
return res;
}
}
var _evalListCache = [
[],
[0],
[0, 0],
[0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
];
function evalList(context, locals, exps: List<any>) {
var length = exps.length;
if (length > 10) {
throw new BaseException("Cannot have more than 10 argument");
}
var result = _evalListCache[length];
for (var i = 0; i < length; i++) {
result[i] = exps[i].eval(context, locals);
}
return result;
}

View File

@ -1,476 +0,0 @@
import {Injectable} from 'angular2/src/di/annotations_impl';
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
import {int, NumberWrapper, StringJoiner, StringWrapper, BaseException} from "angular2/src/facade/lang";
export const TOKEN_TYPE_CHARACTER = 1;
export const TOKEN_TYPE_IDENTIFIER = 2;
export const TOKEN_TYPE_KEYWORD = 3;
export const TOKEN_TYPE_STRING = 4;
export const TOKEN_TYPE_OPERATOR = 5;
export const TOKEN_TYPE_NUMBER = 6;
@Injectable()
export class Lexer {
text:string;
tokenize(text:string):List {
var scanner = new _Scanner(text);
var tokens = [];
var token = scanner.scanToken();
while (token != null) {
ListWrapper.push(tokens, token);
token = scanner.scanToken();
}
return tokens;
}
}
export class Token {
index:int;
type:int;
_numValue:number;
_strValue:string;
constructor(index:int, type:int, numValue:number, strValue:string) {
/**
* NOTE: To ensure that this constructor creates the same hidden class each time, ensure that
* all the fields are assigned to in the exact same order in each run of this constructor.
*/
this.index = index;
this.type = type;
this._numValue = numValue;
this._strValue = strValue;
}
isCharacter(code:int):boolean {
return (this.type == TOKEN_TYPE_CHARACTER && this._numValue == code);
}
isNumber():boolean {
return (this.type == TOKEN_TYPE_NUMBER);
}
isString():boolean {
return (this.type == TOKEN_TYPE_STRING);
}
isOperator(operater:string):boolean {
return (this.type == TOKEN_TYPE_OPERATOR && this._strValue == operater);
}
isIdentifier():boolean {
return (this.type == TOKEN_TYPE_IDENTIFIER);
}
isKeyword():boolean {
return (this.type == TOKEN_TYPE_KEYWORD);
}
isKeywordVar():boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "var");
}
isKeywordNull():boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "null");
}
isKeywordUndefined():boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "undefined");
}
isKeywordTrue():boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "true");
}
isKeywordFalse():boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "false");
}
toNumber():number {
// -1 instead of NULL ok?
return (this.type == TOKEN_TYPE_NUMBER) ? this._numValue : -1;
}
toString():string {
var type:int = this.type;
if (type >= TOKEN_TYPE_CHARACTER && type <= TOKEN_TYPE_STRING) {
return this._strValue;
} else if (type == TOKEN_TYPE_NUMBER) {
return this._numValue.toString();
} else {
return null;
}
}
}
function newCharacterToken(index:int, code:int):Token {
return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code));
}
function newIdentifierToken(index:int, text:string):Token {
return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text);
}
function newKeywordToken(index:int, text:string):Token {
return new Token(index, TOKEN_TYPE_KEYWORD, 0, text);
}
function newOperatorToken(index:int, text:string):Token {
return new Token(index, TOKEN_TYPE_OPERATOR, 0, text);
}
function newStringToken(index:int, text:string):Token {
return new Token(index, TOKEN_TYPE_STRING, 0, text);
}
function newNumberToken(index:int, n:number):Token {
return new Token(index, TOKEN_TYPE_NUMBER, n, "");
}
export var EOF:Token = new Token(-1, 0, 0, "");
export const $EOF = 0;
export const $TAB = 9;
export const $LF = 10;
export const $VTAB = 11;
export const $FF = 12;
export const $CR = 13;
export const $SPACE = 32;
export const $BANG = 33;
export const $DQ = 34;
export const $HASH = 35;
export const $$ = 36;
export const $PERCENT = 37;
export const $AMPERSAND = 38;
export const $SQ = 39;
export const $LPAREN = 40;
export const $RPAREN = 41;
export const $STAR = 42;
export const $PLUS = 43;
export const $COMMA = 44;
export const $MINUS = 45;
export const $PERIOD = 46;
export const $SLASH = 47;
export const $COLON = 58;
export const $SEMICOLON = 59;
export const $LT = 60;
export const $EQ = 61;
export const $GT = 62;
export const $QUESTION = 63;
const $0 = 48;
const $9 = 57;
const $A = 65, $E = 69, $Z = 90;
export const $LBRACKET = 91;
export const $BACKSLASH = 92;
export const $RBRACKET = 93;
const $CARET = 94;
const $_ = 95;
const $a = 97, $e = 101, $f = 102, $n = 110, $r = 114, $t = 116, $u = 117, $v = 118, $z = 122;
export const $LBRACE = 123;
export const $BAR = 124;
export const $RBRACE = 125;
const $NBSP = 160;
export class ScannerError extends BaseException {
message:string;
constructor(message) {
super();
this.message = message;
}
toString() {
return this.message;
}
}
class _Scanner {
input:string;
length:int;
peek:int;
index:int;
constructor(input:string) {
this.input = input;
this.length = input.length;
this.peek = 0;
this.index = -1;
this.advance();
}
advance() {
this.peek = ++this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
}
scanToken():Token {
var input = this.input,
length = this.length,
peek = this.peek,
index = this.index;
// Skip whitespace.
while (peek <= $SPACE) {
if (++index >= length) {
peek = $EOF;
break;
} else {
peek = StringWrapper.charCodeAt(input, index);
}
}
this.peek = peek;
this.index = index;
if (index >= length) {
return null;
}
// Handle identifiers and numbers.
if (isIdentifierStart(peek)) return this.scanIdentifier();
if (isDigit(peek)) return this.scanNumber(index);
var start:int = index;
switch (peek) {
case $PERIOD:
this.advance();
return isDigit(this.peek) ? this.scanNumber(start) :
newCharacterToken(start, $PERIOD);
case $LPAREN: case $RPAREN:
case $LBRACE: case $RBRACE:
case $LBRACKET: case $RBRACKET:
case $COMMA:
case $COLON:
case $SEMICOLON:
return this.scanCharacter(start, peek);
case $SQ:
case $DQ:
return this.scanString();
case $HASH:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $PLUS:
case $MINUS:
case $STAR:
case $SLASH:
case $PERCENT:
case $CARET:
case $QUESTION:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $LT:
case $GT:
case $BANG:
case $EQ:
return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '=');
case $AMPERSAND:
return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
case $BAR:
return this.scanComplexOperator(start, $BAR, '|', '|');
case $NBSP:
while (isWhitespace(this.peek)) this.advance();
return this.scanToken();
}
this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
return null;
}
scanCharacter(start:int, code:int):Token {
assert(this.peek == code);
this.advance();
return newCharacterToken(start, code);
}
scanOperator(start:int, str:string):Token {
assert(this.peek == StringWrapper.charCodeAt(str, 0));
assert(SetWrapper.has(OPERATORS, str));
this.advance();
return newOperatorToken(start, str);
}
scanComplexOperator(start:int, code:int, one:string, two:string):Token {
assert(this.peek == StringWrapper.charCodeAt(one, 0));
this.advance();
var str:string = one;
while (this.peek == code) {
this.advance();
str += two;
}
assert(SetWrapper.has(OPERATORS, str));
return newOperatorToken(start, str);
}
scanIdentifier():Token {
assert(isIdentifierStart(this.peek));
var start:int = this.index;
this.advance();
while (isIdentifierPart(this.peek)) this.advance();
var str:string = this.input.substring(start, this.index);
if (SetWrapper.has(KEYWORDS, str)) {
return newKeywordToken(start, str);
} else {
return newIdentifierToken(start, str);
}
}
scanNumber(start:int):Token {
assert(isDigit(this.peek));
var simple:boolean = (this.index === start);
this.advance(); // Skip initial digit.
while (true) {
if (isDigit(this.peek)) {
// Do nothing.
} else if (this.peek == $PERIOD) {
simple = false;
} else if (isExponentStart(this.peek)) {
this.advance();
if (isExponentSign(this.peek)) this.advance();
if (!isDigit(this.peek)) this.error('Invalid exponent', -1);
simple = false;
} else {
break;
}
this.advance();
}
var str:string = this.input.substring(start, this.index);
// TODO
var value:number = simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
return newNumberToken(start, value);
}
scanString():Token {
assert(this.peek == $SQ || this.peek == $DQ);
var start:int = this.index;
var quote:int = this.peek;
this.advance(); // Skip initial quote.
var buffer:StringJoiner;
var marker:int = this.index;
var input:string = this.input;
while (this.peek != quote) {
if (this.peek == $BACKSLASH) {
if (buffer == null) buffer = new StringJoiner();
buffer.add(input.substring(marker, this.index));
this.advance();
var unescapedCode:int;
if (this.peek == $u) {
// 4 character hex code for unicode character.
var hex:string = input.substring(this.index + 1, this.index + 5);
try {
unescapedCode = NumberWrapper.parseInt(hex, 16);
} catch (e) {
this.error(`Invalid unicode escape [\\u${hex}]`, 0);
}
for (var i:int = 0; i < 5; i++) {
this.advance();
}
} else {
unescapedCode = unescape(this.peek);
this.advance();
}
buffer.add(StringWrapper.fromCharCode(unescapedCode));
marker = this.index;
} else if (this.peek == $EOF) {
this.error('Unterminated quote', 0);
} else {
this.advance();
}
}
var last:string = input.substring(marker, this.index);
this.advance(); // Skip terminating quote.
// Compute the unescaped string value.
var unescaped:string = last;
if (buffer != null) {
buffer.add(last);
unescaped = buffer.toString();
}
return newStringToken(start, unescaped);
}
error(message:string, offset:int) {
var position:int = this.index + offset;
throw new ScannerError(`Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
}
}
function isWhitespace(code:int):boolean {
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
}
function isIdentifierStart(code:int):boolean {
return ($a <= code && code <= $z) ||
($A <= code && code <= $Z) ||
(code == $_) ||
(code == $$);
}
function isIdentifierPart(code:int):boolean {
return ($a <= code && code <= $z) ||
($A <= code && code <= $Z) ||
($0 <= code && code <= $9) ||
(code == $_) ||
(code == $$);
}
function isDigit(code:int):boolean {
return $0 <= code && code <= $9;
}
function isExponentStart(code:int):boolean {
return code == $e || code == $E;
}
function isExponentSign(code:int):boolean {
return code == $MINUS || code == $PLUS;
}
function unescape(code:int):int {
switch(code) {
case $n: return $LF;
case $f: return $FF;
case $r: return $CR;
case $t: return $TAB;
case $v: return $VTAB;
default: return code;
}
}
var OPERATORS = SetWrapper.createFromList([
'+',
'-',
'*',
'/',
'%',
'^',
'=',
'==',
'!=',
'===',
'!==',
'<',
'>',
'<=',
'>=',
'&&',
'||',
'&',
'|',
'!',
'?',
'#'
]);
var KEYWORDS = SetWrapper.createFromList([
'var',
'null',
'undefined',
'true',
'false'
]);

View File

@ -0,0 +1,452 @@
import {Injectable} from 'angular2/src/di/decorators';
import {List, ListWrapper, SetWrapper} from "angular2/src/facade/collection";
import {
int,
NumberWrapper,
StringJoiner,
StringWrapper,
BaseException
} from "angular2/src/facade/lang";
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export const TOKEN_TYPE_CHARACTER = 1;
export const TOKEN_TYPE_IDENTIFIER = 2;
export const TOKEN_TYPE_KEYWORD = 3;
export const TOKEN_TYPE_STRING = 4;
export const TOKEN_TYPE_OPERATOR = 5;
export const TOKEN_TYPE_NUMBER = 6;
@Injectable()
export class Lexer {
tokenize(text: string): List<any> {
var scanner = new _Scanner(text);
var tokens = [];
var token = scanner.scanToken();
while (token != null) {
ListWrapper.push(tokens, token);
token = scanner.scanToken();
}
return tokens;
}
}
export class Token {
constructor(public index: int, public type: int, public numValue: number,
public strValue: string) {}
isCharacter(code: int): boolean {
return (this.type == TOKEN_TYPE_CHARACTER && this.numValue == code);
}
isNumber(): boolean { return (this.type == TOKEN_TYPE_NUMBER); }
isString(): boolean { return (this.type == TOKEN_TYPE_STRING); }
isOperator(operater: string): boolean {
return (this.type == TOKEN_TYPE_OPERATOR && this.strValue == operater);
}
isIdentifier(): boolean { return (this.type == TOKEN_TYPE_IDENTIFIER); }
isKeyword(): boolean { return (this.type == TOKEN_TYPE_KEYWORD); }
isKeywordVar(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "var"); }
isKeywordNull(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "null"); }
isKeywordUndefined(): boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "undefined");
}
isKeywordTrue(): boolean { return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "true"); }
isKeywordFalse(): boolean {
return (this.type == TOKEN_TYPE_KEYWORD && this.strValue == "false");
}
toNumber(): number {
// -1 instead of NULL ok?
return (this.type == TOKEN_TYPE_NUMBER) ? this.numValue : -1;
}
toString(): string {
var t: int = this.type;
if (t >= TOKEN_TYPE_CHARACTER && t <= TOKEN_TYPE_STRING) {
return this.strValue;
} else if (t == TOKEN_TYPE_NUMBER) {
return this.numValue.toString();
} else {
return null;
}
}
}
function newCharacterToken(index: int, code: int): Token {
return new Token(index, TOKEN_TYPE_CHARACTER, code, StringWrapper.fromCharCode(code));
}
function newIdentifierToken(index: int, text: string): Token {
return new Token(index, TOKEN_TYPE_IDENTIFIER, 0, text);
}
function newKeywordToken(index: int, text: string): Token {
return new Token(index, TOKEN_TYPE_KEYWORD, 0, text);
}
function newOperatorToken(index: int, text: string): Token {
return new Token(index, TOKEN_TYPE_OPERATOR, 0, text);
}
function newStringToken(index: int, text: string): Token {
return new Token(index, TOKEN_TYPE_STRING, 0, text);
}
function newNumberToken(index: int, n: number): Token {
return new Token(index, TOKEN_TYPE_NUMBER, n, "");
}
export var EOF: Token = new Token(-1, 0, 0, "");
export const $EOF = 0;
export const $TAB = 9;
export const $LF = 10;
export const $VTAB = 11;
export const $FF = 12;
export const $CR = 13;
export const $SPACE = 32;
export const $BANG = 33;
export const $DQ = 34;
export const $HASH = 35;
export const $$ = 36;
export const $PERCENT = 37;
export const $AMPERSAND = 38;
export const $SQ = 39;
export const $LPAREN = 40;
export const $RPAREN = 41;
export const $STAR = 42;
export const $PLUS = 43;
export const $COMMA = 44;
export const $MINUS = 45;
export const $PERIOD = 46;
export const $SLASH = 47;
export const $COLON = 58;
export const $SEMICOLON = 59;
export const $LT = 60;
export const $EQ = 61;
export const $GT = 62;
export const $QUESTION = 63;
const $0 = 48;
const $9 = 57;
const $A = 65, $E = 69, $Z = 90;
export const $LBRACKET = 91;
export const $BACKSLASH = 92;
export const $RBRACKET = 93;
const $CARET = 94;
const $_ = 95;
const $a = 97, $e = 101, $f = 102, $n = 110, $r = 114, $t = 116, $u = 117, $v = 118, $z = 122;
export const $LBRACE = 123;
export const $BAR = 124;
export const $RBRACE = 125;
const $NBSP = 160;
export class ScannerError extends BaseException {
message: string;
constructor(message) {
super();
this.message = message;
}
toString() { return this.message; }
}
class _Scanner {
length: int;
peek: int;
index: int;
constructor(public input: string) {
this.length = input.length;
this.peek = 0;
this.index = -1;
this.advance();
}
advance() {
this.peek =
++this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
}
scanToken(): Token {
var input = this.input, length = this.length, peek = this.peek, index = this.index;
// Skip whitespace.
while (peek <= $SPACE) {
if (++index >= length) {
peek = $EOF;
break;
} else {
peek = StringWrapper.charCodeAt(input, index);
}
}
this.peek = peek;
this.index = index;
if (index >= length) {
return null;
}
// Handle identifiers and numbers.
if (isIdentifierStart(peek)) return this.scanIdentifier();
if (isDigit(peek)) return this.scanNumber(index);
var start: int = index;
switch (peek) {
case $PERIOD:
this.advance();
return isDigit(this.peek) ? this.scanNumber(start) : newCharacterToken(start, $PERIOD);
case $LPAREN:
case $RPAREN:
case $LBRACE:
case $RBRACE:
case $LBRACKET:
case $RBRACKET:
case $COMMA:
case $COLON:
case $SEMICOLON:
return this.scanCharacter(start, peek);
case $SQ:
case $DQ:
return this.scanString();
case $HASH:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $PLUS:
case $MINUS:
case $STAR:
case $SLASH:
case $PERCENT:
case $CARET:
case $QUESTION:
return this.scanOperator(start, StringWrapper.fromCharCode(peek));
case $LT:
case $GT:
case $BANG:
case $EQ:
return this.scanComplexOperator(start, $EQ, StringWrapper.fromCharCode(peek), '=');
case $AMPERSAND:
return this.scanComplexOperator(start, $AMPERSAND, '&', '&');
case $BAR:
return this.scanComplexOperator(start, $BAR, '|', '|');
case $NBSP:
while (isWhitespace(this.peek)) this.advance();
return this.scanToken();
}
this.error(`Unexpected character [${StringWrapper.fromCharCode(peek)}]`, 0);
return null;
}
scanCharacter(start: int, code: int): Token {
assert(this.peek == code);
this.advance();
return newCharacterToken(start, code);
}
scanOperator(start: int, str: string): Token {
assert(this.peek == StringWrapper.charCodeAt(str, 0));
assert(SetWrapper.has(OPERATORS, str));
this.advance();
return newOperatorToken(start, str);
}
scanComplexOperator(start: int, code: int, one: string, two: string): Token {
assert(this.peek == StringWrapper.charCodeAt(one, 0));
this.advance();
var str: string = one;
while (this.peek == code) {
this.advance();
str += two;
}
assert(SetWrapper.has(OPERATORS, str));
return newOperatorToken(start, str);
}
scanIdentifier(): Token {
assert(isIdentifierStart(this.peek));
var start: int = this.index;
this.advance();
while (isIdentifierPart(this.peek)) this.advance();
var str: string = this.input.substring(start, this.index);
if (SetWrapper.has(KEYWORDS, str)) {
return newKeywordToken(start, str);
} else {
return newIdentifierToken(start, str);
}
}
scanNumber(start: int): Token {
assert(isDigit(this.peek));
var simple: boolean = (this.index === start);
this.advance(); // Skip initial digit.
while (true) {
if (isDigit(this.peek)) {
// Do nothing.
} else if (this.peek == $PERIOD) {
simple = false;
} else if (isExponentStart(this.peek)) {
this.advance();
if (isExponentSign(this.peek)) this.advance();
if (!isDigit(this.peek)) this.error('Invalid exponent', -1);
simple = false;
} else {
break;
}
this.advance();
}
var str: string = this.input.substring(start, this.index);
// TODO
var value: number =
simple ? NumberWrapper.parseIntAutoRadix(str) : NumberWrapper.parseFloat(str);
return newNumberToken(start, value);
}
scanString(): Token {
assert(this.peek == $SQ || this.peek == $DQ);
var start: int = this.index;
var quote: int = this.peek;
this.advance(); // Skip initial quote.
var buffer: StringJoiner;
var marker: int = this.index;
var input: string = this.input;
while (this.peek != quote) {
if (this.peek == $BACKSLASH) {
if (buffer == null) buffer = new StringJoiner();
buffer.add(input.substring(marker, this.index));
this.advance();
var unescapedCode: int;
if (this.peek == $u) {
// 4 character hex code for unicode character.
var hex: string = input.substring(this.index + 1, this.index + 5);
try {
unescapedCode = NumberWrapper.parseInt(hex, 16);
} catch (e) {
this.error(`Invalid unicode escape [\\u${hex}]`, 0);
}
for (var i: int = 0; i < 5; i++) {
this.advance();
}
} else {
unescapedCode = unescape(this.peek);
this.advance();
}
buffer.add(StringWrapper.fromCharCode(unescapedCode));
marker = this.index;
} else if (this.peek == $EOF) {
this.error('Unterminated quote', 0);
} else {
this.advance();
}
}
var last: string = input.substring(marker, this.index);
this.advance(); // Skip terminating quote.
// Compute the unescaped string value.
var unescaped: string = last;
if (buffer != null) {
buffer.add(last);
unescaped = buffer.toString();
}
return newStringToken(start, unescaped);
}
error(message: string, offset: int) {
var position: int = this.index + offset;
throw new ScannerError(
`Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
}
}
function isWhitespace(code: int): boolean {
return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
}
function isIdentifierStart(code: int): boolean {
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || (code == $_) || (code == $$);
}
function isIdentifierPart(code: int): boolean {
return ($a <= code && code <= $z) || ($A <= code && code <= $Z) || ($0 <= code && code <= $9) ||
(code == $_) || (code == $$);
}
function isDigit(code: int): boolean {
return $0 <= code && code <= $9;
}
function isExponentStart(code: int): boolean {
return code == $e || code == $E;
}
function isExponentSign(code: int): boolean {
return code == $MINUS || code == $PLUS;
}
function unescape(code: int): int {
switch (code) {
case $n:
return $LF;
case $f:
return $FF;
case $r:
return $CR;
case $t:
return $TAB;
case $v:
return $VTAB;
default:
return code;
}
}
var OPERATORS = SetWrapper.createFromList([
'+',
'-',
'*',
'/',
'%',
'^',
'=',
'==',
'!=',
'===',
'!==',
'<',
'>',
'<=',
'>=',
'&&',
'||',
'&',
'|',
'!',
'?',
'#'
]);
var KEYWORDS = SetWrapper.createFromList(['var', 'null', 'undefined', 'true', 'false']);

View File

@ -1,16 +1,15 @@
import {isPresent, BaseException} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class Locals {
parent:Locals;
current:Map;
constructor(public parent: Locals, public current: Map<any, any>) {}
constructor(parent:Locals, current:Map) {
this.parent = parent;
this.current = current;
}
contains(name:string):boolean {
contains(name: string): boolean {
if (MapWrapper.contains(this.current, name)) {
return true;
}
@ -22,7 +21,7 @@ export class Locals {
return false;
}
get(name:string) {
get(name: string) {
if (MapWrapper.contains(this.current, name)) {
return MapWrapper.get(this.current, name);
}
@ -34,7 +33,7 @@ export class Locals {
throw new BaseException(`Cannot find '${name}'`);
}
set(name:string, value):void {
set(name: string, value): void {
// TODO(rado): consider removing this check if we can guarantee this is not
// exposed to the public API.
// TODO: vsavkin maybe it should check only the local map
@ -45,7 +44,5 @@ export class Locals {
}
}
clearValues():void {
MapWrapper.clearValues(this.current);
}
clearValues(): void { MapWrapper.clearValues(this.current); }
}

View File

@ -1,8 +1,28 @@
import {Injectable} from 'angular2/src/di/annotations_impl';
import {int, isBlank, isPresent, BaseException, StringWrapper, RegExpWrapper} from 'angular2/src/facade/lang';
import {Injectable} from 'angular2/src/di/decorators';
import {
int,
isBlank,
isPresent,
BaseException,
StringWrapper,
RegExpWrapper
} from 'angular2/src/facade/lang';
import {ListWrapper, List} from 'angular2/src/facade/collection';
import {Lexer, EOF, Token, $PERIOD, $COLON, $SEMICOLON, $LBRACKET, $RBRACKET,
$COMMA, $LBRACE, $RBRACE, $LPAREN, $RPAREN} from './lexer';
import {
Lexer,
EOF,
Token,
$PERIOD,
$COLON,
$SEMICOLON,
$LBRACKET,
$RBRACKET,
$COMMA,
$LBRACE,
$RBRACE,
$LPAREN,
$RPAREN
} from './lexer';
import {reflector, Reflector} from 'angular2/src/reflection/reflection';
import {
AST,
@ -10,7 +30,6 @@ import {
ImplicitReceiver,
AccessMember,
LiteralPrimitive,
Expression,
Binary,
PrefixNot,
Conditional,
@ -23,10 +42,15 @@ import {
Interpolation,
MethodCall,
FunctionCall,
TemplateBindings,
TemplateBinding,
ASTWithSource
} from './ast';
} from './ast';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
var _implicitReceiver = new ImplicitReceiver();
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
@ -34,40 +58,40 @@ var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
@Injectable()
export class Parser {
_lexer:Lexer;
_reflector:Reflector;
constructor(lexer:Lexer, providedReflector:Reflector = null){
_lexer: Lexer;
_reflector: Reflector;
constructor(lexer: Lexer, providedReflector: Reflector = null) {
this._lexer = lexer;
this._reflector = isPresent(providedReflector) ? providedReflector : reflector;
}
parseAction(input:string, location:any):ASTWithSource {
parseAction(input: string, location: any): ASTWithSource {
var tokens = this._lexer.tokenize(input);
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
return new ASTWithSource(ast, input, location);
}
parseBinding(input:string, location:any):ASTWithSource {
parseBinding(input: string, location: any): ASTWithSource {
var tokens = this._lexer.tokenize(input);
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
return new ASTWithSource(ast, input, location);
}
addPipes(bindingAst:ASTWithSource, pipes:List<String>):ASTWithSource {
addPipes(bindingAst: ASTWithSource, pipes: List<string>): ASTWithSource {
if (ListWrapper.isEmpty(pipes)) return bindingAst;
var res = ListWrapper.reduce(pipes,
(result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
bindingAst.ast);
var res: AST = ListWrapper.reduce(
pipes, (result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
bindingAst.ast);
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
}
parseTemplateBindings(input:string, location:any):List<TemplateBinding> {
parseTemplateBindings(input: string, location: any): List<TemplateBinding> {
var tokens = this._lexer.tokenize(input);
return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
}
parseInterpolation(input:string, location:any):ASTWithSource {
parseInterpolation(input: string, location: any): ASTWithSource {
var parts = StringWrapper.split(input, INTERPOLATION_REGEXP);
if (parts.length <= 1) {
return null;
@ -75,9 +99,9 @@ export class Parser {
var strings = [];
var expressions = [];
for (var i=0; i<parts.length; i++) {
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
if (i%2 === 0) {
if (i % 2 === 0) {
// fixed string
ListWrapper.push(strings, part);
} else {
@ -89,46 +113,32 @@ export class Parser {
return new ASTWithSource(new Interpolation(strings, expressions), input, location);
}
wrapLiteralPrimitive(input:string, location:any):ASTWithSource {
wrapLiteralPrimitive(input: string, location: any): ASTWithSource {
return new ASTWithSource(new LiteralPrimitive(input), input, location);
}
}
class _ParseAST {
input:string;
location:any;
tokens:List<Token>;
reflector:Reflector;
parseAction:boolean;
index:int;
constructor(input:string, location:any, tokens:List, reflector:Reflector, parseAction:boolean) {
this.input = input;
this.location = location;
this.tokens = tokens;
index: int;
constructor(public input: string, public location: any, public tokens: List<any>,
public reflector: Reflector, public parseAction: boolean) {
this.index = 0;
this.reflector = reflector;
this.parseAction = parseAction;
}
peek(offset:int):Token {
peek(offset: int): Token {
var i = this.index + offset;
return i < this.tokens.length ? this.tokens[i] : EOF;
}
get next():Token {
return this.peek(0);
}
get next(): Token { return this.peek(0); }
get inputIndex():int {
get inputIndex(): int {
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
}
advance() {
this.index ++;
}
advance() { this.index++; }
optionalCharacter(code:int):boolean {
optionalCharacter(code: int): boolean {
if (this.next.isCharacter(code)) {
this.advance();
return true;
@ -137,7 +147,7 @@ class _ParseAST {
}
}
optionalKeywordVar():boolean {
optionalKeywordVar(): boolean {
if (this.peekKeywordVar()) {
this.advance();
return true;
@ -146,17 +156,15 @@ class _ParseAST {
}
}
peekKeywordVar():boolean {
return this.next.isKeywordVar() || this.next.isOperator('#');
}
peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); }
expectCharacter(code:int) {
expectCharacter(code: int) {
if (this.optionalCharacter(code)) return;
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
}
optionalOperator(op:string):boolean {
optionalOperator(op: string): boolean {
if (this.next.isOperator(op)) {
this.advance();
return true;
@ -165,12 +173,12 @@ class _ParseAST {
}
}
expectOperator(operator:string) {
expectOperator(operator: string) {
if (this.optionalOperator(operator)) return;
this.error(`Missing expected operator ${operator}`);
}
expectIdentifierOrKeyword():string {
expectIdentifierOrKeyword(): string {
var n = this.next;
if (!n.isIdentifier() && !n.isKeyword()) {
this.error(`Unexpected token ${n}, expected identifier or keyword`)
@ -179,7 +187,7 @@ class _ParseAST {
return n.toString();
}
expectIdentifierOrKeywordOrString():string {
expectIdentifierOrKeywordOrString(): string {
var n = this.next;
if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
this.error(`Unexpected token ${n}, expected identifier, keyword, or string`)
@ -188,17 +196,18 @@ class _ParseAST {
return n.toString();
}
parseChain():AST {
parseChain(): AST {
var exprs = [];
while (this.index < this.tokens.length) {
var expr = this.parsePipe();
ListWrapper.push(exprs, expr);
if (this.optionalCharacter($SEMICOLON)) {
if (! this.parseAction) {
if (!this.parseAction) {
this.error("Binding expression cannot contain chained expression");
}
while (this.optionalCharacter($SEMICOLON)){} //read all semicolons
while (this.optionalCharacter($SEMICOLON)) {
} // read all semicolons
} else if (this.index < this.tokens.length) {
this.error(`Unexpected token '${this.next}'`);
}
@ -353,7 +362,7 @@ class _ParseAST {
}
}
parseCallChain():AST {
parseCallChain(): AST {
var result = this.parsePrimary();
while (true) {
if (this.optionalCharacter($PERIOD)) {
@ -410,9 +419,9 @@ class _ParseAST {
return new LiteralPrimitive(value);
} else if (this.next.isString()) {
var value = this.next.toString();
var literalValue = this.next.toString();
this.advance();
return new LiteralPrimitive(value);
return new LiteralPrimitive(literalValue);
} else if (this.index >= this.tokens.length) {
this.error(`Unexpected end of expression: ${this.input}`);
@ -422,7 +431,7 @@ class _ParseAST {
}
}
parseExpressionList(terminator:int):List {
parseExpressionList(terminator: int): List<any> {
var result = [];
if (!this.next.isCharacter(terminator)) {
do {
@ -448,7 +457,7 @@ class _ParseAST {
return new LiteralMap(keys, values);
}
parseAccessMemberOrMethodCall(receiver):AST {
parseAccessMemberOrMethodCall(receiver): AST {
var id = this.expectIdentifierOrKeyword();
if (this.optionalCharacter($LPAREN)) {
@ -471,7 +480,7 @@ class _ParseAST {
}
parseInlinedPipe(result) {
do {
do {
if (this.parseAction) {
this.error("Cannot have a pipe in an action expression");
}
@ -481,7 +490,7 @@ class _ParseAST {
ListWrapper.push(args, this.parseExpression());
}
result = new Pipe(result, name, args, true);
} while(this.optionalOperator("|"));
} while (this.optionalOperator("|"));
return result;
}
@ -515,7 +524,7 @@ class _ParseAST {
parseTemplateBindings() {
var bindings = [];
while (this.index < this.tokens.length) {
var keyIsVar:boolean = this.optionalKeywordVar();
var keyIsVar: boolean = this.optionalKeywordVar();
var key = this.expectTemplateBindingKey();
this.optionalCharacter($COLON);
var name = null;
@ -542,13 +551,13 @@ class _ParseAST {
return bindings;
}
error(message:string, index:int = null) {
error(message: string, index: int = null) {
if (isBlank(index)) index = this.index;
var location = (index < this.tokens.length)
? `at column ${this.tokens[index].index + 1} in`
: `at the end of the expression`;
var location = (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
`at the end of the expression`;
throw new BaseException(`Parser Error: ${message} ${location} [${this.input}] in ${this.location}`);
throw new BaseException(
`Parser Error: ${message} ${location} [${this.input}] in ${this.location}`);
}
}

View File

@ -3,13 +3,21 @@ import {isBlank, isPresent, CONST} from 'angular2/src/facade/lang';
import {Pipe, WrappedValue, PipeFactory} from './pipe';
import {ChangeDetectorRef} from '../change_detector_ref';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* Implements async bindings to Observable.
*
* # Example
*
* In this example we bind the description observable to the DOM. The async pipe will convert an observable to the
* latest value it emitted. It will also request a change detection check when a new value is emitted.
* In this example we bind the description observable to the DOM. The async pipe will convert an
*observable to the
* latest value it emitted. It will also request a change detection check when a new value is
*emitted.
*
* ```
* @Component({
@ -28,15 +36,15 @@ import {ChangeDetectorRef} from '../change_detector_ref';
* @exportedAs angular2/pipes
*/
export class AsyncPipe extends Pipe {
_ref:ChangeDetectorRef;
_ref: ChangeDetectorRef;
_latestValue:Object;
_latestReturnedValue:Object;
_latestValue: Object;
_latestReturnedValue: Object;
_subscription:Object;
_observable:Observable;
_subscription: Object;
_observable: Observable;
constructor(ref:ChangeDetectorRef) {
constructor(ref: ChangeDetectorRef) {
super();
this._ref = ref;
this._latestValue = null;
@ -45,17 +53,15 @@ export class AsyncPipe extends Pipe {
this._observable = null;
}
supports(obs):boolean {
return ObservableWrapper.isObservable(obs);
}
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
onDestroy():void {
onDestroy(): void {
if (isPresent(this._subscription)) {
this._dispose();
}
}
transform(obs:Observable):any {
transform(obs: Observable): any {
if (isBlank(this._subscription)) {
this._subscribe(obs);
return null;
@ -74,15 +80,13 @@ export class AsyncPipe extends Pipe {
}
}
_subscribe(obs:Observable):void {
_subscribe(obs: Observable): void {
this._observable = obs;
this._subscription = ObservableWrapper.subscribe(obs,
value => this._updateLatestValue(value),
e => {throw e;}
);
this._subscription = ObservableWrapper.subscribe(obs, value => {this._updateLatestValue(value)},
e => { throw e; });
}
_dispose():void {
_dispose(): void {
ObservableWrapper.dispose(this._subscription);
this._latestValue = null;
this._latestReturnedValue = null;
@ -90,7 +94,7 @@ export class AsyncPipe extends Pipe {
this._observable = null;
}
_updateLatestValue(value:Object) {
_updateLatestValue(value: Object) {
this._latestValue = value;
this._ref.requestCheck();
}
@ -101,17 +105,11 @@ export class AsyncPipe extends Pipe {
*
* @exportedAs angular2/pipes
*/
@CONST()
export class AsyncPipeFactory extends PipeFactory {
@CONST()
constructor() {
super();
}
constructor() { super(); }
supports(obs):boolean {
return ObservableWrapper.isObservable(obs);
}
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
create(cdRef):Pipe {
return new AsyncPipe(cdRef);
}
create(cdRef): Pipe { return new AsyncPipe(cdRef); }
}

View File

@ -17,38 +17,37 @@ import {
import {WrappedValue, Pipe, PipeFactory} from './pipe';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
@CONST()
export class IterableChangesFactory extends PipeFactory {
@CONST()
constructor() {
super();
}
constructor() { super(); }
supports(obj):boolean {
return IterableChanges.supportsObj(obj);
}
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
create(cdRef):Pipe {
return new IterableChanges();
}
create(cdRef): Pipe { return new IterableChanges(); }
}
/**
* @exportedAs angular2/pipes
*/
export class IterableChanges extends Pipe {
_collection;
_length:int;
_linkedRecords:_DuplicateMap;
_unlinkedRecords:_DuplicateMap;
_previousItHead:CollectionChangeRecord;
_itHead:CollectionChangeRecord;
_itTail:CollectionChangeRecord;
_additionsHead:CollectionChangeRecord;
_additionsTail:CollectionChangeRecord;
_movesHead:CollectionChangeRecord;
_movesTail:CollectionChangeRecord;
_removalsHead:CollectionChangeRecord;
_removalsTail:CollectionChangeRecord;
private _collection;
private _length: int;
private _linkedRecords: _DuplicateMap;
private _unlinkedRecords: _DuplicateMap;
private _previousItHead: CollectionChangeRecord;
private _itHead: CollectionChangeRecord;
private _itTail: CollectionChangeRecord;
private _additionsHead: CollectionChangeRecord;
private _additionsTail: CollectionChangeRecord;
private _movesHead: CollectionChangeRecord;
private _movesTail: CollectionChangeRecord;
private _removalsHead: CollectionChangeRecord;
private _removalsTail: CollectionChangeRecord;
constructor() {
super();
@ -70,58 +69,50 @@ export class IterableChanges extends Pipe {
this._removalsTail = null;
}
static supportsObj(obj):boolean {
return isListLikeIterable(obj);
}
static supportsObj(obj): boolean { return isListLikeIterable(obj); }
supports(obj):boolean {
return IterableChanges.supportsObj(obj);
}
supports(obj): boolean { return IterableChanges.supportsObj(obj); }
get collection() {
return this._collection;
}
get collection() { return this._collection; }
get length():int {
return this._length;
}
get length(): int { return this._length; }
forEachItem(fn:Function) {
var record:CollectionChangeRecord;
forEachItem(fn: Function) {
var record: CollectionChangeRecord;
for (record = this._itHead; record !== null; record = record._next) {
fn(record);
}
}
forEachPreviousItem(fn:Function) {
var record:CollectionChangeRecord;
forEachPreviousItem(fn: Function) {
var record: CollectionChangeRecord;
for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
fn(record);
}
}
forEachAddedItem(fn:Function){
var record:CollectionChangeRecord;
forEachAddedItem(fn: Function) {
var record: CollectionChangeRecord;
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
fn(record);
}
}
forEachMovedItem(fn:Function) {
var record:CollectionChangeRecord;
forEachMovedItem(fn: Function) {
var record: CollectionChangeRecord;
for (record = this._movesHead; record !== null; record = record._nextMoved) {
fn(record);
}
}
forEachRemovedItem(fn:Function){
var record:CollectionChangeRecord;
forEachRemovedItem(fn: Function) {
var record: CollectionChangeRecord;
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
fn(record);
}
}
transform(collection){
transform(collection): any {
if (this.check(collection)) {
return WrappedValue.wrap(this);
} else {
@ -130,12 +121,12 @@ export class IterableChanges extends Pipe {
}
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
check(collection):boolean {
check(collection): boolean {
this._reset();
var record:CollectionChangeRecord = this._itHead;
var mayBeDirty:boolean = false;
var index:int;
var record: CollectionChangeRecord = this._itHead;
var mayBeDirty: boolean = false;
var index: int;
var item;
if (ListWrapper.isList(collection)) {
@ -175,10 +166,8 @@ export class IterableChanges extends Pipe {
}
// CollectionChanges is considered dirty if it has any additions, moves or removals.
get isDirty():boolean {
return this._additionsHead !== null ||
this._movesHead !== null ||
this._removalsHead !== null;
get isDirty(): boolean {
return this._additionsHead !== null || this._movesHead !== null || this._removalsHead !== null;
}
/**
@ -189,8 +178,8 @@ export class IterableChanges extends Pipe {
*/
_reset() {
if (this.isDirty) {
var record:CollectionChangeRecord;
var nextRecord:CollectionChangeRecord;
var record: CollectionChangeRecord;
var nextRecord: CollectionChangeRecord;
for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
record._nextPrevious = record._next;
@ -221,9 +210,9 @@ export class IterableChanges extends Pipe {
* - `item` is the current item in the collection
* - `index` is the position of the item in the collection
*/
_mismatch(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
_mismatch(record: CollectionChangeRecord, item, index: int): CollectionChangeRecord {
// The previous record after which we will append the current one.
var previousRecord:CollectionChangeRecord;
var previousRecord: CollectionChangeRecord;
if (record === null) {
previousRecord = this._itTail;
@ -277,9 +266,9 @@ export class IterableChanges extends Pipe {
* better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
* at the end.
*/
_verifyReinsertion(record:CollectionChangeRecord, item, index:int):CollectionChangeRecord {
var reinsertRecord:CollectionChangeRecord = this._unlinkedRecords === null ?
null : this._unlinkedRecords.get(item);
_verifyReinsertion(record: CollectionChangeRecord, item, index: int): CollectionChangeRecord {
var reinsertRecord: CollectionChangeRecord =
this._unlinkedRecords === null ? null : this._unlinkedRecords.get(item);
if (reinsertRecord !== null) {
record = this._reinsertAfter(reinsertRecord, record._prev, index);
} else if (record.currentIndex != index) {
@ -294,10 +283,10 @@ export class IterableChanges extends Pipe {
*
* - `record` The first excess {@link CollectionChangeRecord}.
*/
_truncate(record:CollectionChangeRecord) {
_truncate(record: CollectionChangeRecord) {
// Anything after that needs to be removed;
while (record !== null) {
var nextRecord:CollectionChangeRecord = record._next;
var nextRecord: CollectionChangeRecord = record._next;
this._addToRemovals(this._unlink(record));
record = nextRecord;
}
@ -319,8 +308,8 @@ export class IterableChanges extends Pipe {
}
}
_reinsertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
index:int):CollectionChangeRecord {
_reinsertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
index: int): CollectionChangeRecord {
if (this._unlinkedRecords !== null) {
this._unlinkedRecords.remove(record);
}
@ -343,42 +332,42 @@ export class IterableChanges extends Pipe {
return record;
}
_moveAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
index:int):CollectionChangeRecord {
_moveAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
index: int): CollectionChangeRecord {
this._unlink(record);
this._insertAfter(record, prevRecord, index);
this._addToMoves(record, index);
return record;
}
_addAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
index:int):CollectionChangeRecord {
_addAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
index: int): CollectionChangeRecord {
this._insertAfter(record, prevRecord, index);
if (this._additionsTail === null) {
// todo(vicb)
//assert(this._additionsHead === null);
// assert(this._additionsHead === null);
this._additionsTail = this._additionsHead = record;
} else {
// todo(vicb)
//assert(_additionsTail._nextAdded === null);
//assert(record._nextAdded === null);
// assert(_additionsTail._nextAdded === null);
// assert(record._nextAdded === null);
this._additionsTail = this._additionsTail._nextAdded = record;
}
return record;
}
_insertAfter(record:CollectionChangeRecord, prevRecord:CollectionChangeRecord,
index:int):CollectionChangeRecord {
_insertAfter(record: CollectionChangeRecord, prevRecord: CollectionChangeRecord,
index: int): CollectionChangeRecord {
// todo(vicb)
//assert(record != prevRecord);
//assert(record._next === null);
//assert(record._prev === null);
// assert(record != prevRecord);
// assert(record._next === null);
// assert(record._prev === null);
var next:CollectionChangeRecord = prevRecord === null ? this._itHead :prevRecord._next;
var next: CollectionChangeRecord = prevRecord === null ? this._itHead : prevRecord._next;
// todo(vicb)
//assert(next != record);
//assert(prevRecord != record);
// assert(next != record);
// assert(prevRecord != record);
record._next = next;
record._prev = prevRecord;
if (next === null) {
@ -401,11 +390,11 @@ export class IterableChanges extends Pipe {
return record;
}
_remove(record:CollectionChangeRecord):CollectionChangeRecord {
_remove(record: CollectionChangeRecord): CollectionChangeRecord {
return this._addToRemovals(this._unlink(record));
}
_unlink(record:CollectionChangeRecord):CollectionChangeRecord {
_unlink(record: CollectionChangeRecord): CollectionChangeRecord {
if (this._linkedRecords !== null) {
this._linkedRecords.remove(record);
}
@ -414,8 +403,8 @@ export class IterableChanges extends Pipe {
var next = record._next;
// todo(vicb)
//assert((record._prev = null) === null);
//assert((record._next = null) === null);
// assert((record._prev = null) === null);
// assert((record._next = null) === null);
if (prev === null) {
this._itHead = next;
@ -431,9 +420,9 @@ export class IterableChanges extends Pipe {
return record;
}
_addToMoves(record:CollectionChangeRecord, toIndex:int):CollectionChangeRecord {
_addToMoves(record: CollectionChangeRecord, toIndex: int): CollectionChangeRecord {
// todo(vicb)
//assert(record._nextMoved === null);
// assert(record._nextMoved === null);
if (record.previousIndex === toIndex) {
return record;
@ -441,18 +430,18 @@ export class IterableChanges extends Pipe {
if (this._movesTail === null) {
// todo(vicb)
//assert(_movesHead === null);
// assert(_movesHead === null);
this._movesTail = this._movesHead = record;
} else {
// todo(vicb)
//assert(_movesTail._nextMoved === null);
// assert(_movesTail._nextMoved === null);
this._movesTail = this._movesTail._nextMoved = record;
}
return record;
}
_addToRemovals(record:CollectionChangeRecord):CollectionChangeRecord {
_addToRemovals(record: CollectionChangeRecord): CollectionChangeRecord {
if (this._unlinkedRecords === null) {
this._unlinkedRecords = new _DuplicateMap();
}
@ -462,21 +451,21 @@ export class IterableChanges extends Pipe {
if (this._removalsTail === null) {
// todo(vicb)
//assert(_removalsHead === null);
// assert(_removalsHead === null);
this._removalsTail = this._removalsHead = record;
record._prevRemoved = null;
} else {
// todo(vicb)
//assert(_removalsTail._nextRemoved === null);
//assert(record._nextRemoved === null);
// assert(_removalsTail._nextRemoved === null);
// assert(record._nextRemoved === null);
record._prevRemoved = this._removalsTail;
this._removalsTail = this._removalsTail._nextRemoved = record;
}
return record;
}
toString():string {
var record:CollectionChangeRecord;
toString(): string {
var record: CollectionChangeRecord;
var list = [];
for (record = this._itHead; record !== null; record = record._next) {
@ -502,10 +491,8 @@ export class IterableChanges extends Pipe {
ListWrapper.push(removals, record);
}
return "collection: " + list.join(', ') + "\n" +
"previous: " + previous.join(', ') + "\n" +
"additions: " + additions.join(', ') + "\n" +
"moves: " + moves.join(', ') + "\n" +
return "collection: " + list.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
"additions: " + additions.join(', ') + "\n" + "moves: " + moves.join(', ') + "\n" +
"removals: " + removals.join(', ') + "\n";
}
}
@ -513,17 +500,20 @@ export class IterableChanges extends Pipe {
/**
* @exportedAs angular2/pipes
*/
export class CollectionChangeRecord {
currentIndex:int;
previousIndex:int;
export class CollectionChangeRecord {
currentIndex: int;
previousIndex: int;
item;
_nextPrevious:CollectionChangeRecord;
_prev:CollectionChangeRecord; _next:CollectionChangeRecord;
_prevDup:CollectionChangeRecord; _nextDup:CollectionChangeRecord;
_prevRemoved:CollectionChangeRecord; _nextRemoved:CollectionChangeRecord;
_nextAdded:CollectionChangeRecord;
_nextMoved:CollectionChangeRecord;
_nextPrevious: CollectionChangeRecord;
_prev: CollectionChangeRecord;
_next: CollectionChangeRecord;
_prevDup: CollectionChangeRecord;
_nextDup: CollectionChangeRecord;
_prevRemoved: CollectionChangeRecord;
_nextRemoved: CollectionChangeRecord;
_nextAdded: CollectionChangeRecord;
_nextMoved: CollectionChangeRecord;
constructor(item) {
this.currentIndex = null;
@ -541,18 +531,18 @@ export class CollectionChangeRecord {
this._nextMoved = null;
}
toString():string {
toString(): string {
return this.previousIndex === this.currentIndex ?
stringify(this.item) :
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
stringify(this.currentIndex) + ']';
stringify(this.item) :
stringify(this.item) + '[' + stringify(this.previousIndex) + '->' +
stringify(this.currentIndex) + ']';
}
}
// A linked list of CollectionChangeRecords with the same CollectionChangeRecord.item
class _DuplicateItemRecordList {
_head:CollectionChangeRecord;
_tail:CollectionChangeRecord;
_head: CollectionChangeRecord;
_tail: CollectionChangeRecord;
constructor() {
this._head = null;
@ -564,14 +554,14 @@ class _DuplicateItemRecordList {
*
* Note: by design all records in the list of duplicates hold the same value in record.item.
*/
add(record:CollectionChangeRecord) {
add(record: CollectionChangeRecord) {
if (this._head === null) {
this._head = this._tail = record;
record._nextDup = null;
record._prevDup = null;
} else {
// todo(vicb)
//assert(record.item == _head.item ||
// assert(record.item == _head.item ||
// record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
this._tail._nextDup = record;
record._prevDup = this._tail;
@ -582,14 +572,14 @@ class _DuplicateItemRecordList {
// Returns a CollectionChangeRecord having CollectionChangeRecord.item == item and
// CollectionChangeRecord.currentIndex >= afterIndex
get(item, afterIndex:int):CollectionChangeRecord {
var record:CollectionChangeRecord;
get(item, afterIndex: int): CollectionChangeRecord {
var record: CollectionChangeRecord;
for (record = this._head; record !== null; record = record._nextDup) {
if ((afterIndex === null || afterIndex < record.currentIndex) &&
looseIdentical(record.item, item)) {
return record;
}
looseIdentical(record.item, item)) {
return record;
}
}
return null;
}
@ -598,9 +588,9 @@ class _DuplicateItemRecordList {
*
* Returns whether the list of duplicates is empty.
*/
remove(record:CollectionChangeRecord):boolean {
remove(record: CollectionChangeRecord): boolean {
// todo(vicb)
//assert(() {
// assert(() {
// // verify that the record being removed is in the list.
// for (CollectionChangeRecord cursor = _head; cursor != null; cursor = cursor._nextDup) {
// if (identical(cursor, record)) return true;
@ -608,8 +598,8 @@ class _DuplicateItemRecordList {
// return false;
//});
var prev:CollectionChangeRecord = record._prevDup;
var next:CollectionChangeRecord = record._nextDup;
var prev: CollectionChangeRecord = record._prevDup;
var next: CollectionChangeRecord = record._nextDup;
if (prev === null) {
this._head = next;
} else {
@ -625,12 +615,10 @@ class _DuplicateItemRecordList {
}
class _DuplicateMap {
map:Map;
constructor() {
this.map = MapWrapper.create();
}
map: Map<any, any>;
constructor() { this.map = MapWrapper.create(); }
put(record:CollectionChangeRecord) {
put(record: CollectionChangeRecord) {
// todo(vicb) handle corner cases
var key = getMapKey(record.item);
@ -649,7 +637,7 @@ class _DuplicateMap {
* Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
* have any more `a`s needs to return the last `a` not the first or second.
*/
get(value, afterIndex = null):CollectionChangeRecord {
get(value, afterIndex = null): CollectionChangeRecord {
var key = getMapKey(value);
var recordList = MapWrapper.get(this.map, key);
@ -661,11 +649,11 @@ class _DuplicateMap {
*
* The list of duplicates also is removed from the map if it gets empty.
*/
remove(record:CollectionChangeRecord):CollectionChangeRecord {
remove(record: CollectionChangeRecord): CollectionChangeRecord {
var key = getMapKey(record.item);
// todo(vicb)
//assert(this.map.containsKey(key));
var recordList:_DuplicateItemRecordList = MapWrapper.get(this.map, key);
// assert(this.map.containsKey(key));
var recordList: _DuplicateItemRecordList = MapWrapper.get(this.map, key);
// Remove the list of duplicates when it gets empty
if (recordList.remove(record)) {
MapWrapper.delete(this.map, key);
@ -673,15 +661,9 @@ class _DuplicateMap {
return record;
}
get isEmpty():boolean {
return MapWrapper.size(this.map) === 0;
}
get isEmpty(): boolean { return MapWrapper.size(this.map) === 0; }
clear() {
MapWrapper.clear(this.map);
}
clear() { MapWrapper.clear(this.map); }
toString():string {
return '_DuplicateMap(' + stringify(this.map) + ')';
}
toString(): string { return '_DuplicateMap(' + stringify(this.map) + ')'; }
}

View File

@ -3,38 +3,36 @@ import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/
import {WrappedValue, Pipe, PipeFactory} from './pipe';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* @exportedAs angular2/pipes
*/
@CONST()
export class KeyValueChangesFactory extends PipeFactory {
@CONST()
constructor() {
super();
}
constructor() { super(); }
supports(obj):boolean {
return KeyValueChanges.supportsObj(obj);
}
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
create(cdRef):Pipe {
return new KeyValueChanges();
}
create(cdRef): Pipe { return new KeyValueChanges(); }
}
/**
* @exportedAs angular2/pipes
*/
export class KeyValueChanges extends Pipe {
_records:Map;
_mapHead:KVChangeRecord;
_previousMapHead:KVChangeRecord;
_changesHead:KVChangeRecord;
_changesTail:KVChangeRecord;
_additionsHead:KVChangeRecord;
_additionsTail:KVChangeRecord;
_removalsHead:KVChangeRecord;
_removalsTail:KVChangeRecord;
private _records: Map<any, any>;
private _mapHead: KVChangeRecord;
private _previousMapHead: KVChangeRecord;
private _changesHead: KVChangeRecord;
private _changesTail: KVChangeRecord;
private _additionsHead: KVChangeRecord;
private _additionsTail: KVChangeRecord;
private _removalsHead: KVChangeRecord;
private _removalsTail: KVChangeRecord;
constructor() {
super();
@ -49,15 +47,11 @@ export class KeyValueChanges extends Pipe {
this._removalsTail = null;
}
static supportsObj(obj):boolean {
return obj instanceof Map || isJsObject(obj);
}
static supportsObj(obj): boolean { return obj instanceof Map || isJsObject(obj); }
supports(obj):boolean {
return KeyValueChanges.supportsObj(obj);
}
supports(obj): boolean { return KeyValueChanges.supportsObj(obj); }
transform(map){
transform(map): any {
if (this.check(map)) {
return WrappedValue.wrap(this);
} else {
@ -65,54 +59,53 @@ export class KeyValueChanges extends Pipe {
}
}
get isDirty():boolean {
return this._additionsHead !== null ||
this._changesHead !== null ||
get isDirty(): boolean {
return this._additionsHead !== null || this._changesHead !== null ||
this._removalsHead !== null;
}
forEachItem(fn:Function) {
var record:KVChangeRecord;
forEachItem(fn: Function) {
var record: KVChangeRecord;
for (record = this._mapHead; record !== null; record = record._next) {
fn(record);
}
}
forEachPreviousItem(fn:Function) {
var record:KVChangeRecord;
forEachPreviousItem(fn: Function) {
var record: KVChangeRecord;
for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
fn(record);
}
}
forEachChangedItem(fn:Function) {
var record:KVChangeRecord;
forEachChangedItem(fn: Function) {
var record: KVChangeRecord;
for (record = this._changesHead; record !== null; record = record._nextChanged) {
fn(record);
}
}
forEachAddedItem(fn:Function){
var record:KVChangeRecord;
forEachAddedItem(fn: Function) {
var record: KVChangeRecord;
for (record = this._additionsHead; record !== null; record = record._nextAdded) {
fn(record);
}
}
forEachRemovedItem(fn:Function){
var record:KVChangeRecord;
forEachRemovedItem(fn: Function) {
var record: KVChangeRecord;
for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
fn(record);
}
}
check(map):boolean {
check(map): boolean {
this._reset();
var records = this._records;
var oldSeqRecord:KVChangeRecord = this._mapHead;
var lastOldSeqRecord:KVChangeRecord = null;
var lastNewSeqRecord:KVChangeRecord = null;
var seqChanged:boolean = false;
var oldSeqRecord: KVChangeRecord = this._mapHead;
var lastOldSeqRecord: KVChangeRecord = null;
var lastNewSeqRecord: KVChangeRecord = null;
var seqChanged: boolean = false;
this._forEach(map, (value, key) => {
var newSeqRecord;
@ -160,11 +153,9 @@ export class KeyValueChanges extends Pipe {
_reset() {
if (this.isDirty) {
var record:KVChangeRecord;
var record: KVChangeRecord;
// Record the state of the mapping
for (record = this._previousMapHead = this._mapHead;
record !== null;
record = record._next) {
for (record = this._previousMapHead = this._mapHead; record !== null; record = record._next) {
record._nextPrevious = record._next;
}
@ -177,7 +168,7 @@ export class KeyValueChanges extends Pipe {
}
// todo(vicb) once assert is supported
//assert(() {
// assert(() {
// var r = _changesHead;
// while (r != null) {
// var nextRecord = r._nextChanged;
@ -207,7 +198,7 @@ export class KeyValueChanges extends Pipe {
}
}
_truncate(lastRecord:KVChangeRecord, record:KVChangeRecord) {
_truncate(lastRecord: KVChangeRecord, record: KVChangeRecord) {
while (record !== null) {
if (lastRecord === null) {
this._mapHead = null;
@ -216,7 +207,7 @@ export class KeyValueChanges extends Pipe {
}
var nextRecord = record._next;
// todo(vicb) assert
//assert((() {
// assert((() {
// record._next = null;
// return true;
//}));
@ -225,26 +216,25 @@ export class KeyValueChanges extends Pipe {
record = nextRecord;
}
for (var rec:KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
for (var rec: KVChangeRecord = this._removalsHead; rec !== null; rec = rec._nextRemoved) {
rec.previousValue = rec.currentValue;
rec.currentValue = null;
MapWrapper.delete(this._records, rec.key);
}
}
_isInRemovals(record:KVChangeRecord) {
return record === this._removalsHead ||
record._nextRemoved !== null ||
_isInRemovals(record: KVChangeRecord) {
return record === this._removalsHead || record._nextRemoved !== null ||
record._prevRemoved !== null;
}
_addToRemovals(record:KVChangeRecord) {
_addToRemovals(record: KVChangeRecord) {
// todo(vicb) assert
//assert(record._next == null);
//assert(record._nextAdded == null);
//assert(record._nextChanged == null);
//assert(record._nextRemoved == null);
//assert(record._prevRemoved == null);
// assert(record._next == null);
// assert(record._nextAdded == null);
// assert(record._nextChanged == null);
// assert(record._nextRemoved == null);
// assert(record._prevRemoved == null);
if (this._removalsHead === null) {
this._removalsHead = this._removalsTail = record;
} else {
@ -254,7 +244,7 @@ export class KeyValueChanges extends Pipe {
}
}
_removeFromSeq(prev:KVChangeRecord, record:KVChangeRecord) {
_removeFromSeq(prev: KVChangeRecord, record: KVChangeRecord) {
var next = record._next;
if (prev === null) {
this._mapHead = next;
@ -262,17 +252,17 @@ export class KeyValueChanges extends Pipe {
prev._next = next;
}
// todo(vicb) assert
//assert((() {
// assert((() {
// record._next = null;
// return true;
//})());
}
_removeFromRemovals(record:KVChangeRecord) {
_removeFromRemovals(record: KVChangeRecord) {
// todo(vicb) assert
//assert(record._next == null);
//assert(record._nextAdded == null);
//assert(record._nextChanged == null);
// assert(record._next == null);
// assert(record._nextAdded == null);
// assert(record._nextChanged == null);
var prev = record._prevRemoved;
var next = record._nextRemoved;
@ -289,13 +279,13 @@ export class KeyValueChanges extends Pipe {
record._prevRemoved = record._nextRemoved = null;
}
_addToAdditions(record:KVChangeRecord) {
_addToAdditions(record: KVChangeRecord) {
// todo(vicb): assert
//assert(record._next == null);
//assert(record._nextAdded == null);
//assert(record._nextChanged == null);
//assert(record._nextRemoved == null);
//assert(record._prevRemoved == null);
// assert(record._next == null);
// assert(record._nextAdded == null);
// assert(record._nextChanged == null);
// assert(record._nextRemoved == null);
// assert(record._prevRemoved == null);
if (this._additionsHead === null) {
this._additionsHead = this._additionsTail = record;
} else {
@ -304,12 +294,12 @@ export class KeyValueChanges extends Pipe {
}
}
_addToChanges(record:KVChangeRecord) {
_addToChanges(record: KVChangeRecord) {
// todo(vicb) assert
//assert(record._nextAdded == null);
//assert(record._nextChanged == null);
//assert(record._nextRemoved == null);
//assert(record._prevRemoved == null);
// assert(record._nextAdded == null);
// assert(record._nextChanged == null);
// assert(record._nextRemoved == null);
// assert(record._prevRemoved == null);
if (this._changesHead === null) {
this._changesHead = this._changesTail = record;
} else {
@ -318,13 +308,13 @@ export class KeyValueChanges extends Pipe {
}
}
toString():string {
toString(): string {
var items = [];
var previous = [];
var changes = [];
var additions = [];
var removals = [];
var record:KVChangeRecord;
var record: KVChangeRecord;
for (record = this._mapHead; record !== null; record = record._next) {
ListWrapper.push(items, stringify(record));
@ -342,14 +332,12 @@ export class KeyValueChanges extends Pipe {
ListWrapper.push(removals, stringify(record));
}
return "map: " + items.join(', ') + "\n" +
"previous: " + previous.join(', ') + "\n" +
"additions: " + additions.join(', ') + "\n" +
"changes: " + changes.join(', ') + "\n" +
return "map: " + items.join(', ') + "\n" + "previous: " + previous.join(', ') + "\n" +
"additions: " + additions.join(', ') + "\n" + "changes: " + changes.join(', ') + "\n" +
"removals: " + removals.join(', ') + "\n";
}
_forEach(obj, fn:Function) {
_forEach(obj, fn: Function) {
if (obj instanceof Map) {
MapWrapper.forEach(obj, fn);
} else {
@ -368,12 +356,12 @@ export class KVChangeRecord {
previousValue;
currentValue;
_nextPrevious:KVChangeRecord;
_next:KVChangeRecord;
_nextAdded:KVChangeRecord;
_nextRemoved:KVChangeRecord;
_prevRemoved:KVChangeRecord;
_nextChanged:KVChangeRecord;
_nextPrevious: KVChangeRecord;
_next: KVChangeRecord;
_nextAdded: KVChangeRecord;
_nextRemoved: KVChangeRecord;
_prevRemoved: KVChangeRecord;
_nextChanged: KVChangeRecord;
constructor(key) {
this.key = key;
@ -388,10 +376,10 @@ export class KVChangeRecord {
this._nextChanged = null;
}
toString():string {
toString(): string {
return looseIdentical(this.previousValue, this.currentValue) ?
stringify(this.key) :
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
stringify(this.currentValue) + ']');
stringify(this.key) :
(stringify(this.key) + '[' + stringify(this.previousValue) + '->' +
stringify(this.currentValue) + ']');
}
}

View File

@ -1,48 +0,0 @@
import {isBlank, CONST} from 'angular2/src/facade/lang';
import {Pipe, WrappedValue, PipeFactory} from './pipe';
/**
* @exportedAs angular2/pipes
*/
export class NullPipeFactory extends PipeFactory {
@CONST()
constructor() {
super();
}
supports(obj):boolean {
return NullPipe.supportsObj(obj);
}
create(cdRef):Pipe {
return new NullPipe();
}
}
/**
* @exportedAs angular2/pipes
*/
export class NullPipe extends Pipe {
called:boolean;
constructor() {
super();
this.called = false;
}
static supportsObj(obj):boolean {
return isBlank(obj);
}
supports(obj) {
return NullPipe.supportsObj(obj);
}
transform(value) {
if (! this.called) {
this.called = true;
return WrappedValue.wrap(null);
} else {
return null;
}
}
}

View File

@ -0,0 +1,43 @@
import {isBlank, CONST} from 'angular2/src/facade/lang';
import {Pipe, WrappedValue, PipeFactory} from './pipe';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* @exportedAs angular2/pipes
*/
@CONST()
export class NullPipeFactory extends PipeFactory {
constructor() { super(); }
supports(obj): boolean { return NullPipe.supportsObj(obj); }
create(cdRef): Pipe { return new NullPipe(); }
}
/**
* @exportedAs angular2/pipes
*/
export class NullPipe extends Pipe {
called: boolean;
constructor() {
super();
this.called = false;
}
static supportsObj(obj): boolean { return isBlank(obj); }
supports(obj) { return NullPipe.supportsObj(obj); }
transform(value) {
if (!this.called) {
this.called = true;
return WrappedValue.wrap(null);
} else {
return null;
}
}
}

View File

@ -1,20 +1,22 @@
import {ABSTRACT, BaseException, CONST} from 'angular2/src/facade/lang';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
/**
* Indicates that the result of a {@link Pipe} transformation has changed even though the reference has not changed.
* Indicates that the result of a {@link Pipe} transformation has changed even though the reference
*has not changed.
*
* The wrapped value will be unwrapped by change detection, and the unwrapped value will be stored.
*
* @exportedAs angular2/pipes
*/
export class WrappedValue {
wrapped:any;
constructor(public wrapped: any) {}
constructor(wrapped:any) {
this.wrapped = wrapped;
}
static wrap(value:any):WrappedValue {
static wrap(value: any): WrappedValue {
var w = _wrappedValues[_wrappedIndex++ % 5];
w.wrapped = value;
return w;
@ -53,26 +55,25 @@ var _wrappedIndex = 0;
* @exportedAs angular2/pipes
*/
export class Pipe {
supports(obj):boolean {return false;}
supports(obj): boolean { return false; }
onDestroy() {}
transform(value:any):any {return null;}
transform(value: any): any { return null; }
}
@ABSTRACT()
// TODO: vsavkin: make it an interface
@CONST()
export class PipeFactory {
@CONST()
constructor() {
supports(obs): boolean {
_abstract();
return false;
}
supports(obs):boolean {
return _abstract();
}
create(cdRef):Pipe {
return _abstract();
create(cdRef): Pipe {
_abstract();
return null;
}
}
function _abstract() {
return new BaseException('This method is abstract');
throw new BaseException('This method is abstract');
}

View File

@ -1,25 +1,25 @@
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
import {Pipe} from './pipe';
import {Injectable} from 'angular2/src/di/annotations_impl';
import {Injectable} from 'angular2/src/di/decorators';
import {ChangeDetectorRef} from '../change_detector_ref';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
@Injectable()
export class PipeRegistry {
config;
constructor(public config) {}
constructor(config){
this.config = config;
}
get(type:string, obj, cdRef:ChangeDetectorRef):Pipe {
get(type: string, obj, cdRef: ChangeDetectorRef): Pipe {
var listOfConfigs = this.config[type];
if (isBlank(listOfConfigs)) {
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);
}
var matchingConfig = ListWrapper.find(listOfConfigs,
(pipeConfig) => pipeConfig.supports(obj));
var matchingConfig = ListWrapper.find(listOfConfigs, (pipeConfig) => pipeConfig.supports(obj));
if (isBlank(matchingConfig)) {
throw new BaseException(`Cannot find '${type}' pipe supporting object '${obj}'`);

View File

@ -1,368 +0,0 @@
import {isPresent, isBlank, BaseException, Type, isString} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {
AccessMember,
Assignment,
AST,
ASTWithSource,
AstVisitor,
Binary,
Chain,
Conditional,
Pipe,
FunctionCall,
ImplicitReceiver,
Interpolation,
KeyedAccess,
LiteralArray,
LiteralMap,
LiteralPrimitive,
MethodCall,
PrefixNot
} from './parser/ast';
import {ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {PipeRegistry} from './pipes/pipe_registry';
import {BindingRecord} from './binding_record';
import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {coalesce} from './coalesce';
import {
ProtoRecord,
RECORD_TYPE_SELF,
RECORD_TYPE_PROPERTY,
RECORD_TYPE_LOCAL,
RECORD_TYPE_INVOKE_METHOD,
RECORD_TYPE_CONST,
RECORD_TYPE_INVOKE_CLOSURE,
RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
_pipeRegistry:PipeRegistry;
_records:List<ProtoRecord>;
_bindingRecords:List<BindingRecord>;
_variableBindings:List<string>;
_directiveRecords:List<DirectiveRecord>;
_changeControlStrategy:string;
constructor(pipeRegistry:PipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._bindingRecords = bindingRecords;
this._variableBindings = variableBindings;
this._directiveRecords = directiveRecords;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any) {
this._createRecordsIfNecessary();
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
this._pipeRegistry, this._records, this._directiveRecords);
}
_createRecordsIfNecessary() {
if (isBlank(this._records)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(this._bindingRecords, (b) => {
recordBuilder.addAst(b, this._variableBindings);
});
this._records = coalesce(recordBuilder.records);
}
}
}
var _jitProtoChangeDetectorClassCounter:number = 0;
export class JitProtoChangeDetector extends ProtoChangeDetector {
_factory:Function;
_pipeRegistry;
_bindingRecords:List<BindingRecord>;
_variableBindings:List<string>;
_directiveRecords:List<DirectiveRecord>;
_changeControlStrategy:string;
constructor(pipeRegistry, bindingRecords:List, variableBindings:List, directiveRecords:List, changeControlStrategy:string) {
super();
this._pipeRegistry = pipeRegistry;
this._factory = null;
this._bindingRecords = bindingRecords;
this._variableBindings = variableBindings;
this._directiveRecords = directiveRecords;
this._changeControlStrategy = changeControlStrategy;
}
instantiate(dispatcher:any) {
this._createFactoryIfNecessary();
return this._factory(dispatcher, this._pipeRegistry);
}
_createFactoryIfNecessary() {
if (isBlank(this._factory)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(this._bindingRecords, (b) => {
recordBuilder.addAst(b, this._variableBindings);
});
var c = _jitProtoChangeDetectorClassCounter++;
var records = coalesce(recordBuilder.records);
var typeName = `ChangeDetector${c}`;
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
this._directiveRecords).generate();
}
}
}
class ProtoRecordBuilder {
records:List<ProtoRecord>;
constructor() {
this.records = [];
}
addAst(b:BindingRecord, variableBindings:List = null) {
var last = ListWrapper.last(this.records);
if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
last.lastInDirective = false;
}
var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
if (! ListWrapper.isEmpty(pr)) {
var last = ListWrapper.last(pr);
last.lastInBinding = true;
last.lastInDirective = true;
this.records = ListWrapper.concat(this.records, pr);
}
}
}
class _ConvertAstIntoProtoRecords {
protoRecords:List;
bindingRecord:BindingRecord;
variableBindings:List;
contextIndex:number;
expressionAsString:string;
constructor(bindingRecord:BindingRecord, contextIndex:number, expressionAsString:string, variableBindings:List) {
this.protoRecords = [];
this.bindingRecord = bindingRecord;
this.contextIndex = contextIndex;
this.expressionAsString = expressionAsString;
this.variableBindings = variableBindings;
}
static convert(b:BindingRecord, contextIndex:number, variableBindings:List) {
var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
b.ast.visit(c);
return c.protoRecords;
}
visitImplicitReceiver(ast:ImplicitReceiver) {
return this.bindingRecord.implicitReceiver;
}
visitInterpolation(ast:Interpolation) {
var args = this._visitAll(ast.expressions);
return this._addRecord(RECORD_TYPE_INTERPOLATE, "interpolate", _interpolationFn(ast.strings),
args, ast.strings, 0);
}
visitLiteralPrimitive(ast:LiteralPrimitive) {
return this._addRecord(RECORD_TYPE_CONST, "literal", ast.value, [], null, 0);
}
visitAccessMember(ast:AccessMember) {
var receiver = ast.receiver.visit(this);
if (isPresent(this.variableBindings) &&
ListWrapper.contains(this.variableBindings, ast.name) &&
ast.receiver instanceof ImplicitReceiver) {
return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
} else {
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
}
}
visitMethodCall(ast:MethodCall) {;
var receiver = ast.receiver.visit(this);
var args = this._visitAll(ast.args);
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
var target = this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
} else {
return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
}
}
visitFunctionCall(ast:FunctionCall) {
var target = ast.target.visit(this);
var args = this._visitAll(ast.args);
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
}
visitLiteralArray(ast:LiteralArray) {
var primitiveName = `arrayFn${ast.expressions.length}`;
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, primitiveName, _arrayFn(ast.expressions.length),
this._visitAll(ast.expressions), null, 0);
}
visitLiteralMap(ast:LiteralMap) {
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _mapPrimitiveName(ast.keys),
ChangeDetectionUtil.mapFn(ast.keys), this._visitAll(ast.values), null, 0);
}
visitBinary(ast:Binary) {
var left = ast.left.visit(this);
var right = ast.right.visit(this);
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _operationToPrimitiveName(ast.operation),
_operationToFunction(ast.operation), [left, right], null, 0);
}
visitPrefixNot(ast:PrefixNot) {
var exp = ast.expression.visit(this)
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "operation_negate",
ChangeDetectionUtil.operation_negate, [exp], null, 0);
}
visitConditional(ast:Conditional) {
var c = ast.condition.visit(this);
var t = ast.trueExp.visit(this);
var f = ast.falseExp.visit(this);
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "cond",
ChangeDetectionUtil.cond, [c,t,f], null, 0);
}
visitPipe(ast:Pipe) {
var value = ast.exp.visit(this);
var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE;
return this._addRecord(type, ast.name, ast.name, [], null, value);
}
visitKeyedAccess(ast:KeyedAccess) {
var obj = ast.obj.visit(this);
var key = ast.key.visit(this);
return this._addRecord(RECORD_TYPE_KEYED_ACCESS, "keyedAccess",
ChangeDetectionUtil.keyedAccess, [key], null, obj);
}
_visitAll(asts:List) {
var res = ListWrapper.createFixedSize(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
return res;
}
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
var selfIndex = ++ this.contextIndex;
if (context instanceof DirectiveIndex) {
ListWrapper.push(this.protoRecords,
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, selfIndex,
this.bindingRecord, this.expressionAsString, false, false));
} else {
ListWrapper.push(this.protoRecords,
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, selfIndex,
this.bindingRecord, this.expressionAsString, false, false));
}
return selfIndex;
}
}
function _arrayFn(length:number):Function {
switch (length) {
case 0: return ChangeDetectionUtil.arrayFn0;
case 1: return ChangeDetectionUtil.arrayFn1;
case 2: return ChangeDetectionUtil.arrayFn2;
case 3: return ChangeDetectionUtil.arrayFn3;
case 4: return ChangeDetectionUtil.arrayFn4;
case 5: return ChangeDetectionUtil.arrayFn5;
case 6: return ChangeDetectionUtil.arrayFn6;
case 7: return ChangeDetectionUtil.arrayFn7;
case 8: return ChangeDetectionUtil.arrayFn8;
case 9: return ChangeDetectionUtil.arrayFn9;
default: throw new BaseException(`Does not support literal maps with more than 9 elements`);
}
}
function _mapPrimitiveName(keys:List) {
var stringifiedKeys = ListWrapper.join(
ListWrapper.map(keys, (k) => isString(k) ? `"${k}"` : `${k}`),
", ");
return `mapFn([${stringifiedKeys}])`;
}
function _operationToPrimitiveName(operation:string):string {
switch(operation) {
case '+' : return "operation_add";
case '-' : return "operation_subtract";
case '*' : return "operation_multiply";
case '/' : return "operation_divide";
case '%' : return "operation_remainder";
case '==' : return "operation_equals";
case '!=' : return "operation_not_equals";
case '<' : return "operation_less_then";
case '>' : return "operation_greater_then";
case '<=' : return "operation_less_or_equals_then";
case '>=' : return "operation_greater_or_equals_then";
case '&&' : return "operation_logical_and";
case '||' : return "operation_logical_or";
default: throw new BaseException(`Unsupported operation ${operation}`);
}
}
function _operationToFunction(operation:string):Function {
switch(operation) {
case '+' : return ChangeDetectionUtil.operation_add;
case '-' : return ChangeDetectionUtil.operation_subtract;
case '*' : return ChangeDetectionUtil.operation_multiply;
case '/' : return ChangeDetectionUtil.operation_divide;
case '%' : return ChangeDetectionUtil.operation_remainder;
case '==' : return ChangeDetectionUtil.operation_equals;
case '!=' : return ChangeDetectionUtil.operation_not_equals;
case '<' : return ChangeDetectionUtil.operation_less_then;
case '>' : return ChangeDetectionUtil.operation_greater_then;
case '<=' : return ChangeDetectionUtil.operation_less_or_equals_then;
case '>=' : return ChangeDetectionUtil.operation_greater_or_equals_then;
case '&&' : return ChangeDetectionUtil.operation_logical_and;
case '||' : return ChangeDetectionUtil.operation_logical_or;
default: throw new BaseException(`Unsupported operation ${operation}`);
}
}
function s(v) {
return isPresent(v) ? `${v}` : '';
}
function _interpolationFn(strings:List) {
var length = strings.length;
var c0 = length > 0 ? strings[0] : null;
var c1 = length > 1 ? strings[1] : null;
var c2 = length > 2 ? strings[2] : null;
var c3 = length > 3 ? strings[3] : null;
var c4 = length > 4 ? strings[4] : null;
var c5 = length > 5 ? strings[5] : null;
var c6 = length > 6 ? strings[6] : null;
var c7 = length > 7 ? strings[7] : null;
var c8 = length > 8 ? strings[8] : null;
var c9 = length > 9 ? strings[9] : null;
switch (length - 1) {
case 1: return (a1) => c0 + s(a1) + c1;
case 2: return (a1, a2) => c0 + s(a1) + c1 + s(a2) + c2;
case 3: return (a1, a2, a3) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3;
case 4: return (a1, a2, a3, a4) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4;
case 5: return (a1, a2, a3, a4, a5) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5;
case 6: return (a1, a2, a3, a4, a5, a6) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6;
case 7: return (a1, a2, a3, a4, a5, a6, a7) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7;
case 8: return (a1, a2, a3, a4, a5, a6, a7, a8) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8;
case 9: return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) + c8 + s(a9) + c9;
default: throw new BaseException(`Does not support more than 9 expressions`);
}
}

View File

@ -0,0 +1,406 @@
import {isPresent, isBlank, BaseException, Type, isString} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {
AccessMember,
Assignment,
AST,
ASTWithSource,
AstVisitor,
Binary,
Chain,
Conditional,
Pipe,
FunctionCall,
ImplicitReceiver,
Interpolation,
KeyedAccess,
LiteralArray,
LiteralMap,
LiteralPrimitive,
MethodCall,
PrefixNot
} from './parser/ast';
import {ChangeDispatcher, ChangeDetector, ProtoChangeDetector} from './interfaces';
import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {PipeRegistry} from './pipes/pipe_registry';
import {BindingRecord} from './binding_record';
import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {coalesce} from './coalesce';
import {
ProtoRecord,
RECORD_TYPE_SELF,
RECORD_TYPE_PROPERTY,
RECORD_TYPE_LOCAL,
RECORD_TYPE_INVOKE_METHOD,
RECORD_TYPE_CONST,
RECORD_TYPE_INVOKE_CLOSURE,
RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE
} from './proto_record';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
_records: List<ProtoRecord>;
constructor(private _pipeRegistry: PipeRegistry, private _bindingRecords: List<any>,
private _variableBindings: List<any>, private _directiveRecords: List<any>,
private _changeControlStrategy: string) {
super();
}
instantiate(dispatcher: any) {
this._createRecordsIfNecessary();
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher, this._pipeRegistry,
this._records, this._directiveRecords);
}
_createRecordsIfNecessary() {
if (isBlank(this._records)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(this._bindingRecords,
(b) => { recordBuilder.addAst(b, this._variableBindings); });
this._records = coalesce(recordBuilder.records);
}
}
}
var _jitProtoChangeDetectorClassCounter: number = 0;
export class JitProtoChangeDetector extends ProtoChangeDetector {
_factory: Function;
constructor(private _pipeRegistry, private _bindingRecords: List<any>,
private _variableBindings: List<any>, private _directiveRecords: List<any>,
private _changeControlStrategy: string) {
super();
this._factory = null;
}
instantiate(dispatcher: any) {
this._createFactoryIfNecessary();
return this._factory(dispatcher, this._pipeRegistry);
}
_createFactoryIfNecessary() {
if (isBlank(this._factory)) {
var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(this._bindingRecords,
(b) => { recordBuilder.addAst(b, this._variableBindings); });
var c = _jitProtoChangeDetectorClassCounter++;
var records = coalesce(recordBuilder.records);
var typeName = `ChangeDetector${c}`;
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
this._directiveRecords)
.generate();
}
}
}
class ProtoRecordBuilder {
records: List<ProtoRecord>;
constructor() { this.records = []; }
addAst(b: BindingRecord, variableBindings: List < any >= null) {
var last = ListWrapper.last(this.records);
if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
last.lastInDirective = false;
}
var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
if (!ListWrapper.isEmpty(pr)) {
var last = ListWrapper.last(pr);
last.lastInBinding = true;
last.lastInDirective = true;
this.records = ListWrapper.concat(this.records, pr);
}
}
}
class _ConvertAstIntoProtoRecords {
protoRecords: List<any>;
constructor(private bindingRecord: BindingRecord, private contextIndex: number,
private expressionAsString: string, private variableBindings: List<any>) {
this.protoRecords = [];
}
static convert(b: BindingRecord, contextIndex: number, variableBindings: List<any>) {
var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
b.ast.visit(c);
return c.protoRecords;
}
visitImplicitReceiver(ast: ImplicitReceiver) { return this.bindingRecord.implicitReceiver; }
visitInterpolation(ast: Interpolation) {
var args = this._visitAll(ast.expressions);
return this._addRecord(RECORD_TYPE_INTERPOLATE, "interpolate", _interpolationFn(ast.strings),
args, ast.strings, 0);
}
visitLiteralPrimitive(ast: LiteralPrimitive) {
return this._addRecord(RECORD_TYPE_CONST, "literal", ast.value, [], null, 0);
}
visitAccessMember(ast: AccessMember) {
var receiver = ast.receiver.visit(this);
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name) &&
ast.receiver instanceof
ImplicitReceiver) {
return this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
} else {
return this._addRecord(RECORD_TYPE_PROPERTY, ast.name, ast.getter, [], null, receiver);
}
}
visitMethodCall(ast: MethodCall) {
;
var receiver = ast.receiver.visit(this);
var args = this._visitAll(ast.args);
if (isPresent(this.variableBindings) && ListWrapper.contains(this.variableBindings, ast.name)) {
var target = this._addRecord(RECORD_TYPE_LOCAL, ast.name, ast.name, [], null, receiver);
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
} else {
return this._addRecord(RECORD_TYPE_INVOKE_METHOD, ast.name, ast.fn, args, null, receiver);
}
}
visitFunctionCall(ast: FunctionCall) {
var target = ast.target.visit(this);
var args = this._visitAll(ast.args);
return this._addRecord(RECORD_TYPE_INVOKE_CLOSURE, "closure", null, args, null, target);
}
visitLiteralArray(ast: LiteralArray) {
var primitiveName = `arrayFn${ast.expressions.length}`;
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, primitiveName,
_arrayFn(ast.expressions.length), this._visitAll(ast.expressions), null,
0);
}
visitLiteralMap(ast: LiteralMap) {
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _mapPrimitiveName(ast.keys),
ChangeDetectionUtil.mapFn(ast.keys), this._visitAll(ast.values), null,
0);
}
visitBinary(ast: Binary) {
var left = ast.left.visit(this);
var right = ast.right.visit(this);
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, _operationToPrimitiveName(ast.operation),
_operationToFunction(ast.operation), [left, right], null, 0);
}
visitPrefixNot(ast: PrefixNot) {
var exp = ast.expression.visit(this);
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "operation_negate",
ChangeDetectionUtil.operation_negate, [exp], null, 0);
}
visitConditional(ast: Conditional) {
var c = ast.condition.visit(this);
var t = ast.trueExp.visit(this);
var f = ast.falseExp.visit(this);
return this._addRecord(RECORD_TYPE_PRIMITIVE_OP, "cond", ChangeDetectionUtil.cond, [c, t, f],
null, 0);
}
visitPipe(ast: Pipe) {
var value = ast.exp.visit(this);
var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE;
return this._addRecord(type, ast.name, ast.name, [], null, value);
}
visitKeyedAccess(ast: KeyedAccess) {
var obj = ast.obj.visit(this);
var key = ast.key.visit(this);
return this._addRecord(RECORD_TYPE_KEYED_ACCESS, "keyedAccess", ChangeDetectionUtil.keyedAccess,
[key], null, obj);
}
_visitAll(asts: List<any>) {
var res = ListWrapper.createFixedSize(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}
return res;
}
_addRecord(type, name, funcOrValue, args, fixedArgs, context) {
var selfIndex = ++this.contextIndex;
if (context instanceof DirectiveIndex) {
ListWrapper.push(
this.protoRecords,
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, selfIndex,
this.bindingRecord, this.expressionAsString, false, false));
} else {
ListWrapper.push(
this.protoRecords,
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, selfIndex,
this.bindingRecord, this.expressionAsString, false, false));
}
return selfIndex;
}
}
function _arrayFn(length: number): Function {
switch (length) {
case 0:
return ChangeDetectionUtil.arrayFn0;
case 1:
return ChangeDetectionUtil.arrayFn1;
case 2:
return ChangeDetectionUtil.arrayFn2;
case 3:
return ChangeDetectionUtil.arrayFn3;
case 4:
return ChangeDetectionUtil.arrayFn4;
case 5:
return ChangeDetectionUtil.arrayFn5;
case 6:
return ChangeDetectionUtil.arrayFn6;
case 7:
return ChangeDetectionUtil.arrayFn7;
case 8:
return ChangeDetectionUtil.arrayFn8;
case 9:
return ChangeDetectionUtil.arrayFn9;
default:
throw new BaseException(`Does not support literal maps with more than 9 elements`);
}
}
function _mapPrimitiveName(keys: List<any>) {
var stringifiedKeys =
ListWrapper.join(ListWrapper.map(keys, (k) => isString(k) ? `"${k}"` : `${k}`), ", ");
return `mapFn([${stringifiedKeys}])`;
}
function _operationToPrimitiveName(operation: string): string {
switch (operation) {
case '+':
return "operation_add";
case '-':
return "operation_subtract";
case '*':
return "operation_multiply";
case '/':
return "operation_divide";
case '%':
return "operation_remainder";
case '==':
return "operation_equals";
case '!=':
return "operation_not_equals";
case '<':
return "operation_less_then";
case '>':
return "operation_greater_then";
case '<=':
return "operation_less_or_equals_then";
case '>=':
return "operation_greater_or_equals_then";
case '&&':
return "operation_logical_and";
case '||':
return "operation_logical_or";
default:
throw new BaseException(`Unsupported operation ${operation}`);
}
}
function _operationToFunction(operation: string): Function {
switch (operation) {
case '+':
return ChangeDetectionUtil.operation_add;
case '-':
return ChangeDetectionUtil.operation_subtract;
case '*':
return ChangeDetectionUtil.operation_multiply;
case '/':
return ChangeDetectionUtil.operation_divide;
case '%':
return ChangeDetectionUtil.operation_remainder;
case '==':
return ChangeDetectionUtil.operation_equals;
case '!=':
return ChangeDetectionUtil.operation_not_equals;
case '<':
return ChangeDetectionUtil.operation_less_then;
case '>':
return ChangeDetectionUtil.operation_greater_then;
case '<=':
return ChangeDetectionUtil.operation_less_or_equals_then;
case '>=':
return ChangeDetectionUtil.operation_greater_or_equals_then;
case '&&':
return ChangeDetectionUtil.operation_logical_and;
case '||':
return ChangeDetectionUtil.operation_logical_or;
default:
throw new BaseException(`Unsupported operation ${operation}`);
}
}
function s(v) {
return isPresent(v) ? `${v}` : '';
}
function _interpolationFn(strings: List<any>) {
var length = strings.length;
var c0 = length > 0 ? strings[0] : null;
var c1 = length > 1 ? strings[1] : null;
var c2 = length > 2 ? strings[2] : null;
var c3 = length > 3 ? strings[3] : null;
var c4 = length > 4 ? strings[4] : null;
var c5 = length > 5 ? strings[5] : null;
var c6 = length > 6 ? strings[6] : null;
var c7 = length > 7 ? strings[7] : null;
var c8 = length > 8 ? strings[8] : null;
var c9 = length > 9 ? strings[9] : null;
switch (length - 1) {
case 1:
return (a1) => c0 + s(a1) + c1;
case 2:
return (a1, a2) => c0 + s(a1) + c1 + s(a2) + c2;
case 3:
return (a1, a2, a3) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3;
case 4:
return (a1, a2, a3, a4) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4;
case 5:
return (a1, a2, a3, a4, a5) =>
c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5;
case 6:
return (a1, a2, a3, a4, a5, a6) =>
c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) + c4 + s(a5) + c5 + s(a6) + c6;
case 7:
return (a1, a2, a3, a4, a5, a6, a7) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) +
c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7;
case 8:
return (a1, a2, a3, a4, a5, a6, a7, a8) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 + s(a4) +
c4 + s(a5) + c5 + s(a6) + c6 + s(a7) + c7 + s(a8) +
c8;
case 9:
return (a1, a2, a3, a4, a5, a6, a7, a8, a9) => c0 + s(a1) + c1 + s(a2) + c2 + s(a3) + c3 +
s(a4) + c4 + s(a5) + c5 + s(a6) + c6 + s(a7) +
c7 + s(a8) + c8 + s(a9) + c9;
default:
throw new BaseException(`Does not support more than 9 expressions`);
}
}

View File

@ -1,66 +0,0 @@
import {List} from 'angular2/src/facade/collection';
import {BindingRecord} from './binding_record';
import {DirectiveIndex} from './directive_record';
export const RECORD_TYPE_SELF = 0;
export const RECORD_TYPE_CONST = 1;
export const RECORD_TYPE_PRIMITIVE_OP = 2;
export const RECORD_TYPE_PROPERTY = 3;
export const RECORD_TYPE_LOCAL = 4;
export const RECORD_TYPE_INVOKE_METHOD = 5;
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
export const RECORD_TYPE_KEYED_ACCESS = 7;
export const RECORD_TYPE_PIPE = 8;
export const RECORD_TYPE_BINDING_PIPE = 9;
export const RECORD_TYPE_INTERPOLATE = 10;
export class ProtoRecord {
mode:number;
name:string;
funcOrValue:any;
args:List;
fixedArgs:List;
contextIndex:number;
directiveIndex:DirectiveIndex;
selfIndex:number;
bindingRecord:BindingRecord;
lastInBinding:boolean;
lastInDirective:boolean;
expressionAsString:string;
constructor(mode:number,
name:string,
funcOrValue,
args:List,
fixedArgs:List,
contextIndex:number,
directiveIndex:DirectiveIndex,
selfIndex:number,
bindingRecord:BindingRecord,
expressionAsString:string,
lastInBinding:boolean,
lastInDirective:boolean) {
this.mode = mode;
this.name = name;
this.funcOrValue = funcOrValue;
this.args = args;
this.fixedArgs = fixedArgs;
this.contextIndex = contextIndex;
this.directiveIndex = directiveIndex;
this.selfIndex = selfIndex;
this.bindingRecord = bindingRecord;
this.lastInBinding = lastInBinding;
this.lastInDirective = lastInDirective;
this.expressionAsString = expressionAsString;
}
isPureFunction():boolean {
return this.mode === RECORD_TYPE_INTERPOLATE ||
this.mode === RECORD_TYPE_PRIMITIVE_OP;
}
}

View File

@ -0,0 +1,32 @@
import {List} from 'angular2/src/facade/collection';
import {BindingRecord} from './binding_record';
import {DirectiveIndex} from './directive_record';
// HACK: workaround for Traceur behavior.
// It expects all transpiled modules to contain this marker.
// TODO: remove this when we no longer use traceur
export var __esModule = true;
export const RECORD_TYPE_SELF = 0;
export const RECORD_TYPE_CONST = 1;
export const RECORD_TYPE_PRIMITIVE_OP = 2;
export const RECORD_TYPE_PROPERTY = 3;
export const RECORD_TYPE_LOCAL = 4;
export const RECORD_TYPE_INVOKE_METHOD = 5;
export const RECORD_TYPE_INVOKE_CLOSURE = 6;
export const RECORD_TYPE_KEYED_ACCESS = 7;
export const RECORD_TYPE_PIPE = 8;
export const RECORD_TYPE_BINDING_PIPE = 9;
export const RECORD_TYPE_INTERPOLATE = 10;
export class ProtoRecord {
constructor(public mode: number, public name: string, public funcOrValue, public args: List<any>,
public fixedArgs: List<any>, public contextIndex: number,
public directiveIndex: DirectiveIndex, public selfIndex: number,
public bindingRecord: BindingRecord, public expressionAsString: string,
public lastInBinding: boolean, public lastInDirective: boolean) {}
isPureFunction(): boolean {
return this.mode === RECORD_TYPE_INTERPOLATE || this.mode === RECORD_TYPE_PRIMITIVE_OP;
}
}

View File

@ -449,7 +449,9 @@ function _constructDependencies(factoryFunction: Function, dependencies: List<an
function _dependenciesFor(typeOrFunc): List<any> {
var params = reflector.parameters(typeOrFunc);
if (isBlank(params)) return [];
if (ListWrapper.any(params, (p) => isBlank(p))) throw new NoAnnotationError(typeOrFunc);
if (ListWrapper.any(params, (p) => isBlank(p))) {
throw new NoAnnotationError(typeOrFunc);
}
return ListWrapper.map(params, (p) => _extractToken(typeOrFunc, p));
}

View File

@ -1,3 +1,5 @@
library angular2.di.decorators;
/* This file is empty because, Dart does not have decorators. */
export 'annotations.dart';

View File

@ -66,12 +66,12 @@ function _isWaiting(obj): boolean {
* @exportedAs angular2/di
*/
export class Injector {
_bindings: List<any>;
_instances: List<any>;
_parent: Injector;
_defaultBindings: boolean;
_asyncStrategy: _AsyncInjectorStrategy;
_syncStrategy: _SyncInjectorStrategy;
private _bindings: List<any>;
private _instances: List<any>;
private _parent: Injector;
private _defaultBindings: boolean;
private _asyncStrategy: _AsyncInjectorStrategy;
private _syncStrategy: _SyncInjectorStrategy;
/**
* Turns a list of binding definitions into an internal resolved list of resolved bindings.

View File

@ -52,7 +52,7 @@ export class PromiseWrapper {
export class ObservableWrapper {
static subscribe(emitter: EventEmitter, onNext, onThrow = null, onReturn = null) {
static subscribe(emitter: Observable, onNext, onThrow = null, onReturn = null): Object {
return emitter.observer({next: onNext, throw: onThrow, return: onReturn});
}
@ -69,7 +69,7 @@ export class ObservableWrapper {
// TODO: vsavkin change to interface
export class Observable {
observer(generator: any) {}
observer(generator: any): Object { return null; }
}
/**

View File

@ -117,9 +117,9 @@ export class ListWrapper {
static indexOf(array: List<any>, value, startIndex = -1) {
return array.indexOf(value, startIndex);
}
static reduce<T>(list: List<T>,
fn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
init: T) {
static reduce<T, E>(list: List<T>,
fn: (accumValue: E, currentValue: T, currentIndex: number, array: T[]) => E,
init: E) {
return list.reduce(fn, init);
}
static filter(array, pred: Function) { return array.filter(pred); }

View File

@ -5,6 +5,9 @@ import 'types.dart';
import 'dart:mirrors';
class ReflectionCapabilities {
ReflectionCapabilities([metadataReader]) {
}
Function factory(Type type) {
ClassMirror classMirror = reflectType(type);
MethodMirror ctor = classMirror.declarations[classMirror.simpleName];

View File

@ -8,37 +8,39 @@ import {GetterFn, SetterFn, MethodFn} from './types';
export var __esModule = true;
export class ReflectionCapabilities {
factory(type: Type): Function {
switch (type.length) {
private _reflect: any;
constructor(reflect?: any) { this._reflect = isPresent(reflect) ? reflect : global.Reflect; }
factory(t: Type): Function {
switch (t.length) {
case 0:
return function() { return new type(); };
return function() { return new t(); };
case 1:
return function(a1) { return new type(a1); };
return function(a1) { return new t(a1); };
case 2:
return function(a1, a2) { return new type(a1, a2); };
return function(a1, a2) { return new t(a1, a2); };
case 3:
return function(a1, a2, a3) { return new type(a1, a2, a3); };
return function(a1, a2, a3) { return new t(a1, a2, a3); };
case 4:
return function(a1, a2, a3, a4) { return new type(a1, a2, a3, a4); };
return function(a1, a2, a3, a4) { return new t(a1, a2, a3, a4); };
case 5:
return function(a1, a2, a3, a4, a5) { return new type(a1, a2, a3, a4, a5); };
return function(a1, a2, a3, a4, a5) { return new t(a1, a2, a3, a4, a5); };
case 6:
return function(a1, a2, a3, a4, a5, a6) { return new type(a1, a2, a3, a4, a5, a6); };
return function(a1, a2, a3, a4, a5, a6) { return new t(a1, a2, a3, a4, a5, a6); };
case 7:
return function(a1, a2, a3, a4, a5, a6, a7) {
return new type(a1, a2, a3, a4, a5, a6, a7);
};
return function(a1, a2, a3, a4, a5, a6, a7) { return new t(a1, a2, a3, a4, a5, a6, a7); };
case 8:
return function(a1, a2, a3, a4, a5, a6, a7, a8) {
return new type(a1, a2, a3, a4, a5, a6, a7, a8);
return new t(a1, a2, a3, a4, a5, a6, a7, a8);
};
case 9:
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
return new type(a1, a2, a3, a4, a5, a6, a7, a8, a9);
return new t(a1, a2, a3, a4, a5, a6, a7, a8, a9);
};
case 10:
return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
return new type(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
return new t(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
};
};
@ -68,9 +70,9 @@ export class ReflectionCapabilities {
if (isPresent(typeOfFunc.parameters)) {
return typeOfFunc.parameters;
}
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
var paramAnnotations = global.Reflect.getMetadata('parameters', typeOfFunc);
var paramTypes = global.Reflect.getMetadata('design:paramtypes', typeOfFunc);
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
var paramAnnotations = this._reflect.getMetadata('parameters', typeOfFunc);
var paramTypes = this._reflect.getMetadata('design:paramtypes', typeOfFunc);
if (isPresent(paramTypes) || isPresent(paramAnnotations)) {
return this._zipTypesAndAnnotaions(paramTypes, paramAnnotations);
}
@ -83,8 +85,8 @@ export class ReflectionCapabilities {
if (isPresent(typeOfFunc.annotations)) {
return typeOfFunc.annotations;
}
if (isPresent(global.Reflect) && isPresent(global.Reflect.getMetadata)) {
var annotations = global.Reflect.getMetadata('annotations', typeOfFunc);
if (isPresent(this._reflect) && isPresent(this._reflect.getMetadata)) {
var annotations = this._reflect.getMetadata('annotations', typeOfFunc);
if (isPresent(annotations)) return annotations;
}
return [];

View File

@ -8,17 +8,6 @@ export function main() {
rc = new ReflectionCapabilities();
});
function mockReflect(mockData, cls) {
// This only makes sense for JS, but Dart passes trivially too.
if (!IS_DARTIUM) {
global.Reflect = {
'getMetadata': (key, targetCls) => {
return (targetCls == cls) ? mockData[key] : null;
}
}
}
}
function assertTestClassAnnotations(annotations) {
expect(annotations[0]).toBeAnInstanceOf(ClassDec1);
expect(annotations[1]).toBeAnInstanceOf(ClassDec2);
@ -54,23 +43,34 @@ export function main() {
expect(rc.parameters(TestClassTypesOnly)).toEqual([[P1], [P2]]);
});
if (!IS_DARTIUM) {
// Mocking in the tests below is needed because the test runs through Traceur.
// After the switch to TS the setup will have to change, where the direct key
// access will be mocked, and the tests below will be direct.
it('can read out class annotations though Reflect APIs', () => {
mockReflect(mockDataForTestClassDec, TestClassDec);
var rc = new ReflectionCapabilities({
'getMetadata': (key, targetCls) => {
return (targetCls == TestClassDec) ? mockDataForTestClassDec[key] : null;
}
});
assertTestClassAnnotations(rc.annotations(TestClassDec));
});
it('can read out parameter annotations though Reflect APIs', () => {
mockReflect(mockDataForTestClassDec, TestClassDec);
var rc = new ReflectionCapabilities({
'getMetadata': (key, targetCls) => {
return (targetCls == TestClassDec) ? mockDataForTestClassDec[key] : null;
}
});
assertTestClassParameters(rc.parameters(TestClassDec));
});
it('can read out parameter annotations though Reflect APIs for types only class', () => {
mockReflect(mockDataForTestClassTypesOnly, TestClassTypesOnlyDec);
var rc = new ReflectionCapabilities({
'getMetadata': (key, targetCls) => {
return (targetCls == TestClassTypesOnlyDec) ? mockDataForTestClassTypesOnly[key] : null;
}
});
expect(rc.parameters(TestClassTypesOnlyDec)).toEqual([[P1], [P2]]);
});
}

View File

@ -100,6 +100,7 @@ module.exports = function makeNodeTree(destinationPath) {
var typescriptTree = compileWithTypescript(modulesTree, {
allowNonTsExtensions: false,
emitDecoratorMetadata: true,
declaration: true,
mapRoot: '', /* force sourcemaps to use relative path */
module: 'commonjs',

View File

@ -5,6 +5,7 @@ var minijasminenode2 = require('minijasminenode2');
var path = require('path');
// Require traceur to exposes $traceurRuntime on global context so that CJS files can run
require('traceur/bin/traceur-runtime.js');
require('reflect-metadata/Reflect');
glob(process.argv[2], function (error, specFiles) {
minijasminenode2.executeSpecs({