build(typescript): Migrated change detection to typescript
This commit is contained in:
parent
f0ef72d6cc
commit
fa28b28d0a
|
@ -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;
|
|
@ -4,9 +4,14 @@ 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;
|
||||
lightDomChildren: List<any>;
|
||||
shadowDomChildren: List<any>;
|
||||
parent: ChangeDetector;
|
||||
mode: string;
|
||||
ref: ChangeDetectorRef;
|
||||
|
@ -24,30 +29,20 @@ export class AbstractChangeDetector extends ChangeDetector {
|
|||
cd.parent = this;
|
||||
}
|
||||
|
||||
removeChild(cd:ChangeDetector) {
|
||||
ListWrapper.remove(this.lightDomChildren, cd);
|
||||
}
|
||||
removeChild(cd: ChangeDetector) { ListWrapper.remove(this.lightDomChildren, cd); }
|
||||
|
||||
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) {
|
||||
if (this.mode === DETACHED || this.mode === CHECKED) return;
|
||||
|
@ -80,12 +75,10 @@ export class AbstractChangeDetector extends ChangeDetector {
|
|||
}
|
||||
}
|
||||
|
||||
markAsCheckOnce() {
|
||||
this.mode = CHECK_ONCE;
|
||||
}
|
||||
markAsCheckOnce() { this.mode = CHECK_ONCE; }
|
||||
|
||||
markPathToRootAsCheckOnce() {
|
||||
var c = this;
|
||||
var c: ChangeDetector = this;
|
||||
while (isPresent(c) && c.mode != DETACHED) {
|
||||
if (c.mode === CHECKED) c.mode = CHECK_ONCE;
|
||||
c = c.parent;
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
|
@ -20,6 +20,12 @@ import {
|
|||
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;
|
||||
|
||||
|
||||
/**
|
||||
* The code generator takes a list of proto records and creates a function/class
|
||||
* that "emulates" what the developer would write by hand to implement the same
|
||||
|
@ -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`;
|
||||
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`;
|
||||
detectorInit +=
|
||||
`${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
|
||||
}
|
||||
|
||||
return `
|
||||
|
@ -129,7 +138,8 @@ function onAllChangesDoneTemplate(directive:string):string {
|
|||
}
|
||||
|
||||
|
||||
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}
|
||||
|
@ -186,19 +198,19 @@ 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}) {
|
||||
|
@ -211,7 +223,8 @@ 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};
|
||||
|
@ -253,21 +266,13 @@ ${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;
|
||||
|
||||
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);
|
||||
|
@ -298,8 +303,9 @@ export class ChangeDetectorJITGenerator {
|
|||
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);
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
|
||||
'directiveRecords', text)(AbstractChangeDetector, ChangeDetectionUtil,
|
||||
this.records, this.directiveRecords);
|
||||
}
|
||||
|
||||
genConstructor(): string {
|
||||
|
@ -318,16 +324,13 @@ export class ChangeDetectorJITGenerator {
|
|||
}
|
||||
|
||||
getDetectorFieldNames(): List<string> {
|
||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection()).map((d) => this.getDetector(d.directiveIndex));
|
||||
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 = [];
|
||||
|
@ -373,13 +376,9 @@ export class ChangeDetectorJITGenerator {
|
|||
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 {
|
||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||
|
@ -402,8 +401,8 @@ 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 {
|
||||
|
@ -488,9 +487,7 @@ 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 "";
|
||||
|
@ -500,7 +497,8 @@ export class ChangeDetectorJITGenerator {
|
|||
|
||||
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);
|
||||
|
@ -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(", "); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() {
|
|
@ -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.
|
||||
|
@ -38,51 +43,26 @@ export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
|
|||
}
|
||||
|
||||
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
|
||||
);
|
||||
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 &&
|
||||
return ListWrapper.find(rs, (rr) => rr.mode === r.mode && rr.funcOrValue === r.funcOrValue &&
|
||||
rr.contextIndex === r.contextIndex &&
|
||||
ListWrapper.equals(rr.args, r.args)
|
||||
);
|
||||
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;
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
// 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.
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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); }
|
||||
}
|
|
@ -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 {
|
||||
|
@ -24,31 +24,27 @@ import {
|
|||
|
||||
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;
|
||||
changes:List;
|
||||
pipes:List;
|
||||
prevContexts:List;
|
||||
|
||||
protos:List<ProtoRecord>;
|
||||
values: List<any>;
|
||||
changes: List<any>;
|
||||
pipes: List<any>;
|
||||
prevContexts: List<any>;
|
||||
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,10 +52,6 @@ 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) {
|
||||
|
@ -86,9 +78,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
|||
}
|
||||
}
|
||||
|
||||
hydrated():boolean {
|
||||
return this.values[0] !== uninitialized;
|
||||
}
|
||||
hydrated(): boolean { return this.values[0] !== uninitialized; }
|
||||
|
||||
detectChangesInRecords(throwOnChange: boolean) {
|
||||
var protos: List < ProtoRecord >= this.protos;
|
||||
|
@ -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);
|
||||
|
@ -284,25 +270,15 @@ 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 {
|
||||
return proto.isPureFunction() && !this._argsChanged(proto);
|
||||
|
@ -334,6 +310,3 @@ function isSame(a, b) {
|
|||
if ((a !== a) && (b !== b)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -3,10 +3,13 @@ import {Locals} from './parser/locals';
|
|||
import {DEFAULT} from './constants';
|
||||
import {BindingRecord} from './binding_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 ProtoChangeDetector {
|
||||
instantiate(dispatcher:any):ChangeDetector{
|
||||
return null;
|
||||
}
|
||||
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,7 +38,8 @@ export class ProtoChangeDetector {
|
|||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
export class ChangeDetection {
|
||||
createProtoChangeDetector(name:string, bindingRecords:List, variableBindings:List, directiveRecords:List,
|
||||
createProtoChangeDetector(name: string, bindingRecords: List<any>, variableBindings: List<any>,
|
||||
directiveRecords: List<any>,
|
||||
changeControlStrategy: string = DEFAULT): ProtoChangeDetector {
|
||||
return null;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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'
|
||||
]);
|
|
@ -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']);
|
|
@ -1,14 +1,13 @@
|
|||
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export class Locals {
|
||||
parent:Locals;
|
||||
current:Map;
|
||||
// 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;
|
||||
|
||||
constructor(parent:Locals, current:Map) {
|
||||
this.parent = parent;
|
||||
this.current = current;
|
||||
}
|
||||
export class Locals {
|
||||
constructor(public parent: Locals, public current: Map<any, any>) {}
|
||||
|
||||
contains(name: string): boolean {
|
||||
if (MapWrapper.contains(this.current, name)) {
|
||||
|
@ -45,7 +44,5 @@ export class Locals {
|
|||
}
|
||||
}
|
||||
|
||||
clearValues():void {
|
||||
MapWrapper.clearValues(this.current);
|
||||
}
|
||||
clearValues(): void { MapWrapper.clearValues(this.current); }
|
||||
}
|
|
@ -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,11 +42,16 @@ import {
|
|||
Interpolation,
|
||||
MethodCall,
|
||||
FunctionCall,
|
||||
TemplateBindings,
|
||||
TemplateBinding,
|
||||
ASTWithSource
|
||||
} 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...
|
||||
var INTERPOLATION_REGEXP = RegExpWrapper.create('\\{\\{(.*?)\\}\\}');
|
||||
|
@ -53,11 +77,11 @@ export class Parser {
|
|||
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),
|
||||
var res: AST = ListWrapper.reduce(
|
||||
pipes, (result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
|
||||
bindingAst.ast);
|
||||
return new ASTWithSource(res, bindingAst.source, bindingAst.location);
|
||||
}
|
||||
|
@ -92,23 +116,13 @@ export class Parser {
|
|||
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;
|
||||
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 {
|
||||
|
@ -116,17 +130,13 @@ class _ParseAST {
|
|||
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 {
|
||||
return (this.index < this.tokens.length) ? this.next.index : this.input.length;
|
||||
}
|
||||
|
||||
advance() {
|
||||
this.index ++;
|
||||
}
|
||||
advance() { this.index++; }
|
||||
|
||||
optionalCharacter(code: int): boolean {
|
||||
if (this.next.isCharacter(code)) {
|
||||
|
@ -146,9 +156,7 @@ class _ParseAST {
|
|||
}
|
||||
}
|
||||
|
||||
peekKeywordVar():boolean {
|
||||
return this.next.isKeywordVar() || this.next.isOperator('#');
|
||||
}
|
||||
peekKeywordVar(): boolean { return this.next.isKeywordVar() || this.next.isOperator('#'); }
|
||||
|
||||
expectCharacter(code: int) {
|
||||
if (this.optionalCharacter(code)) return;
|
||||
|
@ -198,7 +206,8 @@ class _ParseAST {
|
|||
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}'`);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -545,10 +554,10 @@ class _ParseAST {
|
|||
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}`);
|
||||
}
|
||||
}
|
|
@ -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({
|
||||
|
@ -45,9 +53,7 @@ export class AsyncPipe extends Pipe {
|
|||
this._observable = null;
|
||||
}
|
||||
|
||||
supports(obs):boolean {
|
||||
return ObservableWrapper.isObservable(obs);
|
||||
}
|
||||
supports(obs): boolean { return ObservableWrapper.isObservable(obs); }
|
||||
|
||||
onDestroy(): void {
|
||||
if (isPresent(this._subscription)) {
|
||||
|
@ -76,10 +82,8 @@ export class AsyncPipe extends Pipe {
|
|||
|
||||
_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 {
|
||||
|
@ -101,17 +105,11 @@ export class AsyncPipe extends Pipe {
|
|||
*
|
||||
* @exportedAs angular2/pipes
|
||||
*/
|
||||
export class AsyncPipeFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
export class AsyncPipeFactory extends PipeFactory {
|
||||
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); }
|
||||
}
|
|
@ -17,38 +17,37 @@ import {
|
|||
|
||||
import {WrappedValue, Pipe, PipeFactory} from './pipe';
|
||||
|
||||
export class IterableChangesFactory extends PipeFactory {
|
||||
// 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()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
export class IterableChangesFactory extends PipeFactory {
|
||||
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,21 +69,13 @@ 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;
|
||||
|
@ -121,7 +112,7 @@ export class IterableChanges extends Pipe {
|
|||
}
|
||||
}
|
||||
|
||||
transform(collection){
|
||||
transform(collection): any {
|
||||
if (this.check(collection)) {
|
||||
return WrappedValue.wrap(this);
|
||||
} else {
|
||||
|
@ -176,9 +167,7 @@ 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;
|
||||
return this._additionsHead !== null || this._movesHead !== null || this._removalsHead !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,8 +267,8 @@ export class IterableChanges extends Pipe {
|
|||
* at the end.
|
||||
*/
|
||||
_verifyReinsertion(record: CollectionChangeRecord, item, index: int): CollectionChangeRecord {
|
||||
var reinsertRecord:CollectionChangeRecord = this._unlinkedRecords === null ?
|
||||
null : this._unlinkedRecords.get(item);
|
||||
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) {
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -519,9 +506,12 @@ export class CollectionChangeRecord {
|
|||
item;
|
||||
|
||||
_nextPrevious: CollectionChangeRecord;
|
||||
_prev:CollectionChangeRecord; _next:CollectionChangeRecord;
|
||||
_prevDup:CollectionChangeRecord; _nextDup:CollectionChangeRecord;
|
||||
_prevRemoved:CollectionChangeRecord; _nextRemoved:CollectionChangeRecord;
|
||||
_prev: CollectionChangeRecord;
|
||||
_next: CollectionChangeRecord;
|
||||
_prevDup: CollectionChangeRecord;
|
||||
_nextDup: CollectionChangeRecord;
|
||||
_prevRemoved: CollectionChangeRecord;
|
||||
_nextRemoved: CollectionChangeRecord;
|
||||
_nextAdded: CollectionChangeRecord;
|
||||
_nextMoved: CollectionChangeRecord;
|
||||
|
||||
|
@ -625,10 +615,8 @@ class _DuplicateItemRecordList {
|
|||
}
|
||||
|
||||
class _DuplicateMap {
|
||||
map:Map;
|
||||
constructor() {
|
||||
this.map = MapWrapper.create();
|
||||
}
|
||||
map: Map<any, any>;
|
||||
constructor() { this.map = MapWrapper.create(); }
|
||||
|
||||
put(record: CollectionChangeRecord) {
|
||||
// todo(vicb) handle corner cases
|
||||
|
@ -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) + ')'; }
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
export class KeyValueChangesFactory extends PipeFactory {
|
||||
@CONST()
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
export class KeyValueChangesFactory extends PipeFactory {
|
||||
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 {
|
||||
|
@ -66,8 +60,7 @@ export class KeyValueChanges extends Pipe {
|
|||
}
|
||||
|
||||
get isDirty(): boolean {
|
||||
return this._additionsHead !== null ||
|
||||
this._changesHead !== null ||
|
||||
return this._additionsHead !== null || this._changesHead !== null ||
|
||||
this._removalsHead !== null;
|
||||
}
|
||||
|
||||
|
@ -162,9 +155,7 @@ export class KeyValueChanges extends Pipe {
|
|||
if (this.isDirty) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -233,8 +224,7 @@ export class KeyValueChanges extends Pipe {
|
|||
}
|
||||
|
||||
_isInRemovals(record: KVChangeRecord) {
|
||||
return record === this._removalsHead ||
|
||||
record._nextRemoved !== null ||
|
||||
return record === this._removalsHead || record._nextRemoved !== null ||
|
||||
record._prevRemoved !== null;
|
||||
}
|
||||
|
||||
|
@ -342,10 +332,8 @@ 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";
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,20 @@
|
|||
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(wrapped:any) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
constructor(public wrapped: any) {}
|
||||
|
||||
static wrap(value: any): WrappedValue {
|
||||
var w = _wrappedValues[_wrappedIndex++ % 5];
|
||||
|
@ -58,21 +60,20 @@ export class Pipe {
|
|||
transform(value: any): any { return null; }
|
||||
}
|
||||
|
||||
@ABSTRACT()
|
||||
export class PipeFactory {
|
||||
// TODO: vsavkin: make it an interface
|
||||
@CONST()
|
||||
constructor() {
|
||||
}
|
||||
|
||||
export class PipeFactory {
|
||||
supports(obs): boolean {
|
||||
return _abstract();
|
||||
_abstract();
|
||||
return false;
|
||||
}
|
||||
|
||||
create(cdRef): Pipe {
|
||||
return _abstract();
|
||||
_abstract();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _abstract() {
|
||||
return new BaseException('This method is abstract');
|
||||
throw new BaseException('This method is abstract');
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
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(config){
|
||||
this.config = config;
|
||||
}
|
||||
constructor(public config) {}
|
||||
|
||||
get(type: string, obj, cdRef: ChangeDetectorRef): Pipe {
|
||||
var listOfConfigs = this.config[type];
|
||||
|
@ -18,8 +19,7 @@ export class PipeRegistry {
|
|||
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}'`);
|
|
@ -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`);
|
||||
}
|
||||
}
|
|
@ -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`);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
library angular2.di.decorators;
|
||||
|
||||
/* This file is empty because, Dart does not have decorators. */
|
||||
|
||||
export 'annotations.dart';
|
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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 [];
|
||||
|
|
|
@ -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]]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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({
|
||||
|
|
Loading…
Reference in New Issue