refactor(view_compiler): codegen DI and Queries
BREAKING CHANGE: - Renderer: * renderComponent method is removed form `Renderer`, only present on `RootRenderer` * Renderer.setDebugInfo is removed. Renderer.createElement / createText / createTemplateAnchor now take the DebugInfo directly. - Query semantics: * Queries don't work with dynamically loaded components. * e.g. for router-outlet: loaded components can't be queries via @ViewQuery, but router-outlet emits an event `activate` now that emits the activated component - Exception classes and the context inside changed (renamed fields) - DebugElement.attributes is an Object and not a Map in JS any more - ChangeDetectorGenConfig was renamed into CompilerConfig - AppViewManager.createEmbeddedViewInContainer / AppViewManager.createHostViewInContainer are removed, use the methods in ViewContainerRef instead - Change detection order changed: * 1. dirty check component inputs * 2. dirty check content children * 3. update render nodes Closes #6301 Closes #6567
This commit is contained in:
parent
45f09ba686
commit
2b34c88b69
70
gulpfile.js
70
gulpfile.js
|
@ -177,8 +177,8 @@ var PAYLOAD_TESTS_CONFIG = {
|
||||||
return path.join(__dirname, CONFIG.dest.js.prod.es5, 'payload_tests', caseName,
|
return path.join(__dirname, CONFIG.dest.js.prod.es5, 'payload_tests', caseName,
|
||||||
'ts/' + packaging);
|
'ts/' + packaging);
|
||||||
},
|
},
|
||||||
systemjs: {sizeLimits: {'uncompressed': 870 * 1024, 'gzip level=9': 165 * 1024}},
|
systemjs: {sizeLimits: {'uncompressed': 880 * 1024, 'gzip level=9': 170 * 1024}},
|
||||||
webpack: {sizeLimits: {'uncompressed': 550 * 1024, 'gzip level=9': 120 * 1024}}
|
webpack: {sizeLimits: {'uncompressed': 560 * 1024, 'gzip level=9': 130 * 1024}}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -640,7 +640,7 @@ gulp.task('buildRouter.dev', function() {
|
||||||
gulp.task('test.unit.dart', function(done) {
|
gulp.task('test.unit.dart', function(done) {
|
||||||
printModulesWarning();
|
printModulesWarning();
|
||||||
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
||||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
|
'!build/remove-pub-symlinks', function(error) {
|
||||||
var watch = require('./tools/build/watch');
|
var watch = require('./tools/build/watch');
|
||||||
|
|
||||||
// if initial build failed (likely due to build or formatting step) then exit
|
// if initial build failed (likely due to build or formatting step) then exit
|
||||||
|
@ -779,7 +779,7 @@ gulp.task('!checkAndReport.payload.js', function() {
|
||||||
|
|
||||||
gulp.task('watch.dart.dev', function(done) {
|
gulp.task('watch.dart.dev', function(done) {
|
||||||
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
||||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', function(error) {
|
'!build/remove-pub-symlinks', function(error) {
|
||||||
var watch = require('./tools/build/watch');
|
var watch = require('./tools/build/watch');
|
||||||
|
|
||||||
// if initial build failed (likely due to build or formatting step) then exit
|
// if initial build failed (likely due to build or formatting step) then exit
|
||||||
|
@ -789,7 +789,8 @@ gulp.task('watch.dart.dev', function(done) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(['modules/angular2/**'], {ignoreInitial: true}, ['!build/tree.dart']);
|
watch(['modules/angular2/**', 'modules_dart/**'], {ignoreInitial: true},
|
||||||
|
['!build/tree.dart', 'build/pure-packages.dart']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -911,21 +912,20 @@ gulp.task('test.unit.cjs', ['build/clean.js', 'build.tools'], function(neverDone
|
||||||
gulp.task('test.unit.dartvm', function(neverDone) {
|
gulp.task('test.unit.dartvm', function(neverDone) {
|
||||||
var watch = require('./tools/build/watch');
|
var watch = require('./tools/build/watch');
|
||||||
|
|
||||||
runSequence(
|
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
||||||
'build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
'!test.unit.dartvm/run', function(error) {
|
||||||
'!build/change_detect.dart', '!test.unit.dartvm/run', function(error) {
|
// Watch for changes made in the TS and Dart code under "modules" and
|
||||||
// Watch for changes made in the TS and Dart code under "modules" and
|
// run ts2dart and test change detector generator prior to rerunning the
|
||||||
// run ts2dart and test change detector generator prior to rerunning the
|
// tests.
|
||||||
// tests.
|
watch('modules/angular2/**', {ignoreInitial: true},
|
||||||
watch('modules/angular2/**', {ignoreInitial: true},
|
['!build/tree.dart', '!test.unit.dartvm/run']);
|
||||||
['!build/tree.dart', '!build/change_detect.dart', '!test.unit.dartvm/run']);
|
|
||||||
|
|
||||||
// Watch for changes made in Dart code under "modules_dart", then copy it
|
// Watch for changes made in Dart code under "modules_dart", then copy it
|
||||||
// to dist and run test change detector generator prior to retunning the
|
// to dist and run test change detector generator prior to retunning the
|
||||||
// tests.
|
// tests.
|
||||||
watch('modules_dart/**', {ignoreInitial: true},
|
watch('modules_dart/**', {ignoreInitial: true},
|
||||||
['build/pure-packages.dart', '!build/change_detect.dart', '!test.unit.dartvm/run']);
|
['build/pure-packages.dart', '!test.unit.dartvm/run']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('!test.unit.dartvm/run',
|
gulp.task('!test.unit.dartvm/run',
|
||||||
|
@ -1069,9 +1069,9 @@ gulp.task('build/pure-packages.dart/angular2', function() {
|
||||||
|
|
||||||
// Builds all Dart packages, but does not compile them
|
// Builds all Dart packages, but does not compile them
|
||||||
gulp.task('build/packages.dart', function(done) {
|
gulp.task('build/packages.dart', function(done) {
|
||||||
runSequence('lint_protos.dart', 'build/tree.dart', 'build/pure-packages.dart',
|
runSequence('lint_protos.dart', 'pubget.dart', 'build/tree.dart', 'build/pure-packages.dart',
|
||||||
// Run after 'build/tree.dart' because broccoli clears the dist/dart folder
|
// Run after 'build/tree.dart' because broccoli clears the dist/dart folder
|
||||||
'!build/pubget.angular2.dart', '!build/change_detect.dart', sequenceComplete(done));
|
'!build/pubget.angular2.dart', sequenceComplete(done));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Builds and compiles all Dart packages
|
// Builds and compiles all Dart packages
|
||||||
|
@ -1467,34 +1467,6 @@ gulp.task('gen_protos.dart', function(done) {
|
||||||
done);
|
done);
|
||||||
});
|
});
|
||||||
|
|
||||||
// change detection codegen
|
|
||||||
gulp.task('build.change_detect.dart', function(done) {
|
|
||||||
return runSequence('build/packages.dart', '!build/pubget.angular2.dart',
|
|
||||||
'!build/change_detect.dart', done);
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task('!build/change_detect.dart', function(done) {
|
|
||||||
var fs = require('fs');
|
|
||||||
var spawn = require('child_process').spawn;
|
|
||||||
|
|
||||||
var changeDetectDir = path.join(CONFIG.dest.dart, 'angular2/test/core/change_detection/');
|
|
||||||
var srcDir = path.join(changeDetectDir, 'generator');
|
|
||||||
var destDir = path.join(changeDetectDir, 'generated');
|
|
||||||
|
|
||||||
var dartStream = fs.createWriteStream(path.join(destDir, 'change_detector_classes.dart'));
|
|
||||||
var genMain = path.join(srcDir, 'gen_change_detectors.dart');
|
|
||||||
var proc = spawn(DART_SDK.VM, [genMain], {stdio: ['ignore', 'pipe', 'inherit']});
|
|
||||||
proc.on('error', function(code) {
|
|
||||||
done(new Error('Failed while generating change detector classes. Please run manually: ' +
|
|
||||||
DART_SDK.VM + ' ' + dartArgs.join(' ')));
|
|
||||||
});
|
|
||||||
proc.on('close', function() {
|
|
||||||
dartStream.close();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
proc.stdout.pipe(dartStream);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------
|
// ------------
|
||||||
|
|
||||||
gulp.task('cleanup.builder', function() { return angularBuilder.cleanup(); });
|
gulp.task('cleanup.builder', function() { return angularBuilder.cleanup(); });
|
||||||
|
|
|
@ -14,4 +14,5 @@ export 'package:angular2/src/core/application_tokens.dart'
|
||||||
export 'package:angular2/src/platform/dom/dom_tokens.dart';
|
export 'package:angular2/src/platform/dom/dom_tokens.dart';
|
||||||
export 'package:angular2/src/platform/dom/dom_adapter.dart';
|
export 'package:angular2/src/platform/dom/dom_adapter.dart';
|
||||||
export 'package:angular2/src/platform/dom/events/event_manager.dart';
|
export 'package:angular2/src/platform/dom/events/event_manager.dart';
|
||||||
export 'package:angular2/src/compiler/url_resolver.dart';
|
export 'package:angular2/src/compiler/compiler.dart' show UrlResolver, DirectiveResolver, ViewResolver;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,35 @@
|
||||||
* @description
|
* @description
|
||||||
* Starting point to import all compiler APIs.
|
* Starting point to import all compiler APIs.
|
||||||
*/
|
*/
|
||||||
export * from './src/compiler/url_resolver';
|
export {
|
||||||
export * from './src/compiler/xhr';
|
PLATFORM_DIRECTIVES,
|
||||||
export * from './src/compiler/compiler';
|
PLATFORM_PIPES,
|
||||||
|
COMPILER_PROVIDERS,
|
||||||
|
TEMPLATE_TRANSFORMS,
|
||||||
|
CompilerConfig,
|
||||||
|
RenderTypes,
|
||||||
|
UrlResolver,
|
||||||
|
DEFAULT_PACKAGE_URL_PROVIDER,
|
||||||
|
createOfflineCompileUrlResolver,
|
||||||
|
XHR,
|
||||||
|
ViewResolver,
|
||||||
|
DirectiveResolver,
|
||||||
|
PipeResolver,
|
||||||
|
SourceModule,
|
||||||
|
NormalizedComponentWithViewDirectives,
|
||||||
|
OfflineCompiler,
|
||||||
|
CompileMetadataWithIdentifier,
|
||||||
|
CompileMetadataWithType,
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompileDiDependencyMetadata,
|
||||||
|
CompileProviderMetadata,
|
||||||
|
CompileFactoryMetadata,
|
||||||
|
CompileTokenMetadata,
|
||||||
|
CompileTypeMetadata,
|
||||||
|
CompileQueryMetadata,
|
||||||
|
CompileTemplateMetadata,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompilePipeMetadata
|
||||||
|
} from 'angular2/src/compiler/compiler';
|
||||||
|
|
||||||
|
export * from 'angular2/src/compiler/template_ast';
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import {
|
import {
|
||||||
APP_ID,
|
APP_ID,
|
||||||
DirectiveResolver,
|
|
||||||
NgZone,
|
NgZone,
|
||||||
Provider,
|
Provider,
|
||||||
ViewResolver,
|
|
||||||
PLATFORM_COMMON_PROVIDERS,
|
PLATFORM_COMMON_PROVIDERS,
|
||||||
PLATFORM_INITIALIZER
|
PLATFORM_INITIALIZER
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
|
import {DirectiveResolver, ViewResolver} from 'angular2/compiler';
|
||||||
import {BROWSER_APP_COMMON_PROVIDERS} from 'angular2/src/platform/browser_common';
|
import {BROWSER_APP_COMMON_PROVIDERS} from 'angular2/src/platform/browser_common';
|
||||||
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
|
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import {
|
import {
|
||||||
APP_ID,
|
APP_ID,
|
||||||
DirectiveResolver,
|
|
||||||
NgZone,
|
NgZone,
|
||||||
Provider,
|
Provider,
|
||||||
ViewResolver,
|
|
||||||
PLATFORM_COMMON_PROVIDERS,
|
PLATFORM_COMMON_PROVIDERS,
|
||||||
PLATFORM_INITIALIZER,
|
PLATFORM_INITIALIZER,
|
||||||
APPLICATION_COMMON_PROVIDERS,
|
APPLICATION_COMMON_PROVIDERS,
|
||||||
Renderer
|
Renderer
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
|
import {DirectiveResolver, ViewResolver} from 'angular2/compiler';
|
||||||
|
|
||||||
import {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter';
|
import {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter';
|
||||||
|
|
||||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
|
@ -28,7 +28,7 @@ import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
||||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||||
import {RootRenderer} from 'angular2/src/core/render/api';
|
import {RootRenderer} from 'angular2/src/core/render/api';
|
||||||
import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
|
import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
|
||||||
import {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
|
import {DomSharedStylesHost, SharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EventManager,
|
EventManager,
|
||||||
|
@ -78,6 +78,7 @@ export const TEST_SERVER_APPLICATION_PROVIDERS: Array<any /*Type | Provider | an
|
||||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
|
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
|
||||||
new Provider(XHR, {useClass: XHR}),
|
new Provider(XHR, {useClass: XHR}),
|
||||||
new Provider(APP_ID, {useValue: 'a'}),
|
new Provider(APP_ID, {useValue: 'a'}),
|
||||||
|
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
ELEMENT_PROBE_PROVIDERS,
|
ELEMENT_PROBE_PROVIDERS,
|
||||||
new Provider(DirectiveResolver, {useClass: MockDirectiveResolver}),
|
new Provider(DirectiveResolver, {useClass: MockDirectiveResolver}),
|
||||||
|
|
|
@ -153,7 +153,7 @@ export class NgFor implements DoCheck {
|
||||||
var tuple = tuples[i];
|
var tuple = tuples[i];
|
||||||
// separate moved views from removed views.
|
// separate moved views from removed views.
|
||||||
if (isPresent(tuple.record.currentIndex)) {
|
if (isPresent(tuple.record.currentIndex)) {
|
||||||
tuple.view = this._viewContainer.detach(tuple.record.previousIndex);
|
tuple.view = <EmbeddedViewRef>this._viewContainer.detach(tuple.record.previousIndex);
|
||||||
movedTuples.push(tuple);
|
movedTuples.push(tuple);
|
||||||
} else {
|
} else {
|
||||||
this._viewContainer.remove(tuple.record.previousIndex);
|
this._viewContainer.remove(tuple.record.previousIndex);
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
|
||||||
|
|
||||||
import {
|
|
||||||
DirectiveIndex,
|
|
||||||
BindingRecord,
|
|
||||||
DirectiveRecord,
|
|
||||||
ChangeDetectionStrategy,
|
|
||||||
ChangeDetectorDefinition,
|
|
||||||
ChangeDetectorGenConfig,
|
|
||||||
ASTWithSource
|
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileTypeMetadata} from './directive_metadata';
|
|
||||||
import {
|
|
||||||
TemplateAst,
|
|
||||||
ElementAst,
|
|
||||||
BoundTextAst,
|
|
||||||
PropertyBindingType,
|
|
||||||
DirectiveAst,
|
|
||||||
TemplateAstVisitor,
|
|
||||||
templateVisitAll,
|
|
||||||
NgContentAst,
|
|
||||||
EmbeddedTemplateAst,
|
|
||||||
VariableAst,
|
|
||||||
BoundElementPropertyAst,
|
|
||||||
BoundEventAst,
|
|
||||||
BoundDirectivePropertyAst,
|
|
||||||
AttrAst,
|
|
||||||
TextAst
|
|
||||||
} from './template_ast';
|
|
||||||
import {LifecycleHooks} from 'angular2/src/core/linker/interfaces';
|
|
||||||
|
|
||||||
export function createChangeDetectorDefinitions(
|
|
||||||
componentType: CompileTypeMetadata, componentStrategy: ChangeDetectionStrategy,
|
|
||||||
genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] {
|
|
||||||
var pvVisitors = [];
|
|
||||||
var visitor = new ProtoViewVisitor(null, pvVisitors, componentStrategy);
|
|
||||||
templateVisitAll(visitor, parsedTemplate);
|
|
||||||
return createChangeDefinitions(pvVisitors, componentType, genConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProtoViewVisitor implements TemplateAstVisitor {
|
|
||||||
viewIndex: number;
|
|
||||||
nodeCount: number = 0;
|
|
||||||
boundElementCount: number = 0;
|
|
||||||
variableNames: string[] = [];
|
|
||||||
bindingRecords: BindingRecord[] = [];
|
|
||||||
eventRecords: BindingRecord[] = [];
|
|
||||||
directiveRecords: DirectiveRecord[] = [];
|
|
||||||
|
|
||||||
constructor(public parent: ProtoViewVisitor, public allVisitors: ProtoViewVisitor[],
|
|
||||||
public strategy: ChangeDetectionStrategy) {
|
|
||||||
this.viewIndex = allVisitors.length;
|
|
||||||
allVisitors.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
|
||||||
this.nodeCount++;
|
|
||||||
this.boundElementCount++;
|
|
||||||
templateVisitAll(this, ast.outputs);
|
|
||||||
for (var i = 0; i < ast.directives.length; i++) {
|
|
||||||
ast.directives[i].visit(this, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
var childVisitor =
|
|
||||||
new ProtoViewVisitor(this, this.allVisitors, ChangeDetectionStrategy.Default);
|
|
||||||
// Attention: variables present on an embedded template count towards
|
|
||||||
// the embedded template and not the template anchor!
|
|
||||||
templateVisitAll(childVisitor, ast.vars);
|
|
||||||
templateVisitAll(childVisitor, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
|
||||||
this.nodeCount++;
|
|
||||||
if (ast.isBound()) {
|
|
||||||
this.boundElementCount++;
|
|
||||||
}
|
|
||||||
templateVisitAll(this, ast.inputs, null);
|
|
||||||
templateVisitAll(this, ast.outputs);
|
|
||||||
templateVisitAll(this, ast.exportAsVars);
|
|
||||||
for (var i = 0; i < ast.directives.length; i++) {
|
|
||||||
ast.directives[i].visit(this, i);
|
|
||||||
}
|
|
||||||
templateVisitAll(this, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitVariable(ast: VariableAst, context: any): any {
|
|
||||||
this.variableNames.push(ast.name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any {
|
|
||||||
var bindingRecord =
|
|
||||||
isPresent(directiveRecord) ?
|
|
||||||
BindingRecord.createForHostEvent(ast.handler, ast.fullName, directiveRecord) :
|
|
||||||
BindingRecord.createForEvent(ast.handler, ast.fullName, this.boundElementCount - 1);
|
|
||||||
this.eventRecords.push(bindingRecord);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitElementProperty(ast: BoundElementPropertyAst, directiveRecord: DirectiveRecord): any {
|
|
||||||
var boundElementIndex = this.boundElementCount - 1;
|
|
||||||
var dirIndex = isPresent(directiveRecord) ? directiveRecord.directiveIndex : null;
|
|
||||||
var bindingRecord;
|
|
||||||
if (ast.type === PropertyBindingType.Property) {
|
|
||||||
bindingRecord =
|
|
||||||
isPresent(dirIndex) ?
|
|
||||||
BindingRecord.createForHostProperty(dirIndex, ast.value, ast.name) :
|
|
||||||
BindingRecord.createForElementProperty(ast.value, boundElementIndex, ast.name);
|
|
||||||
} else if (ast.type === PropertyBindingType.Attribute) {
|
|
||||||
bindingRecord =
|
|
||||||
isPresent(dirIndex) ?
|
|
||||||
BindingRecord.createForHostAttribute(dirIndex, ast.value, ast.name) :
|
|
||||||
BindingRecord.createForElementAttribute(ast.value, boundElementIndex, ast.name);
|
|
||||||
} else if (ast.type === PropertyBindingType.Class) {
|
|
||||||
bindingRecord =
|
|
||||||
isPresent(dirIndex) ?
|
|
||||||
BindingRecord.createForHostClass(dirIndex, ast.value, ast.name) :
|
|
||||||
BindingRecord.createForElementClass(ast.value, boundElementIndex, ast.name);
|
|
||||||
} else if (ast.type === PropertyBindingType.Style) {
|
|
||||||
bindingRecord =
|
|
||||||
isPresent(dirIndex) ?
|
|
||||||
BindingRecord.createForHostStyle(dirIndex, ast.value, ast.name, ast.unit) :
|
|
||||||
BindingRecord.createForElementStyle(ast.value, boundElementIndex, ast.name, ast.unit);
|
|
||||||
}
|
|
||||||
this.bindingRecords.push(bindingRecord);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitAttr(ast: AttrAst, context: any): any { return null; }
|
|
||||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
|
||||||
var nodeIndex = this.nodeCount++;
|
|
||||||
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, nodeIndex));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitText(ast: TextAst, context: any): any {
|
|
||||||
this.nodeCount++;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
|
||||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
|
||||||
var directiveMetadata = ast.directive;
|
|
||||||
var outputsArray = [];
|
|
||||||
StringMapWrapper.forEach(
|
|
||||||
ast.directive.outputs,
|
|
||||||
(eventName: string, dirProperty: string) => outputsArray.push([dirProperty, eventName]));
|
|
||||||
var directiveRecord = new DirectiveRecord({
|
|
||||||
directiveIndex: directiveIndex,
|
|
||||||
callAfterContentInit:
|
|
||||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1,
|
|
||||||
callAfterContentChecked:
|
|
||||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1,
|
|
||||||
callAfterViewInit:
|
|
||||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1,
|
|
||||||
callAfterViewChecked:
|
|
||||||
directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1,
|
|
||||||
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
|
||||||
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
|
||||||
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
|
|
||||||
callOnDestroy: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1,
|
|
||||||
changeDetection: directiveMetadata.changeDetection,
|
|
||||||
outputs: outputsArray
|
|
||||||
});
|
|
||||||
this.directiveRecords.push(directiveRecord);
|
|
||||||
|
|
||||||
templateVisitAll(this, ast.inputs, directiveRecord);
|
|
||||||
var bindingRecords = this.bindingRecords;
|
|
||||||
if (directiveRecord.callOnChanges) {
|
|
||||||
bindingRecords.push(BindingRecord.createDirectiveOnChanges(directiveRecord));
|
|
||||||
}
|
|
||||||
if (directiveRecord.callOnInit) {
|
|
||||||
bindingRecords.push(BindingRecord.createDirectiveOnInit(directiveRecord));
|
|
||||||
}
|
|
||||||
if (directiveRecord.callDoCheck) {
|
|
||||||
bindingRecords.push(BindingRecord.createDirectiveDoCheck(directiveRecord));
|
|
||||||
}
|
|
||||||
templateVisitAll(this, ast.hostProperties, directiveRecord);
|
|
||||||
templateVisitAll(this, ast.hostEvents, directiveRecord);
|
|
||||||
templateVisitAll(this, ast.exportAsVars);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any {
|
|
||||||
// TODO: these setters should eventually be created by change detection, to make
|
|
||||||
// it monomorphic!
|
|
||||||
var setter = reflector.setter(ast.directiveName);
|
|
||||||
this.bindingRecords.push(
|
|
||||||
BindingRecord.createForDirective(ast.value, ast.directiveName, setter, directiveRecord));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function createChangeDefinitions(pvVisitors: ProtoViewVisitor[], componentType: CompileTypeMetadata,
|
|
||||||
genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] {
|
|
||||||
var pvVariableNames = _collectNestedProtoViewsVariableNames(pvVisitors);
|
|
||||||
return pvVisitors.map(pvVisitor => {
|
|
||||||
var id = `${componentType.name}_${pvVisitor.viewIndex}`;
|
|
||||||
return new ChangeDetectorDefinition(
|
|
||||||
id, pvVisitor.strategy, pvVariableNames[pvVisitor.viewIndex], pvVisitor.bindingRecords,
|
|
||||||
pvVisitor.eventRecords, pvVisitor.directiveRecords, genConfig);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function _collectNestedProtoViewsVariableNames(pvVisitors: ProtoViewVisitor[]): string[][] {
|
|
||||||
var nestedPvVariableNames: string[][] = ListWrapper.createFixedSize(pvVisitors.length);
|
|
||||||
pvVisitors.forEach((pv) => {
|
|
||||||
var parentVariableNames: string[] =
|
|
||||||
isPresent(pv.parent) ? nestedPvVariableNames[pv.parent.viewIndex] : [];
|
|
||||||
nestedPvVariableNames[pv.viewIndex] = parentVariableNames.concat(pv.variableNames);
|
|
||||||
});
|
|
||||||
return nestedPvVariableNames;
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
import {CompileTypeMetadata} from './directive_metadata';
|
|
||||||
import {SourceExpressions, moduleRef} from './source_module';
|
|
||||||
import {
|
|
||||||
ChangeDetectorJITGenerator
|
|
||||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
|
||||||
import {AbstractChangeDetector} from 'angular2/src/core/change_detection/abstract_change_detector';
|
|
||||||
import {ChangeDetectionUtil} from 'angular2/src/core/change_detection/change_detection_util';
|
|
||||||
import {ChangeDetectorState} from 'angular2/src/core/change_detection/constants';
|
|
||||||
|
|
||||||
import {createChangeDetectorDefinitions} from './change_definition_factory';
|
|
||||||
import {IS_DART, isJsObject, CONST_EXPR} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ChangeDetectorGenConfig,
|
|
||||||
ChangeDetectorDefinition,
|
|
||||||
DynamicProtoChangeDetector,
|
|
||||||
ChangeDetectionStrategy
|
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
|
|
||||||
import {TemplateAst} from './template_ast';
|
|
||||||
import {Codegen} from 'angular2/src/transform/template_compiler/change_detector_codegen';
|
|
||||||
import {MODULE_SUFFIX} from './util';
|
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
|
||||||
|
|
||||||
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
|
||||||
const UTIL = "ChangeDetectionUtil";
|
|
||||||
const CHANGE_DETECTOR_STATE = "ChangeDetectorState";
|
|
||||||
|
|
||||||
export const CHANGE_DETECTION_JIT_IMPORTS = CONST_EXPR({
|
|
||||||
'AbstractChangeDetector': AbstractChangeDetector,
|
|
||||||
'ChangeDetectionUtil': ChangeDetectionUtil,
|
|
||||||
'ChangeDetectorState': ChangeDetectorState
|
|
||||||
});
|
|
||||||
|
|
||||||
var ABSTRACT_CHANGE_DETECTOR_MODULE = moduleRef(
|
|
||||||
`package:angular2/src/core/change_detection/abstract_change_detector${MODULE_SUFFIX}`);
|
|
||||||
var UTIL_MODULE =
|
|
||||||
moduleRef(`package:angular2/src/core/change_detection/change_detection_util${MODULE_SUFFIX}`);
|
|
||||||
var PREGEN_PROTO_CHANGE_DETECTOR_MODULE = moduleRef(
|
|
||||||
`package:angular2/src/core/change_detection/pregen_proto_change_detector${MODULE_SUFFIX}`);
|
|
||||||
var CONSTANTS_MODULE =
|
|
||||||
moduleRef(`package:angular2/src/core/change_detection/constants${MODULE_SUFFIX}`);
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ChangeDetectionCompiler {
|
|
||||||
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
|
||||||
|
|
||||||
compileComponentRuntime(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
|
||||||
parsedTemplate: TemplateAst[]): Function[] {
|
|
||||||
var changeDetectorDefinitions =
|
|
||||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
|
||||||
return changeDetectorDefinitions.map(definition =>
|
|
||||||
this._createChangeDetectorFactory(definition));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
|
|
||||||
var proto = new DynamicProtoChangeDetector(definition);
|
|
||||||
return () => proto.instantiate();
|
|
||||||
}
|
|
||||||
|
|
||||||
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
|
||||||
parsedTemplate: TemplateAst[]): SourceExpressions {
|
|
||||||
var changeDetectorDefinitions =
|
|
||||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
|
||||||
var factories = [];
|
|
||||||
var index = 0;
|
|
||||||
var sourceParts = changeDetectorDefinitions.map(definition => {
|
|
||||||
var codegen: any;
|
|
||||||
var sourcePart: string;
|
|
||||||
// TODO(tbosch): move the 2 code generators to the same place, one with .dart and one with .ts
|
|
||||||
// suffix
|
|
||||||
// and have the same API for calling them!
|
|
||||||
if (IS_DART) {
|
|
||||||
codegen = new Codegen(PREGEN_PROTO_CHANGE_DETECTOR_MODULE);
|
|
||||||
var className = `_${definition.id}`;
|
|
||||||
var typeRef = (index === 0 && componentType.isHost) ?
|
|
||||||
'dynamic' :
|
|
||||||
`${moduleRef(componentType.moduleUrl)}${componentType.name}`;
|
|
||||||
codegen.generate(typeRef, className, definition);
|
|
||||||
factories.push(`${className}.newChangeDetector`);
|
|
||||||
sourcePart = codegen.toString();
|
|
||||||
} else {
|
|
||||||
codegen = new ChangeDetectorJITGenerator(
|
|
||||||
definition, `${UTIL_MODULE}${UTIL}`,
|
|
||||||
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`,
|
|
||||||
`${CONSTANTS_MODULE}${CHANGE_DETECTOR_STATE}`);
|
|
||||||
factories.push(`function() { return new ${codegen.typeName}(); }`);
|
|
||||||
sourcePart = codegen.generateSource();
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
return sourcePart;
|
|
||||||
});
|
|
||||||
return new SourceExpressions(sourceParts, factories);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,8 +12,8 @@ import {
|
||||||
StringWrapper,
|
StringWrapper,
|
||||||
isArray
|
isArray
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
import {unimplemented, BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper, MapWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
CHANGE_DETECTION_STRATEGY_VALUES
|
CHANGE_DETECTION_STRATEGY_VALUES
|
||||||
|
@ -21,7 +21,8 @@ import {
|
||||||
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
|
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
|
||||||
import {CssSelector} from 'angular2/src/compiler/selector';
|
import {CssSelector} from 'angular2/src/compiler/selector';
|
||||||
import {splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||||
|
import {getUrlScheme} from './url_resolver';
|
||||||
|
|
||||||
// group 1: "property" from "[property]"
|
// group 1: "property" from "[property]"
|
||||||
// group 2: "event" from "(event)"
|
// group 2: "event" from "(event)"
|
||||||
|
@ -50,46 +51,33 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier
|
||||||
name: string;
|
name: string;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
moduleUrl: string;
|
moduleUrl: string;
|
||||||
constConstructor: boolean;
|
|
||||||
value: any;
|
value: any;
|
||||||
|
|
||||||
constructor({runtime, name, moduleUrl, prefix, constConstructor, value}: {
|
constructor(
|
||||||
runtime?: any,
|
{runtime, name, moduleUrl, prefix, value}:
|
||||||
name?: string,
|
{runtime?: any, name?: string, moduleUrl?: string, prefix?: string, value?: any} = {}) {
|
||||||
moduleUrl?: string,
|
|
||||||
prefix?: string,
|
|
||||||
constConstructor?: boolean,
|
|
||||||
value?: any
|
|
||||||
} = {}) {
|
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
this.moduleUrl = moduleUrl;
|
this.moduleUrl = moduleUrl;
|
||||||
this.constConstructor = constConstructor;
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata {
|
static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata {
|
||||||
let value = isArray(data['value']) ? arrayFromJson(data['value'], metadataFromJson) :
|
let value = isArray(data['value']) ? _arrayFromJson(data['value'], metadataFromJson) :
|
||||||
objFromJson(data['value'], metadataFromJson);
|
_objFromJson(data['value'], metadataFromJson);
|
||||||
return new CompileIdentifierMetadata({
|
return new CompileIdentifierMetadata(
|
||||||
name: data['name'],
|
{name: data['name'], prefix: data['prefix'], moduleUrl: data['moduleUrl'], value: value});
|
||||||
prefix: data['prefix'],
|
|
||||||
moduleUrl: data['moduleUrl'],
|
|
||||||
constConstructor: data['constConstructor'],
|
|
||||||
value: value
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): {[key: string]: any} {
|
toJson(): {[key: string]: any} {
|
||||||
let value = isArray(this.value) ? arrayToJson(this.value) : objToJson(this.value);
|
let value = isArray(this.value) ? _arrayToJson(this.value) : _objToJson(this.value);
|
||||||
return {
|
return {
|
||||||
// Note: Runtime type can't be serialized...
|
// Note: Runtime type can't be serialized...
|
||||||
'class': 'Identifier',
|
'class': 'Identifier',
|
||||||
'name': this.name,
|
'name': this.name,
|
||||||
'moduleUrl': this.moduleUrl,
|
'moduleUrl': this.moduleUrl,
|
||||||
'prefix': this.prefix,
|
'prefix': this.prefix,
|
||||||
'constConstructor': this.constConstructor,
|
|
||||||
'value': value
|
'value': value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -103,72 +91,82 @@ export class CompileDiDependencyMetadata {
|
||||||
isHost: boolean;
|
isHost: boolean;
|
||||||
isSkipSelf: boolean;
|
isSkipSelf: boolean;
|
||||||
isOptional: boolean;
|
isOptional: boolean;
|
||||||
|
isValue: boolean;
|
||||||
query: CompileQueryMetadata;
|
query: CompileQueryMetadata;
|
||||||
viewQuery: CompileQueryMetadata;
|
viewQuery: CompileQueryMetadata;
|
||||||
token: CompileIdentifierMetadata | string;
|
token: CompileTokenMetadata;
|
||||||
|
value: any;
|
||||||
|
|
||||||
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, query, viewQuery, token}: {
|
constructor({isAttribute, isSelf, isHost, isSkipSelf, isOptional, isValue, query, viewQuery,
|
||||||
|
token, value}: {
|
||||||
isAttribute?: boolean,
|
isAttribute?: boolean,
|
||||||
isSelf?: boolean,
|
isSelf?: boolean,
|
||||||
isHost?: boolean,
|
isHost?: boolean,
|
||||||
isSkipSelf?: boolean,
|
isSkipSelf?: boolean,
|
||||||
isOptional?: boolean,
|
isOptional?: boolean,
|
||||||
|
isValue?: boolean,
|
||||||
query?: CompileQueryMetadata,
|
query?: CompileQueryMetadata,
|
||||||
viewQuery?: CompileQueryMetadata,
|
viewQuery?: CompileQueryMetadata,
|
||||||
token?: CompileIdentifierMetadata | string
|
token?: CompileTokenMetadata,
|
||||||
|
value?: any
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.isAttribute = normalizeBool(isAttribute);
|
this.isAttribute = normalizeBool(isAttribute);
|
||||||
this.isSelf = normalizeBool(isSelf);
|
this.isSelf = normalizeBool(isSelf);
|
||||||
this.isHost = normalizeBool(isHost);
|
this.isHost = normalizeBool(isHost);
|
||||||
this.isSkipSelf = normalizeBool(isSkipSelf);
|
this.isSkipSelf = normalizeBool(isSkipSelf);
|
||||||
this.isOptional = normalizeBool(isOptional);
|
this.isOptional = normalizeBool(isOptional);
|
||||||
|
this.isValue = normalizeBool(isValue);
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.viewQuery = viewQuery;
|
this.viewQuery = viewQuery;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: {[key: string]: any}): CompileDiDependencyMetadata {
|
static fromJson(data: {[key: string]: any}): CompileDiDependencyMetadata {
|
||||||
return new CompileDiDependencyMetadata({
|
return new CompileDiDependencyMetadata({
|
||||||
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson),
|
token: _objFromJson(data['token'], CompileTokenMetadata.fromJson),
|
||||||
query: objFromJson(data['query'], CompileQueryMetadata.fromJson),
|
query: _objFromJson(data['query'], CompileQueryMetadata.fromJson),
|
||||||
viewQuery: objFromJson(data['viewQuery'], CompileQueryMetadata.fromJson),
|
viewQuery: _objFromJson(data['viewQuery'], CompileQueryMetadata.fromJson),
|
||||||
|
value: data['value'],
|
||||||
isAttribute: data['isAttribute'],
|
isAttribute: data['isAttribute'],
|
||||||
isSelf: data['isSelf'],
|
isSelf: data['isSelf'],
|
||||||
isHost: data['isHost'],
|
isHost: data['isHost'],
|
||||||
isSkipSelf: data['isSkipSelf'],
|
isSkipSelf: data['isSkipSelf'],
|
||||||
isOptional: data['isOptional']
|
isOptional: data['isOptional'],
|
||||||
|
isValue: data['isValue']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson(): {[key: string]: any} {
|
toJson(): {[key: string]: any} {
|
||||||
return {
|
return {
|
||||||
// Note: Runtime type can't be serialized...
|
'token': _objToJson(this.token),
|
||||||
'token': objToJson(this.token),
|
'query': _objToJson(this.query),
|
||||||
'query': objToJson(this.query),
|
'viewQuery': _objToJson(this.viewQuery),
|
||||||
'viewQuery': objToJson(this.viewQuery),
|
'value': this.value,
|
||||||
'isAttribute': this.isAttribute,
|
'isAttribute': this.isAttribute,
|
||||||
'isSelf': this.isSelf,
|
'isSelf': this.isSelf,
|
||||||
'isHost': this.isHost,
|
'isHost': this.isHost,
|
||||||
'isSkipSelf': this.isSkipSelf,
|
'isSkipSelf': this.isSkipSelf,
|
||||||
'isOptional': this.isOptional
|
'isOptional': this.isOptional,
|
||||||
|
'isValue': this.isValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileProviderMetadata {
|
export class CompileProviderMetadata {
|
||||||
token: CompileIdentifierMetadata | string;
|
token: CompileTokenMetadata;
|
||||||
useClass: CompileTypeMetadata;
|
useClass: CompileTypeMetadata;
|
||||||
useValue: any;
|
useValue: any;
|
||||||
useExisting: CompileIdentifierMetadata | string;
|
useExisting: CompileTokenMetadata;
|
||||||
useFactory: CompileFactoryMetadata;
|
useFactory: CompileFactoryMetadata;
|
||||||
deps: CompileDiDependencyMetadata[];
|
deps: CompileDiDependencyMetadata[];
|
||||||
multi: boolean;
|
multi: boolean;
|
||||||
|
|
||||||
constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
|
constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: {
|
||||||
token?: CompileIdentifierMetadata | string,
|
token?: CompileTokenMetadata,
|
||||||
useClass?: CompileTypeMetadata,
|
useClass?: CompileTypeMetadata,
|
||||||
useValue?: any,
|
useValue?: any,
|
||||||
useExisting?: CompileIdentifierMetadata | string,
|
useExisting?: CompileTokenMetadata,
|
||||||
useFactory?: CompileFactoryMetadata,
|
useFactory?: CompileFactoryMetadata,
|
||||||
deps?: CompileDiDependencyMetadata[],
|
deps?: CompileDiDependencyMetadata[],
|
||||||
multi?: boolean
|
multi?: boolean
|
||||||
|
@ -178,17 +176,19 @@ export class CompileProviderMetadata {
|
||||||
this.useValue = useValue;
|
this.useValue = useValue;
|
||||||
this.useExisting = useExisting;
|
this.useExisting = useExisting;
|
||||||
this.useFactory = useFactory;
|
this.useFactory = useFactory;
|
||||||
this.deps = deps;
|
this.deps = normalizeBlank(deps);
|
||||||
this.multi = multi;
|
this.multi = normalizeBool(multi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: {[key: string]: any}): CompileProviderMetadata {
|
static fromJson(data: {[key: string]: any}): CompileProviderMetadata {
|
||||||
return new CompileProviderMetadata({
|
return new CompileProviderMetadata({
|
||||||
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson),
|
token: _objFromJson(data['token'], CompileTokenMetadata.fromJson),
|
||||||
useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson),
|
useClass: _objFromJson(data['useClass'], CompileTypeMetadata.fromJson),
|
||||||
useExisting: objFromJson(data['useExisting'], CompileIdentifierMetadata.fromJson),
|
useExisting: _objFromJson(data['useExisting'], CompileTokenMetadata.fromJson),
|
||||||
useValue: objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson),
|
useValue: _objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson),
|
||||||
useFactory: objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson)
|
useFactory: _objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson),
|
||||||
|
multi: data['multi'],
|
||||||
|
deps: _arrayFromJson(data['deps'], CompileDiDependencyMetadata.fromJson)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,11 +196,13 @@ export class CompileProviderMetadata {
|
||||||
return {
|
return {
|
||||||
// Note: Runtime type can't be serialized...
|
// Note: Runtime type can't be serialized...
|
||||||
'class': 'Provider',
|
'class': 'Provider',
|
||||||
'token': objToJson(this.token),
|
'token': _objToJson(this.token),
|
||||||
'useClass': objToJson(this.useClass),
|
'useClass': _objToJson(this.useClass),
|
||||||
'useExisting': objToJson(this.useExisting),
|
'useExisting': _objToJson(this.useExisting),
|
||||||
'useValue': objToJson(this.useValue),
|
'useValue': _objToJson(this.useValue),
|
||||||
'useFactory': objToJson(this.useFactory)
|
'useFactory': _objToJson(this.useFactory),
|
||||||
|
'multi': this.multi,
|
||||||
|
'deps': _arrayToJson(this.deps)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,16 +213,14 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata,
|
||||||
name: string;
|
name: string;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
moduleUrl: string;
|
moduleUrl: string;
|
||||||
constConstructor: boolean;
|
|
||||||
value: any;
|
value: any;
|
||||||
diDeps: CompileDiDependencyMetadata[];
|
diDeps: CompileDiDependencyMetadata[];
|
||||||
|
|
||||||
constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps, value}: {
|
constructor({runtime, name, moduleUrl, prefix, diDeps, value}: {
|
||||||
runtime?: Function,
|
runtime?: Function,
|
||||||
name?: string,
|
name?: string,
|
||||||
prefix?: string,
|
prefix?: string,
|
||||||
moduleUrl?: string,
|
moduleUrl?: string,
|
||||||
constConstructor?: boolean,
|
|
||||||
value?: boolean,
|
value?: boolean,
|
||||||
diDeps?: CompileDiDependencyMetadata[]
|
diDeps?: CompileDiDependencyMetadata[]
|
||||||
}) {
|
}) {
|
||||||
|
@ -228,8 +228,7 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata,
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
this.moduleUrl = moduleUrl;
|
this.moduleUrl = moduleUrl;
|
||||||
this.diDeps = diDeps;
|
this.diDeps = _normalizeArray(diDeps);
|
||||||
this.constConstructor = constConstructor;
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,9 +239,8 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata,
|
||||||
name: data['name'],
|
name: data['name'],
|
||||||
prefix: data['prefix'],
|
prefix: data['prefix'],
|
||||||
moduleUrl: data['moduleUrl'],
|
moduleUrl: data['moduleUrl'],
|
||||||
constConstructor: data['constConstructor'],
|
|
||||||
value: data['value'],
|
value: data['value'],
|
||||||
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
|
diDeps: _arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,13 +250,107 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata,
|
||||||
'name': this.name,
|
'name': this.name,
|
||||||
'prefix': this.prefix,
|
'prefix': this.prefix,
|
||||||
'moduleUrl': this.moduleUrl,
|
'moduleUrl': this.moduleUrl,
|
||||||
'constConstructor': this.constConstructor,
|
|
||||||
'value': this.value,
|
'value': this.value,
|
||||||
'diDeps': arrayToJson(this.diDeps)
|
'diDeps': _arrayToJson(this.diDeps)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CompileTokenMetadata implements CompileMetadataWithIdentifier {
|
||||||
|
value: any;
|
||||||
|
identifier: CompileIdentifierMetadata;
|
||||||
|
identifierIsInstance: boolean;
|
||||||
|
|
||||||
|
constructor({value, identifier, identifierIsInstance}: {
|
||||||
|
value?: any,
|
||||||
|
identifier?: CompileIdentifierMetadata,
|
||||||
|
identifierIsInstance?: boolean
|
||||||
|
}) {
|
||||||
|
this.value = value;
|
||||||
|
this.identifier = identifier;
|
||||||
|
this.identifierIsInstance = normalizeBool(identifierIsInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(data: {[key: string]: any}): CompileTokenMetadata {
|
||||||
|
return new CompileTokenMetadata({
|
||||||
|
value: data['value'],
|
||||||
|
identifier: _objFromJson(data['identifier'], CompileIdentifierMetadata.fromJson),
|
||||||
|
identifierIsInstance: data['identifierIsInstance']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson(): {[key: string]: any} {
|
||||||
|
return {
|
||||||
|
'value': this.value,
|
||||||
|
'identifier': _objToJson(this.identifier),
|
||||||
|
'identifierIsInstance': this.identifierIsInstance
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get runtimeCacheKey(): any {
|
||||||
|
if (isPresent(this.identifier)) {
|
||||||
|
return this.identifier.runtime;
|
||||||
|
} else {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get assetCacheKey(): any {
|
||||||
|
if (isPresent(this.identifier)) {
|
||||||
|
return isPresent(this.identifier.moduleUrl) &&
|
||||||
|
isPresent(getUrlScheme(this.identifier.moduleUrl)) ?
|
||||||
|
`${this.identifier.name}|${this.identifier.moduleUrl}|${this.identifierIsInstance}` :
|
||||||
|
null;
|
||||||
|
} else {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
equalsTo(token2: CompileTokenMetadata): boolean {
|
||||||
|
var rk = this.runtimeCacheKey;
|
||||||
|
var ak = this.assetCacheKey;
|
||||||
|
return (isPresent(rk) && rk == token2.runtimeCacheKey) ||
|
||||||
|
(isPresent(ak) && ak == token2.assetCacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string { return isPresent(this.value) ? this.value : this.identifier.name; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileTokenMap<VALUE> {
|
||||||
|
private _valueMap = new Map<any, VALUE>();
|
||||||
|
private _values: VALUE[] = [];
|
||||||
|
|
||||||
|
add(token: CompileTokenMetadata, value: VALUE) {
|
||||||
|
var existing = this.get(token);
|
||||||
|
if (isPresent(existing)) {
|
||||||
|
throw new BaseException(`Can only add to a TokenMap! Token: ${token.name}`);
|
||||||
|
}
|
||||||
|
this._values.push(value);
|
||||||
|
var rk = token.runtimeCacheKey;
|
||||||
|
if (isPresent(rk)) {
|
||||||
|
this._valueMap.set(rk, value);
|
||||||
|
}
|
||||||
|
var ak = token.assetCacheKey;
|
||||||
|
if (isPresent(ak)) {
|
||||||
|
this._valueMap.set(ak, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get(token: CompileTokenMetadata): VALUE {
|
||||||
|
var rk = token.runtimeCacheKey;
|
||||||
|
var ak = token.assetCacheKey;
|
||||||
|
var result;
|
||||||
|
if (isPresent(rk)) {
|
||||||
|
result = this._valueMap.get(rk);
|
||||||
|
}
|
||||||
|
if (isBlank(result) && isPresent(ak)) {
|
||||||
|
result = this._valueMap.get(ak);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
values(): VALUE[] { return this._values; }
|
||||||
|
get size(): number { return this._values.length; }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a type.
|
* Metadata regarding compilation of a type.
|
||||||
*/
|
*/
|
||||||
|
@ -268,17 +360,15 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||||
prefix: string;
|
prefix: string;
|
||||||
moduleUrl: string;
|
moduleUrl: string;
|
||||||
isHost: boolean;
|
isHost: boolean;
|
||||||
constConstructor: boolean;
|
|
||||||
value: any;
|
value: any;
|
||||||
diDeps: CompileDiDependencyMetadata[];
|
diDeps: CompileDiDependencyMetadata[];
|
||||||
|
|
||||||
constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, value, diDeps}: {
|
constructor({runtime, name, moduleUrl, prefix, isHost, value, diDeps}: {
|
||||||
runtime?: Type,
|
runtime?: Type,
|
||||||
name?: string,
|
name?: string,
|
||||||
moduleUrl?: string,
|
moduleUrl?: string,
|
||||||
prefix?: string,
|
prefix?: string,
|
||||||
isHost?: boolean,
|
isHost?: boolean,
|
||||||
constConstructor?: boolean,
|
|
||||||
value?: any,
|
value?: any,
|
||||||
diDeps?: CompileDiDependencyMetadata[]
|
diDeps?: CompileDiDependencyMetadata[]
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
@ -287,9 +377,8 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||||
this.moduleUrl = moduleUrl;
|
this.moduleUrl = moduleUrl;
|
||||||
this.prefix = prefix;
|
this.prefix = prefix;
|
||||||
this.isHost = normalizeBool(isHost);
|
this.isHost = normalizeBool(isHost);
|
||||||
this.constConstructor = constConstructor;
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.diDeps = normalizeBlank(diDeps);
|
this.diDeps = _normalizeArray(diDeps);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: {[key: string]: any}): CompileTypeMetadata {
|
static fromJson(data: {[key: string]: any}): CompileTypeMetadata {
|
||||||
|
@ -298,9 +387,8 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||||
moduleUrl: data['moduleUrl'],
|
moduleUrl: data['moduleUrl'],
|
||||||
prefix: data['prefix'],
|
prefix: data['prefix'],
|
||||||
isHost: data['isHost'],
|
isHost: data['isHost'],
|
||||||
constConstructor: data['constConstructor'],
|
|
||||||
value: data['value'],
|
value: data['value'],
|
||||||
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
|
diDeps: _arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,34 +403,33 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe
|
||||||
'moduleUrl': this.moduleUrl,
|
'moduleUrl': this.moduleUrl,
|
||||||
'prefix': this.prefix,
|
'prefix': this.prefix,
|
||||||
'isHost': this.isHost,
|
'isHost': this.isHost,
|
||||||
'constConstructor': this.constConstructor,
|
|
||||||
'value': this.value,
|
'value': this.value,
|
||||||
'diDeps': arrayToJson(this.diDeps)
|
'diDeps': _arrayToJson(this.diDeps)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CompileQueryMetadata {
|
export class CompileQueryMetadata {
|
||||||
selectors: Array<CompileIdentifierMetadata | string>;
|
selectors: Array<CompileTokenMetadata>;
|
||||||
descendants: boolean;
|
descendants: boolean;
|
||||||
first: boolean;
|
first: boolean;
|
||||||
propertyName: string;
|
propertyName: string;
|
||||||
|
|
||||||
constructor({selectors, descendants, first, propertyName}: {
|
constructor({selectors, descendants, first, propertyName}: {
|
||||||
selectors?: Array<CompileIdentifierMetadata | string>,
|
selectors?: Array<CompileTokenMetadata>,
|
||||||
descendants?: boolean,
|
descendants?: boolean,
|
||||||
first?: boolean,
|
first?: boolean,
|
||||||
propertyName?: string
|
propertyName?: string
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.selectors = selectors;
|
this.selectors = selectors;
|
||||||
this.descendants = descendants;
|
this.descendants = normalizeBool(descendants);
|
||||||
this.first = normalizeBool(first);
|
this.first = normalizeBool(first);
|
||||||
this.propertyName = propertyName;
|
this.propertyName = propertyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(data: {[key: string]: any}): CompileQueryMetadata {
|
static fromJson(data: {[key: string]: any}): CompileQueryMetadata {
|
||||||
return new CompileQueryMetadata({
|
return new CompileQueryMetadata({
|
||||||
selectors: arrayFromJson(data['selectors'], CompileIdentifierMetadata.fromJson),
|
selectors: _arrayFromJson(data['selectors'], CompileTokenMetadata.fromJson),
|
||||||
descendants: data['descendants'],
|
descendants: data['descendants'],
|
||||||
first: data['first'],
|
first: data['first'],
|
||||||
propertyName: data['propertyName']
|
propertyName: data['propertyName']
|
||||||
|
@ -351,8 +438,7 @@ export class CompileQueryMetadata {
|
||||||
|
|
||||||
toJson(): {[key: string]: any} {
|
toJson(): {[key: string]: any} {
|
||||||
return {
|
return {
|
||||||
// Note: Runtime type can't be serialized...
|
'selectors': _arrayToJson(this.selectors),
|
||||||
'selectors': arrayToJson(this.selectors),
|
|
||||||
'descendants': this.descendants,
|
'descendants': this.descendants,
|
||||||
'first': this.first,
|
'first': this.first,
|
||||||
'propertyName': this.propertyName
|
'propertyName': this.propertyName
|
||||||
|
@ -416,12 +502,10 @@ export class CompileTemplateMetadata {
|
||||||
* Metadata regarding compilation of a directive.
|
* Metadata regarding compilation of a directive.
|
||||||
*/
|
*/
|
||||||
export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
|
static create({type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
|
||||||
outputs, host, lifecycleHooks, providers, viewProviders, queries, viewQueries,
|
lifecycleHooks, providers, viewProviders, queries, viewQueries, template}: {
|
||||||
template}: {
|
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
dynamicLoadable?: boolean,
|
|
||||||
selector?: string,
|
selector?: string,
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
@ -474,7 +558,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
return new CompileDirectiveMetadata({
|
return new CompileDirectiveMetadata({
|
||||||
type: type,
|
type: type,
|
||||||
isComponent: normalizeBool(isComponent),
|
isComponent: normalizeBool(isComponent),
|
||||||
dynamicLoadable: normalizeBool(dynamicLoadable),
|
|
||||||
selector: selector,
|
selector: selector,
|
||||||
exportAs: exportAs,
|
exportAs: exportAs,
|
||||||
changeDetection: changeDetection,
|
changeDetection: changeDetection,
|
||||||
|
@ -493,7 +576,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
}
|
}
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
isComponent: boolean;
|
isComponent: boolean;
|
||||||
dynamicLoadable: boolean;
|
|
||||||
selector: string;
|
selector: string;
|
||||||
exportAs: string;
|
exportAs: string;
|
||||||
changeDetection: ChangeDetectionStrategy;
|
changeDetection: ChangeDetectionStrategy;
|
||||||
|
@ -503,17 +585,17 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
hostProperties: {[key: string]: string};
|
hostProperties: {[key: string]: string};
|
||||||
hostAttributes: {[key: string]: string};
|
hostAttributes: {[key: string]: string};
|
||||||
lifecycleHooks: LifecycleHooks[];
|
lifecycleHooks: LifecycleHooks[];
|
||||||
providers: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>;
|
providers: CompileProviderMetadata[];
|
||||||
viewProviders: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>;
|
viewProviders: CompileProviderMetadata[];
|
||||||
queries: CompileQueryMetadata[];
|
queries: CompileQueryMetadata[];
|
||||||
viewQueries: CompileQueryMetadata[];
|
viewQueries: CompileQueryMetadata[];
|
||||||
|
|
||||||
template: CompileTemplateMetadata;
|
template: CompileTemplateMetadata;
|
||||||
constructor({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
|
constructor({type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
|
||||||
outputs, hostListeners, hostProperties, hostAttributes, lifecycleHooks, providers,
|
hostListeners, hostProperties, hostAttributes, lifecycleHooks, providers,
|
||||||
viewProviders, queries, viewQueries, template}: {
|
viewProviders, queries, viewQueries, template}: {
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
isComponent?: boolean,
|
isComponent?: boolean,
|
||||||
dynamicLoadable?: boolean,
|
|
||||||
selector?: string,
|
selector?: string,
|
||||||
exportAs?: string,
|
exportAs?: string,
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
@ -533,7 +615,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.isComponent = isComponent;
|
this.isComponent = isComponent;
|
||||||
this.dynamicLoadable = dynamicLoadable;
|
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.exportAs = exportAs;
|
this.exportAs = exportAs;
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
|
@ -542,11 +623,11 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
this.hostListeners = hostListeners;
|
this.hostListeners = hostListeners;
|
||||||
this.hostProperties = hostProperties;
|
this.hostProperties = hostProperties;
|
||||||
this.hostAttributes = hostAttributes;
|
this.hostAttributes = hostAttributes;
|
||||||
this.lifecycleHooks = lifecycleHooks;
|
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
|
||||||
this.providers = normalizeBlank(providers);
|
this.providers = _normalizeArray(providers);
|
||||||
this.viewProviders = normalizeBlank(viewProviders);
|
this.viewProviders = _normalizeArray(viewProviders);
|
||||||
this.queries = normalizeBlank(queries);
|
this.queries = _normalizeArray(queries);
|
||||||
this.viewQueries = normalizeBlank(viewQueries);
|
this.viewQueries = _normalizeArray(viewQueries);
|
||||||
this.template = template;
|
this.template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,7 +636,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
static fromJson(data: {[key: string]: any}): CompileDirectiveMetadata {
|
static fromJson(data: {[key: string]: any}): CompileDirectiveMetadata {
|
||||||
return new CompileDirectiveMetadata({
|
return new CompileDirectiveMetadata({
|
||||||
isComponent: data['isComponent'],
|
isComponent: data['isComponent'],
|
||||||
dynamicLoadable: data['dynamicLoadable'],
|
|
||||||
selector: data['selector'],
|
selector: data['selector'],
|
||||||
exportAs: data['exportAs'],
|
exportAs: data['exportAs'],
|
||||||
type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
|
type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
|
||||||
|
@ -571,10 +651,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
(<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]),
|
(<any[]>data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]),
|
||||||
template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) :
|
template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) :
|
||||||
data['template'],
|
data['template'],
|
||||||
providers: arrayFromJson(data['providers'], metadataFromJson),
|
providers: _arrayFromJson(data['providers'], metadataFromJson),
|
||||||
viewProviders: arrayFromJson(data['viewProviders'], metadataFromJson),
|
viewProviders: _arrayFromJson(data['viewProviders'], metadataFromJson),
|
||||||
queries: arrayFromJson(data['queries'], CompileQueryMetadata.fromJson),
|
queries: _arrayFromJson(data['queries'], CompileQueryMetadata.fromJson),
|
||||||
viewQueries: arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson)
|
viewQueries: _arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,7 +662,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
return {
|
return {
|
||||||
'class': 'Directive',
|
'class': 'Directive',
|
||||||
'isComponent': this.isComponent,
|
'isComponent': this.isComponent,
|
||||||
'dynamicLoadable': this.dynamicLoadable,
|
|
||||||
'selector': this.selector,
|
'selector': this.selector,
|
||||||
'exportAs': this.exportAs,
|
'exportAs': this.exportAs,
|
||||||
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
'type': isPresent(this.type) ? this.type.toJson() : this.type,
|
||||||
|
@ -595,10 +674,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
'hostAttributes': this.hostAttributes,
|
'hostAttributes': this.hostAttributes,
|
||||||
'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)),
|
'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)),
|
||||||
'template': isPresent(this.template) ? this.template.toJson() : this.template,
|
'template': isPresent(this.template) ? this.template.toJson() : this.template,
|
||||||
'providers': arrayToJson(this.providers),
|
'providers': _arrayToJson(this.providers),
|
||||||
'viewProviders': arrayToJson(this.viewProviders),
|
'viewProviders': _arrayToJson(this.viewProviders),
|
||||||
'queries': arrayToJson(this.queries),
|
'queries': _arrayToJson(this.queries),
|
||||||
'viewQueries': arrayToJson(this.viewQueries)
|
'viewQueries': _arrayToJson(this.viewQueries)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,7 +691,7 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
||||||
return CompileDirectiveMetadata.create({
|
return CompileDirectiveMetadata.create({
|
||||||
type: new CompileTypeMetadata({
|
type: new CompileTypeMetadata({
|
||||||
runtime: Object,
|
runtime: Object,
|
||||||
name: `Host${componentType.name}`,
|
name: `${componentType.name}_Host`,
|
||||||
moduleUrl: componentType.moduleUrl,
|
moduleUrl: componentType.moduleUrl,
|
||||||
isHost: true
|
isHost: true
|
||||||
}),
|
}),
|
||||||
|
@ -624,7 +703,6 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
||||||
host: {},
|
host: {},
|
||||||
lifecycleHooks: [],
|
lifecycleHooks: [],
|
||||||
isComponent: true,
|
isComponent: true,
|
||||||
dynamicLoadable: false,
|
|
||||||
selector: '*',
|
selector: '*',
|
||||||
providers: [],
|
providers: [],
|
||||||
viewProviders: [],
|
viewProviders: [],
|
||||||
|
@ -638,11 +716,18 @@ export class CompilePipeMetadata implements CompileMetadataWithType {
|
||||||
type: CompileTypeMetadata;
|
type: CompileTypeMetadata;
|
||||||
name: string;
|
name: string;
|
||||||
pure: boolean;
|
pure: boolean;
|
||||||
constructor({type, name,
|
lifecycleHooks: LifecycleHooks[];
|
||||||
pure}: {type?: CompileTypeMetadata, name?: string, pure?: boolean} = {}) {
|
|
||||||
|
constructor({type, name, pure, lifecycleHooks}: {
|
||||||
|
type?: CompileTypeMetadata,
|
||||||
|
name?: string,
|
||||||
|
pure?: boolean,
|
||||||
|
lifecycleHooks?: LifecycleHooks[]
|
||||||
|
} = {}) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.pure = normalizeBool(pure);
|
this.pure = normalizeBool(pure);
|
||||||
|
this.lifecycleHooks = _normalizeArray(lifecycleHooks);
|
||||||
}
|
}
|
||||||
get identifier(): CompileIdentifierMetadata { return this.type; }
|
get identifier(): CompileIdentifierMetadata { return this.type; }
|
||||||
|
|
||||||
|
@ -673,22 +758,26 @@ var _COMPILE_METADATA_FROM_JSON = {
|
||||||
'Factory': CompileFactoryMetadata.fromJson
|
'Factory': CompileFactoryMetadata.fromJson
|
||||||
};
|
};
|
||||||
|
|
||||||
function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any {
|
function _arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any {
|
||||||
return isBlank(obj) ? null : obj.map(o => objFromJson(o, fn));
|
return isBlank(obj) ? null : obj.map(o => _objFromJson(o, fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
function arrayToJson(obj: any[]): string | {[key: string]: any} {
|
function _arrayToJson(obj: any[]): string | {[key: string]: any} {
|
||||||
return isBlank(obj) ? null : obj.map(objToJson);
|
return isBlank(obj) ? null : obj.map(_objToJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any {
|
function _objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any {
|
||||||
if (isArray(obj)) return arrayFromJson(obj, fn);
|
if (isArray(obj)) return _arrayFromJson(obj, fn);
|
||||||
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
|
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
|
||||||
return fn(obj);
|
return fn(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
function objToJson(obj: any): string | {[key: string]: any} {
|
function _objToJson(obj: any): string | {[key: string]: any} {
|
||||||
if (isArray(obj)) return arrayToJson(obj);
|
if (isArray(obj)) return _arrayToJson(obj);
|
||||||
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
|
if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj;
|
||||||
return obj.toJson();
|
return obj.toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _normalizeArray(obj: any[]): any[] {
|
||||||
|
return isPresent(obj) ? obj : [];
|
||||||
|
}
|
|
@ -1,35 +1,38 @@
|
||||||
import {RuntimeCompiler_} from "./runtime_compiler";
|
|
||||||
export {TemplateCompiler} from './template_compiler';
|
|
||||||
export {
|
|
||||||
CompileDirectiveMetadata,
|
|
||||||
CompileTypeMetadata,
|
|
||||||
CompileTemplateMetadata
|
|
||||||
} from './directive_metadata';
|
|
||||||
export {SourceModule, SourceWithImports} from './source_module';
|
|
||||||
export {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
export {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||||
export * from 'angular2/src/compiler/template_ast';
|
export * from 'angular2/src/compiler/template_ast';
|
||||||
export {TEMPLATE_TRANSFORMS} from 'angular2/src/compiler/template_parser';
|
export {TEMPLATE_TRANSFORMS} from 'angular2/src/compiler/template_parser';
|
||||||
|
export {CompilerConfig, RenderTypes} from './config';
|
||||||
|
export * from './compile_metadata';
|
||||||
|
export * from './offline_compiler';
|
||||||
|
export * from 'angular2/src/compiler/url_resolver';
|
||||||
|
export * from 'angular2/src/compiler/xhr';
|
||||||
|
|
||||||
|
export {ViewResolver} from './view_resolver';
|
||||||
|
export {DirectiveResolver} from './directive_resolver';
|
||||||
|
export {PipeResolver} from './pipe_resolver';
|
||||||
|
|
||||||
import {assertionsEnabled, Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
import {assertionsEnabled, Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {provide, Provider} from 'angular2/src/core/di';
|
import {provide, Provider} from 'angular2/src/core/di';
|
||||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
||||||
import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
|
import {DirectiveNormalizer} from 'angular2/src/compiler/directive_normalizer';
|
||||||
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
|
||||||
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
||||||
import {ViewCompiler} from 'angular2/src/compiler/view_compiler';
|
import {ViewCompiler} from 'angular2/src/compiler/view_compiler/view_compiler';
|
||||||
import {ProtoViewCompiler} from 'angular2/src/compiler/proto_view_compiler';
|
import {CompilerConfig} from './config';
|
||||||
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
|
|
||||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||||
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
|
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
|
||||||
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
|
import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/element_schema_registry';
|
||||||
import {DomElementSchemaRegistry} from 'angular2/src/compiler/schema/dom_element_schema_registry';
|
import {DomElementSchemaRegistry} from 'angular2/src/compiler/schema/dom_element_schema_registry';
|
||||||
import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from 'angular2/src/compiler/url_resolver';
|
import {UrlResolver, DEFAULT_PACKAGE_URL_PROVIDER} from 'angular2/src/compiler/url_resolver';
|
||||||
import {Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
|
import {Parser} from './expression_parser/parser';
|
||||||
|
import {Lexer} from './expression_parser/lexer';
|
||||||
|
import {ViewResolver} from './view_resolver';
|
||||||
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
|
import {PipeResolver} from './pipe_resolver';
|
||||||
|
|
||||||
function _createChangeDetectorGenConfig() {
|
function _createCompilerConfig() {
|
||||||
return new ChangeDetectorGenConfig(assertionsEnabled(), false, true);
|
return new CompilerConfig(assertionsEnabled(), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,18 +44,18 @@ export const COMPILER_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||||
Parser,
|
Parser,
|
||||||
HtmlParser,
|
HtmlParser,
|
||||||
TemplateParser,
|
TemplateParser,
|
||||||
TemplateNormalizer,
|
DirectiveNormalizer,
|
||||||
RuntimeMetadataResolver,
|
RuntimeMetadataResolver,
|
||||||
DEFAULT_PACKAGE_URL_PROVIDER,
|
DEFAULT_PACKAGE_URL_PROVIDER,
|
||||||
StyleCompiler,
|
StyleCompiler,
|
||||||
ProtoViewCompiler,
|
|
||||||
ViewCompiler,
|
ViewCompiler,
|
||||||
ChangeDetectionCompiler,
|
new Provider(CompilerConfig, {useFactory: _createCompilerConfig, deps: []}),
|
||||||
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
|
RuntimeCompiler,
|
||||||
TemplateCompiler,
|
|
||||||
new Provider(RuntimeCompiler, {useClass: RuntimeCompiler_}),
|
|
||||||
new Provider(Compiler, {useExisting: RuntimeCompiler}),
|
new Provider(Compiler, {useExisting: RuntimeCompiler}),
|
||||||
DomElementSchemaRegistry,
|
DomElementSchemaRegistry,
|
||||||
new Provider(ElementSchemaRegistry, {useExisting: DomElementSchemaRegistry}),
|
new Provider(ElementSchemaRegistry, {useExisting: DomElementSchemaRegistry}),
|
||||||
UrlResolver
|
UrlResolver,
|
||||||
|
ViewResolver,
|
||||||
|
DirectiveResolver,
|
||||||
|
PipeResolver
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
|
import {Identifiers} from './identifiers';
|
||||||
|
import {CompileIdentifierMetadata} from './compile_metadata';
|
||||||
|
|
||||||
|
export class CompilerConfig {
|
||||||
|
public renderTypes: RenderTypes;
|
||||||
|
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
|
||||||
|
public useJit: boolean, renderTypes: RenderTypes = null) {
|
||||||
|
if (isBlank(renderTypes)) {
|
||||||
|
renderTypes = new DefaultRenderTypes();
|
||||||
|
}
|
||||||
|
this.renderTypes = renderTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types used for the renderer.
|
||||||
|
* Can be replaced to specialize the generated output to a specific renderer
|
||||||
|
* to help tree shaking.
|
||||||
|
*/
|
||||||
|
export abstract class RenderTypes {
|
||||||
|
get renderer(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
|
get renderText(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
|
get renderElement(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
|
get renderComment(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
|
get renderNode(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
|
get renderEvent(): CompileIdentifierMetadata { return unimplemented(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DefaultRenderTypes implements RenderTypes {
|
||||||
|
renderer = Identifiers.Renderer;
|
||||||
|
renderText = null;
|
||||||
|
renderElement = null;
|
||||||
|
renderComment = null;
|
||||||
|
renderNode = null;
|
||||||
|
renderEvent = null;
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
library angular2.src.core.compiler.directive_lifecycle_reflector;
|
library angular2.src.core.compiler.directive_lifecycle_reflector;
|
||||||
|
|
||||||
import 'package:angular2/src/core/reflection/reflection.dart';
|
import 'package:angular2/src/core/reflection/reflection.dart';
|
||||||
import 'package:angular2/src/core/linker/interfaces.dart';
|
import 'package:angular2/src/core/metadata/lifecycle_hooks.dart';
|
||||||
|
|
||||||
const INTERFACES = const {
|
const INTERFACES = const {
|
||||||
LifecycleHooks.OnInit: OnInit,
|
LifecycleHooks.OnInit: OnInit,
|
|
@ -1,5 +1,5 @@
|
||||||
import {Type} from 'angular2/src/facade/lang';
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
import {LifecycleHooks} from './interfaces';
|
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||||
|
|
||||||
export function hasLifecycleHook(lcInterface: LifecycleHooks, token): boolean {
|
export function hasLifecycleHook(lcInterface: LifecycleHooks, token): boolean {
|
||||||
if (!(token instanceof Type)) return false;
|
if (!(token instanceof Type)) return false;
|
|
@ -1,9 +1,11 @@
|
||||||
import {
|
import {
|
||||||
CompileTypeMetadata,
|
CompileTypeMetadata,
|
||||||
CompileDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
CompileTemplateMetadata
|
CompileTemplateMetadata,
|
||||||
} from './directive_metadata';
|
CompileProviderMetadata,
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
CompileTokenMetadata
|
||||||
|
} from './compile_metadata';
|
||||||
|
import {isPresent, isBlank, isArray} from 'angular2/src/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
|
@ -28,10 +30,36 @@ import {HtmlParser} from './html_parser';
|
||||||
import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser';
|
import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TemplateNormalizer {
|
export class DirectiveNormalizer {
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
constructor(private _xhr: XHR, private _urlResolver: UrlResolver,
|
||||||
private _htmlParser: HtmlParser) {}
|
private _htmlParser: HtmlParser) {}
|
||||||
|
|
||||||
|
normalizeDirective(directive: CompileDirectiveMetadata): Promise<CompileDirectiveMetadata> {
|
||||||
|
if (!directive.isComponent) {
|
||||||
|
// For non components there is nothing to be normalized yet.
|
||||||
|
return PromiseWrapper.resolve(directive);
|
||||||
|
}
|
||||||
|
return this.normalizeTemplate(directive.type, directive.template)
|
||||||
|
.then((normalizedTemplate: CompileTemplateMetadata) => new CompileDirectiveMetadata({
|
||||||
|
type: directive.type,
|
||||||
|
isComponent: directive.isComponent,
|
||||||
|
selector: directive.selector,
|
||||||
|
exportAs: directive.exportAs,
|
||||||
|
changeDetection: directive.changeDetection,
|
||||||
|
inputs: directive.inputs,
|
||||||
|
outputs: directive.outputs,
|
||||||
|
hostListeners: directive.hostListeners,
|
||||||
|
hostProperties: directive.hostProperties,
|
||||||
|
hostAttributes: directive.hostAttributes,
|
||||||
|
lifecycleHooks: directive.lifecycleHooks,
|
||||||
|
providers: directive.providers,
|
||||||
|
viewProviders: directive.viewProviders,
|
||||||
|
queries: directive.queries,
|
||||||
|
viewQueries: directive.viewQueries,
|
||||||
|
template: normalizedTemplate
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
normalizeTemplate(directiveType: CompileTypeMetadata,
|
normalizeTemplate(directiveType: CompileTypeMetadata,
|
||||||
template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> {
|
template: CompileTemplateMetadata): Promise<CompileTemplateMetadata> {
|
||||||
if (isPresent(template.template)) {
|
if (isPresent(template.template)) {
|
|
@ -0,0 +1,348 @@
|
||||||
|
import {ListWrapper} from "angular2/src/facade/collection";
|
||||||
|
|
||||||
|
export class AST {
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any { return null; }
|
||||||
|
toString(): string { return "AST"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a quoted expression of the form:
|
||||||
|
*
|
||||||
|
* quote = prefix `:` uninterpretedExpression
|
||||||
|
* prefix = identifier
|
||||||
|
* uninterpretedExpression = arbitrary string
|
||||||
|
*
|
||||||
|
* A quoted expression is meant to be pre-processed by an AST transformer that
|
||||||
|
* converts it into another AST that no longer contains quoted expressions.
|
||||||
|
* It is meant to allow third-party developers to extend Angular template
|
||||||
|
* expression language. The `uninterpretedExpression` part of the quote is
|
||||||
|
* therefore not interpreted by the Angular's own expression parser.
|
||||||
|
*/
|
||||||
|
export class Quote extends AST {
|
||||||
|
constructor(public prefix: string, public uninterpretedExpression: string, public location: any) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitQuote(this, context); }
|
||||||
|
toString(): string { return "Quote"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmptyExpr extends AST {
|
||||||
|
visit(visitor: AstVisitor, context: any = null) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ImplicitReceiver extends AST {
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitImplicitReceiver(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple expressions separated by a semicolon.
|
||||||
|
*/
|
||||||
|
export class Chain extends AST {
|
||||||
|
constructor(public expressions: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitChain(this, context); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Conditional extends AST {
|
||||||
|
constructor(public condition: AST, public trueExp: AST, public falseExp: AST) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitConditional(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PropertyRead extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitPropertyRead(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PropertyWrite extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string, public value: AST) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitPropertyWrite(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SafePropertyRead extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitSafePropertyRead(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KeyedRead extends AST {
|
||||||
|
constructor(public obj: AST, public key: AST) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitKeyedRead(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class KeyedWrite extends AST {
|
||||||
|
constructor(public obj: AST, public key: AST, public value: AST) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitKeyedWrite(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BindingPipe extends AST {
|
||||||
|
constructor(public exp: AST, public name: string, public args: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any { return visitor.visitPipe(this, context); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LiteralPrimitive extends AST {
|
||||||
|
constructor(public value) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitLiteralPrimitive(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LiteralArray extends AST {
|
||||||
|
constructor(public expressions: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitLiteralArray(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LiteralMap extends AST {
|
||||||
|
constructor(public keys: any[], public values: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitLiteralMap(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Interpolation extends AST {
|
||||||
|
constructor(public strings: any[], public expressions: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitInterpolation(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Binary extends AST {
|
||||||
|
constructor(public operation: string, public left: AST, public right: AST) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitBinary(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PrefixNot extends AST {
|
||||||
|
constructor(public expression: AST) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitPrefixNot(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MethodCall extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string, public args: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitMethodCall(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SafeMethodCall extends AST {
|
||||||
|
constructor(public receiver: AST, public name: string, public args: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitSafeMethodCall(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FunctionCall extends AST {
|
||||||
|
constructor(public target: AST, public args: any[]) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
return visitor.visitFunctionCall(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ASTWithSource extends AST {
|
||||||
|
constructor(public ast: AST, public source: string, public location: string) { super(); }
|
||||||
|
visit(visitor: AstVisitor, context: any = null): any { return this.ast.visit(visitor, context); }
|
||||||
|
toString(): string { return `${this.source} in ${this.location}`; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TemplateBinding {
|
||||||
|
constructor(public key: string, public keyIsVar: boolean, public name: string,
|
||||||
|
public expression: ASTWithSource) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AstVisitor {
|
||||||
|
visitBinary(ast: Binary, context: any): any;
|
||||||
|
visitChain(ast: Chain, context: any): any;
|
||||||
|
visitConditional(ast: Conditional, context: any): any;
|
||||||
|
visitFunctionCall(ast: FunctionCall, context: any): any;
|
||||||
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any): any;
|
||||||
|
visitInterpolation(ast: Interpolation, context: any): any;
|
||||||
|
visitKeyedRead(ast: KeyedRead, context: any): any;
|
||||||
|
visitKeyedWrite(ast: KeyedWrite, context: any): any;
|
||||||
|
visitLiteralArray(ast: LiteralArray, context: any): any;
|
||||||
|
visitLiteralMap(ast: LiteralMap, context: any): any;
|
||||||
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any;
|
||||||
|
visitMethodCall(ast: MethodCall, context: any): any;
|
||||||
|
visitPipe(ast: BindingPipe, context: any): any;
|
||||||
|
visitPrefixNot(ast: PrefixNot, context: any): any;
|
||||||
|
visitPropertyRead(ast: PropertyRead, context: any): any;
|
||||||
|
visitPropertyWrite(ast: PropertyWrite, context: any): any;
|
||||||
|
visitQuote(ast: Quote, context: any): any;
|
||||||
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): any;
|
||||||
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RecursiveAstVisitor implements AstVisitor {
|
||||||
|
visitBinary(ast: Binary, context: any): any {
|
||||||
|
ast.left.visit(this);
|
||||||
|
ast.right.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitChain(ast: Chain, context: any): any { return this.visitAll(ast.expressions, context); }
|
||||||
|
visitConditional(ast: Conditional, context: any): any {
|
||||||
|
ast.condition.visit(this);
|
||||||
|
ast.trueExp.visit(this);
|
||||||
|
ast.falseExp.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitPipe(ast: BindingPipe, context: any): any {
|
||||||
|
ast.exp.visit(this);
|
||||||
|
this.visitAll(ast.args, context);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitFunctionCall(ast: FunctionCall, context: any): any {
|
||||||
|
ast.target.visit(this);
|
||||||
|
this.visitAll(ast.args, context);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any): any { return null; }
|
||||||
|
visitInterpolation(ast: Interpolation, context: any): any {
|
||||||
|
return this.visitAll(ast.expressions, context);
|
||||||
|
}
|
||||||
|
visitKeyedRead(ast: KeyedRead, context: any): any {
|
||||||
|
ast.obj.visit(this);
|
||||||
|
ast.key.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitKeyedWrite(ast: KeyedWrite, context: any): any {
|
||||||
|
ast.obj.visit(this);
|
||||||
|
ast.key.visit(this);
|
||||||
|
ast.value.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitLiteralArray(ast: LiteralArray, context: any): any {
|
||||||
|
return this.visitAll(ast.expressions, context);
|
||||||
|
}
|
||||||
|
visitLiteralMap(ast: LiteralMap, context: any): any { return this.visitAll(ast.values, context); }
|
||||||
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): any { return null; }
|
||||||
|
visitMethodCall(ast: MethodCall, context: any): any {
|
||||||
|
ast.receiver.visit(this);
|
||||||
|
return this.visitAll(ast.args, context);
|
||||||
|
}
|
||||||
|
visitPrefixNot(ast: PrefixNot, context: any): any {
|
||||||
|
ast.expression.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitPropertyRead(ast: PropertyRead, context: any): any {
|
||||||
|
ast.receiver.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitPropertyWrite(ast: PropertyWrite, context: any): any {
|
||||||
|
ast.receiver.visit(this);
|
||||||
|
ast.value.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): any {
|
||||||
|
ast.receiver.visit(this);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): any {
|
||||||
|
ast.receiver.visit(this);
|
||||||
|
return this.visitAll(ast.args, context);
|
||||||
|
}
|
||||||
|
visitAll(asts: AST[], context: any): any {
|
||||||
|
asts.forEach(ast => ast.visit(this, context));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitQuote(ast: Quote, context: any): any { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AstTransformer implements AstVisitor {
|
||||||
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any): AST { return ast; }
|
||||||
|
|
||||||
|
visitInterpolation(ast: Interpolation, context: any): AST {
|
||||||
|
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any): AST {
|
||||||
|
return new LiteralPrimitive(ast.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPropertyRead(ast: PropertyRead, context: any): AST {
|
||||||
|
return new PropertyRead(ast.receiver.visit(this), ast.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPropertyWrite(ast: PropertyWrite, context: any): AST {
|
||||||
|
return new PropertyWrite(ast.receiver.visit(this), ast.name, ast.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): AST {
|
||||||
|
return new SafePropertyRead(ast.receiver.visit(this), ast.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitMethodCall(ast: MethodCall, context: any): AST {
|
||||||
|
return new MethodCall(ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): AST {
|
||||||
|
return new SafeMethodCall(ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFunctionCall(ast: FunctionCall, context: any): AST {
|
||||||
|
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralArray(ast: LiteralArray, context: any): AST {
|
||||||
|
return new LiteralArray(this.visitAll(ast.expressions));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteralMap(ast: LiteralMap, context: any): AST {
|
||||||
|
return new LiteralMap(ast.keys, this.visitAll(ast.values));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBinary(ast: Binary, context: any): AST {
|
||||||
|
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPrefixNot(ast: PrefixNot, context: any): AST {
|
||||||
|
return new PrefixNot(ast.expression.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitConditional(ast: Conditional, context: any): AST {
|
||||||
|
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
|
||||||
|
ast.falseExp.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitPipe(ast: BindingPipe, context: any): AST {
|
||||||
|
return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitKeyedRead(ast: KeyedRead, context: any): AST {
|
||||||
|
return new KeyedRead(ast.obj.visit(this), ast.key.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitKeyedWrite(ast: KeyedWrite, context: any): AST {
|
||||||
|
return new KeyedWrite(ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAll(asts: any[]): any[] {
|
||||||
|
var res = ListWrapper.createFixedSize(asts.length);
|
||||||
|
for (var i = 0; i < asts.length; ++i) {
|
||||||
|
res[i] = asts[i].visit(this);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitChain(ast: Chain, context: any): AST { return new Chain(this.visitAll(ast.expressions)); }
|
||||||
|
|
||||||
|
visitQuote(ast: Quote, context: any): AST {
|
||||||
|
return new Quote(ast.prefix, ast.uninterpretedExpression, ast.location);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ import {
|
||||||
$LPAREN,
|
$LPAREN,
|
||||||
$RPAREN
|
$RPAREN
|
||||||
} from './lexer';
|
} from './lexer';
|
||||||
import {reflector, Reflector} from 'angular2/src/core/reflection/reflection';
|
|
||||||
import {
|
import {
|
||||||
AST,
|
AST,
|
||||||
EmptyExpr,
|
EmptyExpr,
|
||||||
|
@ -64,18 +63,13 @@ export class SplitInterpolation {
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Parser {
|
export class Parser {
|
||||||
/** @internal */
|
|
||||||
_reflector: Reflector;
|
|
||||||
|
|
||||||
constructor(/** @internal */
|
constructor(/** @internal */
|
||||||
public _lexer: Lexer, providedReflector: Reflector = null) {
|
public _lexer: Lexer) {}
|
||||||
this._reflector = isPresent(providedReflector) ? providedReflector : reflector;
|
|
||||||
}
|
|
||||||
|
|
||||||
parseAction(input: string, location: any): ASTWithSource {
|
parseAction(input: string, location: any): ASTWithSource {
|
||||||
this._checkNoInterpolation(input, location);
|
this._checkNoInterpolation(input, location);
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(input));
|
var tokens = this._lexer.tokenize(this._stripComments(input));
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, true).parseChain();
|
var ast = new _ParseAST(input, location, tokens, true).parseChain();
|
||||||
return new ASTWithSource(ast, input, location);
|
return new ASTWithSource(ast, input, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +98,7 @@ export class Parser {
|
||||||
|
|
||||||
this._checkNoInterpolation(input, location);
|
this._checkNoInterpolation(input, location);
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(input));
|
var tokens = this._lexer.tokenize(this._stripComments(input));
|
||||||
return new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
return new _ParseAST(input, location, tokens, false).parseChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseQuote(input: string, location: any): AST {
|
private _parseQuote(input: string, location: any): AST {
|
||||||
|
@ -119,7 +113,7 @@ export class Parser {
|
||||||
|
|
||||||
parseTemplateBindings(input: string, location: any): TemplateBinding[] {
|
parseTemplateBindings(input: string, location: any): TemplateBinding[] {
|
||||||
var tokens = this._lexer.tokenize(input);
|
var tokens = this._lexer.tokenize(input);
|
||||||
return new _ParseAST(input, location, tokens, this._reflector, false).parseTemplateBindings();
|
return new _ParseAST(input, location, tokens, false).parseTemplateBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseInterpolation(input: string, location: any): ASTWithSource {
|
parseInterpolation(input: string, location: any): ASTWithSource {
|
||||||
|
@ -130,7 +124,7 @@ export class Parser {
|
||||||
|
|
||||||
for (let i = 0; i < split.expressions.length; ++i) {
|
for (let i = 0; i < split.expressions.length; ++i) {
|
||||||
var tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
var tokens = this._lexer.tokenize(this._stripComments(split.expressions[i]));
|
||||||
var ast = new _ParseAST(input, location, tokens, this._reflector, false).parseChain();
|
var ast = new _ParseAST(input, location, tokens, false).parseChain();
|
||||||
expressions.push(ast);
|
expressions.push(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +185,7 @@ export class Parser {
|
||||||
export class _ParseAST {
|
export class _ParseAST {
|
||||||
index: number = 0;
|
index: number = 0;
|
||||||
constructor(public input: string, public location: any, public tokens: any[],
|
constructor(public input: string, public location: any, public tokens: any[],
|
||||||
public reflector: Reflector, public parseAction: boolean) {}
|
public parseAction: boolean) {}
|
||||||
|
|
||||||
peek(offset: number): Token {
|
peek(offset: number): Token {
|
||||||
var i = this.index + offset;
|
var i = this.index + offset;
|
||||||
|
@ -531,16 +525,14 @@ export class _ParseAST {
|
||||||
if (this.optionalCharacter($LPAREN)) {
|
if (this.optionalCharacter($LPAREN)) {
|
||||||
let args = this.parseCallArguments();
|
let args = this.parseCallArguments();
|
||||||
this.expectCharacter($RPAREN);
|
this.expectCharacter($RPAREN);
|
||||||
let fn = this.reflector.method(id);
|
return isSafe ? new SafeMethodCall(receiver, id, args) : new MethodCall(receiver, id, args);
|
||||||
return isSafe ? new SafeMethodCall(receiver, id, fn, args) :
|
|
||||||
new MethodCall(receiver, id, fn, args);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (isSafe) {
|
if (isSafe) {
|
||||||
if (this.optionalOperator("=")) {
|
if (this.optionalOperator("=")) {
|
||||||
this.error("The '?.' operator cannot be used in the assignment");
|
this.error("The '?.' operator cannot be used in the assignment");
|
||||||
} else {
|
} else {
|
||||||
return new SafePropertyRead(receiver, id, this.reflector.getter(id));
|
return new SafePropertyRead(receiver, id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.optionalOperator("=")) {
|
if (this.optionalOperator("=")) {
|
||||||
|
@ -549,9 +541,9 @@ export class _ParseAST {
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = this.parseConditional();
|
let value = this.parseConditional();
|
||||||
return new PropertyWrite(receiver, id, this.reflector.setter(id), value);
|
return new PropertyWrite(receiver, id, value);
|
||||||
} else {
|
} else {
|
||||||
return new PropertyRead(receiver, id, this.reflector.getter(id));
|
return new PropertyRead(receiver, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -661,39 +653,39 @@ class SimpleExpressionChecker implements AstVisitor {
|
||||||
|
|
||||||
simple = true;
|
simple = true;
|
||||||
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver) {}
|
visitImplicitReceiver(ast: ImplicitReceiver, context: any) {}
|
||||||
|
|
||||||
visitInterpolation(ast: Interpolation) { this.simple = false; }
|
visitInterpolation(ast: Interpolation, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive) {}
|
visitLiteralPrimitive(ast: LiteralPrimitive, context: any) {}
|
||||||
|
|
||||||
visitPropertyRead(ast: PropertyRead) {}
|
visitPropertyRead(ast: PropertyRead, context: any) {}
|
||||||
|
|
||||||
visitPropertyWrite(ast: PropertyWrite) { this.simple = false; }
|
visitPropertyWrite(ast: PropertyWrite, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitSafePropertyRead(ast: SafePropertyRead) { this.simple = false; }
|
visitSafePropertyRead(ast: SafePropertyRead, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitMethodCall(ast: MethodCall) { this.simple = false; }
|
visitMethodCall(ast: MethodCall, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitSafeMethodCall(ast: SafeMethodCall) { this.simple = false; }
|
visitSafeMethodCall(ast: SafeMethodCall, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitFunctionCall(ast: FunctionCall) { this.simple = false; }
|
visitFunctionCall(ast: FunctionCall, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitLiteralArray(ast: LiteralArray) { this.visitAll(ast.expressions); }
|
visitLiteralArray(ast: LiteralArray, context: any) { this.visitAll(ast.expressions); }
|
||||||
|
|
||||||
visitLiteralMap(ast: LiteralMap) { this.visitAll(ast.values); }
|
visitLiteralMap(ast: LiteralMap, context: any) { this.visitAll(ast.values); }
|
||||||
|
|
||||||
visitBinary(ast: Binary) { this.simple = false; }
|
visitBinary(ast: Binary, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitPrefixNot(ast: PrefixNot) { this.simple = false; }
|
visitPrefixNot(ast: PrefixNot, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitConditional(ast: Conditional) { this.simple = false; }
|
visitConditional(ast: Conditional, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitPipe(ast: BindingPipe) { this.simple = false; }
|
visitPipe(ast: BindingPipe, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitKeyedRead(ast: KeyedRead) { this.simple = false; }
|
visitKeyedRead(ast: KeyedRead, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitKeyedWrite(ast: KeyedWrite) { this.simple = false; }
|
visitKeyedWrite(ast: KeyedWrite, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitAll(asts: any[]): any[] {
|
visitAll(asts: any[]): any[] {
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
var res = ListWrapper.createFixedSize(asts.length);
|
||||||
|
@ -703,7 +695,7 @@ class SimpleExpressionChecker implements AstVisitor {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
visitChain(ast: Chain) { this.simple = false; }
|
visitChain(ast: Chain, context: any) { this.simple = false; }
|
||||||
|
|
||||||
visitQuote(ast: Quote) { this.simple = false; }
|
visitQuote(ast: Quote, context: any) { this.simple = false; }
|
||||||
}
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
|
||||||
|
import {AppView} from 'angular2/src/core/linker/view';
|
||||||
|
import {StaticNodeDebugInfo, DebugContext} from 'angular2/src/core/linker/debug_context';
|
||||||
|
import {
|
||||||
|
flattenNestedViewRenderNodes,
|
||||||
|
interpolate,
|
||||||
|
checkBinding
|
||||||
|
} from 'angular2/src/core/linker/view_utils';
|
||||||
|
import {
|
||||||
|
uninitialized,
|
||||||
|
devModeEqual,
|
||||||
|
SimpleChange,
|
||||||
|
ValueUnwrapper,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
ChangeDetectorState,
|
||||||
|
ChangeDetectionStrategy
|
||||||
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
import {AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
||||||
|
import {AppElement} from 'angular2/src/core/linker/element';
|
||||||
|
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||||
|
import {ViewContainerRef} from 'angular2/src/core/linker/view_container_ref';
|
||||||
|
import {Renderer, RenderComponentType, RenderDebugInfo} from 'angular2/src/core/render/api';
|
||||||
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||||
|
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||||
|
import {QueryList} from 'angular2/src/core/linker';
|
||||||
|
import {Injector} from 'angular2/src/core/di/injector';
|
||||||
|
import {TemplateRef, TemplateRef_} from 'angular2/src/core/linker/template_ref';
|
||||||
|
import {MODULE_SUFFIX} from './util';
|
||||||
|
|
||||||
|
var APP_VIEW_MODULE_URL = 'asset:angular2/lib/src/core/linker/view' + MODULE_SUFFIX;
|
||||||
|
var VIEW_UTILS_MODULE_URL = 'asset:angular2/lib/src/core/linker/view_utils' + MODULE_SUFFIX;
|
||||||
|
var CD_MODULE_URL = 'asset:angular2/lib/src/core/change_detection/change_detection' + MODULE_SUFFIX;
|
||||||
|
|
||||||
|
// Reassign the imports to different variables so we can
|
||||||
|
// define static variables with the name of the import.
|
||||||
|
// (only needed for Dart).
|
||||||
|
var impAppViewManager_ = AppViewManager_;
|
||||||
|
var impAppView = AppView;
|
||||||
|
var impDebugContext = DebugContext;
|
||||||
|
var impAppElement = AppElement;
|
||||||
|
var impElementRef = ElementRef;
|
||||||
|
var impViewContainerRef = ViewContainerRef;
|
||||||
|
var impChangeDetectorRef = ChangeDetectorRef;
|
||||||
|
var impRenderComponentType = RenderComponentType;
|
||||||
|
var impQueryList = QueryList;
|
||||||
|
var impTemplateRef = TemplateRef;
|
||||||
|
var impTemplateRef_ = TemplateRef_;
|
||||||
|
var impValueUnwrapper = ValueUnwrapper;
|
||||||
|
var impInjector = Injector;
|
||||||
|
var impViewEncapsulation = ViewEncapsulation;
|
||||||
|
var impViewType = ViewType;
|
||||||
|
var impChangeDetectionStrategy = ChangeDetectionStrategy;
|
||||||
|
var impStaticNodeDebugInfo = StaticNodeDebugInfo;
|
||||||
|
var impRenderer = Renderer;
|
||||||
|
var impSimpleChange = SimpleChange;
|
||||||
|
var impUninitialized = uninitialized;
|
||||||
|
var impChangeDetectorState = ChangeDetectorState;
|
||||||
|
var impFlattenNestedViewRenderNodes = flattenNestedViewRenderNodes;
|
||||||
|
var impDevModeEqual = devModeEqual;
|
||||||
|
var impInterpolate = interpolate;
|
||||||
|
var impCheckBinding = checkBinding;
|
||||||
|
|
||||||
|
export class Identifiers {
|
||||||
|
static AppViewManager_ = new CompileIdentifierMetadata({
|
||||||
|
name: 'AppViewManager_',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/view_manager' + MODULE_SUFFIX,
|
||||||
|
runtime: impAppViewManager_
|
||||||
|
});
|
||||||
|
static AppView = new CompileIdentifierMetadata(
|
||||||
|
{name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: impAppView});
|
||||||
|
static AppElement = new CompileIdentifierMetadata({
|
||||||
|
name: 'AppElement',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/element' + MODULE_SUFFIX,
|
||||||
|
runtime: impAppElement
|
||||||
|
});
|
||||||
|
static ElementRef = new CompileIdentifierMetadata({
|
||||||
|
name: 'ElementRef',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/element_ref' + MODULE_SUFFIX,
|
||||||
|
runtime: impElementRef
|
||||||
|
});
|
||||||
|
static ViewContainerRef = new CompileIdentifierMetadata({
|
||||||
|
name: 'ViewContainerRef',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/view_container_ref' + MODULE_SUFFIX,
|
||||||
|
runtime: impViewContainerRef
|
||||||
|
});
|
||||||
|
static ChangeDetectorRef = new CompileIdentifierMetadata({
|
||||||
|
name: 'ChangeDetectorRef',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/change_detection/change_detector_ref' + MODULE_SUFFIX,
|
||||||
|
runtime: impChangeDetectorRef
|
||||||
|
});
|
||||||
|
static RenderComponentType = new CompileIdentifierMetadata({
|
||||||
|
name: 'RenderComponentType',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/render/api' + MODULE_SUFFIX,
|
||||||
|
runtime: impRenderComponentType
|
||||||
|
});
|
||||||
|
static QueryList = new CompileIdentifierMetadata({
|
||||||
|
name: 'QueryList',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/query_list' + MODULE_SUFFIX,
|
||||||
|
runtime: impQueryList
|
||||||
|
});
|
||||||
|
static TemplateRef = new CompileIdentifierMetadata({
|
||||||
|
name: 'TemplateRef',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/template_ref' + MODULE_SUFFIX,
|
||||||
|
runtime: impTemplateRef
|
||||||
|
});
|
||||||
|
static TemplateRef_ = new CompileIdentifierMetadata({
|
||||||
|
name: 'TemplateRef_',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/linker/template_ref' + MODULE_SUFFIX,
|
||||||
|
runtime: impTemplateRef_
|
||||||
|
});
|
||||||
|
static ValueUnwrapper = new CompileIdentifierMetadata(
|
||||||
|
{name: 'ValueUnwrapper', moduleUrl: CD_MODULE_URL, runtime: impValueUnwrapper});
|
||||||
|
static Injector = new CompileIdentifierMetadata({
|
||||||
|
name: 'Injector',
|
||||||
|
moduleUrl: `asset:angular2/lib/src/core/di/injector${MODULE_SUFFIX}`,
|
||||||
|
runtime: impInjector
|
||||||
|
});
|
||||||
|
static ViewEncapsulation = new CompileIdentifierMetadata({
|
||||||
|
name: 'ViewEncapsulation',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/metadata/view' + MODULE_SUFFIX,
|
||||||
|
runtime: impViewEncapsulation
|
||||||
|
});
|
||||||
|
static ViewType = new CompileIdentifierMetadata({
|
||||||
|
name: 'ViewType',
|
||||||
|
moduleUrl: `asset:angular2/lib/src/core/linker/view_type${MODULE_SUFFIX}`,
|
||||||
|
runtime: impViewType
|
||||||
|
});
|
||||||
|
static ChangeDetectionStrategy = new CompileIdentifierMetadata({
|
||||||
|
name: 'ChangeDetectionStrategy',
|
||||||
|
moduleUrl: CD_MODULE_URL,
|
||||||
|
runtime: impChangeDetectionStrategy
|
||||||
|
});
|
||||||
|
static StaticNodeDebugInfo = new CompileIdentifierMetadata({
|
||||||
|
name: 'StaticNodeDebugInfo',
|
||||||
|
moduleUrl: `asset:angular2/lib/src/core/linker/debug_context${MODULE_SUFFIX}`,
|
||||||
|
runtime: impStaticNodeDebugInfo
|
||||||
|
});
|
||||||
|
static DebugContext = new CompileIdentifierMetadata({
|
||||||
|
name: 'DebugContext',
|
||||||
|
moduleUrl: `asset:angular2/lib/src/core/linker/debug_context${MODULE_SUFFIX}`,
|
||||||
|
runtime: impDebugContext
|
||||||
|
});
|
||||||
|
static Renderer = new CompileIdentifierMetadata({
|
||||||
|
name: 'Renderer',
|
||||||
|
moduleUrl: 'asset:angular2/lib/src/core/render/api' + MODULE_SUFFIX,
|
||||||
|
runtime: impRenderer
|
||||||
|
});
|
||||||
|
static SimpleChange = new CompileIdentifierMetadata(
|
||||||
|
{name: 'SimpleChange', moduleUrl: CD_MODULE_URL, runtime: impSimpleChange});
|
||||||
|
static uninitialized = new CompileIdentifierMetadata(
|
||||||
|
{name: 'uninitialized', moduleUrl: CD_MODULE_URL, runtime: impUninitialized});
|
||||||
|
static ChangeDetectorState = new CompileIdentifierMetadata(
|
||||||
|
{name: 'ChangeDetectorState', moduleUrl: CD_MODULE_URL, runtime: impChangeDetectorState});
|
||||||
|
static checkBinding = new CompileIdentifierMetadata(
|
||||||
|
{name: 'checkBinding', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impCheckBinding});
|
||||||
|
static flattenNestedViewRenderNodes = new CompileIdentifierMetadata({
|
||||||
|
name: 'flattenNestedViewRenderNodes',
|
||||||
|
moduleUrl: VIEW_UTILS_MODULE_URL,
|
||||||
|
runtime: impFlattenNestedViewRenderNodes
|
||||||
|
});
|
||||||
|
static devModeEqual = new CompileIdentifierMetadata(
|
||||||
|
{name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: impDevModeEqual});
|
||||||
|
static interpolate = new CompileIdentifierMetadata(
|
||||||
|
{name: 'interpolate', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: impInterpolate});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function identifierToken(identifier: CompileIdentifierMetadata): CompileTokenMetadata {
|
||||||
|
return new CompileTokenMetadata({identifier: identifier});
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
import {
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompilePipeMetadata,
|
||||||
|
createHostComponentMeta
|
||||||
|
} from './compile_metadata';
|
||||||
|
|
||||||
|
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {StyleCompiler, StylesCompileDependency, StylesCompileResult} from './style_compiler';
|
||||||
|
import {ViewCompiler, ViewCompileResult} from './view_compiler/view_compiler';
|
||||||
|
import {TemplateParser} from './template_parser';
|
||||||
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
|
import {OutputEmitter} from './output/abstract_emitter';
|
||||||
|
import * as o from './output/output_ast';
|
||||||
|
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MODULE_SUFFIX,
|
||||||
|
} from './util';
|
||||||
|
|
||||||
|
var _HOST_VIEW_FACTORY_IDENTIFIER = new CompileIdentifierMetadata({
|
||||||
|
name: 'HostViewFactory',
|
||||||
|
runtime: HostViewFactory,
|
||||||
|
moduleUrl: `asset:angular2/lib/src/core/linker/view${MODULE_SUFFIX}`
|
||||||
|
});
|
||||||
|
|
||||||
|
export class SourceModule {
|
||||||
|
constructor(public moduleUrl: string, public source: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NormalizedComponentWithViewDirectives {
|
||||||
|
constructor(public component: CompileDirectiveMetadata,
|
||||||
|
public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OfflineCompiler {
|
||||||
|
constructor(private _directiveNormalizer: DirectiveNormalizer,
|
||||||
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||||
|
private _viewCompiler: ViewCompiler, private _outputEmitter: OutputEmitter) {}
|
||||||
|
|
||||||
|
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
||||||
|
Promise<CompileDirectiveMetadata> {
|
||||||
|
return this._directiveNormalizer.normalizeDirective(directive);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTemplates(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||||
|
if (components.length === 0) {
|
||||||
|
throw new BaseException('No components given');
|
||||||
|
}
|
||||||
|
var statements = [];
|
||||||
|
var exportedVars = [];
|
||||||
|
var moduleUrl = _templateModuleUrl(components[0].component);
|
||||||
|
components.forEach(componentWithDirs => {
|
||||||
|
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||||
|
_assertComponent(compMeta);
|
||||||
|
var compViewFactoryVar = this._compileComponent(compMeta, componentWithDirs.directives,
|
||||||
|
componentWithDirs.pipes, statements);
|
||||||
|
exportedVars.push(compViewFactoryVar);
|
||||||
|
|
||||||
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
|
var compHostViewFactoryVar = this._compileComponent(hostMeta, [compMeta], [], statements);
|
||||||
|
var hostViewFactoryVar = `hostViewFactory_${compMeta.type.name}`;
|
||||||
|
statements.push(
|
||||||
|
o.variable(hostViewFactoryVar)
|
||||||
|
.set(o.importExpr(_HOST_VIEW_FACTORY_IDENTIFIER)
|
||||||
|
.instantiate(
|
||||||
|
[o.literal(compMeta.selector), o.variable(compHostViewFactoryVar)],
|
||||||
|
o.importType(_HOST_VIEW_FACTORY_IDENTIFIER, null,
|
||||||
|
[o.TypeModifier.Const])))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
exportedVars.push(hostViewFactoryVar);
|
||||||
|
});
|
||||||
|
return this._codegenSourceModule(moduleUrl, statements, exportedVars);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileStylesheet(stylesheetUrl: string, cssText: string): SourceModule[] {
|
||||||
|
var plainStyles = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, false);
|
||||||
|
var shimStyles = this._styleCompiler.compileStylesheet(stylesheetUrl, cssText, true);
|
||||||
|
return [
|
||||||
|
this._codegenSourceModule(_stylesModuleUrl(stylesheetUrl, false),
|
||||||
|
_resolveStyleStatements(plainStyles), [plainStyles.stylesVar]),
|
||||||
|
this._codegenSourceModule(_stylesModuleUrl(stylesheetUrl, true),
|
||||||
|
_resolveStyleStatements(shimStyles), [shimStyles.stylesVar])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileComponent(compMeta: CompileDirectiveMetadata,
|
||||||
|
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
|
targetStatements: o.Statement[]): string {
|
||||||
|
var styleResult = this._styleCompiler.compileComponent(compMeta);
|
||||||
|
var parsedTemplate = this._templateParser.parse(compMeta, compMeta.template.template,
|
||||||
|
directives, pipes, compMeta.type.name);
|
||||||
|
var viewResult = this._viewCompiler.compileComponent(compMeta, parsedTemplate,
|
||||||
|
o.variable(styleResult.stylesVar), pipes);
|
||||||
|
ListWrapper.addAll(targetStatements, _resolveStyleStatements(styleResult));
|
||||||
|
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
|
||||||
|
return viewResult.viewFactoryVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _codegenSourceModule(moduleUrl: string, statements: o.Statement[],
|
||||||
|
exportedVars: string[]): SourceModule {
|
||||||
|
return new SourceModule(
|
||||||
|
moduleUrl, this._outputEmitter.emitStatements(moduleUrl, statements, exportedVars));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
||||||
|
compileResult.dependencies.forEach(
|
||||||
|
(dep) => { dep.factoryPlaceholder.moduleUrl = _templateModuleUrl(dep.comp); });
|
||||||
|
return compileResult.statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _resolveStyleStatements(compileResult: StylesCompileResult): o.Statement[] {
|
||||||
|
compileResult.dependencies.forEach((dep) => {
|
||||||
|
dep.valuePlaceholder.moduleUrl = _stylesModuleUrl(dep.sourceUrl, dep.isShimmed);
|
||||||
|
});
|
||||||
|
return compileResult.statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _templateModuleUrl(comp: CompileDirectiveMetadata): string {
|
||||||
|
var moduleUrl = comp.type.moduleUrl;
|
||||||
|
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
||||||
|
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean): string {
|
||||||
|
return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
|
if (!meta.isComponent) {
|
||||||
|
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
isString,
|
||||||
|
evalExpression,
|
||||||
|
RegExpWrapper,
|
||||||
|
StringWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
|
import * as o from './output_ast';
|
||||||
|
|
||||||
|
var _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
||||||
|
export var CATCH_ERROR_VAR = o.variable('error');
|
||||||
|
export var CATCH_STACK_VAR = o.variable('stack');
|
||||||
|
|
||||||
|
export abstract class OutputEmitter {
|
||||||
|
abstract emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EmittedLine {
|
||||||
|
parts: string[] = [];
|
||||||
|
constructor(public indent: number) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EmitterVisitorContext {
|
||||||
|
static createRoot(exportedVars: string[]): EmitterVisitorContext {
|
||||||
|
return new EmitterVisitorContext(exportedVars, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _lines: _EmittedLine[];
|
||||||
|
private _classes: o.ClassStmt[] = [];
|
||||||
|
|
||||||
|
constructor(private _exportedVars: string[], private _indent: number) {
|
||||||
|
this._lines = [new _EmittedLine(_indent)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _currentLine(): _EmittedLine { return this._lines[this._lines.length - 1]; }
|
||||||
|
|
||||||
|
isExportedVar(varName: string): boolean { return this._exportedVars.indexOf(varName) !== -1; }
|
||||||
|
|
||||||
|
println(lastPart: string = ''): void { this.print(lastPart, true); }
|
||||||
|
|
||||||
|
lineIsEmpty(): boolean { return this._currentLine.parts.length === 0; }
|
||||||
|
|
||||||
|
print(part: string, newLine: boolean = false) {
|
||||||
|
if (part.length > 0) {
|
||||||
|
this._currentLine.parts.push(part);
|
||||||
|
}
|
||||||
|
if (newLine) {
|
||||||
|
this._lines.push(new _EmittedLine(this._indent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEmptyLastLine() {
|
||||||
|
if (this.lineIsEmpty()) {
|
||||||
|
this._lines.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
incIndent() {
|
||||||
|
this._indent++;
|
||||||
|
this._currentLine.indent = this._indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
decIndent() {
|
||||||
|
this._indent--;
|
||||||
|
this._currentLine.indent = this._indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushClass(clazz: o.ClassStmt) { this._classes.push(clazz); }
|
||||||
|
|
||||||
|
popClass(): o.ClassStmt { return this._classes.pop(); }
|
||||||
|
|
||||||
|
get currentClass(): o.ClassStmt {
|
||||||
|
return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
toSource(): any {
|
||||||
|
var lines = this._lines;
|
||||||
|
if (lines[lines.length - 1].parts.length === 0) {
|
||||||
|
lines = lines.slice(0, lines.length - 1);
|
||||||
|
}
|
||||||
|
return lines.map((line) => {
|
||||||
|
if (line.parts.length > 0) {
|
||||||
|
return _createIndent(line.indent) + line.parts.join('');
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
|
||||||
|
constructor(private _escapeDollarInStrings: boolean) {}
|
||||||
|
|
||||||
|
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
|
||||||
|
stmt.expr.visitExpression(this, ctx);
|
||||||
|
ctx.println(';');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`return `);
|
||||||
|
stmt.value.visitExpression(this, ctx);
|
||||||
|
ctx.println(';');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract visitCastExpr(ast: o.CastExpr, context: any): any;
|
||||||
|
|
||||||
|
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
|
||||||
|
|
||||||
|
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`if (`);
|
||||||
|
stmt.condition.visitExpression(this, ctx);
|
||||||
|
ctx.print(`) {`);
|
||||||
|
var hasElseCase = isPresent(stmt.falseCase) && stmt.falseCase.length > 0;
|
||||||
|
if (stmt.trueCase.length <= 1 && !hasElseCase) {
|
||||||
|
ctx.print(` `);
|
||||||
|
this.visitAllStatements(stmt.trueCase, ctx);
|
||||||
|
ctx.removeEmptyLastLine();
|
||||||
|
ctx.print(` `);
|
||||||
|
} else {
|
||||||
|
ctx.println();
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.trueCase, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
if (hasElseCase) {
|
||||||
|
ctx.println(`} else {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.falseCase, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
|
||||||
|
|
||||||
|
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`throw `);
|
||||||
|
stmt.error.visitExpression(this, ctx);
|
||||||
|
ctx.println(`;`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
var lines = stmt.comment.split('\n');
|
||||||
|
lines.forEach((line) => { ctx.println(`// ${line}`); });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;
|
||||||
|
visitWriteVarExpr(expr: o.WriteVarExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var lineWasEmpty = ctx.lineIsEmpty();
|
||||||
|
if (!lineWasEmpty) {
|
||||||
|
ctx.print('(');
|
||||||
|
}
|
||||||
|
ctx.print(`${expr.name} = `);
|
||||||
|
expr.value.visitExpression(this, ctx);
|
||||||
|
if (!lineWasEmpty) {
|
||||||
|
ctx.print(')');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var lineWasEmpty = ctx.lineIsEmpty();
|
||||||
|
if (!lineWasEmpty) {
|
||||||
|
ctx.print('(');
|
||||||
|
}
|
||||||
|
expr.receiver.visitExpression(this, ctx);
|
||||||
|
ctx.print(`[`);
|
||||||
|
expr.index.visitExpression(this, ctx);
|
||||||
|
ctx.print(`] = `);
|
||||||
|
expr.value.visitExpression(this, ctx);
|
||||||
|
if (!lineWasEmpty) {
|
||||||
|
ctx.print(')');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitWritePropExpr(expr: o.WritePropExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var lineWasEmpty = ctx.lineIsEmpty();
|
||||||
|
if (!lineWasEmpty) {
|
||||||
|
ctx.print('(');
|
||||||
|
}
|
||||||
|
expr.receiver.visitExpression(this, ctx);
|
||||||
|
ctx.print(`.${expr.name} = `);
|
||||||
|
expr.value.visitExpression(this, ctx);
|
||||||
|
if (!lineWasEmpty) {
|
||||||
|
ctx.print(')');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
expr.receiver.visitExpression(this, ctx);
|
||||||
|
var name = expr.name;
|
||||||
|
if (isPresent(expr.builtin)) {
|
||||||
|
name = this.getBuiltinMethodName(expr.builtin);
|
||||||
|
}
|
||||||
|
ctx.print(`.${name}(`);
|
||||||
|
this.visitAllExpressions(expr.args, ctx, `,`);
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getBuiltinMethodName(method: o.BuiltinMethod): string;
|
||||||
|
|
||||||
|
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
expr.fn.visitExpression(this, ctx);
|
||||||
|
ctx.print(`(`);
|
||||||
|
this.visitAllExpressions(expr.args, ctx, ',');
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var varName = ast.name;
|
||||||
|
if (isPresent(ast.builtin)) {
|
||||||
|
switch (ast.builtin) {
|
||||||
|
case o.BuiltinVar.Super:
|
||||||
|
varName = 'super';
|
||||||
|
break;
|
||||||
|
case o.BuiltinVar.This:
|
||||||
|
varName = 'this';
|
||||||
|
break;
|
||||||
|
case o.BuiltinVar.CatchError:
|
||||||
|
varName = CATCH_ERROR_VAR.name;
|
||||||
|
break;
|
||||||
|
case o.BuiltinVar.CatchStack:
|
||||||
|
varName = CATCH_STACK_VAR.name;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown builtin variable ${ast.builtin}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.print(varName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`new `);
|
||||||
|
ast.classExpr.visitExpression(this, ctx);
|
||||||
|
ctx.print(`(`);
|
||||||
|
this.visitAllExpressions(ast.args, ctx, ',');
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitLiteralExpr(ast: o.LiteralExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var value = ast.value;
|
||||||
|
if (isString(value)) {
|
||||||
|
ctx.print(escapeSingleQuoteString(value, this._escapeDollarInStrings));
|
||||||
|
} else if (isBlank(value)) {
|
||||||
|
ctx.print('null');
|
||||||
|
} else {
|
||||||
|
ctx.print(`${value}`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any;
|
||||||
|
|
||||||
|
visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ast.condition.visitExpression(this, ctx);
|
||||||
|
ctx.print('? ');
|
||||||
|
ast.trueCase.visitExpression(this, ctx);
|
||||||
|
ctx.print(': ');
|
||||||
|
ast.falseCase.visitExpression(this, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitNotExpr(ast: o.NotExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print('!');
|
||||||
|
ast.condition.visitExpression(this, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
abstract visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any;
|
||||||
|
abstract visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: any): any;
|
||||||
|
|
||||||
|
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var opStr;
|
||||||
|
switch (ast.operator) {
|
||||||
|
case o.BinaryOperator.Equals:
|
||||||
|
opStr = '==';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Identical:
|
||||||
|
opStr = '===';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.NotEquals:
|
||||||
|
opStr = '!=';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.NotIdentical:
|
||||||
|
opStr = '!==';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.And:
|
||||||
|
opStr = '&&';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Or:
|
||||||
|
opStr = '||';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Plus:
|
||||||
|
opStr = '+';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Minus:
|
||||||
|
opStr = '-';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Divide:
|
||||||
|
opStr = '/';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Multiply:
|
||||||
|
opStr = '*';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Modulo:
|
||||||
|
opStr = '%';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Lower:
|
||||||
|
opStr = '<';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.LowerEquals:
|
||||||
|
opStr = '<=';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.Bigger:
|
||||||
|
opStr = '>';
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.BiggerEquals:
|
||||||
|
opStr = '>=';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown operator ${ast.operator}`);
|
||||||
|
}
|
||||||
|
ctx.print(`(`);
|
||||||
|
ast.lhs.visitExpression(this, ctx);
|
||||||
|
ctx.print(` ${opStr} `);
|
||||||
|
ast.rhs.visitExpression(this, ctx);
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitReadPropExpr(ast: o.ReadPropExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ast.receiver.visitExpression(this, ctx);
|
||||||
|
ctx.print(`.`);
|
||||||
|
ctx.print(ast.name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ast.receiver.visitExpression(this, ctx);
|
||||||
|
ctx.print(`[`);
|
||||||
|
ast.index.visitExpression(this, ctx);
|
||||||
|
ctx.print(`]`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var useNewLine = ast.entries.length > 1;
|
||||||
|
ctx.print(`[`, useNewLine);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllExpressions(ast.entries, ctx, ',', useNewLine);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.print(`]`, useNewLine);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var useNewLine = ast.entries.length > 1;
|
||||||
|
ctx.print(`{`, useNewLine);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllObjects((entry) => {
|
||||||
|
ctx.print(`${escapeSingleQuoteString(entry[0], this._escapeDollarInStrings)}: `);
|
||||||
|
entry[1].visitExpression(this, ctx);
|
||||||
|
}, ast.entries, ctx, ',', useNewLine);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.print(`}`, useNewLine);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAllExpressions(expressions: o.Expression[], ctx: EmitterVisitorContext, separator: string,
|
||||||
|
newLine: boolean = false): void {
|
||||||
|
this.visitAllObjects((expr) => expr.visitExpression(this, ctx), expressions, ctx, separator,
|
||||||
|
newLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAllObjects(handler: Function, expressions: any, ctx: EmitterVisitorContext,
|
||||||
|
separator: string, newLine: boolean = false): void {
|
||||||
|
for (var i = 0; i < expressions.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
ctx.print(separator, newLine);
|
||||||
|
}
|
||||||
|
handler(expressions[i]);
|
||||||
|
}
|
||||||
|
if (newLine) {
|
||||||
|
ctx.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAllStatements(statements: o.Statement[], ctx: EmitterVisitorContext): void {
|
||||||
|
statements.forEach((stmt) => { return stmt.visitStatement(this, ctx); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function escapeSingleQuoteString(input: string, escapeDollar: boolean): any {
|
||||||
|
if (isBlank(input)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var body = StringWrapper.replaceAllMapped(input, _SINGLE_QUOTE_ESCAPE_STRING_RE, (match) => {
|
||||||
|
if (match[0] == '$') {
|
||||||
|
return escapeDollar ? '\\$' : '$';
|
||||||
|
} else if (match[0] == '\n') {
|
||||||
|
return '\\n';
|
||||||
|
} else if (match[0] == '\r') {
|
||||||
|
return '\\r';
|
||||||
|
} else {
|
||||||
|
return `\\${match[0]}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return `'${body}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createIndent(count: number): string {
|
||||||
|
var res = '';
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
res += ' ';
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import * as o from './output_ast';
|
||||||
|
import {
|
||||||
|
EmitterVisitorContext,
|
||||||
|
AbstractEmitterVisitor,
|
||||||
|
CATCH_ERROR_VAR,
|
||||||
|
CATCH_STACK_VAR
|
||||||
|
} from './abstract_emitter';
|
||||||
|
|
||||||
|
export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
||||||
|
constructor() { super(false); }
|
||||||
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.pushClass(stmt);
|
||||||
|
this._visitClassConstructor(stmt, ctx);
|
||||||
|
|
||||||
|
if (isPresent(stmt.parent)) {
|
||||||
|
ctx.print(`${stmt.name}.prototype = Object.create(`);
|
||||||
|
stmt.parent.visitExpression(this, ctx);
|
||||||
|
ctx.println(`.prototype);`);
|
||||||
|
}
|
||||||
|
stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
|
||||||
|
stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
|
||||||
|
ctx.popClass();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
|
||||||
|
ctx.print(`function ${stmt.name}(`);
|
||||||
|
if (isPresent(stmt.constructorMethod)) {
|
||||||
|
this._visitParams(stmt.constructorMethod.params, ctx);
|
||||||
|
}
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
if (isPresent(stmt.constructorMethod)) {
|
||||||
|
if (stmt.constructorMethod.body.length > 0) {
|
||||||
|
ctx.println(`var self = this;`);
|
||||||
|
this.visitAllStatements(stmt.constructorMethod.body, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitClassGetter(stmt: o.ClassStmt, getter: o.ClassGetter, ctx: EmitterVisitorContext) {
|
||||||
|
ctx.println(
|
||||||
|
`Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
if (getter.body.length > 0) {
|
||||||
|
ctx.println(`var self = this;`);
|
||||||
|
this.visitAllStatements(getter.body, ctx);
|
||||||
|
}
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}});`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitClassMethod(stmt: o.ClassStmt, method: o.ClassMethod, ctx: EmitterVisitorContext) {
|
||||||
|
ctx.print(`${stmt.name}.prototype.${method.name} = function(`);
|
||||||
|
this._visitParams(method.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
if (method.body.length > 0) {
|
||||||
|
ctx.println(`var self = this;`);
|
||||||
|
this.visitAllStatements(method.body, ctx);
|
||||||
|
}
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`};`);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitReadVarExpr(ast: o.ReadVarExpr, ctx: EmitterVisitorContext): string {
|
||||||
|
if (ast.builtin === o.BuiltinVar.This) {
|
||||||
|
ctx.print('self');
|
||||||
|
} else if (ast.builtin === o.BuiltinVar.Super) {
|
||||||
|
throw new BaseException(
|
||||||
|
`'super' needs to be handled at a parent ast node, not at the variable level!`);
|
||||||
|
} else {
|
||||||
|
super.visitReadVarExpr(ast, ctx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`var ${stmt.name} = `);
|
||||||
|
stmt.value.visitExpression(this, ctx);
|
||||||
|
ctx.println(`;`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ast.value.visitExpression(this, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitInvokeFunctionExpr(expr: o.InvokeFunctionExpr, ctx: EmitterVisitorContext): string {
|
||||||
|
var fnExpr = expr.fn;
|
||||||
|
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
|
||||||
|
ctx.currentClass.parent.visitExpression(this, ctx);
|
||||||
|
ctx.print(`.call(this`);
|
||||||
|
if (expr.args.length > 0) {
|
||||||
|
ctx.print(`, `);
|
||||||
|
this.visitAllExpressions(expr.args, ctx, ',');
|
||||||
|
}
|
||||||
|
ctx.print(`)`);
|
||||||
|
} else {
|
||||||
|
super.visitInvokeFunctionExpr(expr, ctx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`function(`);
|
||||||
|
this._visitParams(ast.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(ast.statements, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.print(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`function ${stmt.name}(`);
|
||||||
|
this._visitParams(stmt.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.statements, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.println(`try {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.bodyStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
var catchStmts = [
|
||||||
|
<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack'))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final])
|
||||||
|
].concat(stmt.catchStmts);
|
||||||
|
this.visitAllStatements(catchStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||||
|
this.visitAllObjects((param) => ctx.print(param.name), params, ctx, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
||||||
|
var name;
|
||||||
|
switch (method) {
|
||||||
|
case o.BuiltinMethod.ConcatArray:
|
||||||
|
name = 'concat';
|
||||||
|
break;
|
||||||
|
case o.BuiltinMethod.SubscribeObservable:
|
||||||
|
name = 'subscribe';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown builtin method: ${method}`);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,378 @@
|
||||||
|
import {
|
||||||
|
StringWrapper,
|
||||||
|
RegExpWrapper,
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
Math,
|
||||||
|
isString,
|
||||||
|
isArray
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
|
import * as o from './output_ast';
|
||||||
|
import {
|
||||||
|
OutputEmitter,
|
||||||
|
EmitterVisitorContext,
|
||||||
|
AbstractEmitterVisitor,
|
||||||
|
CATCH_ERROR_VAR,
|
||||||
|
CATCH_STACK_VAR,
|
||||||
|
escapeSingleQuoteString
|
||||||
|
} from './abstract_emitter';
|
||||||
|
import {getImportModulePath, ImportEnv} from './path_util';
|
||||||
|
|
||||||
|
var _debugModuleUrl = 'asset://debug/lib';
|
||||||
|
|
||||||
|
export function debugOutputAstAsDart(ast: o.Statement | o.Expression | o.Type | any[]): string {
|
||||||
|
var converter = new _DartEmitterVisitor(_debugModuleUrl);
|
||||||
|
var ctx = EmitterVisitorContext.createRoot([]);
|
||||||
|
var asts: any[];
|
||||||
|
if (isArray(ast)) {
|
||||||
|
asts = <any[]>ast;
|
||||||
|
} else {
|
||||||
|
asts = [ast];
|
||||||
|
}
|
||||||
|
asts.forEach((ast) => {
|
||||||
|
if (ast instanceof o.Statement) {
|
||||||
|
ast.visitStatement(converter, ctx);
|
||||||
|
} else if (ast instanceof o.Expression) {
|
||||||
|
ast.visitExpression(converter, ctx);
|
||||||
|
} else if (ast instanceof o.Type) {
|
||||||
|
ast.visitType(converter, ctx);
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`Don't know how to print debug info for ${ast}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ctx.toSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DartEmitter implements OutputEmitter {
|
||||||
|
constructor() {}
|
||||||
|
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
|
var srcParts = [];
|
||||||
|
// Note: We are not creating a library here as Dart does not need it.
|
||||||
|
// Dart analzyer might complain about it though.
|
||||||
|
|
||||||
|
var converter = new _DartEmitterVisitor(moduleUrl);
|
||||||
|
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
|
converter.visitAllStatements(stmts, ctx);
|
||||||
|
|
||||||
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
|
srcParts.push(
|
||||||
|
`import '${getImportModulePath(moduleUrl, importedModuleUrl, ImportEnv.Dart)}' as ${prefix};`);
|
||||||
|
});
|
||||||
|
srcParts.push(ctx.toSource());
|
||||||
|
return srcParts.join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DartEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||||
|
importsWithPrefixes = new Map<string, string>();
|
||||||
|
|
||||||
|
constructor(private _moduleUrl: string) { super(true); }
|
||||||
|
|
||||||
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
if (stmt.hasModifier(o.StmtModifier.Final)) {
|
||||||
|
if (isConstType(stmt.type)) {
|
||||||
|
ctx.print(`const `);
|
||||||
|
} else {
|
||||||
|
ctx.print(`final `);
|
||||||
|
}
|
||||||
|
} else if (isBlank(stmt.type)) {
|
||||||
|
ctx.print(`var `);
|
||||||
|
}
|
||||||
|
if (isPresent(stmt.type)) {
|
||||||
|
stmt.type.visitType(this, ctx);
|
||||||
|
ctx.print(` `);
|
||||||
|
}
|
||||||
|
ctx.print(`${stmt.name} = `);
|
||||||
|
stmt.value.visitExpression(this, ctx);
|
||||||
|
ctx.println(`;`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`(`);
|
||||||
|
ast.value.visitExpression(this, ctx);
|
||||||
|
ctx.print(` as `);
|
||||||
|
ast.type.visitType(this, ctx);
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.pushClass(stmt);
|
||||||
|
ctx.print(`class ${stmt.name}`);
|
||||||
|
if (isPresent(stmt.parent)) {
|
||||||
|
ctx.print(` extends `);
|
||||||
|
stmt.parent.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
ctx.println(` {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
stmt.fields.forEach((field) => this._visitClassField(field, ctx));
|
||||||
|
if (isPresent(stmt.constructorMethod)) {
|
||||||
|
this._visitClassConstructor(stmt, ctx);
|
||||||
|
}
|
||||||
|
stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx));
|
||||||
|
stmt.methods.forEach((method) => this._visitClassMethod(method, ctx));
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
ctx.popClass();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
|
||||||
|
if (field.hasModifier(o.StmtModifier.Final)) {
|
||||||
|
ctx.print(`final `);
|
||||||
|
} else if (isBlank(field.type)) {
|
||||||
|
ctx.print(`var `);
|
||||||
|
}
|
||||||
|
if (isPresent(field.type)) {
|
||||||
|
field.type.visitType(this, ctx);
|
||||||
|
ctx.print(` `);
|
||||||
|
}
|
||||||
|
ctx.println(`${field.name};`);
|
||||||
|
}
|
||||||
|
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
|
||||||
|
if (isPresent(getter.type)) {
|
||||||
|
getter.type.visitType(this, ctx);
|
||||||
|
ctx.print(` `);
|
||||||
|
}
|
||||||
|
ctx.println(`get ${getter.name} {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(getter.body, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
|
||||||
|
ctx.print(`${stmt.name}(`);
|
||||||
|
this._visitParams(stmt.constructorMethod.params, ctx);
|
||||||
|
ctx.print(`)`);
|
||||||
|
|
||||||
|
var ctorStmts = stmt.constructorMethod.body;
|
||||||
|
var superCtorExpr = ctorStmts.length > 0 ? getSuperConstructorCallExpr(ctorStmts[0]) : null;
|
||||||
|
if (isPresent(superCtorExpr)) {
|
||||||
|
ctx.print(`: `);
|
||||||
|
superCtorExpr.visitExpression(this, ctx);
|
||||||
|
ctorStmts = ctorStmts.slice(1);
|
||||||
|
}
|
||||||
|
ctx.println(` {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(ctorStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
|
||||||
|
if (isPresent(method.type)) {
|
||||||
|
method.type.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`void`);
|
||||||
|
}
|
||||||
|
ctx.print(` ${method.name}(`);
|
||||||
|
this._visitParams(method.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(method.body, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`(`);
|
||||||
|
this._visitParams(ast.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(ast.statements, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.print(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
if (isPresent(stmt.type)) {
|
||||||
|
stmt.type.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`void`);
|
||||||
|
}
|
||||||
|
ctx.print(` ${stmt.name}(`);
|
||||||
|
this._visitParams(stmt.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.statements, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
||||||
|
var name;
|
||||||
|
switch (method) {
|
||||||
|
case o.BuiltinMethod.ConcatArray:
|
||||||
|
name = '.addAll';
|
||||||
|
break;
|
||||||
|
case o.BuiltinMethod.SubscribeObservable:
|
||||||
|
name = 'listen';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown builtin method: ${method}`);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.println(`try {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.bodyStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`} catch (${CATCH_ERROR_VAR.name}, ${CATCH_STACK_VAR.name}) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.catchStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
switch (ast.operator) {
|
||||||
|
case o.BinaryOperator.Identical:
|
||||||
|
ctx.print(`identical(`);
|
||||||
|
ast.lhs.visitExpression(this, ctx);
|
||||||
|
ctx.print(`, `);
|
||||||
|
ast.rhs.visitExpression(this, ctx);
|
||||||
|
ctx.print(`)`);
|
||||||
|
break;
|
||||||
|
case o.BinaryOperator.NotIdentical:
|
||||||
|
ctx.print(`!identical(`);
|
||||||
|
ast.lhs.visitExpression(this, ctx);
|
||||||
|
ctx.print(`, `);
|
||||||
|
ast.rhs.visitExpression(this, ctx);
|
||||||
|
ctx.print(`)`);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.visitBinaryOperatorExpr(ast, ctx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
if (isConstType(ast.type)) {
|
||||||
|
ctx.print(`const `);
|
||||||
|
}
|
||||||
|
return super.visitLiteralArrayExpr(ast, ctx);
|
||||||
|
}
|
||||||
|
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
if (isConstType(ast.type)) {
|
||||||
|
ctx.print(`const `);
|
||||||
|
}
|
||||||
|
if (isPresent(ast.valueType)) {
|
||||||
|
ctx.print(`<String, `);
|
||||||
|
ast.valueType.visitType(this, ctx);
|
||||||
|
ctx.print(`>`);
|
||||||
|
}
|
||||||
|
return super.visitLiteralMapExpr(ast, ctx);
|
||||||
|
}
|
||||||
|
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(isConstType(ast.type) ? `const` : `new`);
|
||||||
|
ctx.print(' ');
|
||||||
|
ast.classExpr.visitExpression(this, ctx);
|
||||||
|
ctx.print(`(`);
|
||||||
|
this.visitAllExpressions(ast.args, ctx, `,`);
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
|
||||||
|
var typeStr;
|
||||||
|
switch (type.name) {
|
||||||
|
case o.BuiltinTypeName.Bool:
|
||||||
|
typeStr = 'bool';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Dynamic:
|
||||||
|
typeStr = 'dynamic';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Function:
|
||||||
|
typeStr = 'Function';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Number:
|
||||||
|
typeStr = 'num';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Int:
|
||||||
|
typeStr = 'int';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.String:
|
||||||
|
typeStr = 'String';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unsupported builtin type ${type.name}`);
|
||||||
|
}
|
||||||
|
ctx.print(typeStr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
|
||||||
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`List<`);
|
||||||
|
if (isPresent(type.of)) {
|
||||||
|
type.of.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`dynamic`);
|
||||||
|
}
|
||||||
|
ctx.print(`>`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`Map<String, `);
|
||||||
|
if (isPresent(type.valueType)) {
|
||||||
|
type.valueType.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`dynamic`);
|
||||||
|
}
|
||||||
|
ctx.print(`>`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||||
|
this.visitAllObjects((param) => {
|
||||||
|
if (isPresent(param.type)) {
|
||||||
|
param.type.visitType(this, ctx);
|
||||||
|
ctx.print(' ');
|
||||||
|
}
|
||||||
|
ctx.print(param.name);
|
||||||
|
}, params, ctx, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
|
||||||
|
ctx: EmitterVisitorContext): void {
|
||||||
|
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
||||||
|
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
||||||
|
if (isBlank(prefix)) {
|
||||||
|
prefix = `import${this.importsWithPrefixes.size}`;
|
||||||
|
this.importsWithPrefixes.set(value.moduleUrl, prefix);
|
||||||
|
}
|
||||||
|
ctx.print(`${prefix}.`);
|
||||||
|
}
|
||||||
|
ctx.print(value.name);
|
||||||
|
if (isPresent(typeParams) && typeParams.length > 0) {
|
||||||
|
ctx.print(`<`);
|
||||||
|
this.visitAllObjects((type) => type.visitType(this, ctx), typeParams, ctx, ',');
|
||||||
|
ctx.print(`>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSuperConstructorCallExpr(stmt: o.Statement): o.Expression {
|
||||||
|
if (stmt instanceof o.ExpressionStatement) {
|
||||||
|
var expr = stmt.expr;
|
||||||
|
if (expr instanceof o.InvokeFunctionExpr) {
|
||||||
|
var fn = expr.fn;
|
||||||
|
if (fn instanceof o.ReadVarExpr) {
|
||||||
|
if (fn.builtin === o.BuiltinVar.Super) {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isConstType(type: o.Type): boolean {
|
||||||
|
return isPresent(type) && type.hasModifier(o.TypeModifier.Const);
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {AppView} from 'angular2/src/core/linker/view';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {InstanceFactory, DynamicInstance} from './output_interpreter';
|
||||||
|
|
||||||
|
export class InterpretiveAppViewInstanceFactory implements InstanceFactory {
|
||||||
|
createInstance(superClass: any, clazz: any, args: any[], props: Map<string, any>,
|
||||||
|
getters: Map<string, Function>, methods: Map<string, Function>): any {
|
||||||
|
if (superClass === AppView) {
|
||||||
|
return new _InterpretiveAppView(args, props, getters, methods);
|
||||||
|
}
|
||||||
|
throw new BaseException(`Can't instantiate class ${superClass} in interpretative mode`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InterpretiveAppView extends AppView<any> implements DynamicInstance {
|
||||||
|
constructor(args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
|
||||||
|
public methods: Map<string, Function>) {
|
||||||
|
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9],
|
||||||
|
args[10]);
|
||||||
|
}
|
||||||
|
createInternal(rootSelector: string): void {
|
||||||
|
var m = this.methods.get('createInternal');
|
||||||
|
if (isPresent(m)) {
|
||||||
|
m(rootSelector);
|
||||||
|
} else {
|
||||||
|
super.createInternal(rootSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
injectorGetInternal(token: any, nodeIndex: number, notFoundResult: any): any {
|
||||||
|
var m = this.methods.get('injectorGetInternal');
|
||||||
|
if (isPresent(m)) {
|
||||||
|
return m(token, nodeIndex, notFoundResult);
|
||||||
|
} else {
|
||||||
|
return super.injectorGet(token, nodeIndex, notFoundResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destroyInternal(): void {
|
||||||
|
var m = this.methods.get('destroyInternal');
|
||||||
|
if (isPresent(m)) {
|
||||||
|
return m();
|
||||||
|
} else {
|
||||||
|
return super.destroyInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dirtyParentQueriesInternal(): void {
|
||||||
|
var m = this.methods.get('dirtyParentQueriesInternal');
|
||||||
|
if (isPresent(m)) {
|
||||||
|
return m();
|
||||||
|
} else {
|
||||||
|
return super.dirtyParentQueriesInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
detectChangesInternal(throwOnChange: boolean): void {
|
||||||
|
var m = this.methods.get('detectChangesInternal');
|
||||||
|
if (isPresent(m)) {
|
||||||
|
return m(throwOnChange);
|
||||||
|
} else {
|
||||||
|
return super.detectChangesInternal(throwOnChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
import * as o from './output_ast';
|
||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
isString,
|
||||||
|
evalExpression,
|
||||||
|
RegExpWrapper,
|
||||||
|
StringWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {OutputEmitter, EmitterVisitorContext} from './abstract_emitter';
|
||||||
|
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||||
|
import {getImportModulePath, ImportEnv} from './path_util';
|
||||||
|
|
||||||
|
export class JavaScriptEmitter implements OutputEmitter {
|
||||||
|
constructor() {}
|
||||||
|
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
|
var converter = new JsEmitterVisitor(moduleUrl);
|
||||||
|
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
|
converter.visitAllStatements(stmts, ctx);
|
||||||
|
var srcParts = [];
|
||||||
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
|
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||||
|
srcParts.push(`var ${prefix} = req` +
|
||||||
|
`uire('${getImportModulePath(moduleUrl, importedModuleUrl, ImportEnv.JS)}');`);
|
||||||
|
});
|
||||||
|
srcParts.push(ctx.toSource());
|
||||||
|
return srcParts.join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||||
|
importsWithPrefixes = new Map<string, string>();
|
||||||
|
|
||||||
|
constructor(private _moduleUrl: string) { super(); }
|
||||||
|
|
||||||
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
|
||||||
|
var prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
|
||||||
|
if (isBlank(prefix)) {
|
||||||
|
prefix = `import${this.importsWithPrefixes.size}`;
|
||||||
|
this.importsWithPrefixes.set(ast.value.moduleUrl, prefix);
|
||||||
|
}
|
||||||
|
ctx.print(`${prefix}.`);
|
||||||
|
}
|
||||||
|
ctx.print(ast.value.name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
super.visitDeclareVarStmt(stmt, ctx);
|
||||||
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
ctx.println(exportVar(stmt.name));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
super.visitDeclareFunctionStmt(stmt, ctx);
|
||||||
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
ctx.println(exportVar(stmt.name));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
super.visitDeclareClassStmt(stmt, ctx);
|
||||||
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
ctx.println(exportVar(stmt.name));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportVar(varName: string): string {
|
||||||
|
return `Object.defineProperty(exports, '${varName}', { get: function() { return ${varName}; }});`;
|
||||||
|
}
|
|
@ -0,0 +1,869 @@
|
||||||
|
import {CONST_EXPR, isString, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
|
|
||||||
|
//// Types
|
||||||
|
export enum TypeModifier {
|
||||||
|
Const
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Type {
|
||||||
|
constructor(public modifiers: TypeModifier[] = null) {
|
||||||
|
if (isBlank(modifiers)) {
|
||||||
|
this.modifiers = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abstract visitType(visitor: TypeVisitor, context: any): any;
|
||||||
|
|
||||||
|
hasModifier(modifier: TypeModifier): boolean { return this.modifiers.indexOf(modifier) !== -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BuiltinTypeName {
|
||||||
|
Dynamic,
|
||||||
|
Bool,
|
||||||
|
String,
|
||||||
|
Int,
|
||||||
|
Number,
|
||||||
|
Function
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BuiltinType extends Type {
|
||||||
|
constructor(public name: BuiltinTypeName, modifiers: TypeModifier[] = null) { super(modifiers); }
|
||||||
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
|
return visitor.visitBuiltintType(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExternalType extends Type {
|
||||||
|
constructor(public value: CompileIdentifierMetadata, public typeParams: Type[] = null,
|
||||||
|
modifiers: TypeModifier[] = null) {
|
||||||
|
super(modifiers);
|
||||||
|
}
|
||||||
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
|
return visitor.visitExternalType(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ArrayType extends Type {
|
||||||
|
constructor(public of: Type, modifiers: TypeModifier[] = null) { super(modifiers); }
|
||||||
|
visitType(visitor: TypeVisitor, context: any): any {
|
||||||
|
return visitor.visitArrayType(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class MapType extends Type {
|
||||||
|
constructor(public valueType: Type, modifiers: TypeModifier[] = null) { super(modifiers); }
|
||||||
|
visitType(visitor: TypeVisitor, context: any): any { return visitor.visitMapType(this, context); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export var DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
|
||||||
|
export var BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
|
||||||
|
export var INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
|
||||||
|
export var NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
|
||||||
|
export var STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
|
||||||
|
export var FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
|
||||||
|
|
||||||
|
|
||||||
|
export interface TypeVisitor {
|
||||||
|
visitBuiltintType(type: BuiltinType, context: any): any;
|
||||||
|
visitExternalType(type: ExternalType, context: any): any;
|
||||||
|
visitArrayType(type: ArrayType, context: any): any;
|
||||||
|
visitMapType(type: MapType, context: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
///// Expressions
|
||||||
|
|
||||||
|
export enum BinaryOperator {
|
||||||
|
Equals,
|
||||||
|
NotEquals,
|
||||||
|
Identical,
|
||||||
|
NotIdentical,
|
||||||
|
Minus,
|
||||||
|
Plus,
|
||||||
|
Divide,
|
||||||
|
Multiply,
|
||||||
|
Modulo,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Lower,
|
||||||
|
LowerEquals,
|
||||||
|
Bigger,
|
||||||
|
BiggerEquals
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export abstract class Expression {
|
||||||
|
constructor(public type: Type) {}
|
||||||
|
|
||||||
|
abstract visitExpression(visitor: ExpressionVisitor, context: any): any;
|
||||||
|
|
||||||
|
prop(name: string): ReadPropExpr { return new ReadPropExpr(this, name); }
|
||||||
|
|
||||||
|
key(index: Expression, type: Type = null): ReadKeyExpr {
|
||||||
|
return new ReadKeyExpr(this, index, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
callMethod(name: string | BuiltinMethod, params: Expression[]): InvokeMethodExpr {
|
||||||
|
return new InvokeMethodExpr(this, name, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
callFn(params: Expression[]): InvokeFunctionExpr { return new InvokeFunctionExpr(this, params); }
|
||||||
|
|
||||||
|
instantiate(params: Expression[], type: Type = null): InstantiateExpr {
|
||||||
|
return new InstantiateExpr(this, params, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
conditional(trueCase: Expression, falseCase: Expression = null): ConditionalExpr {
|
||||||
|
return new ConditionalExpr(this, trueCase, falseCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
equals(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs);
|
||||||
|
}
|
||||||
|
notEquals(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs);
|
||||||
|
}
|
||||||
|
identical(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs);
|
||||||
|
}
|
||||||
|
notIdentical(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs);
|
||||||
|
}
|
||||||
|
minus(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs);
|
||||||
|
}
|
||||||
|
plus(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs);
|
||||||
|
}
|
||||||
|
divide(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs);
|
||||||
|
}
|
||||||
|
multiply(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs);
|
||||||
|
}
|
||||||
|
modulo(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs);
|
||||||
|
}
|
||||||
|
and(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.And, this, rhs);
|
||||||
|
}
|
||||||
|
or(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs);
|
||||||
|
}
|
||||||
|
lower(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs);
|
||||||
|
}
|
||||||
|
lowerEquals(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs);
|
||||||
|
}
|
||||||
|
bigger(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs);
|
||||||
|
}
|
||||||
|
biggerEquals(rhs: Expression): BinaryOperatorExpr {
|
||||||
|
return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs);
|
||||||
|
}
|
||||||
|
isBlank(): Expression {
|
||||||
|
// Note: We use equals by purpose here to compare to null and undefined in JS.
|
||||||
|
return this.equals(NULL_EXPR);
|
||||||
|
}
|
||||||
|
cast(type: Type): Expression { return new CastExpr(this, type); }
|
||||||
|
toStmt(): Statement { return new ExpressionStatement(this); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BuiltinVar {
|
||||||
|
This,
|
||||||
|
Super,
|
||||||
|
CatchError,
|
||||||
|
CatchStack
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ReadVarExpr extends Expression {
|
||||||
|
public name;
|
||||||
|
public builtin: BuiltinVar;
|
||||||
|
|
||||||
|
constructor(name: string | BuiltinVar, type: Type = null) {
|
||||||
|
super(type);
|
||||||
|
if (isString(name)) {
|
||||||
|
this.name = <string>name;
|
||||||
|
this.builtin = null;
|
||||||
|
} else {
|
||||||
|
this.name = null;
|
||||||
|
this.builtin = <BuiltinVar>name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitReadVarExpr(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(value: Expression): WriteVarExpr { return new WriteVarExpr(this.name, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class WriteVarExpr extends Expression {
|
||||||
|
public value: Expression;
|
||||||
|
constructor(public name: string, value: Expression, type: Type = null) {
|
||||||
|
super(isPresent(type) ? type : value.type);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitWriteVarExpr(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
toDeclStmt(type: Type = null, modifiers: StmtModifier[] = null): DeclareVarStmt {
|
||||||
|
return new DeclareVarStmt(this.name, this.value, type, modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class WriteKeyExpr extends Expression {
|
||||||
|
public value: Expression;
|
||||||
|
constructor(public receiver: Expression, public index: Expression, value: Expression,
|
||||||
|
type: Type = null) {
|
||||||
|
super(isPresent(type) ? type : value.type);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitWriteKeyExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class WritePropExpr extends Expression {
|
||||||
|
public value: Expression;
|
||||||
|
constructor(public receiver: Expression, public name: string, value: Expression,
|
||||||
|
type: Type = null) {
|
||||||
|
super(isPresent(type) ? type : value.type);
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitWritePropExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum BuiltinMethod {
|
||||||
|
ConcatArray,
|
||||||
|
SubscribeObservable
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvokeMethodExpr extends Expression {
|
||||||
|
public name: string;
|
||||||
|
public builtin: BuiltinMethod;
|
||||||
|
constructor(public receiver: Expression, method: string | BuiltinMethod,
|
||||||
|
public args: Expression[], type: Type = null) {
|
||||||
|
super(type);
|
||||||
|
if (isString(method)) {
|
||||||
|
this.name = <string>method;
|
||||||
|
this.builtin = null;
|
||||||
|
} else {
|
||||||
|
this.name = null;
|
||||||
|
this.builtin = <BuiltinMethod>method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitInvokeMethodExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class InvokeFunctionExpr extends Expression {
|
||||||
|
constructor(public fn: Expression, public args: Expression[], type: Type = null) { super(type); }
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitInvokeFunctionExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class InstantiateExpr extends Expression {
|
||||||
|
constructor(public classExpr: Expression, public args: Expression[], type?: Type) { super(type); }
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitInstantiateExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class LiteralExpr extends Expression {
|
||||||
|
constructor(public value: any, type: Type = null) { super(type); }
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitLiteralExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ExternalExpr extends Expression {
|
||||||
|
constructor(public value: CompileIdentifierMetadata, type: Type = null,
|
||||||
|
public typeParams: Type[] = null) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitExternalExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ConditionalExpr extends Expression {
|
||||||
|
public trueCase: Expression;
|
||||||
|
constructor(public condition: Expression, trueCase: Expression,
|
||||||
|
public falseCase: Expression = null, type: Type = null) {
|
||||||
|
super(isPresent(type) ? type : trueCase.type);
|
||||||
|
this.trueCase = trueCase;
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitConditionalExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class NotExpr extends Expression {
|
||||||
|
constructor(public condition: Expression) { super(BOOL_TYPE); }
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitNotExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CastExpr extends Expression {
|
||||||
|
constructor(public value: Expression, type: Type) { super(type); }
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitCastExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class FnParam {
|
||||||
|
constructor(public name: string, public type: Type = null) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class FunctionExpr extends Expression {
|
||||||
|
constructor(public params: FnParam[], public statements: Statement[], type: Type = null) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitFunctionExpr(this, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
toDeclStmt(name: string, modifiers: StmtModifier[] = null): DeclareFunctionStmt {
|
||||||
|
return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class BinaryOperatorExpr extends Expression {
|
||||||
|
public lhs: Expression;
|
||||||
|
constructor(public operator: BinaryOperator, lhs: Expression, public rhs: Expression,
|
||||||
|
type: Type = null) {
|
||||||
|
super(isPresent(type) ? type : lhs.type);
|
||||||
|
this.lhs = lhs;
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitBinaryOperatorExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ReadPropExpr extends Expression {
|
||||||
|
constructor(public receiver: Expression, public name: string, type: Type = null) { super(type); }
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitReadPropExpr(this, context);
|
||||||
|
}
|
||||||
|
set(value: Expression): WritePropExpr {
|
||||||
|
return new WritePropExpr(this.receiver, this.name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ReadKeyExpr extends Expression {
|
||||||
|
constructor(public receiver: Expression, public index: Expression, type: Type = null) {
|
||||||
|
super(type);
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitReadKeyExpr(this, context);
|
||||||
|
}
|
||||||
|
set(value: Expression): WriteKeyExpr {
|
||||||
|
return new WriteKeyExpr(this.receiver, this.index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class LiteralArrayExpr extends Expression {
|
||||||
|
public entries: Expression[];
|
||||||
|
constructor(entries: Expression[], type: Type = null) {
|
||||||
|
super(type);
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitLiteralArrayExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class LiteralMapExpr extends Expression {
|
||||||
|
public valueType: Type = null;
|
||||||
|
;
|
||||||
|
constructor(public entries: Array<Array<string | Expression>>, type: MapType = null) {
|
||||||
|
super(type);
|
||||||
|
if (isPresent(type)) {
|
||||||
|
this.valueType = type.valueType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitExpression(visitor: ExpressionVisitor, context: any): any {
|
||||||
|
return visitor.visitLiteralMapExpr(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExpressionVisitor {
|
||||||
|
visitReadVarExpr(ast: ReadVarExpr, context: any): any;
|
||||||
|
visitWriteVarExpr(expr: WriteVarExpr, context: any): any;
|
||||||
|
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any;
|
||||||
|
visitWritePropExpr(expr: WritePropExpr, context: any): any;
|
||||||
|
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any;
|
||||||
|
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any;
|
||||||
|
visitInstantiateExpr(ast: InstantiateExpr, context: any): any;
|
||||||
|
visitLiteralExpr(ast: LiteralExpr, context: any): any;
|
||||||
|
visitExternalExpr(ast: ExternalExpr, context: any): any;
|
||||||
|
visitConditionalExpr(ast: ConditionalExpr, context: any): any;
|
||||||
|
visitNotExpr(ast: NotExpr, context: any): any;
|
||||||
|
visitCastExpr(ast: CastExpr, context: any): any;
|
||||||
|
visitFunctionExpr(ast: FunctionExpr, context: any): any;
|
||||||
|
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any;
|
||||||
|
visitReadPropExpr(ast: ReadPropExpr, context: any): any;
|
||||||
|
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any;
|
||||||
|
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any;
|
||||||
|
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export var THIS_EXPR = new ReadVarExpr(BuiltinVar.This);
|
||||||
|
export var SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super);
|
||||||
|
export var CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError);
|
||||||
|
export var CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack);
|
||||||
|
export var NULL_EXPR = new LiteralExpr(null, null);
|
||||||
|
|
||||||
|
//// Statements
|
||||||
|
export enum StmtModifier {
|
||||||
|
Final,
|
||||||
|
Private
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Statement {
|
||||||
|
constructor(public modifiers: StmtModifier[] = null) {
|
||||||
|
if (isBlank(modifiers)) {
|
||||||
|
this.modifiers = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract visitStatement(visitor: StatementVisitor, context: any): any;
|
||||||
|
|
||||||
|
hasModifier(modifier: StmtModifier): boolean { return this.modifiers.indexOf(modifier) !== -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class DeclareVarStmt extends Statement {
|
||||||
|
public type: Type;
|
||||||
|
constructor(public name: string, public value: Expression, type: Type = null,
|
||||||
|
modifiers: StmtModifier[] = null) {
|
||||||
|
super(modifiers);
|
||||||
|
this.type = isPresent(type) ? type : value.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitDeclareVarStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DeclareFunctionStmt extends Statement {
|
||||||
|
constructor(public name: string, public params: FnParam[], public statements: Statement[],
|
||||||
|
public type: Type = null, modifiers: StmtModifier[] = null) {
|
||||||
|
super(modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitDeclareFunctionStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExpressionStatement extends Statement {
|
||||||
|
constructor(public expr: Expression) { super(); }
|
||||||
|
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitExpressionStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ReturnStatement extends Statement {
|
||||||
|
constructor(public value: Expression) { super(); }
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitReturnStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AbstractClassPart {
|
||||||
|
constructor(public type: Type = null, public modifiers: StmtModifier[]) {
|
||||||
|
if (isBlank(modifiers)) {
|
||||||
|
this.modifiers = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasModifier(modifier: StmtModifier): boolean { return this.modifiers.indexOf(modifier) !== -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ClassField extends AbstractClassPart {
|
||||||
|
constructor(public name: string, type: Type = null, modifiers: StmtModifier[] = null) {
|
||||||
|
super(type, modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ClassMethod extends AbstractClassPart {
|
||||||
|
constructor(public name: string, public params: FnParam[], public body: Statement[],
|
||||||
|
type: Type = null, modifiers: StmtModifier[] = null) {
|
||||||
|
super(type, modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ClassGetter extends AbstractClassPart {
|
||||||
|
constructor(public name: string, public body: Statement[], type: Type = null,
|
||||||
|
modifiers: StmtModifier[] = null) {
|
||||||
|
super(type, modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ClassStmt extends Statement {
|
||||||
|
constructor(public name: string, public parent: Expression, public fields: ClassField[],
|
||||||
|
public getters: ClassGetter[], public constructorMethod: ClassMethod,
|
||||||
|
public methods: ClassMethod[], modifiers: StmtModifier[] = null) {
|
||||||
|
super(modifiers);
|
||||||
|
}
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitDeclareClassStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class IfStmt extends Statement {
|
||||||
|
constructor(public condition: Expression, public trueCase: Statement[],
|
||||||
|
public falseCase: Statement[] = CONST_EXPR([])) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitIfStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class CommentStmt extends Statement {
|
||||||
|
constructor(public comment: string) { super(); }
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitCommentStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class TryCatchStmt extends Statement {
|
||||||
|
constructor(public bodyStmts: Statement[], public catchStmts: Statement[]) { super(); }
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitTryCatchStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ThrowStmt extends Statement {
|
||||||
|
constructor(public error: Expression) { super(); }
|
||||||
|
visitStatement(visitor: StatementVisitor, context: any): any {
|
||||||
|
return visitor.visitThrowStmt(this, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatementVisitor {
|
||||||
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any;
|
||||||
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any;
|
||||||
|
visitExpressionStmt(stmt: ExpressionStatement, context: any): any;
|
||||||
|
visitReturnStmt(stmt: ReturnStatement, context: any): any;
|
||||||
|
visitDeclareClassStmt(stmt: ClassStmt, context: any): any;
|
||||||
|
visitIfStmt(stmt: IfStmt, context: any): any;
|
||||||
|
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any;
|
||||||
|
visitThrowStmt(stmt: ThrowStmt, context: any): any;
|
||||||
|
visitCommentStmt(stmt: CommentStmt, context: any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExpressionTransformer implements StatementVisitor, ExpressionVisitor {
|
||||||
|
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; }
|
||||||
|
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
|
||||||
|
return new WriteVarExpr(expr.name, expr.value.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any {
|
||||||
|
return new WriteKeyExpr(expr.receiver.visitExpression(this, context),
|
||||||
|
expr.index.visitExpression(this, context),
|
||||||
|
expr.value.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitWritePropExpr(expr: WritePropExpr, context: any): any {
|
||||||
|
return new WritePropExpr(expr.receiver.visitExpression(this, context), expr.name,
|
||||||
|
expr.value.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
|
||||||
|
var method = isPresent(ast.builtin) ? ast.builtin : ast.name;
|
||||||
|
return new InvokeMethodExpr(ast.receiver.visitExpression(this, context), method,
|
||||||
|
this.visitAllExpressions(ast.args, context), ast.type);
|
||||||
|
}
|
||||||
|
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any {
|
||||||
|
return new InvokeFunctionExpr(ast.fn.visitExpression(this, context),
|
||||||
|
this.visitAllExpressions(ast.args, context), ast.type);
|
||||||
|
}
|
||||||
|
visitInstantiateExpr(ast: InstantiateExpr, context: any): any {
|
||||||
|
return new InstantiateExpr(ast.classExpr.visitExpression(this, context),
|
||||||
|
this.visitAllExpressions(ast.args, context), ast.type);
|
||||||
|
}
|
||||||
|
visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; }
|
||||||
|
visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; }
|
||||||
|
visitConditionalExpr(ast: ConditionalExpr, context: any): any {
|
||||||
|
return new ConditionalExpr(ast.condition.visitExpression(this, context),
|
||||||
|
ast.trueCase.visitExpression(this, context),
|
||||||
|
ast.falseCase.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitNotExpr(ast: NotExpr, context: any): any {
|
||||||
|
return new NotExpr(ast.condition.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitCastExpr(ast: CastExpr, context: any): any {
|
||||||
|
return new CastExpr(ast.value.visitExpression(this, context), context);
|
||||||
|
}
|
||||||
|
visitFunctionExpr(ast: FunctionExpr, context: any): any {
|
||||||
|
// Don't descend into nested functions
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any {
|
||||||
|
return new BinaryOperatorExpr(ast.operator, ast.lhs.visitExpression(this, context),
|
||||||
|
ast.rhs.visitExpression(this, context), ast.type);
|
||||||
|
}
|
||||||
|
visitReadPropExpr(ast: ReadPropExpr, context: any): any {
|
||||||
|
return new ReadPropExpr(ast.receiver.visitExpression(this, context), ast.name, ast.type);
|
||||||
|
}
|
||||||
|
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any {
|
||||||
|
return new ReadKeyExpr(ast.receiver.visitExpression(this, context),
|
||||||
|
ast.index.visitExpression(this, context), ast.type);
|
||||||
|
}
|
||||||
|
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
|
||||||
|
return new LiteralArrayExpr(this.visitAllExpressions(ast.entries, context));
|
||||||
|
}
|
||||||
|
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
||||||
|
return new LiteralMapExpr(ast.entries.map(
|
||||||
|
(entry) => [entry[0], (<Expression>entry[1]).visitExpression(this, context)]));
|
||||||
|
}
|
||||||
|
visitAllExpressions(exprs: Expression[], context: any): Expression[] {
|
||||||
|
return exprs.map(expr => expr.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
||||||
|
return new DeclareVarStmt(stmt.name, stmt.value.visitExpression(this, context), stmt.type,
|
||||||
|
stmt.modifiers);
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
|
||||||
|
// Don't descend into nested functions
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
|
||||||
|
return new ExpressionStatement(stmt.expr.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitReturnStmt(stmt: ReturnStatement, context: any): any {
|
||||||
|
return new ReturnStatement(stmt.value.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
|
||||||
|
// Don't descend into nested functions
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitIfStmt(stmt: IfStmt, context: any): any {
|
||||||
|
return new IfStmt(stmt.condition.visitExpression(this, context),
|
||||||
|
this.visitAllStatements(stmt.trueCase, context),
|
||||||
|
this.visitAllStatements(stmt.falseCase, context));
|
||||||
|
}
|
||||||
|
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any {
|
||||||
|
return new TryCatchStmt(this.visitAllStatements(stmt.bodyStmts, context),
|
||||||
|
this.visitAllStatements(stmt.catchStmts, context));
|
||||||
|
}
|
||||||
|
visitThrowStmt(stmt: ThrowStmt, context: any): any {
|
||||||
|
return new ThrowStmt(stmt.error.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; }
|
||||||
|
visitAllStatements(stmts: Statement[], context: any): Statement[] {
|
||||||
|
return stmts.map(stmt => stmt.visitStatement(this, context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class RecursiveExpressionVisitor implements StatementVisitor, ExpressionVisitor {
|
||||||
|
visitReadVarExpr(ast: ReadVarExpr, context: any): any { return ast; }
|
||||||
|
visitWriteVarExpr(expr: WriteVarExpr, context: any): any {
|
||||||
|
expr.value.visitExpression(this, context);
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
visitWriteKeyExpr(expr: WriteKeyExpr, context: any): any {
|
||||||
|
expr.receiver.visitExpression(this, context);
|
||||||
|
expr.index.visitExpression(this, context);
|
||||||
|
expr.value.visitExpression(this, context);
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
visitWritePropExpr(expr: WritePropExpr, context: any): any {
|
||||||
|
expr.receiver.visitExpression(this, context);
|
||||||
|
expr.value.visitExpression(this, context);
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
visitInvokeMethodExpr(ast: InvokeMethodExpr, context: any): any {
|
||||||
|
ast.receiver.visitExpression(this, context);
|
||||||
|
this.visitAllExpressions(ast.args, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any {
|
||||||
|
ast.fn.visitExpression(this, context);
|
||||||
|
this.visitAllExpressions(ast.args, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitInstantiateExpr(ast: InstantiateExpr, context: any): any {
|
||||||
|
ast.classExpr.visitExpression(this, context);
|
||||||
|
this.visitAllExpressions(ast.args, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitLiteralExpr(ast: LiteralExpr, context: any): any { return ast; }
|
||||||
|
visitExternalExpr(ast: ExternalExpr, context: any): any { return ast; }
|
||||||
|
visitConditionalExpr(ast: ConditionalExpr, context: any): any {
|
||||||
|
ast.condition.visitExpression(this, context);
|
||||||
|
ast.trueCase.visitExpression(this, context);
|
||||||
|
ast.falseCase.visitExpression(this, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitNotExpr(ast: NotExpr, context: any): any {
|
||||||
|
ast.condition.visitExpression(this, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitCastExpr(ast: CastExpr, context: any): any {
|
||||||
|
ast.value.visitExpression(this, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitFunctionExpr(ast: FunctionExpr, context: any): any { return ast; }
|
||||||
|
visitBinaryOperatorExpr(ast: BinaryOperatorExpr, context: any): any {
|
||||||
|
ast.lhs.visitExpression(this, context);
|
||||||
|
ast.rhs.visitExpression(this, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitReadPropExpr(ast: ReadPropExpr, context: any): any {
|
||||||
|
ast.receiver.visitExpression(this, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitReadKeyExpr(ast: ReadKeyExpr, context: any): any {
|
||||||
|
ast.receiver.visitExpression(this, context);
|
||||||
|
ast.index.visitExpression(this, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitLiteralArrayExpr(ast: LiteralArrayExpr, context: any): any {
|
||||||
|
this.visitAllExpressions(ast.entries, context);
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any {
|
||||||
|
ast.entries.forEach((entry) => (<Expression>entry[1]).visitExpression(this, context));
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
visitAllExpressions(exprs: Expression[], context: any): void {
|
||||||
|
exprs.forEach(expr => expr.visitExpression(this, context));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
|
||||||
|
stmt.value.visitExpression(this, context);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
|
||||||
|
// Don't descend into nested functions
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
|
||||||
|
stmt.expr.visitExpression(this, context);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitReturnStmt(stmt: ReturnStatement, context: any): any {
|
||||||
|
stmt.value.visitExpression(this, context);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
|
||||||
|
// Don't descend into nested functions
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitIfStmt(stmt: IfStmt, context: any): any {
|
||||||
|
stmt.condition.visitExpression(this, context);
|
||||||
|
this.visitAllStatements(stmt.trueCase, context);
|
||||||
|
this.visitAllStatements(stmt.falseCase, context);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any {
|
||||||
|
this.visitAllStatements(stmt.bodyStmts, context);
|
||||||
|
this.visitAllStatements(stmt.catchStmts, context);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitThrowStmt(stmt: ThrowStmt, context: any): any {
|
||||||
|
stmt.error.visitExpression(this, context);
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
visitCommentStmt(stmt: CommentStmt, context: any): any { return stmt; }
|
||||||
|
visitAllStatements(stmts: Statement[], context: any): void {
|
||||||
|
stmts.forEach(stmt => stmt.visitStatement(this, context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceVarInExpression(varName: string, newValue: Expression,
|
||||||
|
expression: Expression): Expression {
|
||||||
|
var transformer = new _ReplaceVariableTransformer(varName, newValue);
|
||||||
|
return expression.visitExpression(transformer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReplaceVariableTransformer extends ExpressionTransformer {
|
||||||
|
constructor(private _varName: string, private _newValue: Expression) { super(); }
|
||||||
|
visitReadVarExpr(ast: ReadVarExpr, context: any): any {
|
||||||
|
return ast.name == this._varName ? this._newValue : ast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findReadVarNames(stmts: Statement[]): Set<string> {
|
||||||
|
var finder = new _VariableFinder();
|
||||||
|
finder.visitAllStatements(stmts, null);
|
||||||
|
return finder.varNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VariableFinder extends RecursiveExpressionVisitor {
|
||||||
|
varNames = new Set<string>();
|
||||||
|
visitReadVarExpr(ast: ReadVarExpr, context: any): any {
|
||||||
|
this.varNames.add(ast.name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function variable(name: string, type: Type = null): ReadVarExpr {
|
||||||
|
return new ReadVarExpr(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function importExpr(id: CompileIdentifierMetadata, typeParams: Type[] = null): ExternalExpr {
|
||||||
|
return new ExternalExpr(id, null, typeParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function importType(id: CompileIdentifierMetadata, typeParams: Type[] = null,
|
||||||
|
typeModifiers: TypeModifier[] = null): ExternalType {
|
||||||
|
return isPresent(id) ? new ExternalType(id, typeParams, typeModifiers) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function literal(value: any, type: Type = null): LiteralExpr {
|
||||||
|
return new LiteralExpr(value, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function literalArr(values: Expression[], type: Type = null): LiteralArrayExpr {
|
||||||
|
return new LiteralArrayExpr(values, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function literalMap(values: Array<Array<string | Expression>>,
|
||||||
|
type: MapType = null): LiteralMapExpr {
|
||||||
|
return new LiteralMapExpr(values, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function not(expr: Expression): NotExpr {
|
||||||
|
return new NotExpr(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fn(params: FnParam[], body: Statement[], type: Type = null): FunctionExpr {
|
||||||
|
return new FunctionExpr(params, body, type);
|
||||||
|
}
|
|
@ -0,0 +1,413 @@
|
||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
isString,
|
||||||
|
evalExpression,
|
||||||
|
IS_DART,
|
||||||
|
FunctionWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
import * as o from './output_ast';
|
||||||
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
|
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
|
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {debugOutputAstAsDart} from './dart_emitter';
|
||||||
|
import {debugOutputAstAsTypeScript} from './ts_emitter';
|
||||||
|
|
||||||
|
export function interpretStatements(statements: o.Statement[], resultVar: string,
|
||||||
|
instanceFactory: InstanceFactory): any {
|
||||||
|
var stmtsWithReturn = statements.concat([new o.ReturnStatement(o.variable(resultVar))]);
|
||||||
|
var ctx = new _ExecutionContext(null, null, null, null, new Map<string, any>(),
|
||||||
|
new Map<string, any>(), new Map<string, Function>(),
|
||||||
|
new Map<string, Function>(), instanceFactory);
|
||||||
|
var visitor = new StatementInterpreter();
|
||||||
|
var result = visitor.visitAllStatements(stmtsWithReturn, ctx);
|
||||||
|
return isPresent(result) ? result.value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InstanceFactory {
|
||||||
|
createInstance(superClass: any, clazz: any, constructorArgs: any[], props: Map<string, any>,
|
||||||
|
getters: Map<string, Function>, methods: Map<string, Function>): DynamicInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class DynamicInstance {
|
||||||
|
get props(): Map<string, any> { return unimplemented(); }
|
||||||
|
get getters(): Map<string, Function> { return unimplemented(); }
|
||||||
|
get methods(): Map<string, any> { return unimplemented(); }
|
||||||
|
get clazz(): any { return unimplemented(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDynamicInstance(instance: any): any {
|
||||||
|
if (IS_DART) {
|
||||||
|
return instance instanceof DynamicInstance;
|
||||||
|
} else {
|
||||||
|
return isPresent(instance) && isPresent(instance.props) && isPresent(instance.getters) &&
|
||||||
|
isPresent(instance.methods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _executeFunctionStatements(varNames: string[], varValues: any[], statements: o.Statement[],
|
||||||
|
ctx: _ExecutionContext, visitor: StatementInterpreter): any {
|
||||||
|
var childCtx = ctx.createChildWihtLocalVars();
|
||||||
|
for (var i = 0; i < varNames.length; i++) {
|
||||||
|
childCtx.vars.set(varNames[i], varValues[i]);
|
||||||
|
}
|
||||||
|
var result = visitor.visitAllStatements(statements, childCtx);
|
||||||
|
return isPresent(result) ? result.value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ExecutionContext {
|
||||||
|
constructor(public parent: _ExecutionContext, public superClass: any, public superInstance: any,
|
||||||
|
public className: string, public vars: Map<string, any>,
|
||||||
|
public props: Map<string, any>, public getters: Map<string, Function>,
|
||||||
|
public methods: Map<string, Function>, public instanceFactory: InstanceFactory) {}
|
||||||
|
|
||||||
|
createChildWihtLocalVars(): _ExecutionContext {
|
||||||
|
return new _ExecutionContext(this, this.superClass, this.superInstance, this.className,
|
||||||
|
new Map<string, any>(), this.props, this.getters, this.methods,
|
||||||
|
this.instanceFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReturnValue {
|
||||||
|
constructor(public value: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DynamicClass {
|
||||||
|
constructor(private _classStmt: o.ClassStmt, private _ctx: _ExecutionContext,
|
||||||
|
private _visitor: StatementInterpreter) {}
|
||||||
|
|
||||||
|
instantiate(args: any[]): DynamicInstance {
|
||||||
|
var props = new Map<string, any>();
|
||||||
|
var getters = new Map<string, Function>();
|
||||||
|
var methods = new Map<string, Function>();
|
||||||
|
var superClass = this._classStmt.parent.visitExpression(this._visitor, this._ctx);
|
||||||
|
var instanceCtx =
|
||||||
|
new _ExecutionContext(this._ctx, superClass, null, this._classStmt.name, this._ctx.vars,
|
||||||
|
props, getters, methods, this._ctx.instanceFactory);
|
||||||
|
|
||||||
|
this._classStmt.fields.forEach((field: o.ClassField) => { props.set(field.name, null); });
|
||||||
|
this._classStmt.getters.forEach((getter: o.ClassGetter) => {
|
||||||
|
getters.set(getter.name, () => _executeFunctionStatements([], [], getter.body, instanceCtx,
|
||||||
|
this._visitor));
|
||||||
|
});
|
||||||
|
this._classStmt.methods.forEach((method: o.ClassMethod) => {
|
||||||
|
var paramNames = method.params.map(param => param.name);
|
||||||
|
methods.set(method.name, _declareFn(paramNames, method.body, instanceCtx, this._visitor));
|
||||||
|
});
|
||||||
|
|
||||||
|
var ctorParamNames = this._classStmt.constructorMethod.params.map(param => param.name);
|
||||||
|
_executeFunctionStatements(ctorParamNames, args, this._classStmt.constructorMethod.body,
|
||||||
|
instanceCtx, this._visitor);
|
||||||
|
return instanceCtx.superInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugAst(): string { return this._visitor.debugAst(this._classStmt); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
|
||||||
|
debugAst(ast: o.Expression | o.Statement | o.Type): string {
|
||||||
|
return IS_DART ? debugOutputAstAsDart(ast) : debugOutputAstAsTypeScript(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: _ExecutionContext): any {
|
||||||
|
ctx.vars.set(stmt.name, stmt.value.visitExpression(this, ctx));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitWriteVarExpr(expr: o.WriteVarExpr, ctx: _ExecutionContext): any {
|
||||||
|
var value = expr.value.visitExpression(this, ctx);
|
||||||
|
var currCtx = ctx;
|
||||||
|
while (currCtx != null) {
|
||||||
|
if (currCtx.vars.has(expr.name)) {
|
||||||
|
currCtx.vars.set(expr.name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
currCtx = currCtx.parent;
|
||||||
|
}
|
||||||
|
throw new BaseException(`Not declared variable ${expr.name}`);
|
||||||
|
}
|
||||||
|
visitReadVarExpr(ast: o.ReadVarExpr, ctx: _ExecutionContext): any {
|
||||||
|
var varName = ast.name;
|
||||||
|
if (isPresent(ast.builtin)) {
|
||||||
|
switch (ast.builtin) {
|
||||||
|
case o.BuiltinVar.Super:
|
||||||
|
case o.BuiltinVar.This:
|
||||||
|
return ctx.superInstance;
|
||||||
|
case o.BuiltinVar.CatchError:
|
||||||
|
varName = CATCH_ERROR_VAR;
|
||||||
|
break;
|
||||||
|
case o.BuiltinVar.CatchStack:
|
||||||
|
varName = CATCH_STACK_VAR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown builtin variable ${ast.builtin}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var currCtx = ctx;
|
||||||
|
while (currCtx != null) {
|
||||||
|
if (currCtx.vars.has(varName)) {
|
||||||
|
return currCtx.vars.get(varName);
|
||||||
|
}
|
||||||
|
currCtx = currCtx.parent;
|
||||||
|
}
|
||||||
|
throw new BaseException(`Not declared variable ${varName}`);
|
||||||
|
}
|
||||||
|
visitWriteKeyExpr(expr: o.WriteKeyExpr, ctx: _ExecutionContext): any {
|
||||||
|
var receiver = expr.receiver.visitExpression(this, ctx);
|
||||||
|
var index = expr.index.visitExpression(this, ctx);
|
||||||
|
var value = expr.value.visitExpression(this, ctx);
|
||||||
|
receiver[index] = value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
visitWritePropExpr(expr: o.WritePropExpr, ctx: _ExecutionContext): any {
|
||||||
|
var receiver = expr.receiver.visitExpression(this, ctx);
|
||||||
|
var value = expr.value.visitExpression(this, ctx);
|
||||||
|
if (isDynamicInstance(receiver)) {
|
||||||
|
var di = <DynamicInstance>receiver;
|
||||||
|
if (di.props.has(expr.name)) {
|
||||||
|
di.props.set(expr.name, value);
|
||||||
|
} else {
|
||||||
|
reflector.setter(expr.name)(receiver, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reflector.setter(expr.name)(receiver, value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitInvokeMethodExpr(expr: o.InvokeMethodExpr, ctx: _ExecutionContext): any {
|
||||||
|
var receiver = expr.receiver.visitExpression(this, ctx);
|
||||||
|
var args = this.visitAllExpressions(expr.args, ctx);
|
||||||
|
var result;
|
||||||
|
if (isPresent(expr.builtin)) {
|
||||||
|
switch (expr.builtin) {
|
||||||
|
case o.BuiltinMethod.ConcatArray:
|
||||||
|
result = ListWrapper.concat(receiver, args[0]);
|
||||||
|
break;
|
||||||
|
case o.BuiltinMethod.SubscribeObservable:
|
||||||
|
result = ObservableWrapper.subscribe(receiver, args[0]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown builtin method ${expr.builtin}`);
|
||||||
|
}
|
||||||
|
} else if (isDynamicInstance(receiver)) {
|
||||||
|
var di = <DynamicInstance>receiver;
|
||||||
|
if (di.methods.has(expr.name)) {
|
||||||
|
result = FunctionWrapper.apply(di.methods.get(expr.name), args);
|
||||||
|
} else {
|
||||||
|
result = reflector.method(expr.name)(receiver, args);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = reflector.method(expr.name)(receiver, args);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
visitInvokeFunctionExpr(stmt: o.InvokeFunctionExpr, ctx: _ExecutionContext): any {
|
||||||
|
var args = this.visitAllExpressions(stmt.args, ctx);
|
||||||
|
var fnExpr = stmt.fn;
|
||||||
|
if (fnExpr instanceof o.ReadVarExpr && fnExpr.builtin === o.BuiltinVar.Super) {
|
||||||
|
ctx.superInstance = ctx.instanceFactory.createInstance(ctx.superClass, ctx.className, args,
|
||||||
|
ctx.props, ctx.getters, ctx.methods);
|
||||||
|
ctx.parent.superInstance = ctx.superInstance;
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
var fn = stmt.fn.visitExpression(this, ctx);
|
||||||
|
return FunctionWrapper.apply(fn, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitReturnStmt(stmt: o.ReturnStatement, ctx: _ExecutionContext): any {
|
||||||
|
return new ReturnValue(stmt.value.visitExpression(this, ctx));
|
||||||
|
}
|
||||||
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: _ExecutionContext): any {
|
||||||
|
var clazz = new _DynamicClass(stmt, ctx, this);
|
||||||
|
ctx.vars.set(stmt.name, clazz);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: _ExecutionContext): any {
|
||||||
|
return stmt.expr.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
visitIfStmt(stmt: o.IfStmt, ctx: _ExecutionContext): any {
|
||||||
|
var condition = stmt.condition.visitExpression(this, ctx);
|
||||||
|
if (condition) {
|
||||||
|
return this.visitAllStatements(stmt.trueCase, ctx);
|
||||||
|
} else if (isPresent(stmt.falseCase)) {
|
||||||
|
return this.visitAllStatements(stmt.falseCase, ctx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: _ExecutionContext): any {
|
||||||
|
try {
|
||||||
|
return this.visitAllStatements(stmt.bodyStmts, ctx);
|
||||||
|
} catch (e) {
|
||||||
|
var childCtx = ctx.createChildWihtLocalVars();
|
||||||
|
childCtx.vars.set(CATCH_ERROR_VAR, e);
|
||||||
|
childCtx.vars.set(CATCH_STACK_VAR, e.stack);
|
||||||
|
return this.visitAllStatements(stmt.catchStmts, childCtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any {
|
||||||
|
throw stmt.error.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
visitCommentStmt(stmt: o.CommentStmt, context?: any): any { return null; }
|
||||||
|
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any {
|
||||||
|
var args = this.visitAllExpressions(ast.args, ctx);
|
||||||
|
var clazz = ast.classExpr.visitExpression(this, ctx);
|
||||||
|
if (clazz instanceof _DynamicClass) {
|
||||||
|
return clazz.instantiate(args);
|
||||||
|
} else {
|
||||||
|
return FunctionWrapper.apply(reflector.factory(clazz), args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { return ast.value; }
|
||||||
|
visitExternalExpr(ast: o.ExternalExpr, ctx: _ExecutionContext): any { return ast.value.runtime; }
|
||||||
|
visitConditionalExpr(ast: o.ConditionalExpr, ctx: _ExecutionContext): any {
|
||||||
|
if (ast.condition.visitExpression(this, ctx)) {
|
||||||
|
return ast.trueCase.visitExpression(this, ctx);
|
||||||
|
} else if (isPresent(ast.falseCase)) {
|
||||||
|
return ast.falseCase.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitNotExpr(ast: o.NotExpr, ctx: _ExecutionContext): any {
|
||||||
|
return !ast.condition.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
visitCastExpr(ast: o.CastExpr, ctx: _ExecutionContext): any {
|
||||||
|
return ast.value.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
visitFunctionExpr(ast: o.FunctionExpr, ctx: _ExecutionContext): any {
|
||||||
|
var paramNames = ast.params.map((param) => param.name);
|
||||||
|
return _declareFn(paramNames, ast.statements, ctx, this);
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: _ExecutionContext): any {
|
||||||
|
var paramNames = stmt.params.map((param) => param.name);
|
||||||
|
ctx.vars.set(stmt.name, _declareFn(paramNames, stmt.statements, ctx, this));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, ctx: _ExecutionContext): any {
|
||||||
|
var lhs = () => ast.lhs.visitExpression(this, ctx);
|
||||||
|
var rhs = () => ast.rhs.visitExpression(this, ctx);
|
||||||
|
|
||||||
|
switch (ast.operator) {
|
||||||
|
case o.BinaryOperator.Equals:
|
||||||
|
return lhs() == rhs();
|
||||||
|
case o.BinaryOperator.Identical:
|
||||||
|
return lhs() === rhs();
|
||||||
|
case o.BinaryOperator.NotEquals:
|
||||||
|
return lhs() != rhs();
|
||||||
|
case o.BinaryOperator.NotIdentical:
|
||||||
|
return lhs() !== rhs();
|
||||||
|
case o.BinaryOperator.And:
|
||||||
|
return lhs() && rhs();
|
||||||
|
case o.BinaryOperator.Or:
|
||||||
|
return lhs() || rhs();
|
||||||
|
case o.BinaryOperator.Plus:
|
||||||
|
return lhs() + rhs();
|
||||||
|
case o.BinaryOperator.Minus:
|
||||||
|
return lhs() - rhs();
|
||||||
|
case o.BinaryOperator.Divide:
|
||||||
|
return lhs() / rhs();
|
||||||
|
case o.BinaryOperator.Multiply:
|
||||||
|
return lhs() * rhs();
|
||||||
|
case o.BinaryOperator.Modulo:
|
||||||
|
return lhs() % rhs();
|
||||||
|
case o.BinaryOperator.Lower:
|
||||||
|
return lhs() < rhs();
|
||||||
|
case o.BinaryOperator.LowerEquals:
|
||||||
|
return lhs() <= rhs();
|
||||||
|
case o.BinaryOperator.Bigger:
|
||||||
|
return lhs() > rhs();
|
||||||
|
case o.BinaryOperator.BiggerEquals:
|
||||||
|
return lhs() >= rhs();
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown operator ${ast.operator}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitReadPropExpr(ast: o.ReadPropExpr, ctx: _ExecutionContext): any {
|
||||||
|
var result;
|
||||||
|
var receiver = ast.receiver.visitExpression(this, ctx);
|
||||||
|
if (isDynamicInstance(receiver)) {
|
||||||
|
var di = <DynamicInstance>receiver;
|
||||||
|
if (di.props.has(ast.name)) {
|
||||||
|
result = di.props.get(ast.name);
|
||||||
|
} else if (di.getters.has(ast.name)) {
|
||||||
|
result = di.getters.get(ast.name)();
|
||||||
|
} else {
|
||||||
|
result = reflector.getter(ast.name)(receiver);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = reflector.getter(ast.name)(receiver);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
visitReadKeyExpr(ast: o.ReadKeyExpr, ctx: _ExecutionContext): any {
|
||||||
|
var receiver = ast.receiver.visitExpression(this, ctx);
|
||||||
|
var prop = ast.index.visitExpression(this, ctx);
|
||||||
|
return receiver[prop];
|
||||||
|
}
|
||||||
|
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: _ExecutionContext): any {
|
||||||
|
return this.visitAllExpressions(ast.entries, ctx);
|
||||||
|
}
|
||||||
|
visitLiteralMapExpr(ast: o.LiteralMapExpr, ctx: _ExecutionContext): any {
|
||||||
|
var result = {};
|
||||||
|
ast.entries.forEach((entry) => result[<string>entry[0]] =
|
||||||
|
(<o.Expression>entry[1]).visitExpression(this, ctx));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAllExpressions(expressions: o.Expression[], ctx: _ExecutionContext): any {
|
||||||
|
return expressions.map((expr) => expr.visitExpression(this, ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAllStatements(statements: o.Statement[], ctx: _ExecutionContext): ReturnValue {
|
||||||
|
for (var i = 0; i < statements.length; i++) {
|
||||||
|
var stmt = statements[i];
|
||||||
|
var val = stmt.visitStatement(this, ctx);
|
||||||
|
if (val instanceof ReturnValue) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _declareFn(varNames: string[], statements: o.Statement[], ctx: _ExecutionContext,
|
||||||
|
visitor: StatementInterpreter): Function {
|
||||||
|
switch (varNames.length) {
|
||||||
|
case 0:
|
||||||
|
return () => _executeFunctionStatements(varNames, [], statements, ctx, visitor);
|
||||||
|
case 1:
|
||||||
|
return (d0) => _executeFunctionStatements(varNames, [d0], statements, ctx, visitor);
|
||||||
|
case 2:
|
||||||
|
return (d0, d1) => _executeFunctionStatements(varNames, [d0, d1], statements, ctx, visitor);
|
||||||
|
case 3:
|
||||||
|
return (d0, d1, d2) =>
|
||||||
|
_executeFunctionStatements(varNames, [d0, d1, d2], statements, ctx, visitor);
|
||||||
|
case 4:
|
||||||
|
return (d0, d1, d2, d3) =>
|
||||||
|
_executeFunctionStatements(varNames, [d0, d1, d2, d3], statements, ctx, visitor);
|
||||||
|
case 5:
|
||||||
|
return (d0, d1, d2, d3, d4) => _executeFunctionStatements(varNames, [d0, d1, d2, d3, d4],
|
||||||
|
statements, ctx, visitor);
|
||||||
|
case 6:
|
||||||
|
return (d0, d1, d2, d3, d4, d5) => _executeFunctionStatements(
|
||||||
|
varNames, [d0, d1, d2, d3, d4, d5], statements, ctx, visitor);
|
||||||
|
case 7:
|
||||||
|
return (d0, d1, d2, d3, d4, d5, d6) => _executeFunctionStatements(
|
||||||
|
varNames, [d0, d1, d2, d3, d4, d5, d6], statements, ctx, visitor);
|
||||||
|
case 8:
|
||||||
|
return (d0, d1, d2, d3, d4, d5, d6, d7) => _executeFunctionStatements(
|
||||||
|
varNames, [d0, d1, d2, d3, d4, d5, d6, d7], statements, ctx, visitor);
|
||||||
|
case 9:
|
||||||
|
return (d0, d1, d2, d3, d4, d5, d6, d7, d8) => _executeFunctionStatements(
|
||||||
|
varNames, [d0, d1, d2, d3, d4, d5, d6, d7, d8], statements, ctx, visitor);
|
||||||
|
case 10:
|
||||||
|
return (d0, d1, d2, d3, d4, d5, d6, d7, d8, d9) => _executeFunctionStatements(
|
||||||
|
varNames, [d0, d1, d2, d3, d4, d5, d6, d7, d8, d9], statements, ctx, visitor);
|
||||||
|
default:
|
||||||
|
throw new BaseException(
|
||||||
|
'Declaring functions with more than 10 arguments is not supported right now');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var CATCH_ERROR_VAR = 'error';
|
||||||
|
var CATCH_STACK_VAR = 'stack';
|
|
@ -0,0 +1,48 @@
|
||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
isString,
|
||||||
|
evalExpression,
|
||||||
|
RegExpWrapper,
|
||||||
|
StringWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import * as o from './output_ast';
|
||||||
|
import {EmitterVisitorContext} from './abstract_emitter';
|
||||||
|
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
|
||||||
|
|
||||||
|
export function jitStatements(sourceUrl: string, statements: o.Statement[],
|
||||||
|
resultVar: string): any {
|
||||||
|
var converter = new JitEmitterVisitor();
|
||||||
|
var ctx = EmitterVisitorContext.createRoot([resultVar]);
|
||||||
|
converter.visitAllStatements(statements, ctx);
|
||||||
|
return evalExpression(sourceUrl, resultVar, ctx.toSource(), converter.getArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
||||||
|
private _evalArgNames: string[] = [];
|
||||||
|
private _evalArgValues: any[] = [];
|
||||||
|
|
||||||
|
getArgs(): {[key: string]: any} {
|
||||||
|
var result = {};
|
||||||
|
for (var i = 0; i < this._evalArgNames.length; i++) {
|
||||||
|
result[this._evalArgNames[i]] = this._evalArgValues[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
var value = ast.value.runtime;
|
||||||
|
var id = this._evalArgValues.indexOf(value);
|
||||||
|
if (id === -1) {
|
||||||
|
id = this._evalArgValues.length;
|
||||||
|
this._evalArgValues.push(value);
|
||||||
|
this._evalArgNames.push(sanitizeJitArgName(`jit_${ast.value.name}${id}`));
|
||||||
|
}
|
||||||
|
ctx.print(this._evalArgNames[id]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeJitArgName(name: string): string {
|
||||||
|
return StringWrapper.replaceAll(name, /[\.\/]/g, '_');
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {isPresent, isBlank, RegExpWrapper, Math} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
// asset:<package-name>/<realm>/<path-to-module>
|
||||||
|
var _ASSET_URL_RE = /asset:([^\/]+)\/([^\/]+)\/(.+)/g;
|
||||||
|
|
||||||
|
var _PATH_SEP = '/';
|
||||||
|
var _PATH_SEP_RE = /\//g;
|
||||||
|
|
||||||
|
export enum ImportEnv {
|
||||||
|
Dart,
|
||||||
|
JS
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the module path to use for an import.
|
||||||
|
*/
|
||||||
|
export function getImportModulePath(moduleUrlStr: string, importedUrlStr: string,
|
||||||
|
importEnv: ImportEnv): string {
|
||||||
|
var absolutePathPrefix: string = importEnv === ImportEnv.Dart ? `package:` : '';
|
||||||
|
var moduleUrl = _AssetUrl.parse(moduleUrlStr, false);
|
||||||
|
var importedUrl = _AssetUrl.parse(importedUrlStr, true);
|
||||||
|
if (isBlank(importedUrl)) {
|
||||||
|
return importedUrlStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create a relative path first
|
||||||
|
if (moduleUrl.firstLevelDir == importedUrl.firstLevelDir &&
|
||||||
|
moduleUrl.packageName == importedUrl.packageName) {
|
||||||
|
return getRelativePath(moduleUrl.modulePath, importedUrl.modulePath, importEnv);
|
||||||
|
} else if (importedUrl.firstLevelDir == 'lib') {
|
||||||
|
return `${absolutePathPrefix}${importedUrl.packageName}/${importedUrl.modulePath}`;
|
||||||
|
}
|
||||||
|
throw new BaseException(`Can't import url ${importedUrlStr} from ${moduleUrlStr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AssetUrl {
|
||||||
|
static parse(url: string, allowNonMatching: boolean): _AssetUrl {
|
||||||
|
var match = RegExpWrapper.firstMatch(_ASSET_URL_RE, url);
|
||||||
|
if (isPresent(match)) {
|
||||||
|
return new _AssetUrl(match[1], match[2], match[3]);
|
||||||
|
}
|
||||||
|
if (allowNonMatching) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw new BaseException(`Url ${url} is not a valid asset: url`);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(public packageName: string, public firstLevelDir: string, public modulePath: string) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRelativePath(modulePath: string, importedPath: string,
|
||||||
|
importEnv: ImportEnv): string {
|
||||||
|
var moduleParts = modulePath.split(_PATH_SEP_RE);
|
||||||
|
var importedParts = importedPath.split(_PATH_SEP_RE);
|
||||||
|
var longestPrefix = getLongestPathSegmentPrefix(moduleParts, importedParts);
|
||||||
|
|
||||||
|
var resultParts = [];
|
||||||
|
var goParentCount = moduleParts.length - 1 - longestPrefix;
|
||||||
|
for (var i = 0; i < goParentCount; i++) {
|
||||||
|
resultParts.push('..');
|
||||||
|
}
|
||||||
|
if (goParentCount <= 0 && importEnv === ImportEnv.JS) {
|
||||||
|
resultParts.push('.');
|
||||||
|
}
|
||||||
|
for (var i = longestPrefix; i < importedParts.length; i++) {
|
||||||
|
resultParts.push(importedParts[i]);
|
||||||
|
}
|
||||||
|
return resultParts.join(_PATH_SEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLongestPathSegmentPrefix(arr1: string[], arr2: string[]): number {
|
||||||
|
var prefixSize = 0;
|
||||||
|
var minLen = Math.min(arr1.length, arr2.length);
|
||||||
|
while (prefixSize < minLen && arr1[prefixSize] == arr2[prefixSize]) {
|
||||||
|
prefixSize++;
|
||||||
|
}
|
||||||
|
return prefixSize;
|
||||||
|
}
|
|
@ -0,0 +1,324 @@
|
||||||
|
import * as o from './output_ast';
|
||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
isString,
|
||||||
|
evalExpression,
|
||||||
|
RegExpWrapper,
|
||||||
|
StringWrapper,
|
||||||
|
isArray
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {CompileIdentifierMetadata} from '../compile_metadata';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {
|
||||||
|
OutputEmitter,
|
||||||
|
EmitterVisitorContext,
|
||||||
|
AbstractEmitterVisitor,
|
||||||
|
CATCH_ERROR_VAR,
|
||||||
|
CATCH_STACK_VAR
|
||||||
|
} from './abstract_emitter';
|
||||||
|
import {getImportModulePath, ImportEnv} from './path_util';
|
||||||
|
|
||||||
|
var _debugModuleUrl = 'asset://debug/lib';
|
||||||
|
|
||||||
|
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type |
|
||||||
|
any[]): string {
|
||||||
|
var converter = new _TsEmitterVisitor(_debugModuleUrl);
|
||||||
|
var ctx = EmitterVisitorContext.createRoot([]);
|
||||||
|
var asts: any[];
|
||||||
|
if (isArray(ast)) {
|
||||||
|
asts = <any[]>ast;
|
||||||
|
} else {
|
||||||
|
asts = [ast];
|
||||||
|
}
|
||||||
|
asts.forEach((ast) => {
|
||||||
|
if (ast instanceof o.Statement) {
|
||||||
|
ast.visitStatement(converter, ctx);
|
||||||
|
} else if (ast instanceof o.Expression) {
|
||||||
|
ast.visitExpression(converter, ctx);
|
||||||
|
} else if (ast instanceof o.Type) {
|
||||||
|
ast.visitType(converter, ctx);
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`Don't know how to print debug info for ${ast}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ctx.toSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TypeScriptEmitter implements OutputEmitter {
|
||||||
|
constructor() {}
|
||||||
|
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
|
||||||
|
var converter = new _TsEmitterVisitor(moduleUrl);
|
||||||
|
var ctx = EmitterVisitorContext.createRoot(exportedVars);
|
||||||
|
converter.visitAllStatements(stmts, ctx);
|
||||||
|
var srcParts = [];
|
||||||
|
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
|
||||||
|
// Note: can't write the real word for import as it screws up system.js auto detection...
|
||||||
|
srcParts.push(
|
||||||
|
`imp` +
|
||||||
|
`ort * as ${prefix} from '${getImportModulePath(moduleUrl, importedModuleUrl, ImportEnv.JS)}';`);
|
||||||
|
});
|
||||||
|
srcParts.push(ctx.toSource());
|
||||||
|
return srcParts.join('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
|
||||||
|
constructor(private _moduleUrl: string) { super(false); }
|
||||||
|
|
||||||
|
importsWithPrefixes = new Map<string, string>();
|
||||||
|
|
||||||
|
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
ctx.print(`export `);
|
||||||
|
}
|
||||||
|
if (stmt.hasModifier(o.StmtModifier.Final)) {
|
||||||
|
ctx.print(`const`);
|
||||||
|
} else {
|
||||||
|
ctx.print(`var`);
|
||||||
|
}
|
||||||
|
ctx.print(` ${stmt.name}`);
|
||||||
|
if (isPresent(stmt.type)) {
|
||||||
|
ctx.print(`:`);
|
||||||
|
stmt.type.visitType(this, ctx);
|
||||||
|
}
|
||||||
|
ctx.print(` = `);
|
||||||
|
stmt.value.visitExpression(this, ctx);
|
||||||
|
ctx.println(`;`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitCastExpr(ast: o.CastExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`(<`);
|
||||||
|
ast.type.visitType(this, ctx);
|
||||||
|
ctx.print(`>`);
|
||||||
|
ast.value.visitExpression(this, ctx);
|
||||||
|
ctx.print(`)`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.pushClass(stmt);
|
||||||
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
ctx.print(`export `);
|
||||||
|
}
|
||||||
|
ctx.print(`class ${stmt.name}`);
|
||||||
|
if (isPresent(stmt.parent)) {
|
||||||
|
ctx.print(` extends `);
|
||||||
|
stmt.parent.visitExpression(this, ctx);
|
||||||
|
}
|
||||||
|
ctx.println(` {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
stmt.fields.forEach((field) => this._visitClassField(field, ctx));
|
||||||
|
if (isPresent(stmt.constructorMethod)) {
|
||||||
|
this._visitClassConstructor(stmt, ctx);
|
||||||
|
}
|
||||||
|
stmt.getters.forEach((getter) => this._visitClassGetter(getter, ctx));
|
||||||
|
stmt.methods.forEach((method) => this._visitClassMethod(method, ctx));
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
ctx.popClass();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private _visitClassField(field: o.ClassField, ctx: EmitterVisitorContext) {
|
||||||
|
if (field.hasModifier(o.StmtModifier.Private)) {
|
||||||
|
ctx.print(`private `);
|
||||||
|
}
|
||||||
|
ctx.print(field.name);
|
||||||
|
if (isPresent(field.type)) {
|
||||||
|
ctx.print(`:`);
|
||||||
|
field.type.visitType(this, ctx);
|
||||||
|
}
|
||||||
|
ctx.println(`;`);
|
||||||
|
}
|
||||||
|
private _visitClassGetter(getter: o.ClassGetter, ctx: EmitterVisitorContext) {
|
||||||
|
if (getter.hasModifier(o.StmtModifier.Private)) {
|
||||||
|
ctx.print(`private `);
|
||||||
|
}
|
||||||
|
ctx.print(`get ${getter.name}()`);
|
||||||
|
if (isPresent(getter.type)) {
|
||||||
|
ctx.print(`:`);
|
||||||
|
getter.type.visitType(this, ctx);
|
||||||
|
}
|
||||||
|
ctx.println(` {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(getter.body, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
private _visitClassConstructor(stmt: o.ClassStmt, ctx: EmitterVisitorContext) {
|
||||||
|
ctx.print(`constructor(`);
|
||||||
|
this._visitParams(stmt.constructorMethod.params, ctx);
|
||||||
|
ctx.println(`) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.constructorMethod.body, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
private _visitClassMethod(method: o.ClassMethod, ctx: EmitterVisitorContext) {
|
||||||
|
if (method.hasModifier(o.StmtModifier.Private)) {
|
||||||
|
ctx.print(`private `);
|
||||||
|
}
|
||||||
|
ctx.print(`${method.name}(`);
|
||||||
|
this._visitParams(method.params, ctx);
|
||||||
|
ctx.print(`):`);
|
||||||
|
if (isPresent(method.type)) {
|
||||||
|
method.type.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`void`);
|
||||||
|
}
|
||||||
|
ctx.println(` {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(method.body, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
}
|
||||||
|
visitFunctionExpr(ast: o.FunctionExpr, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`(`);
|
||||||
|
this._visitParams(ast.params, ctx);
|
||||||
|
ctx.print(`):`);
|
||||||
|
if (isPresent(ast.type)) {
|
||||||
|
ast.type.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`void`);
|
||||||
|
}
|
||||||
|
ctx.println(` => {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(ast.statements, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.print(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
if (ctx.isExportedVar(stmt.name)) {
|
||||||
|
ctx.print(`export `);
|
||||||
|
}
|
||||||
|
ctx.print(`function ${stmt.name}(`);
|
||||||
|
this._visitParams(stmt.params, ctx);
|
||||||
|
ctx.print(`):`);
|
||||||
|
if (isPresent(stmt.type)) {
|
||||||
|
stmt.type.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`void`);
|
||||||
|
}
|
||||||
|
ctx.println(` {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.statements, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.println(`try {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
this.visitAllStatements(stmt.bodyStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`} catch (${CATCH_ERROR_VAR.name}) {`);
|
||||||
|
ctx.incIndent();
|
||||||
|
var catchStmts = [
|
||||||
|
<o.Statement>CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack'))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final])
|
||||||
|
].concat(stmt.catchStmts);
|
||||||
|
this.visitAllStatements(catchStmts, ctx);
|
||||||
|
ctx.decIndent();
|
||||||
|
ctx.println(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBuiltintType(type: o.BuiltinType, ctx: EmitterVisitorContext): any {
|
||||||
|
var typeStr;
|
||||||
|
switch (type.name) {
|
||||||
|
case o.BuiltinTypeName.Bool:
|
||||||
|
typeStr = 'boolean';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Dynamic:
|
||||||
|
typeStr = 'any';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Function:
|
||||||
|
typeStr = 'Function';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Number:
|
||||||
|
typeStr = 'number';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.Int:
|
||||||
|
typeStr = 'number';
|
||||||
|
break;
|
||||||
|
case o.BuiltinTypeName.String:
|
||||||
|
typeStr = 'string';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unsupported builtin type ${type.name}`);
|
||||||
|
}
|
||||||
|
ctx.print(typeStr);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitExternalType(ast: o.ExternalType, ctx: EmitterVisitorContext): any {
|
||||||
|
this._visitIdentifier(ast.value, ast.typeParams, ctx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitArrayType(type: o.ArrayType, ctx: EmitterVisitorContext): any {
|
||||||
|
if (isPresent(type.of)) {
|
||||||
|
type.of.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`any`);
|
||||||
|
}
|
||||||
|
ctx.print(`[]`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitMapType(type: o.MapType, ctx: EmitterVisitorContext): any {
|
||||||
|
ctx.print(`{[key: string]:`);
|
||||||
|
if (isPresent(type.valueType)) {
|
||||||
|
type.valueType.visitType(this, ctx);
|
||||||
|
} else {
|
||||||
|
ctx.print(`any`);
|
||||||
|
}
|
||||||
|
ctx.print(`}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBuiltinMethodName(method: o.BuiltinMethod): string {
|
||||||
|
var name;
|
||||||
|
switch (method) {
|
||||||
|
case o.BuiltinMethod.ConcatArray:
|
||||||
|
name = 'concat';
|
||||||
|
break;
|
||||||
|
case o.BuiltinMethod.SubscribeObservable:
|
||||||
|
name = 'subscribe';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unknown builtin method: ${method}`);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
|
||||||
|
this.visitAllObjects((param) => {
|
||||||
|
ctx.print(param.name);
|
||||||
|
if (isPresent(param.type)) {
|
||||||
|
ctx.print(`:`);
|
||||||
|
param.type.visitType(this, ctx);
|
||||||
|
}
|
||||||
|
}, params, ctx, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitIdentifier(value: CompileIdentifierMetadata, typeParams: o.Type[],
|
||||||
|
ctx: EmitterVisitorContext): void {
|
||||||
|
if (isPresent(value.moduleUrl) && value.moduleUrl != this._moduleUrl) {
|
||||||
|
var prefix = this.importsWithPrefixes.get(value.moduleUrl);
|
||||||
|
if (isBlank(prefix)) {
|
||||||
|
prefix = `import${this.importsWithPrefixes.size}`;
|
||||||
|
this.importsWithPrefixes.set(value.moduleUrl, prefix);
|
||||||
|
}
|
||||||
|
ctx.print(`${prefix}.`);
|
||||||
|
}
|
||||||
|
ctx.print(value.name);
|
||||||
|
if (isPresent(typeParams) && typeParams.length > 0) {
|
||||||
|
ctx.print(`<`);
|
||||||
|
this.visitAllObjects((type) => type.visitType(this, ctx), typeParams, ctx, ',');
|
||||||
|
ctx.print(`>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,399 +0,0 @@
|
||||||
import {
|
|
||||||
isPresent,
|
|
||||||
isBlank,
|
|
||||||
Type,
|
|
||||||
isString,
|
|
||||||
StringWrapper,
|
|
||||||
IS_DART,
|
|
||||||
CONST_EXPR
|
|
||||||
} from 'angular2/src/facade/lang';
|
|
||||||
import {
|
|
||||||
SetWrapper,
|
|
||||||
StringMapWrapper,
|
|
||||||
ListWrapper,
|
|
||||||
MapWrapper
|
|
||||||
} from 'angular2/src/facade/collection';
|
|
||||||
import {
|
|
||||||
TemplateAst,
|
|
||||||
TemplateAstVisitor,
|
|
||||||
NgContentAst,
|
|
||||||
EmbeddedTemplateAst,
|
|
||||||
ElementAst,
|
|
||||||
VariableAst,
|
|
||||||
BoundEventAst,
|
|
||||||
BoundElementPropertyAst,
|
|
||||||
AttrAst,
|
|
||||||
BoundTextAst,
|
|
||||||
TextAst,
|
|
||||||
DirectiveAst,
|
|
||||||
BoundDirectivePropertyAst,
|
|
||||||
templateVisitAll
|
|
||||||
} from './template_ast';
|
|
||||||
import {
|
|
||||||
CompileTypeMetadata,
|
|
||||||
CompileDirectiveMetadata,
|
|
||||||
CompilePipeMetadata
|
|
||||||
} from './directive_metadata';
|
|
||||||
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
|
||||||
import {AppProtoView, AppView} from 'angular2/src/core/linker/view';
|
|
||||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
|
||||||
import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
|
|
||||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
|
||||||
import {
|
|
||||||
escapeSingleQuoteString,
|
|
||||||
codeGenConstConstructorCall,
|
|
||||||
codeGenValueFn,
|
|
||||||
codeGenFnHeader,
|
|
||||||
MODULE_SUFFIX,
|
|
||||||
codeGenStringMap,
|
|
||||||
Expression,
|
|
||||||
Statement
|
|
||||||
} from './util';
|
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
|
||||||
|
|
||||||
export const PROTO_VIEW_JIT_IMPORTS = CONST_EXPR(
|
|
||||||
{'AppProtoView': AppProtoView, 'AppProtoElement': AppProtoElement, 'ViewType': ViewType});
|
|
||||||
|
|
||||||
// TODO: have a single file that reexports everything needed for
|
|
||||||
// codegen explicitly
|
|
||||||
// - helps understanding what codegen works against
|
|
||||||
// - less imports in codegen code
|
|
||||||
export var APP_VIEW_MODULE_REF = moduleRef('package:angular2/src/core/linker/view' + MODULE_SUFFIX);
|
|
||||||
export var VIEW_TYPE_MODULE_REF =
|
|
||||||
moduleRef('package:angular2/src/core/linker/view_type' + MODULE_SUFFIX);
|
|
||||||
export var APP_EL_MODULE_REF =
|
|
||||||
moduleRef('package:angular2/src/core/linker/element' + MODULE_SUFFIX);
|
|
||||||
export var METADATA_MODULE_REF =
|
|
||||||
moduleRef('package:angular2/src/core/metadata/view' + MODULE_SUFFIX);
|
|
||||||
|
|
||||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
|
||||||
const CLASS_ATTR = 'class';
|
|
||||||
const STYLE_ATTR = 'style';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ProtoViewCompiler {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
compileProtoViewRuntime(metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
|
|
||||||
template: TemplateAst[], pipes: CompilePipeMetadata[]):
|
|
||||||
CompileProtoViews<AppProtoView, AppProtoElement, any> {
|
|
||||||
var protoViewFactory = new RuntimeProtoViewFactory(metadataCache, component, pipes);
|
|
||||||
var allProtoViews = [];
|
|
||||||
protoViewFactory.createCompileProtoView(template, [], [], allProtoViews);
|
|
||||||
return new CompileProtoViews<AppProtoView, AppProtoElement, any>([], allProtoViews);
|
|
||||||
}
|
|
||||||
|
|
||||||
compileProtoViewCodeGen(resolvedMetadataCacheExpr: Expression,
|
|
||||||
component: CompileDirectiveMetadata, template: TemplateAst[],
|
|
||||||
pipes: CompilePipeMetadata[]):
|
|
||||||
CompileProtoViews<Expression, Expression, string> {
|
|
||||||
var protoViewFactory = new CodeGenProtoViewFactory(resolvedMetadataCacheExpr, component, pipes);
|
|
||||||
var allProtoViews = [];
|
|
||||||
var allStatements = [];
|
|
||||||
protoViewFactory.createCompileProtoView(template, [], allStatements, allProtoViews);
|
|
||||||
return new CompileProtoViews<Expression, Expression, string>(
|
|
||||||
allStatements.map(stmt => stmt.statement), allProtoViews);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompileProtoViews<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
|
|
||||||
constructor(public declarations: STATEMENT[],
|
|
||||||
public protoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
|
|
||||||
constructor(public embeddedTemplateIndex: number,
|
|
||||||
public protoElements: CompileProtoElement<APP_PROTO_EL>[],
|
|
||||||
public protoView: APP_PROTO_VIEW) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompileProtoElement<APP_PROTO_EL> {
|
|
||||||
constructor(public boundElementIndex, public attrNameAndValues: string[][],
|
|
||||||
public variableNameAndValues: string[][], public renderEvents: BoundEventAst[],
|
|
||||||
public directives: CompileDirectiveMetadata[], public embeddedTemplateIndex: number,
|
|
||||||
public appProtoEl: APP_PROTO_EL) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
|
|
||||||
context: any): any {
|
|
||||||
templateVisitAll(visitor, asts, context);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
|
|
||||||
constructor(public component: CompileDirectiveMetadata) {}
|
|
||||||
|
|
||||||
abstract createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
|
||||||
templateVariableBindings: string[][],
|
|
||||||
targetStatements: STATEMENT[]): APP_PROTO_VIEW;
|
|
||||||
|
|
||||||
abstract createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
|
||||||
variableNameAndValues: string[][],
|
|
||||||
directives: CompileDirectiveMetadata[],
|
|
||||||
targetStatements: STATEMENT[]): APP_PROTO_EL;
|
|
||||||
|
|
||||||
createCompileProtoView(template: TemplateAst[], templateVariableBindings: string[][],
|
|
||||||
targetStatements: STATEMENT[],
|
|
||||||
targetProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]):
|
|
||||||
CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
|
|
||||||
var embeddedTemplateIndex = targetProtoViews.length;
|
|
||||||
// Note: targetProtoViews needs to be in depth first order.
|
|
||||||
// So we "reserve" a space here that we fill after the recursion is done
|
|
||||||
targetProtoViews.push(null);
|
|
||||||
var builder = new ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, any>(
|
|
||||||
this, targetStatements, targetProtoViews);
|
|
||||||
templateVisitAll(builder, template);
|
|
||||||
var viewType = getViewType(this.component, embeddedTemplateIndex);
|
|
||||||
var appProtoView = this.createAppProtoView(embeddedTemplateIndex, viewType,
|
|
||||||
templateVariableBindings, targetStatements);
|
|
||||||
var cpv = new CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>(
|
|
||||||
embeddedTemplateIndex, builder.protoElements, appProtoView);
|
|
||||||
targetProtoViews[embeddedTemplateIndex] = cpv;
|
|
||||||
return cpv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CodeGenProtoViewFactory extends ProtoViewFactory<Expression, Expression, Statement> {
|
|
||||||
private _nextVarId: number = 0;
|
|
||||||
|
|
||||||
constructor(public resolvedMetadataCacheExpr: Expression, component: CompileDirectiveMetadata,
|
|
||||||
public pipes: CompilePipeMetadata[]) {
|
|
||||||
super(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _nextProtoViewVar(embeddedTemplateIndex: number): string {
|
|
||||||
return `appProtoView${this._nextVarId++}_${this.component.type.name}${embeddedTemplateIndex}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
|
||||||
templateVariableBindings: string[][],
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var protoViewVarName = this._nextProtoViewVar(embeddedTemplateIndex);
|
|
||||||
var viewTypeExpr = codeGenViewType(viewType);
|
|
||||||
var pipesExpr = embeddedTemplateIndex === 0 ?
|
|
||||||
codeGenTypesArray(this.pipes.map(pipeMeta => pipeMeta.type)) :
|
|
||||||
null;
|
|
||||||
var statement =
|
|
||||||
`var ${protoViewVarName} = ${APP_VIEW_MODULE_REF}AppProtoView.create(${this.resolvedMetadataCacheExpr.expression}, ${viewTypeExpr}, ${pipesExpr}, ${codeGenStringMap(templateVariableBindings)});`;
|
|
||||||
targetStatements.push(new Statement(statement));
|
|
||||||
return new Expression(protoViewVarName);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
|
||||||
variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var varName = `appProtoEl${this._nextVarId++}_${this.component.type.name}`;
|
|
||||||
var value = `${APP_EL_MODULE_REF}AppProtoElement.create(
|
|
||||||
${this.resolvedMetadataCacheExpr.expression},
|
|
||||||
${boundElementIndex},
|
|
||||||
${codeGenStringMap(attrNameAndValues)},
|
|
||||||
${codeGenDirectivesArray(directives)},
|
|
||||||
${codeGenStringMap(variableNameAndValues)}
|
|
||||||
)`;
|
|
||||||
var statement = `var ${varName} = ${value};`;
|
|
||||||
targetStatements.push(new Statement(statement));
|
|
||||||
return new Expression(varName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RuntimeProtoViewFactory extends ProtoViewFactory<AppProtoView, AppProtoElement, any> {
|
|
||||||
constructor(public metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
|
|
||||||
public pipes: CompilePipeMetadata[]) {
|
|
||||||
super(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
|
||||||
templateVariableBindings: string[][], targetStatements: any[]): AppProtoView {
|
|
||||||
var pipes =
|
|
||||||
embeddedTemplateIndex === 0 ? this.pipes.map(pipeMeta => pipeMeta.type.runtime) : [];
|
|
||||||
var templateVars = keyValueArrayToStringMap(templateVariableBindings);
|
|
||||||
return AppProtoView.create(this.metadataCache, viewType, pipes, templateVars);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
|
||||||
variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
|
|
||||||
targetStatements: any[]): AppProtoElement {
|
|
||||||
var attrs = keyValueArrayToStringMap(attrNameAndValues);
|
|
||||||
return AppProtoElement.create(this.metadataCache, boundElementIndex, attrs,
|
|
||||||
directives.map(dirMeta => dirMeta.type.runtime),
|
|
||||||
keyValueArrayToStringMap(variableNameAndValues));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> implements
|
|
||||||
TemplateAstVisitor {
|
|
||||||
protoElements: CompileProtoElement<APP_PROTO_EL>[] = [];
|
|
||||||
boundElementCount: number = 0;
|
|
||||||
|
|
||||||
constructor(public factory: ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT>,
|
|
||||||
public allStatements: STATEMENT[],
|
|
||||||
public allProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
|
|
||||||
|
|
||||||
private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
|
|
||||||
attrAsts: TemplateAst[]): string[][] {
|
|
||||||
var attrs = visitAndReturnContext(this, attrAsts, {});
|
|
||||||
directives.forEach(directiveMeta => {
|
|
||||||
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value: string, name: string) => {
|
|
||||||
var prevValue = attrs[name];
|
|
||||||
attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return mapToKeyValueArray(attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBoundText(ast: BoundTextAst, context: any): any { return null; }
|
|
||||||
visitText(ast: TextAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
|
||||||
var boundElementIndex = null;
|
|
||||||
if (ast.isBound()) {
|
|
||||||
boundElementIndex = this.boundElementCount++;
|
|
||||||
}
|
|
||||||
var component = ast.getComponent();
|
|
||||||
|
|
||||||
var variableNameAndValues: string[][] = [];
|
|
||||||
if (isBlank(component)) {
|
|
||||||
ast.exportAsVars.forEach((varAst) => { variableNameAndValues.push([varAst.name, null]); });
|
|
||||||
}
|
|
||||||
var directives = [];
|
|
||||||
var renderEvents: Map<string, BoundEventAst> =
|
|
||||||
visitAndReturnContext(this, ast.outputs, new Map<string, BoundEventAst>());
|
|
||||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
|
||||||
directiveAst.visit(this, new DirectiveContext(index, boundElementIndex, renderEvents,
|
|
||||||
variableNameAndValues, directives));
|
|
||||||
});
|
|
||||||
var renderEventArray = [];
|
|
||||||
renderEvents.forEach((eventAst, _) => renderEventArray.push(eventAst));
|
|
||||||
|
|
||||||
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
|
||||||
this._addProtoElement(ast.isBound(), boundElementIndex, attrNameAndValues,
|
|
||||||
variableNameAndValues, renderEventArray, directives, null);
|
|
||||||
templateVisitAll(this, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
|
||||||
var boundElementIndex = this.boundElementCount++;
|
|
||||||
var directives: CompileDirectiveMetadata[] = [];
|
|
||||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
|
||||||
directiveAst.visit(
|
|
||||||
this, new DirectiveContext(index, boundElementIndex, new Map<string, BoundEventAst>(), [],
|
|
||||||
directives));
|
|
||||||
});
|
|
||||||
|
|
||||||
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
|
||||||
var templateVariableBindings = ast.vars.map(
|
|
||||||
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
|
||||||
var nestedProtoView = this.factory.createCompileProtoView(
|
|
||||||
ast.children, templateVariableBindings, this.allStatements, this.allProtoViews);
|
|
||||||
this._addProtoElement(true, boundElementIndex, attrNameAndValues, [], [], directives,
|
|
||||||
nestedProtoView.embeddedTemplateIndex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addProtoElement(isBound: boolean, boundElementIndex, attrNameAndValues: string[][],
|
|
||||||
variableNameAndValues: string[][], renderEvents: BoundEventAst[],
|
|
||||||
directives: CompileDirectiveMetadata[], embeddedTemplateIndex: number) {
|
|
||||||
var appProtoEl = null;
|
|
||||||
if (isBound) {
|
|
||||||
appProtoEl =
|
|
||||||
this.factory.createAppProtoElement(boundElementIndex, attrNameAndValues,
|
|
||||||
variableNameAndValues, directives, this.allStatements);
|
|
||||||
}
|
|
||||||
var compileProtoEl = new CompileProtoElement<APP_PROTO_EL>(
|
|
||||||
boundElementIndex, attrNameAndValues, variableNameAndValues, renderEvents, directives,
|
|
||||||
embeddedTemplateIndex, appProtoEl);
|
|
||||||
this.protoElements.push(compileProtoEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
|
||||||
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any {
|
|
||||||
attrNameAndValues[ast.name] = ast.value;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
|
|
||||||
ctx.targetDirectives.push(ast.directive);
|
|
||||||
templateVisitAll(this, ast.hostEvents, ctx.hostEventTargetAndNames);
|
|
||||||
ast.exportAsVars.forEach(
|
|
||||||
varAst => { ctx.targetVariableNameAndValues.push([varAst.name, ctx.index]); });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
|
||||||
eventTargetAndNames.set(ast.fullName, ast);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
|
||||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
|
||||||
var entryArray: string[][] = [];
|
|
||||||
StringMapWrapper.forEach(data,
|
|
||||||
(value: string, name: string) => { entryArray.push([name, value]); });
|
|
||||||
// We need to sort to get a defined output order
|
|
||||||
// for tests and for caching generated artifacts...
|
|
||||||
ListWrapper.sort<string[]>(entryArray, (entry1: string[], entry2: string[]) =>
|
|
||||||
StringWrapper.compare(entry1[0], entry2[0]));
|
|
||||||
var keyValueArray: string[][] = [];
|
|
||||||
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
|
|
||||||
return keyValueArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
|
||||||
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
|
||||||
return `${attrValue1} ${attrValue2}`;
|
|
||||||
} else {
|
|
||||||
return attrValue2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirectiveContext {
|
|
||||||
constructor(public index: number, public boundElementIndex: number,
|
|
||||||
public hostEventTargetAndNames: Map<string, BoundEventAst>,
|
|
||||||
public targetVariableNameAndValues: any[][],
|
|
||||||
public targetDirectives: CompileDirectiveMetadata[]) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyValueArrayToStringMap(keyValueArray: any[][]): {[key: string]: any} {
|
|
||||||
var stringMap: {[key: string]: string} = {};
|
|
||||||
for (var i = 0; i < keyValueArray.length; i++) {
|
|
||||||
var entry = keyValueArray[i];
|
|
||||||
stringMap[entry[0]] = entry[1];
|
|
||||||
}
|
|
||||||
return stringMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
|
|
||||||
var expressions = directives.map(directiveType => typeRef(directiveType.type));
|
|
||||||
return `[${expressions.join(',')}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenTypesArray(types: CompileTypeMetadata[]): string {
|
|
||||||
var expressions = types.map(typeRef);
|
|
||||||
return `[${expressions.join(',')}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenViewType(value: ViewType): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `${VIEW_TYPE_MODULE_REF}${value}`;
|
|
||||||
} else {
|
|
||||||
return `${value}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function typeRef(type: CompileTypeMetadata): string {
|
|
||||||
return `${moduleRef(type.moduleUrl)}${type.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
|
|
||||||
if (embeddedTemplateIndex > 0) {
|
|
||||||
return ViewType.EMBEDDED;
|
|
||||||
} else if (component.type.isHost) {
|
|
||||||
return ViewType.HOST;
|
|
||||||
} else {
|
|
||||||
return ViewType.COMPONENT;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
import {isPresent, isBlank, isArray, normalizeBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {
|
||||||
|
TemplateAst,
|
||||||
|
TemplateAstVisitor,
|
||||||
|
NgContentAst,
|
||||||
|
EmbeddedTemplateAst,
|
||||||
|
ElementAst,
|
||||||
|
VariableAst,
|
||||||
|
BoundEventAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
AttrAst,
|
||||||
|
BoundTextAst,
|
||||||
|
TextAst,
|
||||||
|
DirectiveAst,
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
templateVisitAll,
|
||||||
|
PropertyBindingType,
|
||||||
|
ProviderAst,
|
||||||
|
ProviderAstType
|
||||||
|
} from './template_ast';
|
||||||
|
import {
|
||||||
|
CompileTypeMetadata,
|
||||||
|
CompileTokenMap,
|
||||||
|
CompileQueryMetadata,
|
||||||
|
CompileTokenMetadata,
|
||||||
|
CompileProviderMetadata,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileDiDependencyMetadata
|
||||||
|
} from './compile_metadata';
|
||||||
|
import {Identifiers, identifierToken} from './identifiers';
|
||||||
|
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
||||||
|
|
||||||
|
export class ProviderError extends ParseError {
|
||||||
|
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProviderViewContext {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
viewQueries: CompileTokenMap<CompileQueryMetadata[]>;
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
viewProviders: CompileTokenMap<boolean>;
|
||||||
|
errors: ProviderError[] = [];
|
||||||
|
|
||||||
|
constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) {
|
||||||
|
this.viewQueries = _getViewQueries(component);
|
||||||
|
this.viewProviders = new CompileTokenMap<boolean>();
|
||||||
|
_normalizeProviders(component.viewProviders, sourceSpan, this.errors)
|
||||||
|
.forEach((provider) => {
|
||||||
|
if (isBlank(this.viewProviders.get(provider.token))) {
|
||||||
|
this.viewProviders.add(provider.token, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProviderElementContext {
|
||||||
|
private _contentQueries: CompileTokenMap<CompileQueryMetadata[]>;
|
||||||
|
|
||||||
|
private _transformedProviders = new CompileTokenMap<ProviderAst>();
|
||||||
|
private _seenProviders = new CompileTokenMap<boolean>();
|
||||||
|
private _allProviders: CompileTokenMap<ProviderAst>;
|
||||||
|
private _attrs: {[key: string]: string};
|
||||||
|
|
||||||
|
constructor(private _viewContext: ProviderViewContext, private _parent: ProviderElementContext,
|
||||||
|
private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[],
|
||||||
|
attrs: AttrAst[], private _sourceSpan: ParseSourceSpan) {
|
||||||
|
this._attrs = {};
|
||||||
|
attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
|
||||||
|
var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
|
||||||
|
this._allProviders =
|
||||||
|
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors);
|
||||||
|
this._contentQueries = _getContentQueries(directivesMeta);
|
||||||
|
// create the providers that we know are eager first
|
||||||
|
this._allProviders.values().forEach((provider) => {
|
||||||
|
if (provider.eager || this.isQueried(provider.token)) {
|
||||||
|
this._getLocalProvider(provider.providerType, provider.token, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
afterElement() {
|
||||||
|
// collect lazy providers
|
||||||
|
this._allProviders.values().forEach(
|
||||||
|
(provider) => { this._getLocalProvider(provider.providerType, provider.token, false); });
|
||||||
|
}
|
||||||
|
|
||||||
|
get transformProviders(): ProviderAst[] { return this._transformedProviders.values(); }
|
||||||
|
|
||||||
|
get transformedDirectiveAsts(): DirectiveAst[] {
|
||||||
|
var sortedProviderTypes =
|
||||||
|
this._transformedProviders.values().map(provider => provider.token.identifier);
|
||||||
|
var sortedDirectives = ListWrapper.clone(this._directiveAsts);
|
||||||
|
ListWrapper.sort(this._directiveAsts,
|
||||||
|
(dir1, dir2) => sortedProviderTypes.indexOf(dir1.directive.type) -
|
||||||
|
sortedProviderTypes.indexOf(dir2.directive.type));
|
||||||
|
return sortedDirectives;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isQueried(token: CompileTokenMetadata): boolean {
|
||||||
|
var currentEl: ProviderElementContext = this;
|
||||||
|
var distance = 0;
|
||||||
|
while (currentEl !== null) {
|
||||||
|
var localQueries = currentEl._contentQueries.get(token);
|
||||||
|
if (isPresent(localQueries)) {
|
||||||
|
if (localQueries.some((query) => query.descendants || distance <= 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentEl._directiveAsts.length > 0) {
|
||||||
|
distance++;
|
||||||
|
}
|
||||||
|
currentEl = currentEl._parent;
|
||||||
|
}
|
||||||
|
if (isPresent(this._viewContext.viewQueries.get(token))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _getLocalProvider(requestingProviderType: ProviderAstType, token: CompileTokenMetadata,
|
||||||
|
eager: boolean): ProviderAst {
|
||||||
|
var resolvedProvider = this._allProviders.get(token);
|
||||||
|
if (isBlank(resolvedProvider) ||
|
||||||
|
((requestingProviderType === ProviderAstType.Directive ||
|
||||||
|
requestingProviderType === ProviderAstType.PublicService) &&
|
||||||
|
resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
||||||
|
((requestingProviderType === ProviderAstType.PrivateService ||
|
||||||
|
requestingProviderType === ProviderAstType.PublicService) &&
|
||||||
|
resolvedProvider.providerType === ProviderAstType.Builtin)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var transformedProviderAst = this._transformedProviders.get(token);
|
||||||
|
if (isPresent(transformedProviderAst)) {
|
||||||
|
return transformedProviderAst;
|
||||||
|
}
|
||||||
|
if (isPresent(this._seenProviders.get(token))) {
|
||||||
|
this._viewContext.errors.push(new ProviderError(
|
||||||
|
`Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this._seenProviders.add(token, true);
|
||||||
|
var transformedProviders = resolvedProvider.providers.map((provider) => {
|
||||||
|
var transformedUseValue = provider.useValue;
|
||||||
|
var transformedUseExisting = provider.useExisting;
|
||||||
|
var transformedDeps;
|
||||||
|
if (isPresent(provider.useExisting)) {
|
||||||
|
var existingDiDep = this._getDependency(
|
||||||
|
resolvedProvider.providerType,
|
||||||
|
new CompileDiDependencyMetadata({token: provider.useExisting}), eager);
|
||||||
|
if (isPresent(existingDiDep.token)) {
|
||||||
|
transformedUseExisting = existingDiDep.token;
|
||||||
|
} else {
|
||||||
|
transformedUseExisting = null;
|
||||||
|
transformedUseValue = existingDiDep.value;
|
||||||
|
}
|
||||||
|
} else if (isPresent(provider.useFactory)) {
|
||||||
|
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
||||||
|
transformedDeps =
|
||||||
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||||
|
} else if (isPresent(provider.useClass)) {
|
||||||
|
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
||||||
|
transformedDeps =
|
||||||
|
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
||||||
|
}
|
||||||
|
return _transformProvider(provider, {
|
||||||
|
useExisting: transformedUseExisting,
|
||||||
|
useValue: transformedUseValue,
|
||||||
|
deps: transformedDeps
|
||||||
|
});
|
||||||
|
});
|
||||||
|
transformedProviderAst =
|
||||||
|
_transformProviderAst(resolvedProvider, {eager: eager, providers: transformedProviders});
|
||||||
|
this._transformedProviders.add(token, transformedProviderAst);
|
||||||
|
return transformedProviderAst;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getLocalDependency(requestingProviderType: ProviderAstType,
|
||||||
|
dep: CompileDiDependencyMetadata,
|
||||||
|
eager: boolean = null): CompileDiDependencyMetadata {
|
||||||
|
if (dep.isAttribute) {
|
||||||
|
var attrValue = this._attrs[dep.token.value];
|
||||||
|
return new CompileDiDependencyMetadata({isValue: true, value: normalizeBlank(attrValue)});
|
||||||
|
}
|
||||||
|
if (isPresent(dep.query) || isPresent(dep.viewQuery)) {
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(dep.token)) {
|
||||||
|
// access builtints
|
||||||
|
if ((requestingProviderType === ProviderAstType.Directive ||
|
||||||
|
requestingProviderType === ProviderAstType.Component)) {
|
||||||
|
if (dep.token.equalsTo(identifierToken(Identifiers.Renderer)) ||
|
||||||
|
dep.token.equalsTo(identifierToken(Identifiers.ElementRef)) ||
|
||||||
|
dep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef)) ||
|
||||||
|
dep.token.equalsTo(identifierToken(Identifiers.ViewContainerRef)) ||
|
||||||
|
dep.token.equalsTo(identifierToken(Identifiers.TemplateRef))) {
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// access the injector
|
||||||
|
if (dep.token.equalsTo(identifierToken(Identifiers.Injector))) {
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
// access providers
|
||||||
|
if (isPresent(this._getLocalProvider(requestingProviderType, dep.token, eager))) {
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDependency(requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata,
|
||||||
|
eager: boolean = null): CompileDiDependencyMetadata {
|
||||||
|
var currElement: ProviderElementContext = this;
|
||||||
|
var currEager: boolean = eager;
|
||||||
|
var result: CompileDiDependencyMetadata = null;
|
||||||
|
if (!dep.isSkipSelf) {
|
||||||
|
result = this._getLocalDependency(requestingProviderType, dep, eager);
|
||||||
|
}
|
||||||
|
if (dep.isSelf) {
|
||||||
|
if (isBlank(result) && dep.isOptional) {
|
||||||
|
result = new CompileDiDependencyMetadata({isValue: true, value: null});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// check parent elements
|
||||||
|
while (isBlank(result) && isPresent(currElement._parent)) {
|
||||||
|
var prevElement = currElement;
|
||||||
|
currElement = currElement._parent;
|
||||||
|
if (prevElement._isViewRoot) {
|
||||||
|
currEager = false;
|
||||||
|
}
|
||||||
|
result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager);
|
||||||
|
}
|
||||||
|
// check @Host restriction
|
||||||
|
if (isBlank(result)) {
|
||||||
|
if (!dep.isHost || this._viewContext.component.type.isHost ||
|
||||||
|
identifierToken(this._viewContext.component.type).equalsTo(dep.token) ||
|
||||||
|
isPresent(this._viewContext.viewProviders.get(dep.token))) {
|
||||||
|
result = dep;
|
||||||
|
} else {
|
||||||
|
result = dep.isOptional ?
|
||||||
|
result = new CompileDiDependencyMetadata({isValue: true, value: null}) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isBlank(result)) {
|
||||||
|
this._viewContext.errors.push(
|
||||||
|
new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _transformProvider(
|
||||||
|
provider: CompileProviderMetadata,
|
||||||
|
{useExisting, useValue, deps}:
|
||||||
|
{useExisting: CompileTokenMetadata, useValue: any, deps: CompileDiDependencyMetadata[]}) {
|
||||||
|
return new CompileProviderMetadata({
|
||||||
|
token: provider.token,
|
||||||
|
useClass: provider.useClass,
|
||||||
|
useExisting: useExisting,
|
||||||
|
useFactory: provider.useFactory,
|
||||||
|
useValue: useValue,
|
||||||
|
deps: deps,
|
||||||
|
multi: provider.multi
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _transformProviderAst(
|
||||||
|
provider: ProviderAst,
|
||||||
|
{eager, providers}: {eager: boolean, providers: CompileProviderMetadata[]}): ProviderAst {
|
||||||
|
return new ProviderAst(provider.token, provider.multiProvider, provider.eager || eager, providers,
|
||||||
|
provider.providerType, provider.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _normalizeProviders(
|
||||||
|
providers: Array<CompileProviderMetadata | CompileTypeMetadata | any[]>,
|
||||||
|
sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
|
||||||
|
targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] {
|
||||||
|
if (isBlank(targetProviders)) {
|
||||||
|
targetProviders = [];
|
||||||
|
}
|
||||||
|
if (isPresent(providers)) {
|
||||||
|
providers.forEach((provider) => {
|
||||||
|
if (isArray(provider)) {
|
||||||
|
_normalizeProviders(<any[]>provider, sourceSpan, targetErrors, targetProviders);
|
||||||
|
} else {
|
||||||
|
var normalizeProvider: CompileProviderMetadata;
|
||||||
|
if (provider instanceof CompileProviderMetadata) {
|
||||||
|
normalizeProvider = provider;
|
||||||
|
} else if (provider instanceof CompileTypeMetadata) {
|
||||||
|
normalizeProvider = new CompileProviderMetadata(
|
||||||
|
{token: new CompileTokenMetadata({identifier: provider}), useClass: provider});
|
||||||
|
} else {
|
||||||
|
targetErrors.push(new ProviderError(`Unknown provider type ${provider}`, sourceSpan));
|
||||||
|
}
|
||||||
|
if (isPresent(normalizeProvider)) {
|
||||||
|
targetProviders.push(normalizeProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return targetProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _resolveProvidersFromDirectives(directives: CompileDirectiveMetadata[],
|
||||||
|
sourceSpan: ParseSourceSpan,
|
||||||
|
targetErrors: ParseError[]): CompileTokenMap<ProviderAst> {
|
||||||
|
var providersByToken = new CompileTokenMap<ProviderAst>();
|
||||||
|
directives.forEach((directive) => {
|
||||||
|
var dirProvider = new CompileProviderMetadata(
|
||||||
|
{token: new CompileTokenMetadata({identifier: directive.type}), useClass: directive.type});
|
||||||
|
_resolveProviders([dirProvider],
|
||||||
|
directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive,
|
||||||
|
true, sourceSpan, targetErrors, providersByToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: directives need to be able to overwrite providers of a component!
|
||||||
|
var directivesWithComponentFirst =
|
||||||
|
directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
|
||||||
|
directivesWithComponentFirst.forEach((directive) => {
|
||||||
|
_resolveProviders(_normalizeProviders(directive.providers, sourceSpan, targetErrors),
|
||||||
|
ProviderAstType.PublicService, false, sourceSpan, targetErrors,
|
||||||
|
providersByToken);
|
||||||
|
_resolveProviders(_normalizeProviders(directive.viewProviders, sourceSpan, targetErrors),
|
||||||
|
ProviderAstType.PrivateService, false, sourceSpan, targetErrors,
|
||||||
|
providersByToken);
|
||||||
|
});
|
||||||
|
return providersByToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _resolveProviders(providers: CompileProviderMetadata[], providerType: ProviderAstType,
|
||||||
|
eager: boolean, sourceSpan: ParseSourceSpan, targetErrors: ParseError[],
|
||||||
|
targetProvidersByToken: CompileTokenMap<ProviderAst>) {
|
||||||
|
providers.forEach((provider) => {
|
||||||
|
var resolvedProvider = targetProvidersByToken.get(provider.token);
|
||||||
|
if (isPresent(resolvedProvider) && resolvedProvider.multiProvider !== provider.multi) {
|
||||||
|
targetErrors.push(new ProviderError(
|
||||||
|
`Mixing multi and non multi provider is not possible for token ${resolvedProvider.token.name}`,
|
||||||
|
sourceSpan));
|
||||||
|
}
|
||||||
|
if (isBlank(resolvedProvider)) {
|
||||||
|
resolvedProvider = new ProviderAst(provider.token, provider.multi, eager, [provider],
|
||||||
|
providerType, sourceSpan);
|
||||||
|
targetProvidersByToken.add(provider.token, resolvedProvider);
|
||||||
|
} else {
|
||||||
|
if (!provider.multi) {
|
||||||
|
ListWrapper.clear(resolvedProvider.providers);
|
||||||
|
}
|
||||||
|
resolvedProvider.providers.push(provider);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _getViewQueries(
|
||||||
|
component: CompileDirectiveMetadata): CompileTokenMap<CompileQueryMetadata[]> {
|
||||||
|
var viewQueries = new CompileTokenMap<CompileQueryMetadata[]>();
|
||||||
|
if (isPresent(component.viewQueries)) {
|
||||||
|
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
|
||||||
|
}
|
||||||
|
component.type.diDeps.forEach((dep) => {
|
||||||
|
if (isPresent(dep.viewQuery)) {
|
||||||
|
_addQueryToTokenMap(viewQueries, dep.viewQuery);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return viewQueries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getContentQueries(
|
||||||
|
directives: CompileDirectiveMetadata[]): CompileTokenMap<CompileQueryMetadata[]> {
|
||||||
|
var contentQueries = new CompileTokenMap<CompileQueryMetadata[]>();
|
||||||
|
directives.forEach(directive => {
|
||||||
|
if (isPresent(directive.queries)) {
|
||||||
|
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
|
||||||
|
}
|
||||||
|
directive.type.diDeps.forEach((dep) => {
|
||||||
|
if (isPresent(dep.query)) {
|
||||||
|
_addQueryToTokenMap(contentQueries, dep.query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return contentQueries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _addQueryToTokenMap(map: CompileTokenMap<CompileQueryMetadata[]>,
|
||||||
|
query: CompileQueryMetadata) {
|
||||||
|
query.selectors.forEach((selector) => {
|
||||||
|
var entry = map.get(selector);
|
||||||
|
if (isBlank(entry)) {
|
||||||
|
entry = [];
|
||||||
|
map.add(selector, entry);
|
||||||
|
}
|
||||||
|
entry.push(query);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,26 +1,239 @@
|
||||||
import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler';
|
import {
|
||||||
import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
IS_DART,
|
||||||
import {TemplateCompiler} from './template_compiler';
|
Type,
|
||||||
|
Json,
|
||||||
|
isBlank,
|
||||||
|
isPresent,
|
||||||
|
stringify,
|
||||||
|
evalExpression
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {
|
||||||
|
ListWrapper,
|
||||||
|
SetWrapper,
|
||||||
|
MapWrapper,
|
||||||
|
StringMapWrapper
|
||||||
|
} from 'angular2/src/facade/collection';
|
||||||
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {
|
||||||
|
createHostComponentMeta,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileTypeMetadata,
|
||||||
|
CompileTemplateMetadata,
|
||||||
|
CompilePipeMetadata,
|
||||||
|
CompileMetadataWithType,
|
||||||
|
CompileIdentifierMetadata
|
||||||
|
} from './compile_metadata';
|
||||||
|
import {
|
||||||
|
TemplateAst,
|
||||||
|
TemplateAstVisitor,
|
||||||
|
NgContentAst,
|
||||||
|
EmbeddedTemplateAst,
|
||||||
|
ElementAst,
|
||||||
|
VariableAst,
|
||||||
|
BoundEventAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
AttrAst,
|
||||||
|
BoundTextAst,
|
||||||
|
TextAst,
|
||||||
|
DirectiveAst,
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
templateVisitAll
|
||||||
|
} from './template_ast';
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
import {Type} from 'angular2/src/facade/lang';
|
import {StyleCompiler, StylesCompileDependency, StylesCompileResult} from './style_compiler';
|
||||||
|
import {ViewCompiler} from './view_compiler/view_compiler';
|
||||||
|
import {TemplateParser} from './template_parser';
|
||||||
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
|
import {RuntimeMetadataResolver} from './runtime_metadata';
|
||||||
|
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||||
|
import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
||||||
|
import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler';
|
||||||
|
|
||||||
export abstract class RuntimeCompiler extends Compiler {
|
import {CompilerConfig} from './config';
|
||||||
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
|
import * as ir from './output/output_ast';
|
||||||
abstract clearCache();
|
import {jitStatements} from './output/output_jit';
|
||||||
}
|
import {interpretStatements} from './output/output_interpreter';
|
||||||
|
import {InterpretiveAppViewInstanceFactory} from './output/interpretive_view';
|
||||||
|
|
||||||
|
import {XHR} from 'angular2/src/compiler/xhr';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An internal module of the Angular compiler that begins with component types,
|
||||||
|
* extracts templates, and eventually produces a compiled version of the component
|
||||||
|
* ready for linking into an application.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler {
|
export class RuntimeCompiler extends Compiler_ {
|
||||||
constructor(private _templateCompiler: TemplateCompiler) { super(); }
|
private _styleCache: Map<string, Promise<string>> = new Map<string, Promise<string>>();
|
||||||
|
private _hostCacheKeys = new Map<Type, any>();
|
||||||
|
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
||||||
|
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
|
||||||
|
|
||||||
|
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
||||||
|
private _templateNormalizer: DirectiveNormalizer,
|
||||||
|
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||||
|
private _viewCompiler: ViewCompiler, private _xhr: XHR,
|
||||||
|
private _genConfig: CompilerConfig) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||||
return this._templateCompiler.compileHostComponentRuntime(componentType)
|
var compMeta: CompileDirectiveMetadata =
|
||||||
.then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory));
|
this._runtimeMetadataResolver.getDirectiveMetadata(componentType);
|
||||||
|
var hostCacheKey = this._hostCacheKeys.get(componentType);
|
||||||
|
if (isBlank(hostCacheKey)) {
|
||||||
|
hostCacheKey = new Object();
|
||||||
|
this._hostCacheKeys.set(componentType, hostCacheKey);
|
||||||
|
assertComponent(compMeta);
|
||||||
|
var hostMeta: CompileDirectiveMetadata =
|
||||||
|
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
|
|
||||||
|
this._loadAndCompileComponent(hostCacheKey, hostMeta, [compMeta], [], []);
|
||||||
|
}
|
||||||
|
return this._compiledTemplateDone.get(hostCacheKey)
|
||||||
|
.then((compiledTemplate: CompiledTemplate) => new HostViewFactoryRef_(
|
||||||
|
new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory)));
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
super.clearCache();
|
this._styleCache.clear();
|
||||||
this._templateCompiler.clearCache();
|
this._compiledTemplateCache.clear();
|
||||||
|
this._compiledTemplateDone.clear();
|
||||||
|
this._hostCacheKeys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _loadAndCompileComponent(cacheKey: any, compMeta: CompileDirectiveMetadata,
|
||||||
|
viewDirectives: CompileDirectiveMetadata[],
|
||||||
|
pipes: CompilePipeMetadata[],
|
||||||
|
compilingComponentsPath: any[]): CompiledTemplate {
|
||||||
|
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
||||||
|
var done = this._compiledTemplateDone.get(cacheKey);
|
||||||
|
if (isBlank(compiledTemplate)) {
|
||||||
|
compiledTemplate = new CompiledTemplate();
|
||||||
|
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
||||||
|
done =
|
||||||
|
PromiseWrapper.all(
|
||||||
|
[<any>this._compileComponentStyles(compMeta)].concat(viewDirectives.map(
|
||||||
|
dirMeta => this._templateNormalizer.normalizeDirective(dirMeta))))
|
||||||
|
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||||
|
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||||
|
var styles = stylesAndNormalizedViewDirMetas[0];
|
||||||
|
var parsedTemplate =
|
||||||
|
this._templateParser.parse(compMeta, compMeta.template.template,
|
||||||
|
normalizedViewDirMetas, pipes, compMeta.type.name);
|
||||||
|
|
||||||
|
var childPromises = [];
|
||||||
|
compiledTemplate.init(this._compileComponent(compMeta, parsedTemplate, styles,
|
||||||
|
pipes, compilingComponentsPath,
|
||||||
|
childPromises));
|
||||||
|
return PromiseWrapper.all(childPromises).then((_) => { return compiledTemplate; });
|
||||||
|
});
|
||||||
|
this._compiledTemplateDone.set(cacheKey, done);
|
||||||
|
}
|
||||||
|
return compiledTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileComponent(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
||||||
|
styles: string[], pipes: CompilePipeMetadata[],
|
||||||
|
compilingComponentsPath: any[],
|
||||||
|
childPromises: Promise<any>[]): Function {
|
||||||
|
var compileResult = this._viewCompiler.compileComponent(
|
||||||
|
compMeta, parsedTemplate,
|
||||||
|
new ir.ExternalExpr(new CompileIdentifierMetadata({runtime: styles})), pipes);
|
||||||
|
compileResult.dependencies.forEach((dep) => {
|
||||||
|
var childCompilingComponentsPath = ListWrapper.clone(compilingComponentsPath);
|
||||||
|
|
||||||
|
var childCacheKey = dep.comp.type.runtime;
|
||||||
|
var childViewDirectives: CompileDirectiveMetadata[] =
|
||||||
|
this._runtimeMetadataResolver.getViewDirectivesMetadata(dep.comp.type.runtime);
|
||||||
|
var childViewPipes: CompilePipeMetadata[] =
|
||||||
|
this._runtimeMetadataResolver.getViewPipesMetadata(dep.comp.type.runtime);
|
||||||
|
var childIsRecursive = ListWrapper.contains(childCompilingComponentsPath, childCacheKey);
|
||||||
|
childCompilingComponentsPath.push(childCacheKey);
|
||||||
|
|
||||||
|
var childComp =
|
||||||
|
this._loadAndCompileComponent(dep.comp.type.runtime, dep.comp, childViewDirectives,
|
||||||
|
childViewPipes, childCompilingComponentsPath);
|
||||||
|
dep.factoryPlaceholder.runtime = childComp.proxyViewFactory;
|
||||||
|
dep.factoryPlaceholder.name = `viewFactory_${dep.comp.type.name}`;
|
||||||
|
if (!childIsRecursive) {
|
||||||
|
// Only wait for a child if it is not a cycle
|
||||||
|
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var factory;
|
||||||
|
if (IS_DART || !this._genConfig.useJit) {
|
||||||
|
factory = interpretStatements(compileResult.statements, compileResult.viewFactoryVar,
|
||||||
|
new InterpretiveAppViewInstanceFactory());
|
||||||
|
} else {
|
||||||
|
factory = jitStatements(`${compMeta.type.name}.template.js`, compileResult.statements,
|
||||||
|
compileResult.viewFactoryVar);
|
||||||
|
}
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileComponentStyles(compMeta: CompileDirectiveMetadata): Promise<string[]> {
|
||||||
|
var compileResult = this._styleCompiler.compileComponent(compMeta);
|
||||||
|
return this._resolveStylesCompileResult(compMeta.type.name, compileResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _resolveStylesCompileResult(sourceUrl: string,
|
||||||
|
result: StylesCompileResult): Promise<string[]> {
|
||||||
|
var promises = result.dependencies.map((dep) => this._loadStylesheetDep(dep));
|
||||||
|
return PromiseWrapper.all(promises)
|
||||||
|
.then((cssTexts) => {
|
||||||
|
var nestedCompileResultPromises = [];
|
||||||
|
for (var i = 0; i < result.dependencies.length; i++) {
|
||||||
|
var dep = result.dependencies[i];
|
||||||
|
var cssText = cssTexts[i];
|
||||||
|
var nestedCompileResult =
|
||||||
|
this._styleCompiler.compileStylesheet(dep.sourceUrl, cssText, dep.isShimmed);
|
||||||
|
nestedCompileResultPromises.push(
|
||||||
|
this._resolveStylesCompileResult(dep.sourceUrl, nestedCompileResult));
|
||||||
|
}
|
||||||
|
return PromiseWrapper.all(nestedCompileResultPromises);
|
||||||
|
})
|
||||||
|
.then((nestedStylesArr) => {
|
||||||
|
for (var i = 0; i < result.dependencies.length; i++) {
|
||||||
|
var dep = result.dependencies[i];
|
||||||
|
dep.valuePlaceholder.runtime = nestedStylesArr[i];
|
||||||
|
dep.valuePlaceholder.name = `importedStyles${i}`;
|
||||||
|
}
|
||||||
|
if (IS_DART || !this._genConfig.useJit) {
|
||||||
|
return interpretStatements(result.statements, result.stylesVar,
|
||||||
|
new InterpretiveAppViewInstanceFactory());
|
||||||
|
} else {
|
||||||
|
return jitStatements(`${sourceUrl}.css.js`, result.statements, result.stylesVar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadStylesheetDep(dep: StylesCompileDependency): Promise<string> {
|
||||||
|
var cacheKey = `${dep.sourceUrl}${dep.isShimmed ? '.shim' : ''}`;
|
||||||
|
var cssTextPromise = this._styleCache.get(cacheKey);
|
||||||
|
if (isBlank(cssTextPromise)) {
|
||||||
|
cssTextPromise = this._xhr.get(dep.sourceUrl);
|
||||||
|
this._styleCache.set(cacheKey, cssTextPromise);
|
||||||
|
}
|
||||||
|
return cssTextPromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompiledTemplate {
|
||||||
|
viewFactory: Function = null;
|
||||||
|
proxyViewFactory: Function;
|
||||||
|
constructor() {
|
||||||
|
this.proxyViewFactory = (viewManager, childInjector, contextEl) =>
|
||||||
|
this.viewFactory(viewManager, childInjector, contextEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(viewFactory: Function) { this.viewFactory = viewFactory; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||||
|
if (!meta.isComponent) {
|
||||||
|
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,23 +5,36 @@ import {
|
||||||
isPresent,
|
isPresent,
|
||||||
isArray,
|
isArray,
|
||||||
stringify,
|
stringify,
|
||||||
RegExpWrapper
|
isString,
|
||||||
|
RegExpWrapper,
|
||||||
|
StringWrapper
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import * as cpl from './directive_metadata';
|
import {NoAnnotationError} from 'angular2/src/core/di/exceptions';
|
||||||
|
import * as cpl from './compile_metadata';
|
||||||
import * as md from 'angular2/src/core/metadata/directives';
|
import * as md from 'angular2/src/core/metadata/directives';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
import * as dimd from 'angular2/src/core/metadata/di';
|
||||||
import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {ViewResolver} from 'angular2/src/core/linker/view_resolver';
|
import {PipeResolver} from './pipe_resolver';
|
||||||
|
import {ViewResolver} from './view_resolver';
|
||||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||||
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
|
import {hasLifecycleHook} from './directive_lifecycle_reflector';
|
||||||
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
||||||
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||||
import {MODULE_SUFFIX} from './util';
|
import {MODULE_SUFFIX} from './util';
|
||||||
import {assertArrayOfStrings} from './assertions';
|
import {assertArrayOfStrings} from './assertions';
|
||||||
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
||||||
|
import {Provider, constructDependencies, Dependency} from 'angular2/src/core/di/provider';
|
||||||
|
import {
|
||||||
|
OptionalMetadata,
|
||||||
|
SelfMetadata,
|
||||||
|
HostMetadata,
|
||||||
|
SkipSelfMetadata
|
||||||
|
} from 'angular2/src/core/di/metadata';
|
||||||
|
import {AttributeMetadata} from 'angular2/src/core/metadata/di';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RuntimeMetadataResolver {
|
export class RuntimeMetadataResolver {
|
||||||
|
@ -39,12 +52,12 @@ export class RuntimeMetadataResolver {
|
||||||
* Wrap the stringify method to avoid naming things `function (arg1...) {`
|
* Wrap the stringify method to avoid naming things `function (arg1...) {`
|
||||||
*/
|
*/
|
||||||
private sanitizeName(obj: any): string {
|
private sanitizeName(obj: any): string {
|
||||||
let result = stringify(obj);
|
let result = StringWrapper.replaceAll(stringify(obj), /[\s-]/g, '_');
|
||||||
if (result.indexOf('(') < 0) {
|
if (result.indexOf('(') < 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
let found = this._anonymousTypes.get(obj);
|
let found = this._anonymousTypes.get(obj);
|
||||||
if (!found) {
|
if (isBlank(found)) {
|
||||||
this._anonymousTypes.set(obj, this._anonymousTypeIndex++);
|
this._anonymousTypes.set(obj, this._anonymousTypeIndex++);
|
||||||
found = this._anonymousTypes.get(obj);
|
found = this._anonymousTypes.get(obj);
|
||||||
}
|
}
|
||||||
|
@ -58,6 +71,7 @@ export class RuntimeMetadataResolver {
|
||||||
var moduleUrl = null;
|
var moduleUrl = null;
|
||||||
var templateMeta = null;
|
var templateMeta = null;
|
||||||
var changeDetectionStrategy = null;
|
var changeDetectionStrategy = null;
|
||||||
|
var viewProviders = [];
|
||||||
|
|
||||||
if (dirMeta instanceof md.ComponentMetadata) {
|
if (dirMeta instanceof md.ComponentMetadata) {
|
||||||
assertArrayOfStrings('styles', dirMeta.styles);
|
assertArrayOfStrings('styles', dirMeta.styles);
|
||||||
|
@ -73,36 +87,71 @@ export class RuntimeMetadataResolver {
|
||||||
styleUrls: viewMeta.styleUrls
|
styleUrls: viewMeta.styleUrls
|
||||||
});
|
});
|
||||||
changeDetectionStrategy = cmpMeta.changeDetection;
|
changeDetectionStrategy = cmpMeta.changeDetection;
|
||||||
|
if (isPresent(dirMeta.viewProviders)) {
|
||||||
|
viewProviders = this.getProvidersMetadata(dirMeta.viewProviders);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var providers = [];
|
||||||
|
if (isPresent(dirMeta.providers)) {
|
||||||
|
providers = this.getProvidersMetadata(dirMeta.providers);
|
||||||
|
}
|
||||||
|
var queries = [];
|
||||||
|
var viewQueries = [];
|
||||||
|
if (isPresent(dirMeta.queries)) {
|
||||||
|
queries = this.getQueriesMetadata(dirMeta.queries, false);
|
||||||
|
viewQueries = this.getQueriesMetadata(dirMeta.queries, true);
|
||||||
}
|
}
|
||||||
meta = cpl.CompileDirectiveMetadata.create({
|
meta = cpl.CompileDirectiveMetadata.create({
|
||||||
selector: dirMeta.selector,
|
selector: dirMeta.selector,
|
||||||
exportAs: dirMeta.exportAs,
|
exportAs: dirMeta.exportAs,
|
||||||
isComponent: isPresent(templateMeta),
|
isComponent: isPresent(templateMeta),
|
||||||
dynamicLoadable: true,
|
type: this.getTypeMetadata(directiveType, moduleUrl),
|
||||||
type: new cpl.CompileTypeMetadata(
|
|
||||||
{name: this.sanitizeName(directiveType), moduleUrl: moduleUrl, runtime: directiveType}),
|
|
||||||
template: templateMeta,
|
template: templateMeta,
|
||||||
changeDetection: changeDetectionStrategy,
|
changeDetection: changeDetectionStrategy,
|
||||||
inputs: dirMeta.inputs,
|
inputs: dirMeta.inputs,
|
||||||
outputs: dirMeta.outputs,
|
outputs: dirMeta.outputs,
|
||||||
host: dirMeta.host,
|
host: dirMeta.host,
|
||||||
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType))
|
lifecycleHooks:
|
||||||
|
LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType)),
|
||||||
|
providers: providers,
|
||||||
|
viewProviders: viewProviders,
|
||||||
|
queries: queries,
|
||||||
|
viewQueries: viewQueries
|
||||||
});
|
});
|
||||||
this._directiveCache.set(directiveType, meta);
|
this._directiveCache.set(directiveType, meta);
|
||||||
}
|
}
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTypeMetadata(type: Type, moduleUrl: string): cpl.CompileTypeMetadata {
|
||||||
|
return new cpl.CompileTypeMetadata({
|
||||||
|
name: this.sanitizeName(type),
|
||||||
|
moduleUrl: moduleUrl,
|
||||||
|
runtime: type,
|
||||||
|
diDeps: this.getDependenciesMetadata(type, null)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFactoryMetadata(factory: Function, moduleUrl: string): cpl.CompileFactoryMetadata {
|
||||||
|
return new cpl.CompileFactoryMetadata({
|
||||||
|
name: this.sanitizeName(factory),
|
||||||
|
moduleUrl: moduleUrl,
|
||||||
|
runtime: factory,
|
||||||
|
diDeps: this.getDependenciesMetadata(factory, null)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
|
getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
|
||||||
var meta = this._pipeCache.get(pipeType);
|
var meta = this._pipeCache.get(pipeType);
|
||||||
if (isBlank(meta)) {
|
if (isBlank(meta)) {
|
||||||
var pipeMeta = this._pipeResolver.resolve(pipeType);
|
var pipeMeta = this._pipeResolver.resolve(pipeType);
|
||||||
var moduleUrl = reflector.importUri(pipeType);
|
var moduleUrl = reflector.importUri(pipeType);
|
||||||
meta = new cpl.CompilePipeMetadata({
|
meta = new cpl.CompilePipeMetadata({
|
||||||
type: new cpl.CompileTypeMetadata(
|
type: this.getTypeMetadata(pipeType, moduleUrl),
|
||||||
{name: this.sanitizeName(pipeType), moduleUrl: moduleUrl, runtime: pipeType}),
|
|
||||||
name: pipeMeta.name,
|
name: pipeMeta.name,
|
||||||
pure: pipeMeta.pure
|
pure: pipeMeta.pure,
|
||||||
|
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, pipeType)),
|
||||||
});
|
});
|
||||||
this._pipeCache.set(pipeType, meta);
|
this._pipeCache.set(pipeType, meta);
|
||||||
}
|
}
|
||||||
|
@ -133,6 +182,122 @@ export class RuntimeMetadataResolver {
|
||||||
}
|
}
|
||||||
return pipes.map(type => this.getPipeMetadata(type));
|
return pipes.map(type => this.getPipeMetadata(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDependenciesMetadata(typeOrFunc: Type | Function,
|
||||||
|
dependencies: any[]): cpl.CompileDiDependencyMetadata[] {
|
||||||
|
var deps: Dependency[];
|
||||||
|
try {
|
||||||
|
deps = constructDependencies(typeOrFunc, dependencies);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NoAnnotationError) {
|
||||||
|
deps = [];
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deps.map((dep) => {
|
||||||
|
var compileToken;
|
||||||
|
var p = <AttributeMetadata>dep.properties.find(p => p instanceof AttributeMetadata);
|
||||||
|
var isAttribute = false;
|
||||||
|
if (isPresent(p)) {
|
||||||
|
compileToken = this.getTokenMetadata(p.attributeName);
|
||||||
|
isAttribute = true;
|
||||||
|
} else {
|
||||||
|
compileToken = this.getTokenMetadata(dep.key.token);
|
||||||
|
}
|
||||||
|
var compileQuery = null;
|
||||||
|
var q = <dimd.QueryMetadata>dep.properties.find(p => p instanceof dimd.QueryMetadata);
|
||||||
|
if (isPresent(q)) {
|
||||||
|
compileQuery = this.getQueryMetadata(q, null);
|
||||||
|
}
|
||||||
|
return new cpl.CompileDiDependencyMetadata({
|
||||||
|
isAttribute: isAttribute,
|
||||||
|
isHost: dep.upperBoundVisibility instanceof HostMetadata,
|
||||||
|
isSelf: dep.upperBoundVisibility instanceof SelfMetadata,
|
||||||
|
isSkipSelf: dep.lowerBoundVisibility instanceof SkipSelfMetadata,
|
||||||
|
isOptional: dep.optional,
|
||||||
|
query: isPresent(q) && !q.isViewQuery ? compileQuery : null,
|
||||||
|
viewQuery: isPresent(q) && q.isViewQuery ? compileQuery : null,
|
||||||
|
token: compileToken
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getRuntimeIdentifier(value: any): cpl.CompileIdentifierMetadata {
|
||||||
|
return new cpl.CompileIdentifierMetadata({runtime: value, name: this.sanitizeName(value)});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
||||||
|
token = resolveForwardRef(token);
|
||||||
|
var compileToken;
|
||||||
|
if (isString(token)) {
|
||||||
|
compileToken = new cpl.CompileTokenMetadata({value: token});
|
||||||
|
} else {
|
||||||
|
compileToken = new cpl.CompileTokenMetadata({identifier: this.getRuntimeIdentifier(token)});
|
||||||
|
}
|
||||||
|
return compileToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProvidersMetadata(providers: any[]):
|
||||||
|
Array<cpl.CompileProviderMetadata | cpl.CompileTypeMetadata | any[]> {
|
||||||
|
return providers.map((provider) => {
|
||||||
|
provider = resolveForwardRef(provider);
|
||||||
|
if (isArray(provider)) {
|
||||||
|
return this.getProvidersMetadata(provider);
|
||||||
|
} else if (provider instanceof Provider) {
|
||||||
|
return this.getProviderMetadata(provider);
|
||||||
|
} else {
|
||||||
|
return this.getTypeMetadata(provider, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getProviderMetadata(provider: Provider): cpl.CompileProviderMetadata {
|
||||||
|
var compileDeps;
|
||||||
|
if (isPresent(provider.useClass)) {
|
||||||
|
compileDeps = this.getDependenciesMetadata(provider.useClass, provider.dependencies);
|
||||||
|
} else if (isPresent(provider.useFactory)) {
|
||||||
|
compileDeps = this.getDependenciesMetadata(provider.useFactory, provider.dependencies);
|
||||||
|
}
|
||||||
|
return new cpl.CompileProviderMetadata({
|
||||||
|
token: this.getTokenMetadata(provider.token),
|
||||||
|
useClass: isPresent(provider.useClass) ? this.getTypeMetadata(provider.useClass, null) : null,
|
||||||
|
useValue: isPresent(provider.useValue) ? this.getRuntimeIdentifier(provider.useValue) : null,
|
||||||
|
useFactory: isPresent(provider.useFactory) ?
|
||||||
|
this.getFactoryMetadata(provider.useFactory, null) :
|
||||||
|
null,
|
||||||
|
useExisting: isPresent(provider.useExisting) ? this.getTokenMetadata(provider.useExisting) :
|
||||||
|
null,
|
||||||
|
deps: compileDeps,
|
||||||
|
multi: provider.multi
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueriesMetadata(queries: {[key: string]: dimd.QueryMetadata},
|
||||||
|
isViewQuery: boolean): cpl.CompileQueryMetadata[] {
|
||||||
|
var compileQueries = [];
|
||||||
|
StringMapWrapper.forEach(queries, (query, propertyName) => {
|
||||||
|
if (query.isViewQuery === isViewQuery) {
|
||||||
|
compileQueries.push(this.getQueryMetadata(query, propertyName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return compileQueries;
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueryMetadata(q: dimd.QueryMetadata, propertyName: string): cpl.CompileQueryMetadata {
|
||||||
|
var selectors;
|
||||||
|
if (q.isVarBindingQuery) {
|
||||||
|
selectors = q.varBindings.map(varName => this.getTokenMetadata(varName));
|
||||||
|
} else {
|
||||||
|
selectors = [this.getTokenMetadata(q.selector)];
|
||||||
|
}
|
||||||
|
return new cpl.CompileQueryMetadata({
|
||||||
|
selectors: selectors,
|
||||||
|
first: q.first,
|
||||||
|
descendants: q.descendants,
|
||||||
|
propertyName: propertyName
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[] {
|
function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[] {
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
import {StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
var MODULE_REGEXP = /#MODULE\[([^\]]*)\]/g;
|
|
||||||
|
|
||||||
export function moduleRef(moduleUrl): string {
|
|
||||||
return `#MODULE[${moduleUrl}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents generated source code with module references. Internal to the Angular compiler.
|
|
||||||
*/
|
|
||||||
export class SourceModule {
|
|
||||||
static getSourceWithoutImports(sourceWithModuleRefs: string): string {
|
|
||||||
return StringWrapper.replaceAllMapped(sourceWithModuleRefs, MODULE_REGEXP, (match) => '');
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(public moduleUrl: string, public sourceWithModuleRefs: string) {}
|
|
||||||
|
|
||||||
getSourceWithImports(): SourceWithImports {
|
|
||||||
var moduleAliases = {};
|
|
||||||
var imports: string[][] = [];
|
|
||||||
var newSource =
|
|
||||||
StringWrapper.replaceAllMapped(this.sourceWithModuleRefs, MODULE_REGEXP, (match) => {
|
|
||||||
var moduleUrl = match[1];
|
|
||||||
var alias = moduleAliases[moduleUrl];
|
|
||||||
if (isBlank(alias)) {
|
|
||||||
if (moduleUrl == this.moduleUrl) {
|
|
||||||
alias = '';
|
|
||||||
} else {
|
|
||||||
alias = `import${imports.length}`;
|
|
||||||
imports.push([moduleUrl, alias]);
|
|
||||||
}
|
|
||||||
moduleAliases[moduleUrl] = alias;
|
|
||||||
}
|
|
||||||
return alias.length > 0 ? `${alias}.` : '';
|
|
||||||
});
|
|
||||||
return new SourceWithImports(newSource, imports);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SourceExpression {
|
|
||||||
constructor(public declarations: string[], public expression: string) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SourceExpressions {
|
|
||||||
constructor(public declarations: string[], public expressions: string[]) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents generated source code with imports. Internal to the Angular compiler.
|
|
||||||
*/
|
|
||||||
export class SourceWithImports {
|
|
||||||
constructor(public source: string, public imports: string[][]) {}
|
|
||||||
}
|
|
|
@ -1,106 +1,77 @@
|
||||||
import {CompileTypeMetadata, CompileTemplateMetadata} from './directive_metadata';
|
import {
|
||||||
import {SourceModule, SourceExpression, moduleRef} from './source_module';
|
CompileTemplateMetadata,
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompileDirectiveMetadata
|
||||||
|
} from './compile_metadata';
|
||||||
|
import * as o from './output/output_ast';
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||||
import {XHR} from 'angular2/src/compiler/xhr';
|
|
||||||
import {IS_DART, StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
|
||||||
import {ShadowCss} from 'angular2/src/compiler/shadow_css';
|
import {ShadowCss} from 'angular2/src/compiler/shadow_css';
|
||||||
import {UrlResolver} from 'angular2/src/compiler/url_resolver';
|
import {UrlResolver} from 'angular2/src/compiler/url_resolver';
|
||||||
import {extractStyleUrls} from './style_url_resolver';
|
import {extractStyleUrls} from './style_url_resolver';
|
||||||
import {
|
|
||||||
escapeSingleQuoteString,
|
|
||||||
codeGenExportVariable,
|
|
||||||
codeGenToString,
|
|
||||||
MODULE_SUFFIX
|
|
||||||
} from './util';
|
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
const COMPONENT_VARIABLE = '%COMP%';
|
const COMPONENT_VARIABLE = '%COMP%';
|
||||||
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||||
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||||
|
|
||||||
|
export class StylesCompileDependency {
|
||||||
|
constructor(public sourceUrl: string, public isShimmed: boolean,
|
||||||
|
public valuePlaceholder: CompileIdentifierMetadata) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StylesCompileResult {
|
||||||
|
constructor(public statements: o.Statement[], public stylesVar: string,
|
||||||
|
public dependencies: StylesCompileDependency[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StyleCompiler {
|
export class StyleCompiler {
|
||||||
private _styleCache: Map<string, Promise<string[]>> = new Map<string, Promise<string[]>>();
|
|
||||||
private _shadowCss: ShadowCss = new ShadowCss();
|
private _shadowCss: ShadowCss = new ShadowCss();
|
||||||
|
|
||||||
constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {}
|
constructor(private _urlResolver: UrlResolver) {}
|
||||||
|
|
||||||
compileComponentRuntime(template: CompileTemplateMetadata): Promise<Array<string | any[]>> {
|
compileComponent(comp: CompileDirectiveMetadata): StylesCompileResult {
|
||||||
var styles = template.styles;
|
var shim = comp.template.encapsulation === ViewEncapsulation.Emulated;
|
||||||
var styleAbsUrls = template.styleUrls;
|
return this._compileStyles(getStylesVarName(comp), comp.template.styles,
|
||||||
return this._loadStyles(styles, styleAbsUrls,
|
comp.template.styleUrls, shim);
|
||||||
template.encapsulation === ViewEncapsulation.Emulated);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(template: CompileTemplateMetadata): SourceExpression {
|
compileStylesheet(stylesheetUrl: string, cssText: string,
|
||||||
var shim = template.encapsulation === ViewEncapsulation.Emulated;
|
isShimmed: boolean): StylesCompileResult {
|
||||||
return this._styleCodeGen(template.styles, template.styleUrls, shim);
|
|
||||||
}
|
|
||||||
|
|
||||||
compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] {
|
|
||||||
var styleWithImports = extractStyleUrls(this._urlResolver, stylesheetUrl, cssText);
|
var styleWithImports = extractStyleUrls(this._urlResolver, stylesheetUrl, cssText);
|
||||||
return [
|
return this._compileStyles(getStylesVarName(null), [styleWithImports.style],
|
||||||
this._styleModule(
|
styleWithImports.styleUrls, isShimmed);
|
||||||
stylesheetUrl, false,
|
|
||||||
this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false)),
|
|
||||||
this._styleModule(stylesheetUrl, true, this._styleCodeGen([styleWithImports.style],
|
|
||||||
styleWithImports.styleUrls, true))
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() { this._styleCache.clear(); }
|
private _compileStyles(stylesVar: string, plainStyles: string[], absUrls: string[],
|
||||||
|
shim: boolean): StylesCompileResult {
|
||||||
private _loadStyles(plainStyles: string[], absUrls: string[],
|
var styleExpressions =
|
||||||
encapsulate: boolean): Promise<Array<string | any[]>> {
|
plainStyles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim)));
|
||||||
var promises: Promise<string[]>[] = absUrls.map((absUrl: string): Promise<string[]> => {
|
var dependencies = [];
|
||||||
var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`;
|
|
||||||
var result: Promise<string[]> = this._styleCache.get(cacheKey);
|
|
||||||
if (isBlank(result)) {
|
|
||||||
result = this._xhr.get(absUrl).then((style) => {
|
|
||||||
var styleWithImports = extractStyleUrls(this._urlResolver, absUrl, style);
|
|
||||||
return this._loadStyles([styleWithImports.style], styleWithImports.styleUrls,
|
|
||||||
encapsulate);
|
|
||||||
});
|
|
||||||
this._styleCache.set(cacheKey, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
return PromiseWrapper.all<string[]>(promises).then((nestedStyles: string[][]) => {
|
|
||||||
var result: Array<string | any[]> =
|
|
||||||
plainStyles.map(plainStyle => this._shimIfNeeded(plainStyle, encapsulate));
|
|
||||||
nestedStyles.forEach(styles => result.push(styles));
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _styleCodeGen(plainStyles: string[], absUrls: string[], shim: boolean): SourceExpression {
|
|
||||||
var arrayPrefix = IS_DART ? `const` : '';
|
|
||||||
var styleExpressions = plainStyles.map(
|
|
||||||
plainStyle => escapeSingleQuoteString(this._shimIfNeeded(plainStyle, shim)));
|
|
||||||
|
|
||||||
for (var i = 0; i < absUrls.length; i++) {
|
for (var i = 0; i < absUrls.length; i++) {
|
||||||
var moduleUrl = this._createModuleUrl(absUrls[i], shim);
|
var identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)});
|
||||||
styleExpressions.push(`${moduleRef(moduleUrl)}STYLES`);
|
dependencies.push(new StylesCompileDependency(absUrls[i], shim, identifier));
|
||||||
|
styleExpressions.push(new o.ExternalExpr(identifier));
|
||||||
}
|
}
|
||||||
var expressionSource = `${arrayPrefix} [${styleExpressions.join(',')}]`;
|
// styles variable contains plain strings and arrays of other styles arrays (recursive),
|
||||||
return new SourceExpression([], expressionSource);
|
// so we set its type to dynamic.
|
||||||
}
|
var stmt = o.variable(stylesVar)
|
||||||
|
.set(o.literalArr(styleExpressions,
|
||||||
private _styleModule(stylesheetUrl: string, shim: boolean,
|
new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])))
|
||||||
expression: SourceExpression): SourceModule {
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||||
var moduleSource = `
|
return new StylesCompileResult([stmt], stylesVar, dependencies);
|
||||||
${expression.declarations.join('\n')}
|
|
||||||
${codeGenExportVariable('STYLES')}${expression.expression};
|
|
||||||
`;
|
|
||||||
return new SourceModule(this._createModuleUrl(stylesheetUrl, shim), moduleSource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _shimIfNeeded(style: string, shim: boolean): string {
|
private _shimIfNeeded(style: string, shim: boolean): string {
|
||||||
return shim ? this._shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR) : style;
|
return shim ? this._shadowCss.shimCssText(style, CONTENT_ATTR, HOST_ATTR) : style;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private _createModuleUrl(stylesheetUrl: string, shim: boolean): string {
|
|
||||||
return shim ? `${stylesheetUrl}.shim${MODULE_SUFFIX}` : `${stylesheetUrl}${MODULE_SUFFIX}`;
|
function getStylesVarName(component: CompileDirectiveMetadata): string {
|
||||||
}
|
var result = `styles`;
|
||||||
|
if (isPresent(component)) {
|
||||||
|
result += `_${component.type.name}`;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
import {AST} from 'angular2/src/core/change_detection/change_detection';
|
import {AST} from './expression_parser/ast';
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
import {CompileDirectiveMetadata} from './directive_metadata';
|
import {
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileTokenMetadata,
|
||||||
|
CompileProviderMetadata,
|
||||||
|
CompileTokenMap,
|
||||||
|
CompileQueryMetadata
|
||||||
|
} from './compile_metadata';
|
||||||
import {ParseSourceSpan} from './parse_util';
|
import {ParseSourceSpan} from './parse_util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,8 +98,9 @@ export class ElementAst implements TemplateAst {
|
||||||
constructor(public name: string, public attrs: AttrAst[],
|
constructor(public name: string, public attrs: AttrAst[],
|
||||||
public inputs: BoundElementPropertyAst[], public outputs: BoundEventAst[],
|
public inputs: BoundElementPropertyAst[], public outputs: BoundEventAst[],
|
||||||
public exportAsVars: VariableAst[], public directives: DirectiveAst[],
|
public exportAsVars: VariableAst[], public directives: DirectiveAst[],
|
||||||
public children: TemplateAst[], public ngContentIndex: number,
|
public providers: ProviderAst[], public children: TemplateAst[],
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitElement(this, context);
|
return visitor.visitElement(this, context);
|
||||||
}
|
}
|
||||||
|
@ -121,8 +128,10 @@ export class ElementAst implements TemplateAst {
|
||||||
*/
|
*/
|
||||||
export class EmbeddedTemplateAst implements TemplateAst {
|
export class EmbeddedTemplateAst implements TemplateAst {
|
||||||
constructor(public attrs: AttrAst[], public outputs: BoundEventAst[], public vars: VariableAst[],
|
constructor(public attrs: AttrAst[], public outputs: BoundEventAst[], public vars: VariableAst[],
|
||||||
public directives: DirectiveAst[], public children: TemplateAst[],
|
public directives: DirectiveAst[], public providers: ProviderAst[],
|
||||||
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
|
public children: TemplateAst[], public ngContentIndex: number,
|
||||||
|
public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitEmbeddedTemplate(this, context);
|
return visitor.visitEmbeddedTemplate(this, context);
|
||||||
}
|
}
|
||||||
|
@ -152,6 +161,28 @@ export class DirectiveAst implements TemplateAst {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A provider declared on an element
|
||||||
|
*/
|
||||||
|
export class ProviderAst implements TemplateAst {
|
||||||
|
constructor(public token: CompileTokenMetadata, public multiProvider: boolean,
|
||||||
|
public eager: boolean, public providers: CompileProviderMetadata[],
|
||||||
|
public providerType: ProviderAstType, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
|
// No visit method in the visitor for now...
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProviderAstType {
|
||||||
|
PublicService,
|
||||||
|
PrivateService,
|
||||||
|
Component,
|
||||||
|
Directive,
|
||||||
|
Builtin
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position where content is to be projected (instance of `<ng-content>` in a template).
|
* Position where content is to be projected (instance of `<ng-content>` in a template).
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,456 +0,0 @@
|
||||||
import {
|
|
||||||
IS_DART,
|
|
||||||
Type,
|
|
||||||
Json,
|
|
||||||
isBlank,
|
|
||||||
isPresent,
|
|
||||||
stringify,
|
|
||||||
evalExpression
|
|
||||||
} from 'angular2/src/facade/lang';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
import {
|
|
||||||
ListWrapper,
|
|
||||||
SetWrapper,
|
|
||||||
MapWrapper,
|
|
||||||
StringMapWrapper
|
|
||||||
} from 'angular2/src/facade/collection';
|
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
|
||||||
import {
|
|
||||||
createHostComponentMeta,
|
|
||||||
CompileDirectiveMetadata,
|
|
||||||
CompileTypeMetadata,
|
|
||||||
CompileTemplateMetadata,
|
|
||||||
CompilePipeMetadata,
|
|
||||||
CompileMetadataWithType
|
|
||||||
} from './directive_metadata';
|
|
||||||
import {
|
|
||||||
TemplateAst,
|
|
||||||
TemplateAstVisitor,
|
|
||||||
NgContentAst,
|
|
||||||
EmbeddedTemplateAst,
|
|
||||||
ElementAst,
|
|
||||||
VariableAst,
|
|
||||||
BoundEventAst,
|
|
||||||
BoundElementPropertyAst,
|
|
||||||
AttrAst,
|
|
||||||
BoundTextAst,
|
|
||||||
TextAst,
|
|
||||||
DirectiveAst,
|
|
||||||
BoundDirectivePropertyAst,
|
|
||||||
templateVisitAll
|
|
||||||
} from './template_ast';
|
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
|
||||||
import {SourceModule, moduleRef, SourceExpression} from './source_module';
|
|
||||||
import {ChangeDetectionCompiler, CHANGE_DETECTION_JIT_IMPORTS} from './change_detector_compiler';
|
|
||||||
import {StyleCompiler} from './style_compiler';
|
|
||||||
import {ViewCompiler, VIEW_JIT_IMPORTS} from './view_compiler';
|
|
||||||
import {
|
|
||||||
ProtoViewCompiler,
|
|
||||||
APP_VIEW_MODULE_REF,
|
|
||||||
CompileProtoView,
|
|
||||||
PROTO_VIEW_JIT_IMPORTS
|
|
||||||
} from './proto_view_compiler';
|
|
||||||
import {TemplateParser, PipeCollector} from './template_parser';
|
|
||||||
import {TemplateNormalizer} from './template_normalizer';
|
|
||||||
import {RuntimeMetadataResolver} from './runtime_metadata';
|
|
||||||
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
|
||||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
|
||||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
|
||||||
|
|
||||||
import {
|
|
||||||
codeGenExportVariable,
|
|
||||||
escapeSingleQuoteString,
|
|
||||||
codeGenValueFn,
|
|
||||||
MODULE_SUFFIX,
|
|
||||||
addAll,
|
|
||||||
Expression
|
|
||||||
} from './util';
|
|
||||||
|
|
||||||
export var METADATA_CACHE_MODULE_REF =
|
|
||||||
moduleRef('package:angular2/src/core/linker/resolved_metadata_cache' + MODULE_SUFFIX);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An internal module of the Angular compiler that begins with component types,
|
|
||||||
* extracts templates, and eventually produces a compiled version of the component
|
|
||||||
* ready for linking into an application.
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class TemplateCompiler {
|
|
||||||
private _hostCacheKeys = new Map<Type, any>();
|
|
||||||
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
|
||||||
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
|
|
||||||
|
|
||||||
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
|
||||||
private _templateNormalizer: TemplateNormalizer,
|
|
||||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
|
||||||
private _cdCompiler: ChangeDetectionCompiler,
|
|
||||||
private _protoViewCompiler: ProtoViewCompiler, private _viewCompiler: ViewCompiler,
|
|
||||||
private _resolvedMetadataCache: ResolvedMetadataCache,
|
|
||||||
private _genConfig: ChangeDetectorGenConfig) {}
|
|
||||||
|
|
||||||
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
|
||||||
Promise<CompileDirectiveMetadata> {
|
|
||||||
if (!directive.isComponent) {
|
|
||||||
// For non components there is nothing to be normalized yet.
|
|
||||||
return PromiseWrapper.resolve(directive);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._templateNormalizer.normalizeTemplate(directive.type, directive.template)
|
|
||||||
.then((normalizedTemplate: CompileTemplateMetadata) => new CompileDirectiveMetadata({
|
|
||||||
type: directive.type,
|
|
||||||
isComponent: directive.isComponent,
|
|
||||||
dynamicLoadable: directive.dynamicLoadable,
|
|
||||||
selector: directive.selector,
|
|
||||||
exportAs: directive.exportAs,
|
|
||||||
changeDetection: directive.changeDetection,
|
|
||||||
inputs: directive.inputs,
|
|
||||||
outputs: directive.outputs,
|
|
||||||
hostListeners: directive.hostListeners,
|
|
||||||
hostProperties: directive.hostProperties,
|
|
||||||
hostAttributes: directive.hostAttributes,
|
|
||||||
lifecycleHooks: directive.lifecycleHooks,
|
|
||||||
providers: directive.providers,
|
|
||||||
viewProviders: directive.viewProviders,
|
|
||||||
queries: directive.queries,
|
|
||||||
viewQueries: directive.viewQueries,
|
|
||||||
template: normalizedTemplate
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
compileHostComponentRuntime(type: Type): Promise<HostViewFactory> {
|
|
||||||
var compMeta: CompileDirectiveMetadata =
|
|
||||||
this._runtimeMetadataResolver.getDirectiveMetadata(type);
|
|
||||||
var hostCacheKey = this._hostCacheKeys.get(type);
|
|
||||||
if (isBlank(hostCacheKey)) {
|
|
||||||
hostCacheKey = new Object();
|
|
||||||
this._hostCacheKeys.set(type, hostCacheKey);
|
|
||||||
assertComponent(compMeta);
|
|
||||||
var hostMeta: CompileDirectiveMetadata =
|
|
||||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
|
||||||
|
|
||||||
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], [], []);
|
|
||||||
}
|
|
||||||
return this._compiledTemplateDone.get(hostCacheKey)
|
|
||||||
.then((compiledTemplate: CompiledTemplate) =>
|
|
||||||
new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCache() {
|
|
||||||
this._styleCompiler.clearCache();
|
|
||||||
this._compiledTemplateCache.clear();
|
|
||||||
this._compiledTemplateDone.clear();
|
|
||||||
this._hostCacheKeys.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
|
||||||
if (components.length === 0) {
|
|
||||||
throw new BaseException('No components given');
|
|
||||||
}
|
|
||||||
var declarations = [];
|
|
||||||
components.forEach(componentWithDirs => {
|
|
||||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
|
||||||
assertComponent(compMeta);
|
|
||||||
this._compileComponentCodeGen(compMeta, componentWithDirs.directives, componentWithDirs.pipes,
|
|
||||||
declarations);
|
|
||||||
if (compMeta.dynamicLoadable) {
|
|
||||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
|
||||||
var viewFactoryExpression =
|
|
||||||
this._compileComponentCodeGen(hostMeta, [compMeta], [], declarations);
|
|
||||||
var constructionKeyword = IS_DART ? 'const' : 'new';
|
|
||||||
var compiledTemplateExpr =
|
|
||||||
`${constructionKeyword} ${APP_VIEW_MODULE_REF}HostViewFactory('${compMeta.selector}',${viewFactoryExpression})`;
|
|
||||||
var varName = codeGenHostViewFactoryName(compMeta.type);
|
|
||||||
declarations.push(`${codeGenExportVariable(varName)}${compiledTemplateExpr};`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var moduleUrl = components[0].component.type.moduleUrl;
|
|
||||||
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] {
|
|
||||||
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
|
|
||||||
viewDirectives: CompileDirectiveMetadata[],
|
|
||||||
pipes: CompilePipeMetadata[],
|
|
||||||
compilingComponentsPath: any[]): CompiledTemplate {
|
|
||||||
let uniqViewDirectives = <CompileDirectiveMetadata[]>removeDuplicates(viewDirectives);
|
|
||||||
let uniqViewPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
|
|
||||||
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
|
||||||
var done = this._compiledTemplateDone.get(cacheKey);
|
|
||||||
if (isBlank(compiledTemplate)) {
|
|
||||||
compiledTemplate = new CompiledTemplate();
|
|
||||||
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
|
||||||
done = PromiseWrapper
|
|
||||||
.all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
|
|
||||||
uniqViewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
|
||||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
|
||||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
|
||||||
var styles = stylesAndNormalizedViewDirMetas[0];
|
|
||||||
var parsedTemplate = this._templateParser.parse(
|
|
||||||
compMeta.template.template, normalizedViewDirMetas, uniqViewPipes,
|
|
||||||
compMeta.type.name);
|
|
||||||
|
|
||||||
var childPromises = [];
|
|
||||||
var usedDirectives = DirectiveCollector.findUsedDirectives(parsedTemplate);
|
|
||||||
usedDirectives.components.forEach(
|
|
||||||
component => this._compileNestedComponentRuntime(
|
|
||||||
component, compilingComponentsPath, childPromises));
|
|
||||||
return PromiseWrapper.all(childPromises)
|
|
||||||
.then((_) => {
|
|
||||||
var filteredPipes = filterPipes(parsedTemplate, uniqViewPipes);
|
|
||||||
compiledTemplate.init(this._createViewFactoryRuntime(
|
|
||||||
compMeta, parsedTemplate, usedDirectives.directives, styles,
|
|
||||||
filteredPipes));
|
|
||||||
return compiledTemplate;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this._compiledTemplateDone.set(cacheKey, done);
|
|
||||||
}
|
|
||||||
return compiledTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _compileNestedComponentRuntime(childComponentDir: CompileDirectiveMetadata,
|
|
||||||
parentCompilingComponentsPath: any[],
|
|
||||||
childPromises: Promise<any>[]) {
|
|
||||||
var compilingComponentsPath = ListWrapper.clone(parentCompilingComponentsPath);
|
|
||||||
|
|
||||||
var childCacheKey = childComponentDir.type.runtime;
|
|
||||||
var childViewDirectives: CompileDirectiveMetadata[] =
|
|
||||||
this._runtimeMetadataResolver.getViewDirectivesMetadata(childComponentDir.type.runtime);
|
|
||||||
var childViewPipes: CompilePipeMetadata[] =
|
|
||||||
this._runtimeMetadataResolver.getViewPipesMetadata(childComponentDir.type.runtime);
|
|
||||||
var childIsRecursive = ListWrapper.contains(compilingComponentsPath, childCacheKey);
|
|
||||||
compilingComponentsPath.push(childCacheKey);
|
|
||||||
this._compileComponentRuntime(childCacheKey, childComponentDir, childViewDirectives,
|
|
||||||
childViewPipes, compilingComponentsPath);
|
|
||||||
if (!childIsRecursive) {
|
|
||||||
// Only wait for a child if it is not a cycle
|
|
||||||
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createViewFactoryRuntime(compMeta: CompileDirectiveMetadata,
|
|
||||||
parsedTemplate: TemplateAst[],
|
|
||||||
directives: CompileDirectiveMetadata[], styles: string[],
|
|
||||||
pipes: CompilePipeMetadata[]): Function {
|
|
||||||
if (IS_DART || !this._genConfig.useJit) {
|
|
||||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
|
||||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
|
||||||
var protoViews = this._protoViewCompiler.compileProtoViewRuntime(
|
|
||||||
this._resolvedMetadataCache, compMeta, parsedTemplate, pipes);
|
|
||||||
return this._viewCompiler.compileComponentRuntime(
|
|
||||||
compMeta, parsedTemplate, styles, protoViews.protoViews, changeDetectorFactories,
|
|
||||||
(compMeta) => this._getNestedComponentViewFactory(compMeta));
|
|
||||||
} else {
|
|
||||||
var declarations = [];
|
|
||||||
var viewFactoryExpr = this._createViewFactoryCodeGen('resolvedMetadataCache', compMeta,
|
|
||||||
new SourceExpression([], 'styles'),
|
|
||||||
parsedTemplate, pipes, declarations);
|
|
||||||
var vars: {[key: string]: any} =
|
|
||||||
{'exports': {}, 'styles': styles, 'resolvedMetadataCache': this._resolvedMetadataCache};
|
|
||||||
directives.forEach(dirMeta => {
|
|
||||||
vars[dirMeta.type.name] = dirMeta.type.runtime;
|
|
||||||
if (dirMeta.isComponent && dirMeta.type.runtime !== compMeta.type.runtime) {
|
|
||||||
vars[`viewFactory_${dirMeta.type.name}0`] = this._getNestedComponentViewFactory(dirMeta);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
pipes.forEach(pipeMeta => vars[pipeMeta.type.name] = pipeMeta.type.runtime);
|
|
||||||
var declarationsWithoutImports =
|
|
||||||
SourceModule.getSourceWithoutImports(declarations.join('\n'));
|
|
||||||
return evalExpression(
|
|
||||||
`viewFactory_${compMeta.type.name}`, viewFactoryExpr, declarationsWithoutImports,
|
|
||||||
mergeStringMaps(
|
|
||||||
[vars, CHANGE_DETECTION_JIT_IMPORTS, PROTO_VIEW_JIT_IMPORTS, VIEW_JIT_IMPORTS]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getNestedComponentViewFactory(compMeta: CompileDirectiveMetadata): Function {
|
|
||||||
return this._compiledTemplateCache.get(compMeta.type.runtime).viewFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _compileComponentCodeGen(compMeta: CompileDirectiveMetadata,
|
|
||||||
directives: CompileDirectiveMetadata[],
|
|
||||||
pipes: CompilePipeMetadata[],
|
|
||||||
targetDeclarations: string[]): string {
|
|
||||||
let uniqueDirectives = <CompileDirectiveMetadata[]>removeDuplicates(directives);
|
|
||||||
let uniqPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
|
|
||||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
|
|
||||||
var parsedTemplate = this._templateParser.parse(compMeta.template.template, uniqueDirectives,
|
|
||||||
uniqPipes, compMeta.type.name);
|
|
||||||
var filteredPipes = filterPipes(parsedTemplate, uniqPipes);
|
|
||||||
return this._createViewFactoryCodeGen(
|
|
||||||
`${METADATA_CACHE_MODULE_REF}CODEGEN_RESOLVED_METADATA_CACHE`, compMeta, styleExpr,
|
|
||||||
parsedTemplate, filteredPipes, targetDeclarations);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createViewFactoryCodeGen(resolvedMetadataCacheExpr: string,
|
|
||||||
compMeta: CompileDirectiveMetadata, styleExpr: SourceExpression,
|
|
||||||
parsedTemplate: TemplateAst[], pipes: CompilePipeMetadata[],
|
|
||||||
targetDeclarations: string[]): string {
|
|
||||||
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
|
||||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
|
||||||
var protoViewExprs = this._protoViewCompiler.compileProtoViewCodeGen(
|
|
||||||
new Expression(resolvedMetadataCacheExpr), compMeta, parsedTemplate, pipes);
|
|
||||||
var viewFactoryExpr = this._viewCompiler.compileComponentCodeGen(
|
|
||||||
compMeta, parsedTemplate, styleExpr, protoViewExprs.protoViews, changeDetectorsExprs,
|
|
||||||
codeGenComponentViewFactoryName);
|
|
||||||
|
|
||||||
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
|
||||||
addAll(protoViewExprs.declarations, targetDeclarations);
|
|
||||||
addAll(viewFactoryExpr.declarations, targetDeclarations);
|
|
||||||
|
|
||||||
return viewFactoryExpr.expression;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NormalizedComponentWithViewDirectives {
|
|
||||||
constructor(public component: CompileDirectiveMetadata,
|
|
||||||
public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CompiledTemplate {
|
|
||||||
viewFactory: Function = null;
|
|
||||||
init(viewFactory: Function) { this.viewFactory = viewFactory; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
|
||||||
if (!meta.isComponent) {
|
|
||||||
throw new BaseException(`Could not compile '${meta.type.name}' because it is not a component.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function templateModuleUrl(moduleUrl: string): string {
|
|
||||||
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
|
||||||
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function codeGenHostViewFactoryName(type: CompileTypeMetadata): string {
|
|
||||||
return `hostViewFactory_${type.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenComponentViewFactoryName(nestedCompType: CompileDirectiveMetadata): string {
|
|
||||||
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}viewFactory_${nestedCompType.type.name}0`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeStringMaps(maps: Array<{[key: string]: any}>): {[key: string]: any} {
|
|
||||||
var result = {};
|
|
||||||
maps.forEach(
|
|
||||||
(map) => { StringMapWrapper.forEach(map, (value, key) => { result[key] = value; }); });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeDuplicates(items: CompileMetadataWithType[]): CompileMetadataWithType[] {
|
|
||||||
let res = [];
|
|
||||||
items.forEach(item => {
|
|
||||||
let hasMatch =
|
|
||||||
res.filter(r => r.type.name == item.type.name && r.type.moduleUrl == item.type.moduleUrl &&
|
|
||||||
r.type.runtime == item.type.runtime)
|
|
||||||
.length > 0;
|
|
||||||
if (!hasMatch) {
|
|
||||||
res.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
class DirectiveCollector implements TemplateAstVisitor {
|
|
||||||
static findUsedDirectives(parsedTemplate: TemplateAst[]): DirectiveCollector {
|
|
||||||
var collector = new DirectiveCollector();
|
|
||||||
templateVisitAll(collector, parsedTemplate);
|
|
||||||
return collector;
|
|
||||||
}
|
|
||||||
|
|
||||||
directives: CompileDirectiveMetadata[] = [];
|
|
||||||
components: CompileDirectiveMetadata[] = [];
|
|
||||||
|
|
||||||
visitBoundText(ast: BoundTextAst, context: any): any { return null; }
|
|
||||||
visitText(ast: TextAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
|
||||||
templateVisitAll(this, ast.directives);
|
|
||||||
templateVisitAll(this, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
|
||||||
templateVisitAll(this, ast.directives);
|
|
||||||
templateVisitAll(this, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
|
||||||
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any { return null; }
|
|
||||||
visitDirective(ast: DirectiveAst, ctx: any): any {
|
|
||||||
if (ast.directive.isComponent) {
|
|
||||||
this.components.push(ast.directive);
|
|
||||||
}
|
|
||||||
this.directives.push(ast.directive);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
|
||||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function filterPipes(template: TemplateAst[],
|
|
||||||
allPipes: CompilePipeMetadata[]): CompilePipeMetadata[] {
|
|
||||||
var visitor = new PipeVisitor();
|
|
||||||
templateVisitAll(visitor, template);
|
|
||||||
return allPipes.filter((pipeMeta) => SetWrapper.has(visitor.collector.pipes, pipeMeta.name));
|
|
||||||
}
|
|
||||||
|
|
||||||
class PipeVisitor implements TemplateAstVisitor {
|
|
||||||
collector: PipeCollector = new PipeCollector();
|
|
||||||
|
|
||||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
|
||||||
ast.value.visit(this.collector);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitText(ast: TextAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
|
||||||
|
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
|
||||||
templateVisitAll(this, ast.inputs);
|
|
||||||
templateVisitAll(this, ast.outputs);
|
|
||||||
templateVisitAll(this, ast.directives);
|
|
||||||
templateVisitAll(this, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
|
||||||
templateVisitAll(this, ast.outputs);
|
|
||||||
templateVisitAll(this, ast.directives);
|
|
||||||
templateVisitAll(this, ast.children);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
|
||||||
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any { return null; }
|
|
||||||
visitDirective(ast: DirectiveAst, ctx: any): any {
|
|
||||||
templateVisitAll(this, ast.inputs);
|
|
||||||
templateVisitAll(this, ast.hostEvents);
|
|
||||||
templateVisitAll(this, ast.hostProperties);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
|
||||||
ast.handler.visit(this.collector);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
|
|
||||||
ast.value.visit(this.collector);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {
|
|
||||||
ast.value.visit(this.collector);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,35 @@
|
||||||
import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
import {
|
||||||
import {RegExpWrapper, isPresent, StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
ListWrapper,
|
||||||
|
StringMapWrapper,
|
||||||
|
SetWrapper,
|
||||||
|
MapWrapper
|
||||||
|
} from 'angular2/src/facade/collection';
|
||||||
|
import {RegExpWrapper, isPresent, StringWrapper, isBlank, isArray} from 'angular2/src/facade/lang';
|
||||||
import {Injectable, Inject, OpaqueToken, Optional} from 'angular2/core';
|
import {Injectable, Inject, OpaqueToken, Optional} from 'angular2/core';
|
||||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
import {
|
||||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
AST,
|
||||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
|
Interpolation,
|
||||||
|
ASTWithSource,
|
||||||
|
TemplateBinding,
|
||||||
|
RecursiveAstVisitor,
|
||||||
|
BindingPipe
|
||||||
|
} from './expression_parser/ast';
|
||||||
|
import {Parser} from './expression_parser/parser';
|
||||||
|
import {
|
||||||
|
CompileTokenMap,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompilePipeMetadata,
|
||||||
|
CompileMetadataWithType,
|
||||||
|
CompileProviderMetadata,
|
||||||
|
CompileTokenMetadata,
|
||||||
|
CompileTypeMetadata
|
||||||
|
} from './compile_metadata';
|
||||||
import {HtmlParser} from './html_parser';
|
import {HtmlParser} from './html_parser';
|
||||||
import {splitNsName, mergeNsAndName} from './html_tags';
|
import {splitNsName, mergeNsAndName} from './html_tags';
|
||||||
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
||||||
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
|
import {MAX_INTERPOLATION_VALUES} from 'angular2/src/core/linker/view_utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ElementAst,
|
ElementAst,
|
||||||
|
@ -26,7 +46,9 @@ import {
|
||||||
NgContentAst,
|
NgContentAst,
|
||||||
PropertyBindingType,
|
PropertyBindingType,
|
||||||
DirectiveAst,
|
DirectiveAst,
|
||||||
BoundDirectivePropertyAst
|
BoundDirectivePropertyAst,
|
||||||
|
ProviderAst,
|
||||||
|
ProviderAstType
|
||||||
} from './template_ast';
|
} from './template_ast';
|
||||||
import {CssSelector, SelectorMatcher} from 'angular2/src/compiler/selector';
|
import {CssSelector, SelectorMatcher} from 'angular2/src/compiler/selector';
|
||||||
|
|
||||||
|
@ -47,6 +69,8 @@ import {
|
||||||
|
|
||||||
import {splitAtColon} from './util';
|
import {splitAtColon} from './util';
|
||||||
|
|
||||||
|
import {ProviderElementContext, ProviderViewContext} from './provider_parser';
|
||||||
|
|
||||||
// Group 1 = "bind-"
|
// Group 1 = "bind-"
|
||||||
// Group 2 = "var-" or "#"
|
// Group 2 = "var-" or "#"
|
||||||
// Group 3 = "on-"
|
// Group 3 = "on-"
|
||||||
|
@ -93,9 +117,10 @@ export class TemplateParser {
|
||||||
private _htmlParser: HtmlParser,
|
private _htmlParser: HtmlParser,
|
||||||
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
||||||
|
|
||||||
parse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
parse(component: CompileDirectiveMetadata, template: string,
|
||||||
|
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
templateUrl: string): TemplateAst[] {
|
templateUrl: string): TemplateAst[] {
|
||||||
var result = this.tryParse(template, directives, pipes, templateUrl);
|
var result = this.tryParse(component, template, directives, pipes, templateUrl);
|
||||||
if (isPresent(result.errors)) {
|
if (isPresent(result.errors)) {
|
||||||
var errorString = result.errors.join('\n');
|
var errorString = result.errors.join('\n');
|
||||||
throw new BaseException(`Template parse errors:\n${errorString}`);
|
throw new BaseException(`Template parse errors:\n${errorString}`);
|
||||||
|
@ -103,13 +128,25 @@ export class TemplateParser {
|
||||||
return result.templateAst;
|
return result.templateAst;
|
||||||
}
|
}
|
||||||
|
|
||||||
tryParse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
tryParse(component: CompileDirectiveMetadata, template: string,
|
||||||
|
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
templateUrl: string): TemplateParseResult {
|
templateUrl: string): TemplateParseResult {
|
||||||
var parseVisitor =
|
|
||||||
new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry);
|
|
||||||
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
|
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
|
||||||
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
|
var errors: ParseError[] = htmlAstWithErrors.errors;
|
||||||
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
|
var result;
|
||||||
|
if (htmlAstWithErrors.rootNodes.length > 0) {
|
||||||
|
var uniqDirectives = <CompileDirectiveMetadata[]>removeDuplicates(directives);
|
||||||
|
var uniqPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
|
||||||
|
var providerViewContext =
|
||||||
|
new ProviderViewContext(component, htmlAstWithErrors.rootNodes[0].sourceSpan);
|
||||||
|
var parseVisitor = new TemplateParseVisitor(providerViewContext, uniqDirectives, uniqPipes,
|
||||||
|
this._exprParser, this._schemaRegistry);
|
||||||
|
|
||||||
|
result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
||||||
|
errors = errors.concat(parseVisitor.errors).concat(providerViewContext.errors);
|
||||||
|
} else {
|
||||||
|
result = [];
|
||||||
|
}
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return new TemplateParseResult(result, errors);
|
return new TemplateParseResult(result, errors);
|
||||||
}
|
}
|
||||||
|
@ -128,7 +165,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
ngContentCount: number = 0;
|
ngContentCount: number = 0;
|
||||||
pipesByName: Map<string, CompilePipeMetadata>;
|
pipesByName: Map<string, CompilePipeMetadata>;
|
||||||
|
|
||||||
constructor(directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
constructor(public providerViewContext: ProviderViewContext,
|
||||||
|
directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {
|
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {
|
||||||
this.selectorMatcher = new SelectorMatcher();
|
this.selectorMatcher = new SelectorMatcher();
|
||||||
ListWrapper.forEachWithIndex(directives,
|
ListWrapper.forEachWithIndex(directives,
|
||||||
|
@ -150,6 +188,11 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
try {
|
try {
|
||||||
var ast = this._exprParser.parseInterpolation(value, sourceInfo);
|
var ast = this._exprParser.parseInterpolation(value, sourceInfo);
|
||||||
this._checkPipes(ast, sourceSpan);
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
if (isPresent(ast) &&
|
||||||
|
(<Interpolation>ast.ast).expressions.length > MAX_INTERPOLATION_VALUES) {
|
||||||
|
throw new BaseException(
|
||||||
|
`Only support at most ${MAX_INTERPOLATION_VALUES} interpolation values!`);
|
||||||
|
}
|
||||||
return ast;
|
return ast;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._reportError(`${e}`, sourceSpan);
|
this._reportError(`${e}`, sourceSpan);
|
||||||
|
@ -209,8 +252,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitText(ast: HtmlTextAst, component: Component): any {
|
visitText(ast: HtmlTextAst, parent: ElementContext): any {
|
||||||
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||||
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
||||||
if (isPresent(expr)) {
|
if (isPresent(expr)) {
|
||||||
return new BoundTextAst(expr, ngContentIndex, ast.sourceSpan);
|
return new BoundTextAst(expr, ngContentIndex, ast.sourceSpan);
|
||||||
|
@ -225,7 +268,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
|
|
||||||
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
|
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
|
||||||
|
|
||||||
visitElement(element: HtmlElementAst, component: Component): any {
|
visitElement(element: HtmlElementAst, parent: ElementContext): any {
|
||||||
var nodeName = element.name;
|
var nodeName = element.name;
|
||||||
var preparsedElement = preparseElement(element);
|
var preparsedElement = preparseElement(element);
|
||||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||||
|
@ -270,19 +313,26 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
var lcElName = splitNsName(nodeName.toLowerCase())[1];
|
var lcElName = splitNsName(nodeName.toLowerCase())[1];
|
||||||
var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
|
var isTemplateElement = lcElName == TEMPLATE_ELEMENT;
|
||||||
var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
|
var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs);
|
||||||
var directives = this._createDirectiveAsts(
|
var directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
||||||
element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector),
|
var directiveAsts =
|
||||||
elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceSpan);
|
this._createDirectiveAsts(element.name, directiveMetas, elementOrDirectiveProps,
|
||||||
|
isTemplateElement ? [] : vars, element.sourceSpan);
|
||||||
var elementProps: BoundElementPropertyAst[] =
|
var elementProps: BoundElementPropertyAst[] =
|
||||||
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives);
|
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
|
||||||
var children = htmlVisitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this,
|
var isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||||
element.children, Component.create(directives));
|
var providerContext =
|
||||||
|
new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot,
|
||||||
|
directiveAsts, attrs, element.sourceSpan);
|
||||||
|
var children = htmlVisitAll(
|
||||||
|
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
|
||||||
|
ElementContext.create(isTemplateElement, directiveAsts,
|
||||||
|
isTemplateElement ? parent.providerContext : providerContext));
|
||||||
|
providerContext.afterElement();
|
||||||
// Override the actual selector when the `ngProjectAs` attribute is provided
|
// Override the actual selector when the `ngProjectAs` attribute is provided
|
||||||
var projectionSelector = isPresent(preparsedElement.projectAs) ?
|
var projectionSelector = isPresent(preparsedElement.projectAs) ?
|
||||||
CssSelector.parse(preparsedElement.projectAs)[0] :
|
CssSelector.parse(preparsedElement.projectAs)[0] :
|
||||||
elementCssSelector;
|
elementCssSelector;
|
||||||
var ngContentIndex = component.findNgContentIndex(projectionSelector);
|
var ngContentIndex = parent.findNgContentIndex(projectionSelector);
|
||||||
var parsedElement;
|
var parsedElement;
|
||||||
|
|
||||||
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||||
|
@ -295,34 +345,43 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
parsedElement = new NgContentAst(
|
parsedElement = new NgContentAst(
|
||||||
this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||||
} else if (isTemplateElement) {
|
} else if (isTemplateElement) {
|
||||||
this._assertAllEventsPublishedByDirectives(directives, events);
|
this._assertAllEventsPublishedByDirectives(directiveAsts, events);
|
||||||
this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps,
|
this._assertNoComponentsNorElementBindingsOnTemplate(directiveAsts, elementProps,
|
||||||
element.sourceSpan);
|
element.sourceSpan);
|
||||||
|
|
||||||
parsedElement =
|
parsedElement =
|
||||||
new EmbeddedTemplateAst(attrs, events, vars, directives, children,
|
new EmbeddedTemplateAst(attrs, events, vars, providerContext.transformedDirectiveAsts,
|
||||||
|
providerContext.transformProviders, children,
|
||||||
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||||
} else {
|
} else {
|
||||||
this._assertOnlyOneComponent(directives, element.sourceSpan);
|
this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
|
||||||
var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0);
|
var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0);
|
||||||
let ngContentIndex =
|
let ngContentIndex =
|
||||||
hasInlineTemplates ? null : component.findNgContentIndex(projectionSelector);
|
hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
|
||||||
|
|
||||||
parsedElement =
|
parsedElement = new ElementAst(
|
||||||
new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives,
|
nodeName, attrs, elementProps, events, elementExportAsVars,
|
||||||
children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
providerContext.transformedDirectiveAsts, providerContext.transformProviders, children,
|
||||||
|
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
||||||
}
|
}
|
||||||
if (hasInlineTemplates) {
|
if (hasInlineTemplates) {
|
||||||
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
|
||||||
var templateDirectives = this._createDirectiveAsts(
|
var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector);
|
||||||
element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector),
|
var templateDirectiveAsts =
|
||||||
templateElementOrDirectiveProps, [], element.sourceSpan);
|
this._createDirectiveAsts(element.name, templateDirectiveMetas,
|
||||||
|
templateElementOrDirectiveProps, [], element.sourceSpan);
|
||||||
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||||
element.name, templateElementOrDirectiveProps, templateDirectives);
|
element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
|
||||||
this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps,
|
this._assertNoComponentsNorElementBindingsOnTemplate(
|
||||||
element.sourceSpan);
|
templateDirectiveAsts, templateElementProps, element.sourceSpan);
|
||||||
|
var templateProviderContext = new ProviderElementContext(
|
||||||
|
this.providerViewContext, parent.providerContext, parent.isTemplateElement,
|
||||||
|
templateDirectiveAsts, [], element.sourceSpan);
|
||||||
|
templateProviderContext.afterElement();
|
||||||
|
|
||||||
parsedElement = new EmbeddedTemplateAst([], [], templateVars, templateDirectives,
|
parsedElement = new EmbeddedTemplateAst([], [], templateVars,
|
||||||
|
templateProviderContext.transformedDirectiveAsts,
|
||||||
|
templateProviderContext.transformProviders,
|
||||||
[parsedElement], ngContentIndex, element.sourceSpan);
|
[parsedElement], ngContentIndex, element.sourceSpan);
|
||||||
}
|
}
|
||||||
return parsedElement;
|
return parsedElement;
|
||||||
|
@ -685,7 +744,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
class NonBindableVisitor implements HtmlAstVisitor {
|
class NonBindableVisitor implements HtmlAstVisitor {
|
||||||
visitElement(ast: HtmlElementAst, component: Component): ElementAst {
|
visitElement(ast: HtmlElementAst, parent: ElementContext): ElementAst {
|
||||||
var preparsedElement = preparseElement(ast);
|
var preparsedElement = preparseElement(ast);
|
||||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||||
preparsedElement.type === PreparsedElementType.STYLE ||
|
preparsedElement.type === PreparsedElementType.STYLE ||
|
||||||
|
@ -698,17 +757,17 @@ class NonBindableVisitor implements HtmlAstVisitor {
|
||||||
|
|
||||||
var attrNameAndValues = ast.attrs.map(attrAst => [attrAst.name, attrAst.value]);
|
var attrNameAndValues = ast.attrs.map(attrAst => [attrAst.name, attrAst.value]);
|
||||||
var selector = createElementCssSelector(ast.name, attrNameAndValues);
|
var selector = createElementCssSelector(ast.name, attrNameAndValues);
|
||||||
var ngContentIndex = component.findNgContentIndex(selector);
|
var ngContentIndex = parent.findNgContentIndex(selector);
|
||||||
var children = htmlVisitAll(this, ast.children, EMPTY_COMPONENT);
|
var children = htmlVisitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
||||||
return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children,
|
return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], [], children,
|
||||||
ngContentIndex, ast.sourceSpan);
|
ngContentIndex, ast.sourceSpan);
|
||||||
}
|
}
|
||||||
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
|
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
|
||||||
visitAttr(ast: HtmlAttrAst, context: any): AttrAst {
|
visitAttr(ast: HtmlAttrAst, context: any): AttrAst {
|
||||||
return new AttrAst(ast.name, ast.value, ast.sourceSpan);
|
return new AttrAst(ast.name, ast.value, ast.sourceSpan);
|
||||||
}
|
}
|
||||||
visitText(ast: HtmlTextAst, component: Component): TextAst {
|
visitText(ast: HtmlTextAst, parent: ElementContext): TextAst {
|
||||||
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||||
return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
|
return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,34 +781,35 @@ export function splitClasses(classAttrValue: string): string[] {
|
||||||
return StringWrapper.split(classAttrValue.trim(), /\s+/g);
|
return StringWrapper.split(classAttrValue.trim(), /\s+/g);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Component {
|
class ElementContext {
|
||||||
static create(directives: DirectiveAst[]): Component {
|
static create(isTemplateElement: boolean, directives: DirectiveAst[],
|
||||||
if (directives.length === 0 || !directives[0].directive.isComponent) {
|
providerContext: ProviderElementContext): ElementContext {
|
||||||
return EMPTY_COMPONENT;
|
|
||||||
}
|
|
||||||
var matcher = new SelectorMatcher();
|
var matcher = new SelectorMatcher();
|
||||||
var ngContentSelectors = directives[0].directive.template.ngContentSelectors;
|
|
||||||
var wildcardNgContentIndex = null;
|
var wildcardNgContentIndex = null;
|
||||||
for (var i = 0; i < ngContentSelectors.length; i++) {
|
if (directives.length > 0 && directives[0].directive.isComponent) {
|
||||||
var selector = ngContentSelectors[i];
|
var ngContentSelectors = directives[0].directive.template.ngContentSelectors;
|
||||||
if (StringWrapper.equals(selector, '*')) {
|
for (var i = 0; i < ngContentSelectors.length; i++) {
|
||||||
wildcardNgContentIndex = i;
|
var selector = ngContentSelectors[i];
|
||||||
} else {
|
if (StringWrapper.equals(selector, '*')) {
|
||||||
matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
|
wildcardNgContentIndex = i;
|
||||||
|
} else {
|
||||||
|
matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Component(matcher, wildcardNgContentIndex);
|
return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext);
|
||||||
}
|
}
|
||||||
constructor(public ngContentIndexMatcher: SelectorMatcher,
|
constructor(public isTemplateElement: boolean, private _ngContentIndexMatcher: SelectorMatcher,
|
||||||
public wildcardNgContentIndex: number) {}
|
private _wildcardNgContentIndex: number,
|
||||||
|
public providerContext: ProviderElementContext) {}
|
||||||
|
|
||||||
findNgContentIndex(selector: CssSelector): number {
|
findNgContentIndex(selector: CssSelector): number {
|
||||||
var ngContentIndices = [];
|
var ngContentIndices = [];
|
||||||
this.ngContentIndexMatcher.match(
|
this._ngContentIndexMatcher.match(
|
||||||
selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); });
|
selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); });
|
||||||
ListWrapper.sort(ngContentIndices);
|
ListWrapper.sort(ngContentIndices);
|
||||||
if (isPresent(this.wildcardNgContentIndex)) {
|
if (isPresent(this._wildcardNgContentIndex)) {
|
||||||
ngContentIndices.push(this.wildcardNgContentIndex);
|
ngContentIndices.push(this._wildcardNgContentIndex);
|
||||||
}
|
}
|
||||||
return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
|
return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
|
||||||
}
|
}
|
||||||
|
@ -775,16 +835,30 @@ function createElementCssSelector(elementName: string, matchableAttrs: string[][
|
||||||
return cssSelector;
|
return cssSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
|
var EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
|
||||||
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
||||||
|
|
||||||
|
|
||||||
export class PipeCollector extends RecursiveAstVisitor {
|
export class PipeCollector extends RecursiveAstVisitor {
|
||||||
pipes: Set<string> = new Set<string>();
|
pipes: Set<string> = new Set<string>();
|
||||||
visitPipe(ast: BindingPipe): any {
|
visitPipe(ast: BindingPipe, context: any): any {
|
||||||
this.pipes.add(ast.name);
|
this.pipes.add(ast.name);
|
||||||
ast.exp.visit(this);
|
ast.exp.visit(this);
|
||||||
this.visitAll(ast.args);
|
this.visitAll(ast.args, context);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeDuplicates(items: CompileMetadataWithType[]): CompileMetadataWithType[] {
|
||||||
|
let res = [];
|
||||||
|
items.forEach(item => {
|
||||||
|
let hasMatch =
|
||||||
|
res.filter(r => r.type.name == item.type.name && r.type.moduleUrl == item.type.moduleUrl &&
|
||||||
|
r.type.runtime == item.type.runtime)
|
||||||
|
.length > 0;
|
||||||
|
if (!hasMatch) {
|
||||||
|
res.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
@ -4,10 +4,16 @@ import 'package:angular2/src/core/di.dart' show Injectable, Inject, Provider;
|
||||||
import 'package:angular2/src/facade/lang.dart' show isPresent, StringWrapper;
|
import 'package:angular2/src/facade/lang.dart' show isPresent, StringWrapper;
|
||||||
import 'package:angular2/src/core/application_tokens.dart' show PACKAGE_ROOT_URL;
|
import 'package:angular2/src/core/application_tokens.dart' show PACKAGE_ROOT_URL;
|
||||||
|
|
||||||
UrlResolver createWithoutPackagePrefix() {
|
const _ASSET_SCHEME = 'asset:';
|
||||||
|
|
||||||
|
UrlResolver createUrlResolverWithoutPackagePrefix() {
|
||||||
return new UrlResolver.withUrlPrefix(null);
|
return new UrlResolver.withUrlPrefix(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UrlResolver createOfflineCompileUrlResolver() {
|
||||||
|
return new UrlResolver.withUrlPrefix(_ASSET_SCHEME);
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_PACKAGE_URL_PROVIDER = const Provider(PACKAGE_ROOT_URL, useValue: "/packages");
|
const DEFAULT_PACKAGE_URL_PROVIDER = const Provider(PACKAGE_ROOT_URL, useValue: "/packages");
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -44,9 +50,14 @@ class UrlResolver {
|
||||||
|
|
||||||
var prefix = this._packagePrefix;
|
var prefix = this._packagePrefix;
|
||||||
if (prefix != null && uri.scheme == 'package') {
|
if (prefix != null && uri.scheme == 'package') {
|
||||||
prefix = StringWrapper.stripRight(prefix, '/');
|
if (prefix == _ASSET_SCHEME) {
|
||||||
var path = StringWrapper.stripLeft(uri.path, '/');
|
var pathSegments = uri.pathSegments.toList()..insert(1, 'lib');
|
||||||
return '$prefix/$path';
|
return new Uri(scheme: 'asset', pathSegments: pathSegments).toString();
|
||||||
|
} else {
|
||||||
|
prefix = StringWrapper.stripRight(prefix, '/');
|
||||||
|
var path = StringWrapper.stripLeft(uri.path, '/');
|
||||||
|
return '$prefix/$path';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return uri.toString();
|
return uri.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,19 @@ import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {PACKAGE_ROOT_URL} from 'angular2/src/core/application_tokens';
|
import {PACKAGE_ROOT_URL} from 'angular2/src/core/application_tokens';
|
||||||
import {Provider} from 'angular2/src/core/di';
|
import {Provider} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
const _ASSET_SCHEME = 'asset:';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link UrlResolver} with no package prefix.
|
* Create a {@link UrlResolver} with no package prefix.
|
||||||
*/
|
*/
|
||||||
export function createWithoutPackagePrefix(): UrlResolver {
|
export function createUrlResolverWithoutPackagePrefix(): UrlResolver {
|
||||||
return new UrlResolver();
|
return new UrlResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createOfflineCompileUrlResolver(): UrlResolver {
|
||||||
|
return new UrlResolver(_ASSET_SCHEME);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A default provider for {@link PACKAGE_ROOT_URL} that maps to '/'.
|
* A default provider for {@link PACKAGE_ROOT_URL} that maps to '/'.
|
||||||
*/
|
*/
|
||||||
|
@ -36,13 +42,7 @@ export var DEFAULT_PACKAGE_URL_PROVIDER = new Provider(PACKAGE_ROOT_URL, {useVal
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UrlResolver {
|
export class UrlResolver {
|
||||||
private _packagePrefix: string;
|
constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string = null) {}
|
||||||
|
|
||||||
constructor(@Inject(PACKAGE_ROOT_URL) packagePrefix: string = null) {
|
|
||||||
if (isPresent(packagePrefix)) {
|
|
||||||
this._packagePrefix = StringWrapper.stripRight(packagePrefix, "/") + "/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the `url` given the `baseUrl`:
|
* Resolves the `url` given the `baseUrl`:
|
||||||
|
@ -61,8 +61,19 @@ export class UrlResolver {
|
||||||
if (isPresent(baseUrl) && baseUrl.length > 0) {
|
if (isPresent(baseUrl) && baseUrl.length > 0) {
|
||||||
resolvedUrl = _resolveUrl(baseUrl, resolvedUrl);
|
resolvedUrl = _resolveUrl(baseUrl, resolvedUrl);
|
||||||
}
|
}
|
||||||
if (isPresent(this._packagePrefix) && getUrlScheme(resolvedUrl) == "package") {
|
var resolvedParts = _split(resolvedUrl);
|
||||||
resolvedUrl = resolvedUrl.replace("package:", this._packagePrefix);
|
var prefix = this._packagePrefix;
|
||||||
|
if (isPresent(prefix) && isPresent(resolvedParts) &&
|
||||||
|
resolvedParts[_ComponentIndex.Scheme] == "package") {
|
||||||
|
var path = resolvedParts[_ComponentIndex.Path];
|
||||||
|
if (this._packagePrefix === _ASSET_SCHEME) {
|
||||||
|
var pathSegements = path.split(/\//);
|
||||||
|
resolvedUrl = `asset:${pathSegements[0]}/lib/${pathSegements.slice(1).join('/')}`;
|
||||||
|
} else {
|
||||||
|
prefix = StringWrapper.stripRight(prefix, '/');
|
||||||
|
path = StringWrapper.stripLeft(path, '/');
|
||||||
|
return `${prefix}/${path}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return resolvedUrl;
|
return resolvedUrl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,9 @@
|
||||||
import {
|
import {IS_DART, StringWrapper, Math, isBlank} from 'angular2/src/facade/lang';
|
||||||
IS_DART,
|
|
||||||
StringWrapper,
|
export var MODULE_SUFFIX = IS_DART ? '.dart' : '';
|
||||||
isBlank,
|
|
||||||
isPresent,
|
|
||||||
isString,
|
|
||||||
isArray
|
|
||||||
} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
var DASH_CASE_REGEXP = /-([a-z])/g;
|
var DASH_CASE_REGEXP = /-([a-z])/g;
|
||||||
var SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
|
||||||
var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n|\r|\$/g;
|
|
||||||
|
|
||||||
export var MODULE_SUFFIX = IS_DART ? '.dart' : '.js';
|
|
||||||
|
|
||||||
export var CONST_VAR = IS_DART ? 'const' : 'var';
|
|
||||||
|
|
||||||
export function camelCaseToDashCase(input: string): string {
|
export function camelCaseToDashCase(input: string): string {
|
||||||
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
||||||
|
@ -26,74 +15,6 @@ export function dashCaseToCamelCase(input: string): string {
|
||||||
(m) => { return m[1].toUpperCase(); });
|
(m) => { return m[1].toUpperCase(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function escapeSingleQuoteString(input: string): string {
|
|
||||||
if (isBlank(input)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return `'${escapeString(input, SINGLE_QUOTE_ESCAPE_STRING_RE)}'`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function escapeDoubleQuoteString(input: string): string {
|
|
||||||
if (isBlank(input)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return `"${escapeString(input, DOUBLE_QUOTE_ESCAPE_STRING_RE)}"`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeString(input: string, re: RegExp): string {
|
|
||||||
return StringWrapper.replaceAllMapped(input, re, (match) => {
|
|
||||||
if (match[0] == '$') {
|
|
||||||
return IS_DART ? '\\$' : '$';
|
|
||||||
} else if (match[0] == '\n') {
|
|
||||||
return '\\n';
|
|
||||||
} else if (match[0] == '\r') {
|
|
||||||
return '\\r';
|
|
||||||
} else {
|
|
||||||
return `\\${match[0]}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenExportVariable(name: string): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `const ${name} = `;
|
|
||||||
} else {
|
|
||||||
return `var ${name} = exports['${name}'] = `;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenConstConstructorCall(name: string): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `const ${name}`;
|
|
||||||
} else {
|
|
||||||
return `new ${name}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenValueFn(params: string[], value: string, fnName: string = ''): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `${codeGenFnHeader(params, fnName)} => ${value}`;
|
|
||||||
} else {
|
|
||||||
return `${codeGenFnHeader(params, fnName)} { return ${value}; }`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenFnHeader(params: string[], fnName: string = ''): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `${fnName}(${params.join(',')})`;
|
|
||||||
} else {
|
|
||||||
return `function ${fnName}(${params.join(',')})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export function codeGenToString(expr: string): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `'\${${expr}}'`;
|
|
||||||
} else {
|
|
||||||
// JS automatically converts to string...
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||||
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
|
var parts = StringWrapper.split(input.trim(), /\s*:\s*/g);
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
|
@ -102,77 +23,3 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||||
return defaultValues;
|
return defaultValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class Statement {
|
|
||||||
constructor(public statement: string) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Expression {
|
|
||||||
constructor(public expression: string, public isArray = false) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function escapeValue(value: any): string {
|
|
||||||
if (value instanceof Expression) {
|
|
||||||
return value.expression;
|
|
||||||
} else if (isString(value)) {
|
|
||||||
return escapeSingleQuoteString(value);
|
|
||||||
} else if (isBlank(value)) {
|
|
||||||
return 'null';
|
|
||||||
} else {
|
|
||||||
return `${value}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenArray(data: any[]): string {
|
|
||||||
return `[${data.map(escapeValue).join(',')}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenFlatArray(values: any[]): string {
|
|
||||||
var result = '([';
|
|
||||||
var isFirstArrayEntry = true;
|
|
||||||
var concatFn = IS_DART ? '.addAll' : 'concat';
|
|
||||||
for (var i = 0; i < values.length; i++) {
|
|
||||||
var value = values[i];
|
|
||||||
if (value instanceof Expression && (<Expression>value).isArray) {
|
|
||||||
result += `]).${concatFn}(${value.expression}).${concatFn}([`;
|
|
||||||
isFirstArrayEntry = true;
|
|
||||||
} else {
|
|
||||||
if (!isFirstArrayEntry) {
|
|
||||||
result += ',';
|
|
||||||
}
|
|
||||||
isFirstArrayEntry = false;
|
|
||||||
result += escapeValue(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result += '])';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function codeGenStringMap(keyValueArray: any[][]): string {
|
|
||||||
return `{${keyValueArray.map(codeGenKeyValue).join(',')}}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenKeyValue(keyValue: any[]): string {
|
|
||||||
return `${escapeValue(keyValue[0])}:${escapeValue(keyValue[1])}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addAll(source: any[], target: any[]) {
|
|
||||||
for (var i = 0; i < source.length; i++) {
|
|
||||||
target.push(source[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function flattenArray(source: any[], target: any[]): any[] {
|
|
||||||
if (isPresent(source)) {
|
|
||||||
for (var i = 0; i < source.length; i++) {
|
|
||||||
var item = source[i];
|
|
||||||
if (isArray(item)) {
|
|
||||||
flattenArray(item, target);
|
|
||||||
} else {
|
|
||||||
target.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,606 +0,0 @@
|
||||||
import {
|
|
||||||
isPresent,
|
|
||||||
isBlank,
|
|
||||||
Type,
|
|
||||||
isString,
|
|
||||||
StringWrapper,
|
|
||||||
IS_DART,
|
|
||||||
CONST_EXPR
|
|
||||||
} from 'angular2/src/facade/lang';
|
|
||||||
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {
|
|
||||||
TemplateAst,
|
|
||||||
TemplateAstVisitor,
|
|
||||||
NgContentAst,
|
|
||||||
EmbeddedTemplateAst,
|
|
||||||
ElementAst,
|
|
||||||
VariableAst,
|
|
||||||
BoundEventAst,
|
|
||||||
BoundElementPropertyAst,
|
|
||||||
AttrAst,
|
|
||||||
BoundTextAst,
|
|
||||||
TextAst,
|
|
||||||
DirectiveAst,
|
|
||||||
BoundDirectivePropertyAst,
|
|
||||||
templateVisitAll
|
|
||||||
} from './template_ast';
|
|
||||||
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
|
||||||
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
|
||||||
import {
|
|
||||||
AppProtoView,
|
|
||||||
AppView,
|
|
||||||
flattenNestedViewRenderNodes,
|
|
||||||
checkSlotCount
|
|
||||||
} from 'angular2/src/core/linker/view';
|
|
||||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
|
||||||
import {AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
|
||||||
import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
|
|
||||||
import {Renderer, ParentRenderer} from 'angular2/src/core/render/api';
|
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
|
||||||
import {
|
|
||||||
escapeSingleQuoteString,
|
|
||||||
codeGenConstConstructorCall,
|
|
||||||
codeGenValueFn,
|
|
||||||
codeGenFnHeader,
|
|
||||||
MODULE_SUFFIX,
|
|
||||||
Statement,
|
|
||||||
escapeValue,
|
|
||||||
codeGenArray,
|
|
||||||
codeGenFlatArray,
|
|
||||||
Expression,
|
|
||||||
flattenArray,
|
|
||||||
CONST_VAR
|
|
||||||
} from './util';
|
|
||||||
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
|
|
||||||
|
|
||||||
import {
|
|
||||||
APP_VIEW_MODULE_REF,
|
|
||||||
APP_EL_MODULE_REF,
|
|
||||||
METADATA_MODULE_REF,
|
|
||||||
CompileProtoView,
|
|
||||||
CompileProtoElement
|
|
||||||
} from './proto_view_compiler';
|
|
||||||
|
|
||||||
export const VIEW_JIT_IMPORTS = CONST_EXPR({
|
|
||||||
'AppView': AppView,
|
|
||||||
'AppElement': AppElement,
|
|
||||||
'flattenNestedViewRenderNodes': flattenNestedViewRenderNodes,
|
|
||||||
'checkSlotCount': checkSlotCount
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ViewCompiler {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
|
|
||||||
styles: Array<string | any[]>,
|
|
||||||
protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
|
|
||||||
changeDetectorFactories: Function[],
|
|
||||||
componentViewFactory: Function): Function {
|
|
||||||
var viewFactory = new RuntimeViewFactory(component, styles, protoViews, changeDetectorFactories,
|
|
||||||
componentViewFactory);
|
|
||||||
return viewFactory.createViewFactory(template, 0, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
|
|
||||||
styles: SourceExpression,
|
|
||||||
protoViews: CompileProtoView<Expression, Expression>[],
|
|
||||||
changeDetectorFactoryExpressions: SourceExpressions,
|
|
||||||
componentViewFactory: Function): SourceExpression {
|
|
||||||
var viewFactory = new CodeGenViewFactory(
|
|
||||||
component, styles, protoViews, changeDetectorFactoryExpressions, componentViewFactory);
|
|
||||||
var targetStatements: Statement[] = [];
|
|
||||||
var viewFactoryExpression = viewFactory.createViewFactory(template, 0, targetStatements);
|
|
||||||
return new SourceExpression(targetStatements.map(stmt => stmt.statement),
|
|
||||||
viewFactoryExpression.expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ViewFactory<EXPRESSION, STATEMENT> {
|
|
||||||
createText(renderer: EXPRESSION, parent: EXPRESSION, text: string,
|
|
||||||
targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
|
|
||||||
createElement(renderer: EXPRESSION, parent: EXPRESSION, name: string, rootSelector: EXPRESSION,
|
|
||||||
targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
|
|
||||||
createTemplateAnchor(renderer: EXPRESSION, parent: EXPRESSION,
|
|
||||||
targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
|
|
||||||
createGlobalEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
|
|
||||||
eventAst: BoundEventAst, targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
|
|
||||||
createElementEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
|
|
||||||
renderNode: EXPRESSION, eventAst: BoundEventAst,
|
|
||||||
targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
|
|
||||||
setElementAttribute(renderer: EXPRESSION, renderNode: EXPRESSION, attrName: string,
|
|
||||||
attrValue: string, targetStatements: STATEMENT[]);
|
|
||||||
|
|
||||||
createAppElement(appProtoEl: EXPRESSION, view: EXPRESSION, renderNode: EXPRESSION,
|
|
||||||
parentAppEl: EXPRESSION, embeddedViewFactory: EXPRESSION,
|
|
||||||
targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
|
|
||||||
createAndSetComponentView(renderer: EXPRESSION, viewManager: EXPRESSION, view: EXPRESSION,
|
|
||||||
appEl: EXPRESSION, component: CompileDirectiveMetadata,
|
|
||||||
contentNodesByNgContentIndex: EXPRESSION[][],
|
|
||||||
targetStatements: STATEMENT[]);
|
|
||||||
|
|
||||||
getProjectedNodes(projectableNodes: EXPRESSION, ngContentIndex: number): EXPRESSION;
|
|
||||||
|
|
||||||
appendProjectedNodes(renderer: EXPRESSION, parent: EXPRESSION, nodes: EXPRESSION,
|
|
||||||
targetStatements: STATEMENT[]);
|
|
||||||
|
|
||||||
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
|
||||||
targetStatements: STATEMENT[]): EXPRESSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CodeGenViewFactory implements ViewFactory<Expression, Statement> {
|
|
||||||
private _nextVarId: number = 0;
|
|
||||||
constructor(public component: CompileDirectiveMetadata, public styles: SourceExpression,
|
|
||||||
public protoViews: CompileProtoView<Expression, Expression>[],
|
|
||||||
public changeDetectorExpressions: SourceExpressions,
|
|
||||||
public componentViewFactory: Function) {}
|
|
||||||
|
|
||||||
private _nextVar(prefix: string): string {
|
|
||||||
return `${prefix}${this._nextVarId++}_${this.component.type.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _nextRenderVar(): string { return this._nextVar('render'); }
|
|
||||||
|
|
||||||
private _nextAppVar(): string { return this._nextVar('app'); }
|
|
||||||
|
|
||||||
private _nextDisposableVar(): string {
|
|
||||||
return `disposable${this._nextVarId++}_${this.component.type.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
createText(renderer: Expression, parent: Expression, text: string,
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var varName = this._nextRenderVar();
|
|
||||||
var statement =
|
|
||||||
`var ${varName} = ${renderer.expression}.createText(${isPresent(parent) ? parent.expression : null}, ${escapeSingleQuoteString(text)});`;
|
|
||||||
targetStatements.push(new Statement(statement));
|
|
||||||
return new Expression(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
createElement(renderer: Expression, parentRenderNode: Expression, name: string,
|
|
||||||
rootSelector: Expression, targetStatements: Statement[]): Expression {
|
|
||||||
var varName = this._nextRenderVar();
|
|
||||||
var valueExpr;
|
|
||||||
if (isPresent(rootSelector)) {
|
|
||||||
valueExpr = `${rootSelector.expression} == null ?
|
|
||||||
${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)}) :
|
|
||||||
${renderer.expression}.selectRootElement(${rootSelector.expression});`;
|
|
||||||
} else {
|
|
||||||
valueExpr =
|
|
||||||
`${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)})`;
|
|
||||||
}
|
|
||||||
var statement = `var ${varName} = ${valueExpr};`;
|
|
||||||
targetStatements.push(new Statement(statement));
|
|
||||||
return new Expression(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
createTemplateAnchor(renderer: Expression, parentRenderNode: Expression,
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var varName = this._nextRenderVar();
|
|
||||||
var valueExpr =
|
|
||||||
`${renderer.expression}.createTemplateAnchor(${isPresent(parentRenderNode) ? parentRenderNode.expression : null});`;
|
|
||||||
targetStatements.push(new Statement(`var ${varName} = ${valueExpr}`));
|
|
||||||
return new Expression(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
createGlobalEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
|
||||||
eventAst: BoundEventAst, targetStatements: Statement[]): Expression {
|
|
||||||
var disposableVar = this._nextDisposableVar();
|
|
||||||
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
|
||||||
targetStatements.push(new Statement(
|
|
||||||
`var ${disposableVar} = ${renderer.expression}.listenGlobal(${escapeValue(eventAst.target)}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
|
|
||||||
return new Expression(disposableVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
createElementEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
|
||||||
renderNode: Expression, eventAst: BoundEventAst,
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var disposableVar = this._nextDisposableVar();
|
|
||||||
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
|
||||||
targetStatements.push(new Statement(
|
|
||||||
`var ${disposableVar} = ${renderer.expression}.listen(${renderNode.expression}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
|
|
||||||
return new Expression(disposableVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
setElementAttribute(renderer: Expression, renderNode: Expression, attrName: string,
|
|
||||||
attrValue: string, targetStatements: Statement[]) {
|
|
||||||
targetStatements.push(new Statement(
|
|
||||||
`${renderer.expression}.setElementAttribute(${renderNode.expression}, ${escapeSingleQuoteString(attrName)}, ${escapeSingleQuoteString(attrValue)});`));
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppElement(appProtoEl: Expression, appView: Expression, renderNode: Expression,
|
|
||||||
parentAppEl: Expression, embeddedViewFactory: Expression,
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var appVar = this._nextAppVar();
|
|
||||||
var varValue =
|
|
||||||
`new ${APP_EL_MODULE_REF}AppElement(${appProtoEl.expression}, ${appView.expression},
|
|
||||||
${isPresent(parentAppEl) ? parentAppEl.expression : null}, ${renderNode.expression}, ${isPresent(embeddedViewFactory) ? embeddedViewFactory.expression : null})`;
|
|
||||||
targetStatements.push(new Statement(`var ${appVar} = ${varValue};`));
|
|
||||||
return new Expression(appVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAndSetComponentView(renderer: Expression, viewManager: Expression, view: Expression,
|
|
||||||
appEl: Expression, component: CompileDirectiveMetadata,
|
|
||||||
contentNodesByNgContentIndex: Expression[][],
|
|
||||||
targetStatements: Statement[]) {
|
|
||||||
var codeGenContentNodes;
|
|
||||||
if (this.component.type.isHost) {
|
|
||||||
codeGenContentNodes = `${view.expression}.projectableNodes`;
|
|
||||||
} else {
|
|
||||||
codeGenContentNodes =
|
|
||||||
`[${contentNodesByNgContentIndex.map( nodes => codeGenFlatArray(nodes) ).join(',')}]`;
|
|
||||||
}
|
|
||||||
targetStatements.push(new Statement(
|
|
||||||
`${this.componentViewFactory(component)}(${renderer.expression}, ${viewManager.expression}, ${appEl.expression}, ${codeGenContentNodes}, null, null, null);`));
|
|
||||||
}
|
|
||||||
|
|
||||||
getProjectedNodes(projectableNodes: Expression, ngContentIndex: number): Expression {
|
|
||||||
return new Expression(`${projectableNodes.expression}[${ngContentIndex}]`, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
appendProjectedNodes(renderer: Expression, parent: Expression, nodes: Expression,
|
|
||||||
targetStatements: Statement[]) {
|
|
||||||
targetStatements.push(new Statement(
|
|
||||||
`${renderer.expression}.projectNodes(${parent.expression}, ${APP_VIEW_MODULE_REF}flattenNestedViewRenderNodes(${nodes.expression}));`));
|
|
||||||
}
|
|
||||||
|
|
||||||
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
|
||||||
targetStatements: Statement[]): Expression {
|
|
||||||
var compileProtoView = this.protoViews[embeddedTemplateIndex];
|
|
||||||
var isHostView = this.component.type.isHost;
|
|
||||||
var isComponentView = embeddedTemplateIndex === 0 && !isHostView;
|
|
||||||
var visitor = new ViewBuilderVisitor<Expression, Statement>(
|
|
||||||
new Expression('renderer'), new Expression('viewManager'),
|
|
||||||
new Expression('projectableNodes'), isHostView ? new Expression('rootSelector') : null,
|
|
||||||
new Expression('view'), compileProtoView, targetStatements, this);
|
|
||||||
|
|
||||||
templateVisitAll(
|
|
||||||
visitor, asts,
|
|
||||||
new ParentElement(isComponentView ? new Expression('parentRenderNode') : null, null, null));
|
|
||||||
|
|
||||||
var appProtoView = compileProtoView.protoView.expression;
|
|
||||||
var viewFactoryName = codeGenViewFactoryName(this.component, embeddedTemplateIndex);
|
|
||||||
var changeDetectorFactory = this.changeDetectorExpressions.expressions[embeddedTemplateIndex];
|
|
||||||
var factoryArgs = [
|
|
||||||
'parentRenderer',
|
|
||||||
'viewManager',
|
|
||||||
'containerEl',
|
|
||||||
'projectableNodes',
|
|
||||||
'rootSelector',
|
|
||||||
'dynamicallyCreatedProviders',
|
|
||||||
'rootInjector'
|
|
||||||
];
|
|
||||||
var initRendererStmts = [];
|
|
||||||
var rendererExpr = `parentRenderer`;
|
|
||||||
if (embeddedTemplateIndex === 0) {
|
|
||||||
var renderCompTypeVar = this._nextVar('renderType');
|
|
||||||
targetStatements.push(new Statement(`var ${renderCompTypeVar} = null;`));
|
|
||||||
var stylesVar = this._nextVar('styles');
|
|
||||||
targetStatements.push(
|
|
||||||
new Statement(`${CONST_VAR} ${stylesVar} = ${this.styles.expression};`));
|
|
||||||
var encapsulation = this.component.template.encapsulation;
|
|
||||||
initRendererStmts.push(`if (${renderCompTypeVar} == null) {
|
|
||||||
${renderCompTypeVar} = viewManager.createRenderComponentType(${codeGenViewEncapsulation(encapsulation)}, ${stylesVar});
|
|
||||||
}`);
|
|
||||||
rendererExpr = `parentRenderer.renderComponent(${renderCompTypeVar})`;
|
|
||||||
}
|
|
||||||
var statement = `
|
|
||||||
${codeGenFnHeader(factoryArgs, viewFactoryName)}{
|
|
||||||
${initRendererStmts.join('\n')}
|
|
||||||
var renderer = ${rendererExpr};
|
|
||||||
var view = new ${APP_VIEW_MODULE_REF}AppView(
|
|
||||||
${appProtoView}, renderer, viewManager,
|
|
||||||
projectableNodes,
|
|
||||||
containerEl,
|
|
||||||
dynamicallyCreatedProviders, rootInjector,
|
|
||||||
${changeDetectorFactory}()
|
|
||||||
);
|
|
||||||
${APP_VIEW_MODULE_REF}checkSlotCount(${escapeValue(this.component.type.name)}, ${this.component.template.ngContentSelectors.length}, projectableNodes);
|
|
||||||
${isComponentView ? 'var parentRenderNode = renderer.createViewRoot(view.containerAppElement.nativeElement);' : ''}
|
|
||||||
${visitor.renderStmts.map(stmt => stmt.statement).join('\n')}
|
|
||||||
${visitor.appStmts.map(stmt => stmt.statement).join('\n')}
|
|
||||||
|
|
||||||
view.init(${codeGenFlatArray(visitor.rootNodesOrAppElements)}, ${codeGenArray(visitor.renderNodes)}, ${codeGenArray(visitor.appDisposables)},
|
|
||||||
${codeGenArray(visitor.appElements)});
|
|
||||||
return view;
|
|
||||||
}`;
|
|
||||||
targetStatements.push(new Statement(statement));
|
|
||||||
return new Expression(viewFactoryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RuntimeViewFactory implements ViewFactory<any, any> {
|
|
||||||
constructor(public component: CompileDirectiveMetadata, public styles: Array<string | any[]>,
|
|
||||||
public protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
|
|
||||||
public changeDetectorFactories: Function[], public componentViewFactory: Function) {}
|
|
||||||
|
|
||||||
createText(renderer: Renderer, parent: any, text: string, targetStatements: any[]): any {
|
|
||||||
return renderer.createText(parent, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
createElement(renderer: Renderer, parent: any, name: string, rootSelector: string,
|
|
||||||
targetStatements: any[]): any {
|
|
||||||
var el;
|
|
||||||
if (isPresent(rootSelector)) {
|
|
||||||
el = renderer.selectRootElement(rootSelector);
|
|
||||||
} else {
|
|
||||||
el = renderer.createElement(parent, name);
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
createTemplateAnchor(renderer: Renderer, parent: any, targetStatements: any[]): any {
|
|
||||||
return renderer.createTemplateAnchor(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
createGlobalEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
|
|
||||||
eventAst: BoundEventAst, targetStatements: any[]): any {
|
|
||||||
return renderer.listenGlobal(
|
|
||||||
eventAst.target, eventAst.name,
|
|
||||||
(event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
createElementEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
|
|
||||||
renderNode: any, eventAst: BoundEventAst,
|
|
||||||
targetStatements: any[]): any {
|
|
||||||
return renderer.listen(
|
|
||||||
renderNode, eventAst.name,
|
|
||||||
(event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
setElementAttribute(renderer: Renderer, renderNode: any, attrName: string, attrValue: string,
|
|
||||||
targetStatements: any[]) {
|
|
||||||
renderer.setElementAttribute(renderNode, attrName, attrValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppElement(appProtoEl: AppProtoElement, appView: AppView, renderNode: any,
|
|
||||||
parentAppEl: AppElement, embeddedViewFactory: Function,
|
|
||||||
targetStatements: any[]): any {
|
|
||||||
return new AppElement(appProtoEl, appView, parentAppEl, renderNode, embeddedViewFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
createAndSetComponentView(renderer: Renderer, viewManager: AppViewManager_, appView: AppView,
|
|
||||||
appEl: AppElement, component: CompileDirectiveMetadata,
|
|
||||||
contentNodesByNgContentIndex: Array<Array<any | any[]>>,
|
|
||||||
targetStatements: any[]) {
|
|
||||||
var flattenedContentNodes;
|
|
||||||
if (this.component.type.isHost) {
|
|
||||||
flattenedContentNodes = appView.projectableNodes;
|
|
||||||
} else {
|
|
||||||
flattenedContentNodes = ListWrapper.createFixedSize(contentNodesByNgContentIndex.length);
|
|
||||||
for (var i = 0; i < contentNodesByNgContentIndex.length; i++) {
|
|
||||||
flattenedContentNodes[i] = flattenArray(contentNodesByNgContentIndex[i], []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.componentViewFactory(component)(renderer, viewManager, appEl, flattenedContentNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
getProjectedNodes(projectableNodes: any[][], ngContentIndex: number): any[] {
|
|
||||||
return projectableNodes[ngContentIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
appendProjectedNodes(renderer: Renderer, parent: any, nodes: any[], targetStatements: any[]) {
|
|
||||||
renderer.projectNodes(parent, flattenNestedViewRenderNodes(nodes));
|
|
||||||
}
|
|
||||||
|
|
||||||
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
|
||||||
targetStatements: any[]): Function {
|
|
||||||
var compileProtoView = this.protoViews[embeddedTemplateIndex];
|
|
||||||
var isComponentView = compileProtoView.protoView.type === ViewType.COMPONENT;
|
|
||||||
var renderComponentType = null;
|
|
||||||
return (parentRenderer: ParentRenderer, viewManager: AppViewManager_, containerEl: AppElement,
|
|
||||||
projectableNodes: any[][], rootSelector: string = null,
|
|
||||||
dynamicallyCreatedProviders: ResolvedProvider[] = null,
|
|
||||||
rootInjector: Injector = null) => {
|
|
||||||
checkSlotCount(this.component.type.name, this.component.template.ngContentSelectors.length,
|
|
||||||
projectableNodes);
|
|
||||||
var renderer;
|
|
||||||
if (embeddedTemplateIndex === 0) {
|
|
||||||
if (isBlank(renderComponentType)) {
|
|
||||||
renderComponentType = viewManager.createRenderComponentType(
|
|
||||||
this.component.template.encapsulation, this.styles);
|
|
||||||
}
|
|
||||||
renderer = parentRenderer.renderComponent(renderComponentType);
|
|
||||||
} else {
|
|
||||||
renderer = <Renderer>parentRenderer;
|
|
||||||
}
|
|
||||||
var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex]();
|
|
||||||
var view =
|
|
||||||
new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes,
|
|
||||||
containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector);
|
|
||||||
var visitor = new ViewBuilderVisitor<any, any>(
|
|
||||||
renderer, viewManager, projectableNodes, rootSelector, view, compileProtoView, [], this);
|
|
||||||
var parentRenderNode =
|
|
||||||
isComponentView ? renderer.createViewRoot(containerEl.nativeElement) : null;
|
|
||||||
templateVisitAll(visitor, asts, new ParentElement(parentRenderNode, null, null));
|
|
||||||
view.init(flattenArray(visitor.rootNodesOrAppElements, []), visitor.renderNodes,
|
|
||||||
visitor.appDisposables, visitor.appElements);
|
|
||||||
return view;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ParentElement<EXPRESSION> {
|
|
||||||
public contentNodesByNgContentIndex: Array<EXPRESSION>[];
|
|
||||||
|
|
||||||
constructor(public renderNode: EXPRESSION, public appEl: EXPRESSION,
|
|
||||||
public component: CompileDirectiveMetadata) {
|
|
||||||
if (isPresent(component)) {
|
|
||||||
this.contentNodesByNgContentIndex =
|
|
||||||
ListWrapper.createFixedSize(component.template.ngContentSelectors.length);
|
|
||||||
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
|
||||||
this.contentNodesByNgContentIndex[i] = [];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.contentNodesByNgContentIndex = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addContentNode(ngContentIndex: number, nodeExpr: EXPRESSION) {
|
|
||||||
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewBuilderVisitor<EXPRESSION, STATEMENT> implements TemplateAstVisitor {
|
|
||||||
renderStmts: Array<STATEMENT> = [];
|
|
||||||
renderNodes: EXPRESSION[] = [];
|
|
||||||
appStmts: Array<STATEMENT> = [];
|
|
||||||
appElements: EXPRESSION[] = [];
|
|
||||||
appDisposables: EXPRESSION[] = [];
|
|
||||||
|
|
||||||
rootNodesOrAppElements: EXPRESSION[] = [];
|
|
||||||
|
|
||||||
elementCount: number = 0;
|
|
||||||
|
|
||||||
constructor(public renderer: EXPRESSION, public viewManager: EXPRESSION,
|
|
||||||
public projectableNodes: EXPRESSION, public rootSelector: EXPRESSION,
|
|
||||||
public view: EXPRESSION, public protoView: CompileProtoView<EXPRESSION, EXPRESSION>,
|
|
||||||
public targetStatements: STATEMENT[],
|
|
||||||
public factory: ViewFactory<EXPRESSION, STATEMENT>) {}
|
|
||||||
|
|
||||||
private _addRenderNode(renderNode: EXPRESSION, appEl: EXPRESSION, ngContentIndex: number,
|
|
||||||
parent: ParentElement<EXPRESSION>) {
|
|
||||||
this.renderNodes.push(renderNode);
|
|
||||||
if (isPresent(parent.component)) {
|
|
||||||
if (isPresent(ngContentIndex)) {
|
|
||||||
parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : renderNode);
|
|
||||||
}
|
|
||||||
} else if (isBlank(parent.renderNode)) {
|
|
||||||
this.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : renderNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getParentRenderNode(ngContentIndex: number,
|
|
||||||
parent: ParentElement<EXPRESSION>): EXPRESSION {
|
|
||||||
return isPresent(parent.component) &&
|
|
||||||
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
|
|
||||||
null :
|
|
||||||
parent.renderNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBoundText(ast: BoundTextAst, parent: ParentElement<EXPRESSION>): any {
|
|
||||||
return this._visitText('', ast.ngContentIndex, parent);
|
|
||||||
}
|
|
||||||
visitText(ast: TextAst, parent: ParentElement<EXPRESSION>): any {
|
|
||||||
return this._visitText(ast.value, ast.ngContentIndex, parent);
|
|
||||||
}
|
|
||||||
private _visitText(value: string, ngContentIndex: number, parent: ParentElement<EXPRESSION>) {
|
|
||||||
var renderNode = this.factory.createText(
|
|
||||||
this.renderer, this._getParentRenderNode(ngContentIndex, parent), value, this.renderStmts);
|
|
||||||
this._addRenderNode(renderNode, null, ngContentIndex, parent);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitNgContent(ast: NgContentAst, parent: ParentElement<EXPRESSION>): any {
|
|
||||||
var nodesExpression = this.factory.getProjectedNodes(this.projectableNodes, ast.index);
|
|
||||||
if (isPresent(parent.component)) {
|
|
||||||
if (isPresent(ast.ngContentIndex)) {
|
|
||||||
parent.addContentNode(ast.ngContentIndex, nodesExpression);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isPresent(parent.renderNode)) {
|
|
||||||
this.factory.appendProjectedNodes(this.renderer, parent.renderNode, nodesExpression,
|
|
||||||
this.renderStmts);
|
|
||||||
} else {
|
|
||||||
this.rootNodesOrAppElements.push(nodesExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitElement(ast: ElementAst, parent: ParentElement<EXPRESSION>): any {
|
|
||||||
var renderNode = this.factory.createElement(
|
|
||||||
this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), ast.name,
|
|
||||||
this.rootSelector, this.renderStmts);
|
|
||||||
|
|
||||||
var component = ast.getComponent();
|
|
||||||
var elementIndex = this.elementCount++;
|
|
||||||
var protoEl = this.protoView.protoElements[elementIndex];
|
|
||||||
|
|
||||||
protoEl.renderEvents.forEach((eventAst) => {
|
|
||||||
var disposable;
|
|
||||||
if (isPresent(eventAst.target)) {
|
|
||||||
disposable = this.factory.createGlobalEventListener(
|
|
||||||
this.renderer, this.view, protoEl.boundElementIndex, eventAst, this.renderStmts);
|
|
||||||
} else {
|
|
||||||
disposable = this.factory.createElementEventListener(this.renderer, this.view,
|
|
||||||
protoEl.boundElementIndex, renderNode,
|
|
||||||
eventAst, this.renderStmts);
|
|
||||||
}
|
|
||||||
this.appDisposables.push(disposable);
|
|
||||||
});
|
|
||||||
for (var i = 0; i < protoEl.attrNameAndValues.length; i++) {
|
|
||||||
var attrName = protoEl.attrNameAndValues[i][0];
|
|
||||||
var attrValue = protoEl.attrNameAndValues[i][1];
|
|
||||||
this.factory.setElementAttribute(this.renderer, renderNode, attrName, attrValue,
|
|
||||||
this.renderStmts);
|
|
||||||
}
|
|
||||||
var appEl = null;
|
|
||||||
if (isPresent(protoEl.appProtoEl)) {
|
|
||||||
appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode, parent.appEl,
|
|
||||||
null, this.appStmts);
|
|
||||||
this.appElements.push(appEl);
|
|
||||||
}
|
|
||||||
this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
|
|
||||||
|
|
||||||
var newParent = new ParentElement<EXPRESSION>(
|
|
||||||
renderNode, isPresent(appEl) ? appEl : parent.appEl, component);
|
|
||||||
templateVisitAll(this, ast.children, newParent);
|
|
||||||
if (isPresent(appEl) && isPresent(component)) {
|
|
||||||
this.factory.createAndSetComponentView(this.renderer, this.viewManager, this.view, appEl,
|
|
||||||
component, newParent.contentNodesByNgContentIndex,
|
|
||||||
this.appStmts);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: ParentElement<EXPRESSION>): any {
|
|
||||||
var renderNode = this.factory.createTemplateAnchor(
|
|
||||||
this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), this.renderStmts);
|
|
||||||
|
|
||||||
var elementIndex = this.elementCount++;
|
|
||||||
var protoEl = this.protoView.protoElements[elementIndex];
|
|
||||||
var embeddedViewFactory = this.factory.createViewFactory(
|
|
||||||
ast.children, protoEl.embeddedTemplateIndex, this.targetStatements);
|
|
||||||
|
|
||||||
var appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode,
|
|
||||||
parent.appEl, embeddedViewFactory, this.appStmts);
|
|
||||||
this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
|
|
||||||
this.appElements.push(appEl);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
|
||||||
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
|
||||||
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
|
||||||
visitEvent(ast: BoundEventAst, ctx: any): any { return null; }
|
|
||||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
|
||||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function codeGenEventHandler(view: Expression, boundElementIndex: number,
|
|
||||||
eventName: string): string {
|
|
||||||
return codeGenValueFn(
|
|
||||||
['event'],
|
|
||||||
`${view.expression}.triggerEventHandlers(${escapeValue(eventName)}, event, ${boundElementIndex})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenViewFactoryName(component: CompileDirectiveMetadata,
|
|
||||||
embeddedTemplateIndex: number): string {
|
|
||||||
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codeGenViewEncapsulation(value: ViewEncapsulation): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `${METADATA_MODULE_REF}${value}`;
|
|
||||||
} else {
|
|
||||||
return `${value}`;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import {CompileNode} from './compile_element';
|
||||||
|
import {TemplateAst} from '../template_ast';
|
||||||
|
|
||||||
|
export class CompileBinding {
|
||||||
|
constructor(public node: CompileNode, public sourceAst: TemplateAst) {}
|
||||||
|
}
|
|
@ -0,0 +1,402 @@
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers, identifierToken} from '../identifiers';
|
||||||
|
import {InjectMethodVars} from './constants';
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {TemplateAst, ProviderAst, ProviderAstType} from '../template_ast';
|
||||||
|
import {
|
||||||
|
CompileTokenMap,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileTokenMetadata,
|
||||||
|
CompileQueryMetadata,
|
||||||
|
CompileProviderMetadata,
|
||||||
|
CompileDiDependencyMetadata,
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompileTypeMetadata
|
||||||
|
} from '../compile_metadata';
|
||||||
|
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
|
||||||
|
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
||||||
|
import {CompileMethod} from './compile_method';
|
||||||
|
|
||||||
|
export class CompileNode {
|
||||||
|
constructor(public parent: CompileElement, public view: CompileView, public nodeIndex: number,
|
||||||
|
public renderNode: o.Expression, public sourceAst: TemplateAst) {}
|
||||||
|
|
||||||
|
isNull(): boolean { return isBlank(this.renderNode); }
|
||||||
|
|
||||||
|
isRootElement(): boolean { return this.view != this.parent.view; }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileElement extends CompileNode {
|
||||||
|
static createNull(): CompileElement {
|
||||||
|
return new CompileElement(null, null, null, null, null, [], [], {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compViewExpr: o.Expression = null;
|
||||||
|
public component: CompileDirectiveMetadata = null;
|
||||||
|
private _appElement: o.Expression;
|
||||||
|
private _defaultInjector: o.Expression;
|
||||||
|
private _instances = new CompileTokenMap<o.Expression>();
|
||||||
|
private _resolvedProviders: CompileTokenMap<ProviderAst>;
|
||||||
|
|
||||||
|
private _queryCount = 0;
|
||||||
|
private _queries = new CompileTokenMap<CompileQuery[]>();
|
||||||
|
private _componentConstructorViewQueryLists: o.Expression[] = [];
|
||||||
|
|
||||||
|
public contentNodesByNgContentIndex: Array<o.Expression>[] = null;
|
||||||
|
public embeddedView: CompileView;
|
||||||
|
public directiveInstances: o.Expression[];
|
||||||
|
|
||||||
|
constructor(parent: CompileElement, view: CompileView, nodeIndex: number,
|
||||||
|
renderNode: o.Expression, sourceAst: TemplateAst,
|
||||||
|
private _directives: CompileDirectiveMetadata[],
|
||||||
|
private _resolvedProvidersArray: ProviderAst[],
|
||||||
|
public variableTokens: {[key: string]: CompileTokenMetadata}) {
|
||||||
|
super(parent, view, nodeIndex, renderNode, sourceAst);
|
||||||
|
}
|
||||||
|
|
||||||
|
setComponent(component: CompileDirectiveMetadata, compViewExpr: o.Expression) {
|
||||||
|
this.component = component;
|
||||||
|
this._compViewExpr = compViewExpr;
|
||||||
|
this.contentNodesByNgContentIndex =
|
||||||
|
ListWrapper.createFixedSize(component.template.ngContentSelectors.length);
|
||||||
|
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||||
|
this.contentNodesByNgContentIndex[i] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setEmbeddedView(embeddedView: CompileView) {
|
||||||
|
this.embeddedView = embeddedView;
|
||||||
|
if (isPresent(embeddedView)) {
|
||||||
|
var createTemplateRefExpr =
|
||||||
|
o.importExpr(Identifiers.TemplateRef_)
|
||||||
|
.instantiate([this.getOrCreateAppElement(), this.embeddedView.viewFactory]);
|
||||||
|
var provider = new CompileProviderMetadata(
|
||||||
|
{token: identifierToken(Identifiers.TemplateRef), useValue: createTemplateRefExpr});
|
||||||
|
// Add TemplateRef as first provider as it does not have deps on other providers
|
||||||
|
this._resolvedProvidersArray.unshift(new ProviderAst(provider.token, false, true, [provider],
|
||||||
|
ProviderAstType.Builtin,
|
||||||
|
this.sourceAst.sourceSpan));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeChildren(): void {
|
||||||
|
this._resolvedProviders = new CompileTokenMap<ProviderAst>();
|
||||||
|
this._resolvedProvidersArray.forEach(provider =>
|
||||||
|
this._resolvedProviders.add(provider.token, provider));
|
||||||
|
|
||||||
|
// create all the provider instances, some in the view constructor,
|
||||||
|
// some as getters. We rely on the fact that they are already sorted topologically.
|
||||||
|
this._resolvedProviders.values().forEach((resolvedProvider) => {
|
||||||
|
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
||||||
|
if (isPresent(provider.useExisting)) {
|
||||||
|
return this._getDependency(
|
||||||
|
resolvedProvider.providerType,
|
||||||
|
new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||||
|
} else if (isPresent(provider.useFactory)) {
|
||||||
|
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
||||||
|
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||||
|
return o.importExpr(provider.useFactory).callFn(depsExpr);
|
||||||
|
} else if (isPresent(provider.useClass)) {
|
||||||
|
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
||||||
|
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||||
|
return o.importExpr(provider.useClass)
|
||||||
|
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||||
|
} else {
|
||||||
|
if (provider.useValue instanceof CompileIdentifierMetadata) {
|
||||||
|
return o.importExpr(provider.useValue);
|
||||||
|
} else if (provider.useValue instanceof o.Expression) {
|
||||||
|
return provider.useValue;
|
||||||
|
} else {
|
||||||
|
return o.literal(provider.useValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`;
|
||||||
|
var instance =
|
||||||
|
createProviderProperty(propName, resolvedProvider, providerValueExpressions,
|
||||||
|
resolvedProvider.multiProvider, resolvedProvider.eager, this);
|
||||||
|
this._instances.add(resolvedProvider.token, instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.directiveInstances =
|
||||||
|
this._directives.map((directive) => this._instances.get(identifierToken(directive.type)));
|
||||||
|
for (var i = 0; i < this.directiveInstances.length; i++) {
|
||||||
|
var directiveInstance = this.directiveInstances[i];
|
||||||
|
var directive = this._directives[i];
|
||||||
|
directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); });
|
||||||
|
}
|
||||||
|
this._resolvedProviders.values().forEach((resolvedProvider) => {
|
||||||
|
var queriesForProvider = this._getQueriesFor(resolvedProvider.token);
|
||||||
|
var providerExpr = this._instances.get(resolvedProvider.token);
|
||||||
|
queriesForProvider.forEach((query) => { query.addValue(providerExpr, this.view); });
|
||||||
|
});
|
||||||
|
StringMapWrapper.forEach(this.variableTokens, (_, varName) => {
|
||||||
|
var token = this.variableTokens[varName];
|
||||||
|
var varValue;
|
||||||
|
var varValueForQuery;
|
||||||
|
if (isPresent(token)) {
|
||||||
|
varValue = varValueForQuery = this._instances.get(token);
|
||||||
|
} else {
|
||||||
|
varValueForQuery = this.getOrCreateAppElement().prop('ref');
|
||||||
|
varValue = this.renderNode;
|
||||||
|
}
|
||||||
|
this.view.variables.set(varName, varValue);
|
||||||
|
this.view.namedAppElements.push([varName, this.getOrCreateAppElement()]);
|
||||||
|
|
||||||
|
var queriesForProvider = this._getQueriesFor(new CompileTokenMetadata({value: varName}));
|
||||||
|
queriesForProvider.forEach((query) => { query.addValue(varValueForQuery, this.view); });
|
||||||
|
});
|
||||||
|
if (isPresent(this.component)) {
|
||||||
|
var componentConstructorViewQueryList =
|
||||||
|
isPresent(this.component) ? o.literalArr(this._componentConstructorViewQueryLists) :
|
||||||
|
o.NULL_EXPR;
|
||||||
|
var compExpr = isPresent(this.getComponent()) ? this.getComponent() : o.NULL_EXPR;
|
||||||
|
this.view.createMethod.addStmt(
|
||||||
|
this.getOrCreateAppElement()
|
||||||
|
.callMethod('initComponent',
|
||||||
|
[compExpr, componentConstructorViewQueryList, this._compViewExpr])
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
afterChildren(childNodeCount: number) {
|
||||||
|
this._resolvedProviders.values().forEach((resolvedProvider) => {
|
||||||
|
// Note: afterChildren is called after recursing into children.
|
||||||
|
// This is good so that an injector match in an element that is closer to a requesting element
|
||||||
|
// matches first.
|
||||||
|
var providerExpr = this._instances.get(resolvedProvider.token);
|
||||||
|
// Note: view providers are only visible on the injector of that element.
|
||||||
|
// This is not fully correct as the rules during codegen don't allow a directive
|
||||||
|
// to get hold of a view provdier on the same element. We still do this semantic
|
||||||
|
// as it simplifies our model to having only one runtime injector per element.
|
||||||
|
var providerChildNodeCount =
|
||||||
|
resolvedProvider.providerType === ProviderAstType.PrivateService ? 0 : childNodeCount;
|
||||||
|
this.view.injectorGetMethod.addStmt(createInjectInternalCondition(
|
||||||
|
this.nodeIndex, providerChildNodeCount, resolvedProvider, providerExpr));
|
||||||
|
});
|
||||||
|
|
||||||
|
this._queries.values().forEach(
|
||||||
|
(queries) =>
|
||||||
|
queries.forEach((query) => query.afterChildren(this.view.updateContentQueriesMethod)));
|
||||||
|
}
|
||||||
|
|
||||||
|
addContentNode(ngContentIndex: number, nodeExpr: o.Expression) {
|
||||||
|
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponent(): o.Expression {
|
||||||
|
return isPresent(this.component) ? this._instances.get(identifierToken(this.component.type)) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProviderTokens(): o.Expression[] {
|
||||||
|
return this._resolvedProviders.values().map(
|
||||||
|
(resolvedProvider) => createDiTokenExpression(resolvedProvider.token));
|
||||||
|
}
|
||||||
|
|
||||||
|
getDeclaredVariablesNames(): string[] {
|
||||||
|
var res = [];
|
||||||
|
StringMapWrapper.forEach(this.variableTokens, (_, key) => { res.push(key); });
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptionalAppElement(): o.Expression { return this._appElement; }
|
||||||
|
|
||||||
|
getOrCreateAppElement(): o.Expression {
|
||||||
|
if (isBlank(this._appElement)) {
|
||||||
|
var parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
||||||
|
var fieldName = `_appEl_${this.nodeIndex}`;
|
||||||
|
this.view.fields.push(new o.ClassField(fieldName, o.importType(Identifiers.AppElement),
|
||||||
|
[o.StmtModifier.Private]));
|
||||||
|
var statement = o.THIS_EXPR.prop(fieldName)
|
||||||
|
.set(o.importExpr(Identifiers.AppElement)
|
||||||
|
.instantiate([
|
||||||
|
o.literal(this.nodeIndex),
|
||||||
|
o.literal(parentNodeIndex),
|
||||||
|
o.THIS_EXPR,
|
||||||
|
this.renderNode
|
||||||
|
]))
|
||||||
|
.toStmt();
|
||||||
|
this.view.createMethod.addStmt(statement);
|
||||||
|
this._appElement = o.THIS_EXPR.prop(fieldName);
|
||||||
|
}
|
||||||
|
return this._appElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrCreateInjector(): o.Expression {
|
||||||
|
if (isBlank(this._defaultInjector)) {
|
||||||
|
var fieldName = `_inj_${this.nodeIndex}`;
|
||||||
|
this.view.fields.push(new o.ClassField(fieldName, o.importType(Identifiers.Injector),
|
||||||
|
[o.StmtModifier.Private]));
|
||||||
|
var statement = o.THIS_EXPR.prop(fieldName)
|
||||||
|
.set(o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]))
|
||||||
|
.toStmt();
|
||||||
|
this.view.createMethod.addStmt(statement);
|
||||||
|
this._defaultInjector = o.THIS_EXPR.prop(fieldName);
|
||||||
|
}
|
||||||
|
return this._defaultInjector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getQueriesFor(token: CompileTokenMetadata): CompileQuery[] {
|
||||||
|
var result: CompileQuery[] = [];
|
||||||
|
var currentEl: CompileElement = this;
|
||||||
|
var distance = 0;
|
||||||
|
var queries: CompileQuery[];
|
||||||
|
while (!currentEl.isNull()) {
|
||||||
|
queries = currentEl._queries.get(token);
|
||||||
|
if (isPresent(queries)) {
|
||||||
|
ListWrapper.addAll(result,
|
||||||
|
queries.filter((query) => query.meta.descendants || distance <= 1));
|
||||||
|
}
|
||||||
|
if (currentEl._directives.length > 0) {
|
||||||
|
distance++;
|
||||||
|
}
|
||||||
|
currentEl = currentEl.parent;
|
||||||
|
}
|
||||||
|
queries = this.view.componentView.viewQueries.get(token);
|
||||||
|
if (isPresent(queries)) {
|
||||||
|
ListWrapper.addAll(result, queries);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addQuery(queryMeta: CompileQueryMetadata,
|
||||||
|
directiveInstance: o.Expression): CompileQuery {
|
||||||
|
var propName = `_query_${queryMeta.selectors[0].name}_${this.nodeIndex}_${this._queryCount++}`;
|
||||||
|
var queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
|
||||||
|
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
||||||
|
addQueryToTokenMap(this._queries, query);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getLocalDependency(requestingProviderType: ProviderAstType,
|
||||||
|
dep: CompileDiDependencyMetadata): o.Expression {
|
||||||
|
var result = null;
|
||||||
|
// constructor content query
|
||||||
|
if (isBlank(result) && isPresent(dep.query)) {
|
||||||
|
result = this._addQuery(dep.query, null).queryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructor view query
|
||||||
|
if (isBlank(result) && isPresent(dep.viewQuery)) {
|
||||||
|
result = createQueryList(
|
||||||
|
dep.viewQuery, null,
|
||||||
|
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
|
||||||
|
this.view);
|
||||||
|
this._componentConstructorViewQueryLists.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPresent(dep.token)) {
|
||||||
|
// access builtins
|
||||||
|
if (isBlank(result)) {
|
||||||
|
if (dep.token.equalsTo(identifierToken(Identifiers.Renderer))) {
|
||||||
|
result = o.THIS_EXPR.prop('renderer');
|
||||||
|
} else if (dep.token.equalsTo(identifierToken(Identifiers.ElementRef))) {
|
||||||
|
result = this.getOrCreateAppElement().prop('ref');
|
||||||
|
} else if (dep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef))) {
|
||||||
|
if (requestingProviderType === ProviderAstType.Component) {
|
||||||
|
return this._compViewExpr.prop('ref');
|
||||||
|
} else {
|
||||||
|
return o.THIS_EXPR.prop('ref');
|
||||||
|
}
|
||||||
|
} else if (dep.token.equalsTo(identifierToken(Identifiers.ViewContainerRef))) {
|
||||||
|
result = this.getOrCreateAppElement().prop('vcRef');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// access providers
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = this._instances.get(dep.token);
|
||||||
|
}
|
||||||
|
// access the injector
|
||||||
|
if (isBlank(result) && dep.token.equalsTo(identifierToken(Identifiers.Injector))) {
|
||||||
|
result = this.getOrCreateInjector();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDependency(requestingProviderType: ProviderAstType,
|
||||||
|
dep: CompileDiDependencyMetadata): o.Expression {
|
||||||
|
var currElement: CompileElement = this;
|
||||||
|
var currView = currElement.view;
|
||||||
|
var result = null;
|
||||||
|
if (dep.isValue) {
|
||||||
|
result = o.literal(dep.value);
|
||||||
|
}
|
||||||
|
if (isBlank(result) && !dep.isSkipSelf) {
|
||||||
|
result = this._getLocalDependency(requestingProviderType, dep);
|
||||||
|
}
|
||||||
|
var resultViewPath = [];
|
||||||
|
// check parent elements
|
||||||
|
while (isBlank(result) && !currElement.parent.isNull()) {
|
||||||
|
currElement = currElement.parent;
|
||||||
|
while (currElement.view !== currView && currView != null) {
|
||||||
|
currView = currView.declarationElement.view;
|
||||||
|
resultViewPath.push(currView);
|
||||||
|
}
|
||||||
|
result = currElement._getLocalDependency(ProviderAstType.PublicService,
|
||||||
|
new CompileDiDependencyMetadata({token: dep.token}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = injectFromViewParentInjector(dep.token, dep.isOptional);
|
||||||
|
}
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = o.NULL_EXPR;
|
||||||
|
}
|
||||||
|
return getPropertyInView(result, resultViewPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInjectInternalCondition(nodeIndex: number, childNodeCount: number,
|
||||||
|
provider: ProviderAst,
|
||||||
|
providerExpr: o.Expression): o.Statement {
|
||||||
|
var indexCondition;
|
||||||
|
if (childNodeCount > 0) {
|
||||||
|
indexCondition = o.literal(nodeIndex)
|
||||||
|
.lowerEquals(InjectMethodVars.requestNodeIndex)
|
||||||
|
.and(InjectMethodVars.requestNodeIndex.lowerEquals(
|
||||||
|
o.literal(nodeIndex + childNodeCount)));
|
||||||
|
} else {
|
||||||
|
indexCondition = o.literal(nodeIndex).identical(InjectMethodVars.requestNodeIndex);
|
||||||
|
}
|
||||||
|
return new o.IfStmt(
|
||||||
|
InjectMethodVars.token.identical(createDiTokenExpression(provider.token)).and(indexCondition),
|
||||||
|
[new o.ReturnStatement(providerExpr)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProviderProperty(propName: string, provider: ProviderAst,
|
||||||
|
providerValueExpressions: o.Expression[], isMulti: boolean,
|
||||||
|
isEager: boolean, compileElement: CompileElement): o.Expression {
|
||||||
|
var view = compileElement.view;
|
||||||
|
var resolvedProviderValueExpr;
|
||||||
|
var type;
|
||||||
|
if (isMulti) {
|
||||||
|
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
|
||||||
|
type = new o.ArrayType(o.DYNAMIC_TYPE);
|
||||||
|
} else {
|
||||||
|
resolvedProviderValueExpr = providerValueExpressions[0];
|
||||||
|
type = providerValueExpressions[0].type;
|
||||||
|
}
|
||||||
|
if (isBlank(type)) {
|
||||||
|
type = o.DYNAMIC_TYPE;
|
||||||
|
}
|
||||||
|
if (isEager) {
|
||||||
|
view.fields.push(new o.ClassField(propName, type, [o.StmtModifier.Private]));
|
||||||
|
view.createMethod.addStmt(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
||||||
|
} else {
|
||||||
|
var internalField = `_${propName}`;
|
||||||
|
view.fields.push(new o.ClassField(internalField, type, [o.StmtModifier.Private]));
|
||||||
|
var getter = new CompileMethod(view);
|
||||||
|
getter.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
|
// Note: Equals is important for JS so that it also checks the undefined case!
|
||||||
|
getter.addStmt(
|
||||||
|
new o.IfStmt(o.THIS_EXPR.prop(internalField).isBlank(),
|
||||||
|
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]));
|
||||||
|
getter.addStmt(new o.ReturnStatement(o.THIS_EXPR.prop(internalField)));
|
||||||
|
view.getters.push(new o.ClassGetter(propName, getter.finish(), type));
|
||||||
|
}
|
||||||
|
return o.THIS_EXPR.prop(propName);
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {TemplateAst} from '../template_ast';
|
||||||
|
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
|
||||||
|
class _DebugState {
|
||||||
|
constructor(public nodeIndex: number, public sourceAst: TemplateAst) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
var NULL_DEBUG_STATE = new _DebugState(null, null);
|
||||||
|
|
||||||
|
export class CompileMethod {
|
||||||
|
private _newState: _DebugState = NULL_DEBUG_STATE;
|
||||||
|
private _currState: _DebugState = NULL_DEBUG_STATE;
|
||||||
|
|
||||||
|
private _debugEnabled: boolean;
|
||||||
|
|
||||||
|
private _bodyStatements: o.Statement[] = [];
|
||||||
|
|
||||||
|
constructor(private _view: CompileView) {
|
||||||
|
this._debugEnabled = this._view.genConfig.genDebugInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _updateDebugContextIfNeeded() {
|
||||||
|
if (this._newState.nodeIndex !== this._currState.nodeIndex ||
|
||||||
|
this._newState.sourceAst !== this._currState.sourceAst) {
|
||||||
|
var expr = this._updateDebugContext(this._newState);
|
||||||
|
if (isPresent(expr)) {
|
||||||
|
this._bodyStatements.push(expr.toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _updateDebugContext(newState: _DebugState): o.Expression {
|
||||||
|
this._currState = this._newState = newState;
|
||||||
|
if (this._debugEnabled) {
|
||||||
|
var sourceLocation =
|
||||||
|
isPresent(newState.sourceAst) ? newState.sourceAst.sourceSpan.start : null;
|
||||||
|
|
||||||
|
return o.THIS_EXPR.callMethod('debug', [
|
||||||
|
o.literal(newState.nodeIndex),
|
||||||
|
isPresent(sourceLocation) ? o.literal(sourceLocation.line) : o.NULL_EXPR,
|
||||||
|
isPresent(sourceLocation) ? o.literal(sourceLocation.col) : o.NULL_EXPR
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
|
||||||
|
var res = this._updateDebugContext(new _DebugState(nodeIndex, templateAst));
|
||||||
|
return isPresent(res) ? res : o.NULL_EXPR;
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDebugInfo(nodeIndex: number, templateAst: TemplateAst) {
|
||||||
|
this._newState = new _DebugState(nodeIndex, templateAst);
|
||||||
|
}
|
||||||
|
|
||||||
|
addStmt(stmt: o.Statement) {
|
||||||
|
this._updateDebugContextIfNeeded();
|
||||||
|
this._bodyStatements.push(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
addStmts(stmts: o.Statement[]) {
|
||||||
|
this._updateDebugContextIfNeeded();
|
||||||
|
ListWrapper.addAll(this._bodyStatements, stmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
finish(): o.Statement[] { return this._bodyStatements; }
|
||||||
|
|
||||||
|
isEmpty(): boolean { return this._bodyStatements.length === 0; }
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers} from '../identifiers';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CompileQueryMetadata,
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompileTokenMap
|
||||||
|
} from '../compile_metadata';
|
||||||
|
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
import {CompileElement} from './compile_element';
|
||||||
|
import {CompileMethod} from './compile_method';
|
||||||
|
import {getPropertyInView} from './util';
|
||||||
|
|
||||||
|
class ViewQueryValues {
|
||||||
|
constructor(public view: CompileView, public values: Array<o.Expression | ViewQueryValues>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileQuery {
|
||||||
|
private _values: ViewQueryValues;
|
||||||
|
|
||||||
|
constructor(public meta: CompileQueryMetadata, public queryList: o.Expression,
|
||||||
|
public ownerDirectiveExpression: o.Expression, public view: CompileView) {
|
||||||
|
this._values = new ViewQueryValues(view, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
addValue(value: o.Expression, view: CompileView) {
|
||||||
|
var currentView = view;
|
||||||
|
var elPath: CompileElement[] = [];
|
||||||
|
var viewPath: CompileView[] = [];
|
||||||
|
while (isPresent(currentView) && currentView !== this.view) {
|
||||||
|
var parentEl = currentView.declarationElement;
|
||||||
|
elPath.unshift(parentEl);
|
||||||
|
currentView = parentEl.view;
|
||||||
|
viewPath.push(currentView);
|
||||||
|
}
|
||||||
|
var queryListForDirtyExpr = getPropertyInView(this.queryList, viewPath);
|
||||||
|
|
||||||
|
var viewValues = this._values;
|
||||||
|
elPath.forEach((el) => {
|
||||||
|
var last =
|
||||||
|
viewValues.values.length > 0 ? viewValues.values[viewValues.values.length - 1] : null;
|
||||||
|
if (last instanceof ViewQueryValues && last.view === el.embeddedView) {
|
||||||
|
viewValues = last;
|
||||||
|
} else {
|
||||||
|
var newViewValues = new ViewQueryValues(el.embeddedView, []);
|
||||||
|
viewValues.values.push(newViewValues);
|
||||||
|
viewValues = newViewValues;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewValues.values.push(value);
|
||||||
|
|
||||||
|
if (elPath.length > 0) {
|
||||||
|
view.dirtyParentQueriesMethod.addStmt(
|
||||||
|
queryListForDirtyExpr.callMethod('setDirty', []).toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
afterChildren(targetMethod: CompileMethod) {
|
||||||
|
var values = createQueryValues(this._values);
|
||||||
|
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
||||||
|
if (isPresent(this.ownerDirectiveExpression)) {
|
||||||
|
var valueExpr = this.meta.first ? this.queryList.prop('first') : this.queryList;
|
||||||
|
updateStmts.push(
|
||||||
|
this.ownerDirectiveExpression.prop(this.meta.propertyName).set(valueExpr).toStmt());
|
||||||
|
}
|
||||||
|
if (!this.meta.first) {
|
||||||
|
updateStmts.push(this.queryList.callMethod('notifyOnChanges', []).toStmt());
|
||||||
|
}
|
||||||
|
targetMethod.addStmt(new o.IfStmt(this.queryList.prop('dirty'), updateStmts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createQueryValues(viewValues: ViewQueryValues): o.Expression[] {
|
||||||
|
return ListWrapper.flatten(viewValues.values.map((entry) => {
|
||||||
|
if (entry instanceof ViewQueryValues) {
|
||||||
|
return mapNestedViews(entry.view.declarationElement.getOrCreateAppElement(), entry.view,
|
||||||
|
createQueryValues(entry));
|
||||||
|
} else {
|
||||||
|
return <o.Expression>entry;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapNestedViews(declarationAppElement: o.Expression, view: CompileView,
|
||||||
|
expressions: o.Expression[]): o.Expression {
|
||||||
|
var adjustedExpressions: o.Expression[] = expressions.map((expr) => {
|
||||||
|
return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr);
|
||||||
|
});
|
||||||
|
return declarationAppElement.callMethod('mapNestedViews', [
|
||||||
|
o.variable(view.className),
|
||||||
|
o.fn([new o.FnParam('nestedView', view.classType)],
|
||||||
|
[new o.ReturnStatement(o.literalArr(adjustedExpressions))])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createQueryList(query: CompileQueryMetadata, directiveInstance: o.Expression,
|
||||||
|
propertyName: string, compileView: CompileView): o.Expression {
|
||||||
|
compileView.fields.push(new o.ClassField(propertyName, o.importType(Identifiers.QueryList),
|
||||||
|
[o.StmtModifier.Private]));
|
||||||
|
var expr = o.THIS_EXPR.prop(propertyName);
|
||||||
|
compileView.createMethod.addStmt(o.THIS_EXPR.prop(propertyName)
|
||||||
|
.set(o.importExpr(Identifiers.QueryList).instantiate([]))
|
||||||
|
.toStmt());
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addQueryToTokenMap(map: CompileTokenMap<CompileQuery[]>, query: CompileQuery) {
|
||||||
|
query.meta.selectors.forEach((selector) => {
|
||||||
|
var entry = map.get(selector);
|
||||||
|
if (isBlank(entry)) {
|
||||||
|
entry = [];
|
||||||
|
map.add(selector, entry);
|
||||||
|
}
|
||||||
|
entry.push(query);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers, identifierToken} from '../identifiers';
|
||||||
|
import {EventHandlerVars} from './constants';
|
||||||
|
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
||||||
|
import {NameResolver} from './expression_converter';
|
||||||
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
|
import {CompileMethod} from './compile_method';
|
||||||
|
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||||
|
import {
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompilePipeMetadata,
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompileTokenMap
|
||||||
|
} from '../compile_metadata';
|
||||||
|
import {
|
||||||
|
getViewFactoryName,
|
||||||
|
injectFromViewParentInjector,
|
||||||
|
createDiTokenExpression,
|
||||||
|
getPropertyInView
|
||||||
|
} from './util';
|
||||||
|
import {CompilerConfig} from '../config';
|
||||||
|
import {CompileBinding} from './compile_binding';
|
||||||
|
|
||||||
|
import {bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
|
||||||
|
|
||||||
|
export class CompilePipe {
|
||||||
|
constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileView implements NameResolver {
|
||||||
|
public viewType: ViewType;
|
||||||
|
public viewQueries: CompileTokenMap<CompileQuery[]>;
|
||||||
|
public namedAppElements: Array<Array<string | o.Expression>> = [];
|
||||||
|
|
||||||
|
public nodes: CompileNode[] = [];
|
||||||
|
public rootNodesOrAppElements: o.Expression[] = [];
|
||||||
|
|
||||||
|
public bindings: CompileBinding[] = [];
|
||||||
|
|
||||||
|
public classStatements: o.Statement[] = [];
|
||||||
|
public createMethod: CompileMethod;
|
||||||
|
public injectorGetMethod: CompileMethod;
|
||||||
|
public updateContentQueriesMethod: CompileMethod;
|
||||||
|
public dirtyParentQueriesMethod: CompileMethod;
|
||||||
|
public updateViewQueriesMethod: CompileMethod;
|
||||||
|
public detectChangesInInputsMethod: CompileMethod;
|
||||||
|
public detectChangesHostPropertiesMethod: CompileMethod;
|
||||||
|
public afterContentLifecycleCallbacksMethod: CompileMethod;
|
||||||
|
public afterViewLifecycleCallbacksMethod: CompileMethod;
|
||||||
|
public destroyMethod: CompileMethod;
|
||||||
|
public eventHandlerMethods: o.ClassMethod[] = [];
|
||||||
|
|
||||||
|
public fields: o.ClassField[] = [];
|
||||||
|
public getters: o.ClassGetter[] = [];
|
||||||
|
public disposables: o.Expression[] = [];
|
||||||
|
public subscriptions: o.Expression[] = [];
|
||||||
|
|
||||||
|
public componentView: CompileView;
|
||||||
|
public pipes = new Map<string, o.Expression>();
|
||||||
|
public variables = new Map<string, o.Expression>();
|
||||||
|
public className: string;
|
||||||
|
public classType: o.Type;
|
||||||
|
public viewFactory: o.ReadVarExpr;
|
||||||
|
|
||||||
|
public literalArrayCount = 0;
|
||||||
|
public literalMapCount = 0;
|
||||||
|
|
||||||
|
constructor(public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||||
|
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
|
||||||
|
public viewIndex: number, public declarationElement: CompileElement,
|
||||||
|
public templateVariableBindings: string[][]) {
|
||||||
|
this.createMethod = new CompileMethod(this);
|
||||||
|
this.injectorGetMethod = new CompileMethod(this);
|
||||||
|
this.updateContentQueriesMethod = new CompileMethod(this);
|
||||||
|
this.dirtyParentQueriesMethod = new CompileMethod(this);
|
||||||
|
this.updateViewQueriesMethod = new CompileMethod(this);
|
||||||
|
this.detectChangesInInputsMethod = new CompileMethod(this);
|
||||||
|
this.detectChangesHostPropertiesMethod = new CompileMethod(this);
|
||||||
|
|
||||||
|
this.afterContentLifecycleCallbacksMethod = new CompileMethod(this);
|
||||||
|
this.afterViewLifecycleCallbacksMethod = new CompileMethod(this);
|
||||||
|
this.destroyMethod = new CompileMethod(this);
|
||||||
|
|
||||||
|
this.viewType = getViewType(component, viewIndex);
|
||||||
|
this.className = `_View_${component.type.name}${viewIndex}`;
|
||||||
|
this.classType = o.importType(new CompileIdentifierMetadata({name: this.className}));
|
||||||
|
this.viewFactory = o.variable(getViewFactoryName(component, viewIndex));
|
||||||
|
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
|
||||||
|
this.componentView = this;
|
||||||
|
} else {
|
||||||
|
this.componentView = this.declarationElement.view.componentView;
|
||||||
|
}
|
||||||
|
var viewQueries = new CompileTokenMap<CompileQuery[]>();
|
||||||
|
if (this.viewType === ViewType.COMPONENT) {
|
||||||
|
var directiveInstance = o.THIS_EXPR.prop('context');
|
||||||
|
ListWrapper.forEachWithIndex(this.component.viewQueries, (queryMeta, queryIndex) => {
|
||||||
|
var propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`;
|
||||||
|
var queryList = createQueryList(queryMeta, directiveInstance, propName, this);
|
||||||
|
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
||||||
|
addQueryToTokenMap(viewQueries, query);
|
||||||
|
});
|
||||||
|
var constructorViewQueryCount = 0;
|
||||||
|
this.component.type.diDeps.forEach((dep) => {
|
||||||
|
if (isPresent(dep.viewQuery)) {
|
||||||
|
var queryList = o.THIS_EXPR.prop('declarationAppElement')
|
||||||
|
.prop('componentConstructorViewQueries')
|
||||||
|
.key(o.literal(constructorViewQueryCount++));
|
||||||
|
var query = new CompileQuery(dep.viewQuery, queryList, null, this);
|
||||||
|
addQueryToTokenMap(viewQueries, query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.viewQueries = viewQueries;
|
||||||
|
templateVariableBindings.forEach((entry) => {
|
||||||
|
this.variables.set(entry[1], o.THIS_EXPR.prop('locals').key(o.literal(entry[0])));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.declarationElement.isNull()) {
|
||||||
|
this.declarationElement.setEmbeddedView(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createPipe(name: string): o.Expression {
|
||||||
|
var pipeMeta: CompilePipeMetadata = this.pipeMetas.find((pipeMeta) => pipeMeta.name == name);
|
||||||
|
var pipeFieldName = pipeMeta.pure ? `_pipe_${name}` : `_pipe_${name}_${this.pipes.size}`;
|
||||||
|
var pipeExpr = this.pipes.get(pipeFieldName);
|
||||||
|
if (isBlank(pipeExpr)) {
|
||||||
|
var deps = pipeMeta.type.diDeps.map((diDep) => {
|
||||||
|
if (diDep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef))) {
|
||||||
|
return o.THIS_EXPR.prop('ref');
|
||||||
|
}
|
||||||
|
return injectFromViewParentInjector(diDep.token, false);
|
||||||
|
});
|
||||||
|
this.fields.push(
|
||||||
|
new o.ClassField(pipeFieldName, o.importType(pipeMeta.type), [o.StmtModifier.Private]));
|
||||||
|
this.createMethod.resetDebugInfo(null, null);
|
||||||
|
this.createMethod.addStmt(o.THIS_EXPR.prop(pipeFieldName)
|
||||||
|
.set(o.importExpr(pipeMeta.type).instantiate(deps))
|
||||||
|
.toStmt());
|
||||||
|
pipeExpr = o.THIS_EXPR.prop(pipeFieldName);
|
||||||
|
this.pipes.set(pipeFieldName, pipeExpr);
|
||||||
|
bindPipeDestroyLifecycleCallbacks(pipeMeta, pipeExpr, this);
|
||||||
|
}
|
||||||
|
return pipeExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVariable(name: string): o.Expression {
|
||||||
|
if (name == EventHandlerVars.event.name) {
|
||||||
|
return EventHandlerVars.event;
|
||||||
|
}
|
||||||
|
var currView: CompileView = this;
|
||||||
|
var result = currView.variables.get(name);
|
||||||
|
var viewPath = [];
|
||||||
|
while (isBlank(result) && isPresent(currView.declarationElement.view)) {
|
||||||
|
currView = currView.declarationElement.view;
|
||||||
|
result = currView.variables.get(name);
|
||||||
|
viewPath.push(currView);
|
||||||
|
}
|
||||||
|
if (isPresent(result)) {
|
||||||
|
return getPropertyInView(result, viewPath);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createLiteralArray(values: o.Expression[]): o.Expression {
|
||||||
|
return o.THIS_EXPR.callMethod('literalArray',
|
||||||
|
[o.literal(this.literalArrayCount++), o.literalArr(values)]);
|
||||||
|
}
|
||||||
|
createLiteralMap(values: Array<Array<string | o.Expression>>): o.Expression {
|
||||||
|
return o.THIS_EXPR.callMethod('literalMap',
|
||||||
|
[o.literal(this.literalMapCount++), o.literalMap(values)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterNodes() {
|
||||||
|
this.viewQueries.values().forEach(
|
||||||
|
(queries) => queries.forEach((query) => query.afterChildren(this.updateViewQueriesMethod)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
|
||||||
|
if (embeddedTemplateIndex > 0) {
|
||||||
|
return ViewType.EMBEDDED;
|
||||||
|
} else if (component.type.isHost) {
|
||||||
|
return ViewType.HOST;
|
||||||
|
} else {
|
||||||
|
return ViewType.COMPONENT;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
import {serializeEnum, isBlank, resolveEnumToken} from 'angular2/src/facade/lang';
|
||||||
|
import {CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
|
import {
|
||||||
|
ChangeDetectorState,
|
||||||
|
ChangeDetectionStrategy
|
||||||
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||||
|
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers} from '../identifiers';
|
||||||
|
|
||||||
|
function _enumExpression(classIdentifier: CompileIdentifierMetadata, value: any): o.Expression {
|
||||||
|
if (isBlank(value)) return o.NULL_EXPR;
|
||||||
|
var name = resolveEnumToken(classIdentifier.runtime, value);
|
||||||
|
return o.importExpr(new CompileIdentifierMetadata({
|
||||||
|
name: `${classIdentifier.name}.${name}`,
|
||||||
|
moduleUrl: classIdentifier.moduleUrl,
|
||||||
|
runtime: value
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewTypeEnum {
|
||||||
|
static fromValue(value: ViewType): o.Expression {
|
||||||
|
return _enumExpression(Identifiers.ViewType, value);
|
||||||
|
}
|
||||||
|
static HOST = ViewTypeEnum.fromValue(ViewType.HOST);
|
||||||
|
static COMPONENT = ViewTypeEnum.fromValue(ViewType.COMPONENT);
|
||||||
|
static EMBEDDED = ViewTypeEnum.fromValue(ViewType.EMBEDDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewEncapsulationEnum {
|
||||||
|
static fromValue(value: ViewEncapsulation): o.Expression {
|
||||||
|
return _enumExpression(Identifiers.ViewEncapsulation, value);
|
||||||
|
}
|
||||||
|
static Emulated = ViewEncapsulationEnum.fromValue(ViewEncapsulation.Emulated);
|
||||||
|
static Native = ViewEncapsulationEnum.fromValue(ViewEncapsulation.Native);
|
||||||
|
static None = ViewEncapsulationEnum.fromValue(ViewEncapsulation.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeDetectorStateEnum {
|
||||||
|
static fromValue(value: ChangeDetectorState): o.Expression {
|
||||||
|
return _enumExpression(Identifiers.ChangeDetectorState, value);
|
||||||
|
}
|
||||||
|
static NeverChecked = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.NeverChecked);
|
||||||
|
static CheckedBefore = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.CheckedBefore);
|
||||||
|
static Errored = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.Errored);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeDetectionStrategyEnum {
|
||||||
|
static fromValue(value: ChangeDetectionStrategy): o.Expression {
|
||||||
|
return _enumExpression(Identifiers.ChangeDetectionStrategy, value);
|
||||||
|
}
|
||||||
|
static CheckOnce = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.CheckOnce);
|
||||||
|
static Checked = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Checked);
|
||||||
|
static CheckAlways = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.CheckAlways);
|
||||||
|
static Detached = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Detached);
|
||||||
|
static OnPush = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.OnPush);
|
||||||
|
static Default = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewConstructorVars {
|
||||||
|
static viewManager = o.variable('viewManager');
|
||||||
|
static parentInjector = o.variable('parentInjector');
|
||||||
|
static declarationEl = o.variable('declarationEl');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ViewProperties {
|
||||||
|
static renderer = o.THIS_EXPR.prop('renderer');
|
||||||
|
static projectableNodes = o.THIS_EXPR.prop('projectableNodes');
|
||||||
|
static viewManager = o.THIS_EXPR.prop('viewManager');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventHandlerVars { static event = o.variable('$event'); }
|
||||||
|
|
||||||
|
export class InjectMethodVars {
|
||||||
|
static token = o.variable('token');
|
||||||
|
static requestNodeIndex = o.variable('requestNodeIndex');
|
||||||
|
static notFoundResult = o.variable('notFoundResult');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DetectChangesVars {
|
||||||
|
static throwOnChange = o.variable(`throwOnChange`);
|
||||||
|
static changes = o.variable(`changes`);
|
||||||
|
static changed = o.variable(`changed`);
|
||||||
|
static valUnwrapper = o.variable(`valUnwrapper`);
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {EventHandlerVars, ViewProperties} from './constants';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {CompileElement} from './compile_element';
|
||||||
|
import {CompileMethod} from './compile_method';
|
||||||
|
|
||||||
|
import {BoundEventAst, DirectiveAst} from '../template_ast';
|
||||||
|
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
|
|
||||||
|
import {convertCdStatementToIr} from './expression_converter';
|
||||||
|
import {CompileBinding} from './compile_binding';
|
||||||
|
|
||||||
|
export class CompileEventListener {
|
||||||
|
private _method: CompileMethod;
|
||||||
|
private _hasComponentHostListener: boolean = false;
|
||||||
|
private _methodName: string;
|
||||||
|
private _eventParam: o.FnParam;
|
||||||
|
private _actionResultExprs: o.Expression[] = [];
|
||||||
|
|
||||||
|
static getOrCreate(compileElement: CompileElement, eventTarget: string, eventName: string,
|
||||||
|
targetEventListeners: CompileEventListener[]): CompileEventListener {
|
||||||
|
var listener = targetEventListeners.find(listener => listener.eventTarget == eventTarget &&
|
||||||
|
listener.eventName == eventName);
|
||||||
|
if (isBlank(listener)) {
|
||||||
|
listener = new CompileEventListener(compileElement, eventTarget, eventName,
|
||||||
|
targetEventListeners.length);
|
||||||
|
targetEventListeners.push(listener);
|
||||||
|
}
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(public compileElement: CompileElement, public eventTarget: string,
|
||||||
|
public eventName: string, listenerIndex: number) {
|
||||||
|
this._method = new CompileMethod(compileElement.view);
|
||||||
|
this._methodName =
|
||||||
|
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
||||||
|
this._eventParam =
|
||||||
|
new o.FnParam(EventHandlerVars.event.name,
|
||||||
|
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
addAction(hostEvent: BoundEventAst, directive: CompileDirectiveMetadata,
|
||||||
|
directiveInstance: o.Expression) {
|
||||||
|
if (isPresent(directive) && directive.isComponent) {
|
||||||
|
this._hasComponentHostListener = true;
|
||||||
|
}
|
||||||
|
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
|
||||||
|
var context = isPresent(directiveInstance) ? directiveInstance : o.THIS_EXPR.prop('context');
|
||||||
|
var actionStmts = convertCdStatementToIr(this.compileElement.view, context, hostEvent.handler);
|
||||||
|
var lastIndex = actionStmts.length - 1;
|
||||||
|
if (lastIndex >= 0) {
|
||||||
|
var lastStatement = actionStmts[lastIndex];
|
||||||
|
var returnExpr = convertStmtIntoExpression(lastStatement);
|
||||||
|
var preventDefaultVar = o.variable(`pd_${this._actionResultExprs.length}`);
|
||||||
|
this._actionResultExprs.push(preventDefaultVar);
|
||||||
|
if (isPresent(returnExpr)) {
|
||||||
|
// Note: We need to cast the result of the method call to dynamic,
|
||||||
|
// as it might be a void method!
|
||||||
|
actionStmts[lastIndex] =
|
||||||
|
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._method.addStmts(actionStmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
finishMethod() {
|
||||||
|
var markPathToRootStart =
|
||||||
|
this._hasComponentHostListener ?
|
||||||
|
this.compileElement.getOrCreateAppElement().prop('componentView') :
|
||||||
|
o.THIS_EXPR;
|
||||||
|
var resultExpr: o.Expression = o.literal(true);
|
||||||
|
this._actionResultExprs.forEach((expr) => { resultExpr = resultExpr.and(expr); });
|
||||||
|
var stmts =
|
||||||
|
(<o.Statement[]>[markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt()])
|
||||||
|
.concat(this._method.finish())
|
||||||
|
.concat([new o.ReturnStatement(resultExpr)]);
|
||||||
|
this.compileElement.view.eventHandlerMethods.push(new o.ClassMethod(
|
||||||
|
this._methodName, [this._eventParam], stmts, o.BOOL_TYPE, [o.StmtModifier.Private]));
|
||||||
|
}
|
||||||
|
|
||||||
|
listenToRenderer() {
|
||||||
|
var listenExpr;
|
||||||
|
var eventListener = o.THIS_EXPR.callMethod('eventHandler', [
|
||||||
|
o.fn([this._eventParam],
|
||||||
|
[
|
||||||
|
new o.ReturnStatement(
|
||||||
|
o.THIS_EXPR.callMethod(this._methodName, [EventHandlerVars.event]))
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
if (isPresent(this.eventTarget)) {
|
||||||
|
listenExpr = ViewProperties.renderer.callMethod(
|
||||||
|
'listenGlobal', [o.literal(this.eventTarget), o.literal(this.eventName), eventListener]);
|
||||||
|
} else {
|
||||||
|
listenExpr = ViewProperties.renderer.callMethod(
|
||||||
|
'listen', [this.compileElement.renderNode, o.literal(this.eventName), eventListener]);
|
||||||
|
}
|
||||||
|
var disposable = o.variable(`disposable_${this.compileElement.view.disposables.length}`);
|
||||||
|
this.compileElement.view.disposables.push(disposable);
|
||||||
|
this.compileElement.view.createMethod.addStmt(
|
||||||
|
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||||
|
}
|
||||||
|
|
||||||
|
listenToDirective(directiveInstance: o.Expression, observablePropName: string) {
|
||||||
|
var subscription = o.variable(`subscription_${this.compileElement.view.subscriptions.length}`);
|
||||||
|
this.compileElement.view.subscriptions.push(subscription);
|
||||||
|
var eventListener = o.THIS_EXPR.callMethod('eventHandler', [
|
||||||
|
o.fn([this._eventParam],
|
||||||
|
[o.THIS_EXPR.callMethod(this._methodName, [EventHandlerVars.event]).toStmt()])
|
||||||
|
]);
|
||||||
|
this.compileElement.view.createMethod.addStmt(
|
||||||
|
subscription.set(directiveInstance.prop(observablePropName)
|
||||||
|
.callMethod(o.BuiltinMethod.SubscribeObservable, [eventListener]))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectEventListeners(hostEvents: BoundEventAst[], dirs: DirectiveAst[],
|
||||||
|
compileElement: CompileElement): CompileEventListener[] {
|
||||||
|
var eventListeners: CompileEventListener[] = [];
|
||||||
|
hostEvents.forEach((hostEvent) => {
|
||||||
|
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||||
|
var listener = CompileEventListener.getOrCreate(compileElement, hostEvent.target,
|
||||||
|
hostEvent.name, eventListeners);
|
||||||
|
listener.addAction(hostEvent, null, null);
|
||||||
|
});
|
||||||
|
ListWrapper.forEachWithIndex(dirs, (directiveAst, i) => {
|
||||||
|
var directiveInstance = compileElement.directiveInstances[i];
|
||||||
|
directiveAst.hostEvents.forEach((hostEvent) => {
|
||||||
|
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||||
|
var listener = CompileEventListener.getOrCreate(compileElement, hostEvent.target,
|
||||||
|
hostEvent.name, eventListeners);
|
||||||
|
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
eventListeners.forEach((listener) => listener.finishMethod());
|
||||||
|
return eventListeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveOutputs(directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||||
|
eventListeners: CompileEventListener[]) {
|
||||||
|
StringMapWrapper.forEach(directiveAst.directive.outputs, (eventName, observablePropName) => {
|
||||||
|
eventListeners.filter(listener => listener.eventName == eventName)
|
||||||
|
.forEach(
|
||||||
|
(listener) => { listener.listenToDirective(directiveInstance, observablePropName); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
|
||||||
|
eventListeners.forEach(listener => listener.listenToRenderer());
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
||||||
|
if (stmt instanceof o.ExpressionStatement) {
|
||||||
|
return stmt.expr;
|
||||||
|
} else if (stmt instanceof o.ReturnStatement) {
|
||||||
|
return stmt.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function santitizeEventName(name: string): string {
|
||||||
|
return StringWrapper.replaceAll(name, /[^a-zA-Z_]/g, '_');
|
||||||
|
}
|
|
@ -0,0 +1,254 @@
|
||||||
|
import * as cdAst from '../expression_parser/ast';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers} from '../identifiers';
|
||||||
|
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {isBlank, isPresent, isArray, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
var IMPLICIT_RECEIVER = o.variable('#implicit');
|
||||||
|
|
||||||
|
export interface NameResolver {
|
||||||
|
createPipe(name: string): o.Expression;
|
||||||
|
getVariable(name: string): o.Expression;
|
||||||
|
createLiteralArray(values: o.Expression[]): o.Expression;
|
||||||
|
createLiteralMap(values: Array<Array<string | o.Expression>>): o.Expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExpressionWithWrappedValueInfo {
|
||||||
|
constructor(public expression: o.Expression, public needsValueUnwrapper: boolean) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertCdExpressionToIr(
|
||||||
|
nameResolver: NameResolver, implicitReceiver: o.Expression, expression: cdAst.AST,
|
||||||
|
valueUnwrapper: o.ReadVarExpr): ExpressionWithWrappedValueInfo {
|
||||||
|
var visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, valueUnwrapper);
|
||||||
|
var irAst: o.Expression = expression.visit(visitor, _Mode.Expression);
|
||||||
|
return new ExpressionWithWrappedValueInfo(irAst, visitor.needsValueUnwrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertCdStatementToIr(nameResolver: NameResolver, implicitReceiver: o.Expression,
|
||||||
|
stmt: cdAst.AST): o.Statement[] {
|
||||||
|
var visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, null);
|
||||||
|
var statements = [];
|
||||||
|
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements);
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum _Mode {
|
||||||
|
Statement,
|
||||||
|
Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureStatementMode(mode: _Mode, ast: cdAst.AST) {
|
||||||
|
if (mode !== _Mode.Statement) {
|
||||||
|
throw new BaseException(`Expected a statement, but saw ${ast}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureExpressionMode(mode: _Mode, ast: cdAst.AST) {
|
||||||
|
if (mode !== _Mode.Expression) {
|
||||||
|
throw new BaseException(`Expected an expression, but saw ${ast}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expression | o.Statement {
|
||||||
|
if (mode === _Mode.Statement) {
|
||||||
|
return expr.toStmt();
|
||||||
|
} else {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||||
|
public needsValueUnwrapper: boolean = false;
|
||||||
|
|
||||||
|
constructor(private _nameResolver: NameResolver, private _implicitReceiver: o.Expression,
|
||||||
|
private _valueUnwrapper: o.ReadVarExpr) {}
|
||||||
|
|
||||||
|
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
||||||
|
var op;
|
||||||
|
switch (ast.operation) {
|
||||||
|
case '+':
|
||||||
|
op = o.BinaryOperator.Plus;
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
op = o.BinaryOperator.Minus;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
op = o.BinaryOperator.Multiply;
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
op = o.BinaryOperator.Divide;
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
op = o.BinaryOperator.Modulo;
|
||||||
|
break;
|
||||||
|
case '&&':
|
||||||
|
op = o.BinaryOperator.And;
|
||||||
|
break;
|
||||||
|
case '||':
|
||||||
|
op = o.BinaryOperator.Or;
|
||||||
|
break;
|
||||||
|
case '==':
|
||||||
|
op = o.BinaryOperator.Equals;
|
||||||
|
break;
|
||||||
|
case '!=':
|
||||||
|
op = o.BinaryOperator.NotEquals;
|
||||||
|
break;
|
||||||
|
case '===':
|
||||||
|
op = o.BinaryOperator.Identical;
|
||||||
|
break;
|
||||||
|
case '!==':
|
||||||
|
op = o.BinaryOperator.NotIdentical;
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
op = o.BinaryOperator.Lower;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
op = o.BinaryOperator.Bigger;
|
||||||
|
break;
|
||||||
|
case '<=':
|
||||||
|
op = o.BinaryOperator.LowerEquals;
|
||||||
|
break;
|
||||||
|
case '>=':
|
||||||
|
op = o.BinaryOperator.BiggerEquals;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new BaseException(`Unsupported operation ${ast.operation}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, new o.BinaryOperatorExpr(op, ast.left.visit(this, _Mode.Expression),
|
||||||
|
ast.right.visit(this, _Mode.Expression)));
|
||||||
|
}
|
||||||
|
visitChain(ast: cdAst.Chain, mode: _Mode): any {
|
||||||
|
ensureStatementMode(mode, ast);
|
||||||
|
return this.visitAll(ast.expressions, mode);
|
||||||
|
}
|
||||||
|
visitConditional(ast: cdAst.Conditional, mode: _Mode): any {
|
||||||
|
var value: o.Expression = ast.condition.visit(this, _Mode.Expression);
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, value.conditional(ast.trueExp.visit(this, _Mode.Expression),
|
||||||
|
ast.falseExp.visit(this, _Mode.Expression)));
|
||||||
|
}
|
||||||
|
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
|
||||||
|
var pipeInstance = this._nameResolver.createPipe(ast.name);
|
||||||
|
var input = ast.exp.visit(this, _Mode.Expression);
|
||||||
|
var args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
|
this.needsValueUnwrapper = true;
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, this._valueUnwrapper.callMethod(
|
||||||
|
'unwrap', [pipeInstance.callMethod('transform', [input, o.literalArr(args)])]));
|
||||||
|
}
|
||||||
|
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
|
||||||
|
return convertToStatementIfNeeded(mode, ast.target.visit(this, _Mode.Expression)
|
||||||
|
.callFn(this.visitAll(ast.args, _Mode.Expression)));
|
||||||
|
}
|
||||||
|
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
|
||||||
|
ensureExpressionMode(mode, ast);
|
||||||
|
return IMPLICIT_RECEIVER;
|
||||||
|
}
|
||||||
|
visitInterpolation(ast: cdAst.Interpolation, mode: _Mode): any {
|
||||||
|
ensureExpressionMode(mode, ast);
|
||||||
|
var args = [o.literal(ast.expressions.length)];
|
||||||
|
for (var i = 0; i < ast.strings.length - 1; i++) {
|
||||||
|
args.push(o.literal(ast.strings[i]));
|
||||||
|
args.push(ast.expressions[i].visit(this, _Mode.Expression));
|
||||||
|
}
|
||||||
|
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||||
|
return o.importExpr(Identifiers.interpolate).callFn(args);
|
||||||
|
}
|
||||||
|
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, ast.obj.visit(this, _Mode.Expression).key(ast.key.visit(this, _Mode.Expression)));
|
||||||
|
}
|
||||||
|
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
||||||
|
var obj: o.Expression = ast.obj.visit(this, _Mode.Expression);
|
||||||
|
var key: o.Expression = ast.key.visit(this, _Mode.Expression);
|
||||||
|
var value: o.Expression = ast.value.visit(this, _Mode.Expression);
|
||||||
|
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
||||||
|
}
|
||||||
|
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
|
||||||
|
}
|
||||||
|
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||||
|
var parts = [];
|
||||||
|
for (var i = 0; i < ast.keys.length; i++) {
|
||||||
|
parts.push([ast.keys[i], ast.values[i].visit(this, _Mode.Expression)]);
|
||||||
|
}
|
||||||
|
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
|
||||||
|
}
|
||||||
|
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
||||||
|
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
||||||
|
}
|
||||||
|
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
||||||
|
var args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
|
var result = null;
|
||||||
|
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||||
|
if (receiver === IMPLICIT_RECEIVER) {
|
||||||
|
var varExpr = this._nameResolver.getVariable(ast.name);
|
||||||
|
if (isPresent(varExpr)) {
|
||||||
|
result = varExpr.callFn(args);
|
||||||
|
} else {
|
||||||
|
receiver = this._implicitReceiver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = receiver.callMethod(ast.name, args);
|
||||||
|
}
|
||||||
|
return convertToStatementIfNeeded(mode, result);
|
||||||
|
}
|
||||||
|
visitPrefixNot(ast: cdAst.PrefixNot, mode: _Mode): any {
|
||||||
|
return convertToStatementIfNeeded(mode, o.not(ast.expression.visit(this, _Mode.Expression)));
|
||||||
|
}
|
||||||
|
visitPropertyRead(ast: cdAst.PropertyRead, mode: _Mode): any {
|
||||||
|
var result = null;
|
||||||
|
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||||
|
if (receiver === IMPLICIT_RECEIVER) {
|
||||||
|
result = this._nameResolver.getVariable(ast.name);
|
||||||
|
if (isBlank(result)) {
|
||||||
|
receiver = this._implicitReceiver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = receiver.prop(ast.name);
|
||||||
|
}
|
||||||
|
return convertToStatementIfNeeded(mode, result);
|
||||||
|
}
|
||||||
|
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||||
|
var receiver: o.Expression = ast.receiver.visit(this, _Mode.Expression);
|
||||||
|
if (receiver === IMPLICIT_RECEIVER) {
|
||||||
|
var varExpr = this._nameResolver.getVariable(ast.name);
|
||||||
|
if (isPresent(varExpr)) {
|
||||||
|
throw new BaseException('Cannot reassign a variable binding');
|
||||||
|
}
|
||||||
|
receiver = this._implicitReceiver;
|
||||||
|
}
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, receiver.prop(ast.name).set(ast.value.visit(this, _Mode.Expression)));
|
||||||
|
}
|
||||||
|
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
||||||
|
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, receiver.isBlank().conditional(o.NULL_EXPR, receiver.prop(ast.name)));
|
||||||
|
}
|
||||||
|
visitSafeMethodCall(ast: cdAst.SafeMethodCall, mode: _Mode): any {
|
||||||
|
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||||
|
var args = this.visitAll(ast.args, _Mode.Expression);
|
||||||
|
return convertToStatementIfNeeded(
|
||||||
|
mode, receiver.isBlank().conditional(o.NULL_EXPR, receiver.callMethod(ast.name, args)));
|
||||||
|
}
|
||||||
|
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => ast.visit(this, mode)); }
|
||||||
|
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
|
||||||
|
throw new BaseException('Quotes are not supported for evaluation!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenStatements(arg: any, output: o.Statement[]) {
|
||||||
|
if (isArray(arg)) {
|
||||||
|
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
|
||||||
|
} else {
|
||||||
|
output.push(arg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {DetectChangesVars, ChangeDetectorStateEnum} from './constants';
|
||||||
|
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||||
|
|
||||||
|
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
import {DirectiveAst} from '../template_ast';
|
||||||
|
import {CompileElement} from './compile_element';
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
|
||||||
|
var STATE_IS_NEVER_CHECKED =
|
||||||
|
o.THIS_EXPR.prop('cdState').identical(ChangeDetectorStateEnum.NeverChecked);
|
||||||
|
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
|
||||||
|
|
||||||
|
export function bindDirectiveDetectChangesLifecycleCallbacks(
|
||||||
|
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
|
||||||
|
var view = compileElement.view;
|
||||||
|
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
||||||
|
var lifecycleHooks = directiveAst.directive.lifecycleHooks;
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 && directiveAst.inputs.length > 0) {
|
||||||
|
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
||||||
|
DetectChangesVars.changes.notIdentical(o.NULL_EXPR),
|
||||||
|
[directiveInstance.callMethod('ngOnChanges', [DetectChangesVars.changes]).toStmt()]));
|
||||||
|
}
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1) {
|
||||||
|
detectChangesInInputsMethod.addStmt(
|
||||||
|
new o.IfStmt(STATE_IS_NEVER_CHECKED.and(NOT_THROW_ON_CHANGES),
|
||||||
|
[directiveInstance.callMethod('ngOnInit', []).toStmt()]));
|
||||||
|
}
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1) {
|
||||||
|
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
||||||
|
NOT_THROW_ON_CHANGES, [directiveInstance.callMethod('ngDoCheck', []).toStmt()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveAfterContentLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
|
||||||
|
directiveInstance: o.Expression,
|
||||||
|
compileElement: CompileElement) {
|
||||||
|
var view = compileElement.view;
|
||||||
|
var lifecycleHooks = directiveMeta.lifecycleHooks;
|
||||||
|
var afterContentLifecycleCallbacksMethod = view.afterContentLifecycleCallbacksMethod;
|
||||||
|
afterContentLifecycleCallbacksMethod.resetDebugInfo(compileElement.nodeIndex,
|
||||||
|
compileElement.sourceAst);
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1) {
|
||||||
|
afterContentLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||||
|
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterContentInit', []).toStmt()]));
|
||||||
|
}
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1) {
|
||||||
|
afterContentLifecycleCallbacksMethod.addStmt(
|
||||||
|
directiveInstance.callMethod('ngAfterContentChecked', []).toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveAfterViewLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
|
||||||
|
directiveInstance: o.Expression,
|
||||||
|
compileElement: CompileElement) {
|
||||||
|
var view = compileElement.view;
|
||||||
|
var lifecycleHooks = directiveMeta.lifecycleHooks;
|
||||||
|
var afterViewLifecycleCallbacksMethod = view.afterViewLifecycleCallbacksMethod;
|
||||||
|
afterViewLifecycleCallbacksMethod.resetDebugInfo(compileElement.nodeIndex,
|
||||||
|
compileElement.sourceAst);
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1) {
|
||||||
|
afterViewLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||||
|
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterViewInit', []).toStmt()]));
|
||||||
|
}
|
||||||
|
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1) {
|
||||||
|
afterViewLifecycleCallbacksMethod.addStmt(
|
||||||
|
directiveInstance.callMethod('ngAfterViewChecked', []).toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveDestroyLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
|
||||||
|
directiveInstance: o.Expression,
|
||||||
|
compileElement: CompileElement) {
|
||||||
|
var onDestroyMethod = compileElement.view.destroyMethod;
|
||||||
|
onDestroyMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
|
if (directiveMeta.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||||
|
onDestroyMethod.addStmt(directiveInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindPipeDestroyLifecycleCallbacks(
|
||||||
|
pipeMeta: CompilePipeMetadata, directiveInstance: o.Expression, view: CompileView) {
|
||||||
|
var onDestroyMethod = view.destroyMethod;
|
||||||
|
if (pipeMeta.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||||
|
onDestroyMethod.addStmt(directiveInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
import * as cdAst from '../expression_parser/ast';
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers} from '../identifiers';
|
||||||
|
import {DetectChangesVars} from './constants';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BoundTextAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
DirectiveAst,
|
||||||
|
PropertyBindingType,
|
||||||
|
TemplateAst
|
||||||
|
} from '../template_ast';
|
||||||
|
|
||||||
|
import {isBlank, isPresent, isArray, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
|
import {CompileMethod} from './compile_method';
|
||||||
|
|
||||||
|
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||||
|
import {isDefaultChangeDetectionStrategy} from 'angular2/src/core/change_detection/constants';
|
||||||
|
import {camelCaseToDashCase} from '../util';
|
||||||
|
|
||||||
|
import {convertCdExpressionToIr} from './expression_converter';
|
||||||
|
|
||||||
|
import {CompileBinding} from './compile_binding';
|
||||||
|
|
||||||
|
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
|
||||||
|
return o.THIS_EXPR.prop(`_expr_${exprIndex}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
|
||||||
|
return o.variable(`currVal_${exprIndex}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind(view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
||||||
|
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
|
||||||
|
method: CompileMethod) {
|
||||||
|
var checkExpression =
|
||||||
|
convertCdExpressionToIr(view, context, parsedExpression, DetectChangesVars.valUnwrapper);
|
||||||
|
if (isBlank(checkExpression.expression)) {
|
||||||
|
// e.g. an empty expression was given
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||||
|
view.createMethod.addStmt(
|
||||||
|
o.THIS_EXPR.prop(fieldExpr.name).set(o.importExpr(Identifiers.uninitialized)).toStmt());
|
||||||
|
|
||||||
|
if (checkExpression.needsValueUnwrapper) {
|
||||||
|
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
|
||||||
|
method.addStmt(initValueUnwrapperStmt);
|
||||||
|
}
|
||||||
|
method.addStmt(
|
||||||
|
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
|
||||||
|
var condition: o.Expression =
|
||||||
|
o.importExpr(Identifiers.checkBinding)
|
||||||
|
.callFn([DetectChangesVars.throwOnChange, fieldExpr, currValExpr]);
|
||||||
|
if (checkExpression.needsValueUnwrapper) {
|
||||||
|
condition = DetectChangesVars.valUnwrapper.prop('hasWrappedValue').or(condition);
|
||||||
|
}
|
||||||
|
method.addStmt(new o.IfStmt(
|
||||||
|
condition,
|
||||||
|
actions.concat([<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(currValExpr).toStmt()])));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindRenderText(boundText: BoundTextAst, compileNode: CompileNode,
|
||||||
|
view: CompileView) {
|
||||||
|
var bindingIndex = view.bindings.length;
|
||||||
|
view.bindings.push(new CompileBinding(compileNode, boundText));
|
||||||
|
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||||
|
var valueField = createBindFieldExpr(bindingIndex);
|
||||||
|
view.detectChangesInInputsMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
||||||
|
|
||||||
|
bind(view, currValExpr, valueField, boundText.value, o.THIS_EXPR.prop('context'),
|
||||||
|
[
|
||||||
|
o.THIS_EXPR.prop('renderer')
|
||||||
|
.callMethod('setText', [compileNode.renderNode, currValExpr])
|
||||||
|
.toStmt()
|
||||||
|
],
|
||||||
|
view.detectChangesInInputsMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindAndWriteToRenderer(boundProps: BoundElementPropertyAst[], context: o.Expression,
|
||||||
|
compileElement: CompileElement) {
|
||||||
|
var view = compileElement.view;
|
||||||
|
var renderNode = compileElement.renderNode;
|
||||||
|
boundProps.forEach((boundProp) => {
|
||||||
|
var bindingIndex = view.bindings.length;
|
||||||
|
view.bindings.push(new CompileBinding(compileElement, boundProp));
|
||||||
|
view.detectChangesHostPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||||
|
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||||
|
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||||
|
var renderMethod: string;
|
||||||
|
var renderValue: o.Expression = currValExpr;
|
||||||
|
var updateStmts = [];
|
||||||
|
switch (boundProp.type) {
|
||||||
|
case PropertyBindingType.Property:
|
||||||
|
renderMethod = 'setElementProperty';
|
||||||
|
if (view.genConfig.logBindingUpdate) {
|
||||||
|
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, currValExpr));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Attribute:
|
||||||
|
renderMethod = 'setElementAttribute';
|
||||||
|
renderValue =
|
||||||
|
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Class:
|
||||||
|
renderMethod = 'setElementClass';
|
||||||
|
break;
|
||||||
|
case PropertyBindingType.Style:
|
||||||
|
renderMethod = 'setElementStyle';
|
||||||
|
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||||
|
if (isPresent(boundProp.unit)) {
|
||||||
|
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||||
|
}
|
||||||
|
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
updateStmts.push(
|
||||||
|
o.THIS_EXPR.prop('renderer')
|
||||||
|
.callMethod(renderMethod, [renderNode, o.literal(boundProp.name), renderValue])
|
||||||
|
.toStmt());
|
||||||
|
|
||||||
|
bind(view, currValExpr, fieldExpr, boundProp.value, context, updateStmts,
|
||||||
|
view.detectChangesHostPropertiesMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindRenderInputs(boundProps: BoundElementPropertyAst[],
|
||||||
|
compileElement: CompileElement): void {
|
||||||
|
bindAndWriteToRenderer(boundProps, o.THIS_EXPR.prop('context'), compileElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveHostProps(directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||||
|
compileElement: CompileElement): void {
|
||||||
|
bindAndWriteToRenderer(directiveAst.hostProperties, directiveInstance, compileElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindDirectiveInputs(directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||||
|
compileElement: CompileElement) {
|
||||||
|
if (directiveAst.inputs.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var view = compileElement.view;
|
||||||
|
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
||||||
|
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||||
|
|
||||||
|
var lifecycleHooks = directiveAst.directive.lifecycleHooks;
|
||||||
|
var calcChangesMap = lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
|
||||||
|
var isOnPushComp = directiveAst.directive.isComponent &&
|
||||||
|
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
||||||
|
if (calcChangesMap) {
|
||||||
|
detectChangesInInputsMethod.addStmt(DetectChangesVars.changes.set(o.NULL_EXPR).toStmt());
|
||||||
|
}
|
||||||
|
if (isOnPushComp) {
|
||||||
|
detectChangesInInputsMethod.addStmt(DetectChangesVars.changed.set(o.literal(false)).toStmt());
|
||||||
|
}
|
||||||
|
directiveAst.inputs.forEach((input) => {
|
||||||
|
var bindingIndex = view.bindings.length;
|
||||||
|
view.bindings.push(new CompileBinding(compileElement, input));
|
||||||
|
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
||||||
|
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||||
|
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||||
|
var statements: o.Statement[] =
|
||||||
|
[directiveInstance.prop(input.directiveName).set(currValExpr).toStmt()];
|
||||||
|
if (calcChangesMap) {
|
||||||
|
statements.push(new o.IfStmt(DetectChangesVars.changes.identical(o.NULL_EXPR), [
|
||||||
|
DetectChangesVars.changes.set(o.literalMap([], new o.MapType(
|
||||||
|
o.importType(Identifiers.SimpleChange))))
|
||||||
|
.toStmt()
|
||||||
|
]));
|
||||||
|
statements.push(
|
||||||
|
DetectChangesVars.changes.key(o.literal(input.directiveName))
|
||||||
|
.set(o.importExpr(Identifiers.SimpleChange).instantiate([fieldExpr, currValExpr]))
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
if (isOnPushComp) {
|
||||||
|
statements.push(DetectChangesVars.changed.set(o.literal(true)).toStmt());
|
||||||
|
}
|
||||||
|
if (view.genConfig.logBindingUpdate) {
|
||||||
|
statements.push(
|
||||||
|
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
|
||||||
|
}
|
||||||
|
bind(view, currValExpr, fieldExpr, input.value, o.THIS_EXPR.prop('context'), statements,
|
||||||
|
detectChangesInInputsMethod);
|
||||||
|
});
|
||||||
|
if (isOnPushComp) {
|
||||||
|
detectChangesInInputsMethod.addStmt(new o.IfStmt(DetectChangesVars.changed, [
|
||||||
|
compileElement.getOrCreateAppElement()
|
||||||
|
.prop('componentView')
|
||||||
|
.callMethod('markAsCheckOnce', [])
|
||||||
|
.toStmt()
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logBindingUpdateStmt(renderNode: o.Expression, propName: string,
|
||||||
|
value: o.Expression): o.Statement {
|
||||||
|
return o.THIS_EXPR.prop('renderer')
|
||||||
|
.callMethod('setBindingDebugInfo',
|
||||||
|
[
|
||||||
|
renderNode,
|
||||||
|
o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
|
||||||
|
value.isBlank().conditional(o.NULL_EXPR, value.callMethod('toString', []))
|
||||||
|
])
|
||||||
|
.toStmt();
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
var nodeDebugInfos_MyComp1 = [
|
||||||
|
new jit_StaticNodeDebugInfo0([],null,{}),
|
||||||
|
new jit_StaticNodeDebugInfo0([],null,{})
|
||||||
|
]
|
||||||
|
;
|
||||||
|
function _View_MyComp1(viewManager,renderer,parentInjector,declarationEl,projectableNodes) {
|
||||||
|
var self = this;
|
||||||
|
jit_AppView1.call(this, './MyComp',_View_MyComp1,jit_ViewType_EMBEDDED2,{'some-tmpl': null},renderer,viewManager,parentInjector,projectableNodes,declarationEl,jit_ChangeDetectionStrategy_Default3,nodeDebugInfos_MyComp1);
|
||||||
|
}
|
||||||
|
_View_MyComp1.prototype = Object.create(jit_AppView1.prototype);
|
||||||
|
_View_MyComp1.prototype.createInternal = function(rootSelector) {
|
||||||
|
var self = this;
|
||||||
|
self._el_0 = self.renderer.createElement(null,'copy-me',self.debug(0,0,49));
|
||||||
|
self._text_1 = self.renderer.createText(self._el_0,'',self.debug(1,0,58));
|
||||||
|
self._expr_0 = jit_uninitialized4;
|
||||||
|
self.init([].concat([self._el_0]),[
|
||||||
|
self._el_0,
|
||||||
|
self._text_1
|
||||||
|
]
|
||||||
|
,{},[],[]);
|
||||||
|
};
|
||||||
|
_View_MyComp1.prototype.detectChangesInternal = function(throwOnChange) {
|
||||||
|
var self = this;
|
||||||
|
var currVal = null;
|
||||||
|
self.debug(1,0,58);
|
||||||
|
currVal = jit_interpolate5(1,'',self.locals['some-tmpl'],'');
|
||||||
|
if (jit_checkBinding6(throwOnChange,self._expr_0,currVal)) {
|
||||||
|
self.renderer.setText(self._text_1,currVal);
|
||||||
|
self._expr_0 = currVal;
|
||||||
|
}
|
||||||
|
self.detectContentChildrenChanges(throwOnChange); }
|
||||||
|
self.detectViewChildrenChanges(throwOnChange); }
|
||||||
|
};
|
||||||
|
function viewFactory_MyComp1(viewManager,parentInjector,declarationEl,projectableNodes,rootSelector) {
|
||||||
|
projectableNodes = jit_ensureSlotCount7(projectableNodes,0);
|
||||||
|
var renderer = declarationEl.parentView.renderer;
|
||||||
|
var view = new _View_MyComp1(viewManager,renderer,parentInjector,declarationEl,projectableNodes);
|
||||||
|
view.create(rootSelector);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
var nodeDebugInfos_MyComp0 = [new jit_StaticNodeDebugInfo0([
|
||||||
|
jit_TemplateRef8,
|
||||||
|
jit_SomeViewport9
|
||||||
|
]
|
||||||
|
,null,{})];
|
||||||
|
var renderType_MyComp = null;
|
||||||
|
function _View_MyComp0(viewManager,renderer,parentInjector,declarationEl,projectableNodes) {
|
||||||
|
var self = this;
|
||||||
|
jit_AppView1.call(this, './MyComp',_View_MyComp0,jit_ViewType_COMPONENT10,{},renderer,viewManager,parentInjector,projectableNodes,declarationEl,jit_ChangeDetectionStrategy_Default3,nodeDebugInfos_MyComp0);
|
||||||
|
}
|
||||||
|
_View_MyComp0.prototype = Object.create(jit_AppView1.prototype);
|
||||||
|
_View_MyComp0.prototype.createInternal = function(rootSelector) {
|
||||||
|
var self = this;
|
||||||
|
var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement);
|
||||||
|
self._anchor_0 = self.renderer.createTemplateAnchor(parentRenderNode,self.debug(0,0,0));
|
||||||
|
self.debug(null,null,null);
|
||||||
|
self._appEl_0 = new jit_AppElement11(0,null,self,self._anchor_0);
|
||||||
|
self._TemplateRef_0_0 = new jit_TemplateRef_12(self._appEl_0,viewFactory_MyComp1);
|
||||||
|
self._SomeViewport_0_1 = new jit_SomeViewport9(self._appEl_0.vcRef,self._TemplateRef_0_0);
|
||||||
|
self.init([],[self._anchor_0],{},[],[]);
|
||||||
|
};
|
||||||
|
_View_MyComp0.prototype.injectorGetInternal = function(token,requestNodeIndex,notFoundResult) {
|
||||||
|
var self = this;
|
||||||
|
if (((token === jit_TemplateRef8) && (0 === requestNodeIndex))) { return self._TemplateRef_0_0; }
|
||||||
|
if (((token === jit_SomeViewport9) && (0 === requestNodeIndex))) { return self._SomeViewport_0_1; }
|
||||||
|
return notFoundResult;
|
||||||
|
};
|
||||||
|
function viewFactory_MyComp0(viewManager,parentInjector,declarationEl,projectableNodes,rootSelector) {
|
||||||
|
if ((renderType_MyComp === null)) { (renderType_MyComp = viewManager.createRenderComponentType(jit_ViewType_EMBEDDED2,jit_undefined13)); }
|
||||||
|
projectableNodes = jit_ensureSlotCount7(projectableNodes,0);
|
||||||
|
var renderer = viewManager.renderComponent(renderType_MyComp);
|
||||||
|
var view = new _View_MyComp0(viewManager,renderer,parentInjector,declarationEl,projectableNodes);
|
||||||
|
view.create(rootSelector);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
return viewFactory_MyComp0
|
||||||
|
//# sourceURL=MyComp.template.js
|
|
@ -0,0 +1,76 @@
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {
|
||||||
|
CompileTokenMetadata,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileIdentifierMetadata
|
||||||
|
} from '../compile_metadata';
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
|
||||||
|
export function getPropertyInView(property: o.Expression, viewPath: CompileView[]): o.Expression {
|
||||||
|
if (viewPath.length === 0) {
|
||||||
|
return property;
|
||||||
|
} else {
|
||||||
|
var viewProp: o.Expression = o.THIS_EXPR;
|
||||||
|
for (var i = 0; i < viewPath.length; i++) {
|
||||||
|
viewProp = viewProp.prop('declarationAppElement').prop('parentView');
|
||||||
|
}
|
||||||
|
if (property instanceof o.ReadPropExpr) {
|
||||||
|
var lastView = viewPath[viewPath.length - 1];
|
||||||
|
let readPropExpr: o.ReadPropExpr = property;
|
||||||
|
// Note: Don't cast for members of the AppView base class...
|
||||||
|
if (lastView.fields.some((field) => field.name == readPropExpr.name) ||
|
||||||
|
lastView.getters.some((field) => field.name == readPropExpr.name)) {
|
||||||
|
viewProp = viewProp.cast(lastView.classType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o.replaceVarInExpression(o.THIS_EXPR.name, viewProp, property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function injectFromViewParentInjector(token: CompileTokenMetadata,
|
||||||
|
optional: boolean): o.Expression {
|
||||||
|
var method = optional ? 'getOptional' : 'get';
|
||||||
|
return o.THIS_EXPR.prop('parentInjector').callMethod(method, [createDiTokenExpression(token)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getViewFactoryName(component: CompileDirectiveMetadata,
|
||||||
|
embeddedTemplateIndex: number): string {
|
||||||
|
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||||
|
if (isPresent(token.value)) {
|
||||||
|
return o.literal(token.value);
|
||||||
|
} else if (token.identifierIsInstance) {
|
||||||
|
return o.importExpr(token.identifier)
|
||||||
|
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
||||||
|
} else {
|
||||||
|
return o.importExpr(token.identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
||||||
|
var lastNonArrayExpressions = [];
|
||||||
|
var result: o.Expression = o.literalArr([]);
|
||||||
|
for (var i = 0; i < expressions.length; i++) {
|
||||||
|
var expr = expressions[i];
|
||||||
|
if (expr.type instanceof o.ArrayType) {
|
||||||
|
if (lastNonArrayExpressions.length > 0) {
|
||||||
|
result =
|
||||||
|
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
|
||||||
|
lastNonArrayExpressions = [];
|
||||||
|
}
|
||||||
|
result = result.callMethod(o.BuiltinMethod.ConcatArray, [expr]);
|
||||||
|
} else {
|
||||||
|
lastNonArrayExpressions.push(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastNonArrayExpressions.length > 0) {
|
||||||
|
result =
|
||||||
|
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
import {
|
||||||
|
ListWrapper,
|
||||||
|
} from 'angular2/src/facade/collection';
|
||||||
|
import {
|
||||||
|
TemplateAst,
|
||||||
|
TemplateAstVisitor,
|
||||||
|
NgContentAst,
|
||||||
|
EmbeddedTemplateAst,
|
||||||
|
ElementAst,
|
||||||
|
VariableAst,
|
||||||
|
BoundEventAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
AttrAst,
|
||||||
|
BoundTextAst,
|
||||||
|
TextAst,
|
||||||
|
DirectiveAst,
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
templateVisitAll,
|
||||||
|
PropertyBindingType,
|
||||||
|
ProviderAst
|
||||||
|
} from '../template_ast';
|
||||||
|
import {
|
||||||
|
bindRenderText,
|
||||||
|
bindRenderInputs,
|
||||||
|
bindDirectiveInputs,
|
||||||
|
bindDirectiveHostProps
|
||||||
|
} from './property_binder';
|
||||||
|
import {bindRenderOutputs, collectEventListeners, bindDirectiveOutputs} from './event_binder';
|
||||||
|
import {
|
||||||
|
bindDirectiveAfterContentLifecycleCallbacks,
|
||||||
|
bindDirectiveAfterViewLifecycleCallbacks,
|
||||||
|
bindDirectiveDestroyLifecycleCallbacks,
|
||||||
|
bindPipeDestroyLifecycleCallbacks,
|
||||||
|
bindDirectiveDetectChangesLifecycleCallbacks
|
||||||
|
} from './lifecycle_binder';
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
|
|
||||||
|
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
|
||||||
|
var visitor = new ViewBinderVisitor(view);
|
||||||
|
templateVisitAll(visitor, parsedTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewBinderVisitor implements TemplateAstVisitor {
|
||||||
|
private _nodeIndex: number = 0;
|
||||||
|
|
||||||
|
constructor(public view: CompileView) {}
|
||||||
|
|
||||||
|
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||||
|
var node = this.view.nodes[this._nodeIndex++];
|
||||||
|
bindRenderText(ast, node, this.view);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitText(ast: TextAst, parent: CompileElement): any {
|
||||||
|
this._nodeIndex++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitNgContent(ast: NgContentAst, parent: CompileElement): any { return null; }
|
||||||
|
|
||||||
|
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||||
|
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||||
|
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
|
||||||
|
bindRenderInputs(ast.inputs, compileElement);
|
||||||
|
bindRenderOutputs(eventListeners);
|
||||||
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
|
||||||
|
var directiveInstance = compileElement.directiveInstances[index];
|
||||||
|
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
|
||||||
|
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
|
||||||
|
|
||||||
|
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement);
|
||||||
|
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
|
||||||
|
});
|
||||||
|
templateVisitAll(this, ast.children, compileElement);
|
||||||
|
// afterContent and afterView lifecycles need to be called bottom up
|
||||||
|
// so that children are notified before parents
|
||||||
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
|
||||||
|
var directiveInstance = compileElement.directiveInstances[index];
|
||||||
|
bindDirectiveAfterContentLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
|
compileElement);
|
||||||
|
bindDirectiveAfterViewLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
|
compileElement);
|
||||||
|
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
|
compileElement);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
||||||
|
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||||
|
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
|
||||||
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
|
||||||
|
var directiveInstance = compileElement.directiveInstances[index];
|
||||||
|
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
|
||||||
|
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
|
||||||
|
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
|
||||||
|
bindDirectiveAfterContentLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
|
compileElement);
|
||||||
|
bindDirectiveAfterViewLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
|
compileElement);
|
||||||
|
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||||
|
compileElement);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
||||||
|
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
||||||
|
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||||
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||||
|
}
|
|
@ -0,0 +1,602 @@
|
||||||
|
import {isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {Identifiers, identifierToken} from '../identifiers';
|
||||||
|
import {
|
||||||
|
ViewConstructorVars,
|
||||||
|
InjectMethodVars,
|
||||||
|
DetectChangesVars,
|
||||||
|
ViewTypeEnum,
|
||||||
|
ViewEncapsulationEnum,
|
||||||
|
ChangeDetectionStrategyEnum,
|
||||||
|
ViewProperties
|
||||||
|
} from './constants';
|
||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
isDefaultChangeDetectionStrategy
|
||||||
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
import {CompileElement, CompileNode} from './compile_element';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TemplateAst,
|
||||||
|
TemplateAstVisitor,
|
||||||
|
NgContentAst,
|
||||||
|
EmbeddedTemplateAst,
|
||||||
|
ElementAst,
|
||||||
|
VariableAst,
|
||||||
|
BoundEventAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
AttrAst,
|
||||||
|
BoundTextAst,
|
||||||
|
TextAst,
|
||||||
|
DirectiveAst,
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
templateVisitAll,
|
||||||
|
PropertyBindingType,
|
||||||
|
ProviderAst
|
||||||
|
} from '../template_ast';
|
||||||
|
|
||||||
|
import {getViewFactoryName, createFlatArray, createDiTokenExpression} from './util';
|
||||||
|
|
||||||
|
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||||
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||||
|
import {HOST_VIEW_ELEMENT_NAME} from 'angular2/src/core/linker/view';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CompileIdentifierMetadata,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompileTokenMetadata
|
||||||
|
} from '../compile_metadata';
|
||||||
|
|
||||||
|
import {bindView} from './view_binder';
|
||||||
|
|
||||||
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
|
const CLASS_ATTR = 'class';
|
||||||
|
const STYLE_ATTR = 'style';
|
||||||
|
|
||||||
|
var parentRenderNodeVar = o.variable('parentRenderNode');
|
||||||
|
var rootSelectorVar = o.variable('rootSelector');
|
||||||
|
|
||||||
|
export class ViewCompileDependency {
|
||||||
|
constructor(public comp: CompileDirectiveMetadata,
|
||||||
|
public factoryPlaceholder: CompileIdentifierMetadata) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildView(view: CompileView, template: TemplateAst[],
|
||||||
|
targetDependencies: ViewCompileDependency[],
|
||||||
|
targetStatements: o.Statement[]): number {
|
||||||
|
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies, targetStatements);
|
||||||
|
templateVisitAll(builderVisitor, template, view.declarationElement.isNull() ?
|
||||||
|
view.declarationElement :
|
||||||
|
view.declarationElement.parent);
|
||||||
|
// Need to separate binding from creation to be able to refer to
|
||||||
|
// variables that have been declared after usage.
|
||||||
|
bindView(view, template);
|
||||||
|
view.afterNodes();
|
||||||
|
|
||||||
|
createViewTopLevelStmts(view, targetStatements);
|
||||||
|
|
||||||
|
return builderVisitor.nestedViewCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||||
|
nestedViewCount: number = 0;
|
||||||
|
|
||||||
|
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[],
|
||||||
|
public targetStatements: o.Statement[]) {}
|
||||||
|
|
||||||
|
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||||
|
|
||||||
|
private _addRootNodeAndProject(node: CompileNode, ngContentIndex: number,
|
||||||
|
parent: CompileElement) {
|
||||||
|
var appEl = node instanceof CompileElement ? node.getOptionalAppElement() : null;
|
||||||
|
if (this._isRootNode(parent)) {
|
||||||
|
// store root nodes only for embedded/host views
|
||||||
|
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||||
|
this.view.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : node.renderNode);
|
||||||
|
}
|
||||||
|
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
||||||
|
parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : node.renderNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getParentRenderNode(parent: CompileElement): o.Expression {
|
||||||
|
if (this._isRootNode(parent)) {
|
||||||
|
if (this.view.viewType === ViewType.COMPONENT) {
|
||||||
|
return parentRenderNodeVar;
|
||||||
|
} else {
|
||||||
|
// root node of an embedded/host view
|
||||||
|
return o.NULL_EXPR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return isPresent(parent.component) &&
|
||||||
|
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
|
||||||
|
o.NULL_EXPR :
|
||||||
|
parent.renderNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||||
|
return this._visitText(ast, '', ast.ngContentIndex, parent);
|
||||||
|
}
|
||||||
|
visitText(ast: TextAst, parent: CompileElement): any {
|
||||||
|
return this._visitText(ast, ast.value, ast.ngContentIndex, parent);
|
||||||
|
}
|
||||||
|
private _visitText(ast: TemplateAst, value: string, ngContentIndex: number,
|
||||||
|
parent: CompileElement): o.Expression {
|
||||||
|
var fieldName = `_text_${this.view.nodes.length}`;
|
||||||
|
this.view.fields.push(new o.ClassField(fieldName,
|
||||||
|
o.importType(this.view.genConfig.renderTypes.renderText),
|
||||||
|
[o.StmtModifier.Private]));
|
||||||
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||||
|
var compileNode = new CompileNode(parent, this.view, this.view.nodes.length, renderNode, ast);
|
||||||
|
var createRenderNode =
|
||||||
|
o.THIS_EXPR.prop(fieldName)
|
||||||
|
.set(ViewProperties.renderer.callMethod(
|
||||||
|
'createText',
|
||||||
|
[
|
||||||
|
this._getParentRenderNode(parent),
|
||||||
|
o.literal(value),
|
||||||
|
this.view.createMethod.resetDebugInfoExpr(this.view.nodes.length, ast)
|
||||||
|
]))
|
||||||
|
.toStmt();
|
||||||
|
this.view.nodes.push(compileNode);
|
||||||
|
this.view.createMethod.addStmt(createRenderNode);
|
||||||
|
this._addRootNodeAndProject(compileNode, ngContentIndex, parent);
|
||||||
|
return renderNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitNgContent(ast: NgContentAst, parent: CompileElement): any {
|
||||||
|
// the projected nodes originate from a different view, so we don't
|
||||||
|
// have debug information for them...
|
||||||
|
this.view.createMethod.resetDebugInfo(null, ast);
|
||||||
|
var parentRenderNode = this._getParentRenderNode(parent);
|
||||||
|
var nodesExpression = ViewProperties.projectableNodes.key(
|
||||||
|
o.literal(ast.index),
|
||||||
|
new o.ArrayType(o.importType(this.view.genConfig.renderTypes.renderNode)));
|
||||||
|
if (parentRenderNode !== o.NULL_EXPR) {
|
||||||
|
this.view.createMethod.addStmt(
|
||||||
|
ViewProperties.renderer.callMethod(
|
||||||
|
'projectNodes',
|
||||||
|
[
|
||||||
|
parentRenderNode,
|
||||||
|
o.importExpr(Identifiers.flattenNestedViewRenderNodes)
|
||||||
|
.callFn([nodesExpression])
|
||||||
|
])
|
||||||
|
.toStmt());
|
||||||
|
} else if (this._isRootNode(parent)) {
|
||||||
|
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||||
|
// store root nodes only for embedded/host views
|
||||||
|
this.view.rootNodesOrAppElements.push(nodesExpression);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isPresent(parent.component) && isPresent(ast.ngContentIndex)) {
|
||||||
|
parent.addContentNode(ast.ngContentIndex, nodesExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||||
|
var nodeIndex = this.view.nodes.length;
|
||||||
|
var createRenderNodeExpr;
|
||||||
|
var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
|
||||||
|
var createElementExpr = ViewProperties.renderer.callMethod(
|
||||||
|
'createElement',
|
||||||
|
[this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]);
|
||||||
|
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||||||
|
createRenderNodeExpr =
|
||||||
|
rootSelectorVar.identical(o.NULL_EXPR)
|
||||||
|
.conditional(createElementExpr,
|
||||||
|
ViewProperties.renderer.callMethod('selectRootElement',
|
||||||
|
[rootSelectorVar, debugContextExpr]));
|
||||||
|
} else {
|
||||||
|
createRenderNodeExpr = createElementExpr;
|
||||||
|
}
|
||||||
|
var fieldName = `_el_${nodeIndex}`;
|
||||||
|
this.view.fields.push(
|
||||||
|
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderElement),
|
||||||
|
[o.StmtModifier.Private]));
|
||||||
|
var createRenderNode = o.THIS_EXPR.prop(fieldName).set(createRenderNodeExpr).toStmt();
|
||||||
|
|
||||||
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||||
|
|
||||||
|
var component = ast.getComponent();
|
||||||
|
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||||
|
var variables =
|
||||||
|
_readHtmlAndDirectiveVariables(ast.exportAsVars, ast.directives, this.view.viewType);
|
||||||
|
this.view.createMethod.addStmt(createRenderNode);
|
||||||
|
var htmlAttrs = _readHtmlAttrs(ast.attrs);
|
||||||
|
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
|
||||||
|
for (var i = 0; i < attrNameAndValues.length; i++) {
|
||||||
|
var attrName = attrNameAndValues[i][0];
|
||||||
|
var attrValue = attrNameAndValues[i][1];
|
||||||
|
this.view.createMethod.addStmt(
|
||||||
|
ViewProperties.renderer.callMethod(
|
||||||
|
'setElementAttribute',
|
||||||
|
[renderNode, o.literal(attrName), o.literal(attrValue)])
|
||||||
|
.toStmt());
|
||||||
|
}
|
||||||
|
var compileElement = new CompileElement(parent, this.view, nodeIndex, renderNode, ast,
|
||||||
|
directives, ast.providers, variables);
|
||||||
|
this.view.nodes.push(compileElement);
|
||||||
|
var compViewExpr: o.ReadVarExpr = null;
|
||||||
|
if (isPresent(component)) {
|
||||||
|
var nestedComponentIdentifier =
|
||||||
|
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
|
||||||
|
this.targetDependencies.push(new ViewCompileDependency(component, nestedComponentIdentifier));
|
||||||
|
compViewExpr = o.variable(`compView_${nodeIndex}`);
|
||||||
|
this.view.createMethod.addStmt(compViewExpr.set(o.importExpr(nestedComponentIdentifier)
|
||||||
|
.callFn([
|
||||||
|
ViewProperties.viewManager,
|
||||||
|
compileElement.getOrCreateInjector(),
|
||||||
|
compileElement.getOrCreateAppElement()
|
||||||
|
]))
|
||||||
|
.toDeclStmt());
|
||||||
|
compileElement.setComponent(component, compViewExpr);
|
||||||
|
}
|
||||||
|
compileElement.beforeChildren();
|
||||||
|
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
|
||||||
|
templateVisitAll(this, ast.children, compileElement);
|
||||||
|
compileElement.afterChildren(this.view.nodes.length - nodeIndex - 1);
|
||||||
|
|
||||||
|
if (isPresent(compViewExpr)) {
|
||||||
|
var codeGenContentNodes;
|
||||||
|
if (this.view.component.type.isHost) {
|
||||||
|
codeGenContentNodes = ViewProperties.projectableNodes;
|
||||||
|
} else {
|
||||||
|
codeGenContentNodes = o.literalArr(
|
||||||
|
compileElement.contentNodesByNgContentIndex.map(nodes => createFlatArray(nodes)));
|
||||||
|
}
|
||||||
|
this.view.createMethod.addStmt(
|
||||||
|
compViewExpr.callMethod('create', [codeGenContentNodes, o.NULL_EXPR]).toStmt());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
||||||
|
var nodeIndex = this.view.nodes.length;
|
||||||
|
var fieldName = `_anchor_${nodeIndex}`;
|
||||||
|
this.view.fields.push(
|
||||||
|
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderComment),
|
||||||
|
[o.StmtModifier.Private]));
|
||||||
|
var createRenderNode = o.THIS_EXPR.prop(fieldName)
|
||||||
|
.set(ViewProperties.renderer.callMethod(
|
||||||
|
'createTemplateAnchor',
|
||||||
|
[
|
||||||
|
this._getParentRenderNode(parent),
|
||||||
|
this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast)
|
||||||
|
]))
|
||||||
|
.toStmt();
|
||||||
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||||
|
|
||||||
|
var templateVariableBindings = ast.vars.map(
|
||||||
|
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
||||||
|
|
||||||
|
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||||
|
var compileElement = new CompileElement(parent, this.view, nodeIndex, renderNode, ast,
|
||||||
|
directives, ast.providers, {});
|
||||||
|
this.view.nodes.push(compileElement);
|
||||||
|
this.view.createMethod.addStmt(createRenderNode);
|
||||||
|
|
||||||
|
this.nestedViewCount++;
|
||||||
|
var embeddedView = new CompileView(
|
||||||
|
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
||||||
|
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
|
||||||
|
this.nestedViewCount +=
|
||||||
|
buildView(embeddedView, ast.children, this.targetDependencies, this.targetStatements);
|
||||||
|
|
||||||
|
compileElement.beforeChildren();
|
||||||
|
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
|
||||||
|
compileElement.afterChildren(0);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
||||||
|
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
||||||
|
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||||
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function _mergeHtmlAndDirectiveAttrs(declaredHtmlAttrs: {[key: string]: string},
|
||||||
|
directives: CompileDirectiveMetadata[]): string[][] {
|
||||||
|
var result: {[key: string]: string} = {};
|
||||||
|
StringMapWrapper.forEach(declaredHtmlAttrs, (value, key) => { result[key] = value; });
|
||||||
|
directives.forEach(directiveMeta => {
|
||||||
|
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||||
|
var prevValue = result[name];
|
||||||
|
result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return mapToKeyValueArray(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
|
||||||
|
var htmlAttrs: {[key: string]: string} = {};
|
||||||
|
attrs.forEach((ast) => { htmlAttrs[ast.name] = ast.value; });
|
||||||
|
return htmlAttrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _readHtmlAndDirectiveVariables(elementExportAsVars: VariableAst[],
|
||||||
|
directives: DirectiveAst[],
|
||||||
|
viewType: ViewType): {[key: string]: CompileTokenMetadata} {
|
||||||
|
var variables: {[key: string]: CompileTokenMetadata} = {};
|
||||||
|
var component: CompileDirectiveMetadata = null;
|
||||||
|
directives.forEach((directive) => {
|
||||||
|
if (directive.directive.isComponent) {
|
||||||
|
component = directive.directive;
|
||||||
|
}
|
||||||
|
directive.exportAsVars.forEach(
|
||||||
|
varAst => { variables[varAst.name] = identifierToken(directive.directive.type); });
|
||||||
|
});
|
||||||
|
elementExportAsVars.forEach((varAst) => {
|
||||||
|
variables[varAst.name] = isPresent(component) ? identifierToken(component.type) : null;
|
||||||
|
});
|
||||||
|
if (viewType === ViewType.HOST) {
|
||||||
|
variables[HOST_VIEW_ELEMENT_NAME] = null;
|
||||||
|
}
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||||
|
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||||
|
return `${attrValue1} ${attrValue2}`;
|
||||||
|
} else {
|
||||||
|
return attrValue2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
||||||
|
var entryArray = [];
|
||||||
|
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
|
||||||
|
// We need to sort to get a defined output order
|
||||||
|
// for tests and for caching generated artifacts...
|
||||||
|
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
||||||
|
var keyValueArray = [];
|
||||||
|
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
|
||||||
|
return keyValueArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statement[]) {
|
||||||
|
var nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
||||||
|
if (view.genConfig.genDebugInfo) {
|
||||||
|
nodeDebugInfosVar = o.variable(`nodeDebugInfos_${view.component.type.name}${view.viewIndex}`);
|
||||||
|
targetStatements.push(
|
||||||
|
(<o.ReadVarExpr>nodeDebugInfosVar)
|
||||||
|
.set(o.literalArr(view.nodes.map(createStaticNodeDebugInfo),
|
||||||
|
new o.ArrayType(new o.ExternalType(Identifiers.StaticNodeDebugInfo),
|
||||||
|
[o.TypeModifier.Const])))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var renderCompTypeVar: o.ReadVarExpr = o.variable(`renderType_${view.component.type.name}`);
|
||||||
|
if (view.viewIndex === 0) {
|
||||||
|
targetStatements.push(renderCompTypeVar.set(o.NULL_EXPR)
|
||||||
|
.toDeclStmt(o.importType(Identifiers.RenderComponentType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar);
|
||||||
|
targetStatements.push(viewClass);
|
||||||
|
targetStatements.push(createViewFactory(view, viewClass, renderCompTypeVar));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
|
||||||
|
var compileElement = node instanceof CompileElement ? node : null;
|
||||||
|
var providerTokens: o.Expression[] = [];
|
||||||
|
var componentToken: o.Expression = o.NULL_EXPR;
|
||||||
|
var varTokenEntries = [];
|
||||||
|
if (isPresent(compileElement)) {
|
||||||
|
providerTokens = compileElement.getProviderTokens();
|
||||||
|
if (isPresent(compileElement.component)) {
|
||||||
|
componentToken = createDiTokenExpression(identifierToken(compileElement.component.type));
|
||||||
|
}
|
||||||
|
StringMapWrapper.forEach(compileElement.variableTokens, (token, varName) => {
|
||||||
|
varTokenEntries.push(
|
||||||
|
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return o.importExpr(Identifiers.StaticNodeDebugInfo)
|
||||||
|
.instantiate(
|
||||||
|
[
|
||||||
|
o.literalArr(providerTokens, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])),
|
||||||
|
componentToken,
|
||||||
|
o.literalMap(varTokenEntries, new o.MapType(o.DYNAMIC_TYPE, [o.TypeModifier.Const]))
|
||||||
|
],
|
||||||
|
o.importType(Identifiers.StaticNodeDebugInfo, null, [o.TypeModifier.Const]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
|
||||||
|
nodeDebugInfosVar: o.Expression): o.ClassStmt {
|
||||||
|
var emptyTemplateVariableBindings =
|
||||||
|
view.templateVariableBindings.map((entry) => [entry[0], o.NULL_EXPR]);
|
||||||
|
var viewConstructorArgs = [
|
||||||
|
new o.FnParam(ViewConstructorVars.viewManager.name, o.importType(Identifiers.AppViewManager_)),
|
||||||
|
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
||||||
|
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
||||||
|
];
|
||||||
|
var viewConstructor = new o.ClassMethod(null, viewConstructorArgs, [
|
||||||
|
o.SUPER_EXPR.callFn([
|
||||||
|
o.variable(view.className),
|
||||||
|
renderCompTypeVar,
|
||||||
|
ViewTypeEnum.fromValue(view.viewType),
|
||||||
|
o.literalMap(emptyTemplateVariableBindings),
|
||||||
|
ViewConstructorVars.viewManager,
|
||||||
|
ViewConstructorVars.parentInjector,
|
||||||
|
ViewConstructorVars.declarationEl,
|
||||||
|
ChangeDetectionStrategyEnum.fromValue(getChangeDetectionMode(view)),
|
||||||
|
o.literal(view.literalArrayCount),
|
||||||
|
o.literal(view.literalMapCount),
|
||||||
|
nodeDebugInfosVar
|
||||||
|
])
|
||||||
|
.toStmt()
|
||||||
|
]);
|
||||||
|
|
||||||
|
var viewMethods = [
|
||||||
|
new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||||||
|
generateCreateMethod(view)),
|
||||||
|
new o.ClassMethod(
|
||||||
|
'injectorGetInternal',
|
||||||
|
[
|
||||||
|
new o.FnParam(InjectMethodVars.token.name, o.DYNAMIC_TYPE),
|
||||||
|
// Note: Can't use o.INT_TYPE here as the method in AppView uses number
|
||||||
|
new o.FnParam(InjectMethodVars.requestNodeIndex.name, o.NUMBER_TYPE),
|
||||||
|
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
|
||||||
|
],
|
||||||
|
addReturnValuefNotEmpty(view.injectorGetMethod.finish(), InjectMethodVars.notFoundResult),
|
||||||
|
o.DYNAMIC_TYPE),
|
||||||
|
new o.ClassMethod('detectChangesInternal',
|
||||||
|
[new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
|
||||||
|
generateDetectChangesMethod(view)),
|
||||||
|
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
||||||
|
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
|
||||||
|
].concat(view.eventHandlerMethods);
|
||||||
|
var viewClass = new o.ClassStmt(
|
||||||
|
view.className, o.importExpr(Identifiers.AppView, [getContextType(view)]), view.fields,
|
||||||
|
view.getters, viewConstructor, viewMethods.filter((method) => method.body.length > 0));
|
||||||
|
return viewClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createViewFactory(view: CompileView, viewClass: o.ClassStmt,
|
||||||
|
renderCompTypeVar: o.ReadVarExpr): o.Statement {
|
||||||
|
var viewFactoryArgs = [
|
||||||
|
new o.FnParam(ViewConstructorVars.viewManager.name, o.importType(Identifiers.AppViewManager_)),
|
||||||
|
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
||||||
|
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
||||||
|
];
|
||||||
|
var initRenderCompTypeStmts = [];
|
||||||
|
var templateUrlInfo;
|
||||||
|
if (view.component.template.templateUrl == view.component.type.moduleUrl) {
|
||||||
|
templateUrlInfo =
|
||||||
|
`${view.component.type.moduleUrl} class ${view.component.type.name} - inline template`;
|
||||||
|
} else {
|
||||||
|
templateUrlInfo = view.component.template.templateUrl;
|
||||||
|
}
|
||||||
|
if (view.viewIndex === 0) {
|
||||||
|
initRenderCompTypeStmts = [
|
||||||
|
new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR),
|
||||||
|
[
|
||||||
|
renderCompTypeVar.set(ViewConstructorVars.viewManager
|
||||||
|
.callMethod('createRenderComponentType',
|
||||||
|
[
|
||||||
|
o.literal(templateUrlInfo),
|
||||||
|
o.literal(
|
||||||
|
view.component.template.ngContentSelectors.length),
|
||||||
|
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation),
|
||||||
|
view.styles
|
||||||
|
]))
|
||||||
|
.toStmt()
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return o.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([
|
||||||
|
new o.ReturnStatement(o.variable(viewClass.name)
|
||||||
|
.instantiate(viewClass.constructorMethod.params.map(
|
||||||
|
(param) => o.variable(param.name))))
|
||||||
|
]),
|
||||||
|
o.importType(Identifiers.AppView, [getContextType(view)]))
|
||||||
|
.toDeclStmt(view.viewFactory.name, [o.StmtModifier.Final]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCreateMethod(view: CompileView): o.Statement[] {
|
||||||
|
var parentRenderNodeExpr: o.Expression = o.NULL_EXPR;
|
||||||
|
var parentRenderNodeStmts = [];
|
||||||
|
if (view.viewType === ViewType.COMPONENT) {
|
||||||
|
parentRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||||
|
'createViewRoot', [o.THIS_EXPR.prop('declarationAppElement').prop('nativeElement')]);
|
||||||
|
parentRenderNodeStmts = [
|
||||||
|
parentRenderNodeVar.set(parentRenderNodeExpr)
|
||||||
|
.toDeclStmt(o.importType(view.genConfig.renderTypes.renderNode), [o.StmtModifier.Final])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return parentRenderNodeStmts.concat(view.createMethod.finish())
|
||||||
|
.concat([
|
||||||
|
o.THIS_EXPR.callMethod('init',
|
||||||
|
[
|
||||||
|
createFlatArray(view.rootNodesOrAppElements),
|
||||||
|
o.literalArr(view.nodes.map(node => node.renderNode)),
|
||||||
|
o.literalMap(view.namedAppElements),
|
||||||
|
o.literalArr(view.disposables),
|
||||||
|
o.literalArr(view.subscriptions)
|
||||||
|
])
|
||||||
|
.toStmt()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||||||
|
var stmts = [];
|
||||||
|
if (view.detectChangesInInputsMethod.isEmpty() && view.updateContentQueriesMethod.isEmpty() &&
|
||||||
|
view.afterContentLifecycleCallbacksMethod.isEmpty() &&
|
||||||
|
view.detectChangesHostPropertiesMethod.isEmpty() && view.updateViewQueriesMethod.isEmpty() &&
|
||||||
|
view.afterViewLifecycleCallbacksMethod.isEmpty()) {
|
||||||
|
return stmts;
|
||||||
|
}
|
||||||
|
ListWrapper.addAll(stmts, view.detectChangesInInputsMethod.finish());
|
||||||
|
stmts.push(
|
||||||
|
o.THIS_EXPR.callMethod('detectContentChildrenChanges', [DetectChangesVars.throwOnChange])
|
||||||
|
.toStmt());
|
||||||
|
var afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
||||||
|
view.afterContentLifecycleCallbacksMethod.finish());
|
||||||
|
if (afterContentStmts.length > 0) {
|
||||||
|
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterContentStmts));
|
||||||
|
}
|
||||||
|
ListWrapper.addAll(stmts, view.detectChangesHostPropertiesMethod.finish());
|
||||||
|
stmts.push(o.THIS_EXPR.callMethod('detectViewChildrenChanges', [DetectChangesVars.throwOnChange])
|
||||||
|
.toStmt());
|
||||||
|
var afterViewStmts =
|
||||||
|
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
||||||
|
if (afterViewStmts.length > 0) {
|
||||||
|
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterViewStmts));
|
||||||
|
}
|
||||||
|
|
||||||
|
var varStmts = [];
|
||||||
|
var readVars = o.findReadVarNames(stmts);
|
||||||
|
if (SetWrapper.has(readVars, DetectChangesVars.changed.name)) {
|
||||||
|
varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
|
||||||
|
}
|
||||||
|
if (SetWrapper.has(readVars, DetectChangesVars.changes.name)) {
|
||||||
|
varStmts.push(DetectChangesVars.changes.set(o.NULL_EXPR)
|
||||||
|
.toDeclStmt(new o.MapType(o.importType(Identifiers.SimpleChange))));
|
||||||
|
}
|
||||||
|
if (SetWrapper.has(readVars, DetectChangesVars.valUnwrapper.name)) {
|
||||||
|
varStmts.push(
|
||||||
|
DetectChangesVars.valUnwrapper.set(o.importExpr(Identifiers.ValueUnwrapper).instantiate([]))
|
||||||
|
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||||
|
}
|
||||||
|
return varStmts.concat(stmts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addReturnValuefNotEmpty(statements: o.Statement[], value: o.Expression): o.Statement[] {
|
||||||
|
if (statements.length > 0) {
|
||||||
|
return statements.concat([new o.ReturnStatement(value)]);
|
||||||
|
} else {
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContextType(view: CompileView): o.Type {
|
||||||
|
var typeMeta = view.component.type;
|
||||||
|
return typeMeta.isHost ? o.DYNAMIC_TYPE : o.importType(typeMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChangeDetectionMode(view: CompileView): ChangeDetectionStrategy {
|
||||||
|
var mode: ChangeDetectionStrategy;
|
||||||
|
if (view.viewType === ViewType.COMPONENT) {
|
||||||
|
mode = isDefaultChangeDetectionStrategy(view.component.changeDetection) ?
|
||||||
|
ChangeDetectionStrategy.CheckAlways :
|
||||||
|
ChangeDetectionStrategy.CheckOnce;
|
||||||
|
} else {
|
||||||
|
mode = ChangeDetectionStrategy.CheckAlways;
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
import * as o from '../output/output_ast';
|
||||||
|
import {CompileElement} from './compile_element';
|
||||||
|
import {CompileView} from './compile_view';
|
||||||
|
import {buildView, ViewCompileDependency} from './view_builder';
|
||||||
|
|
||||||
|
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||||
|
|
||||||
|
import {TemplateAst} from '../template_ast';
|
||||||
|
import {CompilerConfig} from '../config';
|
||||||
|
|
||||||
|
export class ViewCompileResult {
|
||||||
|
constructor(public statements: o.Statement[], public viewFactoryVar: string,
|
||||||
|
public dependencies: ViewCompileDependency[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewCompiler {
|
||||||
|
constructor(private _genConfig: CompilerConfig) {}
|
||||||
|
|
||||||
|
compileComponent(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
styles: o.Expression, pipes: CompilePipeMetadata[]): ViewCompileResult {
|
||||||
|
var statements = [];
|
||||||
|
var dependencies = [];
|
||||||
|
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
|
||||||
|
CompileElement.createNull(), []);
|
||||||
|
buildView(view, template, dependencies, statements);
|
||||||
|
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
import {ViewMetadata} from '../metadata/view';
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||||
import {ComponentMetadata} from '../metadata/directives';
|
import {ComponentMetadata} from 'angular2/src/core/metadata/directives';
|
||||||
|
|
||||||
import {Type, stringify, isBlank, isPresent} from 'angular2/src/facade/lang';
|
import {Type, stringify, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
@ -11,17 +11,15 @@ import {
|
||||||
KeyValueDiffers,
|
KeyValueDiffers,
|
||||||
defaultKeyValueDiffers
|
defaultKeyValueDiffers
|
||||||
} from './change_detection/change_detection';
|
} from './change_detection/change_detection';
|
||||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
|
||||||
import {AppViewManager} from './linker/view_manager';
|
import {AppViewManager} from './linker/view_manager';
|
||||||
import {AppViewManager_} from "./linker/view_manager";
|
import {AppViewManager_} from "./linker/view_manager";
|
||||||
import {ViewResolver} from './linker/view_resolver';
|
|
||||||
import {DirectiveResolver} from './linker/directive_resolver';
|
|
||||||
import {PipeResolver} from './linker/pipe_resolver';
|
|
||||||
import {Compiler} from './linker/compiler';
|
import {Compiler} from './linker/compiler';
|
||||||
import {Compiler_} from "./linker/compiler";
|
import {Compiler_} from "./linker/compiler";
|
||||||
import {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
import {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
||||||
import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
||||||
|
|
||||||
|
var __unused: Type; // avoid unused import when Type union types are erased
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A default set of providers which should be included in any Angular
|
* A default set of providers which should be included in any Angular
|
||||||
* application, regardless of the platform it runs onto.
|
* application, regardless of the platform it runs onto.
|
||||||
|
@ -29,12 +27,8 @@ import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
||||||
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||||
new Provider(Compiler, {useClass: Compiler_}),
|
new Provider(Compiler, {useClass: Compiler_}),
|
||||||
APP_ID_RANDOM_PROVIDER,
|
APP_ID_RANDOM_PROVIDER,
|
||||||
ResolvedMetadataCache,
|
|
||||||
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
||||||
ViewResolver,
|
|
||||||
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||||
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||||
DirectiveResolver,
|
|
||||||
PipeResolver,
|
|
||||||
new Provider(DynamicComponentLoader, {useClass: DynamicComponentLoader_})
|
new Provider(DynamicComponentLoader, {useClass: DynamicComponentLoader_})
|
||||||
]);
|
]);
|
|
@ -457,8 +457,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_loadComponent(componentRef: ComponentRef): void {
|
_loadComponent(componentRef: ComponentRef): void {
|
||||||
var appChangeDetector =
|
var appChangeDetector = (<ElementRef_>componentRef.location).internalElement.parentView;
|
||||||
(<ElementRef_>componentRef.location).internalElement.parentView.changeDetector;
|
|
||||||
this._changeDetectorRefs.push(appChangeDetector.ref);
|
this._changeDetectorRefs.push(appChangeDetector.ref);
|
||||||
this.tick();
|
this.tick();
|
||||||
this._rootComponents.push(componentRef);
|
this._rootComponents.push(componentRef);
|
||||||
|
@ -471,7 +470,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.unregisterChangeDetector(
|
this.unregisterChangeDetector(
|
||||||
(<ElementRef_>componentRef.location).internalElement.parentView.changeDetector.ref);
|
(<ElementRef_>componentRef.location).internalElement.parentView.ref);
|
||||||
ListWrapper.remove(this._rootComponents, componentRef);
|
ListWrapper.remove(this._rootComponents, componentRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,6 @@
|
||||||
export {
|
export {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
|
|
||||||
ExpressionChangedAfterItHasBeenCheckedException,
|
|
||||||
ChangeDetectionError,
|
|
||||||
|
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
|
|
||||||
WrappedValue,
|
WrappedValue,
|
||||||
|
|
|
@ -1,296 +0,0 @@
|
||||||
import {assertionsEnabled, isPresent, isBlank, StringWrapper} from 'angular2/src/facade/lang';
|
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
|
||||||
import {ChangeDetectorRef, ChangeDetectorRef_} from './change_detector_ref';
|
|
||||||
import {DirectiveIndex} from './directive_record';
|
|
||||||
import {ChangeDetector, ChangeDispatcher} from './interfaces';
|
|
||||||
import {Pipes} from './pipes';
|
|
||||||
import {
|
|
||||||
ChangeDetectionError,
|
|
||||||
ExpressionChangedAfterItHasBeenCheckedException,
|
|
||||||
DehydratedException,
|
|
||||||
EventEvaluationErrorContext,
|
|
||||||
EventEvaluationError
|
|
||||||
} from './exceptions';
|
|
||||||
import {BindingTarget} from './binding_record';
|
|
||||||
import {Locals} from './parser/locals';
|
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
|
||||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
|
||||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
|
||||||
|
|
||||||
var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
|
|
||||||
|
|
||||||
class _Context {
|
|
||||||
constructor(public element: any, public componentElement: any, public context: any,
|
|
||||||
public locals: any, public injector: any, public expression: any) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AbstractChangeDetector<T> implements ChangeDetector {
|
|
||||||
contentChildren: any[] = [];
|
|
||||||
viewChildren: any[] = [];
|
|
||||||
parent: ChangeDetector;
|
|
||||||
ref: ChangeDetectorRef;
|
|
||||||
|
|
||||||
// The names of the below fields must be kept in sync with codegen_name_util.ts or
|
|
||||||
// change detection will fail.
|
|
||||||
state: ChangeDetectorState = ChangeDetectorState.NeverChecked;
|
|
||||||
context: T;
|
|
||||||
locals: Locals = null;
|
|
||||||
mode: ChangeDetectionStrategy = null;
|
|
||||||
pipes: Pipes = null;
|
|
||||||
propertyBindingIndex: number;
|
|
||||||
outputSubscriptions: any[];
|
|
||||||
|
|
||||||
dispatcher: ChangeDispatcher;
|
|
||||||
|
|
||||||
|
|
||||||
constructor(public id: string, public numberOfPropertyProtoRecords: number,
|
|
||||||
public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
|
|
||||||
public strategy: ChangeDetectionStrategy) {
|
|
||||||
this.ref = new ChangeDetectorRef_(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
addContentChild(cd: ChangeDetector): void {
|
|
||||||
this.contentChildren.push(cd);
|
|
||||||
cd.parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeContentChild(cd: ChangeDetector): void { ListWrapper.remove(this.contentChildren, cd); }
|
|
||||||
|
|
||||||
addViewChild(cd: ChangeDetector): void {
|
|
||||||
this.viewChildren.push(cd);
|
|
||||||
cd.parent = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeViewChild(cd: ChangeDetector): void { ListWrapper.remove(this.viewChildren, cd); }
|
|
||||||
|
|
||||||
remove(): void { this.parent.removeContentChild(this); }
|
|
||||||
|
|
||||||
handleEvent(eventName: string, elIndex: number, event: any): boolean {
|
|
||||||
if (!this.hydrated()) {
|
|
||||||
this.throwDehydratedError(`${this.id} -> ${eventName}`);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var locals = new Map<string, any>();
|
|
||||||
locals.set('$event', event);
|
|
||||||
var res = !this.handleEventInternal(eventName, elIndex, new Locals(this.locals, locals));
|
|
||||||
this.markPathToRootAsCheckOnce();
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
var c = this.dispatcher.getDebugContext(null, elIndex, null);
|
|
||||||
var context = isPresent(c) ?
|
|
||||||
new EventEvaluationErrorContext(c.element, c.componentElement, c.context,
|
|
||||||
c.locals, c.injector) :
|
|
||||||
null;
|
|
||||||
throw new EventEvaluationError(eventName, e, e.stack, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
|
|
||||||
|
|
||||||
detectChanges(): void { this.runDetectChanges(false); }
|
|
||||||
|
|
||||||
checkNoChanges(): void {
|
|
||||||
if (assertionsEnabled()) {
|
|
||||||
this.runDetectChanges(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runDetectChanges(throwOnChange: boolean): void {
|
|
||||||
if (this.mode === ChangeDetectionStrategy.Detached ||
|
|
||||||
this.mode === ChangeDetectionStrategy.Checked || this.state === ChangeDetectorState.Errored)
|
|
||||||
return;
|
|
||||||
var s = _scope_check(this.id, throwOnChange);
|
|
||||||
|
|
||||||
this.detectChangesInRecords(throwOnChange);
|
|
||||||
|
|
||||||
this._detectChangesContentChildren(throwOnChange);
|
|
||||||
if (!throwOnChange) this.afterContentLifecycleCallbacks();
|
|
||||||
|
|
||||||
this._detectChangesInViewChildren(throwOnChange);
|
|
||||||
if (!throwOnChange) this.afterViewLifecycleCallbacks();
|
|
||||||
|
|
||||||
if (this.mode === ChangeDetectionStrategy.CheckOnce)
|
|
||||||
this.mode = ChangeDetectionStrategy.Checked;
|
|
||||||
|
|
||||||
this.state = ChangeDetectorState.CheckedBefore;
|
|
||||||
wtfLeave(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
|
||||||
// implementation of `detectChangesInRecordsInternal` which does the work of detecting changes
|
|
||||||
// and which this method will call.
|
|
||||||
// This method expects that `detectChangesInRecordsInternal` will set the property
|
|
||||||
// `this.propertyBindingIndex` to the propertyBindingIndex of the first proto record. This is to
|
|
||||||
// facilitate error reporting.
|
|
||||||
detectChangesInRecords(throwOnChange: boolean): void {
|
|
||||||
if (!this.hydrated()) {
|
|
||||||
this.throwDehydratedError(this.id);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.detectChangesInRecordsInternal(throwOnChange);
|
|
||||||
} catch (e) {
|
|
||||||
// throwOnChange errors aren't counted as fatal errors.
|
|
||||||
if (!(e instanceof ExpressionChangedAfterItHasBeenCheckedException)) {
|
|
||||||
this.state = ChangeDetectorState.Errored;
|
|
||||||
}
|
|
||||||
this._throwError(e, e.stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subclasses should override this method to perform any work necessary to detect and report
|
|
||||||
// changes. For example, changes should be reported via `ChangeDetectionUtil.addChange`, lifecycle
|
|
||||||
// methods should be called, etc.
|
|
||||||
// This implementation should also set `this.propertyBindingIndex` to the propertyBindingIndex of
|
|
||||||
// the
|
|
||||||
// first proto record to facilitate error reporting. See {@link #detectChangesInRecords}.
|
|
||||||
detectChangesInRecordsInternal(throwOnChange: boolean): void {}
|
|
||||||
|
|
||||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
|
||||||
// implementation of `hydrateDirectives`.
|
|
||||||
hydrate(context: T, locals: Locals, dispatcher: ChangeDispatcher, pipes: Pipes): void {
|
|
||||||
this.dispatcher = dispatcher;
|
|
||||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
|
|
||||||
this.context = context;
|
|
||||||
|
|
||||||
this.locals = locals;
|
|
||||||
this.pipes = pipes;
|
|
||||||
this.hydrateDirectives(dispatcher);
|
|
||||||
this.state = ChangeDetectorState.NeverChecked;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subclasses should override this method to hydrate any directives.
|
|
||||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {}
|
|
||||||
|
|
||||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
|
||||||
// implementation of `dehydrateDirectives`.
|
|
||||||
dehydrate(): void {
|
|
||||||
this.dehydrateDirectives(true);
|
|
||||||
|
|
||||||
this._unsubscribeFromOutputs();
|
|
||||||
|
|
||||||
this.dispatcher = null;
|
|
||||||
this.context = null;
|
|
||||||
this.locals = null;
|
|
||||||
this.pipes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subclasses should override this method to dehydrate any directives. This method should reverse
|
|
||||||
// any work done in `hydrateDirectives`.
|
|
||||||
dehydrateDirectives(destroyPipes: boolean): void {}
|
|
||||||
|
|
||||||
hydrated(): boolean { return isPresent(this.context); }
|
|
||||||
|
|
||||||
destroyRecursive(): void {
|
|
||||||
this.dispatcher.notifyOnDestroy();
|
|
||||||
this.dehydrate();
|
|
||||||
var children = this.contentChildren;
|
|
||||||
for (var i = 0; i < children.length; i++) {
|
|
||||||
children[i].destroyRecursive();
|
|
||||||
}
|
|
||||||
children = this.viewChildren;
|
|
||||||
for (var i = 0; i < children.length; i++) {
|
|
||||||
children[i].destroyRecursive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
afterContentLifecycleCallbacks(): void {
|
|
||||||
this.dispatcher.notifyAfterContentChecked();
|
|
||||||
this.afterContentLifecycleCallbacksInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
afterContentLifecycleCallbacksInternal(): void {}
|
|
||||||
|
|
||||||
afterViewLifecycleCallbacks(): void {
|
|
||||||
this.dispatcher.notifyAfterViewChecked();
|
|
||||||
this.afterViewLifecycleCallbacksInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
afterViewLifecycleCallbacksInternal(): void {}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_detectChangesContentChildren(throwOnChange: boolean): void {
|
|
||||||
var c = this.contentChildren;
|
|
||||||
for (var i = 0; i < c.length; ++i) {
|
|
||||||
c[i].runDetectChanges(throwOnChange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_detectChangesInViewChildren(throwOnChange: boolean): void {
|
|
||||||
var c = this.viewChildren;
|
|
||||||
for (var i = 0; i < c.length; ++i) {
|
|
||||||
c[i].runDetectChanges(throwOnChange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
markAsCheckOnce(): void { this.mode = ChangeDetectionStrategy.CheckOnce; }
|
|
||||||
|
|
||||||
markPathToRootAsCheckOnce(): void {
|
|
||||||
var c: ChangeDetector = this;
|
|
||||||
while (isPresent(c) && c.mode !== ChangeDetectionStrategy.Detached) {
|
|
||||||
if (c.mode === ChangeDetectionStrategy.Checked) c.mode = ChangeDetectionStrategy.CheckOnce;
|
|
||||||
c = c.parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _unsubscribeFromOutputs(): void {
|
|
||||||
if (isPresent(this.outputSubscriptions)) {
|
|
||||||
for (var i = 0; i < this.outputSubscriptions.length; ++i) {
|
|
||||||
ObservableWrapper.dispose(this.outputSubscriptions[i]);
|
|
||||||
this.outputSubscriptions[i] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getDirectiveFor(directives: any, index: number): any {
|
|
||||||
return directives.getDirectiveFor(this.directiveIndices[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDetectorFor(directives: any, index: number): ChangeDetector {
|
|
||||||
return directives.getDetectorFor(this.directiveIndices[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyDispatcher(value: any): void {
|
|
||||||
this.dispatcher.notifyOnBinding(this._currentBinding(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
logBindingUpdate(value: any): void {
|
|
||||||
this.dispatcher.logBindingUpdate(this._currentBinding(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
addChange(changes: {[key: string]: any}, oldValue: any, newValue: any): {[key: string]: any} {
|
|
||||||
if (isBlank(changes)) {
|
|
||||||
changes = {};
|
|
||||||
}
|
|
||||||
changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _throwError(exception: any, stack: any): void {
|
|
||||||
var error;
|
|
||||||
try {
|
|
||||||
var c = this.dispatcher.getDebugContext(null, this._currentBinding().elementIndex, null);
|
|
||||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
|
||||||
c.injector, this._currentBinding().debug) :
|
|
||||||
null;
|
|
||||||
error = new ChangeDetectionError(this._currentBinding().debug, exception, stack, context);
|
|
||||||
} catch (e) {
|
|
||||||
// if an error happens during getting the debug context, we throw a ChangeDetectionError
|
|
||||||
// without the extra information.
|
|
||||||
error = new ChangeDetectionError(null, exception, stack, null);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
throwOnChangeError(oldValue: any, newValue: any): void {
|
|
||||||
throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBinding().debug,
|
|
||||||
oldValue, newValue, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
throwDehydratedError(detail: string): void { throw new DehydratedException(detail); }
|
|
||||||
|
|
||||||
private _currentBinding(): BindingTarget {
|
|
||||||
return this.bindingTargets[this.propertyBindingIndex];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
import {SetterFn} from 'angular2/src/core/reflection/types';
|
|
||||||
import {AST} from './parser/ast';
|
|
||||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
|
||||||
|
|
||||||
const DIRECTIVE_LIFECYCLE = "directiveLifecycle";
|
|
||||||
const BINDING = "native";
|
|
||||||
|
|
||||||
const DIRECTIVE = "directive";
|
|
||||||
const ELEMENT_PROPERTY = "elementProperty";
|
|
||||||
const ELEMENT_ATTRIBUTE = "elementAttribute";
|
|
||||||
const ELEMENT_CLASS = "elementClass";
|
|
||||||
const ELEMENT_STYLE = "elementStyle";
|
|
||||||
const TEXT_NODE = "textNode";
|
|
||||||
const EVENT = "event";
|
|
||||||
const HOST_EVENT = "hostEvent";
|
|
||||||
|
|
||||||
export class BindingTarget {
|
|
||||||
constructor(public mode: string, public elementIndex: number, public name: string,
|
|
||||||
public unit: string, public debug: string) {}
|
|
||||||
|
|
||||||
isDirective(): boolean { return this.mode === DIRECTIVE; }
|
|
||||||
|
|
||||||
isElementProperty(): boolean { return this.mode === ELEMENT_PROPERTY; }
|
|
||||||
|
|
||||||
isElementAttribute(): boolean { return this.mode === ELEMENT_ATTRIBUTE; }
|
|
||||||
|
|
||||||
isElementClass(): boolean { return this.mode === ELEMENT_CLASS; }
|
|
||||||
|
|
||||||
isElementStyle(): boolean { return this.mode === ELEMENT_STYLE; }
|
|
||||||
|
|
||||||
isTextNode(): boolean { return this.mode === TEXT_NODE; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BindingRecord {
|
|
||||||
constructor(public mode: string, public target: BindingTarget, public implicitReceiver: any,
|
|
||||||
public ast: AST, public setter: SetterFn, public lifecycleEvent: string,
|
|
||||||
public directiveRecord: DirectiveRecord) {}
|
|
||||||
|
|
||||||
isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; }
|
|
||||||
|
|
||||||
callOnChanges(): boolean {
|
|
||||||
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDefaultChangeDetection(): boolean {
|
|
||||||
return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection();
|
|
||||||
}
|
|
||||||
|
|
||||||
static createDirectiveDoCheck(directiveRecord: DirectiveRecord): BindingRecord {
|
|
||||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "DoCheck", directiveRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createDirectiveOnInit(directiveRecord: DirectiveRecord): BindingRecord {
|
|
||||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "OnInit", directiveRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createDirectiveOnChanges(directiveRecord: DirectiveRecord): BindingRecord {
|
|
||||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "OnChanges",
|
|
||||||
directiveRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
|
|
||||||
directiveRecord: DirectiveRecord): BindingRecord {
|
|
||||||
var elementIndex = directiveRecord.directiveIndex.elementIndex;
|
|
||||||
var t = new BindingTarget(DIRECTIVE, elementIndex, propertyName, null, ast.toString());
|
|
||||||
return new BindingRecord(DIRECTIVE, t, 0, ast, setter, null, directiveRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static createForElementProperty(ast: AST, elementIndex: number,
|
|
||||||
propertyName: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_PROPERTY, elementIndex, propertyName, null, ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForElementAttribute(ast: AST, elementIndex: number,
|
|
||||||
attributeName: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_ATTRIBUTE, elementIndex, attributeName, null, ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForElementClass(ast: AST, elementIndex: number, className: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_CLASS, elementIndex, className, null, ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForElementStyle(ast: AST, elementIndex: number, styleName: string,
|
|
||||||
unit: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_STYLE, elementIndex, styleName, unit, ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST,
|
|
||||||
propertyName: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_PROPERTY, directiveIndex.elementIndex, propertyName, null,
|
|
||||||
ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForHostAttribute(directiveIndex: DirectiveIndex, ast: AST,
|
|
||||||
attributeName: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_ATTRIBUTE, directiveIndex.elementIndex, attributeName, null,
|
|
||||||
ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForHostClass(directiveIndex: DirectiveIndex, ast: AST,
|
|
||||||
className: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_CLASS, directiveIndex.elementIndex, className, null,
|
|
||||||
ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForHostStyle(directiveIndex: DirectiveIndex, ast: AST, styleName: string,
|
|
||||||
unit: string): BindingRecord {
|
|
||||||
var t = new BindingTarget(ELEMENT_STYLE, directiveIndex.elementIndex, styleName, unit,
|
|
||||||
ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static createForTextNode(ast: AST, elementIndex: number): BindingRecord {
|
|
||||||
var t = new BindingTarget(TEXT_NODE, elementIndex, null, null, ast.toString());
|
|
||||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static createForEvent(ast: AST, eventName: string, elementIndex: number): BindingRecord {
|
|
||||||
var t = new BindingTarget(EVENT, elementIndex, eventName, null, ast.toString());
|
|
||||||
return new BindingRecord(EVENT, t, 0, ast, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
static createForHostEvent(ast: AST, eventName: string,
|
|
||||||
directiveRecord: DirectiveRecord): BindingRecord {
|
|
||||||
var directiveIndex = directiveRecord.directiveIndex;
|
|
||||||
var t =
|
|
||||||
new BindingTarget(HOST_EVENT, directiveIndex.elementIndex, eventName, null, ast.toString());
|
|
||||||
return new BindingRecord(HOST_EVENT, t, directiveIndex, ast, null, null, directiveRecord);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,38 +15,14 @@ export {
|
||||||
DefaultIterableDifferFactory,
|
DefaultIterableDifferFactory,
|
||||||
CollectionChangeRecord
|
CollectionChangeRecord
|
||||||
} from './differs/default_iterable_differ';
|
} from './differs/default_iterable_differ';
|
||||||
export {
|
|
||||||
ASTWithSource,
|
|
||||||
AST,
|
|
||||||
AstTransformer,
|
|
||||||
PropertyRead,
|
|
||||||
LiteralArray,
|
|
||||||
ImplicitReceiver
|
|
||||||
} from './parser/ast';
|
|
||||||
|
|
||||||
export {Lexer} from './parser/lexer';
|
|
||||||
export {Parser} from './parser/parser';
|
|
||||||
export {Locals} from './parser/locals';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
DehydratedException,
|
ChangeDetectionStrategy,
|
||||||
ExpressionChangedAfterItHasBeenCheckedException,
|
CHANGE_DETECTION_STRATEGY_VALUES,
|
||||||
ChangeDetectionError
|
ChangeDetectorState,
|
||||||
} from './exceptions';
|
CHANGE_DETECTOR_STATE_VALUES,
|
||||||
export {
|
isDefaultChangeDetectionStrategy
|
||||||
ProtoChangeDetector,
|
} from './constants';
|
||||||
ChangeDetector,
|
|
||||||
ChangeDispatcher,
|
|
||||||
ChangeDetectorDefinition,
|
|
||||||
DebugContext,
|
|
||||||
ChangeDetectorGenConfig
|
|
||||||
} from './interfaces';
|
|
||||||
export {ChangeDetectionStrategy, CHANGE_DETECTION_STRATEGY_VALUES} from './constants';
|
|
||||||
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
|
||||||
export {JitProtoChangeDetector} from './jit_proto_change_detector';
|
|
||||||
export {BindingRecord, BindingTarget} from './binding_record';
|
|
||||||
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
|
||||||
export {DynamicChangeDetector} from './dynamic_change_detector';
|
|
||||||
export {ChangeDetectorRef} from './change_detector_ref';
|
export {ChangeDetectorRef} from './change_detector_ref';
|
||||||
export {
|
export {
|
||||||
IterableDiffers,
|
IterableDiffers,
|
||||||
|
@ -56,7 +32,15 @@ export {
|
||||||
} from './differs/iterable_differs';
|
} from './differs/iterable_differs';
|
||||||
export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||||
export {PipeTransform} from './pipe_transform';
|
export {PipeTransform} from './pipe_transform';
|
||||||
export {WrappedValue, SimpleChange} from './change_detection_util';
|
|
||||||
|
export {
|
||||||
|
WrappedValue,
|
||||||
|
ValueUnwrapper,
|
||||||
|
SimpleChange,
|
||||||
|
devModeEqual,
|
||||||
|
looseIdentical,
|
||||||
|
uninitialized
|
||||||
|
} from './change_detection_util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structural diffing for `Object`s and `Map`s.
|
* Structural diffing for `Object`s and `Map`s.
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
library change_detection.change_detection_jit_generator;
|
|
||||||
|
|
||||||
/// Placeholder JIT generator for Dart.
|
|
||||||
/// Dart does not support `eval`, so JIT generation is not an option. Instead,
|
|
||||||
/// the Dart transformer pre-generates these Change Detector classes and
|
|
||||||
/// registers them with the system. See `PreGeneratedChangeDetection`,
|
|
||||||
/// `PregenProtoChangeDetector`, and
|
|
||||||
/// `src/transform/template_compiler/change_detector_codegen.dart` for details.
|
|
||||||
class ChangeDetectorJITGenerator {
|
|
||||||
String typeName;
|
|
||||||
ChangeDetectorJITGenerator(
|
|
||||||
definition, changeDetectionUtilVarName, abstractChangeDetectorVarName, changeDetectorStateVarName) {}
|
|
||||||
|
|
||||||
generate() {
|
|
||||||
throw "Jit Change Detection is not supported in Dart";
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSource() {
|
|
||||||
throw "Jit Change Detection is not supported in Dart";
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isSupported() => false;
|
|
||||||
}
|
|
|
@ -1,501 +0,0 @@
|
||||||
import {Type, assertionsEnabled, isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
|
||||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
|
||||||
|
|
||||||
import {ProtoRecord, RecordType} from './proto_record';
|
|
||||||
import {CodegenNameUtil, sanitizeName} from './codegen_name_util';
|
|
||||||
import {CodegenLogicUtil} from './codegen_logic_util';
|
|
||||||
import {codify} from './codegen_facade';
|
|
||||||
import {EventBinding} from './event_binding';
|
|
||||||
import {BindingTarget} from './binding_record';
|
|
||||||
import {ChangeDetectorGenConfig, ChangeDetectorDefinition} from './interfaces';
|
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
|
||||||
import {createPropertyRecords, createEventRecords} from './proto_change_detector';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* kind of behaviour.
|
|
||||||
*
|
|
||||||
* This code should be kept in sync with the Dart transformer's
|
|
||||||
* `angular2.transform.template_compiler.change_detector_codegen` library. If you make updates
|
|
||||||
* here, please make equivalent changes there.
|
|
||||||
*/
|
|
||||||
const IS_CHANGED_LOCAL = "isChanged";
|
|
||||||
const CHANGES_LOCAL = "changes";
|
|
||||||
|
|
||||||
export class ChangeDetectorJITGenerator {
|
|
||||||
private _logic: CodegenLogicUtil;
|
|
||||||
private _names: CodegenNameUtil;
|
|
||||||
private _endOfBlockIdxs: number[];
|
|
||||||
private id: string;
|
|
||||||
private changeDetectionStrategy: ChangeDetectionStrategy;
|
|
||||||
private records: ProtoRecord[];
|
|
||||||
private propertyBindingTargets: BindingTarget[];
|
|
||||||
private eventBindings: EventBinding[];
|
|
||||||
private directiveRecords: any[];
|
|
||||||
private genConfig: ChangeDetectorGenConfig;
|
|
||||||
typeName: string;
|
|
||||||
|
|
||||||
constructor(definition: ChangeDetectorDefinition, private changeDetectionUtilVarName: string,
|
|
||||||
private abstractChangeDetectorVarName: string,
|
|
||||||
private changeDetectorStateVarName: string) {
|
|
||||||
var propertyBindingRecords = createPropertyRecords(definition);
|
|
||||||
var eventBindingRecords = createEventRecords(definition);
|
|
||||||
var propertyBindingTargets = definition.bindingRecords.map(b => b.target);
|
|
||||||
this.id = definition.id;
|
|
||||||
this.changeDetectionStrategy = definition.strategy;
|
|
||||||
this.genConfig = definition.genConfig;
|
|
||||||
|
|
||||||
this.records = propertyBindingRecords;
|
|
||||||
this.propertyBindingTargets = propertyBindingTargets;
|
|
||||||
this.eventBindings = eventBindingRecords;
|
|
||||||
this.directiveRecords = definition.directiveRecords;
|
|
||||||
this._names = new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords,
|
|
||||||
this.changeDetectionUtilVarName);
|
|
||||||
this._logic = new CodegenLogicUtil(this._names, this.changeDetectionUtilVarName,
|
|
||||||
this.changeDetectorStateVarName);
|
|
||||||
this.typeName = sanitizeName(`ChangeDetector_${this.id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
generate(): Function {
|
|
||||||
var factorySource = `
|
|
||||||
${this.generateSource()}
|
|
||||||
return function() {
|
|
||||||
return new ${this.typeName}();
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
|
|
||||||
this.changeDetectorStateVarName, factorySource)(
|
|
||||||
AbstractChangeDetector, ChangeDetectionUtil, ChangeDetectorState);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSource(): string {
|
|
||||||
return `
|
|
||||||
var ${this.typeName} = function ${this.typeName}() {
|
|
||||||
${this.abstractChangeDetectorVarName}.call(
|
|
||||||
this, ${JSON.stringify(this.id)}, ${this.records.length},
|
|
||||||
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
|
|
||||||
${codify(this.changeDetectionStrategy)});
|
|
||||||
this.dehydrateDirectives(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
${this.typeName}.prototype = Object.create(${this.abstractChangeDetectorVarName}.prototype);
|
|
||||||
|
|
||||||
${this.typeName}.prototype.detectChangesInRecordsInternal = function(throwOnChange) {
|
|
||||||
${this._names.genInitLocals()}
|
|
||||||
var ${IS_CHANGED_LOCAL} = false;
|
|
||||||
var ${CHANGES_LOCAL} = null;
|
|
||||||
|
|
||||||
${this._genAllRecords(this.records)}
|
|
||||||
}
|
|
||||||
|
|
||||||
${this._maybeGenHandleEventInternal()}
|
|
||||||
|
|
||||||
${this._maybeGenAfterContentLifecycleCallbacks()}
|
|
||||||
|
|
||||||
${this._maybeGenAfterViewLifecycleCallbacks()}
|
|
||||||
|
|
||||||
${this._maybeGenHydrateDirectives()}
|
|
||||||
|
|
||||||
${this._maybeGenDehydrateDirectives()}
|
|
||||||
|
|
||||||
${this._genPropertyBindingTargets()}
|
|
||||||
|
|
||||||
${this._genDirectiveIndices()}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genPropertyBindingTargets(): string {
|
|
||||||
var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets,
|
|
||||||
this.genConfig.genDebugInfo);
|
|
||||||
return `${this.typeName}.gen_propertyBindingTargets = ${targets};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genDirectiveIndices(): string {
|
|
||||||
var indices = this._logic.genDirectiveIndices(this.directiveRecords);
|
|
||||||
return `${this.typeName}.gen_directiveIndices = ${indices};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeGenHandleEventInternal(): string {
|
|
||||||
if (this.eventBindings.length > 0) {
|
|
||||||
var handlers = this.eventBindings.map(eb => this._genEventBinding(eb)).join("\n");
|
|
||||||
return `
|
|
||||||
${this.typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
|
|
||||||
var ${this._names.getPreventDefaultAccesor()} = false;
|
|
||||||
${this._names.genInitEventLocals()}
|
|
||||||
${handlers}
|
|
||||||
return ${this._names.getPreventDefaultAccesor()};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genEventBinding(eb: EventBinding): string {
|
|
||||||
let codes: String[] = [];
|
|
||||||
this._endOfBlockIdxs = [];
|
|
||||||
|
|
||||||
ListWrapper.forEachWithIndex(eb.records, (r, i) => {
|
|
||||||
let code;
|
|
||||||
|
|
||||||
if (r.isConditionalSkipRecord()) {
|
|
||||||
code = this._genConditionalSkip(r, this._names.getEventLocalName(eb, i));
|
|
||||||
} else if (r.isUnconditionalSkipRecord()) {
|
|
||||||
code = this._genUnconditionalSkip(r);
|
|
||||||
} else {
|
|
||||||
code = this._genEventBindingEval(eb, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
code += this._genEndOfSkipBlock(i);
|
|
||||||
|
|
||||||
codes.push(code);
|
|
||||||
});
|
|
||||||
|
|
||||||
return `
|
|
||||||
if (eventName === "${eb.eventName}" && elIndex === ${eb.elIndex}) {
|
|
||||||
${codes.join("\n")}
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genEventBindingEval(eb: EventBinding, r: ProtoRecord): string {
|
|
||||||
if (r.lastInBinding) {
|
|
||||||
var evalRecord = this._logic.genEventBindingEvalValue(eb, r);
|
|
||||||
var markPath = this._genMarkPathToRootAsCheckOnce(r);
|
|
||||||
var prevDefault = this._genUpdatePreventDefault(eb, r);
|
|
||||||
return `${markPath}\n${evalRecord}\n${prevDefault}`;
|
|
||||||
} else {
|
|
||||||
return this._logic.genEventBindingEvalValue(eb, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genMarkPathToRootAsCheckOnce(r: ProtoRecord): string {
|
|
||||||
var br = r.bindingRecord;
|
|
||||||
if (br.isDefaultChangeDetection()) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return `${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markPathToRootAsCheckOnce();`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genUpdatePreventDefault(eb: EventBinding, r: ProtoRecord): string {
|
|
||||||
var local = this._names.getEventLocalName(eb, r.selfIndex);
|
|
||||||
return `if (${local} === false) { ${this._names.getPreventDefaultAccesor()} = true};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeGenDehydrateDirectives(): string {
|
|
||||||
var destroyPipesCode = this._names.genPipeOnDestroy();
|
|
||||||
var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
|
|
||||||
var dehydrateFieldsCode = this._names.genDehydrateFields();
|
|
||||||
if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
|
|
||||||
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
|
|
||||||
if (destroyPipes) {
|
|
||||||
${destroyPipesCode}
|
|
||||||
${destroyDirectivesCode}
|
|
||||||
}
|
|
||||||
${dehydrateFieldsCode}
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeGenHydrateDirectives(): string {
|
|
||||||
var hydrateDirectivesCode = this._logic.genHydrateDirectives(this.directiveRecords);
|
|
||||||
var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords);
|
|
||||||
if (!hydrateDirectivesCode && !hydrateDetectorsCode) return '';
|
|
||||||
return `${this.typeName}.prototype.hydrateDirectives = function(directives) {
|
|
||||||
${hydrateDirectivesCode}
|
|
||||||
${hydrateDetectorsCode}
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeGenAfterContentLifecycleCallbacks(): string {
|
|
||||||
var notifications = this._logic.genContentLifecycleCallbacks(this.directiveRecords);
|
|
||||||
if (notifications.length > 0) {
|
|
||||||
var directiveNotifications = notifications.join("\n");
|
|
||||||
return `
|
|
||||||
${this.typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
|
|
||||||
${directiveNotifications}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeGenAfterViewLifecycleCallbacks(): string {
|
|
||||||
var notifications = this._logic.genViewLifecycleCallbacks(this.directiveRecords);
|
|
||||||
if (notifications.length > 0) {
|
|
||||||
var directiveNotifications = notifications.join("\n");
|
|
||||||
return `
|
|
||||||
${this.typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
|
|
||||||
${directiveNotifications}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genAllRecords(rs: ProtoRecord[]): string {
|
|
||||||
var codes: String[] = [];
|
|
||||||
this._endOfBlockIdxs = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < rs.length; i++) {
|
|
||||||
let code;
|
|
||||||
let r = rs[i];
|
|
||||||
|
|
||||||
if (r.isLifeCycleRecord()) {
|
|
||||||
code = this._genDirectiveLifecycle(r);
|
|
||||||
} else if (r.isPipeRecord()) {
|
|
||||||
code = this._genPipeCheck(r);
|
|
||||||
} else if (r.isConditionalSkipRecord()) {
|
|
||||||
code = this._genConditionalSkip(r, this._names.getLocalName(r.contextIndex));
|
|
||||||
} else if (r.isUnconditionalSkipRecord()) {
|
|
||||||
code = this._genUnconditionalSkip(r);
|
|
||||||
} else {
|
|
||||||
code = this._genReferenceCheck(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
code = `
|
|
||||||
${this._maybeFirstInBinding(r)}
|
|
||||||
${code}
|
|
||||||
${this._maybeGenLastInDirective(r)}
|
|
||||||
${this._genEndOfSkipBlock(i)}
|
|
||||||
`;
|
|
||||||
|
|
||||||
codes.push(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
return codes.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genConditionalSkip(r: ProtoRecord, condition: string): string {
|
|
||||||
let maybeNegate = r.mode === RecordType.SkipRecordsIf ? '!' : '';
|
|
||||||
this._endOfBlockIdxs.push(r.fixedArgs[0] - 1);
|
|
||||||
|
|
||||||
return `if (${maybeNegate}${condition}) {`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genUnconditionalSkip(r: ProtoRecord): string {
|
|
||||||
this._endOfBlockIdxs.pop();
|
|
||||||
this._endOfBlockIdxs.push(r.fixedArgs[0] - 1);
|
|
||||||
return `} else {`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genEndOfSkipBlock(protoIndex: number): string {
|
|
||||||
if (!ListWrapper.isEmpty(this._endOfBlockIdxs)) {
|
|
||||||
let endOfBlock = ListWrapper.last(this._endOfBlockIdxs);
|
|
||||||
if (protoIndex === endOfBlock) {
|
|
||||||
this._endOfBlockIdxs.pop();
|
|
||||||
return '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genDirectiveLifecycle(r: ProtoRecord): string {
|
|
||||||
if (r.name === "DoCheck") {
|
|
||||||
return this._genOnCheck(r);
|
|
||||||
} else if (r.name === "OnInit") {
|
|
||||||
return this._genOnInit(r);
|
|
||||||
} else if (r.name === "OnChanges") {
|
|
||||||
return this._genOnChange(r);
|
|
||||||
} else {
|
|
||||||
throw new BaseException(`Unknown lifecycle event '${r.name}'`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genPipeCheck(r: ProtoRecord): string {
|
|
||||||
var context = this._names.getLocalName(r.contextIndex);
|
|
||||||
var argString = r.args.map((arg) => this._names.getLocalName(arg)).join(", ");
|
|
||||||
|
|
||||||
var oldValue = this._names.getFieldName(r.selfIndex);
|
|
||||||
var newValue = this._names.getLocalName(r.selfIndex);
|
|
||||||
|
|
||||||
var pipe = this._names.getPipeName(r.selfIndex);
|
|
||||||
var pipeName = r.name;
|
|
||||||
|
|
||||||
var init = `
|
|
||||||
if (${pipe} === ${this.changeDetectionUtilVarName}.uninitialized) {
|
|
||||||
${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeName}');
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
var read = `${newValue} = ${pipe}.pipe.transform(${context}, [${argString}]);`;
|
|
||||||
|
|
||||||
var contexOrArgCheck = r.args.map((a) => this._names.getChangeName(a));
|
|
||||||
contexOrArgCheck.push(this._names.getChangeName(r.contextIndex));
|
|
||||||
var condition = `!${pipe}.pure || (${contexOrArgCheck.join(" || ")})`;
|
|
||||||
|
|
||||||
var check = `
|
|
||||||
${this._genThrowOnChangeCheck(oldValue, newValue)}
|
|
||||||
if (${this.changeDetectionUtilVarName}.looseNotIdentical(${oldValue}, ${newValue})) {
|
|
||||||
${newValue} = ${this.changeDetectionUtilVarName}.unwrapValue(${newValue})
|
|
||||||
${this._genChangeMarker(r)}
|
|
||||||
${this._genUpdateDirectiveOrElement(r)}
|
|
||||||
${this._genAddToChanges(r)}
|
|
||||||
${oldValue} = ${newValue};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
var genCode = r.shouldBeChecked() ? `${read}${check}` : read;
|
|
||||||
|
|
||||||
if (r.isUsedByOtherRecord()) {
|
|
||||||
return `${init} if (${condition}) { ${genCode} } else { ${newValue} = ${oldValue}; }`;
|
|
||||||
} else {
|
|
||||||
return `${init} if (${condition}) { ${genCode} }`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genReferenceCheck(r: ProtoRecord): string {
|
|
||||||
var oldValue = this._names.getFieldName(r.selfIndex);
|
|
||||||
var newValue = this._names.getLocalName(r.selfIndex);
|
|
||||||
var read = `
|
|
||||||
${this._logic.genPropertyBindingEvalValue(r)}
|
|
||||||
`;
|
|
||||||
|
|
||||||
var check = `
|
|
||||||
${this._genThrowOnChangeCheck(oldValue, newValue)}
|
|
||||||
if (${this.changeDetectionUtilVarName}.looseNotIdentical(${oldValue}, ${newValue})) {
|
|
||||||
${this._genChangeMarker(r)}
|
|
||||||
${this._genUpdateDirectiveOrElement(r)}
|
|
||||||
${this._genAddToChanges(r)}
|
|
||||||
${oldValue} = ${newValue};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
var genCode = r.shouldBeChecked() ? `${read}${check}` : read;
|
|
||||||
|
|
||||||
if (r.isPureFunction()) {
|
|
||||||
var condition = r.args.map((a) => this._names.getChangeName(a)).join(" || ");
|
|
||||||
if (r.isUsedByOtherRecord()) {
|
|
||||||
return `if (${condition}) { ${genCode} } else { ${newValue} = ${oldValue}; }`;
|
|
||||||
} else {
|
|
||||||
return `if (${condition}) { ${genCode} }`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return genCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genChangeMarker(r: ProtoRecord): string {
|
|
||||||
return r.argumentToPureFunction ? `${this._names.getChangeName(r.selfIndex)} = true` : ``;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genUpdateDirectiveOrElement(r: ProtoRecord): string {
|
|
||||||
if (!r.lastInBinding) return "";
|
|
||||||
|
|
||||||
var newValue = this._names.getLocalName(r.selfIndex);
|
|
||||||
var notifyDebug = this.genConfig.logBindingUpdate ? `this.logBindingUpdate(${newValue});` : "";
|
|
||||||
|
|
||||||
var br = r.bindingRecord;
|
|
||||||
if (br.target.isDirective()) {
|
|
||||||
var directiveProperty =
|
|
||||||
`${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}`;
|
|
||||||
return `
|
|
||||||
${directiveProperty} = ${newValue};
|
|
||||||
${notifyDebug}
|
|
||||||
${IS_CHANGED_LOCAL} = true;
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
return `
|
|
||||||
this.notifyDispatcher(${newValue});
|
|
||||||
${notifyDebug}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genThrowOnChangeCheck(oldValue: string, newValue: string): string {
|
|
||||||
if (assertionsEnabled()) {
|
|
||||||
return `
|
|
||||||
if (throwOnChange && !${this.changeDetectionUtilVarName}.devModeEqual(${oldValue}, ${newValue})) {
|
|
||||||
this.throwOnChangeError(${oldValue}, ${newValue});
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genAddToChanges(r: ProtoRecord): string {
|
|
||||||
var newValue = this._names.getLocalName(r.selfIndex);
|
|
||||||
var oldValue = this._names.getFieldName(r.selfIndex);
|
|
||||||
if (!r.bindingRecord.callOnChanges()) return "";
|
|
||||||
return `${CHANGES_LOCAL} = this.addChange(${CHANGES_LOCAL}, ${oldValue}, ${newValue});`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeFirstInBinding(r: ProtoRecord): string {
|
|
||||||
var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
|
|
||||||
var firstInBinding = isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
|
|
||||||
return firstInBinding && !r.bindingRecord.isDirectiveLifecycle() ?
|
|
||||||
`${this._names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};` :
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_maybeGenLastInDirective(r: ProtoRecord): string {
|
|
||||||
if (!r.lastInDirective) return "";
|
|
||||||
return `
|
|
||||||
${CHANGES_LOCAL} = null;
|
|
||||||
${this._genNotifyOnPushDetectors(r)}
|
|
||||||
${IS_CHANGED_LOCAL} = false;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genOnCheck(r: ProtoRecord): string {
|
|
||||||
var br = r.bindingRecord;
|
|
||||||
return `if (!throwOnChange) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.ngDoCheck();`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genOnInit(r: ProtoRecord): string {
|
|
||||||
var br = r.bindingRecord;
|
|
||||||
return `if (!throwOnChange && ${this._names.getStateName()} === ${this.changeDetectorStateVarName}.NeverChecked) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.ngOnInit();`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genOnChange(r: ProtoRecord): string {
|
|
||||||
var br = r.bindingRecord;
|
|
||||||
return `if (!throwOnChange && ${CHANGES_LOCAL}) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.ngOnChanges(${CHANGES_LOCAL});`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genNotifyOnPushDetectors(r: ProtoRecord): string {
|
|
||||||
var br = r.bindingRecord;
|
|
||||||
if (!r.lastInDirective || br.isDefaultChangeDetection()) return "";
|
|
||||||
var retVal = `
|
|
||||||
if(${IS_CHANGED_LOCAL}) {
|
|
||||||
${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markAsCheckOnce();
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +1,25 @@
|
||||||
|
import {CONST_EXPR, isBlank, looseIdentical, isPrimitive} from 'angular2/src/facade/lang';
|
||||||
import {
|
import {
|
||||||
CONST_EXPR,
|
|
||||||
isPresent,
|
|
||||||
isBlank,
|
|
||||||
Type,
|
|
||||||
StringWrapper,
|
|
||||||
looseIdentical,
|
|
||||||
isPrimitive
|
|
||||||
} from 'angular2/src/facade/lang';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
import {
|
|
||||||
ListWrapper,
|
|
||||||
MapWrapper,
|
|
||||||
StringMapWrapper,
|
StringMapWrapper,
|
||||||
isListLikeIterable,
|
isListLikeIterable,
|
||||||
areIterablesEqual
|
areIterablesEqual
|
||||||
} from 'angular2/src/facade/collection';
|
} from 'angular2/src/facade/collection';
|
||||||
import {ProtoRecord} from './proto_record';
|
|
||||||
import {ChangeDetectionStrategy, isDefaultChangeDetectionStrategy} from './constants';
|
|
||||||
import {implementsOnDestroy} from './pipe_lifecycle_reflector';
|
|
||||||
import {BindingTarget} from './binding_record';
|
|
||||||
import {DirectiveIndex} from './directive_record';
|
|
||||||
import {SelectedPipe} from './pipes';
|
|
||||||
|
|
||||||
|
export {looseIdentical} from 'angular2/src/facade/lang';
|
||||||
|
export var uninitialized: Object = CONST_EXPR<Object>(new Object());
|
||||||
|
|
||||||
|
export function devModeEqual(a: any, b: any): boolean {
|
||||||
|
if (isListLikeIterable(a) && isListLikeIterable(b)) {
|
||||||
|
return areIterablesEqual(a, b, devModeEqual);
|
||||||
|
|
||||||
|
} else if (!isListLikeIterable(a) && !isPrimitive(a) && !isListLikeIterable(b) &&
|
||||||
|
!isPrimitive(b)) {
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return looseIdentical(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the result of a {@link PipeMetadata} transformation has changed even though the
|
* Indicates that the result of a {@link PipeMetadata} transformation has changed even though the
|
||||||
|
@ -44,22 +42,25 @@ import {SelectedPipe} from './pipes';
|
||||||
export class WrappedValue {
|
export class WrappedValue {
|
||||||
constructor(public wrapped: any) {}
|
constructor(public wrapped: any) {}
|
||||||
|
|
||||||
static wrap(value: any): WrappedValue {
|
static wrap(value: any): WrappedValue { return new WrappedValue(value); }
|
||||||
var w = _wrappedValues[_wrappedIndex++ % 5];
|
|
||||||
w.wrapped = value;
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _wrappedValues = [
|
/**
|
||||||
new WrappedValue(null),
|
* Helper class for unwrapping WrappedValue s
|
||||||
new WrappedValue(null),
|
*/
|
||||||
new WrappedValue(null),
|
export class ValueUnwrapper {
|
||||||
new WrappedValue(null),
|
public hasWrappedValue = false;
|
||||||
new WrappedValue(null)
|
|
||||||
];
|
|
||||||
|
|
||||||
var _wrappedIndex = 0;
|
unwrap(value: any): any {
|
||||||
|
if (value instanceof WrappedValue) {
|
||||||
|
this.hasWrappedValue = true;
|
||||||
|
return value.wrapped;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() { this.hasWrappedValue = false; }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a basic change from a previous to a new value.
|
* Represents a basic change from a previous to a new value.
|
||||||
|
@ -70,140 +71,5 @@ export class SimpleChange {
|
||||||
/**
|
/**
|
||||||
* Check whether the new value is the first value assigned.
|
* Check whether the new value is the first value assigned.
|
||||||
*/
|
*/
|
||||||
isFirstChange(): boolean { return this.previousValue === ChangeDetectionUtil.uninitialized; }
|
isFirstChange(): boolean { return this.previousValue === uninitialized; }
|
||||||
}
|
|
||||||
|
|
||||||
function _simpleChange(previousValue, currentValue): SimpleChange {
|
|
||||||
return new SimpleChange(previousValue, currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tslint:disable:requireParameterType */
|
|
||||||
export class ChangeDetectionUtil {
|
|
||||||
static uninitialized: Object = CONST_EXPR<Object>(new Object());
|
|
||||||
|
|
||||||
static arrayFn0(): any[] { return []; }
|
|
||||||
static arrayFn1(a1): any[] { return [a1]; }
|
|
||||||
static arrayFn2(a1, a2): any[] { return [a1, a2]; }
|
|
||||||
static arrayFn3(a1, a2, a3): any[] { return [a1, a2, a3]; }
|
|
||||||
static arrayFn4(a1, a2, a3, a4): any[] { return [a1, a2, a3, a4]; }
|
|
||||||
static arrayFn5(a1, a2, a3, a4, a5): any[] { return [a1, a2, a3, a4, a5]; }
|
|
||||||
static arrayFn6(a1, a2, a3, a4, a5, a6): any[] { return [a1, a2, a3, a4, a5, a6]; }
|
|
||||||
static arrayFn7(a1, a2, a3, a4, a5, a6, a7): any[] { return [a1, a2, a3, a4, a5, a6, a7]; }
|
|
||||||
static arrayFn8(a1, a2, a3, a4, a5, a6, a7, a8): any[] {
|
|
||||||
return [a1, a2, a3, a4, a5, a6, a7, a8];
|
|
||||||
}
|
|
||||||
static arrayFn9(a1, a2, a3, a4, a5, a6, a7, a8, a9): any[] {
|
|
||||||
return [a1, a2, a3, a4, a5, a6, a7, a8, a9];
|
|
||||||
}
|
|
||||||
|
|
||||||
static operation_negate(value): any { return !value; }
|
|
||||||
static operation_add(left, right): any { return left + right; }
|
|
||||||
static operation_subtract(left, right): any { return left - right; }
|
|
||||||
static operation_multiply(left, right): any { return left * right; }
|
|
||||||
static operation_divide(left, right): any { return left / right; }
|
|
||||||
static operation_remainder(left, right): any { return left % right; }
|
|
||||||
static operation_equals(left, right): any { return left == right; }
|
|
||||||
static operation_not_equals(left, right): any { return left != right; }
|
|
||||||
static operation_identical(left, right): any { return left === right; }
|
|
||||||
static operation_not_identical(left, right): any { return left !== right; }
|
|
||||||
static operation_less_then(left, right): any { return left < right; }
|
|
||||||
static operation_greater_then(left, right): any { return left > right; }
|
|
||||||
static operation_less_or_equals_then(left, right): any { return left <= right; }
|
|
||||||
static operation_greater_or_equals_then(left, right): any { return left >= right; }
|
|
||||||
static cond(cond, trueVal, falseVal): any { return cond ? trueVal : falseVal; }
|
|
||||||
|
|
||||||
static mapFn(keys: any[]): any {
|
|
||||||
function buildMap(values): {[k: /*any*/ string]: any} {
|
|
||||||
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): any { return obj[args[0]]; }
|
|
||||||
|
|
||||||
static unwrapValue(value: any): any {
|
|
||||||
if (value instanceof WrappedValue) {
|
|
||||||
return value.wrapped;
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static changeDetectionMode(strategy: ChangeDetectionStrategy): ChangeDetectionStrategy {
|
|
||||||
return isDefaultChangeDetectionStrategy(strategy) ? ChangeDetectionStrategy.CheckAlways :
|
|
||||||
ChangeDetectionStrategy.CheckOnce;
|
|
||||||
}
|
|
||||||
|
|
||||||
static simpleChange(previousValue: any, currentValue: any): SimpleChange {
|
|
||||||
return _simpleChange(previousValue, currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static isValueBlank(value: any): boolean { return isBlank(value); }
|
|
||||||
|
|
||||||
static s(value: any): string { return isPresent(value) ? `${value}` : ''; }
|
|
||||||
|
|
||||||
static protoByIndex(protos: ProtoRecord[], selfIndex: number): ProtoRecord {
|
|
||||||
return selfIndex < 1 ?
|
|
||||||
null :
|
|
||||||
protos[selfIndex - 1]; // self index is shifted by one because of context
|
|
||||||
}
|
|
||||||
|
|
||||||
static callPipeOnDestroy(selectedPipe: SelectedPipe): void {
|
|
||||||
if (implementsOnDestroy(selectedPipe.pipe)) {
|
|
||||||
(<any>selectedPipe.pipe).ngOnDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bindingTarget(mode: string, elementIndex: number, name: string, unit: string,
|
|
||||||
debug: string): BindingTarget {
|
|
||||||
return new BindingTarget(mode, elementIndex, name, unit, debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
static directiveIndex(elementIndex: number, directiveIndex: number): DirectiveIndex {
|
|
||||||
return new DirectiveIndex(elementIndex, directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static looseNotIdentical(a: any, b: any): boolean { return !looseIdentical(a, b); }
|
|
||||||
|
|
||||||
static devModeEqual(a: any, b: any): boolean {
|
|
||||||
if (isListLikeIterable(a) && isListLikeIterable(b)) {
|
|
||||||
return areIterablesEqual(a, b, ChangeDetectionUtil.devModeEqual);
|
|
||||||
|
|
||||||
} else if (!isListLikeIterable(a) && !isPrimitive(a) && !isListLikeIterable(b) &&
|
|
||||||
!isPrimitive(b)) {
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return looseIdentical(a, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import {ChangeDetector} from './interfaces';
|
|
||||||
import {ChangeDetectionStrategy} from './constants';
|
|
||||||
|
|
||||||
export abstract class ChangeDetectorRef {
|
export abstract class ChangeDetectorRef {
|
||||||
/**
|
/**
|
||||||
* Marks all {@link ChangeDetectionStrategy#OnPush} ancestors as to be checked.
|
* Marks all {@link ChangeDetectionStrategy#OnPush} ancestors as to be checked.
|
||||||
|
@ -193,16 +190,3 @@ export abstract class ChangeDetectorRef {
|
||||||
*/
|
*/
|
||||||
abstract reattach(): void;
|
abstract reattach(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
|
||||||
constructor(private _cd: ChangeDetector) { super(); }
|
|
||||||
|
|
||||||
markForCheck(): void { this._cd.markPathToRootAsCheckOnce(); }
|
|
||||||
detach(): void { this._cd.mode = ChangeDetectionStrategy.Detached; }
|
|
||||||
detectChanges(): void { this._cd.detectChanges(); }
|
|
||||||
checkNoChanges(): void { this._cd.checkNoChanges(); }
|
|
||||||
reattach(): void {
|
|
||||||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
|
||||||
this.markForCheck();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
import {isPresent, isBlank, looseIdentical} from 'angular2/src/facade/lang';
|
|
||||||
import {ListWrapper, Map} from 'angular2/src/facade/collection';
|
|
||||||
import {RecordType, ProtoRecord} from './proto_record';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes "duplicate" records. It assumes that record evaluation does not have side-effects.
|
|
||||||
*
|
|
||||||
* Records that are not last in bindings are removed and all the indices of the records that depend
|
|
||||||
* on them are updated.
|
|
||||||
*
|
|
||||||
* Records that are last in bindings CANNOT be removed, and instead are replaced with very cheap
|
|
||||||
* SELF records.
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export function coalesce(srcRecords: ProtoRecord[]): ProtoRecord[] {
|
|
||||||
let dstRecords = [];
|
|
||||||
let excludedIdxs = [];
|
|
||||||
let indexMap: Map<number, number> = new Map<number, number>();
|
|
||||||
let skipDepth = 0;
|
|
||||||
let skipSources: ProtoRecord[] = ListWrapper.createFixedSize(srcRecords.length);
|
|
||||||
|
|
||||||
for (let protoIndex = 0; protoIndex < srcRecords.length; protoIndex++) {
|
|
||||||
let skipRecord = skipSources[protoIndex];
|
|
||||||
if (isPresent(skipRecord)) {
|
|
||||||
skipDepth--;
|
|
||||||
skipRecord.fixedArgs[0] = dstRecords.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
let src = srcRecords[protoIndex];
|
|
||||||
let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap);
|
|
||||||
|
|
||||||
if (dst.isSkipRecord()) {
|
|
||||||
dstRecords.push(dst);
|
|
||||||
skipDepth++;
|
|
||||||
skipSources[dst.fixedArgs[0]] = dst;
|
|
||||||
} else {
|
|
||||||
let record = _mayBeAddRecord(dst, dstRecords, excludedIdxs, skipDepth > 0);
|
|
||||||
indexMap.set(src.selfIndex, record.selfIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _optimizeSkips(dstRecords);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* - Conditional skip of 1 record followed by an unconditional skip of N are replaced by a
|
|
||||||
* conditional skip of N with the negated condition,
|
|
||||||
* - Skips of 0 records are removed
|
|
||||||
*/
|
|
||||||
function _optimizeSkips(srcRecords: ProtoRecord[]): ProtoRecord[] {
|
|
||||||
let dstRecords = [];
|
|
||||||
let skipSources = ListWrapper.createFixedSize(srcRecords.length);
|
|
||||||
let indexMap: Map<number, number> = new Map<number, number>();
|
|
||||||
|
|
||||||
for (let protoIndex = 0; protoIndex < srcRecords.length; protoIndex++) {
|
|
||||||
let skipRecord = skipSources[protoIndex];
|
|
||||||
if (isPresent(skipRecord)) {
|
|
||||||
skipRecord.fixedArgs[0] = dstRecords.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
let src = srcRecords[protoIndex];
|
|
||||||
|
|
||||||
if (src.isSkipRecord()) {
|
|
||||||
if (src.isConditionalSkipRecord() && src.fixedArgs[0] === protoIndex + 2 &&
|
|
||||||
protoIndex < srcRecords.length - 1 &&
|
|
||||||
srcRecords[protoIndex + 1].mode === RecordType.SkipRecords) {
|
|
||||||
src.mode = src.mode === RecordType.SkipRecordsIf ? RecordType.SkipRecordsIfNot :
|
|
||||||
RecordType.SkipRecordsIf;
|
|
||||||
src.fixedArgs[0] = srcRecords[protoIndex + 1].fixedArgs[0];
|
|
||||||
protoIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src.fixedArgs[0] > protoIndex + 1) {
|
|
||||||
let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap);
|
|
||||||
dstRecords.push(dst);
|
|
||||||
skipSources[dst.fixedArgs[0]] = dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap);
|
|
||||||
dstRecords.push(dst);
|
|
||||||
indexMap.set(src.selfIndex, dst.selfIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dstRecords;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new record or re-use one of the existing records.
|
|
||||||
*/
|
|
||||||
function _mayBeAddRecord(record: ProtoRecord, dstRecords: ProtoRecord[], excludedIdxs: number[],
|
|
||||||
excluded: boolean): ProtoRecord {
|
|
||||||
let match = _findFirstMatch(record, dstRecords, excludedIdxs);
|
|
||||||
|
|
||||||
if (isPresent(match)) {
|
|
||||||
if (record.lastInBinding) {
|
|
||||||
dstRecords.push(_createSelfRecord(record, match.selfIndex, dstRecords.length + 1));
|
|
||||||
match.referencedBySelf = true;
|
|
||||||
} else {
|
|
||||||
if (record.argumentToPureFunction) {
|
|
||||||
match.argumentToPureFunction = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excluded) {
|
|
||||||
excludedIdxs.push(record.selfIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
dstRecords.push(record);
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first `ProtoRecord` that matches the record.
|
|
||||||
*/
|
|
||||||
function _findFirstMatch(record: ProtoRecord, dstRecords: ProtoRecord[],
|
|
||||||
excludedIdxs: number[]): ProtoRecord {
|
|
||||||
return dstRecords.find(
|
|
||||||
// TODO(vicb): optimize excludedIdxs.indexOf (sorted array)
|
|
||||||
rr => excludedIdxs.indexOf(rr.selfIndex) == -1 && rr.mode !== RecordType.DirectiveLifecycle &&
|
|
||||||
_haveSameDirIndex(rr, record) && rr.mode === record.mode &&
|
|
||||||
looseIdentical(rr.funcOrValue, record.funcOrValue) &&
|
|
||||||
rr.contextIndex === record.contextIndex && looseIdentical(rr.name, record.name) &&
|
|
||||||
ListWrapper.equals(rr.args, record.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clone the `ProtoRecord` and changes the indexes for the ones in the destination array for:
|
|
||||||
* - the arguments,
|
|
||||||
* - the context,
|
|
||||||
* - self
|
|
||||||
*/
|
|
||||||
function _cloneAndUpdateIndexes(record: ProtoRecord, dstRecords: ProtoRecord[],
|
|
||||||
indexMap: Map<number, number>): ProtoRecord {
|
|
||||||
let args = record.args.map(src => _srcToDstSelfIndex(indexMap, src));
|
|
||||||
let contextIndex = _srcToDstSelfIndex(indexMap, record.contextIndex);
|
|
||||||
let selfIndex = dstRecords.length + 1;
|
|
||||||
|
|
||||||
return new ProtoRecord(record.mode, record.name, record.funcOrValue, args, record.fixedArgs,
|
|
||||||
contextIndex, record.directiveIndex, selfIndex, record.bindingRecord,
|
|
||||||
record.lastInBinding, record.lastInDirective,
|
|
||||||
record.argumentToPureFunction, record.referencedBySelf,
|
|
||||||
record.propertyBindingIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index in the destination array corresponding to the index in the src array.
|
|
||||||
* When the element is not present in the destination array, return the source index.
|
|
||||||
*/
|
|
||||||
function _srcToDstSelfIndex(indexMap: Map<number, number>, srcIdx: number): number {
|
|
||||||
var dstIdx = indexMap.get(srcIdx);
|
|
||||||
return isPresent(dstIdx) ? dstIdx : srcIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _createSelfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord {
|
|
||||||
return new ProtoRecord(RecordType.Self, "self", null, [], r.fixedArgs, contextIndex,
|
|
||||||
r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding,
|
|
||||||
r.lastInDirective, false, false, r.propertyBindingIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _haveSameDirIndex(a: ProtoRecord, b: ProtoRecord): boolean {
|
|
||||||
var di1 = isBlank(a.directiveIndex) ? null : a.directiveIndex.directiveIndex;
|
|
||||||
var ei1 = isBlank(a.directiveIndex) ? null : a.directiveIndex.elementIndex;
|
|
||||||
|
|
||||||
var di2 = isBlank(b.directiveIndex) ? null : b.directiveIndex.directiveIndex;
|
|
||||||
var ei2 = isBlank(b.directiveIndex) ? null : b.directiveIndex.elementIndex;
|
|
||||||
|
|
||||||
return di1 === di2 && ei1 === ei2;
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
library angular2.src.change_detection.codegen_facade;
|
|
||||||
|
|
||||||
import 'dart:convert' show JSON;
|
|
||||||
|
|
||||||
/// Converts `funcOrValue` to a string which can be used in generated code.
|
|
||||||
String codify(funcOrValue) => JSON.encode(funcOrValue).replaceAll(r'$', r'\$');
|
|
||||||
|
|
||||||
/// Combine the strings of generated code into a single interpolated string.
|
|
||||||
/// Each element of `vals` is expected to be a string literal or a codegen'd
|
|
||||||
/// call to a method returning a string.
|
|
||||||
/// The return format interpolates each value as an expression which reads
|
|
||||||
/// poorly, but the resulting code is easily flattened by dart2js.
|
|
||||||
String combineGeneratedStrings(List<String> vals) {
|
|
||||||
return '"${vals.map((v) => '\${$v}').join('')}"';
|
|
||||||
}
|
|
||||||
|
|
||||||
String rawString(String str) {
|
|
||||||
return "r'$str'";
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts `funcOrValue` to a string which can be used in generated code.
|
|
||||||
*/
|
|
||||||
export function codify(obj: any): string {
|
|
||||||
return JSON.stringify(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rawString(str: string): string {
|
|
||||||
return `'${str}'`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combine the strings of generated code into a single interpolated string.
|
|
||||||
* Each element of `vals` is expected to be a string literal or a codegen'd
|
|
||||||
* call to a method returning a string.
|
|
||||||
*/
|
|
||||||
export function combineGeneratedStrings(vals: string[]): string {
|
|
||||||
return vals.join(' + ');
|
|
||||||
}
|
|
|
@ -1,240 +0,0 @@
|
||||||
import {IS_DART, Json, StringWrapper, isPresent, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
import {CodegenNameUtil} from './codegen_name_util';
|
|
||||||
import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
|
|
||||||
import {ProtoRecord, RecordType} from './proto_record';
|
|
||||||
import {BindingTarget} from './binding_record';
|
|
||||||
import {DirectiveRecord} from './directive_record';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible for providing change detection logic for change detector classes.
|
|
||||||
*/
|
|
||||||
export class CodegenLogicUtil {
|
|
||||||
constructor(private _names: CodegenNameUtil, private _utilName: string,
|
|
||||||
private _changeDetectorStateName: string) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a statement which updates the local variable representing `protoRec` with the current
|
|
||||||
* value of the record. Used by property bindings.
|
|
||||||
*/
|
|
||||||
genPropertyBindingEvalValue(protoRec: ProtoRecord): string {
|
|
||||||
return this._genEvalValue(protoRec, idx => this._names.getLocalName(idx),
|
|
||||||
this._names.getLocalsAccessorName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a statement which updates the local variable representing `protoRec` with the current
|
|
||||||
* value of the record. Used by event bindings.
|
|
||||||
*/
|
|
||||||
genEventBindingEvalValue(eventRecord: any, protoRec: ProtoRecord): string {
|
|
||||||
return this._genEvalValue(protoRec, idx => this._names.getEventLocalName(eventRecord, idx),
|
|
||||||
"locals");
|
|
||||||
}
|
|
||||||
|
|
||||||
private _genEvalValue(protoRec: ProtoRecord, getLocalName: Function,
|
|
||||||
localsAccessor: string): string {
|
|
||||||
var context = (protoRec.contextIndex == -1) ?
|
|
||||||
this._names.getDirectiveName(protoRec.directiveIndex) :
|
|
||||||
getLocalName(protoRec.contextIndex);
|
|
||||||
var argString = protoRec.args.map(arg => getLocalName(arg)).join(", ");
|
|
||||||
|
|
||||||
var rhs: string;
|
|
||||||
switch (protoRec.mode) {
|
|
||||||
case RecordType.Self:
|
|
||||||
rhs = context;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.Const:
|
|
||||||
rhs = codify(protoRec.funcOrValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.PropertyRead:
|
|
||||||
rhs = `${context}.${protoRec.name}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.SafeProperty:
|
|
||||||
var read = `${context}.${protoRec.name}`;
|
|
||||||
rhs = `${this._utilName}.isValueBlank(${context}) ? null : ${read}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.PropertyWrite:
|
|
||||||
rhs = `${context}.${protoRec.name} = ${getLocalName(protoRec.args[0])}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.Local:
|
|
||||||
rhs = `${localsAccessor}.get(${rawString(protoRec.name)})`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.InvokeMethod:
|
|
||||||
rhs = `${context}.${protoRec.name}(${argString})`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.SafeMethodInvoke:
|
|
||||||
var invoke = `${context}.${protoRec.name}(${argString})`;
|
|
||||||
rhs = `${this._utilName}.isValueBlank(${context}) ? null : ${invoke}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.InvokeClosure:
|
|
||||||
rhs = `${context}(${argString})`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.PrimitiveOp:
|
|
||||||
rhs = `${this._utilName}.${protoRec.name}(${argString})`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.CollectionLiteral:
|
|
||||||
rhs = `${this._utilName}.${protoRec.name}(${argString})`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.Interpolate:
|
|
||||||
rhs = this._genInterpolation(protoRec);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.KeyedRead:
|
|
||||||
rhs = `${context}[${getLocalName(protoRec.args[0])}]`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.KeyedWrite:
|
|
||||||
rhs = `${context}[${getLocalName(protoRec.args[0])}] = ${getLocalName(protoRec.args[1])}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RecordType.Chain:
|
|
||||||
rhs = `${getLocalName(protoRec.args[protoRec.args.length - 1])}`;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new BaseException(`Unknown operation ${protoRec.mode}`);
|
|
||||||
}
|
|
||||||
return `${getLocalName(protoRec.selfIndex)} = ${rhs};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
genPropertyBindingTargets(propertyBindingTargets: BindingTarget[],
|
|
||||||
genDebugInfo: boolean): string {
|
|
||||||
var bs = propertyBindingTargets.map(b => {
|
|
||||||
if (isBlank(b)) return "null";
|
|
||||||
|
|
||||||
var debug = genDebugInfo ? codify(b.debug) : "null";
|
|
||||||
return `${this._utilName}.bindingTarget(${codify(b.mode)}, ${b.elementIndex}, ${codify(b.name)}, ${codify(b.unit)}, ${debug})`;
|
|
||||||
});
|
|
||||||
return `[${bs.join(", ")}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
genDirectiveIndices(directiveRecords: DirectiveRecord[]): string {
|
|
||||||
var bs = directiveRecords.map(
|
|
||||||
b =>
|
|
||||||
`${this._utilName}.directiveIndex(${b.directiveIndex.elementIndex}, ${b.directiveIndex.directiveIndex})`);
|
|
||||||
return `[${bs.join(", ")}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_genInterpolation(protoRec: ProtoRecord): string {
|
|
||||||
var iVals = [];
|
|
||||||
for (var i = 0; i < protoRec.args.length; ++i) {
|
|
||||||
iVals.push(codify(protoRec.fixedArgs[i]));
|
|
||||||
iVals.push(`${this._utilName}.s(${this._names.getLocalName(protoRec.args[i])})`);
|
|
||||||
}
|
|
||||||
iVals.push(codify(protoRec.fixedArgs[protoRec.args.length]));
|
|
||||||
return combineGeneratedStrings(iVals);
|
|
||||||
}
|
|
||||||
|
|
||||||
genHydrateDirectives(directiveRecords: DirectiveRecord[]): string {
|
|
||||||
var res = [];
|
|
||||||
var outputCount = 0;
|
|
||||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
|
||||||
var r = directiveRecords[i];
|
|
||||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
|
||||||
res.push(`${dirVarName} = ${this._genReadDirective(i)};`);
|
|
||||||
if (isPresent(r.outputs)) {
|
|
||||||
r.outputs.forEach(output => {
|
|
||||||
var eventHandlerExpr = this._genEventHandler(r.directiveIndex.elementIndex, output[1]);
|
|
||||||
var statementStart =
|
|
||||||
`this.outputSubscriptions[${outputCount++}] = ${dirVarName}.${output[0]}`;
|
|
||||||
if (IS_DART) {
|
|
||||||
res.push(`${statementStart}.listen(${eventHandlerExpr});`);
|
|
||||||
} else {
|
|
||||||
res.push(`${statementStart}.subscribe({next: ${eventHandlerExpr}});`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outputCount > 0) {
|
|
||||||
var statementStart = 'this.outputSubscriptions';
|
|
||||||
if (IS_DART) {
|
|
||||||
res.unshift(`${statementStart} = new List(${outputCount});`);
|
|
||||||
} else {
|
|
||||||
res.unshift(`${statementStart} = new Array(${outputCount});`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
genDirectivesOnDestroy(directiveRecords: DirectiveRecord[]): string {
|
|
||||||
var res = [];
|
|
||||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
|
||||||
var r = directiveRecords[i];
|
|
||||||
if (r.callOnDestroy) {
|
|
||||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
|
||||||
res.push(`${dirVarName}.ngOnDestroy();`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
private _genEventHandler(boundElementIndex: number, eventName: string): string {
|
|
||||||
if (IS_DART) {
|
|
||||||
return `(event) => this.handleEvent('${eventName}', ${boundElementIndex}, event)`;
|
|
||||||
} else {
|
|
||||||
return `(function(event) { return this.handleEvent('${eventName}', ${boundElementIndex}, event); }).bind(this)`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _genReadDirective(index: number) { return `this.getDirectiveFor(directives, ${index})`; }
|
|
||||||
|
|
||||||
genHydrateDetectors(directiveRecords: DirectiveRecord[]): string {
|
|
||||||
var res = [];
|
|
||||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
|
||||||
var r = directiveRecords[i];
|
|
||||||
if (!r.isDefaultChangeDetection()) {
|
|
||||||
res.push(
|
|
||||||
`${this._names.getDetectorName(r.directiveIndex)} = this.getDetectorFor(directives, ${i});`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.join("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
genContentLifecycleCallbacks(directiveRecords: DirectiveRecord[]): string[] {
|
|
||||||
var res = [];
|
|
||||||
var eq = IS_DART ? '==' : '===';
|
|
||||||
// NOTE(kegluneq): Order is important!
|
|
||||||
for (var i = directiveRecords.length - 1; i >= 0; --i) {
|
|
||||||
var dir = directiveRecords[i];
|
|
||||||
if (dir.callAfterContentInit) {
|
|
||||||
res.push(
|
|
||||||
`if(${this._names.getStateName()} ${eq} ${this._changeDetectorStateName}.NeverChecked) ${this._names.getDirectiveName(dir.directiveIndex)}.ngAfterContentInit();`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir.callAfterContentChecked) {
|
|
||||||
res.push(`${this._names.getDirectiveName(dir.directiveIndex)}.ngAfterContentChecked();`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
genViewLifecycleCallbacks(directiveRecords: DirectiveRecord[]): string[] {
|
|
||||||
var res = [];
|
|
||||||
var eq = IS_DART ? '==' : '===';
|
|
||||||
// NOTE(kegluneq): Order is important!
|
|
||||||
for (var i = directiveRecords.length - 1; i >= 0; --i) {
|
|
||||||
var dir = directiveRecords[i];
|
|
||||||
if (dir.callAfterViewInit) {
|
|
||||||
res.push(
|
|
||||||
`if(${this._names.getStateName()} ${eq} ${this._changeDetectorStateName}.NeverChecked) ${this._names.getDirectiveName(dir.directiveIndex)}.ngAfterViewInit();`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir.callAfterViewChecked) {
|
|
||||||
res.push(`${this._names.getDirectiveName(dir.directiveIndex)}.ngAfterViewChecked();`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
import {RegExpWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
|
||||||
import {ListWrapper, MapWrapper, Map} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
import {DirectiveIndex} from './directive_record';
|
|
||||||
|
|
||||||
import {ProtoRecord} from './proto_record';
|
|
||||||
import {EventBinding} from './event_binding';
|
|
||||||
|
|
||||||
// The names of these fields must be kept in sync with abstract_change_detector.ts or change
|
|
||||||
// detection will fail.
|
|
||||||
const _STATE_ACCESSOR = "state";
|
|
||||||
const _CONTEXT_ACCESSOR = "context";
|
|
||||||
const _PROP_BINDING_INDEX = "propertyBindingIndex";
|
|
||||||
const _DIRECTIVES_ACCESSOR = "directiveIndices";
|
|
||||||
const _DISPATCHER_ACCESSOR = "dispatcher";
|
|
||||||
const _LOCALS_ACCESSOR = "locals";
|
|
||||||
const _MODE_ACCESSOR = "mode";
|
|
||||||
const _PIPES_ACCESSOR = "pipes";
|
|
||||||
const _PROTOS_ACCESSOR = "protos";
|
|
||||||
export const CONTEXT_ACCESSOR = "context";
|
|
||||||
|
|
||||||
// `context` is always first.
|
|
||||||
export const CONTEXT_INDEX = 0;
|
|
||||||
const _FIELD_PREFIX = 'this.';
|
|
||||||
|
|
||||||
var _whiteSpaceRegExp = /\W/g;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns `s` with all non-identifier characters removed.
|
|
||||||
*/
|
|
||||||
export function sanitizeName(s: string): string {
|
|
||||||
return StringWrapper.replaceAll(s, _whiteSpaceRegExp, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible for providing field and local variable names for change detector classes.
|
|
||||||
* Also provides some convenience functions, for example, declaring variables, destroying pipes,
|
|
||||||
* and dehydrating the detector.
|
|
||||||
*/
|
|
||||||
export class CodegenNameUtil {
|
|
||||||
/**
|
|
||||||
* Record names sanitized for use as fields.
|
|
||||||
* See [sanitizeName] for details.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
_sanitizedNames: string[];
|
|
||||||
/** @internal */
|
|
||||||
_sanitizedEventNames = new Map<EventBinding, string[]>();
|
|
||||||
|
|
||||||
constructor(private _records: ProtoRecord[], private _eventBindings: EventBinding[],
|
|
||||||
private _directiveRecords: any[], private _utilName: string) {
|
|
||||||
this._sanitizedNames = ListWrapper.createFixedSize(this._records.length + 1);
|
|
||||||
this._sanitizedNames[CONTEXT_INDEX] = CONTEXT_ACCESSOR;
|
|
||||||
for (var i = 0, iLen = this._records.length; i < iLen; ++i) {
|
|
||||||
this._sanitizedNames[i + 1] = sanitizeName(`${this._records[i].name}${i}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var ebIndex = 0; ebIndex < _eventBindings.length; ++ebIndex) {
|
|
||||||
var eb = _eventBindings[ebIndex];
|
|
||||||
var names = [CONTEXT_ACCESSOR];
|
|
||||||
for (var i = 0, iLen = eb.records.length; i < iLen; ++i) {
|
|
||||||
names.push(sanitizeName(`${eb.records[i].name}${i}_${ebIndex}`));
|
|
||||||
}
|
|
||||||
this._sanitizedEventNames.set(eb, names);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_addFieldPrefix(name: string): string { return `${_FIELD_PREFIX}${name}`; }
|
|
||||||
|
|
||||||
getDispatcherName(): string { return this._addFieldPrefix(_DISPATCHER_ACCESSOR); }
|
|
||||||
|
|
||||||
getPipesAccessorName(): string { return this._addFieldPrefix(_PIPES_ACCESSOR); }
|
|
||||||
|
|
||||||
getProtosName(): string { return this._addFieldPrefix(_PROTOS_ACCESSOR); }
|
|
||||||
|
|
||||||
getDirectivesAccessorName(): string { return this._addFieldPrefix(_DIRECTIVES_ACCESSOR); }
|
|
||||||
|
|
||||||
getLocalsAccessorName(): string { return this._addFieldPrefix(_LOCALS_ACCESSOR); }
|
|
||||||
|
|
||||||
getStateName(): string { return this._addFieldPrefix(_STATE_ACCESSOR); }
|
|
||||||
|
|
||||||
getModeName(): string { return this._addFieldPrefix(_MODE_ACCESSOR); }
|
|
||||||
|
|
||||||
getPropertyBindingIndex(): string { return this._addFieldPrefix(_PROP_BINDING_INDEX); }
|
|
||||||
|
|
||||||
getLocalName(idx: number): string { return `l_${this._sanitizedNames[idx]}`; }
|
|
||||||
|
|
||||||
getEventLocalName(eb: EventBinding, idx: number): string {
|
|
||||||
return `l_${this._sanitizedEventNames.get(eb)[idx]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getChangeName(idx: number): string { return `c_${this._sanitizedNames[idx]}`; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a statement initializing local variables used when detecting changes.
|
|
||||||
*/
|
|
||||||
genInitLocals(): string {
|
|
||||||
var declarations = [];
|
|
||||||
var assignments = [];
|
|
||||||
for (var i = 0, iLen = this.getFieldCount(); i < iLen; ++i) {
|
|
||||||
if (i == CONTEXT_INDEX) {
|
|
||||||
declarations.push(`${this.getLocalName(i)} = ${this.getFieldName(i)}`);
|
|
||||||
} else {
|
|
||||||
var rec = this._records[i - 1];
|
|
||||||
if (rec.argumentToPureFunction) {
|
|
||||||
var changeName = this.getChangeName(i);
|
|
||||||
declarations.push(`${this.getLocalName(i)},${changeName}`);
|
|
||||||
assignments.push(changeName);
|
|
||||||
} else {
|
|
||||||
declarations.push(`${this.getLocalName(i)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var assignmentsCode =
|
|
||||||
ListWrapper.isEmpty(assignments) ? '' : `${assignments.join('=')} = false;`;
|
|
||||||
return `var ${declarations.join(',')};${assignmentsCode}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a statement initializing local variables for event handlers.
|
|
||||||
*/
|
|
||||||
genInitEventLocals(): string {
|
|
||||||
var res = [`${this.getLocalName(CONTEXT_INDEX)} = ${this.getFieldName(CONTEXT_INDEX)}`];
|
|
||||||
this._sanitizedEventNames.forEach((names, eb) => {
|
|
||||||
for (var i = 0; i < names.length; ++i) {
|
|
||||||
if (i !== CONTEXT_INDEX) {
|
|
||||||
res.push(`${this.getEventLocalName(eb, i)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return res.length > 1 ? `var ${res.join(',')};` : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
getPreventDefaultAccesor(): string { return "preventDefault"; }
|
|
||||||
|
|
||||||
getFieldCount(): number { return this._sanitizedNames.length; }
|
|
||||||
|
|
||||||
getFieldName(idx: number): string { return this._addFieldPrefix(this._sanitizedNames[idx]); }
|
|
||||||
|
|
||||||
getAllFieldNames(): string[] {
|
|
||||||
var fieldList = [];
|
|
||||||
for (var k = 0, kLen = this.getFieldCount(); k < kLen; ++k) {
|
|
||||||
if (k === 0 || this._records[k - 1].shouldBeChecked()) {
|
|
||||||
fieldList.push(this.getFieldName(k));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0, iLen = this._records.length; i < iLen; ++i) {
|
|
||||||
var rec = this._records[i];
|
|
||||||
if (rec.isPipeRecord()) {
|
|
||||||
fieldList.push(this.getPipeName(rec.selfIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0, jLen = this._directiveRecords.length; j < jLen; ++j) {
|
|
||||||
var dRec = this._directiveRecords[j];
|
|
||||||
fieldList.push(this.getDirectiveName(dRec.directiveIndex));
|
|
||||||
if (!dRec.isDefaultChangeDetection()) {
|
|
||||||
fieldList.push(this.getDetectorName(dRec.directiveIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fieldList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates statements which clear all fields so that the change detector is dehydrated.
|
|
||||||
*/
|
|
||||||
genDehydrateFields(): string {
|
|
||||||
var fields = this.getAllFieldNames();
|
|
||||||
ListWrapper.removeAt(fields, CONTEXT_INDEX);
|
|
||||||
if (ListWrapper.isEmpty(fields)) return '';
|
|
||||||
|
|
||||||
// At least one assignment.
|
|
||||||
fields.push(`${this._utilName}.uninitialized;`);
|
|
||||||
return fields.join(' = ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates statements destroying all pipe variables.
|
|
||||||
*/
|
|
||||||
genPipeOnDestroy(): string {
|
|
||||||
return this._records.filter(r => r.isPipeRecord())
|
|
||||||
.map(r => `${this._utilName}.callPipeOnDestroy(${this.getPipeName(r.selfIndex)});`)
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
getPipeName(idx: number): string {
|
|
||||||
return this._addFieldPrefix(`${this._sanitizedNames[idx]}_pipe`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDirectiveName(d: DirectiveIndex): string {
|
|
||||||
return this._addFieldPrefix(`directive_${d.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDetectorName(d: DirectiveIndex): string { return this._addFieldPrefix(`detector_${d.name}`); }
|
|
||||||
}
|
|
|
@ -24,6 +24,7 @@ export enum ChangeDetectorState {
|
||||||
Errored
|
Errored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes within the change detector which strategy will be used the next time change
|
* Describes within the change detector which strategy will be used the next time change
|
||||||
* detection is triggered.
|
* detection is triggered.
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
import {isDefaultChangeDetectionStrategy, ChangeDetectionStrategy} from './constants';
|
|
||||||
|
|
||||||
export class DirectiveIndex {
|
|
||||||
constructor(public elementIndex: number, public directiveIndex: number) {}
|
|
||||||
|
|
||||||
get name() { return `${this.elementIndex}_${this.directiveIndex}`; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DirectiveRecord {
|
|
||||||
directiveIndex: DirectiveIndex;
|
|
||||||
callAfterContentInit: boolean;
|
|
||||||
callAfterContentChecked: boolean;
|
|
||||||
callAfterViewInit: boolean;
|
|
||||||
callAfterViewChecked: boolean;
|
|
||||||
callOnChanges: boolean;
|
|
||||||
callDoCheck: boolean;
|
|
||||||
callOnInit: boolean;
|
|
||||||
callOnDestroy: boolean;
|
|
||||||
changeDetection: ChangeDetectionStrategy;
|
|
||||||
// array of [emitter property name, eventName]
|
|
||||||
outputs: string[][];
|
|
||||||
|
|
||||||
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
|
||||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
|
|
||||||
changeDetection, outputs}: {
|
|
||||||
directiveIndex?: DirectiveIndex,
|
|
||||||
callAfterContentInit?: boolean,
|
|
||||||
callAfterContentChecked?: boolean,
|
|
||||||
callAfterViewInit?: boolean,
|
|
||||||
callAfterViewChecked?: boolean,
|
|
||||||
callOnChanges?: boolean,
|
|
||||||
callDoCheck?: boolean,
|
|
||||||
callOnInit?: boolean,
|
|
||||||
callOnDestroy?: boolean,
|
|
||||||
changeDetection?: ChangeDetectionStrategy,
|
|
||||||
outputs?: string[][]
|
|
||||||
} = {}) {
|
|
||||||
this.directiveIndex = directiveIndex;
|
|
||||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
|
||||||
this.callAfterContentChecked = normalizeBool(callAfterContentChecked);
|
|
||||||
this.callOnChanges = normalizeBool(callOnChanges);
|
|
||||||
this.callAfterViewInit = normalizeBool(callAfterViewInit);
|
|
||||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
|
||||||
this.callDoCheck = normalizeBool(callDoCheck);
|
|
||||||
this.callOnInit = normalizeBool(callOnInit);
|
|
||||||
this.callOnDestroy = normalizeBool(callOnDestroy);
|
|
||||||
this.changeDetection = changeDetection;
|
|
||||||
this.outputs = outputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDefaultChangeDetection(): boolean {
|
|
||||||
return isDefaultChangeDetectionStrategy(this.changeDetection);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,490 +0,0 @@
|
||||||
import {isPresent, isBlank, FunctionWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
|
||||||
import {EventBinding} from './event_binding';
|
|
||||||
import {BindingRecord, BindingTarget} from './binding_record';
|
|
||||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
|
||||||
import {Locals} from './parser/locals';
|
|
||||||
import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
|
|
||||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
|
||||||
import {ProtoRecord, RecordType} from './proto_record';
|
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
|
||||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
|
||||||
|
|
||||||
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|
||||||
values: any[];
|
|
||||||
changes: any[];
|
|
||||||
localPipes: any[];
|
|
||||||
prevContexts: any[];
|
|
||||||
|
|
||||||
constructor(id: string, numberOfPropertyProtoRecords: number,
|
|
||||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
|
||||||
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
|
|
||||||
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
|
|
||||||
private _genConfig: ChangeDetectorGenConfig) {
|
|
||||||
super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
|
|
||||||
var len = _records.length + 1;
|
|
||||||
this.values = ListWrapper.createFixedSize(len);
|
|
||||||
this.localPipes = ListWrapper.createFixedSize(len);
|
|
||||||
this.prevContexts = ListWrapper.createFixedSize(len);
|
|
||||||
this.changes = ListWrapper.createFixedSize(len);
|
|
||||||
|
|
||||||
this.dehydrateDirectives(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean {
|
|
||||||
var preventDefault = false;
|
|
||||||
|
|
||||||
this._matchingEventBindings(eventName, elIndex)
|
|
||||||
.forEach(rec => {
|
|
||||||
var res = this._processEventBinding(rec, locals);
|
|
||||||
if (res === false) {
|
|
||||||
preventDefault = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return preventDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_processEventBinding(eb: EventBinding, locals: Locals): any {
|
|
||||||
var values = ListWrapper.createFixedSize(eb.records.length);
|
|
||||||
values[0] = this.values[0];
|
|
||||||
|
|
||||||
for (var protoIdx = 0; protoIdx < eb.records.length; ++protoIdx) {
|
|
||||||
var proto = eb.records[protoIdx];
|
|
||||||
|
|
||||||
if (proto.isSkipRecord()) {
|
|
||||||
protoIdx += this._computeSkipLength(protoIdx, proto, values);
|
|
||||||
} else {
|
|
||||||
if (proto.lastInBinding) {
|
|
||||||
this._markPathAsCheckOnce(proto);
|
|
||||||
}
|
|
||||||
var res = this._calculateCurrValue(proto, values, locals);
|
|
||||||
if (proto.lastInBinding) {
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
this._writeSelf(proto, res, values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new BaseException("Cannot be reached");
|
|
||||||
}
|
|
||||||
|
|
||||||
private _computeSkipLength(protoIndex: number, proto: ProtoRecord, values: any[]): number {
|
|
||||||
if (proto.mode === RecordType.SkipRecords) {
|
|
||||||
return proto.fixedArgs[0] - protoIndex - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.mode === RecordType.SkipRecordsIf) {
|
|
||||||
let condition = this._readContext(proto, values);
|
|
||||||
return condition ? proto.fixedArgs[0] - protoIndex - 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.mode === RecordType.SkipRecordsIfNot) {
|
|
||||||
let condition = this._readContext(proto, values);
|
|
||||||
return condition ? 0 : proto.fixedArgs[0] - protoIndex - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new BaseException("Cannot be reached");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_markPathAsCheckOnce(proto: ProtoRecord): void {
|
|
||||||
if (!proto.bindingRecord.isDefaultChangeDetection()) {
|
|
||||||
var dir = proto.bindingRecord.directiveRecord;
|
|
||||||
this._getDetectorFor(dir.directiveIndex).markPathToRootAsCheckOnce();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_matchingEventBindings(eventName: string, elIndex: number): EventBinding[] {
|
|
||||||
return this._eventBindings.filter(eb => eb.eventName == eventName && eb.elIndex === elIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {
|
|
||||||
this.values[0] = this.context;
|
|
||||||
this.dispatcher = dispatcher;
|
|
||||||
|
|
||||||
this.outputSubscriptions = [];
|
|
||||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
|
||||||
var r = this._directiveRecords[i];
|
|
||||||
if (isPresent(r.outputs)) {
|
|
||||||
r.outputs.forEach(output => {
|
|
||||||
var eventHandler =
|
|
||||||
<any>this._createEventHandler(r.directiveIndex.elementIndex, output[1]);
|
|
||||||
var directive = this._getDirectiveFor(r.directiveIndex);
|
|
||||||
var getter = reflector.getter(output[0]);
|
|
||||||
this.outputSubscriptions.push(
|
|
||||||
ObservableWrapper.subscribe(getter(directive), eventHandler));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createEventHandler(boundElementIndex: number, eventName: string): Function {
|
|
||||||
return (event) => this.handleEvent(eventName, boundElementIndex, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dehydrateDirectives(destroyPipes: boolean) {
|
|
||||||
if (destroyPipes) {
|
|
||||||
this._destroyPipes();
|
|
||||||
this._destroyDirectives();
|
|
||||||
}
|
|
||||||
this.values[0] = null;
|
|
||||||
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
|
|
||||||
ListWrapper.fill(this.changes, false);
|
|
||||||
ListWrapper.fill(this.localPipes, null);
|
|
||||||
ListWrapper.fill(this.prevContexts, ChangeDetectionUtil.uninitialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_destroyPipes() {
|
|
||||||
for (var i = 0; i < this.localPipes.length; ++i) {
|
|
||||||
if (isPresent(this.localPipes[i])) {
|
|
||||||
ChangeDetectionUtil.callPipeOnDestroy(this.localPipes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_destroyDirectives() {
|
|
||||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
|
||||||
var record = this._directiveRecords[i];
|
|
||||||
if (record.callOnDestroy) {
|
|
||||||
this._getDirectiveFor(record.directiveIndex).ngOnDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNoChanges(): void { this.runDetectChanges(true); }
|
|
||||||
|
|
||||||
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
|
||||||
var protos = this._records;
|
|
||||||
|
|
||||||
var changes = null;
|
|
||||||
var isChanged = false;
|
|
||||||
for (var protoIdx = 0; protoIdx < protos.length; ++protoIdx) {
|
|
||||||
var proto: ProtoRecord = protos[protoIdx];
|
|
||||||
var bindingRecord = proto.bindingRecord;
|
|
||||||
var directiveRecord = bindingRecord.directiveRecord;
|
|
||||||
|
|
||||||
if (this._firstInBinding(proto)) {
|
|
||||||
this.propertyBindingIndex = proto.propertyBindingIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.isLifeCycleRecord()) {
|
|
||||||
if (proto.name === "DoCheck" && !throwOnChange) {
|
|
||||||
this._getDirectiveFor(directiveRecord.directiveIndex).ngDoCheck();
|
|
||||||
} else if (proto.name === "OnInit" && !throwOnChange &&
|
|
||||||
this.state == ChangeDetectorState.NeverChecked) {
|
|
||||||
this._getDirectiveFor(directiveRecord.directiveIndex).ngOnInit();
|
|
||||||
} else if (proto.name === "OnChanges" && isPresent(changes) && !throwOnChange) {
|
|
||||||
this._getDirectiveFor(directiveRecord.directiveIndex).ngOnChanges(changes);
|
|
||||||
}
|
|
||||||
} else if (proto.isSkipRecord()) {
|
|
||||||
protoIdx += this._computeSkipLength(protoIdx, proto, this.values);
|
|
||||||
} else {
|
|
||||||
var change = this._check(proto, throwOnChange, this.values, this.locals);
|
|
||||||
if (isPresent(change)) {
|
|
||||||
this._updateDirectiveOrElement(change, bindingRecord);
|
|
||||||
isChanged = true;
|
|
||||||
changes = this._addChange(bindingRecord, change, changes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proto.lastInDirective) {
|
|
||||||
changes = null;
|
|
||||||
if (isChanged && !bindingRecord.isDefaultChangeDetection()) {
|
|
||||||
this._getDetectorFor(directiveRecord.directiveIndex).markAsCheckOnce();
|
|
||||||
}
|
|
||||||
|
|
||||||
isChanged = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_firstInBinding(r: ProtoRecord): boolean {
|
|
||||||
var prev = ChangeDetectionUtil.protoByIndex(this._records, r.selfIndex - 1);
|
|
||||||
return isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterContentLifecycleCallbacksInternal() {
|
|
||||||
var dirs = this._directiveRecords;
|
|
||||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
|
||||||
var dir = dirs[i];
|
|
||||||
if (dir.callAfterContentInit && this.state == ChangeDetectorState.NeverChecked) {
|
|
||||||
this._getDirectiveFor(dir.directiveIndex).ngAfterContentInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dir.callAfterContentChecked) {
|
|
||||||
this._getDirectiveFor(dir.directiveIndex).ngAfterContentChecked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
afterViewLifecycleCallbacksInternal() {
|
|
||||||
var dirs = this._directiveRecords;
|
|
||||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
|
||||||
var dir = dirs[i];
|
|
||||||
if (dir.callAfterViewInit && this.state == ChangeDetectorState.NeverChecked) {
|
|
||||||
this._getDirectiveFor(dir.directiveIndex).ngAfterViewInit();
|
|
||||||
}
|
|
||||||
if (dir.callAfterViewChecked) {
|
|
||||||
this._getDirectiveFor(dir.directiveIndex).ngAfterViewChecked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _updateDirectiveOrElement(change, bindingRecord) {
|
|
||||||
if (isBlank(bindingRecord.directiveRecord)) {
|
|
||||||
super.notifyDispatcher(change.currentValue);
|
|
||||||
} else {
|
|
||||||
var directiveIndex = bindingRecord.directiveRecord.directiveIndex;
|
|
||||||
bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._genConfig.logBindingUpdate) {
|
|
||||||
super.logBindingUpdate(change.currentValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _addChange(bindingRecord: BindingRecord, change, changes) {
|
|
||||||
if (bindingRecord.callOnChanges()) {
|
|
||||||
return super.addChange(changes, change.previousValue, change.currentValue);
|
|
||||||
} else {
|
|
||||||
return changes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _getDirectiveFor(directiveIndex: DirectiveIndex) {
|
|
||||||
return this.dispatcher.getDirectiveFor(directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _getDetectorFor(directiveIndex: DirectiveIndex) {
|
|
||||||
return this.dispatcher.getDetectorFor(directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
|
||||||
locals: Locals): SimpleChange {
|
|
||||||
if (proto.isPipeRecord()) {
|
|
||||||
return this._pipeCheck(proto, throwOnChange, values);
|
|
||||||
} else {
|
|
||||||
return this._referenceCheck(proto, throwOnChange, values, locals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
private _referenceCheck(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
|
||||||
locals: Locals) {
|
|
||||||
if (this._pureFuncAndArgsDidNotChange(proto)) {
|
|
||||||
this._setChanged(proto, false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currValue = this._calculateCurrValue(proto, values, locals);
|
|
||||||
|
|
||||||
if (proto.shouldBeChecked()) {
|
|
||||||
var prevValue = this._readSelf(proto, values);
|
|
||||||
var detectedChange = throwOnChange ?
|
|
||||||
!ChangeDetectionUtil.devModeEqual(prevValue, currValue) :
|
|
||||||
ChangeDetectionUtil.looseNotIdentical(prevValue, currValue);
|
|
||||||
if (detectedChange) {
|
|
||||||
if (proto.lastInBinding) {
|
|
||||||
var change = ChangeDetectionUtil.simpleChange(prevValue, currValue);
|
|
||||||
if (throwOnChange) this.throwOnChangeError(prevValue, currValue);
|
|
||||||
|
|
||||||
this._writeSelf(proto, currValue, values);
|
|
||||||
this._setChanged(proto, true);
|
|
||||||
return change;
|
|
||||||
} else {
|
|
||||||
this._writeSelf(proto, currValue, values);
|
|
||||||
this._setChanged(proto, true);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._setChanged(proto, false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this._writeSelf(proto, currValue, values);
|
|
||||||
this._setChanged(proto, true);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _calculateCurrValue(proto: ProtoRecord, values: any[], locals: Locals) {
|
|
||||||
switch (proto.mode) {
|
|
||||||
case RecordType.Self:
|
|
||||||
return this._readContext(proto, values);
|
|
||||||
|
|
||||||
case RecordType.Const:
|
|
||||||
return proto.funcOrValue;
|
|
||||||
|
|
||||||
case RecordType.PropertyRead:
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
return proto.funcOrValue(context);
|
|
||||||
|
|
||||||
case RecordType.SafeProperty:
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
return isBlank(context) ? null : proto.funcOrValue(context);
|
|
||||||
|
|
||||||
case RecordType.PropertyWrite:
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
var value = this._readArgs(proto, values)[0];
|
|
||||||
proto.funcOrValue(context, value);
|
|
||||||
return value;
|
|
||||||
|
|
||||||
case RecordType.KeyedWrite:
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
var key = this._readArgs(proto, values)[0];
|
|
||||||
var value = this._readArgs(proto, values)[1];
|
|
||||||
context[key] = value;
|
|
||||||
return value;
|
|
||||||
|
|
||||||
case RecordType.Local:
|
|
||||||
return locals.get(proto.name);
|
|
||||||
|
|
||||||
case RecordType.InvokeMethod:
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
var args = this._readArgs(proto, values);
|
|
||||||
return proto.funcOrValue(context, args);
|
|
||||||
|
|
||||||
case RecordType.SafeMethodInvoke:
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
if (isBlank(context)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var args = this._readArgs(proto, values);
|
|
||||||
return proto.funcOrValue(context, args);
|
|
||||||
|
|
||||||
case RecordType.KeyedRead:
|
|
||||||
var arg = this._readArgs(proto, values)[0];
|
|
||||||
return this._readContext(proto, values)[arg];
|
|
||||||
|
|
||||||
case RecordType.Chain:
|
|
||||||
var args = this._readArgs(proto, values);
|
|
||||||
return args[args.length - 1];
|
|
||||||
|
|
||||||
case RecordType.InvokeClosure:
|
|
||||||
return FunctionWrapper.apply(this._readContext(proto, values),
|
|
||||||
this._readArgs(proto, values));
|
|
||||||
|
|
||||||
case RecordType.Interpolate:
|
|
||||||
case RecordType.PrimitiveOp:
|
|
||||||
case RecordType.CollectionLiteral:
|
|
||||||
return FunctionWrapper.apply(proto.funcOrValue, this._readArgs(proto, values));
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new BaseException(`Unknown operation ${proto.mode}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _pipeCheck(proto: ProtoRecord, throwOnChange: boolean, values: any[]) {
|
|
||||||
var context = this._readContext(proto, values);
|
|
||||||
var selectedPipe = this._pipeFor(proto, context);
|
|
||||||
if (!selectedPipe.pure || this._argsOrContextChanged(proto)) {
|
|
||||||
var args = this._readArgs(proto, values);
|
|
||||||
var currValue = selectedPipe.pipe.transform(context, args);
|
|
||||||
|
|
||||||
if (proto.shouldBeChecked()) {
|
|
||||||
var prevValue = this._readSelf(proto, values);
|
|
||||||
var detectedChange = throwOnChange ?
|
|
||||||
!ChangeDetectionUtil.devModeEqual(prevValue, currValue) :
|
|
||||||
ChangeDetectionUtil.looseNotIdentical(prevValue, currValue);
|
|
||||||
if (detectedChange) {
|
|
||||||
currValue = ChangeDetectionUtil.unwrapValue(currValue);
|
|
||||||
|
|
||||||
if (proto.lastInBinding) {
|
|
||||||
var change = ChangeDetectionUtil.simpleChange(prevValue, currValue);
|
|
||||||
if (throwOnChange) this.throwOnChangeError(prevValue, currValue);
|
|
||||||
|
|
||||||
this._writeSelf(proto, currValue, values);
|
|
||||||
this._setChanged(proto, true);
|
|
||||||
|
|
||||||
return change;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this._writeSelf(proto, currValue, values);
|
|
||||||
this._setChanged(proto, true);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._setChanged(proto, false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._writeSelf(proto, currValue, values);
|
|
||||||
this._setChanged(proto, true);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _pipeFor(proto: ProtoRecord, context) {
|
|
||||||
var storedPipe = this._readPipe(proto);
|
|
||||||
if (isPresent(storedPipe)) return storedPipe;
|
|
||||||
|
|
||||||
var pipe = this.pipes.get(proto.name);
|
|
||||||
this._writePipe(proto, pipe);
|
|
||||||
return pipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _readContext(proto: ProtoRecord, values: any[]) {
|
|
||||||
if (proto.contextIndex == -1) {
|
|
||||||
return this._getDirectiveFor(proto.directiveIndex);
|
|
||||||
}
|
|
||||||
return values[proto.contextIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _readSelf(proto: ProtoRecord, values: any[]) { return values[proto.selfIndex]; }
|
|
||||||
|
|
||||||
private _writeSelf(proto: ProtoRecord, value, values: any[]) { values[proto.selfIndex] = value; }
|
|
||||||
|
|
||||||
private _readPipe(proto: ProtoRecord) { return this.localPipes[proto.selfIndex]; }
|
|
||||||
|
|
||||||
private _writePipe(proto: ProtoRecord, value) { this.localPipes[proto.selfIndex] = value; }
|
|
||||||
|
|
||||||
private _setChanged(proto: ProtoRecord, value: boolean) {
|
|
||||||
if (proto.argumentToPureFunction) this.changes[proto.selfIndex] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _pureFuncAndArgsDidNotChange(proto: ProtoRecord): boolean {
|
|
||||||
return proto.isPureFunction() && !this._argsChanged(proto);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _argsChanged(proto: ProtoRecord): boolean {
|
|
||||||
var args = proto.args;
|
|
||||||
for (var i = 0; i < args.length; ++i) {
|
|
||||||
if (this.changes[args[i]]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _argsOrContextChanged(proto: ProtoRecord): boolean {
|
|
||||||
return this._argsChanged(proto) || this.changes[proto.contextIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _readArgs(proto: ProtoRecord, values: any[]) {
|
|
||||||
var res = ListWrapper.createFixedSize(proto.args.length);
|
|
||||||
var args = proto.args;
|
|
||||||
for (var i = 0; i < args.length; ++i) {
|
|
||||||
res[i] = values[args[i]];
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import {DirectiveIndex} from './directive_record';
|
|
||||||
import {ProtoRecord} from './proto_record';
|
|
||||||
|
|
||||||
export class EventBinding {
|
|
||||||
constructor(public eventName: string, public elIndex: number, public dirIndex: DirectiveIndex,
|
|
||||||
public records: ProtoRecord[]) {}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
import {BaseException, WrappedException} from "angular2/src/facade/exceptions";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An error thrown if application changes model breaking the top-down data flow.
|
|
||||||
*
|
|
||||||
* This exception is only thrown in dev mode.
|
|
||||||
*
|
|
||||||
* <!-- TODO: Add a link once the dev mode option is configurable -->
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'parent',
|
|
||||||
* template: `
|
|
||||||
* <child [prop]="parentProp"></child>
|
|
||||||
* `,
|
|
||||||
* directives: [forwardRef(() => Child)]
|
|
||||||
* })
|
|
||||||
* class Parent {
|
|
||||||
* parentProp = "init";
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Directive({selector: 'child', inputs: ['prop']})
|
|
||||||
* class Child {
|
|
||||||
* constructor(public parent: Parent) {}
|
|
||||||
*
|
|
||||||
* set prop(v) {
|
|
||||||
* // this updates the parent property, which is disallowed during change detection
|
|
||||||
* // this will result in ExpressionChangedAfterItHasBeenCheckedException
|
|
||||||
* this.parent.parentProp = "updated";
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export class ExpressionChangedAfterItHasBeenCheckedException extends BaseException {
|
|
||||||
constructor(exp: string, oldValue: any, currValue: any, context: any) {
|
|
||||||
super(`Expression '${exp}' has changed after it was checked. ` +
|
|
||||||
`Previous value: '${oldValue}'. Current value: '${currValue}'`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when an expression evaluation raises an exception.
|
|
||||||
*
|
|
||||||
* This error wraps the original exception to attach additional contextual information that can
|
|
||||||
* be useful for debugging.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/2Kywoz?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Directive({selector: 'child', inputs: ['prop']})
|
|
||||||
* class Child {
|
|
||||||
* prop;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <child [prop]="field.first"></child>
|
|
||||||
* `,
|
|
||||||
* directives: [Child]
|
|
||||||
* })
|
|
||||||
* class App {
|
|
||||||
* field = null;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* bootstrap(App);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* You can access the original exception and stack through the `originalException` and
|
|
||||||
* `originalStack` properties.
|
|
||||||
*/
|
|
||||||
export class ChangeDetectionError extends WrappedException {
|
|
||||||
/**
|
|
||||||
* Information about the expression that triggered the exception.
|
|
||||||
*/
|
|
||||||
location: string;
|
|
||||||
|
|
||||||
constructor(exp: string, originalException: any, originalStack: any, context: any) {
|
|
||||||
super(`${originalException} in [${exp}]`, originalException, originalStack, context);
|
|
||||||
this.location = exp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when change detector executes on dehydrated view.
|
|
||||||
*
|
|
||||||
* This error indicates a bug in the framework.
|
|
||||||
*
|
|
||||||
* This is an internal Angular error.
|
|
||||||
*/
|
|
||||||
export class DehydratedException extends BaseException {
|
|
||||||
constructor(details: string) { super(`Attempt to use a dehydrated detector: ${details}`); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps an exception thrown by an event handler.
|
|
||||||
*/
|
|
||||||
export class EventEvaluationError extends WrappedException {
|
|
||||||
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
|
|
||||||
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error context included when an event handler throws an exception.
|
|
||||||
*/
|
|
||||||
export class EventEvaluationErrorContext {
|
|
||||||
constructor(public element: any, public componentElement: any, public context: any,
|
|
||||||
public locals: any, public injector: any) {}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
import {Locals} from './parser/locals';
|
|
||||||
import {BindingTarget, BindingRecord} from './binding_record';
|
|
||||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
|
||||||
import {ChangeDetectionStrategy} from './constants';
|
|
||||||
import {ChangeDetectorRef} from './change_detector_ref';
|
|
||||||
|
|
||||||
export class DebugContext {
|
|
||||||
constructor(public element: any, public componentElement: any, public directive: any,
|
|
||||||
public context: any, public locals: any, public injector: any) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChangeDispatcher {
|
|
||||||
getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext;
|
|
||||||
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
|
|
||||||
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
|
|
||||||
notifyAfterContentChecked(): void;
|
|
||||||
notifyAfterViewChecked(): void;
|
|
||||||
notifyOnDestroy(): void;
|
|
||||||
getDetectorFor(directiveIndex: DirectiveIndex): ChangeDetector;
|
|
||||||
getDirectiveFor(directiveIndex: DirectiveIndex): any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChangeDetector {
|
|
||||||
parent: ChangeDetector;
|
|
||||||
mode: ChangeDetectionStrategy;
|
|
||||||
ref: ChangeDetectorRef;
|
|
||||||
|
|
||||||
addContentChild(cd: ChangeDetector): void;
|
|
||||||
addViewChild(cd: ChangeDetector): void;
|
|
||||||
removeContentChild(cd: ChangeDetector): void;
|
|
||||||
removeViewChild(cd: ChangeDetector): void;
|
|
||||||
remove(): void;
|
|
||||||
hydrate(context: any, locals: Locals, dispatcher: ChangeDispatcher, pipes: any): void;
|
|
||||||
dehydrate(): void;
|
|
||||||
markPathToRootAsCheckOnce(): void;
|
|
||||||
|
|
||||||
handleEvent(eventName: string, elIndex: number, event: any);
|
|
||||||
detectChanges(): void;
|
|
||||||
checkNoChanges(): void;
|
|
||||||
destroyRecursive(): void;
|
|
||||||
markAsCheckOnce(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProtoChangeDetector { instantiate(): ChangeDetector; }
|
|
||||||
|
|
||||||
export class ChangeDetectorGenConfig {
|
|
||||||
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
|
|
||||||
public useJit: boolean) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChangeDetectorDefinition {
|
|
||||||
constructor(public id: string, public strategy: ChangeDetectionStrategy,
|
|
||||||
public variableNames: string[], public bindingRecords: BindingRecord[],
|
|
||||||
public eventRecords: BindingRecord[], public directiveRecords: DirectiveRecord[],
|
|
||||||
public genConfig: ChangeDetectorGenConfig) {}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
library change_detection.jit_proto_change_detector;
|
|
||||||
|
|
||||||
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
|
|
||||||
|
|
||||||
class JitProtoChangeDetector implements ProtoChangeDetector {
|
|
||||||
JitProtoChangeDetector(definition);
|
|
||||||
|
|
||||||
static bool isSupported() => false;
|
|
||||||
|
|
||||||
ChangeDetector instantiate() {
|
|
||||||
throw "Jit Change Detection not supported in Dart";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
|
||||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
|
||||||
|
|
||||||
export class JitProtoChangeDetector implements ProtoChangeDetector {
|
|
||||||
/** @internal */
|
|
||||||
_factory: Function;
|
|
||||||
|
|
||||||
constructor(private definition: ChangeDetectorDefinition) {
|
|
||||||
this._factory = this._createFactory(definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
static isSupported(): boolean { return true; }
|
|
||||||
|
|
||||||
instantiate(): ChangeDetector { return this._factory(); }
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_createFactory(definition: ChangeDetectorDefinition) {
|
|
||||||
return new ChangeDetectorJITGenerator(definition, 'util', 'AbstractChangeDetector',
|
|
||||||
'ChangeDetectorStatus')
|
|
||||||
.generate();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,315 +0,0 @@
|
||||||
import {ListWrapper} from "angular2/src/facade/collection";
|
|
||||||
|
|
||||||
export class AST {
|
|
||||||
visit(visitor: AstVisitor): any { return null; }
|
|
||||||
toString(): string { return "AST"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a quoted expression of the form:
|
|
||||||
*
|
|
||||||
* quote = prefix `:` uninterpretedExpression
|
|
||||||
* prefix = identifier
|
|
||||||
* uninterpretedExpression = arbitrary string
|
|
||||||
*
|
|
||||||
* A quoted expression is meant to be pre-processed by an AST transformer that
|
|
||||||
* converts it into another AST that no longer contains quoted expressions.
|
|
||||||
* It is meant to allow third-party developers to extend Angular template
|
|
||||||
* expression language. The `uninterpretedExpression` part of the quote is
|
|
||||||
* therefore not interpreted by the Angular's own expression parser.
|
|
||||||
*/
|
|
||||||
export class Quote extends AST {
|
|
||||||
constructor(public prefix: string, public uninterpretedExpression: string, public location: any) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitQuote(this); }
|
|
||||||
toString(): string { return "Quote"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EmptyExpr extends AST {
|
|
||||||
visit(visitor: AstVisitor) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ImplicitReceiver extends AST {
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitImplicitReceiver(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multiple expressions separated by a semicolon.
|
|
||||||
*/
|
|
||||||
export class Chain extends AST {
|
|
||||||
constructor(public expressions: any[]) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitChain(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Conditional extends AST {
|
|
||||||
constructor(public condition: AST, public trueExp: AST, public falseExp: AST) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitConditional(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PropertyRead extends AST {
|
|
||||||
constructor(public receiver: AST, public name: string, public getter: Function) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitPropertyRead(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PropertyWrite extends AST {
|
|
||||||
constructor(public receiver: AST, public name: string, public setter: Function,
|
|
||||||
public value: AST) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitPropertyWrite(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SafePropertyRead extends AST {
|
|
||||||
constructor(public receiver: AST, public name: string, public getter: Function) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitSafePropertyRead(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class KeyedRead extends AST {
|
|
||||||
constructor(public obj: AST, public key: AST) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitKeyedRead(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class KeyedWrite extends AST {
|
|
||||||
constructor(public obj: AST, public key: AST, public value: AST) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitKeyedWrite(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BindingPipe extends AST {
|
|
||||||
constructor(public exp: AST, public name: string, public args: any[]) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitPipe(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiteralPrimitive extends AST {
|
|
||||||
constructor(public value) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitLiteralPrimitive(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiteralArray extends AST {
|
|
||||||
constructor(public expressions: any[]) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitLiteralArray(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LiteralMap extends AST {
|
|
||||||
constructor(public keys: any[], public values: any[]) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitLiteralMap(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Interpolation extends AST {
|
|
||||||
constructor(public strings: any[], public expressions: any[]) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitInterpolation(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Binary extends AST {
|
|
||||||
constructor(public operation: string, public left: AST, public right: AST) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitBinary(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PrefixNot extends AST {
|
|
||||||
constructor(public expression: AST) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitPrefixNot(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MethodCall extends AST {
|
|
||||||
constructor(public receiver: AST, public name: string, public fn: Function, public args: any[]) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitMethodCall(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SafeMethodCall extends AST {
|
|
||||||
constructor(public receiver: AST, public name: string, public fn: Function, public args: any[]) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitSafeMethodCall(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FunctionCall extends AST {
|
|
||||||
constructor(public target: AST, public args: any[]) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { return visitor.visitFunctionCall(this); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ASTWithSource extends AST {
|
|
||||||
constructor(public ast: AST, public source: string, public location: string) { super(); }
|
|
||||||
visit(visitor: AstVisitor): any { 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) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AstVisitor {
|
|
||||||
visitBinary(ast: Binary): any;
|
|
||||||
visitChain(ast: Chain): any;
|
|
||||||
visitConditional(ast: Conditional): any;
|
|
||||||
visitFunctionCall(ast: FunctionCall): any;
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): any;
|
|
||||||
visitInterpolation(ast: Interpolation): any;
|
|
||||||
visitKeyedRead(ast: KeyedRead): any;
|
|
||||||
visitKeyedWrite(ast: KeyedWrite): any;
|
|
||||||
visitLiteralArray(ast: LiteralArray): any;
|
|
||||||
visitLiteralMap(ast: LiteralMap): any;
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive): any;
|
|
||||||
visitMethodCall(ast: MethodCall): any;
|
|
||||||
visitPipe(ast: BindingPipe): any;
|
|
||||||
visitPrefixNot(ast: PrefixNot): any;
|
|
||||||
visitPropertyRead(ast: PropertyRead): any;
|
|
||||||
visitPropertyWrite(ast: PropertyWrite): any;
|
|
||||||
visitQuote(ast: Quote): any;
|
|
||||||
visitSafeMethodCall(ast: SafeMethodCall): any;
|
|
||||||
visitSafePropertyRead(ast: SafePropertyRead): any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RecursiveAstVisitor implements AstVisitor {
|
|
||||||
visitBinary(ast: Binary): any {
|
|
||||||
ast.left.visit(this);
|
|
||||||
ast.right.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitChain(ast: Chain): any { return this.visitAll(ast.expressions); }
|
|
||||||
visitConditional(ast: Conditional): any {
|
|
||||||
ast.condition.visit(this);
|
|
||||||
ast.trueExp.visit(this);
|
|
||||||
ast.falseExp.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitPipe(ast: BindingPipe): any {
|
|
||||||
ast.exp.visit(this);
|
|
||||||
this.visitAll(ast.args);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitFunctionCall(ast: FunctionCall): any {
|
|
||||||
ast.target.visit(this);
|
|
||||||
this.visitAll(ast.args);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): any { return null; }
|
|
||||||
visitInterpolation(ast: Interpolation): any { return this.visitAll(ast.expressions); }
|
|
||||||
visitKeyedRead(ast: KeyedRead): any {
|
|
||||||
ast.obj.visit(this);
|
|
||||||
ast.key.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitKeyedWrite(ast: KeyedWrite): any {
|
|
||||||
ast.obj.visit(this);
|
|
||||||
ast.key.visit(this);
|
|
||||||
ast.value.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitLiteralArray(ast: LiteralArray): any { return this.visitAll(ast.expressions); }
|
|
||||||
visitLiteralMap(ast: LiteralMap): any { return this.visitAll(ast.values); }
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive): any { return null; }
|
|
||||||
visitMethodCall(ast: MethodCall): any {
|
|
||||||
ast.receiver.visit(this);
|
|
||||||
return this.visitAll(ast.args);
|
|
||||||
}
|
|
||||||
visitPrefixNot(ast: PrefixNot): any {
|
|
||||||
ast.expression.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitPropertyRead(ast: PropertyRead): any {
|
|
||||||
ast.receiver.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitPropertyWrite(ast: PropertyWrite): any {
|
|
||||||
ast.receiver.visit(this);
|
|
||||||
ast.value.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitSafePropertyRead(ast: SafePropertyRead): any {
|
|
||||||
ast.receiver.visit(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitSafeMethodCall(ast: SafeMethodCall): any {
|
|
||||||
ast.receiver.visit(this);
|
|
||||||
return this.visitAll(ast.args);
|
|
||||||
}
|
|
||||||
visitAll(asts: AST[]): any {
|
|
||||||
asts.forEach(ast => ast.visit(this));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
visitQuote(ast: Quote): any { return null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AstTransformer implements AstVisitor {
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): AST { return ast; }
|
|
||||||
|
|
||||||
visitInterpolation(ast: Interpolation): AST {
|
|
||||||
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive): AST { return new LiteralPrimitive(ast.value); }
|
|
||||||
|
|
||||||
visitPropertyRead(ast: PropertyRead): AST {
|
|
||||||
return new PropertyRead(ast.receiver.visit(this), ast.name, ast.getter);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPropertyWrite(ast: PropertyWrite): AST {
|
|
||||||
return new PropertyWrite(ast.receiver.visit(this), ast.name, ast.setter, ast.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitSafePropertyRead(ast: SafePropertyRead): AST {
|
|
||||||
return new SafePropertyRead(ast.receiver.visit(this), ast.name, ast.getter);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitMethodCall(ast: MethodCall): AST {
|
|
||||||
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitSafeMethodCall(ast: SafeMethodCall): AST {
|
|
||||||
return new SafeMethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitFunctionCall(ast: FunctionCall): AST {
|
|
||||||
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralArray(ast: LiteralArray): AST {
|
|
||||||
return new LiteralArray(this.visitAll(ast.expressions));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralMap(ast: LiteralMap): AST {
|
|
||||||
return new LiteralMap(ast.keys, this.visitAll(ast.values));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBinary(ast: Binary): AST {
|
|
||||||
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPrefixNot(ast: PrefixNot): AST { return new PrefixNot(ast.expression.visit(this)); }
|
|
||||||
|
|
||||||
visitConditional(ast: Conditional): AST {
|
|
||||||
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
|
|
||||||
ast.falseExp.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPipe(ast: BindingPipe): AST {
|
|
||||||
return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitKeyedRead(ast: KeyedRead): AST {
|
|
||||||
return new KeyedRead(ast.obj.visit(this), ast.key.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitKeyedWrite(ast: KeyedWrite): AST {
|
|
||||||
return new KeyedWrite(ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
visitAll(asts: any[]): any[] {
|
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
|
||||||
res[i] = asts[i].visit(this);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitChain(ast: Chain): AST { return new Chain(this.visitAll(ast.expressions)); }
|
|
||||||
|
|
||||||
visitQuote(ast: Quote): AST {
|
|
||||||
return new Quote(ast.prefix, ast.uninterpretedExpression, ast.location);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
export class Locals {
|
|
||||||
constructor(public parent: Locals, public current: Map<any, any>) {}
|
|
||||||
|
|
||||||
contains(name: string): boolean {
|
|
||||||
if (this.current.has(name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(this.parent)) {
|
|
||||||
return this.parent.contains(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(name: string): any {
|
|
||||||
if (this.current.has(name)) {
|
|
||||||
return this.current.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(this.parent)) {
|
|
||||||
return this.parent.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new BaseException(`Cannot find '${name}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
set(name: string, value: any): void {
|
|
||||||
// TODO(rado): consider removing this check if we can guarantee this is not
|
|
||||||
// exposed to the public API.
|
|
||||||
// TODO: vsavkin maybe it should check only the local map
|
|
||||||
if (this.current.has(name)) {
|
|
||||||
this.current.set(name, value);
|
|
||||||
} else {
|
|
||||||
throw new BaseException(
|
|
||||||
`Setting of new keys post-construction is not supported. Key: ${name}.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLocalValues(): void { MapWrapper.clearValues(this.current); }
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
library angular2.core.compiler.pipe_lifecycle_reflector;
|
|
||||||
|
|
||||||
import 'package:angular2/src/core/linker/interfaces.dart';
|
|
||||||
|
|
||||||
bool implementsOnDestroy(Object pipe) => pipe is OnDestroy;
|
|
|
@ -1,3 +0,0 @@
|
||||||
export function implementsOnDestroy(pipe: any): boolean {
|
|
||||||
return pipe.constructor.prototype.ngOnDestroy;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import {PipeTransform} from './pipe_transform';
|
|
||||||
|
|
||||||
export interface Pipes { get(name: string): SelectedPipe; }
|
|
||||||
|
|
||||||
export class SelectedPipe {
|
|
||||||
constructor(public pipe: PipeTransform, public pure: boolean) {}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
library angular2.src.change_detection.pregen_proto_change_detector;
|
|
||||||
|
|
||||||
export 'dart:core' show List;
|
|
||||||
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
|
|
||||||
show AbstractChangeDetector;
|
|
||||||
export 'package:angular2/src/core/change_detection/change_detection.dart'
|
|
||||||
show ChangeDetectionStrategy;
|
|
||||||
export 'package:angular2/src/core/change_detection/constants.dart'
|
|
||||||
show ChangeDetectorState;
|
|
||||||
export 'package:angular2/src/core/change_detection/directive_record.dart'
|
|
||||||
show DirectiveIndex, DirectiveRecord;
|
|
||||||
export 'package:angular2/src/core/change_detection/interfaces.dart'
|
|
||||||
show ChangeDetector, ChangeDetectorDefinition, ProtoChangeDetector;
|
|
||||||
export 'package:angular2/src/core/change_detection/pipes.dart' show Pipes;
|
|
||||||
export 'package:angular2/src/core/change_detection/proto_record.dart'
|
|
||||||
show ProtoRecord;
|
|
||||||
export 'package:angular2/src/core/change_detection/change_detection_util.dart'
|
|
||||||
show ChangeDetectionUtil;
|
|
||||||
export 'package:angular2/src/facade/lang.dart' show assertionsEnabled, looseIdentical;
|
|
|
@ -1 +0,0 @@
|
||||||
// empty file as we only need the dart version
|
|
|
@ -1,471 +0,0 @@
|
||||||
import {Type, isBlank, isPresent, isString} from 'angular2/src/facade/lang';
|
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
|
||||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
import {
|
|
||||||
PropertyRead,
|
|
||||||
PropertyWrite,
|
|
||||||
KeyedWrite,
|
|
||||||
AST,
|
|
||||||
ASTWithSource,
|
|
||||||
AstVisitor,
|
|
||||||
Binary,
|
|
||||||
Chain,
|
|
||||||
Conditional,
|
|
||||||
BindingPipe,
|
|
||||||
FunctionCall,
|
|
||||||
ImplicitReceiver,
|
|
||||||
Interpolation,
|
|
||||||
KeyedRead,
|
|
||||||
LiteralArray,
|
|
||||||
LiteralMap,
|
|
||||||
LiteralPrimitive,
|
|
||||||
MethodCall,
|
|
||||||
PrefixNot,
|
|
||||||
Quote,
|
|
||||||
SafePropertyRead,
|
|
||||||
SafeMethodCall
|
|
||||||
} from './parser/ast';
|
|
||||||
|
|
||||||
import {ChangeDetector, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
|
||||||
import {DynamicChangeDetector} from './dynamic_change_detector';
|
|
||||||
import {BindingRecord, BindingTarget} from './binding_record';
|
|
||||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
|
||||||
import {EventBinding} from './event_binding';
|
|
||||||
|
|
||||||
import {coalesce} from './coalesce';
|
|
||||||
import {ProtoRecord, RecordType} from './proto_record';
|
|
||||||
|
|
||||||
export class DynamicProtoChangeDetector implements ProtoChangeDetector {
|
|
||||||
/** @internal */
|
|
||||||
_propertyBindingRecords: ProtoRecord[];
|
|
||||||
/** @internal */
|
|
||||||
_propertyBindingTargets: BindingTarget[];
|
|
||||||
/** @internal */
|
|
||||||
_eventBindingRecords: EventBinding[];
|
|
||||||
/** @internal */
|
|
||||||
_directiveIndices: DirectiveIndex[];
|
|
||||||
|
|
||||||
constructor(private _definition: ChangeDetectorDefinition) {
|
|
||||||
this._propertyBindingRecords = createPropertyRecords(_definition);
|
|
||||||
this._eventBindingRecords = createEventRecords(_definition);
|
|
||||||
this._propertyBindingTargets = this._definition.bindingRecords.map(b => b.target);
|
|
||||||
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
instantiate(): ChangeDetector {
|
|
||||||
return new DynamicChangeDetector(
|
|
||||||
this._definition.id, this._propertyBindingRecords.length, this._propertyBindingTargets,
|
|
||||||
this._directiveIndices, this._definition.strategy, this._propertyBindingRecords,
|
|
||||||
this._eventBindingRecords, this._definition.directiveRecords, this._definition.genConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createPropertyRecords(definition: ChangeDetectorDefinition): ProtoRecord[] {
|
|
||||||
var recordBuilder = new ProtoRecordBuilder();
|
|
||||||
ListWrapper.forEachWithIndex(
|
|
||||||
definition.bindingRecords,
|
|
||||||
(b: BindingRecord, index: number) => recordBuilder.add(b, definition.variableNames, index));
|
|
||||||
return coalesce(recordBuilder.records);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createEventRecords(definition: ChangeDetectorDefinition): EventBinding[] {
|
|
||||||
// TODO: vsavkin: remove $event when the compiler handles render-side variables properly
|
|
||||||
var varNames = ListWrapper.concat(['$event'], definition.variableNames);
|
|
||||||
return definition.eventRecords.map(er => {
|
|
||||||
var records = _ConvertAstIntoProtoRecords.create(er, varNames);
|
|
||||||
var dirIndex = er.implicitReceiver instanceof DirectiveIndex ? er.implicitReceiver : null;
|
|
||||||
return new EventBinding(er.target.name, er.target.elementIndex, dirIndex, records);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ProtoRecordBuilder {
|
|
||||||
records: ProtoRecord[] = [];
|
|
||||||
|
|
||||||
add(b: BindingRecord, variableNames: string[], bindingIndex: number) {
|
|
||||||
var oldLast = ListWrapper.last(this.records);
|
|
||||||
if (isPresent(oldLast) && oldLast.bindingRecord.directiveRecord == b.directiveRecord) {
|
|
||||||
oldLast.lastInDirective = false;
|
|
||||||
}
|
|
||||||
var numberOfRecordsBefore = this.records.length;
|
|
||||||
this._appendRecords(b, variableNames, bindingIndex);
|
|
||||||
var newLast = ListWrapper.last(this.records);
|
|
||||||
if (isPresent(newLast) && newLast !== oldLast) {
|
|
||||||
newLast.lastInBinding = true;
|
|
||||||
newLast.lastInDirective = true;
|
|
||||||
this._setArgumentToPureFunction(numberOfRecordsBefore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_setArgumentToPureFunction(startIndex: number): void {
|
|
||||||
for (var i = startIndex; i < this.records.length; ++i) {
|
|
||||||
var rec = this.records[i];
|
|
||||||
if (rec.isPureFunction()) {
|
|
||||||
rec.args.forEach(recordIndex => this.records[recordIndex - 1].argumentToPureFunction =
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
if (rec.mode === RecordType.Pipe) {
|
|
||||||
rec.args.forEach(recordIndex => this.records[recordIndex - 1].argumentToPureFunction =
|
|
||||||
true);
|
|
||||||
this.records[rec.contextIndex - 1].argumentToPureFunction = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_appendRecords(b: BindingRecord, variableNames: string[], bindingIndex: number) {
|
|
||||||
if (b.isDirectiveLifecycle()) {
|
|
||||||
this.records.push(new ProtoRecord(RecordType.DirectiveLifecycle, b.lifecycleEvent, null, [],
|
|
||||||
[], -1, null, this.records.length + 1, b, false, false,
|
|
||||||
false, false, null));
|
|
||||||
} else {
|
|
||||||
_ConvertAstIntoProtoRecords.append(this.records, b, variableNames, bindingIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ConvertAstIntoProtoRecords implements AstVisitor {
|
|
||||||
constructor(private _records: ProtoRecord[], private _bindingRecord: BindingRecord,
|
|
||||||
private _variableNames: string[], private _bindingIndex: number) {}
|
|
||||||
|
|
||||||
static append(records: ProtoRecord[], b: BindingRecord, variableNames: string[],
|
|
||||||
bindingIndex: number) {
|
|
||||||
var c = new _ConvertAstIntoProtoRecords(records, b, variableNames, bindingIndex);
|
|
||||||
b.ast.visit(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static create(b: BindingRecord, variableNames: any[]): ProtoRecord[] {
|
|
||||||
var rec = [];
|
|
||||||
_ConvertAstIntoProtoRecords.append(rec, b, variableNames, null);
|
|
||||||
rec[rec.length - 1].lastInBinding = true;
|
|
||||||
return rec;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitImplicitReceiver(ast: ImplicitReceiver): any { return this._bindingRecord.implicitReceiver; }
|
|
||||||
|
|
||||||
visitInterpolation(ast: Interpolation): number {
|
|
||||||
var args = this._visitAll(ast.expressions);
|
|
||||||
return this._addRecord(RecordType.Interpolate, "interpolate", _interpolationFn(ast.strings),
|
|
||||||
args, ast.strings, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralPrimitive(ast: LiteralPrimitive): number {
|
|
||||||
return this._addRecord(RecordType.Const, "literal", ast.value, [], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPropertyRead(ast: PropertyRead): number {
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
if (isPresent(this._variableNames) && ListWrapper.contains(this._variableNames, ast.name) &&
|
|
||||||
ast.receiver instanceof ImplicitReceiver) {
|
|
||||||
return this._addRecord(RecordType.Local, ast.name, ast.name, [], null, receiver);
|
|
||||||
} else {
|
|
||||||
return this._addRecord(RecordType.PropertyRead, ast.name, ast.getter, [], null, receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPropertyWrite(ast: PropertyWrite): number {
|
|
||||||
if (isPresent(this._variableNames) && ListWrapper.contains(this._variableNames, ast.name) &&
|
|
||||||
ast.receiver instanceof ImplicitReceiver) {
|
|
||||||
throw new BaseException(`Cannot reassign a variable binding ${ast.name}`);
|
|
||||||
} else {
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
var value = ast.value.visit(this);
|
|
||||||
return this._addRecord(RecordType.PropertyWrite, ast.name, ast.setter, [value], null,
|
|
||||||
receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitKeyedWrite(ast: KeyedWrite): number {
|
|
||||||
var obj = ast.obj.visit(this);
|
|
||||||
var key = ast.key.visit(this);
|
|
||||||
var value = ast.value.visit(this);
|
|
||||||
return this._addRecord(RecordType.KeyedWrite, null, null, [key, value], null, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitSafePropertyRead(ast: SafePropertyRead): number {
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
return this._addRecord(RecordType.SafeProperty, ast.name, ast.getter, [], null, receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitMethodCall(ast: MethodCall): number {
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
var args = this._visitAll(ast.args);
|
|
||||||
if (isPresent(this._variableNames) && ListWrapper.contains(this._variableNames, ast.name)) {
|
|
||||||
var target = this._addRecord(RecordType.Local, ast.name, ast.name, [], null, receiver);
|
|
||||||
return this._addRecord(RecordType.InvokeClosure, "closure", null, args, null, target);
|
|
||||||
} else {
|
|
||||||
return this._addRecord(RecordType.InvokeMethod, ast.name, ast.fn, args, null, receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitSafeMethodCall(ast: SafeMethodCall): number {
|
|
||||||
var receiver = ast.receiver.visit(this);
|
|
||||||
var args = this._visitAll(ast.args);
|
|
||||||
return this._addRecord(RecordType.SafeMethodInvoke, ast.name, ast.fn, args, null, receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitFunctionCall(ast: FunctionCall): number {
|
|
||||||
var target = ast.target.visit(this);
|
|
||||||
var args = this._visitAll(ast.args);
|
|
||||||
return this._addRecord(RecordType.InvokeClosure, "closure", null, args, null, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralArray(ast: LiteralArray): number {
|
|
||||||
var primitiveName = `arrayFn${ast.expressions.length}`;
|
|
||||||
return this._addRecord(RecordType.CollectionLiteral, primitiveName,
|
|
||||||
_arrayFn(ast.expressions.length), this._visitAll(ast.expressions), null,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteralMap(ast: LiteralMap): number {
|
|
||||||
return this._addRecord(RecordType.CollectionLiteral, _mapPrimitiveName(ast.keys),
|
|
||||||
ChangeDetectionUtil.mapFn(ast.keys), this._visitAll(ast.values), null,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBinary(ast: Binary): number {
|
|
||||||
var left = ast.left.visit(this);
|
|
||||||
switch (ast.operation) {
|
|
||||||
case '&&':
|
|
||||||
var branchEnd = [null];
|
|
||||||
this._addRecord(RecordType.SkipRecordsIfNot, "SkipRecordsIfNot", null, [], branchEnd, left);
|
|
||||||
var right = ast.right.visit(this);
|
|
||||||
branchEnd[0] = right;
|
|
||||||
return this._addRecord(RecordType.PrimitiveOp, "cond", ChangeDetectionUtil.cond,
|
|
||||||
[left, right, left], null, 0);
|
|
||||||
|
|
||||||
case '||':
|
|
||||||
var branchEnd = [null];
|
|
||||||
this._addRecord(RecordType.SkipRecordsIf, "SkipRecordsIf", null, [], branchEnd, left);
|
|
||||||
var right = ast.right.visit(this);
|
|
||||||
branchEnd[0] = right;
|
|
||||||
return this._addRecord(RecordType.PrimitiveOp, "cond", ChangeDetectionUtil.cond,
|
|
||||||
[left, left, right], null, 0);
|
|
||||||
|
|
||||||
default:
|
|
||||||
var right = ast.right.visit(this);
|
|
||||||
return this._addRecord(RecordType.PrimitiveOp, _operationToPrimitiveName(ast.operation),
|
|
||||||
_operationToFunction(ast.operation), [left, right], null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPrefixNot(ast: PrefixNot): number {
|
|
||||||
var exp = ast.expression.visit(this);
|
|
||||||
return this._addRecord(RecordType.PrimitiveOp, "operation_negate",
|
|
||||||
ChangeDetectionUtil.operation_negate, [exp], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitConditional(ast: Conditional): number {
|
|
||||||
var condition = ast.condition.visit(this);
|
|
||||||
var startOfFalseBranch = [null];
|
|
||||||
var endOfFalseBranch = [null];
|
|
||||||
this._addRecord(RecordType.SkipRecordsIfNot, "SkipRecordsIfNot", null, [], startOfFalseBranch,
|
|
||||||
condition);
|
|
||||||
var whenTrue = ast.trueExp.visit(this);
|
|
||||||
var skip =
|
|
||||||
this._addRecord(RecordType.SkipRecords, "SkipRecords", null, [], endOfFalseBranch, 0);
|
|
||||||
var whenFalse = ast.falseExp.visit(this);
|
|
||||||
startOfFalseBranch[0] = skip;
|
|
||||||
endOfFalseBranch[0] = whenFalse;
|
|
||||||
|
|
||||||
return this._addRecord(RecordType.PrimitiveOp, "cond", ChangeDetectionUtil.cond,
|
|
||||||
[condition, whenTrue, whenFalse], null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitPipe(ast: BindingPipe): number {
|
|
||||||
var value = ast.exp.visit(this);
|
|
||||||
var args = this._visitAll(ast.args);
|
|
||||||
return this._addRecord(RecordType.Pipe, ast.name, ast.name, args, null, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitKeyedRead(ast: KeyedRead): number {
|
|
||||||
var obj = ast.obj.visit(this);
|
|
||||||
var key = ast.key.visit(this);
|
|
||||||
return this._addRecord(RecordType.KeyedRead, "keyedAccess", ChangeDetectionUtil.keyedAccess,
|
|
||||||
[key], null, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitChain(ast: Chain): number {
|
|
||||||
var args = ast.expressions.map(e => e.visit(this));
|
|
||||||
return this._addRecord(RecordType.Chain, "chain", null, args, null, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitQuote(ast: Quote): void {
|
|
||||||
throw new BaseException(
|
|
||||||
`Caught uninterpreted expression at ${ast.location}: ${ast.uninterpretedExpression}. ` +
|
|
||||||
`Expression prefix ${ast.prefix} did not match a template transformer to interpret the expression.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _visitAll(asts: any[]) {
|
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
|
||||||
res[i] = asts[i].visit(this);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a `ProtoRecord` and returns its selfIndex.
|
|
||||||
*/
|
|
||||||
private _addRecord(type, name, funcOrValue, args, fixedArgs, context): number {
|
|
||||||
var selfIndex = this._records.length + 1;
|
|
||||||
if (context instanceof DirectiveIndex) {
|
|
||||||
this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context,
|
|
||||||
selfIndex, this._bindingRecord, false, false, false, false,
|
|
||||||
this._bindingIndex));
|
|
||||||
} else {
|
|
||||||
this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null,
|
|
||||||
selfIndex, this._bindingRecord, false, false, false, false,
|
|
||||||
this._bindingIndex));
|
|
||||||
}
|
|
||||||
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: any[]) {
|
|
||||||
var stringifiedKeys = keys.map(k => isString(k) ? `"${k}"` : `${k}`).join(', ');
|
|
||||||
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_identical";
|
|
||||||
case '!==':
|
|
||||||
return "operation_not_identical";
|
|
||||||
case '<':
|
|
||||||
return "operation_less_then";
|
|
||||||
case '>':
|
|
||||||
return "operation_greater_then";
|
|
||||||
case '<=':
|
|
||||||
return "operation_less_or_equals_then";
|
|
||||||
case '>=':
|
|
||||||
return "operation_greater_or_equals_then";
|
|
||||||
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_identical;
|
|
||||||
case '!==':
|
|
||||||
return ChangeDetectionUtil.operation_not_identical;
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
throw new BaseException(`Unsupported operation ${operation}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function s(v): string {
|
|
||||||
return isPresent(v) ? `${v}` : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _interpolationFn(strings: 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,59 +0,0 @@
|
||||||
import {BindingRecord} from './binding_record';
|
|
||||||
import {DirectiveIndex} from './directive_record';
|
|
||||||
|
|
||||||
export enum RecordType {
|
|
||||||
Self,
|
|
||||||
Const,
|
|
||||||
PrimitiveOp,
|
|
||||||
PropertyRead,
|
|
||||||
PropertyWrite,
|
|
||||||
Local,
|
|
||||||
InvokeMethod,
|
|
||||||
InvokeClosure,
|
|
||||||
KeyedRead,
|
|
||||||
KeyedWrite,
|
|
||||||
Pipe,
|
|
||||||
Interpolate,
|
|
||||||
SafeProperty,
|
|
||||||
CollectionLiteral,
|
|
||||||
SafeMethodInvoke,
|
|
||||||
DirectiveLifecycle,
|
|
||||||
Chain,
|
|
||||||
SkipRecordsIf, // Skip records when the condition is true
|
|
||||||
SkipRecordsIfNot, // Skip records when the condition is false
|
|
||||||
SkipRecords // Skip records unconditionally
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ProtoRecord {
|
|
||||||
constructor(public mode: RecordType, public name: string, public funcOrValue, public args: any[],
|
|
||||||
public fixedArgs: any[], public contextIndex: number,
|
|
||||||
public directiveIndex: DirectiveIndex, public selfIndex: number,
|
|
||||||
public bindingRecord: BindingRecord, public lastInBinding: boolean,
|
|
||||||
public lastInDirective: boolean, public argumentToPureFunction: boolean,
|
|
||||||
public referencedBySelf: boolean, public propertyBindingIndex: number) {}
|
|
||||||
|
|
||||||
isPureFunction(): boolean {
|
|
||||||
return this.mode === RecordType.Interpolate || this.mode === RecordType.CollectionLiteral;
|
|
||||||
}
|
|
||||||
|
|
||||||
isUsedByOtherRecord(): boolean { return !this.lastInBinding || this.referencedBySelf; }
|
|
||||||
|
|
||||||
shouldBeChecked(): boolean {
|
|
||||||
return this.argumentToPureFunction || this.lastInBinding || this.isPureFunction() ||
|
|
||||||
this.isPipeRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
isPipeRecord(): boolean { return this.mode === RecordType.Pipe; }
|
|
||||||
|
|
||||||
isConditionalSkipRecord(): boolean {
|
|
||||||
return this.mode === RecordType.SkipRecordsIfNot || this.mode === RecordType.SkipRecordsIf;
|
|
||||||
}
|
|
||||||
|
|
||||||
isUnconditionalSkipRecord(): boolean { return this.mode === RecordType.SkipRecords; }
|
|
||||||
|
|
||||||
isSkipRecord(): boolean {
|
|
||||||
return this.isConditionalSkipRecord() || this.isUnconditionalSkipRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
isLifeCycleRecord(): boolean { return this.mode === RecordType.DirectiveLifecycle; }
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
import {isPresent, Type} from 'angular2/src/facade/lang';
|
||||||
import {Predicate} from 'angular2/src/facade/collection';
|
import {Predicate} from 'angular2/src/facade/collection';
|
||||||
import {Injector} from 'angular2/src/core/di';
|
import {Injector} from 'angular2/src/core/di';
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
@ -10,12 +10,8 @@ export class DebugNode {
|
||||||
nativeNode: any;
|
nativeNode: any;
|
||||||
listeners: EventListener[];
|
listeners: EventListener[];
|
||||||
parent: DebugElement;
|
parent: DebugElement;
|
||||||
providerTokens: any[];
|
|
||||||
locals: Map<string, any>;
|
|
||||||
injector: Injector;
|
|
||||||
componentInstance: any;
|
|
||||||
|
|
||||||
constructor(nativeNode: any, parent: DebugNode) {
|
constructor(nativeNode: any, parent: DebugNode, private _debugInfo: RenderDebugInfo) {
|
||||||
this.nativeNode = nativeNode;
|
this.nativeNode = nativeNode;
|
||||||
if (isPresent(parent) && parent instanceof DebugElement) {
|
if (isPresent(parent) && parent instanceof DebugElement) {
|
||||||
parent.addChild(this);
|
parent.addChild(this);
|
||||||
|
@ -23,32 +19,40 @@ export class DebugNode {
|
||||||
this.parent = null;
|
this.parent = null;
|
||||||
}
|
}
|
||||||
this.listeners = [];
|
this.listeners = [];
|
||||||
this.providerTokens = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDebugInfo(info: RenderDebugInfo) {
|
get injector(): Injector { return isPresent(this._debugInfo) ? this._debugInfo.injector : null; }
|
||||||
this.injector = info.injector;
|
|
||||||
this.providerTokens = info.providerTokens;
|
get componentInstance(): any {
|
||||||
this.locals = info.locals;
|
return isPresent(this._debugInfo) ? this._debugInfo.component : null;
|
||||||
this.componentInstance = info.component;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get locals(): {[key: string]: any} {
|
||||||
|
return isPresent(this._debugInfo) ? this._debugInfo.locals : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get providerTokens(): any[] {
|
||||||
|
return isPresent(this._debugInfo) ? this._debugInfo.providerTokens : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get source(): string { return isPresent(this._debugInfo) ? this._debugInfo.source : null; }
|
||||||
|
|
||||||
inject(token: any): any { return this.injector.get(token); }
|
inject(token: any): any { return this.injector.get(token); }
|
||||||
|
|
||||||
getLocal(name: string): any { return this.locals.get(name); }
|
getLocal(name: string): any { return this.locals[name]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DebugElement extends DebugNode {
|
export class DebugElement extends DebugNode {
|
||||||
name: string;
|
name: string;
|
||||||
properties: Map<string, any>;
|
properties: {[key: string]: string};
|
||||||
attributes: Map<string, any>;
|
attributes: {[key: string]: string};
|
||||||
childNodes: DebugNode[];
|
childNodes: DebugNode[];
|
||||||
nativeElement: any;
|
nativeElement: any;
|
||||||
|
|
||||||
constructor(nativeNode: any, parent: any) {
|
constructor(nativeNode: any, parent: any, _debugInfo: RenderDebugInfo) {
|
||||||
super(nativeNode, parent);
|
super(nativeNode, parent, _debugInfo);
|
||||||
this.properties = new Map<string, any>();
|
this.properties = {};
|
||||||
this.attributes = new Map<string, any>();
|
this.attributes = {};
|
||||||
this.childNodes = [];
|
this.childNodes = [];
|
||||||
this.nativeElement = nativeNode;
|
this.nativeElement = nativeNode;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +116,7 @@ export class DebugElement extends DebugNode {
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerEventHandler(eventName: string, eventObj: Event) {
|
triggerEventHandler(eventName: string, eventObj: any) {
|
||||||
this.listeners.forEach((listener) => {
|
this.listeners.forEach((listener) => {
|
||||||
if (listener.name == eventName) {
|
if (listener.name == eventName) {
|
||||||
listener.callback(eventObj);
|
listener.callback(eventObj);
|
||||||
|
|
|
@ -18,27 +18,23 @@ export class DebugDomRootRenderer implements RootRenderer {
|
||||||
constructor(private _delegate: RootRenderer) {}
|
constructor(private _delegate: RootRenderer) {}
|
||||||
|
|
||||||
renderComponent(componentProto: RenderComponentType): Renderer {
|
renderComponent(componentProto: RenderComponentType): Renderer {
|
||||||
return new DebugDomRenderer(this, this._delegate.renderComponent(componentProto));
|
return new DebugDomRenderer(this._delegate.renderComponent(componentProto));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DebugDomRenderer implements Renderer {
|
export class DebugDomRenderer implements Renderer {
|
||||||
constructor(private _rootRenderer: DebugDomRootRenderer, private _delegate: Renderer) {}
|
constructor(private _delegate: Renderer) {}
|
||||||
|
|
||||||
renderComponent(componentType: RenderComponentType): Renderer {
|
selectRootElement(selector: string, debugInfo: RenderDebugInfo): any {
|
||||||
return this._rootRenderer.renderComponent(componentType);
|
var nativeEl = this._delegate.selectRootElement(selector, debugInfo);
|
||||||
}
|
var debugEl = new DebugElement(nativeEl, null, debugInfo);
|
||||||
|
|
||||||
selectRootElement(selector: string): any {
|
|
||||||
var nativeEl = this._delegate.selectRootElement(selector);
|
|
||||||
var debugEl = new DebugElement(nativeEl, null);
|
|
||||||
indexDebugNode(debugEl);
|
indexDebugNode(debugEl);
|
||||||
return nativeEl;
|
return nativeEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
createElement(parentElement: any, name: string): any {
|
createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo): any {
|
||||||
var nativeEl = this._delegate.createElement(parentElement, name);
|
var nativeEl = this._delegate.createElement(parentElement, name, debugInfo);
|
||||||
var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement));
|
var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement), debugInfo);
|
||||||
debugEl.name = name;
|
debugEl.name = name;
|
||||||
indexDebugNode(debugEl);
|
indexDebugNode(debugEl);
|
||||||
return nativeEl;
|
return nativeEl;
|
||||||
|
@ -46,16 +42,16 @@ export class DebugDomRenderer implements Renderer {
|
||||||
|
|
||||||
createViewRoot(hostElement: any): any { return this._delegate.createViewRoot(hostElement); }
|
createViewRoot(hostElement: any): any { return this._delegate.createViewRoot(hostElement); }
|
||||||
|
|
||||||
createTemplateAnchor(parentElement: any): any {
|
createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any {
|
||||||
var comment = this._delegate.createTemplateAnchor(parentElement);
|
var comment = this._delegate.createTemplateAnchor(parentElement, debugInfo);
|
||||||
var debugEl = new DebugNode(comment, getDebugNode(parentElement));
|
var debugEl = new DebugNode(comment, getDebugNode(parentElement), debugInfo);
|
||||||
indexDebugNode(debugEl);
|
indexDebugNode(debugEl);
|
||||||
return comment;
|
return comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
createText(parentElement: any, value: string): any {
|
createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any {
|
||||||
var text = this._delegate.createText(parentElement, value);
|
var text = this._delegate.createText(parentElement, value, debugInfo);
|
||||||
var debugEl = new DebugNode(text, getDebugNode(parentElement));
|
var debugEl = new DebugNode(text, getDebugNode(parentElement), debugInfo);
|
||||||
indexDebugNode(debugEl);
|
indexDebugNode(debugEl);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +108,7 @@ export class DebugDomRenderer implements Renderer {
|
||||||
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
|
setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
|
||||||
var debugEl = getDebugNode(renderElement);
|
var debugEl = getDebugNode(renderElement);
|
||||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||||
debugEl.properties.set(propertyName, propertyValue);
|
debugEl.properties[propertyName] = propertyValue;
|
||||||
}
|
}
|
||||||
this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
|
this._delegate.setElementProperty(renderElement, propertyName, propertyValue);
|
||||||
}
|
}
|
||||||
|
@ -120,28 +116,15 @@ export class DebugDomRenderer implements Renderer {
|
||||||
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
|
||||||
var debugEl = getDebugNode(renderElement);
|
var debugEl = getDebugNode(renderElement);
|
||||||
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
if (isPresent(debugEl) && debugEl instanceof DebugElement) {
|
||||||
debugEl.attributes.set(attributeName, attributeValue);
|
debugEl.attributes[attributeName] = attributeValue;
|
||||||
}
|
}
|
||||||
this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
|
this._delegate.setElementAttribute(renderElement, attributeName, attributeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only in debug mode to serialize property changes to comment nodes,
|
|
||||||
* such as <template> placeholders.
|
|
||||||
*/
|
|
||||||
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
|
||||||
this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
this._delegate.setBindingDebugInfo(renderElement, propertyName, propertyValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used only in development mode to set information needed by the DebugNode for this element.
|
|
||||||
*/
|
|
||||||
setElementDebugInfo(renderElement: any, info: RenderDebugInfo) {
|
|
||||||
var debugEl = getDebugNode(renderElement);
|
|
||||||
debugEl.setDebugInfo(info);
|
|
||||||
this._delegate.setElementDebugInfo(renderElement, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
setElementClass(renderElement: any, className: string, isAdd: boolean) {
|
||||||
this._delegate.setElementClass(renderElement, className, isAdd);
|
this._delegate.setElementClass(renderElement, className, isAdd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ export {
|
||||||
export * from './di/decorators';
|
export * from './di/decorators';
|
||||||
|
|
||||||
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
|
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
|
||||||
|
|
||||||
export {Injector} from './di/injector';
|
export {Injector} from './di/injector';
|
||||||
export {
|
export {
|
||||||
Binding,
|
Binding,
|
||||||
|
|
|
@ -16,43 +16,21 @@ import {
|
||||||
InvalidProviderError,
|
InvalidProviderError,
|
||||||
OutOfBoundsError
|
OutOfBoundsError
|
||||||
} from './exceptions';
|
} from './exceptions';
|
||||||
import {FunctionWrapper, Type, isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
import {Type, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
import {Key} from './key';
|
import {Key} from './key';
|
||||||
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
|
import {SelfMetadata, HostMetadata, SkipSelfMetadata} from './metadata';
|
||||||
|
|
||||||
|
var __unused: Type; // avoid unused import when Type union types are erased
|
||||||
|
|
||||||
// Threshold for the dynamic version
|
// Threshold for the dynamic version
|
||||||
const _MAX_CONSTRUCTION_COUNTER = 10;
|
const _MAX_CONSTRUCTION_COUNTER = 10;
|
||||||
|
|
||||||
export const UNDEFINED: Object = CONST_EXPR(new Object());
|
export const UNDEFINED: Object = CONST_EXPR(new Object());
|
||||||
|
|
||||||
/**
|
|
||||||
* Visibility of a {@link Provider}.
|
|
||||||
*/
|
|
||||||
export enum Visibility {
|
|
||||||
/**
|
|
||||||
* A `Public` {@link Provider} is only visible to regular (as opposed to host) child injectors.
|
|
||||||
*/
|
|
||||||
Public,
|
|
||||||
/**
|
|
||||||
* A `Private` {@link Provider} is only visible to host (as opposed to regular) child injectors.
|
|
||||||
*/
|
|
||||||
Private,
|
|
||||||
/**
|
|
||||||
* A `PublicAndPrivate` {@link Provider} is visible to both host and regular child injectors.
|
|
||||||
*/
|
|
||||||
PublicAndPrivate
|
|
||||||
}
|
|
||||||
|
|
||||||
function canSee(src: Visibility, dst: Visibility): boolean {
|
|
||||||
return (src === dst) ||
|
|
||||||
(dst === Visibility.PublicAndPrivate || src === Visibility.PublicAndPrivate);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface ProtoInjectorStrategy {
|
export interface ProtoInjectorStrategy {
|
||||||
getProviderAtIndex(index: number): ResolvedProvider;
|
getProviderAtIndex(index: number): ResolvedProvider;
|
||||||
createInjectorStrategy(inj: Injector): InjectorStrategy;
|
createInjectorStrategy(inj: Injector_): InjectorStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
||||||
|
@ -78,69 +56,48 @@ export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
||||||
keyId8: number = null;
|
keyId8: number = null;
|
||||||
keyId9: number = null;
|
keyId9: number = null;
|
||||||
|
|
||||||
visibility0: Visibility = null;
|
constructor(protoEI: ProtoInjector, providers: ResolvedProvider[]) {
|
||||||
visibility1: Visibility = null;
|
var length = providers.length;
|
||||||
visibility2: Visibility = null;
|
|
||||||
visibility3: Visibility = null;
|
|
||||||
visibility4: Visibility = null;
|
|
||||||
visibility5: Visibility = null;
|
|
||||||
visibility6: Visibility = null;
|
|
||||||
visibility7: Visibility = null;
|
|
||||||
visibility8: Visibility = null;
|
|
||||||
visibility9: Visibility = null;
|
|
||||||
|
|
||||||
constructor(protoEI: ProtoInjector, bwv: ProviderWithVisibility[]) {
|
|
||||||
var length = bwv.length;
|
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
this.provider0 = bwv[0].provider;
|
this.provider0 = providers[0];
|
||||||
this.keyId0 = bwv[0].getKeyId();
|
this.keyId0 = providers[0].key.id;
|
||||||
this.visibility0 = bwv[0].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 1) {
|
if (length > 1) {
|
||||||
this.provider1 = bwv[1].provider;
|
this.provider1 = providers[1];
|
||||||
this.keyId1 = bwv[1].getKeyId();
|
this.keyId1 = providers[1].key.id;
|
||||||
this.visibility1 = bwv[1].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 2) {
|
if (length > 2) {
|
||||||
this.provider2 = bwv[2].provider;
|
this.provider2 = providers[2];
|
||||||
this.keyId2 = bwv[2].getKeyId();
|
this.keyId2 = providers[2].key.id;
|
||||||
this.visibility2 = bwv[2].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 3) {
|
if (length > 3) {
|
||||||
this.provider3 = bwv[3].provider;
|
this.provider3 = providers[3];
|
||||||
this.keyId3 = bwv[3].getKeyId();
|
this.keyId3 = providers[3].key.id;
|
||||||
this.visibility3 = bwv[3].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 4) {
|
if (length > 4) {
|
||||||
this.provider4 = bwv[4].provider;
|
this.provider4 = providers[4];
|
||||||
this.keyId4 = bwv[4].getKeyId();
|
this.keyId4 = providers[4].key.id;
|
||||||
this.visibility4 = bwv[4].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 5) {
|
if (length > 5) {
|
||||||
this.provider5 = bwv[5].provider;
|
this.provider5 = providers[5];
|
||||||
this.keyId5 = bwv[5].getKeyId();
|
this.keyId5 = providers[5].key.id;
|
||||||
this.visibility5 = bwv[5].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 6) {
|
if (length > 6) {
|
||||||
this.provider6 = bwv[6].provider;
|
this.provider6 = providers[6];
|
||||||
this.keyId6 = bwv[6].getKeyId();
|
this.keyId6 = providers[6].key.id;
|
||||||
this.visibility6 = bwv[6].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 7) {
|
if (length > 7) {
|
||||||
this.provider7 = bwv[7].provider;
|
this.provider7 = providers[7];
|
||||||
this.keyId7 = bwv[7].getKeyId();
|
this.keyId7 = providers[7].key.id;
|
||||||
this.visibility7 = bwv[7].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 8) {
|
if (length > 8) {
|
||||||
this.provider8 = bwv[8].provider;
|
this.provider8 = providers[8];
|
||||||
this.keyId8 = bwv[8].getKeyId();
|
this.keyId8 = providers[8].key.id;
|
||||||
this.visibility8 = bwv[8].visibility;
|
|
||||||
}
|
}
|
||||||
if (length > 9) {
|
if (length > 9) {
|
||||||
this.provider9 = bwv[9].provider;
|
this.provider9 = providers[9];
|
||||||
this.keyId9 = bwv[9].getKeyId();
|
this.keyId9 = providers[9].key.id;
|
||||||
this.visibility9 = bwv[9].visibility;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,27 +115,21 @@ export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
||||||
throw new OutOfBoundsError(index);
|
throw new OutOfBoundsError(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
createInjectorStrategy(injector: Injector): InjectorStrategy {
|
createInjectorStrategy(injector: Injector_): InjectorStrategy {
|
||||||
return new InjectorInlineStrategy(injector, this);
|
return new InjectorInlineStrategy(injector, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||||
providers: ResolvedProvider[];
|
|
||||||
keyIds: number[];
|
keyIds: number[];
|
||||||
visibilities: Visibility[];
|
|
||||||
|
|
||||||
constructor(protoInj: ProtoInjector, bwv: ProviderWithVisibility[]) {
|
constructor(protoInj: ProtoInjector, public providers: ResolvedProvider[]) {
|
||||||
var len = bwv.length;
|
var len = providers.length;
|
||||||
|
|
||||||
this.providers = ListWrapper.createFixedSize(len);
|
|
||||||
this.keyIds = ListWrapper.createFixedSize(len);
|
this.keyIds = ListWrapper.createFixedSize(len);
|
||||||
this.visibilities = ListWrapper.createFixedSize(len);
|
|
||||||
|
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
this.providers[i] = bwv[i].provider;
|
this.keyIds[i] = providers[i].key.id;
|
||||||
this.keyIds[i] = bwv[i].getKeyId();
|
|
||||||
this.visibilities[i] = bwv[i].visibility;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,26 +140,25 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||||
return this.providers[index];
|
return this.providers[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
createInjectorStrategy(ei: Injector): InjectorStrategy {
|
createInjectorStrategy(ei: Injector_): InjectorStrategy {
|
||||||
return new InjectorDynamicStrategy(this, ei);
|
return new InjectorDynamicStrategy(this, ei);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProtoInjector {
|
export class ProtoInjector {
|
||||||
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
|
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
|
||||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
return new ProtoInjector(providers);
|
||||||
return new ProtoInjector(bd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_strategy: ProtoInjectorStrategy;
|
_strategy: ProtoInjectorStrategy;
|
||||||
numberOfProviders: number;
|
numberOfProviders: number;
|
||||||
|
|
||||||
constructor(bwv: ProviderWithVisibility[]) {
|
constructor(providers: ResolvedProvider[]) {
|
||||||
this.numberOfProviders = bwv.length;
|
this.numberOfProviders = providers.length;
|
||||||
this._strategy = bwv.length > _MAX_CONSTRUCTION_COUNTER ?
|
this._strategy = providers.length > _MAX_CONSTRUCTION_COUNTER ?
|
||||||
new ProtoInjectorDynamicStrategy(this, bwv) :
|
new ProtoInjectorDynamicStrategy(this, providers) :
|
||||||
new ProtoInjectorInlineStrategy(this, bwv);
|
new ProtoInjectorInlineStrategy(this, providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviderAtIndex(index: number): ResolvedProvider {
|
getProviderAtIndex(index: number): ResolvedProvider {
|
||||||
|
@ -219,12 +169,12 @@ export class ProtoInjector {
|
||||||
|
|
||||||
|
|
||||||
export interface InjectorStrategy {
|
export interface InjectorStrategy {
|
||||||
getObjByKeyId(keyId: number, visibility: Visibility): any;
|
getObjByKeyId(keyId: number): any;
|
||||||
getObjAtIndex(index: number): any;
|
getObjAtIndex(index: number): any;
|
||||||
getMaxNumberOfObjects(): number;
|
getMaxNumberOfObjects(): number;
|
||||||
|
|
||||||
resetConstructionCounter(): void;
|
resetConstructionCounter(): void;
|
||||||
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
|
instantiateProvider(provider: ResolvedProvider): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InjectorInlineStrategy implements InjectorStrategy {
|
export class InjectorInlineStrategy implements InjectorStrategy {
|
||||||
|
@ -239,75 +189,73 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
||||||
obj8: any = UNDEFINED;
|
obj8: any = UNDEFINED;
|
||||||
obj9: any = UNDEFINED;
|
obj9: any = UNDEFINED;
|
||||||
|
|
||||||
constructor(public injector: Injector, public protoStrategy: ProtoInjectorInlineStrategy) {}
|
constructor(public injector: Injector_, public protoStrategy: ProtoInjectorInlineStrategy) {}
|
||||||
|
|
||||||
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||||
|
|
||||||
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any {
|
instantiateProvider(provider: ResolvedProvider): any { return this.injector._new(provider); }
|
||||||
return this.injector._new(provider, visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
getObjByKeyId(keyId: number): any {
|
||||||
var p = this.protoStrategy;
|
var p = this.protoStrategy;
|
||||||
var inj = this.injector;
|
var inj = this.injector;
|
||||||
|
|
||||||
if (p.keyId0 === keyId && canSee(p.visibility0, visibility)) {
|
if (p.keyId0 === keyId) {
|
||||||
if (this.obj0 === UNDEFINED) {
|
if (this.obj0 === UNDEFINED) {
|
||||||
this.obj0 = inj._new(p.provider0, p.visibility0);
|
this.obj0 = inj._new(p.provider0);
|
||||||
}
|
}
|
||||||
return this.obj0;
|
return this.obj0;
|
||||||
}
|
}
|
||||||
if (p.keyId1 === keyId && canSee(p.visibility1, visibility)) {
|
if (p.keyId1 === keyId) {
|
||||||
if (this.obj1 === UNDEFINED) {
|
if (this.obj1 === UNDEFINED) {
|
||||||
this.obj1 = inj._new(p.provider1, p.visibility1);
|
this.obj1 = inj._new(p.provider1);
|
||||||
}
|
}
|
||||||
return this.obj1;
|
return this.obj1;
|
||||||
}
|
}
|
||||||
if (p.keyId2 === keyId && canSee(p.visibility2, visibility)) {
|
if (p.keyId2 === keyId) {
|
||||||
if (this.obj2 === UNDEFINED) {
|
if (this.obj2 === UNDEFINED) {
|
||||||
this.obj2 = inj._new(p.provider2, p.visibility2);
|
this.obj2 = inj._new(p.provider2);
|
||||||
}
|
}
|
||||||
return this.obj2;
|
return this.obj2;
|
||||||
}
|
}
|
||||||
if (p.keyId3 === keyId && canSee(p.visibility3, visibility)) {
|
if (p.keyId3 === keyId) {
|
||||||
if (this.obj3 === UNDEFINED) {
|
if (this.obj3 === UNDEFINED) {
|
||||||
this.obj3 = inj._new(p.provider3, p.visibility3);
|
this.obj3 = inj._new(p.provider3);
|
||||||
}
|
}
|
||||||
return this.obj3;
|
return this.obj3;
|
||||||
}
|
}
|
||||||
if (p.keyId4 === keyId && canSee(p.visibility4, visibility)) {
|
if (p.keyId4 === keyId) {
|
||||||
if (this.obj4 === UNDEFINED) {
|
if (this.obj4 === UNDEFINED) {
|
||||||
this.obj4 = inj._new(p.provider4, p.visibility4);
|
this.obj4 = inj._new(p.provider4);
|
||||||
}
|
}
|
||||||
return this.obj4;
|
return this.obj4;
|
||||||
}
|
}
|
||||||
if (p.keyId5 === keyId && canSee(p.visibility5, visibility)) {
|
if (p.keyId5 === keyId) {
|
||||||
if (this.obj5 === UNDEFINED) {
|
if (this.obj5 === UNDEFINED) {
|
||||||
this.obj5 = inj._new(p.provider5, p.visibility5);
|
this.obj5 = inj._new(p.provider5);
|
||||||
}
|
}
|
||||||
return this.obj5;
|
return this.obj5;
|
||||||
}
|
}
|
||||||
if (p.keyId6 === keyId && canSee(p.visibility6, visibility)) {
|
if (p.keyId6 === keyId) {
|
||||||
if (this.obj6 === UNDEFINED) {
|
if (this.obj6 === UNDEFINED) {
|
||||||
this.obj6 = inj._new(p.provider6, p.visibility6);
|
this.obj6 = inj._new(p.provider6);
|
||||||
}
|
}
|
||||||
return this.obj6;
|
return this.obj6;
|
||||||
}
|
}
|
||||||
if (p.keyId7 === keyId && canSee(p.visibility7, visibility)) {
|
if (p.keyId7 === keyId) {
|
||||||
if (this.obj7 === UNDEFINED) {
|
if (this.obj7 === UNDEFINED) {
|
||||||
this.obj7 = inj._new(p.provider7, p.visibility7);
|
this.obj7 = inj._new(p.provider7);
|
||||||
}
|
}
|
||||||
return this.obj7;
|
return this.obj7;
|
||||||
}
|
}
|
||||||
if (p.keyId8 === keyId && canSee(p.visibility8, visibility)) {
|
if (p.keyId8 === keyId) {
|
||||||
if (this.obj8 === UNDEFINED) {
|
if (this.obj8 === UNDEFINED) {
|
||||||
this.obj8 = inj._new(p.provider8, p.visibility8);
|
this.obj8 = inj._new(p.provider8);
|
||||||
}
|
}
|
||||||
return this.obj8;
|
return this.obj8;
|
||||||
}
|
}
|
||||||
if (p.keyId9 === keyId && canSee(p.visibility9, visibility)) {
|
if (p.keyId9 === keyId) {
|
||||||
if (this.obj9 === UNDEFINED) {
|
if (this.obj9 === UNDEFINED) {
|
||||||
this.obj9 = inj._new(p.provider9, p.visibility9);
|
this.obj9 = inj._new(p.provider9);
|
||||||
}
|
}
|
||||||
return this.obj9;
|
return this.obj9;
|
||||||
}
|
}
|
||||||
|
@ -336,24 +284,22 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
||||||
export class InjectorDynamicStrategy implements InjectorStrategy {
|
export class InjectorDynamicStrategy implements InjectorStrategy {
|
||||||
objs: any[];
|
objs: any[];
|
||||||
|
|
||||||
constructor(public protoStrategy: ProtoInjectorDynamicStrategy, public injector: Injector) {
|
constructor(public protoStrategy: ProtoInjectorDynamicStrategy, public injector: Injector_) {
|
||||||
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
|
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
|
||||||
ListWrapper.fill(this.objs, UNDEFINED);
|
ListWrapper.fill(this.objs, UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
resetConstructionCounter(): void { this.injector._constructionCounter = 0; }
|
||||||
|
|
||||||
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any {
|
instantiateProvider(provider: ResolvedProvider): any { return this.injector._new(provider); }
|
||||||
return this.injector._new(provider, visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
getObjByKeyId(keyId: number): any {
|
||||||
var p = this.protoStrategy;
|
var p = this.protoStrategy;
|
||||||
|
|
||||||
for (var i = 0; i < p.keyIds.length; i++) {
|
for (var i = 0; i < p.keyIds.length; i++) {
|
||||||
if (p.keyIds[i] === keyId && canSee(p.visibilities[i], visibility)) {
|
if (p.keyIds[i] === keyId) {
|
||||||
if (this.objs[i] === UNDEFINED) {
|
if (this.objs[i] === UNDEFINED) {
|
||||||
this.objs[i] = this.injector._new(p.providers[i], p.visibilities[i]);
|
this.objs[i] = this.injector._new(p.providers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.objs[i];
|
return this.objs[i];
|
||||||
|
@ -374,12 +320,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
|
||||||
getMaxNumberOfObjects(): number { return this.objs.length; }
|
getMaxNumberOfObjects(): number { return this.objs.length; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProviderWithVisibility {
|
|
||||||
constructor(public provider: ResolvedProvider, public visibility: Visibility){};
|
|
||||||
|
|
||||||
getKeyId(): number { return this.provider.key.id; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to provide dependencies that cannot be easily expressed as providers.
|
* Used to provide dependencies that cannot be easily expressed as providers.
|
||||||
*/
|
*/
|
||||||
|
@ -387,39 +327,7 @@ export interface DependencyProvider {
|
||||||
getDependency(injector: Injector, provider: ResolvedProvider, dependency: Dependency): any;
|
getDependency(injector: Injector, provider: ResolvedProvider, dependency: Dependency): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export abstract class Injector {
|
||||||
* A dependency injection container used for instantiating objects and resolving dependencies.
|
|
||||||
*
|
|
||||||
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
|
||||||
* constructor dependencies.
|
|
||||||
*
|
|
||||||
* In typical use, application code asks for the dependencies in the constructor and they are
|
|
||||||
* resolved by the `Injector`.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
|
|
||||||
*
|
|
||||||
* The following example creates an `Injector` configured to create `Engine` and `Car`.
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Injectable()
|
|
||||||
* class Engine {
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Injectable()
|
|
||||||
* class Car {
|
|
||||||
* constructor(public engine:Engine) {}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var injector = Injector.resolveAndCreate([Car, Engine]);
|
|
||||||
* var car = injector.get(Car);
|
|
||||||
* expect(car instanceof Car).toBe(true);
|
|
||||||
* expect(car.engine instanceof Engine).toBe(true);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
|
|
||||||
* resolve all of the object's dependencies automatically.
|
|
||||||
*/
|
|
||||||
export class Injector {
|
|
||||||
/**
|
/**
|
||||||
* Turns an array of provider definitions into an array of resolved providers.
|
* Turns an array of provider definitions into an array of resolved providers.
|
||||||
*
|
*
|
||||||
|
@ -511,7 +419,7 @@ export class Injector {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
|
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
|
||||||
return new Injector(ProtoInjector.fromResolvedProviders(providers));
|
return new Injector_(ProtoInjector.fromResolvedProviders(providers));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -521,37 +429,6 @@ export class Injector {
|
||||||
return Injector.fromResolvedProviders(providers);
|
return Injector.fromResolvedProviders(providers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_strategy: InjectorStrategy;
|
|
||||||
/** @internal */
|
|
||||||
_constructionCounter: number = 0;
|
|
||||||
/** @internal */
|
|
||||||
public _proto: any /* ProtoInjector */;
|
|
||||||
/** @internal */
|
|
||||||
public _parent: Injector;
|
|
||||||
/**
|
|
||||||
* Private
|
|
||||||
*/
|
|
||||||
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
|
||||||
private _isHostBoundary: boolean = false,
|
|
||||||
private _depProvider: any /* DependencyProvider */ = null,
|
|
||||||
private _debugContext: Function = null) {
|
|
||||||
this._proto = _proto;
|
|
||||||
this._parent = _parent;
|
|
||||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this injector is a boundary to a host.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
get hostBoundary() { return this._isHostBoundary; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
debugContext(): any { return this._debugContext(); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an instance from the injector based on the provided token.
|
* Retrieves an instance from the injector based on the provided token.
|
||||||
* Throws {@link NoProviderError} if not found.
|
* Throws {@link NoProviderError} if not found.
|
||||||
|
@ -573,9 +450,7 @@ export class Injector {
|
||||||
* expect(injector.get(Injector)).toBe(injector);
|
* expect(injector.get(Injector)).toBe(injector);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
get(token: any): any {
|
get(token: any): any { return unimplemented(); }
|
||||||
return this._getByKey(Key.get(token), null, null, false, Visibility.PublicAndPrivate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves an instance from the injector based on the provided token.
|
* Retrieves an instance from the injector based on the provided token.
|
||||||
|
@ -598,14 +473,7 @@ export class Injector {
|
||||||
* expect(injector.getOptional(Injector)).toBe(injector);
|
* expect(injector.getOptional(Injector)).toBe(injector);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
getOptional(token: any): any {
|
getOptional(token: any): any { return unimplemented(); }
|
||||||
return this._getByKey(Key.get(token), null, null, true, Visibility.PublicAndPrivate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parent of this injector.
|
* Parent of this injector.
|
||||||
|
@ -621,14 +489,12 @@ export class Injector {
|
||||||
* expect(child.parent).toBe(parent);
|
* expect(child.parent).toBe(parent);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
get parent(): Injector { return this._parent; }
|
get parent(): Injector { return unimplemented(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
* Internal. Do not use.
|
|
||||||
* We return `any` not to export the InjectorStrategy type.
|
|
||||||
*/
|
*/
|
||||||
get internalStrategy(): any { return this._strategy; }
|
debugContext(): any { return null; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves an array of providers and creates a child injector from those providers.
|
* Resolves an array of providers and creates a child injector from those providers.
|
||||||
|
@ -658,8 +524,7 @@ export class Injector {
|
||||||
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
|
* See {@link Injector#resolve} and {@link Injector#createChildFromResolved}.
|
||||||
*/
|
*/
|
||||||
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): Injector {
|
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): Injector {
|
||||||
var resolvedProviders = Injector.resolve(providers);
|
return unimplemented();
|
||||||
return this.createChildFromResolved(resolvedProviders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -687,13 +552,7 @@ export class Injector {
|
||||||
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
|
* expect(child.get(ParentProvider)).toBe(parent.get(ParentProvider));
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
createChildFromResolved(providers: ResolvedProvider[]): Injector {
|
createChildFromResolved(providers: ResolvedProvider[]): Injector { return unimplemented(); }
|
||||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
|
||||||
var proto = new ProtoInjector(bd);
|
|
||||||
var inj = new Injector(proto);
|
|
||||||
inj._parent = this;
|
|
||||||
return inj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a provider and instantiates an object in the context of the injector.
|
* Resolves a provider and instantiates an object in the context of the injector.
|
||||||
|
@ -719,9 +578,7 @@ export class Injector {
|
||||||
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
|
* expect(car).not.toBe(injector.resolveAndInstantiate(Car));
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
resolveAndInstantiate(provider: Type | Provider): any {
|
resolveAndInstantiate(provider: Type | Provider): any { return unimplemented(); }
|
||||||
return this.instantiateResolved(Injector.resolve([provider])[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates an object using a resolved provider in the context of the injector.
|
* Instantiates an object using a resolved provider in the context of the injector.
|
||||||
|
@ -747,32 +604,121 @@ export class Injector {
|
||||||
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
|
* expect(car).not.toBe(injector.instantiateResolved(carProvider));
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
|
instantiateResolved(provider: ResolvedProvider): any { return unimplemented(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dependency injection container used for instantiating objects and resolving dependencies.
|
||||||
|
*
|
||||||
|
* An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
||||||
|
* constructor dependencies.
|
||||||
|
*
|
||||||
|
* In typical use, application code asks for the dependencies in the constructor and they are
|
||||||
|
* resolved by the `Injector`.
|
||||||
|
*
|
||||||
|
* ### Example ([live demo](http://plnkr.co/edit/jzjec0?p=preview))
|
||||||
|
*
|
||||||
|
* The following example creates an `Injector` configured to create `Engine` and `Car`.
|
||||||
|
*
|
||||||
|
* ```typescript
|
||||||
|
* @Injectable()
|
||||||
|
* class Engine {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @Injectable()
|
||||||
|
* class Car {
|
||||||
|
* constructor(public engine:Engine) {}
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* var injector = Injector.resolveAndCreate([Car, Engine]);
|
||||||
|
* var car = injector.get(Car);
|
||||||
|
* expect(car instanceof Car).toBe(true);
|
||||||
|
* expect(car.engine instanceof Engine).toBe(true);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
|
||||||
|
* resolve all of the object's dependencies automatically.
|
||||||
|
*/
|
||||||
|
export class Injector_ implements Injector {
|
||||||
|
/** @internal */
|
||||||
|
_strategy: InjectorStrategy;
|
||||||
|
/** @internal */
|
||||||
|
_constructionCounter: number = 0;
|
||||||
|
/** @internal */
|
||||||
|
public _proto: any /* ProtoInjector */;
|
||||||
|
/** @internal */
|
||||||
|
public _parent: Injector;
|
||||||
|
/**
|
||||||
|
* Private
|
||||||
|
*/
|
||||||
|
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
||||||
|
private _debugContext: Function = null) {
|
||||||
|
this._proto = _proto;
|
||||||
|
this._parent = _parent;
|
||||||
|
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
debugContext(): any { return this._debugContext(); }
|
||||||
|
|
||||||
|
get(token: any): any { return this._getByKey(Key.get(token), null, null, false); }
|
||||||
|
|
||||||
|
getOptional(token: any): any { return this._getByKey(Key.get(token), null, null, true); }
|
||||||
|
|
||||||
|
getAt(index: number): any { return this._strategy.getObjAtIndex(index); }
|
||||||
|
|
||||||
|
get parent(): Injector { return this._parent; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* Internal. Do not use.
|
||||||
|
* We return `any` not to export the InjectorStrategy type.
|
||||||
|
*/
|
||||||
|
get internalStrategy(): any { return this._strategy; }
|
||||||
|
|
||||||
|
resolveAndCreateChild(providers: Array<Type | Provider | any[]>): Injector {
|
||||||
|
var resolvedProviders = Injector.resolve(providers);
|
||||||
|
return this.createChildFromResolved(resolvedProviders);
|
||||||
|
}
|
||||||
|
|
||||||
|
createChildFromResolved(providers: ResolvedProvider[]): Injector {
|
||||||
|
var proto = new ProtoInjector(providers);
|
||||||
|
var inj = new Injector_(proto);
|
||||||
|
inj._parent = this;
|
||||||
|
return inj;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveAndInstantiate(provider: Type | Provider): any {
|
||||||
|
return this.instantiateResolved(Injector.resolve([provider])[0]);
|
||||||
|
}
|
||||||
|
|
||||||
instantiateResolved(provider: ResolvedProvider): any {
|
instantiateResolved(provider: ResolvedProvider): any {
|
||||||
return this._instantiateProvider(provider, Visibility.PublicAndPrivate);
|
return this._instantiateProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_new(provider: ResolvedProvider, visibility: Visibility): any {
|
_new(provider: ResolvedProvider): any {
|
||||||
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
|
if (this._constructionCounter++ > this._strategy.getMaxNumberOfObjects()) {
|
||||||
throw new CyclicDependencyError(this, provider.key);
|
throw new CyclicDependencyError(this, provider.key);
|
||||||
}
|
}
|
||||||
return this._instantiateProvider(provider, visibility);
|
return this._instantiateProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any {
|
private _instantiateProvider(provider: ResolvedProvider): any {
|
||||||
if (provider.multiProvider) {
|
if (provider.multiProvider) {
|
||||||
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
|
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
|
||||||
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
|
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
|
||||||
res[i] = this._instantiate(provider, provider.resolvedFactories[i], visibility);
|
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
return this._instantiate(provider, provider.resolvedFactories[0], visibility);
|
return this._instantiate(provider, provider.resolvedFactories[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _instantiate(provider: ResolvedProvider, resolvedFactory: ResolvedFactory,
|
private _instantiate(provider: ResolvedProvider, resolvedFactory: ResolvedFactory): any {
|
||||||
visibility: Visibility): any {
|
|
||||||
var factory = resolvedFactory.factory;
|
var factory = resolvedFactory.factory;
|
||||||
var deps = resolvedFactory.dependencies;
|
var deps = resolvedFactory.dependencies;
|
||||||
var length = deps.length;
|
var length = deps.length;
|
||||||
|
@ -798,26 +744,26 @@ export class Injector {
|
||||||
var d18: any;
|
var d18: any;
|
||||||
var d19: any;
|
var d19: any;
|
||||||
try {
|
try {
|
||||||
d0 = length > 0 ? this._getByDependency(provider, deps[0], visibility) : null;
|
d0 = length > 0 ? this._getByDependency(provider, deps[0]) : null;
|
||||||
d1 = length > 1 ? this._getByDependency(provider, deps[1], visibility) : null;
|
d1 = length > 1 ? this._getByDependency(provider, deps[1]) : null;
|
||||||
d2 = length > 2 ? this._getByDependency(provider, deps[2], visibility) : null;
|
d2 = length > 2 ? this._getByDependency(provider, deps[2]) : null;
|
||||||
d3 = length > 3 ? this._getByDependency(provider, deps[3], visibility) : null;
|
d3 = length > 3 ? this._getByDependency(provider, deps[3]) : null;
|
||||||
d4 = length > 4 ? this._getByDependency(provider, deps[4], visibility) : null;
|
d4 = length > 4 ? this._getByDependency(provider, deps[4]) : null;
|
||||||
d5 = length > 5 ? this._getByDependency(provider, deps[5], visibility) : null;
|
d5 = length > 5 ? this._getByDependency(provider, deps[5]) : null;
|
||||||
d6 = length > 6 ? this._getByDependency(provider, deps[6], visibility) : null;
|
d6 = length > 6 ? this._getByDependency(provider, deps[6]) : null;
|
||||||
d7 = length > 7 ? this._getByDependency(provider, deps[7], visibility) : null;
|
d7 = length > 7 ? this._getByDependency(provider, deps[7]) : null;
|
||||||
d8 = length > 8 ? this._getByDependency(provider, deps[8], visibility) : null;
|
d8 = length > 8 ? this._getByDependency(provider, deps[8]) : null;
|
||||||
d9 = length > 9 ? this._getByDependency(provider, deps[9], visibility) : null;
|
d9 = length > 9 ? this._getByDependency(provider, deps[9]) : null;
|
||||||
d10 = length > 10 ? this._getByDependency(provider, deps[10], visibility) : null;
|
d10 = length > 10 ? this._getByDependency(provider, deps[10]) : null;
|
||||||
d11 = length > 11 ? this._getByDependency(provider, deps[11], visibility) : null;
|
d11 = length > 11 ? this._getByDependency(provider, deps[11]) : null;
|
||||||
d12 = length > 12 ? this._getByDependency(provider, deps[12], visibility) : null;
|
d12 = length > 12 ? this._getByDependency(provider, deps[12]) : null;
|
||||||
d13 = length > 13 ? this._getByDependency(provider, deps[13], visibility) : null;
|
d13 = length > 13 ? this._getByDependency(provider, deps[13]) : null;
|
||||||
d14 = length > 14 ? this._getByDependency(provider, deps[14], visibility) : null;
|
d14 = length > 14 ? this._getByDependency(provider, deps[14]) : null;
|
||||||
d15 = length > 15 ? this._getByDependency(provider, deps[15], visibility) : null;
|
d15 = length > 15 ? this._getByDependency(provider, deps[15]) : null;
|
||||||
d16 = length > 16 ? this._getByDependency(provider, deps[16], visibility) : null;
|
d16 = length > 16 ? this._getByDependency(provider, deps[16]) : null;
|
||||||
d17 = length > 17 ? this._getByDependency(provider, deps[17], visibility) : null;
|
d17 = length > 17 ? this._getByDependency(provider, deps[17]) : null;
|
||||||
d18 = length > 18 ? this._getByDependency(provider, deps[18], visibility) : null;
|
d18 = length > 18 ? this._getByDependency(provider, deps[18]) : null;
|
||||||
d19 = length > 19 ? this._getByDependency(provider, deps[19], visibility) : null;
|
d19 = length > 19 ? this._getByDependency(provider, deps[19]) : null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
|
if (e instanceof AbstractProviderError || e instanceof InstantiationError) {
|
||||||
e.addKey(this, provider.key);
|
e.addKey(this, provider.key);
|
||||||
|
@ -904,33 +850,22 @@ export class Injector {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getByDependency(provider: ResolvedProvider, dep: Dependency,
|
private _getByDependency(provider: ResolvedProvider, dep: Dependency): any {
|
||||||
providerVisibility: Visibility): any {
|
return this._getByKey(dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
|
||||||
var special = isPresent(this._depProvider) ?
|
dep.optional);
|
||||||
this._depProvider.getDependency(this, provider, dep) :
|
|
||||||
UNDEFINED;
|
|
||||||
if (special !== UNDEFINED) {
|
|
||||||
return special;
|
|
||||||
} else {
|
|
||||||
return this._getByKey(dep.key, dep.lowerBoundVisibility, dep.upperBoundVisibility,
|
|
||||||
dep.optional, providerVisibility);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getByKey(key: Key, lowerBoundVisibility: Object, upperBoundVisibility: Object,
|
private _getByKey(key: Key, lowerBoundVisibility: Object, upperBoundVisibility: Object,
|
||||||
optional: boolean, providerVisibility: Visibility): any {
|
optional: boolean): any {
|
||||||
if (key === INJECTOR_KEY) {
|
if (key === INJECTOR_KEY) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (upperBoundVisibility instanceof SelfMetadata) {
|
if (upperBoundVisibility instanceof SelfMetadata) {
|
||||||
return this._getByKeySelf(key, optional, providerVisibility);
|
return this._getByKeySelf(key, optional);
|
||||||
|
|
||||||
} else if (upperBoundVisibility instanceof HostMetadata) {
|
|
||||||
return this._getByKeyHost(key, optional, providerVisibility, lowerBoundVisibility);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return this._getByKeyDefault(key, optional, providerVisibility, lowerBoundVisibility);
|
return this._getByKeyDefault(key, optional, lowerBoundVisibility);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,63 +879,36 @@ export class Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_getByKeySelf(key: Key, optional: boolean, providerVisibility: Visibility): any {
|
_getByKeySelf(key: Key, optional: boolean): any {
|
||||||
var obj = this._strategy.getObjByKeyId(key.id, providerVisibility);
|
var obj = this._strategy.getObjByKeyId(key.id);
|
||||||
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, optional);
|
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, optional);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_getByKeyHost(key: Key, optional: boolean, providerVisibility: Visibility,
|
_getByKeyDefault(key: Key, optional: boolean, lowerBoundVisibility: Object): any {
|
||||||
lowerBoundVisibility: Object): any {
|
var inj: Injector;
|
||||||
var inj: Injector = this;
|
|
||||||
|
|
||||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||||
if (inj._isHostBoundary) {
|
inj = this._parent;
|
||||||
return this._getPrivateDependency(key, optional, inj);
|
} else {
|
||||||
} else {
|
inj = this;
|
||||||
inj = inj._parent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (inj != null) {
|
while (inj instanceof Injector_) {
|
||||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
var inj_ = <Injector_>inj;
|
||||||
|
var obj = inj_._strategy.getObjByKeyId(key.id);
|
||||||
if (obj !== UNDEFINED) return obj;
|
if (obj !== UNDEFINED) return obj;
|
||||||
|
inj = inj_._parent;
|
||||||
if (isPresent(inj._parent) && inj._isHostBoundary) {
|
}
|
||||||
return this._getPrivateDependency(key, optional, inj);
|
if (inj !== null) {
|
||||||
|
if (optional) {
|
||||||
|
return inj.getOptional(key.token);
|
||||||
} else {
|
} else {
|
||||||
inj = inj._parent;
|
return inj.get(key.token);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return this._throwOrNull(key, optional);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._throwOrNull(key, optional);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_getPrivateDependency(key: Key, optional: boolean, inj: Injector): any {
|
|
||||||
var obj = inj._parent._strategy.getObjByKeyId(key.id, Visibility.Private);
|
|
||||||
return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, optional);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
_getByKeyDefault(key: Key, optional: boolean, providerVisibility: Visibility,
|
|
||||||
lowerBoundVisibility: Object): any {
|
|
||||||
var inj: Injector = this;
|
|
||||||
|
|
||||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
|
||||||
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
|
||||||
inj = inj._parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (inj != null) {
|
|
||||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
|
||||||
if (obj !== UNDEFINED) return obj;
|
|
||||||
|
|
||||||
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
|
||||||
inj = inj._parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._throwOrNull(key, optional);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayName(): string {
|
get displayName(): string {
|
||||||
|
@ -1012,8 +920,7 @@ export class Injector {
|
||||||
|
|
||||||
var INJECTOR_KEY = Key.get(Injector);
|
var INJECTOR_KEY = Key.get(Injector);
|
||||||
|
|
||||||
|
function _mapProviders(injector: Injector_, fn: Function): any[] {
|
||||||
function _mapProviders(injector: Injector, fn: Function): any[] {
|
|
||||||
var res = [];
|
var res = [];
|
||||||
for (var i = 0; i < injector._proto.numberOfProviders; ++i) {
|
for (var i = 0; i < injector._proto.numberOfProviders; ++i) {
|
||||||
res.push(fn(injector._proto.getProviderAtIndex(i)));
|
res.push(fn(injector._proto.getProviderAtIndex(i)));
|
||||||
|
|
|
@ -513,7 +513,7 @@ export class ProviderBuilder {
|
||||||
*/
|
*/
|
||||||
export function resolveFactory(provider: Provider): ResolvedFactory {
|
export function resolveFactory(provider: Provider): ResolvedFactory {
|
||||||
var factoryFn: Function;
|
var factoryFn: Function;
|
||||||
var resolvedDeps: Dependency[];
|
var resolvedDeps;
|
||||||
if (isPresent(provider.useClass)) {
|
if (isPresent(provider.useClass)) {
|
||||||
var useClass = resolveForwardRef(provider.useClass);
|
var useClass = resolveForwardRef(provider.useClass);
|
||||||
factoryFn = reflector.factory(useClass);
|
factoryFn = reflector.factory(useClass);
|
||||||
|
@ -523,7 +523,7 @@ export function resolveFactory(provider: Provider): ResolvedFactory {
|
||||||
resolvedDeps = [Dependency.fromKey(Key.get(provider.useExisting))];
|
resolvedDeps = [Dependency.fromKey(Key.get(provider.useExisting))];
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (isPresent(provider.useFactory)) {
|
||||||
factoryFn = provider.useFactory;
|
factoryFn = provider.useFactory;
|
||||||
resolvedDeps = _constructDependencies(provider.useFactory, provider.dependencies);
|
resolvedDeps = constructDependencies(provider.useFactory, provider.dependencies);
|
||||||
} else {
|
} else {
|
||||||
factoryFn = () => provider.useValue;
|
factoryFn = () => provider.useValue;
|
||||||
resolvedDeps = _EMPTY_LIST;
|
resolvedDeps = _EMPTY_LIST;
|
||||||
|
@ -609,17 +609,17 @@ function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
|
export function constructDependencies(typeOrFunc: any, dependencies: any[]): Dependency[] {
|
||||||
if (isBlank(dependencies)) {
|
if (isBlank(dependencies)) {
|
||||||
return _dependenciesFor(factoryFunction);
|
return _dependenciesFor(typeOrFunc);
|
||||||
} else {
|
} else {
|
||||||
var params: any[][] = dependencies.map(t => [t]);
|
var params: any[][] = dependencies.map(t => [t]);
|
||||||
return dependencies.map(t => _extractToken(factoryFunction, t, params));
|
return dependencies.map(t => _extractToken(typeOrFunc, t, params));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _dependenciesFor(typeOrFunc): Dependency[] {
|
function _dependenciesFor(typeOrFunc: any): Dependency[] {
|
||||||
var params: any[][] = reflector.parameters(typeOrFunc);
|
var params = reflector.parameters(typeOrFunc);
|
||||||
if (isBlank(params)) return [];
|
if (isBlank(params)) return [];
|
||||||
if (params.some(isBlank)) {
|
if (params.some(isBlank)) {
|
||||||
throw new NoAnnotationError(typeOrFunc, params);
|
throw new NoAnnotationError(typeOrFunc, params);
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
library angular2.di.type_info;
|
|
||||||
|
|
||||||
// In dart always return empty, as we can get the co
|
|
||||||
argsLength(Type type) => 0;
|
|
|
@ -1,16 +1,4 @@
|
||||||
// Public API for compiler
|
// Public API for compiler
|
||||||
export {
|
|
||||||
AfterContentInit,
|
|
||||||
AfterContentChecked,
|
|
||||||
AfterViewInit,
|
|
||||||
AfterViewChecked,
|
|
||||||
OnChanges,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
DoCheck
|
|
||||||
} from './linker/interfaces';
|
|
||||||
export {DirectiveResolver} from './linker/directive_resolver';
|
|
||||||
export {ViewResolver} from './linker/view_resolver';
|
|
||||||
export {Compiler} from './linker/compiler';
|
export {Compiler} from './linker/compiler';
|
||||||
export {AppViewManager} from './linker/view_manager';
|
export {AppViewManager} from './linker/view_manager';
|
||||||
export {QueryList} from './linker/query_list';
|
export {QueryList} from './linker/query_list';
|
||||||
|
@ -20,3 +8,4 @@ export {TemplateRef} from './linker/template_ref';
|
||||||
export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref';
|
export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref';
|
||||||
export {ViewContainerRef} from './linker/view_container_ref';
|
export {ViewContainerRef} from './linker/view_container_ref';
|
||||||
export {ComponentRef} from './linker/dynamic_component_loader';
|
export {ComponentRef} from './linker/dynamic_component_loader';
|
||||||
|
export {ExpressionChangedAfterItHasBeenCheckedException} from './linker/exceptions';
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue