From dcdddc63a34b8ee157dfe6a2a887dabe0fa7bce2 Mon Sep 17 00:00:00 2001 From: tycho01 Date: Wed, 18 May 2016 13:50:32 +0800 Subject: [PATCH] fix(Compiler): relax childIsRecursive check Fix how the compiler checks for recursive components by also considering component descendants. Previously, it only checked if the current component was evaluated previously. This failed in certain cases of mutually recursive components, causing `createAsync` in tests to not resolve. closes [7084](https://github.com/angular/angular/issues/7084) --- .../@angular/compiler/src/runtime_compiler.ts | 5 +- .../linker/regression_integration_spec.ts | 71 ++++++++++++++----- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/modules/@angular/compiler/src/runtime_compiler.ts b/modules/@angular/compiler/src/runtime_compiler.ts index 504eab0074f4c6..004f59671cccc6 100644 --- a/modules/@angular/compiler/src/runtime_compiler.ts +++ b/modules/@angular/compiler/src/runtime_compiler.ts @@ -115,7 +115,10 @@ export class RuntimeCompiler implements ComponentResolver { this._metadataResolver.getViewDirectivesMetadata(dep.comp.type.runtime); var childViewPipes: CompilePipeMetadata[] = this._metadataResolver.getViewPipesMetadata(dep.comp.type.runtime); - var childIsRecursive = ListWrapper.contains(childCompilingComponentsPath, childCacheKey); + var childIsRecursive = childCompilingComponentsPath.indexOf(childCacheKey) > -1 || + childViewDirectives.some(dir => + childCompilingComponentsPath.indexOf(dir.type.runtime) > -1 + ); childCompilingComponentsPath.push(childCacheKey); var childComp = this._loadAndCompileComponent( diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index 71eb42383bc0bb..85353d1741fea4 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -4,7 +4,7 @@ import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; import {IS_DART} from '../../src/facade/lang'; -import {Component, Pipe, PipeTransform, provide, ViewMetadata, OpaqueToken, Injector} from '@angular/core'; +import {Component, Pipe, PipeTransform, provide, ViewMetadata, PLATFORM_PIPES, OpaqueToken, Injector, forwardRef} from '@angular/core'; import {NgIf, NgClass} from '@angular/common'; import {CompilerConfig} from '@angular/compiler'; @@ -161,22 +161,28 @@ function declareTests({useJit}: {useJit: boolean}) { })); it('should support ngClass before a component and content projection inside of an ngIf', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView( - MyComp1, new ViewMetadata({ - template: `ABC`, - directives: [NgClass, NgIf, CmpWithNgContent] - })) - .createAsync(MyComp1) - .then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('ABC'); - async.done(); - }); - })); - + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + tcb.overrideView( + MyComp1, new ViewMetadata({ + template: `ABC`, + directives: [NgClass, NgIf, CmpWithNgContent] + })) + .createAsync(MyComp1) + .then((fixture) => { + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('ABC'); + async.done(); + }); + })); + + it('should handle mutual recursion entered from multiple sides - #7084', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + tcb.createAsync(FakeRecursiveComp).then((fixture) => { + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('[]'); + async.done(); + }); + })); }); } @@ -199,3 +205,34 @@ class CustomPipe implements PipeTransform { @Component({selector: 'cmp-content', template: ``}) class CmpWithNgContent { } + +@Component({ + selector: 'left', + template: `L`, + directives: [ + forwardRef(() => RightComp), + ] +}) +class LeftComp { +} + +@Component({ + selector: 'right', + template: `R`, + directives: [ + forwardRef(() => LeftComp), + ] +}) +class RightComp { +} + +@Component({ + selector: 'fakeRecursiveComp', + template: `[]`, + directives: [ + forwardRef(() => LeftComp), + forwardRef(() => RightComp), + ] +}) +export class FakeRecursiveComp { +}