fix(core): attempt to recover from user errors during creation (#36381)
If there's an error during the first creation pass of a `TView`, the data structure may be corrupted which will cause framework assertion failures downstream which can mask the user's error. These changes add a new flag to the `TView` that indicates whether the first creation pass was successful, and if it wasn't we try re-create the `TView`. Fixes #31221. PR Close #36381
This commit is contained in:
@ -143,6 +143,7 @@ export const TViewConstructor = class TView implements ITView {
|
||||
public firstChild: ITNode|null, //
|
||||
public schemas: SchemaMetadata[]|null, //
|
||||
public consts: TConstants|null, //
|
||||
public incompleteFirstPass: boolean //
|
||||
) {}
|
||||
|
||||
get template_(): string {
|
||||
|
@ -374,6 +374,14 @@ export function renderView<T>(tView: TView, lView: LView, context: T): void {
|
||||
renderChildComponents(lView, components);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// If we didn't manage to get past the first template pass due to
|
||||
// an error, mark the view as corrupted so we can try to recover.
|
||||
if (tView.firstCreatePass) {
|
||||
tView.incompleteFirstPass = true;
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
leaveView();
|
||||
@ -598,10 +606,17 @@ export function saveResolvedLocalsInData(
|
||||
* @returns TView
|
||||
*/
|
||||
export function getOrCreateTComponentView(def: ComponentDef<any>): TView {
|
||||
return def.tView ||
|
||||
(def.tView = createTView(
|
||||
TViewType.Component, -1, def.template, def.decls, def.vars, def.directiveDefs,
|
||||
def.pipeDefs, def.viewQuery, def.schemas, def.consts));
|
||||
const tView = def.tView;
|
||||
|
||||
// Create a TView if there isn't one, or recreate it if the first create pass didn't
|
||||
// complete successfuly since we can't know for sure whether it's in a usable shape.
|
||||
if (tView === null || tView.incompleteFirstPass) {
|
||||
return def.tView = createTView(
|
||||
TViewType.Component, -1, def.template, def.decls, def.vars, def.directiveDefs,
|
||||
def.pipeDefs, def.viewQuery, def.schemas, def.consts);
|
||||
}
|
||||
|
||||
return tView;
|
||||
}
|
||||
|
||||
|
||||
@ -662,7 +677,9 @@ export function createTView(
|
||||
typeof pipes === 'function' ? pipes() : pipes, // pipeRegistry: PipeDefList|null,
|
||||
null, // firstChild: TNode|null,
|
||||
schemas, // schemas: SchemaMetadata[]|null,
|
||||
consts) : // consts: TConstants|null
|
||||
consts, // consts: TConstants|null
|
||||
false // incompleteFirstPass: boolean
|
||||
) :
|
||||
{
|
||||
type: type,
|
||||
id: viewIndex,
|
||||
@ -694,6 +711,7 @@ export function createTView(
|
||||
firstChild: null,
|
||||
schemas: schemas,
|
||||
consts: consts,
|
||||
incompleteFirstPass: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -684,6 +684,12 @@ export interface TView {
|
||||
* Used for directive matching, attribute bindings, local definitions and more.
|
||||
*/
|
||||
consts: TConstants|null;
|
||||
|
||||
/**
|
||||
* Indicates that there was an error before we managed to complete the first create pass of the
|
||||
* view. This means that the view is likely corrupted and we should try to recover it.
|
||||
*/
|
||||
incompleteFirstPass: boolean;
|
||||
}
|
||||
|
||||
export const enum RootContextFlags {
|
||||
|
Reference in New Issue
Block a user