fix(ivy): support schemas at runtime (#28637)

Accounts for schemas in when validating properties in Ivy.

This PR resolves FW-819.

A couple of notes:
* I had to rework the test slightly, in order to have it fail when we expect it to. The one in master is passing since Ivy's validation runs during the update phase, rather than creation.
* I had to deviate from the design in FW-819 and not add an `enableSchema` instruction, because the schema is part of the `NgModule` scope, however the scope is only assigned to a component once all of the module's declarations have been resolved and some of them can be async. Instead, I opted to have the `schemas` on the component definition.

PR Close #28637
This commit is contained in:
Kristiyan Kostadinov 2019-02-12 00:03:04 +01:00 committed by Miško Hevery
parent 7cbc36fdac
commit 80a5934af6
18 changed files with 162 additions and 76 deletions

View File

@ -134,6 +134,8 @@ export class NgModuleDecoratorHandler implements DecoratorHandler<NgModuleAnalys
exports: exports.map(exp => this._toR3Reference(exp, valueContext, typeContext)),
imports: imports.map(imp => this._toR3Reference(imp, valueContext, typeContext)),
emitInline: false,
// TODO: to be implemented as a part of FW-1004.
schemas: [],
};
const providers: Expression = ngModule.has('providers') ?

View File

@ -97,6 +97,7 @@ export interface R3NgModuleMetadataFacade {
imports: Function[];
exports: Function[];
emitInline: boolean;
schemas: {name: string}[]|null;
}
export interface R3InjectorMetadataFacade {

View File

@ -86,6 +86,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
imports: facade.imports.map(wrapReference),
exports: facade.exports.map(wrapReference),
emitInline: true,
schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
};
const res = compileNgModule(meta);
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);

View File

@ -57,13 +57,18 @@ export interface R3NgModuleMetadata {
* does not allow components to be tree-shaken, but is useful for JIT mode.
*/
emitInline: boolean;
/**
* The set of schemas that declare elements to be allowed in the NgModule.
*/
schemas: R3Reference[]|null;
}
/**
* Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
*/
export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
const {type: moduleType, bootstrap, declarations, imports, exports} = meta;
const {type: moduleType, bootstrap, declarations, imports, exports, schemas} = meta;
const definitionMap = {
type: moduleType
} as{
@ -71,7 +76,8 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
bootstrap: o.LiteralArrayExpr,
declarations: o.LiteralArrayExpr,
imports: o.LiteralArrayExpr,
exports: o.LiteralArrayExpr
exports: o.LiteralArrayExpr,
schemas: o.LiteralArrayExpr
};
// Only generate the keys in the metadata if the arrays have values.
@ -91,6 +97,10 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
definitionMap.exports = o.literalArr(exports.map(ref => ref.value));
}
if (schemas && schemas.length) {
definitionMap.schemas = o.literalArr(schemas.map(ref => ref.value));
}
const expression = o.importExpr(R3.defineNgModule).callFn([mapToMapExpression(definitionMap)]);
const type = new o.ExpressionType(o.importExpr(R3.NgModuleDefWithMeta, [
new o.ExpressionType(moduleType), tupleTypeOf(declarations), tupleTypeOf(imports),

View File

@ -97,6 +97,7 @@ export interface R3NgModuleMetadataFacade {
imports: Function[];
exports: Function[];
emitInline: boolean;
schemas: {name: string}[]|null;
}
export interface R3InjectorMetadataFacade {

View File

@ -14,12 +14,14 @@
import {Attribute} from './di';
import {ContentChild, ContentChildren, Query, ViewChild, ViewChildren} from './metadata/di';
import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
import {DoBootstrap, ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module';
import {DoBootstrap, ModuleWithProviders, NgModule} from './metadata/ng_module';
import {SchemaMetadata} from './metadata/schema';
import {ViewEncapsulation} from './metadata/view';
export {Attribute} from './di';
export {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './interface/lifecycle_hooks';
export {ANALYZE_FOR_ENTRY_COMPONENTS, ContentChild, ContentChildDecorator, ContentChildren, ContentChildrenDecorator, Query, ViewChild, ViewChildDecorator, ViewChildren, ViewChildrenDecorator} from './metadata/di';
export {Component, ComponentDecorator, Directive, DirectiveDecorator, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
export {CUSTOM_ELEMENTS_SCHEMA, DoBootstrap, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModule, SchemaMetadata} from './metadata/ng_module';
export {DoBootstrap, ModuleWithProviders, NgModule} from './metadata/ng_module';
export {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from './metadata/schema';
export {ViewEncapsulation} from './metadata/view';

View File

@ -11,6 +11,7 @@ import {InjectorType, defineInjector} from '../di/interface/defs';
import {Provider} from '../di/interface/provider';
import {convertInjectableProviderToFactory} from '../di/util';
import {Type} from '../interface/type';
import {SchemaMetadata} from '../metadata/schema';
import {NgModuleType} from '../render3';
import {compileNgModule as render3CompileNgModule} from '../render3/jit/module';
import {TypeDecorator, makeDecorator} from '../util/decorators';
@ -28,6 +29,7 @@ import {TypeDecorator, makeDecorator} from '../util/decorators';
export interface NgModuleTransitiveScopes {
compilation: {directives: Set<any>; pipes: Set<any>;};
exported: {directives: Set<any>; pipes: Set<any>;};
schemas: SchemaMetadata[]|null;
}
export type NgModuleDefWithMeta<T, Declarations, Imports, Exports> = NgModuleDef<T>;
@ -67,6 +69,9 @@ export interface NgModuleDef<T> {
* This should never be read directly, but accessed via `transitiveScopesFor`.
*/
transitiveCompileScopes: NgModuleTransitiveScopes|null;
/** The set of schemas that declare elements to be allowed in the NgModule. */
schemas: SchemaMetadata[]|null;
}
/**
@ -83,38 +88,6 @@ export interface ModuleWithProviders<
providers?: Provider[];
}
/**
* A schema definition associated with an NgModule.
*
* @see `@NgModule`, `CUSTOM_ELEMENTS_SCHEMA`, `NO_ERRORS_SCHEMA`
*
* @param name The name of a defined schema.
*
* @publicApi
*/
export interface SchemaMetadata { name: string; }
/**
* Defines a schema that allows an NgModule to contain the following:
* - Non-Angular elements named with dash case (`-`).
* - Element properties named with dash case (`-`).
* Dash case is the naming convention for custom elements.
*
* @publicApi
*/
export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = {
name: 'custom-elements'
};
/**
* Defines a schema that allows any property on any element.
*
* @publicApi
*/
export const NO_ERRORS_SCHEMA: SchemaMetadata = {
name: 'no-errors-schema'
};
/**
* Type of the NgModule decorator / constructor function.

View File

@ -0,0 +1,40 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* A schema definition associated with an NgModule.
*
* @see `@NgModule`, `CUSTOM_ELEMENTS_SCHEMA`, `NO_ERRORS_SCHEMA`
*
* @param name The name of a defined schema.
*
* @publicApi
*/
export interface SchemaMetadata { name: string; }
/**
* Defines a schema that allows an NgModule to contain the following:
* - Non-Angular elements named with dash case (`-`).
* - Element properties named with dash case (`-`).
* Dash case is the naming convention for custom elements.
*
* @publicApi
*/
export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = {
name: 'custom-elements'
};
/**
* Defines a schema that allows any property on any element.
*
* @publicApi
*/
export const NO_ERRORS_SCHEMA: SchemaMetadata = {
name: 'no-errors-schema'
};

View File

@ -122,7 +122,7 @@ export function renderComponent<T>(
const renderer = rendererFactory.createRenderer(hostRNode, componentDef);
const rootView: LView = createLView(
null, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, null, null,
null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, rootFlags, null, null,
rendererFactory, renderer, undefined, opts.injector || null);
const oldView = enterView(rootView, null);
@ -165,9 +165,9 @@ export function createRootComponentView(
const tView = rootView[TVIEW];
const tNode: TElementNode = createNodeAtIndex(0, TNodeType.Element, rNode, null, null);
const componentView = createLView(
rootView,
getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery),
rootView, getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs,
def.viewQuery, def.schemas),
null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode,
rendererFactory, renderer, sanitizer);

View File

@ -162,8 +162,8 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
// Create the root view. Uses empty TView and ContentTemplate.
const rootLView = createLView(
null, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, null, null,
rendererFactory, renderer, sanitizer, rootViewInjector);
null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, rootFlags, null,
null, rendererFactory, renderer, sanitizer, rootViewInjector);
// rootView is the parent when bootstrapping
const oldLView = enterView(rootLView, null);

View File

@ -11,6 +11,7 @@ import '../util/ng_dev_mode';
import {ChangeDetectionStrategy} from '../change_detection/constants';
import {Mutable, Type} from '../interface/type';
import {NgModuleDef} from '../metadata/ng_module';
import {SchemaMetadata} from '../metadata/schema';
import {ViewEncapsulation} from '../metadata/view';
import {noSideEffects} from '../util/closure';
import {stringify} from '../util/stringify';
@ -234,6 +235,11 @@ export function defineComponent<T>(componentDefinition: {
* `PipeDefs`s. The function is necessary to be able to support forward declarations.
*/
pipes?: PipeTypesOrFactory | null;
/**
* The set of schemas that declare elements to be allowed in the component's template.
*/
schemas?: SchemaMetadata[] | null;
}): never {
const type = componentDefinition.type;
const typePrototype = type.prototype;
@ -274,6 +280,7 @@ export function defineComponent<T>(componentDefinition: {
styles: componentDefinition.styles || EMPTY_ARRAY,
_: null as never,
setInput: null,
schemas: componentDefinition.schemas || null,
};
def._ = noSideEffects(() => {
const directiveTypes = componentDefinition.directives !;
@ -326,6 +333,7 @@ export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T>>): nev
imports: def.imports || EMPTY_ARRAY,
exports: def.exports || EMPTY_ARRAY,
transitiveCompileScopes: null,
schemas: def.schemas || null,
};
return res as never;
}

View File

@ -10,6 +10,7 @@ import {InjectFlags, InjectionToken, Injector} from '../di';
import {resolveForwardRef} from '../di/forward_ref';
import {ErrorHandler} from '../error_handler';
import {Type} from '../interface/type';
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../metadata/schema';
import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../sanitization/sanitization';
import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
@ -320,12 +321,12 @@ export function renderTemplate<T>(
// We need to create a root view so it's possible to look up the host element through its index
const hostLView = createLView(
null, createTView(-1, null, 1, 0, null, null, null), {},
null, createTView(-1, null, 1, 0, null, null, null, null), {},
LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, providedRendererFactory, renderer);
enterView(hostLView, null); // SUSPECT! why do we need to enter the View?
const componentTView =
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null);
getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null, null);
const hostTNode = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null);
componentView = createLView(
hostLView, componentTView, context, LViewFlags.CheckAlways, hostNode, hostTNode,
@ -728,12 +729,14 @@ function saveResolvedLocalsInData(
* @param vars The number of bindings and pure function bindings in this view
* @param directives Directive defs that should be saved on TView
* @param pipes Pipe defs that should be saved on TView
* @param viewQuery View query that should be saved on TView
* @param schemas Schemas that should be saved on TView
* @returns TView
*/
export function getOrCreateTView(
templateFn: ComponentTemplate<any>, consts: number, vars: number,
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
viewQuery: ViewQueriesFunction<any>| null): TView {
viewQuery: ViewQueriesFunction<any>| null, schemas: SchemaMetadata[] | null): TView {
// TODO(misko): reading `ngPrivateData` here is problematic for two reasons
// 1. It is a megamorphic call on each invocation.
// 2. For nested embedded views (ngFor inside ngFor) the template instance is per
@ -742,8 +745,8 @@ export function getOrCreateTView(
// and not on embedded templates.
return templateFn.ngPrivateData ||
(templateFn.ngPrivateData =
createTView(-1, templateFn, consts, vars, directives, pipes, viewQuery) as never);
(templateFn.ngPrivateData = createTView(
-1, templateFn, consts, vars, directives, pipes, viewQuery, schemas) as never);
}
/**
@ -754,11 +757,13 @@ export function getOrCreateTView(
* @param consts The number of nodes, local refs, and pipes in this template
* @param directives Registry of directives for this view
* @param pipes Registry of pipes for this view
* @param viewQuery View queries for this view
* @param schemas Schemas for this view
*/
export function createTView(
viewIndex: number, templateFn: ComponentTemplate<any>| null, consts: number, vars: number,
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
viewQuery: ViewQueriesFunction<any>| null): TView {
viewQuery: ViewQueriesFunction<any>| null, schemas: SchemaMetadata[] | null): TView {
ngDevMode && ngDevMode.tView++;
const bindingStartIndex = HEADER_OFFSET + consts;
// This length does not yet contain host bindings from child directives because at this point,
@ -792,6 +797,7 @@ export function createTView(
directiveRegistry: typeof directives === 'function' ? directives() : directives,
pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
firstChild: null,
schemas: schemas,
};
}
@ -1218,7 +1224,7 @@ function elementPropertyInternal<T>(
} else if (tNode.type === TNodeType.Element) {
if (ngDevMode) {
validateAgainstEventProperties(propName);
validateAgainstUnknownProperties(element, propName, tNode);
validateAgainstUnknownProperties(lView, element, propName, tNode);
ngDevMode.rendererSetProperty++;
}
@ -1238,7 +1244,12 @@ function elementPropertyInternal<T>(
}
function validateAgainstUnknownProperties(
element: RElement | RComment, propName: string, tNode: TNode) {
hostView: LView, element: RElement | RComment, propName: string, tNode: TNode) {
// If the tag matches any of the schemas we shouldn't throw.
if (matchingSchemas(hostView, tNode.tagName)) {
return;
}
// If prop is not a known property of the HTML element...
if (!(propName in element) &&
// and we are in a browser context... (web worker nodes should be skipped)
@ -1251,6 +1262,22 @@ function validateAgainstUnknownProperties(
}
}
function matchingSchemas(hostView: LView, tagName: string | null): boolean {
const schemas = hostView[TVIEW].schemas;
if (schemas !== null) {
for (let i = 0; i < schemas.length; i++) {
const schema = schemas[i];
if (schema === NO_ERRORS_SCHEMA ||
schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
return true;
}
}
}
return false;
}
/**
* Stores debugging data for this property binding on first template pass.
* This enables features like DebugElement.properties.
@ -2040,7 +2067,8 @@ function addComponentLogic<T>(
const native = getNativeByTNode(previousOrParentTNode, lView);
const tView = getOrCreateTView(
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery);
def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery,
def.schemas);
// Only component views should be added to the view tree directly. Embedded views are
// accessed through their containers because they may be removed / re-added later.
@ -2197,7 +2225,7 @@ export function template(
const tContainerNode = containerInternal(index, tagName || null, attrs || null);
if (tView.firstTemplatePass) {
tContainerNode.tViews = createTView(
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null);
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null);
}
createDirectivesAndLocals(tView, lView, localRefs, localRefExtractor);
@ -2435,7 +2463,7 @@ function getOrCreateEmbeddedTView(
ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array');
if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) {
containerTViews[viewIndex] = createTView(
viewIndex, null, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null);
viewIndex, null, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null);
}
return containerTViews[viewIndex];
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ViewEncapsulation} from '../../core';
import {SchemaMetadata, ViewEncapsulation} from '../../core';
import {Type} from '../../interface/type';
import {CssSelectorList} from './projection';
@ -264,7 +264,6 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
readonly onPush: boolean;
/**
* Registry of directives and components that may be found in this view.
*
* The property is either an array of `DirectiveDef`s or a function which returns the array of
@ -280,6 +279,11 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
*/
pipeDefs: PipeDefListOrFactory|null;
/**
* The set of schemas that declare elements to be allowed in the component's template.
*/
schemas: SchemaMetadata[]|null;
/**
* Used to store the result of `noSideEffects` function so that it is not removed by closure
* compiler. The property should never be read.

View File

@ -10,6 +10,7 @@ import {InjectionToken} from '../../di/injection_token';
import {Injector} from '../../di/injector';
import {Type} from '../../interface/type';
import {QueryList} from '../../linker';
import {SchemaMetadata} from '../../metadata';
import {Sanitizer} from '../../sanitization/security';
import {LContainer} from './container';
@ -535,6 +536,11 @@ export interface TView {
* A list of indices for child directives that have content queries.
*/
contentQueries: number[]|null;
/**
* Set of schemas that declare elements to be allowed inside the view.
*/
schemas: SchemaMetadata[]|null;
}
export const enum RootContextFlags {Empty = 0b00, DetectChanges = 0b01, FlushPlayers = 0b10}

View File

@ -115,6 +115,7 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule
exports: flatten(ngModule.exports || EMPTY_ARRAY, resolveForwardRef)
.map(expandModuleWithProviders),
emitInline: true,
schemas: ngModule.schemas ? flatten(ngModule.schemas) : null,
});
}
return ngModuleDef;
@ -353,6 +354,7 @@ export function patchComponentDefWithScope<C>(
.filter(def => !!def);
componentDef.pipeDefs = () =>
Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef(pipe) !);
componentDef.schemas = transitiveScopes.schemas;
}
/**
@ -375,6 +377,7 @@ export function transitiveScopesFor<T>(
}
const scopes: NgModuleTransitiveScopes = {
schemas: def.schemas || null,
compilation: {
directives: new Set<any>(),
pipes: new Set<any>(),

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, forwardRef, getModuleFactory, ɵivyEnabled as ivyEnabled} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, forwardRef, getModuleFactory, ɵivyEnabled as ivyEnabled} from '@angular/core';
import {Console} from '@angular/core/src/console';
import {InjectableDef, defineInjectable} from '@angular/core/src/di/interface/defs';
import {getNgModuleDef} from '@angular/core/src/render3/definition';
@ -266,8 +266,7 @@ function declareTests(config?: {useJit: boolean}) {
expect(() => fixture.detectChanges()).toThrowError(/Can't bind to 'someUnknownProp'/);
});
fixmeIvy('FW-819: ngtsc compiler should support schemas')
.it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA',
it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA',
() => {
@Component({template: '<some-element [someUnknownProp]="true"></some-element>'})
class ComponentUsingInvalidProperty {
@ -275,12 +274,20 @@ function declareTests(config?: {useJit: boolean}) {
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ComponentUsingInvalidProperty]
declarations: [ComponentUsingInvalidProperty],
// Note that we need to add the component to `entryComponents`, because of the
// `createComp` call below. In Ivy the property validation happens during the
// update phase so we need to create the component, in order for it to run.
entryComponents: [ComponentUsingInvalidProperty]
})
class SomeModule {
}
expect(() => createModule(SomeModule)).not.toThrow();
expect(() => {
const fixture = createComp(ComponentUsingInvalidProperty, SomeModule);
fixture.detectChanges();
}).not.toThrow();
});
});

View File

@ -2320,8 +2320,8 @@ describe('di', () => {
describe('getOrCreateNodeInjector', () => {
it('should handle initial undefined state', () => {
const contentView = createLView(
null, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways, null,
null, {} as any, {} as any);
null, createTView(-1, null, 1, 0, null, null, null, null), null, LViewFlags.CheckAlways,
null, null, {} as any, {} as any);
const oldView = enterView(contentView, null);
try {
const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null);

View File

@ -34,8 +34,8 @@ describe('style and class based bindings', () => {
const rootContext =
createRootContext(requestAnimationFrame.bind(window), playerHandler || null);
const lView = createLView(
null, createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot, null,
null, domRendererFactory3, domRendererFactory3.createRenderer(element, null));
null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, LViewFlags.IsRoot,
null, null, domRendererFactory3, domRendererFactory3.createRenderer(element, null));
return lView;
}