From 2b34c88b69af8b0031fdb7006327bb8260e23dda Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Wed, 6 Jan 2016 14:13:44 -0800 Subject: [PATCH] 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 --- gulpfile.js | 70 +- modules/angular2/angular2.dart | 3 +- modules/angular2/compiler.ts | 35 +- .../platform/testing/browser_static.ts | 3 +- modules/angular2/platform/testing/server.ts | 7 +- .../angular2/src/common/directives/ng_for.ts | 2 +- .../src/compiler/change_definition_factory.ts | 218 --- .../src/compiler/change_detector_compiler.ts | 95 - ...ective_metadata.ts => compile_metadata.ts} | 319 ++-- modules/angular2/src/compiler/compiler.ts | 51 +- modules/angular2/src/compiler/config.ts | 38 + .../directive_lifecycle_reflector.dart | 2 +- .../directive_lifecycle_reflector.ts | 2 +- ..._normalizer.ts => directive_normalizer.ts} | 36 +- .../linker => compiler}/directive_resolver.ts | 0 .../src/compiler/expression_parser/ast.ts | 348 ++++ .../expression_parser}/lexer.ts | 0 .../expression_parser}/parser.ts | 66 +- modules/angular2/src/compiler/identifiers.ts | 169 ++ .../angular2/src/compiler/offline_compiler.ts | 137 ++ .../src/compiler/output/abstract_emitter.ts | 419 +++++ .../compiler/output/abstract_js_emitter.ts | 161 ++ .../src/compiler/output/dart_emitter.ts | 378 ++++ .../src/compiler/output/interpretive_view.ts | 62 + .../src/compiler/output/js_emitter.ts | 73 + .../src/compiler/output/output_ast.ts | 869 +++++++++ .../src/compiler/output/output_interpreter.ts | 413 +++++ .../src/compiler/output/output_jit.ts | 48 + .../angular2/src/compiler/output/path_util.ts | 80 + .../src/compiler/output/ts_emitter.ts | 324 ++++ .../linker => compiler}/pipe_resolver.ts | 0 .../src/compiler/proto_view_compiler.ts | 399 ----- .../angular2/src/compiler/provider_parser.ts | 404 +++++ .../angular2/src/compiler/runtime_compiler.ts | 243 ++- .../angular2/src/compiler/runtime_metadata.ts | 197 ++- .../angular2/src/compiler/source_module.ts | 54 - .../angular2/src/compiler/style_compiler.ts | 127 +- modules/angular2/src/compiler/template_ast.ts | 43 +- .../src/compiler/template_compiler.ts | 456 ----- .../angular2/src/compiler/template_parser.ts | 210 ++- .../angular2/src/compiler/url_resolver.dart | 19 +- modules/angular2/src/compiler/url_resolver.ts | 31 +- modules/angular2/src/compiler/util.ts | 159 +- .../angular2/src/compiler/view_compiler.ts | 606 ------- .../compiler/view_compiler/compile_binding.ts | 6 + .../compiler/view_compiler/compile_element.ts | 402 +++++ .../compiler/view_compiler/compile_method.ts | 75 + .../compiler/view_compiler/compile_query.ts | 120 ++ .../compiler/view_compiler/compile_view.ts | 192 ++ .../src/compiler/view_compiler/constants.ts | 86 + .../compiler/view_compiler/event_binder.ts | 166 ++ .../view_compiler/expression_converter.ts | 254 +++ .../view_compiler/lifecycle_binder.ts | 87 + .../compiler/view_compiler/property_binder.ts | 210 +++ .../src/compiler/view_compiler/test.js | 77 + .../src/compiler/view_compiler/util.ts | 76 + .../src/compiler/view_compiler/view_binder.ts | 116 ++ .../compiler/view_compiler/view_builder.ts | 602 +++++++ .../compiler/view_compiler/view_compiler.ts | 31 + .../linker => compiler}/view_resolver.ts | 4 +- .../src/core/application_common_providers.ts | 10 +- modules/angular2/src/core/application_ref.ts | 5 +- modules/angular2/src/core/change_detection.ts | 3 - .../abstract_change_detector.ts | 296 ---- .../core/change_detection/binding_record.ts | 148 -- .../core/change_detection/change_detection.ts | 46 +- .../change_detection_jit_generator.dart | 23 - .../change_detection_jit_generator.ts | 501 ------ .../change_detection/change_detection_util.ts | 200 +-- .../change_detection/change_detector_ref.ts | 16 - .../src/core/change_detection/coalesce.ts | 174 -- .../core/change_detection/codegen_facade.dart | 19 - .../core/change_detection/codegen_facade.ts | 20 - .../change_detection/codegen_logic_util.ts | 240 --- .../change_detection/codegen_name_util.ts | 197 --- .../src/core/change_detection/constants.ts | 1 + .../core/change_detection/directive_record.ts | 55 - .../dynamic_change_detector.ts | 490 ----- .../core/change_detection/event_binding.ts | 7 - .../src/core/change_detection/exceptions.ts | 112 -- .../src/core/change_detection/interfaces.ts | 56 - .../jit_proto_change_detector.dart | 13 - .../jit_proto_change_detector.ts | 25 - .../src/core/change_detection/parser/ast.ts | 315 ---- .../core/change_detection/parser/locals.ts | 45 - .../pipe_lifecycle_reflector.dart | 5 - .../pipe_lifecycle_reflector.ts | 3 - .../src/core/change_detection/pipes.ts | 7 - .../pregen_proto_change_detector.dart | 19 - .../pregen_proto_change_detector.ts | 1 - .../change_detection/proto_change_detector.ts | 471 ----- .../src/core/change_detection/proto_record.ts | 59 - modules/angular2/src/core/debug/debug_node.ts | 44 +- .../angular2/src/core/debug/debug_renderer.ts | 49 +- modules/angular2/src/core/di.ts | 1 + modules/angular2/src/core/di/injector.ts | 531 +++--- modules/angular2/src/core/di/provider.ts | 14 +- modules/angular2/src/core/di/type_info.dart | 4 - modules/angular2/src/core/di/type_info.ts | 0 modules/angular2/src/core/linker.ts | 15 +- .../angular2/src/core/linker/debug_context.ts | 79 + modules/angular2/src/core/linker/element.ts | 909 +--------- .../src/core/linker/element_injector.ts | 23 + .../angular2/src/core/linker/element_ref.ts | 6 +- .../angular2/src/core/linker/exceptions.ts | 64 + .../angular2/src/core/linker/query_list.dart | 17 +- .../angular2/src/core/linker/query_list.ts | 12 +- .../core/linker/resolved_metadata_cache.ts | 35 - .../angular2/src/core/linker/template_ref.ts | 13 +- modules/angular2/src/core/linker/view.ts | 596 ++++--- .../src/core/linker/view_container_ref.ts | 91 +- .../angular2/src/core/linker/view_manager.ts | 220 +-- modules/angular2/src/core/linker/view_ref.ts | 18 +- .../angular2/src/core/linker/view_utils.ts | 134 ++ modules/angular2/src/core/metadata.dart | 9 + modules/angular2/src/core/metadata.ts | 11 + .../lifecycle_hooks.ts} | 1 - .../angular2/src/core/pipes/pipe_provider.ts | 18 - modules/angular2/src/core/pipes/pipes.ts | 58 - .../reflection/reflection_capabilities.dart | 2 +- .../reflection/reflection_capabilities.ts | 2 +- modules/angular2/src/core/render/api.ts | 35 +- modules/angular2/src/core/render/util.ts | 15 - modules/angular2/src/facade/collection.dart | 27 +- modules/angular2/src/facade/collection.ts | 29 +- modules/angular2/src/i18n/i18n_html_parser.ts | 2 +- .../angular2/src/i18n/message_extractor.ts | 2 +- modules/angular2/src/i18n/shared.ts | 2 +- .../src/mock/directive_resolver_mock.ts | 8 +- .../angular2/src/mock/view_resolver_mock.ts | 2 +- .../angular2/src/platform/dom/dom_renderer.ts | 14 +- .../directives/router_link_transform.ts | 11 +- .../src/router/directives/router_outlet.ts | 10 +- .../src/testing/test_component_builder.ts | 46 +- .../change_detector_codegen.ts | 15 - modules/angular2/src/upgrade/metadata.ts | 3 +- .../src/upgrade/upgrade_ng1_adapter.ts | 51 +- .../src/web_workers/shared/serializer.ts | 4 +- .../angular2/src/web_workers/ui/renderer.ts | 8 +- .../src/web_workers/worker/renderer.ts | 14 +- .../test/common/directives/ng_class_spec.ts | 28 +- .../test/common/forms/directives_spec.ts | 2 - .../test/common/pipes/date_pipe_spec.ts | 2 +- .../common/pipes/i18n_plural_pipe_spec.ts | 2 +- .../common/pipes/i18n_select_pipe_spec.ts | 2 +- .../test/common/pipes/pipe_binding_spec.ts | 26 - .../angular2/test/common/pipes/pipes_spec.ts | 76 - .../change_definition_factory_spec.ts | 194 -- .../compiler/change_detector_compiler_spec.ts | 143 -- .../test/compiler/change_detector_mocks.ts | 48 - ...adata_spec.ts => compile_metadata_spec.ts} | 61 +- .../directive_lifecycle_spec.dart | 4 +- .../directive_lifecycle_spec.ts | 4 +- ...r_spec.ts => directive_normalizer_spec.ts} | 69 +- .../directive_resolver_spec.ts | 2 +- .../angular2/test/compiler/eval_module.dart | 55 - modules/angular2/test/compiler/eval_module.ts | 44 - .../test/compiler/eval_module_spec.ts | 55 - .../expression_parser}/lexer_spec.ts | 2 +- .../expression_parser}/parser_spec.ts | 9 +- .../expression_parser}/unparser.ts | 42 +- .../offline_compiler_codegen_typed.ts | 19 + .../offline_compiler_codegen_untyped.ts | 17 + .../offline_compiler_compa.css.shim.ts | 3 + .../test/compiler/offline_compiler_spec.ts | 88 + .../test/compiler/offline_compiler_util.ts | 67 + .../compiler/output/abstract_emitter_spec.ts | 38 + .../test/compiler/output/dart_emitter_spec.ts | 310 ++++ .../test/compiler/output/js_emitter_spec.ts | 296 ++++ .../output/output_emitter_codegen_typed.ts | 21 + .../output/output_emitter_codegen_untyped.ts | 19 + .../compiler/output/output_emitter_spec.ts | 197 +++ .../compiler/output/output_emitter_util.ts | 267 +++ .../test/compiler/output/path_util_spec.ts | 72 + .../test/compiler/output/ts_emitter_spec.ts | 321 ++++ .../test/compiler/runtime_compiler_spec.ts | 57 - .../test/compiler/runtime_metadata_spec.ts | 6 +- .../test/compiler/source_module_spec.ts | 47 - modules/angular2/test/compiler/spies.dart | 4 - modules/angular2/test/compiler/spies.ts | 5 - .../style_compiler_import.css.shim.ts | 4 - .../compiler/style_compiler_import.css.ts | 4 - .../test/compiler/style_compiler_spec.ts | 337 ---- .../test/compiler/template_compiler_spec.ts | 509 ------ .../test/compiler/template_parser_spec.ts | 471 ++++- .../angular2/test/compiler/test_bindings.ts | 7 +- .../test/compiler/url_resolver_spec.ts | 76 +- modules/angular2/test/compiler/util_spec.ts | 61 - .../linker => compiler}/view_resolver_spec.ts | 2 +- .../test/core/application_ref_spec.ts | 9 +- .../change_detector_config.ts | 427 ----- .../change_detector_ref_spec.ts | 31 - .../change_detection/change_detector_spec.ts | 1571 ----------------- .../change_detector_util_spec.ts | 53 +- .../core/change_detection/coalesce_spec.ts | 235 --- .../generated/change_detector_classes.ts | 6 - .../generator/gen_change_detectors.dart | 42 - .../change_detection/parser/locals_spec.ts | 38 - .../proto_record_builder_spec.ts | 38 - .../change_detection/proto_record_spec.ts | 66 - .../test/core/debug/debug_node_spec.ts | 4 +- .../angular2/test/core/di/injector_spec.ts | 155 +- .../change_detection_integration_spec.ts | 1323 ++++++++++++++ .../linker/dynamic_component_loader_spec.ts | 130 +- .../angular2/test/core/linker/element_spec.ts | 843 --------- .../test/core/linker/integration_spec.ts | 123 +- .../linker/projection_integration_spec.ts | 70 +- .../core/linker/query_integration_spec.ts | 26 +- .../linker/view_injector_integration_spec.ts | 661 +++++++ modules/angular2/test/core/spies.dart | 38 +- modules/angular2/test/core/spies.ts | 75 +- .../test/i18n/i18n_html_parser_spec.ts | 4 +- .../test/i18n/message_extractor_spec.ts | 4 +- .../test/platform/browser/bootstrap_spec.ts | 2 +- modules/angular2/test/public_api_spec.ts | 28 +- .../directives/router_link_transform_spec.ts | 4 +- .../test/router/integration/bootstrap_spec.ts | 23 +- .../router/integration/navigation_spec.ts | 20 +- .../router/integration/router_link_spec.ts | 3 +- .../angular2/test/router/integration/util.ts | 3 +- modules/angular2/test/router/router_spec.ts | 2 - .../symbol_inspector/symbol_inspector.dart | 2 +- .../angular2/test/testing/fake_async_spec.ts | 2 +- .../worker/renderer_integration_spec.ts | 56 +- .../e2e_test/change_detection_perf.dart | 3 - .../e2e_test/change_detection_perf.ts | 93 - .../change_detection_benchmark.html | 29 - .../change_detection_benchmark.ts | 389 ---- .../src/compiler/compiler_benchmark.ts | 25 +- modules/benchmarks/src/index.html | 3 - modules/benchmarks/src/tree/tree_benchmark.ts | 4 +- modules_dart/angular2_testing/pubspec.yaml | 1 + .../payload/hello_world/web/index.dart | 2 +- .../transform/lib/extract_messages.dart | 11 +- .../transform/common/class_matcher_base.dart | 2 +- .../common/code/annotation_code.dart | 17 +- .../transform/common/code/ng_deps_code.dart | 40 +- .../transform/common/code/queries_code.dart | 136 -- .../common/code/reflection_info_code.dart | 120 +- .../transform/common/code/source_module.dart | 23 +- .../transform/common/interface_matcher.dart | 32 +- .../common/model/annotation_model.pb.dart | 129 +- .../common/model/annotation_model.proto | 17 +- .../common/model/import_export_model.pb.dart | 50 +- .../common/model/ng_deps_model.pb.dart | 84 +- .../common/model/ng_deps_model.proto | 14 +- .../common/model/parameter_model.pb.dart | 40 +- .../model/reflection_info_model.pb.dart | 188 +- .../common/model/reflection_info_model.proto | 15 +- .../transform/common/model/source_module.dart | 22 +- .../lib/src/transform/common/names.dart | 3 - .../lib/src/transform/common/ng_compiler.dart | 42 +- .../lib/src/transform/common/ng_meta.dart | 5 +- .../lib/src/transform/common/options.dart | 15 +- .../src/transform/common/options_reader.dart | 8 +- .../src/transform/common/property_utils.dart | 36 - .../common/type_metadata_reader.dart | 75 +- .../src/transform/common/url_resolver.dart | 20 +- .../lib/src/transform/common/zone.dart | 12 +- .../transform/deferred_rewriter/rewriter.dart | 2 +- .../ng_meta_linker.dart | 22 +- .../directive_processor/inliner.dart | 2 +- .../directive_processor/rewriter.dart | 6 +- .../inliner_for_test/transformer.dart | 2 +- .../stylesheet_compiler/processor.dart | 4 +- .../change_detector_codegen.dart | 594 ------- .../compile_data_creator.dart | 9 +- .../template_compiler/generator.dart | 42 +- .../template_compiler/reflection/model.dart | 55 - .../reflection/processor.dart | 31 - .../reflection/reflection_capabilities.dart | 36 - .../template_compiler/transformer.dart | 2 +- .../common/code/ng_deps_code_tests.dart | 22 +- .../ng_for.ng_meta.dart | 1 - .../test/transform/common/ng_meta_helper.dart | 5 +- .../test/transform/common/ng_meta_test.dart | 2 +- .../transform/common/url_resolver_tests.dart | 22 +- .../hello.ng_meta.json | 1 - .../hello.ng_meta.json | 1 - .../directive_metadata_linker/all_tests.dart | 157 +- .../directive_processor/all_tests.dart | 250 +-- .../directives_files/components.dart | 8 + .../test/transform/integration/all_tests.dart | 46 +- .../deferred_files/expected/bar.template.dart | 7 +- .../expected/bar.template.dart | 7 +- .../expected/bar.template.dart | 7 +- .../expected/foo.template.dart | 7 +- .../integration/event_getter_files/bar.dart | 9 - .../expected/bar.template.dart | 26 - .../integration/event_getter_files/index.dart | 11 - .../expected/bar.template.dart | 6 +- .../override_annotation_files/bar.dart | 10 - .../expected/bar.template.dart | 29 - .../queries_class_annotation_files/bar.dart | 9 - .../expected/bar.template.dart | 25 - .../queries_prop_annotation_files/bar.dart | 9 - .../expected/bar.template.dart | 30 - .../expected/bar.template.dart | 6 +- .../expected/bar.template.dart | 7 +- .../two_annotations_files/bar.dart | 7 +- .../expected/bar.template.dart | 8 +- .../two_deps_files/expected/bar.template.dart | 6 +- .../template_compiler/all_tests.dart | 86 +- .../expected/hello.ng_deps.dart | 3 +- .../event_files/expected/hello.ng_deps.dart | 3 +- .../expected/registrations.ng_deps.dart | 22 +- scripts/ci/build_dart_ddc.sh | 2 +- tools/broccoli/broccoli-check-imports.ts | 3 +- tools/broccoli/broccoli-generate-for-test.ts | 81 + tools/broccoli/trees/dart_tree.ts | 16 +- tools/broccoli/trees/node_tree.ts | 30 +- tools/public_api_guard/public_api_spec.ts | 290 +-- 312 files changed, 14271 insertions(+), 16566 deletions(-) delete mode 100644 modules/angular2/src/compiler/change_definition_factory.ts delete mode 100644 modules/angular2/src/compiler/change_detector_compiler.ts rename modules/angular2/src/compiler/{directive_metadata.ts => compile_metadata.ts} (68%) create mode 100644 modules/angular2/src/compiler/config.ts rename modules/angular2/src/{core/linker => compiler}/directive_lifecycle_reflector.dart (91%) rename modules/angular2/src/{core/linker => compiler}/directive_lifecycle_reflector.ts (92%) rename modules/angular2/src/compiler/{template_normalizer.ts => directive_normalizer.ts} (77%) rename modules/angular2/src/{core/linker => compiler}/directive_resolver.ts (100%) create mode 100644 modules/angular2/src/compiler/expression_parser/ast.ts rename modules/angular2/src/{core/change_detection/parser => compiler/expression_parser}/lexer.ts (100%) rename modules/angular2/src/{core/change_detection/parser => compiler/expression_parser}/parser.ts (89%) create mode 100644 modules/angular2/src/compiler/identifiers.ts create mode 100644 modules/angular2/src/compiler/offline_compiler.ts create mode 100644 modules/angular2/src/compiler/output/abstract_emitter.ts create mode 100644 modules/angular2/src/compiler/output/abstract_js_emitter.ts create mode 100644 modules/angular2/src/compiler/output/dart_emitter.ts create mode 100644 modules/angular2/src/compiler/output/interpretive_view.ts create mode 100644 modules/angular2/src/compiler/output/js_emitter.ts create mode 100644 modules/angular2/src/compiler/output/output_ast.ts create mode 100644 modules/angular2/src/compiler/output/output_interpreter.ts create mode 100644 modules/angular2/src/compiler/output/output_jit.ts create mode 100644 modules/angular2/src/compiler/output/path_util.ts create mode 100644 modules/angular2/src/compiler/output/ts_emitter.ts rename modules/angular2/src/{core/linker => compiler}/pipe_resolver.ts (100%) delete mode 100644 modules/angular2/src/compiler/proto_view_compiler.ts create mode 100644 modules/angular2/src/compiler/provider_parser.ts delete mode 100644 modules/angular2/src/compiler/source_module.ts delete mode 100644 modules/angular2/src/compiler/template_compiler.ts delete mode 100644 modules/angular2/src/compiler/view_compiler.ts create mode 100644 modules/angular2/src/compiler/view_compiler/compile_binding.ts create mode 100644 modules/angular2/src/compiler/view_compiler/compile_element.ts create mode 100644 modules/angular2/src/compiler/view_compiler/compile_method.ts create mode 100644 modules/angular2/src/compiler/view_compiler/compile_query.ts create mode 100644 modules/angular2/src/compiler/view_compiler/compile_view.ts create mode 100644 modules/angular2/src/compiler/view_compiler/constants.ts create mode 100644 modules/angular2/src/compiler/view_compiler/event_binder.ts create mode 100644 modules/angular2/src/compiler/view_compiler/expression_converter.ts create mode 100644 modules/angular2/src/compiler/view_compiler/lifecycle_binder.ts create mode 100644 modules/angular2/src/compiler/view_compiler/property_binder.ts create mode 100644 modules/angular2/src/compiler/view_compiler/test.js create mode 100644 modules/angular2/src/compiler/view_compiler/util.ts create mode 100644 modules/angular2/src/compiler/view_compiler/view_binder.ts create mode 100644 modules/angular2/src/compiler/view_compiler/view_builder.ts create mode 100644 modules/angular2/src/compiler/view_compiler/view_compiler.ts rename modules/angular2/src/{core/linker => compiler}/view_resolver.ts (96%) delete mode 100644 modules/angular2/src/core/change_detection/abstract_change_detector.ts delete mode 100644 modules/angular2/src/core/change_detection/binding_record.ts delete mode 100644 modules/angular2/src/core/change_detection/change_detection_jit_generator.dart delete mode 100644 modules/angular2/src/core/change_detection/change_detection_jit_generator.ts delete mode 100644 modules/angular2/src/core/change_detection/coalesce.ts delete mode 100644 modules/angular2/src/core/change_detection/codegen_facade.dart delete mode 100644 modules/angular2/src/core/change_detection/codegen_facade.ts delete mode 100644 modules/angular2/src/core/change_detection/codegen_logic_util.ts delete mode 100644 modules/angular2/src/core/change_detection/codegen_name_util.ts delete mode 100644 modules/angular2/src/core/change_detection/directive_record.ts delete mode 100644 modules/angular2/src/core/change_detection/dynamic_change_detector.ts delete mode 100644 modules/angular2/src/core/change_detection/event_binding.ts delete mode 100644 modules/angular2/src/core/change_detection/exceptions.ts delete mode 100644 modules/angular2/src/core/change_detection/interfaces.ts delete mode 100644 modules/angular2/src/core/change_detection/jit_proto_change_detector.dart delete mode 100644 modules/angular2/src/core/change_detection/jit_proto_change_detector.ts delete mode 100644 modules/angular2/src/core/change_detection/parser/ast.ts delete mode 100644 modules/angular2/src/core/change_detection/parser/locals.ts delete mode 100644 modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.dart delete mode 100644 modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.ts delete mode 100644 modules/angular2/src/core/change_detection/pipes.ts delete mode 100644 modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart delete mode 100644 modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts delete mode 100644 modules/angular2/src/core/change_detection/proto_change_detector.ts delete mode 100644 modules/angular2/src/core/change_detection/proto_record.ts delete mode 100644 modules/angular2/src/core/di/type_info.dart delete mode 100644 modules/angular2/src/core/di/type_info.ts create mode 100644 modules/angular2/src/core/linker/debug_context.ts create mode 100644 modules/angular2/src/core/linker/element_injector.ts create mode 100644 modules/angular2/src/core/linker/exceptions.ts delete mode 100644 modules/angular2/src/core/linker/resolved_metadata_cache.ts create mode 100644 modules/angular2/src/core/linker/view_utils.ts rename modules/angular2/src/core/{linker/interfaces.ts => metadata/lifecycle_hooks.ts} (99%) delete mode 100644 modules/angular2/src/core/pipes/pipe_provider.ts delete mode 100644 modules/angular2/src/core/pipes/pipes.ts delete mode 100644 modules/angular2/src/core/render/util.ts delete mode 100644 modules/angular2/src/transform/template_compiler/change_detector_codegen.ts delete mode 100644 modules/angular2/test/common/pipes/pipe_binding_spec.ts delete mode 100644 modules/angular2/test/common/pipes/pipes_spec.ts delete mode 100644 modules/angular2/test/compiler/change_definition_factory_spec.ts delete mode 100644 modules/angular2/test/compiler/change_detector_compiler_spec.ts delete mode 100644 modules/angular2/test/compiler/change_detector_mocks.ts rename modules/angular2/test/compiler/{directive_metadata_spec.ts => compile_metadata_spec.ts} (71%) rename modules/angular2/test/{core/linker => compiler}/directive_lifecycle_spec.dart (97%) rename modules/angular2/test/{core/linker => compiler}/directive_lifecycle_spec.ts (96%) rename modules/angular2/test/compiler/{template_normalizer_spec.ts => directive_normalizer_spec.ts} (85%) rename modules/angular2/test/{core/linker => compiler}/directive_resolver_spec.ts (98%) delete mode 100644 modules/angular2/test/compiler/eval_module.dart delete mode 100644 modules/angular2/test/compiler/eval_module.ts delete mode 100644 modules/angular2/test/compiler/eval_module_spec.ts rename modules/angular2/test/{core/change_detection/parser => compiler/expression_parser}/lexer_spec.ts (99%) rename modules/angular2/test/{core/change_detection/parser => compiler/expression_parser}/parser_spec.ts (98%) rename modules/angular2/test/{core/change_detection/parser => compiler/expression_parser}/unparser.ts (77%) create mode 100644 modules/angular2/test/compiler/offline_compiler_codegen_typed.ts create mode 100644 modules/angular2/test/compiler/offline_compiler_codegen_untyped.ts create mode 100644 modules/angular2/test/compiler/offline_compiler_compa.css.shim.ts create mode 100644 modules/angular2/test/compiler/offline_compiler_spec.ts create mode 100644 modules/angular2/test/compiler/offline_compiler_util.ts create mode 100644 modules/angular2/test/compiler/output/abstract_emitter_spec.ts create mode 100644 modules/angular2/test/compiler/output/dart_emitter_spec.ts create mode 100644 modules/angular2/test/compiler/output/js_emitter_spec.ts create mode 100644 modules/angular2/test/compiler/output/output_emitter_codegen_typed.ts create mode 100644 modules/angular2/test/compiler/output/output_emitter_codegen_untyped.ts create mode 100644 modules/angular2/test/compiler/output/output_emitter_spec.ts create mode 100644 modules/angular2/test/compiler/output/output_emitter_util.ts create mode 100644 modules/angular2/test/compiler/output/path_util_spec.ts create mode 100644 modules/angular2/test/compiler/output/ts_emitter_spec.ts delete mode 100644 modules/angular2/test/compiler/runtime_compiler_spec.ts delete mode 100644 modules/angular2/test/compiler/source_module_spec.ts delete mode 100644 modules/angular2/test/compiler/style_compiler_import.css.shim.ts delete mode 100644 modules/angular2/test/compiler/style_compiler_import.css.ts delete mode 100644 modules/angular2/test/compiler/style_compiler_spec.ts delete mode 100644 modules/angular2/test/compiler/template_compiler_spec.ts delete mode 100644 modules/angular2/test/compiler/util_spec.ts rename modules/angular2/test/{core/linker => compiler}/view_resolver_spec.ts (96%) delete mode 100644 modules/angular2/test/core/change_detection/change_detector_config.ts delete mode 100644 modules/angular2/test/core/change_detection/change_detector_ref_spec.ts delete mode 100644 modules/angular2/test/core/change_detection/change_detector_spec.ts delete mode 100644 modules/angular2/test/core/change_detection/coalesce_spec.ts delete mode 100644 modules/angular2/test/core/change_detection/generated/change_detector_classes.ts delete mode 100644 modules/angular2/test/core/change_detection/generator/gen_change_detectors.dart delete mode 100644 modules/angular2/test/core/change_detection/parser/locals_spec.ts delete mode 100644 modules/angular2/test/core/change_detection/proto_record_builder_spec.ts delete mode 100644 modules/angular2/test/core/change_detection/proto_record_spec.ts create mode 100644 modules/angular2/test/core/linker/change_detection_integration_spec.ts delete mode 100644 modules/angular2/test/core/linker/element_spec.ts create mode 100644 modules/angular2/test/core/linker/view_injector_integration_spec.ts delete mode 100644 modules/benchmarks/e2e_test/change_detection_perf.dart delete mode 100644 modules/benchmarks/e2e_test/change_detection_perf.ts delete mode 100644 modules/benchmarks/src/change_detection/change_detection_benchmark.html delete mode 100644 modules/benchmarks/src/change_detection/change_detection_benchmark.ts delete mode 100644 modules_dart/transform/lib/src/transform/common/code/queries_code.dart delete mode 100644 modules_dart/transform/lib/src/transform/common/property_utils.dart delete mode 100644 modules_dart/transform/lib/src/transform/template_compiler/change_detector_codegen.dart delete mode 100644 modules_dart/transform/lib/src/transform/template_compiler/reflection/model.dart delete mode 100644 modules_dart/transform/lib/src/transform/template_compiler/reflection/processor.dart delete mode 100644 modules_dart/transform/lib/src/transform/template_compiler/reflection/reflection_capabilities.dart delete mode 100644 modules_dart/transform/test/transform/integration/event_getter_files/bar.dart delete mode 100644 modules_dart/transform/test/transform/integration/event_getter_files/expected/bar.template.dart delete mode 100644 modules_dart/transform/test/transform/integration/event_getter_files/index.dart delete mode 100644 modules_dart/transform/test/transform/integration/override_annotation_files/bar.dart delete mode 100644 modules_dart/transform/test/transform/integration/override_annotation_files/expected/bar.template.dart delete mode 100644 modules_dart/transform/test/transform/integration/queries_class_annotation_files/bar.dart delete mode 100644 modules_dart/transform/test/transform/integration/queries_class_annotation_files/expected/bar.template.dart delete mode 100644 modules_dart/transform/test/transform/integration/queries_prop_annotation_files/bar.dart delete mode 100644 modules_dart/transform/test/transform/integration/queries_prop_annotation_files/expected/bar.template.dart create mode 100644 tools/broccoli/broccoli-generate-for-test.ts diff --git a/gulpfile.js b/gulpfile.js index 0dbc6dc5dc..8706e269d2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -177,8 +177,8 @@ var PAYLOAD_TESTS_CONFIG = { return path.join(__dirname, CONFIG.dest.js.prod.es5, 'payload_tests', caseName, 'ts/' + packaging); }, - systemjs: {sizeLimits: {'uncompressed': 870 * 1024, 'gzip level=9': 165 * 1024}}, - webpack: {sizeLimits: {'uncompressed': 550 * 1024, 'gzip level=9': 120 * 1024}} + systemjs: {sizeLimits: {'uncompressed': 880 * 1024, 'gzip level=9': 170 * 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) { printModulesWarning(); 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'); // 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) { 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'); // 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; } - 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) { var watch = require('./tools/build/watch'); - runSequence( - 'build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart', - '!build/change_detect.dart', '!test.unit.dartvm/run', function(error) { - // Watch for changes made in the TS and Dart code under "modules" and - // run ts2dart and test change detector generator prior to rerunning the - // tests. - watch('modules/angular2/**', {ignoreInitial: true}, - ['!build/tree.dart', '!build/change_detect.dart', '!test.unit.dartvm/run']); + runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart', + '!test.unit.dartvm/run', function(error) { + // Watch for changes made in the TS and Dart code under "modules" and + // run ts2dart and test change detector generator prior to rerunning the + // tests. + watch('modules/angular2/**', {ignoreInitial: true}, + ['!build/tree.dart', '!test.unit.dartvm/run']); - // 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 - // tests. - watch('modules_dart/**', {ignoreInitial: true}, - ['build/pure-packages.dart', '!build/change_detect.dart', '!test.unit.dartvm/run']); - }); + // 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 + // tests. + watch('modules_dart/**', {ignoreInitial: true}, + ['build/pure-packages.dart', '!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 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 - '!build/pubget.angular2.dart', '!build/change_detect.dart', sequenceComplete(done)); + '!build/pubget.angular2.dart', sequenceComplete(done)); }); // Builds and compiles all Dart packages @@ -1467,34 +1467,6 @@ gulp.task('gen_protos.dart', function(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(); }); diff --git a/modules/angular2/angular2.dart b/modules/angular2/angular2.dart index 3efe608c9d..f585b57f47 100644 --- a/modules/angular2/angular2.dart +++ b/modules/angular2/angular2.dart @@ -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_adapter.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; + diff --git a/modules/angular2/compiler.ts b/modules/angular2/compiler.ts index 57856c054b..c77f40fecc 100644 --- a/modules/angular2/compiler.ts +++ b/modules/angular2/compiler.ts @@ -3,6 +3,35 @@ * @description * Starting point to import all compiler APIs. */ -export * from './src/compiler/url_resolver'; -export * from './src/compiler/xhr'; -export * from './src/compiler/compiler'; \ No newline at end of file +export { + PLATFORM_DIRECTIVES, + 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'; diff --git a/modules/angular2/platform/testing/browser_static.ts b/modules/angular2/platform/testing/browser_static.ts index d1555c719b..92a4698464 100644 --- a/modules/angular2/platform/testing/browser_static.ts +++ b/modules/angular2/platform/testing/browser_static.ts @@ -1,12 +1,11 @@ import { APP_ID, - DirectiveResolver, NgZone, Provider, - ViewResolver, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER } from 'angular2/core'; +import {DirectiveResolver, ViewResolver} from 'angular2/compiler'; import {BROWSER_APP_COMMON_PROVIDERS} from 'angular2/src/platform/browser_common'; import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter'; diff --git a/modules/angular2/platform/testing/server.ts b/modules/angular2/platform/testing/server.ts index a6bdb3c3b1..b2509a615f 100644 --- a/modules/angular2/platform/testing/server.ts +++ b/modules/angular2/platform/testing/server.ts @@ -1,14 +1,14 @@ import { APP_ID, - DirectiveResolver, NgZone, Provider, - ViewResolver, PLATFORM_COMMON_PROVIDERS, PLATFORM_INITIALIZER, APPLICATION_COMMON_PROVIDERS, Renderer } from 'angular2/core'; +import {DirectiveResolver, ViewResolver} from 'angular2/compiler'; + import {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter'; 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 {RootRenderer} from 'angular2/src/core/render/api'; 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 { EventManager, @@ -78,6 +78,7 @@ export const TEST_SERVER_APPLICATION_PROVIDERS: Arraythis._viewContainer.detach(tuple.record.previousIndex); movedTuples.push(tuple); } else { this._viewContainer.remove(tuple.record.previousIndex); diff --git a/modules/angular2/src/compiler/change_definition_factory.ts b/modules/angular2/src/compiler/change_definition_factory.ts deleted file mode 100644 index 51b8f61263..0000000000 --- a/modules/angular2/src/compiler/change_definition_factory.ts +++ /dev/null @@ -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; -} diff --git a/modules/angular2/src/compiler/change_detector_compiler.ts b/modules/angular2/src/compiler/change_detector_compiler.ts deleted file mode 100644 index db3b727b4a..0000000000 --- a/modules/angular2/src/compiler/change_detector_compiler.ts +++ /dev/null @@ -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); - } -} diff --git a/modules/angular2/src/compiler/directive_metadata.ts b/modules/angular2/src/compiler/compile_metadata.ts similarity index 68% rename from modules/angular2/src/compiler/directive_metadata.ts rename to modules/angular2/src/compiler/compile_metadata.ts index 1240fe12da..3c8ed69bf6 100644 --- a/modules/angular2/src/compiler/directive_metadata.ts +++ b/modules/angular2/src/compiler/compile_metadata.ts @@ -12,8 +12,8 @@ import { StringWrapper, isArray } from 'angular2/src/facade/lang'; -import {unimplemented} from 'angular2/src/facade/exceptions'; -import {StringMapWrapper} from 'angular2/src/facade/collection'; +import {unimplemented, BaseException} from 'angular2/src/facade/exceptions'; +import {StringMapWrapper, MapWrapper, SetWrapper} from 'angular2/src/facade/collection'; import { ChangeDetectionStrategy, CHANGE_DETECTION_STRATEGY_VALUES @@ -21,7 +21,8 @@ import { import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view'; import {CssSelector} from 'angular2/src/compiler/selector'; 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 2: "event" from "(event)" @@ -50,46 +51,33 @@ export class CompileIdentifierMetadata implements CompileMetadataWithIdentifier name: string; prefix: string; moduleUrl: string; - constConstructor: boolean; value: any; - constructor({runtime, name, moduleUrl, prefix, constConstructor, value}: { - runtime?: any, - name?: string, - moduleUrl?: string, - prefix?: string, - constConstructor?: boolean, - value?: any - } = {}) { + constructor( + {runtime, name, moduleUrl, prefix, value}: + {runtime?: any, name?: string, moduleUrl?: string, prefix?: string, value?: any} = {}) { this.runtime = runtime; this.name = name; this.prefix = prefix; this.moduleUrl = moduleUrl; - this.constConstructor = constConstructor; this.value = value; } static fromJson(data: {[key: string]: any}): CompileIdentifierMetadata { - let value = isArray(data['value']) ? arrayFromJson(data['value'], metadataFromJson) : - objFromJson(data['value'], metadataFromJson); - return new CompileIdentifierMetadata({ - name: data['name'], - prefix: data['prefix'], - moduleUrl: data['moduleUrl'], - constConstructor: data['constConstructor'], - value: value - }); + let value = isArray(data['value']) ? _arrayFromJson(data['value'], metadataFromJson) : + _objFromJson(data['value'], metadataFromJson); + return new CompileIdentifierMetadata( + {name: data['name'], prefix: data['prefix'], moduleUrl: data['moduleUrl'], value: value}); } 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 { // Note: Runtime type can't be serialized... 'class': 'Identifier', 'name': this.name, 'moduleUrl': this.moduleUrl, 'prefix': this.prefix, - 'constConstructor': this.constConstructor, 'value': value }; } @@ -103,72 +91,82 @@ export class CompileDiDependencyMetadata { isHost: boolean; isSkipSelf: boolean; isOptional: boolean; + isValue: boolean; query: 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, isSelf?: boolean, isHost?: boolean, isSkipSelf?: boolean, isOptional?: boolean, + isValue?: boolean, query?: CompileQueryMetadata, viewQuery?: CompileQueryMetadata, - token?: CompileIdentifierMetadata | string + token?: CompileTokenMetadata, + value?: any } = {}) { this.isAttribute = normalizeBool(isAttribute); this.isSelf = normalizeBool(isSelf); this.isHost = normalizeBool(isHost); this.isSkipSelf = normalizeBool(isSkipSelf); this.isOptional = normalizeBool(isOptional); + this.isValue = normalizeBool(isValue); this.query = query; this.viewQuery = viewQuery; this.token = token; + this.value = value; } static fromJson(data: {[key: string]: any}): CompileDiDependencyMetadata { return new CompileDiDependencyMetadata({ - token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson), - query: objFromJson(data['query'], CompileQueryMetadata.fromJson), - viewQuery: objFromJson(data['viewQuery'], CompileQueryMetadata.fromJson), + token: _objFromJson(data['token'], CompileTokenMetadata.fromJson), + query: _objFromJson(data['query'], CompileQueryMetadata.fromJson), + viewQuery: _objFromJson(data['viewQuery'], CompileQueryMetadata.fromJson), + value: data['value'], isAttribute: data['isAttribute'], isSelf: data['isSelf'], isHost: data['isHost'], isSkipSelf: data['isSkipSelf'], - isOptional: data['isOptional'] + isOptional: data['isOptional'], + isValue: data['isValue'] }); } toJson(): {[key: string]: any} { return { - // Note: Runtime type can't be serialized... - 'token': objToJson(this.token), - 'query': objToJson(this.query), - 'viewQuery': objToJson(this.viewQuery), + 'token': _objToJson(this.token), + 'query': _objToJson(this.query), + 'viewQuery': _objToJson(this.viewQuery), + 'value': this.value, 'isAttribute': this.isAttribute, 'isSelf': this.isSelf, 'isHost': this.isHost, 'isSkipSelf': this.isSkipSelf, - 'isOptional': this.isOptional + 'isOptional': this.isOptional, + 'isValue': this.isValue }; } } export class CompileProviderMetadata { - token: CompileIdentifierMetadata | string; + token: CompileTokenMetadata; useClass: CompileTypeMetadata; useValue: any; - useExisting: CompileIdentifierMetadata | string; + useExisting: CompileTokenMetadata; useFactory: CompileFactoryMetadata; deps: CompileDiDependencyMetadata[]; multi: boolean; constructor({token, useClass, useValue, useExisting, useFactory, deps, multi}: { - token?: CompileIdentifierMetadata | string, + token?: CompileTokenMetadata, useClass?: CompileTypeMetadata, useValue?: any, - useExisting?: CompileIdentifierMetadata | string, + useExisting?: CompileTokenMetadata, useFactory?: CompileFactoryMetadata, deps?: CompileDiDependencyMetadata[], multi?: boolean @@ -178,17 +176,19 @@ export class CompileProviderMetadata { this.useValue = useValue; this.useExisting = useExisting; this.useFactory = useFactory; - this.deps = deps; - this.multi = multi; + this.deps = normalizeBlank(deps); + this.multi = normalizeBool(multi); } static fromJson(data: {[key: string]: any}): CompileProviderMetadata { return new CompileProviderMetadata({ - token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson), - useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson), - useExisting: objFromJson(data['useExisting'], CompileIdentifierMetadata.fromJson), - useValue: objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson), - useFactory: objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson) + token: _objFromJson(data['token'], CompileTokenMetadata.fromJson), + useClass: _objFromJson(data['useClass'], CompileTypeMetadata.fromJson), + useExisting: _objFromJson(data['useExisting'], CompileTokenMetadata.fromJson), + useValue: _objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson), + useFactory: _objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson), + multi: data['multi'], + deps: _arrayFromJson(data['deps'], CompileDiDependencyMetadata.fromJson) }); } @@ -196,11 +196,13 @@ export class CompileProviderMetadata { return { // Note: Runtime type can't be serialized... 'class': 'Provider', - 'token': objToJson(this.token), - 'useClass': objToJson(this.useClass), - 'useExisting': objToJson(this.useExisting), - 'useValue': objToJson(this.useValue), - 'useFactory': objToJson(this.useFactory) + 'token': _objToJson(this.token), + 'useClass': _objToJson(this.useClass), + 'useExisting': _objToJson(this.useExisting), + 'useValue': _objToJson(this.useValue), + 'useFactory': _objToJson(this.useFactory), + 'multi': this.multi, + 'deps': _arrayToJson(this.deps) }; } } @@ -211,16 +213,14 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata, name: string; prefix: string; moduleUrl: string; - constConstructor: boolean; value: any; diDeps: CompileDiDependencyMetadata[]; - constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps, value}: { + constructor({runtime, name, moduleUrl, prefix, diDeps, value}: { runtime?: Function, name?: string, prefix?: string, moduleUrl?: string, - constConstructor?: boolean, value?: boolean, diDeps?: CompileDiDependencyMetadata[] }) { @@ -228,8 +228,7 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata, this.name = name; this.prefix = prefix; this.moduleUrl = moduleUrl; - this.diDeps = diDeps; - this.constConstructor = constConstructor; + this.diDeps = _normalizeArray(diDeps); this.value = value; } @@ -240,9 +239,8 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata, name: data['name'], prefix: data['prefix'], moduleUrl: data['moduleUrl'], - constConstructor: data['constConstructor'], 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, 'prefix': this.prefix, 'moduleUrl': this.moduleUrl, - 'constConstructor': this.constConstructor, '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 { + private _valueMap = new Map(); + 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. */ @@ -268,17 +360,15 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe prefix: string; moduleUrl: string; isHost: boolean; - constConstructor: boolean; value: any; diDeps: CompileDiDependencyMetadata[]; - constructor({runtime, name, moduleUrl, prefix, isHost, constConstructor, value, diDeps}: { + constructor({runtime, name, moduleUrl, prefix, isHost, value, diDeps}: { runtime?: Type, name?: string, moduleUrl?: string, prefix?: string, isHost?: boolean, - constConstructor?: boolean, value?: any, diDeps?: CompileDiDependencyMetadata[] } = {}) { @@ -287,9 +377,8 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe this.moduleUrl = moduleUrl; this.prefix = prefix; this.isHost = normalizeBool(isHost); - this.constConstructor = constConstructor; this.value = value; - this.diDeps = normalizeBlank(diDeps); + this.diDeps = _normalizeArray(diDeps); } static fromJson(data: {[key: string]: any}): CompileTypeMetadata { @@ -298,9 +387,8 @@ export class CompileTypeMetadata implements CompileIdentifierMetadata, CompileMe moduleUrl: data['moduleUrl'], prefix: data['prefix'], isHost: data['isHost'], - constConstructor: data['constConstructor'], 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, 'prefix': this.prefix, 'isHost': this.isHost, - 'constConstructor': this.constConstructor, 'value': this.value, - 'diDeps': arrayToJson(this.diDeps) + 'diDeps': _arrayToJson(this.diDeps) }; } } export class CompileQueryMetadata { - selectors: Array; + selectors: Array; descendants: boolean; first: boolean; propertyName: string; constructor({selectors, descendants, first, propertyName}: { - selectors?: Array, + selectors?: Array, descendants?: boolean, first?: boolean, propertyName?: string } = {}) { this.selectors = selectors; - this.descendants = descendants; + this.descendants = normalizeBool(descendants); this.first = normalizeBool(first); this.propertyName = propertyName; } static fromJson(data: {[key: string]: any}): CompileQueryMetadata { return new CompileQueryMetadata({ - selectors: arrayFromJson(data['selectors'], CompileIdentifierMetadata.fromJson), + selectors: _arrayFromJson(data['selectors'], CompileTokenMetadata.fromJson), descendants: data['descendants'], first: data['first'], propertyName: data['propertyName'] @@ -351,8 +438,7 @@ export class CompileQueryMetadata { toJson(): {[key: string]: any} { return { - // Note: Runtime type can't be serialized... - 'selectors': arrayToJson(this.selectors), + 'selectors': _arrayToJson(this.selectors), 'descendants': this.descendants, 'first': this.first, 'propertyName': this.propertyName @@ -416,12 +502,10 @@ export class CompileTemplateMetadata { * Metadata regarding compilation of a directive. */ export class CompileDirectiveMetadata implements CompileMetadataWithType { - static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs, - outputs, host, lifecycleHooks, providers, viewProviders, queries, viewQueries, - template}: { + static create({type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, + lifecycleHooks, providers, viewProviders, queries, viewQueries, template}: { type?: CompileTypeMetadata, isComponent?: boolean, - dynamicLoadable?: boolean, selector?: string, exportAs?: string, changeDetection?: ChangeDetectionStrategy, @@ -474,7 +558,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { return new CompileDirectiveMetadata({ type: type, isComponent: normalizeBool(isComponent), - dynamicLoadable: normalizeBool(dynamicLoadable), selector: selector, exportAs: exportAs, changeDetection: changeDetection, @@ -493,7 +576,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { } type: CompileTypeMetadata; isComponent: boolean; - dynamicLoadable: boolean; selector: string; exportAs: string; changeDetection: ChangeDetectionStrategy; @@ -503,17 +585,17 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { hostProperties: {[key: string]: string}; hostAttributes: {[key: string]: string}; lifecycleHooks: LifecycleHooks[]; - providers: Array; - viewProviders: Array; + providers: CompileProviderMetadata[]; + viewProviders: CompileProviderMetadata[]; queries: CompileQueryMetadata[]; viewQueries: CompileQueryMetadata[]; + template: CompileTemplateMetadata; - constructor({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs, - outputs, hostListeners, hostProperties, hostAttributes, lifecycleHooks, providers, + constructor({type, isComponent, selector, exportAs, changeDetection, inputs, outputs, + hostListeners, hostProperties, hostAttributes, lifecycleHooks, providers, viewProviders, queries, viewQueries, template}: { type?: CompileTypeMetadata, isComponent?: boolean, - dynamicLoadable?: boolean, selector?: string, exportAs?: string, changeDetection?: ChangeDetectionStrategy, @@ -533,7 +615,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { } = {}) { this.type = type; this.isComponent = isComponent; - this.dynamicLoadable = dynamicLoadable; this.selector = selector; this.exportAs = exportAs; this.changeDetection = changeDetection; @@ -542,11 +623,11 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { this.hostListeners = hostListeners; this.hostProperties = hostProperties; this.hostAttributes = hostAttributes; - this.lifecycleHooks = lifecycleHooks; - this.providers = normalizeBlank(providers); - this.viewProviders = normalizeBlank(viewProviders); - this.queries = normalizeBlank(queries); - this.viewQueries = normalizeBlank(viewQueries); + this.lifecycleHooks = _normalizeArray(lifecycleHooks); + this.providers = _normalizeArray(providers); + this.viewProviders = _normalizeArray(viewProviders); + this.queries = _normalizeArray(queries); + this.viewQueries = _normalizeArray(viewQueries); this.template = template; } @@ -555,7 +636,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { static fromJson(data: {[key: string]: any}): CompileDirectiveMetadata { return new CompileDirectiveMetadata({ isComponent: data['isComponent'], - dynamicLoadable: data['dynamicLoadable'], selector: data['selector'], exportAs: data['exportAs'], type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'], @@ -571,10 +651,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { (data['lifecycleHooks']).map(hookValue => LIFECYCLE_HOOKS_VALUES[hookValue]), template: isPresent(data['template']) ? CompileTemplateMetadata.fromJson(data['template']) : data['template'], - providers: arrayFromJson(data['providers'], metadataFromJson), - viewProviders: arrayFromJson(data['viewProviders'], metadataFromJson), - queries: arrayFromJson(data['queries'], CompileQueryMetadata.fromJson), - viewQueries: arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson) + providers: _arrayFromJson(data['providers'], metadataFromJson), + viewProviders: _arrayFromJson(data['viewProviders'], metadataFromJson), + queries: _arrayFromJson(data['queries'], CompileQueryMetadata.fromJson), + viewQueries: _arrayFromJson(data['viewQueries'], CompileQueryMetadata.fromJson) }); } @@ -582,7 +662,6 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { return { 'class': 'Directive', 'isComponent': this.isComponent, - 'dynamicLoadable': this.dynamicLoadable, 'selector': this.selector, 'exportAs': this.exportAs, 'type': isPresent(this.type) ? this.type.toJson() : this.type, @@ -595,10 +674,10 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType { 'hostAttributes': this.hostAttributes, 'lifecycleHooks': this.lifecycleHooks.map(hook => serializeEnum(hook)), 'template': isPresent(this.template) ? this.template.toJson() : this.template, - 'providers': arrayToJson(this.providers), - 'viewProviders': arrayToJson(this.viewProviders), - 'queries': arrayToJson(this.queries), - 'viewQueries': arrayToJson(this.viewQueries) + 'providers': _arrayToJson(this.providers), + 'viewProviders': _arrayToJson(this.viewProviders), + 'queries': _arrayToJson(this.queries), + 'viewQueries': _arrayToJson(this.viewQueries) }; } } @@ -612,7 +691,7 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata, return CompileDirectiveMetadata.create({ type: new CompileTypeMetadata({ runtime: Object, - name: `Host${componentType.name}`, + name: `${componentType.name}_Host`, moduleUrl: componentType.moduleUrl, isHost: true }), @@ -624,7 +703,6 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata, host: {}, lifecycleHooks: [], isComponent: true, - dynamicLoadable: false, selector: '*', providers: [], viewProviders: [], @@ -638,11 +716,18 @@ export class CompilePipeMetadata implements CompileMetadataWithType { type: CompileTypeMetadata; name: string; pure: boolean; - constructor({type, name, - pure}: {type?: CompileTypeMetadata, name?: string, pure?: boolean} = {}) { + lifecycleHooks: LifecycleHooks[]; + + constructor({type, name, pure, lifecycleHooks}: { + type?: CompileTypeMetadata, + name?: string, + pure?: boolean, + lifecycleHooks?: LifecycleHooks[] + } = {}) { this.type = type; this.name = name; this.pure = normalizeBool(pure); + this.lifecycleHooks = _normalizeArray(lifecycleHooks); } get identifier(): CompileIdentifierMetadata { return this.type; } @@ -673,22 +758,26 @@ var _COMPILE_METADATA_FROM_JSON = { 'Factory': CompileFactoryMetadata.fromJson }; -function arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any { - return isBlank(obj) ? null : obj.map(o => objFromJson(o, fn)); +function _arrayFromJson(obj: any[], fn: (a: {[key: string]: any}) => any): any { + return isBlank(obj) ? null : obj.map(o => _objFromJson(o, fn)); } -function arrayToJson(obj: any[]): string | {[key: string]: any} { - return isBlank(obj) ? null : obj.map(objToJson); +function _arrayToJson(obj: any[]): string | {[key: string]: any} { + return isBlank(obj) ? null : obj.map(_objToJson); } -function objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any { - if (isArray(obj)) return arrayFromJson(obj, fn); +function _objFromJson(obj: any, fn: (a: {[key: string]: any}) => any): any { + if (isArray(obj)) return _arrayFromJson(obj, fn); if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj; return fn(obj); } -function objToJson(obj: any): string | {[key: string]: any} { - if (isArray(obj)) return arrayToJson(obj); +function _objToJson(obj: any): string | {[key: string]: any} { + if (isArray(obj)) return _arrayToJson(obj); if (isString(obj) || isBlank(obj) || isBoolean(obj) || isNumber(obj)) return obj; return obj.toJson(); } + +function _normalizeArray(obj: any[]): any[] { + return isPresent(obj) ? obj : []; +} diff --git a/modules/angular2/src/compiler/compiler.ts b/modules/angular2/src/compiler/compiler.ts index 23426ce6c1..644c76d969 100644 --- a/modules/angular2/src/compiler/compiler.ts +++ b/modules/angular2/src/compiler/compiler.ts @@ -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 * from 'angular2/src/compiler/template_ast'; 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 {provide, Provider} from 'angular2/src/core/di'; import {TemplateParser} from 'angular2/src/compiler/template_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 {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler'; import {StyleCompiler} from 'angular2/src/compiler/style_compiler'; -import {ViewCompiler} from 'angular2/src/compiler/view_compiler'; -import {ProtoViewCompiler} from 'angular2/src/compiler/proto_view_compiler'; -import {TemplateCompiler} from 'angular2/src/compiler/template_compiler'; -import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection'; +import {ViewCompiler} from 'angular2/src/compiler/view_compiler/view_compiler'; +import {CompilerConfig} from './config'; import {Compiler} from 'angular2/src/core/linker/compiler'; import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler'; import {ElementSchemaRegistry} from 'angular2/src/compiler/schema/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 {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() { - return new ChangeDetectorGenConfig(assertionsEnabled(), false, true); +function _createCompilerConfig() { + return new CompilerConfig(assertionsEnabled(), false, true); } /** @@ -41,18 +44,18 @@ export const COMPILER_PROVIDERS: Array = CONST_EXPR([ Parser, HtmlParser, TemplateParser, - TemplateNormalizer, + DirectiveNormalizer, RuntimeMetadataResolver, DEFAULT_PACKAGE_URL_PROVIDER, StyleCompiler, - ProtoViewCompiler, ViewCompiler, - ChangeDetectionCompiler, - new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}), - TemplateCompiler, - new Provider(RuntimeCompiler, {useClass: RuntimeCompiler_}), + new Provider(CompilerConfig, {useFactory: _createCompilerConfig, deps: []}), + RuntimeCompiler, new Provider(Compiler, {useExisting: RuntimeCompiler}), DomElementSchemaRegistry, new Provider(ElementSchemaRegistry, {useExisting: DomElementSchemaRegistry}), - UrlResolver + UrlResolver, + ViewResolver, + DirectiveResolver, + PipeResolver ]); diff --git a/modules/angular2/src/compiler/config.ts b/modules/angular2/src/compiler/config.ts new file mode 100644 index 0000000000..18c3589408 --- /dev/null +++ b/modules/angular2/src/compiler/config.ts @@ -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; +} diff --git a/modules/angular2/src/core/linker/directive_lifecycle_reflector.dart b/modules/angular2/src/compiler/directive_lifecycle_reflector.dart similarity index 91% rename from modules/angular2/src/core/linker/directive_lifecycle_reflector.dart rename to modules/angular2/src/compiler/directive_lifecycle_reflector.dart index 46c340a834..15fc459565 100644 --- a/modules/angular2/src/core/linker/directive_lifecycle_reflector.dart +++ b/modules/angular2/src/compiler/directive_lifecycle_reflector.dart @@ -1,7 +1,7 @@ library angular2.src.core.compiler.directive_lifecycle_reflector; 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 { LifecycleHooks.OnInit: OnInit, diff --git a/modules/angular2/src/core/linker/directive_lifecycle_reflector.ts b/modules/angular2/src/compiler/directive_lifecycle_reflector.ts similarity index 92% rename from modules/angular2/src/core/linker/directive_lifecycle_reflector.ts rename to modules/angular2/src/compiler/directive_lifecycle_reflector.ts index 9733940a8d..2d9e20d40d 100644 --- a/modules/angular2/src/core/linker/directive_lifecycle_reflector.ts +++ b/modules/angular2/src/compiler/directive_lifecycle_reflector.ts @@ -1,5 +1,5 @@ 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 { if (!(token instanceof Type)) return false; diff --git a/modules/angular2/src/compiler/template_normalizer.ts b/modules/angular2/src/compiler/directive_normalizer.ts similarity index 77% rename from modules/angular2/src/compiler/template_normalizer.ts rename to modules/angular2/src/compiler/directive_normalizer.ts index 905793bcdf..e6d440c9eb 100644 --- a/modules/angular2/src/compiler/template_normalizer.ts +++ b/modules/angular2/src/compiler/directive_normalizer.ts @@ -1,9 +1,11 @@ import { CompileTypeMetadata, CompileDirectiveMetadata, - CompileTemplateMetadata -} from './directive_metadata'; -import {isPresent, isBlank} from 'angular2/src/facade/lang'; + CompileTemplateMetadata, + CompileProviderMetadata, + CompileTokenMetadata +} from './compile_metadata'; +import {isPresent, isBlank, isArray} from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; import {PromiseWrapper} from 'angular2/src/facade/async'; @@ -28,10 +30,36 @@ import {HtmlParser} from './html_parser'; import {preparseElement, PreparsedElement, PreparsedElementType} from './template_preparser'; @Injectable() -export class TemplateNormalizer { +export class DirectiveNormalizer { constructor(private _xhr: XHR, private _urlResolver: UrlResolver, private _htmlParser: HtmlParser) {} + normalizeDirective(directive: CompileDirectiveMetadata): Promise { + 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, template: CompileTemplateMetadata): Promise { if (isPresent(template.template)) { diff --git a/modules/angular2/src/core/linker/directive_resolver.ts b/modules/angular2/src/compiler/directive_resolver.ts similarity index 100% rename from modules/angular2/src/core/linker/directive_resolver.ts rename to modules/angular2/src/compiler/directive_resolver.ts diff --git a/modules/angular2/src/compiler/expression_parser/ast.ts b/modules/angular2/src/compiler/expression_parser/ast.ts new file mode 100644 index 0000000000..f2c138a3eb --- /dev/null +++ b/modules/angular2/src/compiler/expression_parser/ast.ts @@ -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); + } +} diff --git a/modules/angular2/src/core/change_detection/parser/lexer.ts b/modules/angular2/src/compiler/expression_parser/lexer.ts similarity index 100% rename from modules/angular2/src/core/change_detection/parser/lexer.ts rename to modules/angular2/src/compiler/expression_parser/lexer.ts diff --git a/modules/angular2/src/core/change_detection/parser/parser.ts b/modules/angular2/src/compiler/expression_parser/parser.ts similarity index 89% rename from modules/angular2/src/core/change_detection/parser/parser.ts rename to modules/angular2/src/compiler/expression_parser/parser.ts index 9871603f11..2f3edca4d2 100644 --- a/modules/angular2/src/core/change_detection/parser/parser.ts +++ b/modules/angular2/src/compiler/expression_parser/parser.ts @@ -18,7 +18,6 @@ import { $LPAREN, $RPAREN } from './lexer'; -import {reflector, Reflector} from 'angular2/src/core/reflection/reflection'; import { AST, EmptyExpr, @@ -64,18 +63,13 @@ export class SplitInterpolation { @Injectable() export class Parser { - /** @internal */ - _reflector: Reflector; - constructor(/** @internal */ - public _lexer: Lexer, providedReflector: Reflector = null) { - this._reflector = isPresent(providedReflector) ? providedReflector : reflector; - } + public _lexer: Lexer) {} parseAction(input: string, location: any): ASTWithSource { this._checkNoInterpolation(input, location); 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); } @@ -104,7 +98,7 @@ export class Parser { this._checkNoInterpolation(input, location); 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 { @@ -119,7 +113,7 @@ export class Parser { parseTemplateBindings(input: string, location: any): TemplateBinding[] { 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 { @@ -130,7 +124,7 @@ export class Parser { for (let i = 0; i < split.expressions.length; ++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); } @@ -191,7 +185,7 @@ export class Parser { export class _ParseAST { index: number = 0; constructor(public input: string, public location: any, public tokens: any[], - public reflector: Reflector, public parseAction: boolean) {} + public parseAction: boolean) {} peek(offset: number): Token { var i = this.index + offset; @@ -531,16 +525,14 @@ export class _ParseAST { if (this.optionalCharacter($LPAREN)) { let args = this.parseCallArguments(); this.expectCharacter($RPAREN); - let fn = this.reflector.method(id); - return isSafe ? new SafeMethodCall(receiver, id, fn, args) : - new MethodCall(receiver, id, fn, args); + return isSafe ? new SafeMethodCall(receiver, id, args) : new MethodCall(receiver, id, args); } else { if (isSafe) { if (this.optionalOperator("=")) { this.error("The '?.' operator cannot be used in the assignment"); } else { - return new SafePropertyRead(receiver, id, this.reflector.getter(id)); + return new SafePropertyRead(receiver, id); } } else { if (this.optionalOperator("=")) { @@ -549,9 +541,9 @@ export class _ParseAST { } let value = this.parseConditional(); - return new PropertyWrite(receiver, id, this.reflector.setter(id), value); + return new PropertyWrite(receiver, id, value); } 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; - 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[] { var res = ListWrapper.createFixedSize(asts.length); @@ -703,7 +695,7 @@ class SimpleExpressionChecker implements AstVisitor { 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; } } diff --git a/modules/angular2/src/compiler/identifiers.ts b/modules/angular2/src/compiler/identifiers.ts new file mode 100644 index 0000000000..f247a37cf5 --- /dev/null +++ b/modules/angular2/src/compiler/identifiers.ts @@ -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}); +} diff --git a/modules/angular2/src/compiler/offline_compiler.ts b/modules/angular2/src/compiler/offline_compiler.ts new file mode 100644 index 0000000000..a7e14ce292 --- /dev/null +++ b/modules/angular2/src/compiler/offline_compiler.ts @@ -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 { + 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 = 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.`); + } +} diff --git a/modules/angular2/src/compiler/output/abstract_emitter.ts b/modules/angular2/src/compiler/output/abstract_emitter.ts new file mode 100644 index 0000000000..5ea387d79a --- /dev/null +++ b/modules/angular2/src/compiler/output/abstract_emitter.ts @@ -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; +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/output/abstract_js_emitter.ts b/modules/angular2/src/compiler/output/abstract_js_emitter.ts new file mode 100644 index 0000000000..9b85f356e5 --- /dev/null +++ b/modules/angular2/src/compiler/output/abstract_js_emitter.ts @@ -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 = [ + 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; + } +} diff --git a/modules/angular2/src/compiler/output/dart_emitter.ts b/modules/angular2/src/compiler/output/dart_emitter.ts new file mode 100644 index 0000000000..9561a02efb --- /dev/null +++ b/modules/angular2/src/compiler/output/dart_emitter.ts @@ -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 = 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(); + + 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(``); + } + 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`); + 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); +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/output/interpretive_view.ts b/modules/angular2/src/compiler/output/interpretive_view.ts new file mode 100644 index 0000000000..d74eab72a0 --- /dev/null +++ b/modules/angular2/src/compiler/output/interpretive_view.ts @@ -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, + getters: Map, methods: Map): 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 implements DynamicInstance { + constructor(args: any[], public props: Map, public getters: Map, + public methods: Map) { + 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); + } + } +} diff --git a/modules/angular2/src/compiler/output/js_emitter.ts b/modules/angular2/src/compiler/output/js_emitter.ts new file mode 100644 index 0000000000..1646551e3d --- /dev/null +++ b/modules/angular2/src/compiler/output/js_emitter.ts @@ -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(); + + 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}; }});`; +} diff --git a/modules/angular2/src/compiler/output/output_ast.ts b/modules/angular2/src/compiler/output/output_ast.ts new file mode 100644 index 0000000000..c4e7366d7a --- /dev/null +++ b/modules/angular2/src/compiler/output/output_ast.ts @@ -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 = name; + this.builtin = null; + } else { + this.name = null; + this.builtin = 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 = method; + this.builtin = null; + } else { + this.name = null; + this.builtin = 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>, 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], (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) => (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 { + var finder = new _VariableFinder(); + finder.visitAllStatements(stmts, null); + return finder.varNames; +} + +class _VariableFinder extends RecursiveExpressionVisitor { + varNames = new Set(); + 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>, + 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); +} diff --git a/modules/angular2/src/compiler/output/output_interpreter.ts b/modules/angular2/src/compiler/output/output_interpreter.ts new file mode 100644 index 0000000000..3451a3d22c --- /dev/null +++ b/modules/angular2/src/compiler/output/output_interpreter.ts @@ -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(), + new Map(), new Map(), + new Map(), 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, + getters: Map, methods: Map): DynamicInstance; +} + +export abstract class DynamicInstance { + get props(): Map { return unimplemented(); } + get getters(): Map { return unimplemented(); } + get methods(): Map { 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, + public props: Map, public getters: Map, + public methods: Map, public instanceFactory: InstanceFactory) {} + + createChildWihtLocalVars(): _ExecutionContext { + return new _ExecutionContext(this, this.superClass, this.superInstance, this.className, + new Map(), 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(); + var getters = new Map(); + var methods = new Map(); + 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 = 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 = 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 = 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[entry[0]] = + (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'; \ No newline at end of file diff --git a/modules/angular2/src/compiler/output/output_jit.ts b/modules/angular2/src/compiler/output/output_jit.ts new file mode 100644 index 0000000000..e0273b5561 --- /dev/null +++ b/modules/angular2/src/compiler/output/output_jit.ts @@ -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, '_'); +} diff --git a/modules/angular2/src/compiler/output/path_util.ts b/modules/angular2/src/compiler/output/path_util.ts new file mode 100644 index 0000000000..9d7e156a82 --- /dev/null +++ b/modules/angular2/src/compiler/output/path_util.ts @@ -0,0 +1,80 @@ +import {BaseException} from 'angular2/src/facade/exceptions'; +import {isPresent, isBlank, RegExpWrapper, Math} from 'angular2/src/facade/lang'; + +// asset:// +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; +} diff --git a/modules/angular2/src/compiler/output/ts_emitter.ts b/modules/angular2/src/compiler/output/ts_emitter.ts new file mode 100644 index 0000000000..2edc5e81a2 --- /dev/null +++ b/modules/angular2/src/compiler/output/ts_emitter.ts @@ -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 = 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(); + + 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 = [ + 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(`>`); + } + } +} diff --git a/modules/angular2/src/core/linker/pipe_resolver.ts b/modules/angular2/src/compiler/pipe_resolver.ts similarity index 100% rename from modules/angular2/src/core/linker/pipe_resolver.ts rename to modules/angular2/src/compiler/pipe_resolver.ts diff --git a/modules/angular2/src/compiler/proto_view_compiler.ts b/modules/angular2/src/compiler/proto_view_compiler.ts deleted file mode 100644 index 68f489505c..0000000000 --- a/modules/angular2/src/compiler/proto_view_compiler.ts +++ /dev/null @@ -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 { - var protoViewFactory = new RuntimeProtoViewFactory(metadataCache, component, pipes); - var allProtoViews = []; - protoViewFactory.createCompileProtoView(template, [], [], allProtoViews); - return new CompileProtoViews([], allProtoViews); - } - - compileProtoViewCodeGen(resolvedMetadataCacheExpr: Expression, - component: CompileDirectiveMetadata, template: TemplateAst[], - pipes: CompilePipeMetadata[]): - CompileProtoViews { - var protoViewFactory = new CodeGenProtoViewFactory(resolvedMetadataCacheExpr, component, pipes); - var allProtoViews = []; - var allStatements = []; - protoViewFactory.createCompileProtoView(template, [], allStatements, allProtoViews); - return new CompileProtoViews( - allStatements.map(stmt => stmt.statement), allProtoViews); - } -} - -export class CompileProtoViews { - constructor(public declarations: STATEMENT[], - public protoViews: CompileProtoView[]) {} -} - - -export class CompileProtoView { - constructor(public embeddedTemplateIndex: number, - public protoElements: CompileProtoElement[], - public protoView: APP_PROTO_VIEW) {} -} - -export class CompileProtoElement { - 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 { - 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[]): - CompileProtoView { - 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( - this, targetStatements, targetProtoViews); - templateVisitAll(builder, template); - var viewType = getViewType(this.component, embeddedTemplateIndex); - var appProtoView = this.createAppProtoView(embeddedTemplateIndex, viewType, - templateVariableBindings, targetStatements); - var cpv = new CompileProtoView( - embeddedTemplateIndex, builder.protoElements, appProtoView); - targetProtoViews[embeddedTemplateIndex] = cpv; - return cpv; - } -} - -class CodeGenProtoViewFactory extends ProtoViewFactory { - 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 { - 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 implements - TemplateAstVisitor { - protoElements: CompileProtoElement[] = []; - boundElementCount: number = 0; - - constructor(public factory: ProtoViewFactory, - public allStatements: STATEMENT[], - public allProtoViews: CompileProtoView[]) {} - - 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 = - visitAndReturnContext(this, ast.outputs, new Map()); - 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(), [], - 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( - 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): 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(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, - 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; - } -} diff --git a/modules/angular2/src/compiler/provider_parser.ts b/modules/angular2/src/compiler/provider_parser.ts new file mode 100644 index 0000000000..4dc92e918d --- /dev/null +++ b/modules/angular2/src/compiler/provider_parser.ts @@ -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; + /** + * @internal + */ + viewProviders: CompileTokenMap; + errors: ProviderError[] = []; + + constructor(public component: CompileDirectiveMetadata, public sourceSpan: ParseSourceSpan) { + this.viewQueries = _getViewQueries(component); + this.viewProviders = new CompileTokenMap(); + _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; + + private _transformedProviders = new CompileTokenMap(); + private _seenProviders = new CompileTokenMap(); + private _allProviders: CompileTokenMap; + 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, + sourceSpan: ParseSourceSpan, targetErrors: ParseError[], + targetProviders: CompileProviderMetadata[] = null): CompileProviderMetadata[] { + if (isBlank(targetProviders)) { + targetProviders = []; + } + if (isPresent(providers)) { + providers.forEach((provider) => { + if (isArray(provider)) { + _normalizeProviders(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 { + var providersByToken = new CompileTokenMap(); + 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) { + 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 { + var viewQueries = new CompileTokenMap(); + 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 { + var contentQueries = new CompileTokenMap(); + 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, + query: CompileQueryMetadata) { + query.selectors.forEach((selector) => { + var entry = map.get(selector); + if (isBlank(entry)) { + entry = []; + map.add(selector, entry); + } + entry.push(query); + }); +} diff --git a/modules/angular2/src/compiler/runtime_compiler.ts b/modules/angular2/src/compiler/runtime_compiler.ts index 68800146bd..bcb2f42ec0 100644 --- a/modules/angular2/src/compiler/runtime_compiler.ts +++ b/modules/angular2/src/compiler/runtime_compiler.ts @@ -1,26 +1,239 @@ -import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler'; -import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref'; -import {TemplateCompiler} from './template_compiler'; - +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, + 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 {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 { - abstract compileInHost(componentType: Type): Promise; - abstract clearCache(); -} +import {CompilerConfig} from './config'; +import * as ir from './output/output_ast'; +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() -export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler { - constructor(private _templateCompiler: TemplateCompiler) { super(); } +export class RuntimeCompiler extends Compiler_ { + private _styleCache: Map> = new Map>(); + private _hostCacheKeys = new Map(); + private _compiledTemplateCache = new Map(); + private _compiledTemplateDone = new Map>(); + + 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 { - return this._templateCompiler.compileHostComponentRuntime(componentType) - .then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory)); + var compMeta: CompileDirectiveMetadata = + 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() { - super.clearCache(); - this._templateCompiler.clearCache(); + this._styleCache.clear(); + 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( + [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[]): 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 { + var compileResult = this._styleCompiler.compileComponent(compMeta); + return this._resolveStylesCompileResult(compMeta.type.name, compileResult); + } + + private _resolveStylesCompileResult(sourceUrl: string, + result: StylesCompileResult): Promise { + 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 { + 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.`); } } diff --git a/modules/angular2/src/compiler/runtime_metadata.ts b/modules/angular2/src/compiler/runtime_metadata.ts index 6a48faac7a..afecf4d46d 100644 --- a/modules/angular2/src/compiler/runtime_metadata.ts +++ b/modules/angular2/src/compiler/runtime_metadata.ts @@ -5,23 +5,36 @@ import { isPresent, isArray, stringify, - RegExpWrapper + isString, + RegExpWrapper, + StringWrapper } from 'angular2/src/facade/lang'; +import {StringMapWrapper} from 'angular2/src/facade/collection'; 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 {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver'; -import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver'; -import {ViewResolver} from 'angular2/src/core/linker/view_resolver'; +import * as dimd from 'angular2/src/core/metadata/di'; +import {DirectiveResolver} from './directive_resolver'; +import {PipeResolver} from './pipe_resolver'; +import {ViewResolver} from './view_resolver'; import {ViewMetadata} from 'angular2/src/core/metadata/view'; -import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector'; -import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces'; +import {hasLifecycleHook} from './directive_lifecycle_reflector'; +import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/metadata/lifecycle_hooks'; import {reflector} from 'angular2/src/core/reflection/reflection'; import {Injectable, Inject, Optional} from 'angular2/src/core/di'; import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes'; import {MODULE_SUFFIX} from './util'; import {assertArrayOfStrings} from './assertions'; 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() export class RuntimeMetadataResolver { @@ -39,12 +52,12 @@ export class RuntimeMetadataResolver { * Wrap the stringify method to avoid naming things `function (arg1...) {` */ private sanitizeName(obj: any): string { - let result = stringify(obj); + let result = StringWrapper.replaceAll(stringify(obj), /[\s-]/g, '_'); if (result.indexOf('(') < 0) { return result; } let found = this._anonymousTypes.get(obj); - if (!found) { + if (isBlank(found)) { this._anonymousTypes.set(obj, this._anonymousTypeIndex++); found = this._anonymousTypes.get(obj); } @@ -58,6 +71,7 @@ export class RuntimeMetadataResolver { var moduleUrl = null; var templateMeta = null; var changeDetectionStrategy = null; + var viewProviders = []; if (dirMeta instanceof md.ComponentMetadata) { assertArrayOfStrings('styles', dirMeta.styles); @@ -73,36 +87,71 @@ export class RuntimeMetadataResolver { styleUrls: viewMeta.styleUrls }); 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({ selector: dirMeta.selector, exportAs: dirMeta.exportAs, isComponent: isPresent(templateMeta), - dynamicLoadable: true, - type: new cpl.CompileTypeMetadata( - {name: this.sanitizeName(directiveType), moduleUrl: moduleUrl, runtime: directiveType}), + type: this.getTypeMetadata(directiveType, moduleUrl), template: templateMeta, changeDetection: changeDetectionStrategy, inputs: dirMeta.inputs, outputs: dirMeta.outputs, 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); } 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 { var meta = this._pipeCache.get(pipeType); if (isBlank(meta)) { var pipeMeta = this._pipeResolver.resolve(pipeType); var moduleUrl = reflector.importUri(pipeType); meta = new cpl.CompilePipeMetadata({ - type: new cpl.CompileTypeMetadata( - {name: this.sanitizeName(pipeType), moduleUrl: moduleUrl, runtime: pipeType}), + type: this.getTypeMetadata(pipeType, moduleUrl), name: pipeMeta.name, - pure: pipeMeta.pure + pure: pipeMeta.pure, + lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, pipeType)), }); this._pipeCache.set(pipeType, meta); } @@ -133,6 +182,122 @@ export class RuntimeMetadataResolver { } 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 = 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 = 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 { + 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[] { diff --git a/modules/angular2/src/compiler/source_module.ts b/modules/angular2/src/compiler/source_module.ts deleted file mode 100644 index c100d36c29..0000000000 --- a/modules/angular2/src/compiler/source_module.ts +++ /dev/null @@ -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[][]) {} -} diff --git a/modules/angular2/src/compiler/style_compiler.ts b/modules/angular2/src/compiler/style_compiler.ts index 565e33c0c6..155fe9bdd4 100644 --- a/modules/angular2/src/compiler/style_compiler.ts +++ b/modules/angular2/src/compiler/style_compiler.ts @@ -1,106 +1,77 @@ -import {CompileTypeMetadata, CompileTemplateMetadata} from './directive_metadata'; -import {SourceModule, SourceExpression, moduleRef} from './source_module'; +import { + CompileTemplateMetadata, + CompileIdentifierMetadata, + CompileDirectiveMetadata +} from './compile_metadata'; +import * as o from './output/output_ast'; 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 {UrlResolver} from 'angular2/src/compiler/url_resolver'; import {extractStyleUrls} from './style_url_resolver'; -import { - escapeSingleQuoteString, - codeGenExportVariable, - codeGenToString, - MODULE_SUFFIX -} from './util'; import {Injectable} from 'angular2/src/core/di'; +import {isPresent} from 'angular2/src/facade/lang'; const COMPONENT_VARIABLE = '%COMP%'; const HOST_ATTR = `_nghost-${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() export class StyleCompiler { - private _styleCache: Map> = new Map>(); private _shadowCss: ShadowCss = new ShadowCss(); - constructor(private _xhr: XHR, private _urlResolver: UrlResolver) {} + constructor(private _urlResolver: UrlResolver) {} - compileComponentRuntime(template: CompileTemplateMetadata): Promise> { - var styles = template.styles; - var styleAbsUrls = template.styleUrls; - return this._loadStyles(styles, styleAbsUrls, - template.encapsulation === ViewEncapsulation.Emulated); + compileComponent(comp: CompileDirectiveMetadata): StylesCompileResult { + var shim = comp.template.encapsulation === ViewEncapsulation.Emulated; + return this._compileStyles(getStylesVarName(comp), comp.template.styles, + comp.template.styleUrls, shim); } - compileComponentCodeGen(template: CompileTemplateMetadata): SourceExpression { - var shim = template.encapsulation === ViewEncapsulation.Emulated; - return this._styleCodeGen(template.styles, template.styleUrls, shim); - } - - compileStylesheetCodeGen(stylesheetUrl: string, cssText: string): SourceModule[] { + compileStylesheet(stylesheetUrl: string, cssText: string, + isShimmed: boolean): StylesCompileResult { var styleWithImports = extractStyleUrls(this._urlResolver, stylesheetUrl, cssText); - return [ - this._styleModule( - stylesheetUrl, false, - this._styleCodeGen([styleWithImports.style], styleWithImports.styleUrls, false)), - this._styleModule(stylesheetUrl, true, this._styleCodeGen([styleWithImports.style], - styleWithImports.styleUrls, true)) - ]; + return this._compileStyles(getStylesVarName(null), [styleWithImports.style], + styleWithImports.styleUrls, isShimmed); } - clearCache() { this._styleCache.clear(); } - - private _loadStyles(plainStyles: string[], absUrls: string[], - encapsulate: boolean): Promise> { - var promises: Promise[] = absUrls.map((absUrl: string): Promise => { - var cacheKey = `${absUrl}${encapsulate ? '.shim' : ''}`; - var result: Promise = 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(promises).then((nestedStyles: string[][]) => { - var result: Array = - 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))); - + private _compileStyles(stylesVar: string, plainStyles: string[], absUrls: string[], + shim: boolean): StylesCompileResult { + var styleExpressions = + plainStyles.map(plainStyle => o.literal(this._shimIfNeeded(plainStyle, shim))); + var dependencies = []; for (var i = 0; i < absUrls.length; i++) { - var moduleUrl = this._createModuleUrl(absUrls[i], shim); - styleExpressions.push(`${moduleRef(moduleUrl)}STYLES`); + var identifier = new CompileIdentifierMetadata({name: getStylesVarName(null)}); + dependencies.push(new StylesCompileDependency(absUrls[i], shim, identifier)); + styleExpressions.push(new o.ExternalExpr(identifier)); } - var expressionSource = `${arrayPrefix} [${styleExpressions.join(',')}]`; - return new SourceExpression([], expressionSource); - } - - private _styleModule(stylesheetUrl: string, shim: boolean, - expression: SourceExpression): SourceModule { - var moduleSource = ` - ${expression.declarations.join('\n')} - ${codeGenExportVariable('STYLES')}${expression.expression}; - `; - return new SourceModule(this._createModuleUrl(stylesheetUrl, shim), moduleSource); + // styles variable contains plain strings and arrays of other styles arrays (recursive), + // so we set its type to dynamic. + var stmt = o.variable(stylesVar) + .set(o.literalArr(styleExpressions, + new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const]))) + .toDeclStmt(null, [o.StmtModifier.Final]); + return new StylesCompileResult([stmt], stylesVar, dependencies); } private _shimIfNeeded(style: string, shim: boolean): string { 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; +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/template_ast.ts b/modules/angular2/src/compiler/template_ast.ts index 1b79d32be5..d8c3f92899 100644 --- a/modules/angular2/src/compiler/template_ast.ts +++ b/modules/angular2/src/compiler/template_ast.ts @@ -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 {CompileDirectiveMetadata} from './directive_metadata'; +import { + CompileDirectiveMetadata, + CompileTokenMetadata, + CompileProviderMetadata, + CompileTokenMap, + CompileQueryMetadata +} from './compile_metadata'; import {ParseSourceSpan} from './parse_util'; /** @@ -92,8 +98,9 @@ export class ElementAst implements TemplateAst { constructor(public name: string, public attrs: AttrAst[], public inputs: BoundElementPropertyAst[], public outputs: BoundEventAst[], public exportAsVars: VariableAst[], public directives: DirectiveAst[], - public children: TemplateAst[], public ngContentIndex: number, - public sourceSpan: ParseSourceSpan) {} + public providers: ProviderAst[], public children: TemplateAst[], + public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {} + visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitElement(this, context); } @@ -121,8 +128,10 @@ export class ElementAst implements TemplateAst { */ export class EmbeddedTemplateAst implements TemplateAst { constructor(public attrs: AttrAst[], public outputs: BoundEventAst[], public vars: VariableAst[], - public directives: DirectiveAst[], public children: TemplateAst[], - public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {} + public directives: DirectiveAst[], public providers: ProviderAst[], + public children: TemplateAst[], public ngContentIndex: number, + public sourceSpan: ParseSourceSpan) {} + visit(visitor: TemplateAstVisitor, context: any): any { 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 `` in a template). */ diff --git a/modules/angular2/src/compiler/template_compiler.ts b/modules/angular2/src/compiler/template_compiler.ts deleted file mode 100644 index ed5ff0ef78..0000000000 --- a/modules/angular2/src/compiler/template_compiler.ts +++ /dev/null @@ -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(); - private _compiledTemplateCache = new Map(); - private _compiledTemplateDone = new Map>(); - - 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 { - 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 { - 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 = 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 = removeDuplicates(viewDirectives); - let uniqViewPipes = 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([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[]) { - 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 = removeDuplicates(directives); - let uniqPipes = 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): 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): 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; - } -} diff --git a/modules/angular2/src/compiler/template_parser.ts b/modules/angular2/src/compiler/template_parser.ts index 75e28bd8a9..1c08e15894 100644 --- a/modules/angular2/src/compiler/template_parser.ts +++ b/modules/angular2/src/compiler/template_parser.ts @@ -1,15 +1,35 @@ -import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/collection'; -import {RegExpWrapper, isPresent, StringWrapper, isBlank} from 'angular2/src/facade/lang'; +import { + 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 {CONST_EXPR} from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; -import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection'; -import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast'; -import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata'; +import { + AST, + 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 {splitNsName, mergeNsAndName} from './html_tags'; 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 { ElementAst, @@ -26,7 +46,9 @@ import { NgContentAst, PropertyBindingType, DirectiveAst, - BoundDirectivePropertyAst + BoundDirectivePropertyAst, + ProviderAst, + ProviderAstType } from './template_ast'; import {CssSelector, SelectorMatcher} from 'angular2/src/compiler/selector'; @@ -47,6 +69,8 @@ import { import {splitAtColon} from './util'; +import {ProviderElementContext, ProviderViewContext} from './provider_parser'; + // Group 1 = "bind-" // Group 2 = "var-" or "#" // Group 3 = "on-" @@ -93,9 +117,10 @@ export class TemplateParser { private _htmlParser: HtmlParser, @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[] { - var result = this.tryParse(template, directives, pipes, templateUrl); + var result = this.tryParse(component, template, directives, pipes, templateUrl); if (isPresent(result.errors)) { var errorString = result.errors.join('\n'); throw new BaseException(`Template parse errors:\n${errorString}`); @@ -103,13 +128,25 @@ export class TemplateParser { return result.templateAst; } - tryParse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], + tryParse(component: CompileDirectiveMetadata, template: string, + directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], templateUrl: string): TemplateParseResult { - var parseVisitor = - new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry); var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl); - var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT); - var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors); + var errors: ParseError[] = htmlAstWithErrors.errors; + var result; + if (htmlAstWithErrors.rootNodes.length > 0) { + var uniqDirectives = removeDuplicates(directives); + var uniqPipes = 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) { return new TemplateParseResult(result, errors); } @@ -128,7 +165,8 @@ class TemplateParseVisitor implements HtmlAstVisitor { ngContentCount: number = 0; pipesByName: Map; - constructor(directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], + constructor(public providerViewContext: ProviderViewContext, + directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) { this.selectorMatcher = new SelectorMatcher(); ListWrapper.forEachWithIndex(directives, @@ -150,6 +188,11 @@ class TemplateParseVisitor implements HtmlAstVisitor { try { var ast = this._exprParser.parseInterpolation(value, sourceInfo); this._checkPipes(ast, sourceSpan); + if (isPresent(ast) && + (ast.ast).expressions.length > MAX_INTERPOLATION_VALUES) { + throw new BaseException( + `Only support at most ${MAX_INTERPOLATION_VALUES} interpolation values!`); + } return ast; } catch (e) { this._reportError(`${e}`, sourceSpan); @@ -209,8 +252,8 @@ class TemplateParseVisitor implements HtmlAstVisitor { } } - visitText(ast: HtmlTextAst, component: Component): any { - var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR); + visitText(ast: HtmlTextAst, parent: ElementContext): any { + var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR); var expr = this._parseInterpolation(ast.value, ast.sourceSpan); if (isPresent(expr)) { return new BoundTextAst(expr, ngContentIndex, ast.sourceSpan); @@ -225,7 +268,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { 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 preparsedElement = preparseElement(element); if (preparsedElement.type === PreparsedElementType.SCRIPT || @@ -270,19 +313,26 @@ class TemplateParseVisitor implements HtmlAstVisitor { var lcElName = splitNsName(nodeName.toLowerCase())[1]; var isTemplateElement = lcElName == TEMPLATE_ELEMENT; var elementCssSelector = createElementCssSelector(nodeName, matchableAttrs); - var directives = this._createDirectiveAsts( - element.name, this._parseDirectives(this.selectorMatcher, elementCssSelector), - elementOrDirectiveProps, isTemplateElement ? [] : vars, element.sourceSpan); + var directiveMetas = this._parseDirectives(this.selectorMatcher, elementCssSelector); + var directiveAsts = + this._createDirectiveAsts(element.name, directiveMetas, elementOrDirectiveProps, + isTemplateElement ? [] : vars, element.sourceSpan); var elementProps: BoundElementPropertyAst[] = - this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directives); - var children = htmlVisitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, - element.children, Component.create(directives)); - + this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts); + var isViewRoot = parent.isTemplateElement || hasInlineTemplates; + 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 var projectionSelector = isPresent(preparsedElement.projectAs) ? CssSelector.parse(preparsedElement.projectAs)[0] : elementCssSelector; - var ngContentIndex = component.findNgContentIndex(projectionSelector); + var ngContentIndex = parent.findNgContentIndex(projectionSelector); var parsedElement; if (preparsedElement.type === PreparsedElementType.NG_CONTENT) { @@ -295,34 +345,43 @@ class TemplateParseVisitor implements HtmlAstVisitor { parsedElement = new NgContentAst( this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); } else if (isTemplateElement) { - this._assertAllEventsPublishedByDirectives(directives, events); - this._assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, + this._assertAllEventsPublishedByDirectives(directiveAsts, events); + this._assertNoComponentsNorElementBindingsOnTemplate(directiveAsts, elementProps, element.sourceSpan); parsedElement = - new EmbeddedTemplateAst(attrs, events, vars, directives, children, + new EmbeddedTemplateAst(attrs, events, vars, providerContext.transformedDirectiveAsts, + providerContext.transformProviders, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); } else { - this._assertOnlyOneComponent(directives, element.sourceSpan); + this._assertOnlyOneComponent(directiveAsts, element.sourceSpan); var elementExportAsVars = vars.filter(varAst => varAst.value.length === 0); let ngContentIndex = - hasInlineTemplates ? null : component.findNgContentIndex(projectionSelector); + hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector); - parsedElement = - new ElementAst(nodeName, attrs, elementProps, events, elementExportAsVars, directives, - children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); + parsedElement = new ElementAst( + nodeName, attrs, elementProps, events, elementExportAsVars, + providerContext.transformedDirectiveAsts, providerContext.transformProviders, children, + hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); } if (hasInlineTemplates) { var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs); - var templateDirectives = this._createDirectiveAsts( - element.name, this._parseDirectives(this.selectorMatcher, templateCssSelector), - templateElementOrDirectiveProps, [], element.sourceSpan); + var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector); + var templateDirectiveAsts = + this._createDirectiveAsts(element.name, templateDirectiveMetas, + templateElementOrDirectiveProps, [], element.sourceSpan); var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts( - element.name, templateElementOrDirectiveProps, templateDirectives); - this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectives, templateElementProps, - element.sourceSpan); + element.name, templateElementOrDirectiveProps, templateDirectiveAsts); + this._assertNoComponentsNorElementBindingsOnTemplate( + 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); } return parsedElement; @@ -685,7 +744,7 @@ class TemplateParseVisitor implements HtmlAstVisitor { } class NonBindableVisitor implements HtmlAstVisitor { - visitElement(ast: HtmlElementAst, component: Component): ElementAst { + visitElement(ast: HtmlElementAst, parent: ElementContext): ElementAst { var preparsedElement = preparseElement(ast); if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE || @@ -698,17 +757,17 @@ class NonBindableVisitor implements HtmlAstVisitor { var attrNameAndValues = ast.attrs.map(attrAst => [attrAst.name, attrAst.value]); var selector = createElementCssSelector(ast.name, attrNameAndValues); - var ngContentIndex = component.findNgContentIndex(selector); - var children = htmlVisitAll(this, ast.children, EMPTY_COMPONENT); - return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children, + var ngContentIndex = parent.findNgContentIndex(selector); + var children = htmlVisitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT); + return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], [], children, ngContentIndex, ast.sourceSpan); } visitComment(ast: HtmlCommentAst, context: any): any { return null; } visitAttr(ast: HtmlAttrAst, context: any): AttrAst { return new AttrAst(ast.name, ast.value, ast.sourceSpan); } - visitText(ast: HtmlTextAst, component: Component): TextAst { - var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR); + visitText(ast: HtmlTextAst, parent: ElementContext): TextAst { + var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR); 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); } -class Component { - static create(directives: DirectiveAst[]): Component { - if (directives.length === 0 || !directives[0].directive.isComponent) { - return EMPTY_COMPONENT; - } +class ElementContext { + static create(isTemplateElement: boolean, directives: DirectiveAst[], + providerContext: ProviderElementContext): ElementContext { var matcher = new SelectorMatcher(); - var ngContentSelectors = directives[0].directive.template.ngContentSelectors; var wildcardNgContentIndex = null; - for (var i = 0; i < ngContentSelectors.length; i++) { - var selector = ngContentSelectors[i]; - if (StringWrapper.equals(selector, '*')) { - wildcardNgContentIndex = i; - } else { - matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i); + if (directives.length > 0 && directives[0].directive.isComponent) { + var ngContentSelectors = directives[0].directive.template.ngContentSelectors; + for (var i = 0; i < ngContentSelectors.length; i++) { + var selector = ngContentSelectors[i]; + if (StringWrapper.equals(selector, '*')) { + 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, - public wildcardNgContentIndex: number) {} + constructor(public isTemplateElement: boolean, private _ngContentIndexMatcher: SelectorMatcher, + private _wildcardNgContentIndex: number, + public providerContext: ProviderElementContext) {} findNgContentIndex(selector: CssSelector): number { var ngContentIndices = []; - this.ngContentIndexMatcher.match( + this._ngContentIndexMatcher.match( selector, (selector, ngContentIndex) => { ngContentIndices.push(ngContentIndex); }); ListWrapper.sort(ngContentIndices); - if (isPresent(this.wildcardNgContentIndex)) { - ngContentIndices.push(this.wildcardNgContentIndex); + if (isPresent(this._wildcardNgContentIndex)) { + ngContentIndices.push(this._wildcardNgContentIndex); } return ngContentIndices.length > 0 ? ngContentIndices[0] : null; } @@ -775,16 +835,30 @@ function createElementCssSelector(elementName: string, matchableAttrs: string[][ 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(); export class PipeCollector extends RecursiveAstVisitor { pipes: Set = new Set(); - visitPipe(ast: BindingPipe): any { + visitPipe(ast: BindingPipe, context: any): any { this.pipes.add(ast.name); ast.exp.visit(this); - this.visitAll(ast.args); + this.visitAll(ast.args, context); 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; +} diff --git a/modules/angular2/src/compiler/url_resolver.dart b/modules/angular2/src/compiler/url_resolver.dart index 9af5236100..7c18a7e93b 100644 --- a/modules/angular2/src/compiler/url_resolver.dart +++ b/modules/angular2/src/compiler/url_resolver.dart @@ -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/core/application_tokens.dart' show PACKAGE_ROOT_URL; -UrlResolver createWithoutPackagePrefix() { +const _ASSET_SCHEME = 'asset:'; + +UrlResolver createUrlResolverWithoutPackagePrefix() { 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"); @Injectable() @@ -44,9 +50,14 @@ class UrlResolver { var prefix = this._packagePrefix; if (prefix != null && uri.scheme == 'package') { - prefix = StringWrapper.stripRight(prefix, '/'); - var path = StringWrapper.stripLeft(uri.path, '/'); - return '$prefix/$path'; + if (prefix == _ASSET_SCHEME) { + var pathSegments = uri.pathSegments.toList()..insert(1, 'lib'); + return new Uri(scheme: 'asset', pathSegments: pathSegments).toString(); + } else { + prefix = StringWrapper.stripRight(prefix, '/'); + var path = StringWrapper.stripLeft(uri.path, '/'); + return '$prefix/$path'; + } } else { return uri.toString(); } diff --git a/modules/angular2/src/compiler/url_resolver.ts b/modules/angular2/src/compiler/url_resolver.ts index 9174f8ddfb..598ed45a8d 100644 --- a/modules/angular2/src/compiler/url_resolver.ts +++ b/modules/angular2/src/compiler/url_resolver.ts @@ -11,13 +11,19 @@ import {ListWrapper} from 'angular2/src/facade/collection'; import {PACKAGE_ROOT_URL} from 'angular2/src/core/application_tokens'; import {Provider} from 'angular2/src/core/di'; +const _ASSET_SCHEME = 'asset:'; + /** * Create a {@link UrlResolver} with no package prefix. */ -export function createWithoutPackagePrefix(): UrlResolver { +export function createUrlResolverWithoutPackagePrefix(): UrlResolver { return new UrlResolver(); } +export function createOfflineCompileUrlResolver(): UrlResolver { + return new UrlResolver(_ASSET_SCHEME); +} + /** * 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() export class UrlResolver { - private _packagePrefix: string; - - constructor(@Inject(PACKAGE_ROOT_URL) packagePrefix: string = null) { - if (isPresent(packagePrefix)) { - this._packagePrefix = StringWrapper.stripRight(packagePrefix, "/") + "/"; - } - } + constructor(@Inject(PACKAGE_ROOT_URL) private _packagePrefix: string = null) {} /** * Resolves the `url` given the `baseUrl`: @@ -61,8 +61,19 @@ export class UrlResolver { if (isPresent(baseUrl) && baseUrl.length > 0) { resolvedUrl = _resolveUrl(baseUrl, resolvedUrl); } - if (isPresent(this._packagePrefix) && getUrlScheme(resolvedUrl) == "package") { - resolvedUrl = resolvedUrl.replace("package:", this._packagePrefix); + var resolvedParts = _split(resolvedUrl); + 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; } diff --git a/modules/angular2/src/compiler/util.ts b/modules/angular2/src/compiler/util.ts index f3ebdfd1a7..f1292a2f07 100644 --- a/modules/angular2/src/compiler/util.ts +++ b/modules/angular2/src/compiler/util.ts @@ -1,20 +1,9 @@ -import { - IS_DART, - StringWrapper, - isBlank, - isPresent, - isString, - isArray -} from 'angular2/src/facade/lang'; +import {IS_DART, StringWrapper, Math, isBlank} from 'angular2/src/facade/lang'; + +export var MODULE_SUFFIX = IS_DART ? '.dart' : ''; var CAMEL_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 { return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP, @@ -26,74 +15,6 @@ export function dashCaseToCamelCase(input: string): string { (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[] { var parts = StringWrapper.split(input.trim(), /\s*:\s*/g); if (parts.length > 1) { @@ -102,77 +23,3 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] { 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 && (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; -} diff --git a/modules/angular2/src/compiler/view_compiler.ts b/modules/angular2/src/compiler/view_compiler.ts deleted file mode 100644 index 9c7f328931..0000000000 --- a/modules/angular2/src/compiler/view_compiler.ts +++ /dev/null @@ -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, - protoViews: CompileProtoView[], - 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[], - 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 { - 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 { - private _nextVarId: number = 0; - constructor(public component: CompileDirectiveMetadata, public styles: SourceExpression, - public protoViews: CompileProtoView[], - 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( - 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 { - constructor(public component: CompileDirectiveMetadata, public styles: Array, - public protoViews: CompileProtoView[], - 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>, - 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 = parentRenderer; - } - var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex](); - var view = - new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes, - containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector); - var visitor = new ViewBuilderVisitor( - 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 { - public contentNodesByNgContentIndex: Array[]; - - 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 implements TemplateAstVisitor { - renderStmts: Array = []; - renderNodes: EXPRESSION[] = []; - appStmts: Array = []; - 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, - public targetStatements: STATEMENT[], - public factory: ViewFactory) {} - - private _addRenderNode(renderNode: EXPRESSION, appEl: EXPRESSION, ngContentIndex: number, - parent: ParentElement) { - 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 { - return isPresent(parent.component) && - parent.component.template.encapsulation !== ViewEncapsulation.Native ? - null : - parent.renderNode; - } - - visitBoundText(ast: BoundTextAst, parent: ParentElement): any { - return this._visitText('', ast.ngContentIndex, parent); - } - visitText(ast: TextAst, parent: ParentElement): any { - return this._visitText(ast.value, ast.ngContentIndex, parent); - } - private _visitText(value: string, ngContentIndex: number, parent: ParentElement) { - 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): 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): 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( - 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): 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}`; - } -} diff --git a/modules/angular2/src/compiler/view_compiler/compile_binding.ts b/modules/angular2/src/compiler/view_compiler/compile_binding.ts new file mode 100644 index 0000000000..c56c2ebcf0 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/compile_binding.ts @@ -0,0 +1,6 @@ +import {CompileNode} from './compile_element'; +import {TemplateAst} from '../template_ast'; + +export class CompileBinding { + constructor(public node: CompileNode, public sourceAst: TemplateAst) {} +} diff --git a/modules/angular2/src/compiler/view_compiler/compile_element.ts b/modules/angular2/src/compiler/view_compiler/compile_element.ts new file mode 100644 index 0000000000..c1f5ad32d5 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/compile_element.ts @@ -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(); + private _resolvedProviders: CompileTokenMap; + + private _queryCount = 0; + private _queries = new CompileTokenMap(); + private _componentConstructorViewQueryLists: o.Expression[] = []; + + public contentNodesByNgContentIndex: Array[] = 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(); + 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); +} diff --git a/modules/angular2/src/compiler/view_compiler/compile_method.ts b/modules/angular2/src/compiler/view_compiler/compile_method.ts new file mode 100644 index 0000000000..85bb07a0c3 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/compile_method.ts @@ -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; } +} diff --git a/modules/angular2/src/compiler/view_compiler/compile_query.ts b/modules/angular2/src/compiler/view_compiler/compile_query.ts new file mode 100644 index 0000000000..22f920d9d3 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/compile_query.ts @@ -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) {} +} + +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 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, query: CompileQuery) { + query.meta.selectors.forEach((selector) => { + var entry = map.get(selector); + if (isBlank(entry)) { + entry = []; + map.add(selector, entry); + } + entry.push(query); + }); +} diff --git a/modules/angular2/src/compiler/view_compiler/compile_view.ts b/modules/angular2/src/compiler/view_compiler/compile_view.ts new file mode 100644 index 0000000000..36d3ad58be --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/compile_view.ts @@ -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; + public namedAppElements: Array> = []; + + 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(); + public variables = new Map(); + 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(); + 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>): 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; + } +} diff --git a/modules/angular2/src/compiler/view_compiler/constants.ts b/modules/angular2/src/compiler/view_compiler/constants.ts new file mode 100644 index 0000000000..596fd7f095 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/constants.ts @@ -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`); +} diff --git a/modules/angular2/src/compiler/view_compiler/event_binder.ts b/modules/angular2/src/compiler/view_compiler/event_binder.ts new file mode 100644 index 0000000000..3f59de70bf --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/event_binder.ts @@ -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 = + ([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, '_'); +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/view_compiler/expression_converter.ts b/modules/angular2/src/compiler/view_compiler/expression_converter.ts new file mode 100644 index 0000000000..c50f19595b --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/expression_converter.ts @@ -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>): 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)) { + (arg).forEach((entry) => flattenStatements(entry, output)); + } else { + output.push(arg); + } +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/view_compiler/lifecycle_binder.ts b/modules/angular2/src/compiler/view_compiler/lifecycle_binder.ts new file mode 100644 index 0000000000..a70428190e --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/lifecycle_binder.ts @@ -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()); + } +} diff --git a/modules/angular2/src/compiler/view_compiler/property_binder.ts b/modules/angular2/src/compiler/view_compiler/property_binder.ts new file mode 100644 index 0000000000..8184ee2853 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/property_binder.ts @@ -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.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(); +} diff --git a/modules/angular2/src/compiler/view_compiler/test.js b/modules/angular2/src/compiler/view_compiler/test.js new file mode 100644 index 0000000000..0ec0517202 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/test.js @@ -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 \ No newline at end of file diff --git a/modules/angular2/src/compiler/view_compiler/util.ts b/modules/angular2/src/compiler/view_compiler/util.ts new file mode 100644 index 0000000000..8fd2435906 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/util.ts @@ -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; +} diff --git a/modules/angular2/src/compiler/view_compiler/view_binder.ts b/modules/angular2/src/compiler/view_compiler/view_binder.ts new file mode 100644 index 0000000000..9d45c833e7 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/view_binder.ts @@ -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 = 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 = 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): 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; } +} diff --git a/modules/angular2/src/compiler/view_compiler/view_builder.ts b/modules/angular2/src/compiler/view_compiler/view_builder.ts new file mode 100644 index 0000000000..53ba1c41e3 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/view_builder.ts @@ -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): 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( + (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; +} \ No newline at end of file diff --git a/modules/angular2/src/compiler/view_compiler/view_compiler.ts b/modules/angular2/src/compiler/view_compiler/view_compiler.ts new file mode 100644 index 0000000000..39cc3e7169 --- /dev/null +++ b/modules/angular2/src/compiler/view_compiler/view_compiler.ts @@ -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); + } +} diff --git a/modules/angular2/src/core/linker/view_resolver.ts b/modules/angular2/src/compiler/view_resolver.ts similarity index 96% rename from modules/angular2/src/core/linker/view_resolver.ts rename to modules/angular2/src/compiler/view_resolver.ts index 56340b1363..81615facb1 100644 --- a/modules/angular2/src/core/linker/view_resolver.ts +++ b/modules/angular2/src/compiler/view_resolver.ts @@ -1,6 +1,6 @@ import {Injectable} from 'angular2/src/core/di'; -import {ViewMetadata} from '../metadata/view'; -import {ComponentMetadata} from '../metadata/directives'; +import {ViewMetadata} from 'angular2/src/core/metadata/view'; +import {ComponentMetadata} from 'angular2/src/core/metadata/directives'; import {Type, stringify, isBlank, isPresent} from 'angular2/src/facade/lang'; import {BaseException} from 'angular2/src/facade/exceptions'; diff --git a/modules/angular2/src/core/application_common_providers.ts b/modules/angular2/src/core/application_common_providers.ts index 778e5799f4..f96c3d3cec 100644 --- a/modules/angular2/src/core/application_common_providers.ts +++ b/modules/angular2/src/core/application_common_providers.ts @@ -11,17 +11,15 @@ import { KeyValueDiffers, defaultKeyValueDiffers } 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 {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 {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 * 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 = CONST_EXPR([ new Provider(Compiler, {useClass: Compiler_}), APP_ID_RANDOM_PROVIDER, - ResolvedMetadataCache, new Provider(AppViewManager, {useClass: AppViewManager_}), - ViewResolver, new Provider(IterableDiffers, {useValue: defaultIterableDiffers}), new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}), - DirectiveResolver, - PipeResolver, new Provider(DynamicComponentLoader, {useClass: DynamicComponentLoader_}) ]); \ No newline at end of file diff --git a/modules/angular2/src/core/application_ref.ts b/modules/angular2/src/core/application_ref.ts index c670f7d24f..075234b48a 100644 --- a/modules/angular2/src/core/application_ref.ts +++ b/modules/angular2/src/core/application_ref.ts @@ -457,8 +457,7 @@ export class ApplicationRef_ extends ApplicationRef { /** @internal */ _loadComponent(componentRef: ComponentRef): void { - var appChangeDetector = - (componentRef.location).internalElement.parentView.changeDetector; + var appChangeDetector = (componentRef.location).internalElement.parentView; this._changeDetectorRefs.push(appChangeDetector.ref); this.tick(); this._rootComponents.push(componentRef); @@ -471,7 +470,7 @@ export class ApplicationRef_ extends ApplicationRef { return; } this.unregisterChangeDetector( - (componentRef.location).internalElement.parentView.changeDetector.ref); + (componentRef.location).internalElement.parentView.ref); ListWrapper.remove(this._rootComponents, componentRef); } diff --git a/modules/angular2/src/core/change_detection.ts b/modules/angular2/src/core/change_detection.ts index 737bb34786..63b0f48d28 100644 --- a/modules/angular2/src/core/change_detection.ts +++ b/modules/angular2/src/core/change_detection.ts @@ -7,9 +7,6 @@ export { ChangeDetectionStrategy, - ExpressionChangedAfterItHasBeenCheckedException, - ChangeDetectionError, - ChangeDetectorRef, WrappedValue, diff --git a/modules/angular2/src/core/change_detection/abstract_change_detector.ts b/modules/angular2/src/core/change_detection/abstract_change_detector.ts deleted file mode 100644 index cb798affe7..0000000000 --- a/modules/angular2/src/core/change_detection/abstract_change_detector.ts +++ /dev/null @@ -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 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(); - 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]; - } -} diff --git a/modules/angular2/src/core/change_detection/binding_record.ts b/modules/angular2/src/core/change_detection/binding_record.ts deleted file mode 100644 index b6e3ce9b29..0000000000 --- a/modules/angular2/src/core/change_detection/binding_record.ts +++ /dev/null @@ -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); - } -} diff --git a/modules/angular2/src/core/change_detection/change_detection.ts b/modules/angular2/src/core/change_detection/change_detection.ts index 34a1f2c78c..06a93187be 100644 --- a/modules/angular2/src/core/change_detection/change_detection.ts +++ b/modules/angular2/src/core/change_detection/change_detection.ts @@ -15,38 +15,14 @@ export { DefaultIterableDifferFactory, CollectionChangeRecord } 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 { - DehydratedException, - ExpressionChangedAfterItHasBeenCheckedException, - ChangeDetectionError -} from './exceptions'; -export { - ProtoChangeDetector, - 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'; + ChangeDetectionStrategy, + CHANGE_DETECTION_STRATEGY_VALUES, + ChangeDetectorState, + CHANGE_DETECTOR_STATE_VALUES, + isDefaultChangeDetectionStrategy +} from './constants'; export {ChangeDetectorRef} from './change_detector_ref'; export { IterableDiffers, @@ -56,7 +32,15 @@ export { } from './differs/iterable_differs'; export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs'; 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. diff --git a/modules/angular2/src/core/change_detection/change_detection_jit_generator.dart b/modules/angular2/src/core/change_detection/change_detection_jit_generator.dart deleted file mode 100644 index d2ec55ce8c..0000000000 --- a/modules/angular2/src/core/change_detection/change_detection_jit_generator.dart +++ /dev/null @@ -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; -} diff --git a/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts b/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts deleted file mode 100644 index 238ee94c01..0000000000 --- a/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts +++ /dev/null @@ -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; - } -} diff --git a/modules/angular2/src/core/change_detection/change_detection_util.ts b/modules/angular2/src/core/change_detection/change_detection_util.ts index 88c92273b9..d2b41211ee 100644 --- a/modules/angular2/src/core/change_detection/change_detection_util.ts +++ b/modules/angular2/src/core/change_detection/change_detection_util.ts @@ -1,27 +1,25 @@ +import {CONST_EXPR, isBlank, looseIdentical, isPrimitive} from 'angular2/src/facade/lang'; 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, isListLikeIterable, areIterablesEqual } 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(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 @@ -44,22 +42,25 @@ import {SelectedPipe} from './pipes'; export class WrappedValue { constructor(public wrapped: any) {} - static wrap(value: any): WrappedValue { - var w = _wrappedValues[_wrappedIndex++ % 5]; - w.wrapped = value; - return w; - } + static wrap(value: any): WrappedValue { return new WrappedValue(value); } } -var _wrappedValues = [ - new WrappedValue(null), - new WrappedValue(null), - new WrappedValue(null), - new WrappedValue(null), - new WrappedValue(null) -]; +/** + * Helper class for unwrapping WrappedValue s + */ +export class ValueUnwrapper { + public hasWrappedValue = false; -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. @@ -70,140 +71,5 @@ export class SimpleChange { /** * Check whether the new value is the first value assigned. */ - isFirstChange(): boolean { return this.previousValue === ChangeDetectionUtil.uninitialized; } -} - -function _simpleChange(previousValue, currentValue): SimpleChange { - return new SimpleChange(previousValue, currentValue); -} - -/* tslint:disable:requireParameterType */ -export class ChangeDetectionUtil { - static uninitialized: Object = CONST_EXPR(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)) { - (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); - } - } + isFirstChange(): boolean { return this.previousValue === uninitialized; } } diff --git a/modules/angular2/src/core/change_detection/change_detector_ref.ts b/modules/angular2/src/core/change_detection/change_detector_ref.ts index e34fe44857..eec6f0e834 100644 --- a/modules/angular2/src/core/change_detection/change_detector_ref.ts +++ b/modules/angular2/src/core/change_detection/change_detector_ref.ts @@ -1,6 +1,3 @@ -import {ChangeDetector} from './interfaces'; -import {ChangeDetectionStrategy} from './constants'; - export abstract class ChangeDetectorRef { /** * Marks all {@link ChangeDetectionStrategy#OnPush} ancestors as to be checked. @@ -193,16 +190,3 @@ export abstract class ChangeDetectorRef { */ 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(); - } -} \ No newline at end of file diff --git a/modules/angular2/src/core/change_detection/coalesce.ts b/modules/angular2/src/core/change_detection/coalesce.ts deleted file mode 100644 index 1a0832ecf3..0000000000 --- a/modules/angular2/src/core/change_detection/coalesce.ts +++ /dev/null @@ -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 = new Map(); - 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 = new Map(); - - for (let protoIndex = 0; protoIndex < srcRecords.length; protoIndex++) { - let skipRecord = skipSources[protoIndex]; - if (isPresent(skipRecord)) { - skipRecord.fixedArgs[0] = dstRecords.length; - } - - let src = srcRecords[protoIndex]; - - if (src.isSkipRecord()) { - if (src.isConditionalSkipRecord() && src.fixedArgs[0] === protoIndex + 2 && - protoIndex < srcRecords.length - 1 && - srcRecords[protoIndex + 1].mode === RecordType.SkipRecords) { - src.mode = src.mode === RecordType.SkipRecordsIf ? RecordType.SkipRecordsIfNot : - RecordType.SkipRecordsIf; - src.fixedArgs[0] = srcRecords[protoIndex + 1].fixedArgs[0]; - protoIndex++; - } - - if (src.fixedArgs[0] > protoIndex + 1) { - let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap); - dstRecords.push(dst); - skipSources[dst.fixedArgs[0]] = dst; - } - - } else { - let dst = _cloneAndUpdateIndexes(src, dstRecords, indexMap); - dstRecords.push(dst); - indexMap.set(src.selfIndex, dst.selfIndex); - } - } - - return dstRecords; -} - -/** - * Add a new record or re-use one of the existing records. - */ -function _mayBeAddRecord(record: 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): 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, 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; -} diff --git a/modules/angular2/src/core/change_detection/codegen_facade.dart b/modules/angular2/src/core/change_detection/codegen_facade.dart deleted file mode 100644 index 5e995a87b1..0000000000 --- a/modules/angular2/src/core/change_detection/codegen_facade.dart +++ /dev/null @@ -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 vals) { - return '"${vals.map((v) => '\${$v}').join('')}"'; -} - -String rawString(String str) { - return "r'$str'"; -} diff --git a/modules/angular2/src/core/change_detection/codegen_facade.ts b/modules/angular2/src/core/change_detection/codegen_facade.ts deleted file mode 100644 index 2ff2cad549..0000000000 --- a/modules/angular2/src/core/change_detection/codegen_facade.ts +++ /dev/null @@ -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(' + '); -} diff --git a/modules/angular2/src/core/change_detection/codegen_logic_util.ts b/modules/angular2/src/core/change_detection/codegen_logic_util.ts deleted file mode 100644 index ff3f147f13..0000000000 --- a/modules/angular2/src/core/change_detection/codegen_logic_util.ts +++ /dev/null @@ -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; - } -} diff --git a/modules/angular2/src/core/change_detection/codegen_name_util.ts b/modules/angular2/src/core/change_detection/codegen_name_util.ts deleted file mode 100644 index 0c423edd32..0000000000 --- a/modules/angular2/src/core/change_detection/codegen_name_util.ts +++ /dev/null @@ -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(); - - 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}`); } -} diff --git a/modules/angular2/src/core/change_detection/constants.ts b/modules/angular2/src/core/change_detection/constants.ts index 28b9e70a58..a4a13037e1 100644 --- a/modules/angular2/src/core/change_detection/constants.ts +++ b/modules/angular2/src/core/change_detection/constants.ts @@ -24,6 +24,7 @@ export enum ChangeDetectorState { Errored } + /** * Describes within the change detector which strategy will be used the next time change * detection is triggered. diff --git a/modules/angular2/src/core/change_detection/directive_record.ts b/modules/angular2/src/core/change_detection/directive_record.ts deleted file mode 100644 index 53c6f2e6cd..0000000000 --- a/modules/angular2/src/core/change_detection/directive_record.ts +++ /dev/null @@ -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); - } -} diff --git a/modules/angular2/src/core/change_detection/dynamic_change_detector.ts b/modules/angular2/src/core/change_detection/dynamic_change_detector.ts deleted file mode 100644 index e9d58aba66..0000000000 --- a/modules/angular2/src/core/change_detection/dynamic_change_detector.ts +++ /dev/null @@ -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 { - 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 = - 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; - } -} diff --git a/modules/angular2/src/core/change_detection/event_binding.ts b/modules/angular2/src/core/change_detection/event_binding.ts deleted file mode 100644 index 52682ced2d..0000000000 --- a/modules/angular2/src/core/change_detection/event_binding.ts +++ /dev/null @@ -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[]) {} -} \ No newline at end of file diff --git a/modules/angular2/src/core/change_detection/exceptions.ts b/modules/angular2/src/core/change_detection/exceptions.ts deleted file mode 100644 index b80ed08b8e..0000000000 --- a/modules/angular2/src/core/change_detection/exceptions.ts +++ /dev/null @@ -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. - * - * - * - * ### Example - * - * ```typescript - * @Component({ - * selector: 'parent', - * template: ` - * - * `, - * 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: ` - * - * `, - * 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) {} -} diff --git a/modules/angular2/src/core/change_detection/interfaces.ts b/modules/angular2/src/core/change_detection/interfaces.ts deleted file mode 100644 index 0f3c165f56..0000000000 --- a/modules/angular2/src/core/change_detection/interfaces.ts +++ /dev/null @@ -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) {} -} diff --git a/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart b/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart deleted file mode 100644 index 5fb02a8e4a..0000000000 --- a/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart +++ /dev/null @@ -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"; - } -} diff --git a/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts b/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts deleted file mode 100644 index bd747c4931..0000000000 --- a/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts +++ /dev/null @@ -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(); - } -} diff --git a/modules/angular2/src/core/change_detection/parser/ast.ts b/modules/angular2/src/core/change_detection/parser/ast.ts deleted file mode 100644 index 071506ab2c..0000000000 --- a/modules/angular2/src/core/change_detection/parser/ast.ts +++ /dev/null @@ -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); - } -} diff --git a/modules/angular2/src/core/change_detection/parser/locals.ts b/modules/angular2/src/core/change_detection/parser/locals.ts deleted file mode 100644 index fb7fbd2fbd..0000000000 --- a/modules/angular2/src/core/change_detection/parser/locals.ts +++ /dev/null @@ -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) {} - - 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); } -} diff --git a/modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.dart b/modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.dart deleted file mode 100644 index 1aa45742b5..0000000000 --- a/modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.dart +++ /dev/null @@ -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; diff --git a/modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.ts b/modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.ts deleted file mode 100644 index 21ea94379b..0000000000 --- a/modules/angular2/src/core/change_detection/pipe_lifecycle_reflector.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function implementsOnDestroy(pipe: any): boolean { - return pipe.constructor.prototype.ngOnDestroy; -} diff --git a/modules/angular2/src/core/change_detection/pipes.ts b/modules/angular2/src/core/change_detection/pipes.ts deleted file mode 100644 index b23f25d34b..0000000000 --- a/modules/angular2/src/core/change_detection/pipes.ts +++ /dev/null @@ -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) {} -} \ No newline at end of file diff --git a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart b/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart deleted file mode 100644 index 301801ddba..0000000000 --- a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart +++ /dev/null @@ -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; diff --git a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts b/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts deleted file mode 100644 index da60b225c9..0000000000 --- a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts +++ /dev/null @@ -1 +0,0 @@ -// empty file as we only need the dart version \ No newline at end of file diff --git a/modules/angular2/src/core/change_detection/proto_change_detector.ts b/modules/angular2/src/core/change_detection/proto_change_detector.ts deleted file mode 100644 index 2f2d7c22cd..0000000000 --- a/modules/angular2/src/core/change_detection/proto_change_detector.ts +++ /dev/null @@ -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`); - } -} diff --git a/modules/angular2/src/core/change_detection/proto_record.ts b/modules/angular2/src/core/change_detection/proto_record.ts deleted file mode 100644 index f3fd76f906..0000000000 --- a/modules/angular2/src/core/change_detection/proto_record.ts +++ /dev/null @@ -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; } -} diff --git a/modules/angular2/src/core/debug/debug_node.ts b/modules/angular2/src/core/debug/debug_node.ts index 87634f09d3..49163ebfde 100644 --- a/modules/angular2/src/core/debug/debug_node.ts +++ b/modules/angular2/src/core/debug/debug_node.ts @@ -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 {Injector} from 'angular2/src/core/di'; import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; @@ -10,12 +10,8 @@ export class DebugNode { nativeNode: any; listeners: EventListener[]; parent: DebugElement; - providerTokens: any[]; - locals: Map; - injector: Injector; - componentInstance: any; - constructor(nativeNode: any, parent: DebugNode) { + constructor(nativeNode: any, parent: DebugNode, private _debugInfo: RenderDebugInfo) { this.nativeNode = nativeNode; if (isPresent(parent) && parent instanceof DebugElement) { parent.addChild(this); @@ -23,32 +19,40 @@ export class DebugNode { this.parent = null; } this.listeners = []; - this.providerTokens = []; } - setDebugInfo(info: RenderDebugInfo) { - this.injector = info.injector; - this.providerTokens = info.providerTokens; - this.locals = info.locals; - this.componentInstance = info.component; + get injector(): Injector { return isPresent(this._debugInfo) ? this._debugInfo.injector : null; } + + get componentInstance(): any { + return isPresent(this._debugInfo) ? this._debugInfo.component : null; } + 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); } - getLocal(name: string): any { return this.locals.get(name); } + getLocal(name: string): any { return this.locals[name]; } } export class DebugElement extends DebugNode { name: string; - properties: Map; - attributes: Map; + properties: {[key: string]: string}; + attributes: {[key: string]: string}; childNodes: DebugNode[]; nativeElement: any; - constructor(nativeNode: any, parent: any) { - super(nativeNode, parent); - this.properties = new Map(); - this.attributes = new Map(); + constructor(nativeNode: any, parent: any, _debugInfo: RenderDebugInfo) { + super(nativeNode, parent, _debugInfo); + this.properties = {}; + this.attributes = {}; this.childNodes = []; this.nativeElement = nativeNode; } @@ -112,7 +116,7 @@ export class DebugElement extends DebugNode { return children; } - triggerEventHandler(eventName: string, eventObj: Event) { + triggerEventHandler(eventName: string, eventObj: any) { this.listeners.forEach((listener) => { if (listener.name == eventName) { listener.callback(eventObj); diff --git a/modules/angular2/src/core/debug/debug_renderer.ts b/modules/angular2/src/core/debug/debug_renderer.ts index c68c99352e..d0f65e4d56 100644 --- a/modules/angular2/src/core/debug/debug_renderer.ts +++ b/modules/angular2/src/core/debug/debug_renderer.ts @@ -18,27 +18,23 @@ export class DebugDomRootRenderer implements RootRenderer { constructor(private _delegate: RootRenderer) {} renderComponent(componentProto: RenderComponentType): Renderer { - return new DebugDomRenderer(this, this._delegate.renderComponent(componentProto)); + return new DebugDomRenderer(this._delegate.renderComponent(componentProto)); } } export class DebugDomRenderer implements Renderer { - constructor(private _rootRenderer: DebugDomRootRenderer, private _delegate: Renderer) {} + constructor(private _delegate: Renderer) {} - renderComponent(componentType: RenderComponentType): Renderer { - return this._rootRenderer.renderComponent(componentType); - } - - selectRootElement(selector: string): any { - var nativeEl = this._delegate.selectRootElement(selector); - var debugEl = new DebugElement(nativeEl, null); + selectRootElement(selector: string, debugInfo: RenderDebugInfo): any { + var nativeEl = this._delegate.selectRootElement(selector, debugInfo); + var debugEl = new DebugElement(nativeEl, null, debugInfo); indexDebugNode(debugEl); return nativeEl; } - createElement(parentElement: any, name: string): any { - var nativeEl = this._delegate.createElement(parentElement, name); - var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement)); + createElement(parentElement: any, name: string, debugInfo: RenderDebugInfo): any { + var nativeEl = this._delegate.createElement(parentElement, name, debugInfo); + var debugEl = new DebugElement(nativeEl, getDebugNode(parentElement), debugInfo); debugEl.name = name; indexDebugNode(debugEl); return nativeEl; @@ -46,16 +42,16 @@ export class DebugDomRenderer implements Renderer { createViewRoot(hostElement: any): any { return this._delegate.createViewRoot(hostElement); } - createTemplateAnchor(parentElement: any): any { - var comment = this._delegate.createTemplateAnchor(parentElement); - var debugEl = new DebugNode(comment, getDebugNode(parentElement)); + createTemplateAnchor(parentElement: any, debugInfo: RenderDebugInfo): any { + var comment = this._delegate.createTemplateAnchor(parentElement, debugInfo); + var debugEl = new DebugNode(comment, getDebugNode(parentElement), debugInfo); indexDebugNode(debugEl); return comment; } - createText(parentElement: any, value: string): any { - var text = this._delegate.createText(parentElement, value); - var debugEl = new DebugNode(text, getDebugNode(parentElement)); + createText(parentElement: any, value: string, debugInfo: RenderDebugInfo): any { + var text = this._delegate.createText(parentElement, value, debugInfo); + var debugEl = new DebugNode(text, getDebugNode(parentElement), debugInfo); indexDebugNode(debugEl); return text; } @@ -112,7 +108,7 @@ export class DebugDomRenderer implements Renderer { setElementProperty(renderElement: any, propertyName: string, propertyValue: any) { var debugEl = getDebugNode(renderElement); if (isPresent(debugEl) && debugEl instanceof DebugElement) { - debugEl.properties.set(propertyName, propertyValue); + debugEl.properties[propertyName] = propertyValue; } this._delegate.setElementProperty(renderElement, propertyName, propertyValue); } @@ -120,28 +116,15 @@ export class DebugDomRenderer implements Renderer { setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) { var debugEl = getDebugNode(renderElement); if (isPresent(debugEl) && debugEl instanceof DebugElement) { - debugEl.attributes.set(attributeName, attributeValue); + debugEl.attributes[attributeName] = attributeValue; } this._delegate.setElementAttribute(renderElement, attributeName, attributeValue); } - /** - * Used only in debug mode to serialize property changes to comment nodes, - * such as