refactor(ivy): Add i18n create op codes debug info (#29348)
Simply adds a `debug` property to the array of create opcodes while inside `readCreateOpCodes` in i18n. This `debug` property has a property called `operations` that is a human-readable list of operations that will be performed, as derived from the op codes themselves, and the view it's acting upon. PR Close #29348
This commit is contained in:
parent
c7ff728723
commit
699ecac2c2
|
@ -7,14 +7,18 @@
|
|||
*/
|
||||
|
||||
import {assertDefined} from '../util/assert';
|
||||
|
||||
import {ACTIVE_INDEX, LContainer, NATIVE, VIEWS} from './interfaces/container';
|
||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, TIcu} from './interfaces/i18n';
|
||||
import {TNode} from './interfaces/node';
|
||||
import {LQueries} from './interfaces/query';
|
||||
import {RComment, RElement} from './interfaces/renderer';
|
||||
import {StylingContext} from './interfaces/styling';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TVIEW, TView, T_HOST} from './interfaces/view';
|
||||
import {unwrapRNode} from './util/view_utils';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TVIEW, T_HOST} from './interfaces/view';
|
||||
import {getTNode, unwrapRNode} from './util/view_utils';
|
||||
|
||||
function attachDebugObject(obj: any, debug: any) {
|
||||
Object.defineProperty(obj, 'debug', {value: debug, enumerable: false});
|
||||
}
|
||||
|
||||
/*
|
||||
* This file contains conditionally attached classes which provide human readable (debug) level
|
||||
|
@ -47,11 +51,11 @@ import {unwrapRNode} from './util/view_utils';
|
|||
|
||||
|
||||
export function attachLViewDebug(lView: LView) {
|
||||
(lView as any).debug = new LViewDebug(lView);
|
||||
attachDebugObject(lView, new LViewDebug(lView));
|
||||
}
|
||||
|
||||
export function attachLContainerDebug(lContainer: LContainer) {
|
||||
(lContainer as any).debug = new LContainerDebug(lContainer);
|
||||
attachDebugObject(lContainer, new LContainerDebug(lContainer));
|
||||
}
|
||||
|
||||
export function toDebug(obj: LView): LViewDebug;
|
||||
|
@ -230,3 +234,198 @@ export function readLViewValue(value: any): LView|null {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export class I18NDebugItem {
|
||||
[key: string]: any;
|
||||
|
||||
get tNode() { return getTNode(this.nodeIndex, this._lView); }
|
||||
|
||||
constructor(
|
||||
public __raw_opCode: any, private _lView: LView, public nodeIndex: number,
|
||||
public type: string) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a list of "Create" & "Update" OpCodes into a human-readable list of operations for
|
||||
* debugging purposes.
|
||||
* @param mutateOpCodes mutation opCodes to read
|
||||
* @param updateOpCodes update opCodes to read
|
||||
* @param icus list of ICU expressions
|
||||
* @param lView The view the opCodes are acting on
|
||||
*/
|
||||
export function attachI18nOpCodesDebug(
|
||||
mutateOpCodes: I18nMutateOpCodes, updateOpCodes: I18nUpdateOpCodes, icus: TIcu[] | null,
|
||||
lView: LView) {
|
||||
attachDebugObject(mutateOpCodes, new I18nMutateOpCodesDebug(mutateOpCodes, lView));
|
||||
attachDebugObject(updateOpCodes, new I18nUpdateOpCodesDebug(updateOpCodes, icus, lView));
|
||||
|
||||
if (icus) {
|
||||
icus.forEach(icu => {
|
||||
icu.create.forEach(
|
||||
icuCase => { attachDebugObject(icuCase, new I18nMutateOpCodesDebug(icuCase, lView)); });
|
||||
icu.update.forEach(icuCase => {
|
||||
attachDebugObject(icuCase, new I18nUpdateOpCodesDebug(icuCase, icus, lView));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class I18nMutateOpCodesDebug implements I18nOpCodesDebug {
|
||||
constructor(private readonly __raw_opCodes: I18nMutateOpCodes, private readonly __lView: LView) {}
|
||||
|
||||
/**
|
||||
* A list of operation information about how the OpCodes will act on the view.
|
||||
*/
|
||||
get operations() {
|
||||
const {__lView, __raw_opCodes} = this;
|
||||
const results: any[] = [];
|
||||
|
||||
for (let i = 0; i < __raw_opCodes.length; i++) {
|
||||
const opCode = __raw_opCodes[i];
|
||||
let result: any;
|
||||
if (typeof opCode === 'string') {
|
||||
result = {
|
||||
__raw_opCode: opCode,
|
||||
type: 'Create Text Node',
|
||||
nodeIndex: __raw_opCodes[++i],
|
||||
text: opCode,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof opCode === 'number') {
|
||||
switch (opCode & I18nMutateOpCode.MASK_OPCODE) {
|
||||
case I18nMutateOpCode.AppendChild:
|
||||
const destinationNodeIndex = opCode >>> I18nMutateOpCode.SHIFT_PARENT;
|
||||
result = new I18NDebugItem(opCode, __lView, destinationNodeIndex, 'AppendChild');
|
||||
break;
|
||||
case I18nMutateOpCode.Select:
|
||||
const nodeIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
|
||||
result = new I18NDebugItem(opCode, __lView, nodeIndex, 'Select');
|
||||
break;
|
||||
case I18nMutateOpCode.ElementEnd:
|
||||
let elementIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
|
||||
result = new I18NDebugItem(opCode, __lView, elementIndex, 'ElementEnd');
|
||||
break;
|
||||
case I18nMutateOpCode.Attr:
|
||||
elementIndex = opCode >>> I18nMutateOpCode.SHIFT_REF;
|
||||
result = new I18NDebugItem(opCode, __lView, elementIndex, 'Attr');
|
||||
result['attrName'] = __raw_opCodes[++i];
|
||||
result['attrValue'] = __raw_opCodes[++i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
switch (opCode) {
|
||||
case COMMENT_MARKER:
|
||||
result = {
|
||||
__raw_opCode: opCode,
|
||||
type: 'COMMENT_MARKER',
|
||||
commentValue: __raw_opCodes[++i],
|
||||
nodeIndex: __raw_opCodes[++i],
|
||||
};
|
||||
break;
|
||||
case ELEMENT_MARKER:
|
||||
result = {
|
||||
__raw_opCode: opCode,
|
||||
type: 'ELEMENT_MARKER',
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
result = {
|
||||
__raw_opCode: opCode,
|
||||
type: 'Unknown Op Code',
|
||||
code: opCode,
|
||||
};
|
||||
}
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
export class I18nUpdateOpCodesDebug implements I18nOpCodesDebug {
|
||||
constructor(
|
||||
private readonly __raw_opCodes: I18nUpdateOpCodes, private readonly icus: TIcu[]|null,
|
||||
private readonly __lView: LView) {}
|
||||
|
||||
/**
|
||||
* A list of operation information about how the OpCodes will act on the view.
|
||||
*/
|
||||
get operations() {
|
||||
const {__lView, __raw_opCodes, icus} = this;
|
||||
const results: any[] = [];
|
||||
|
||||
for (let i = 0; i < __raw_opCodes.length; i++) {
|
||||
// bit code to check if we should apply the next update
|
||||
const checkBit = __raw_opCodes[i] as number;
|
||||
// Number of opCodes to skip until next set of update codes
|
||||
const skipCodes = __raw_opCodes[++i] as number;
|
||||
let value = '';
|
||||
for (let j = i + 1; j <= (i + skipCodes); j++) {
|
||||
const opCode = __raw_opCodes[j];
|
||||
if (typeof opCode === 'string') {
|
||||
value += opCode;
|
||||
} else if (typeof opCode == 'number') {
|
||||
if (opCode < 0) {
|
||||
// It's a binding index whose value is negative
|
||||
// We cannot know the value of the binding so we only show the index
|
||||
value += `<EFBFBD>${-opCode - 1}<EFBFBD>`;
|
||||
} else {
|
||||
const nodeIndex = opCode >>> I18nUpdateOpCode.SHIFT_REF;
|
||||
let tIcuIndex: number;
|
||||
let tIcu: TIcu;
|
||||
switch (opCode & I18nUpdateOpCode.MASK_OPCODE) {
|
||||
case I18nUpdateOpCode.Attr:
|
||||
const attrName = __raw_opCodes[++j] as string;
|
||||
const sanitizeFn = __raw_opCodes[++j];
|
||||
results.push({
|
||||
__raw_opCode: opCode,
|
||||
checkBit,
|
||||
type: 'Attr',
|
||||
attrValue: value, attrName, sanitizeFn,
|
||||
});
|
||||
break;
|
||||
case I18nUpdateOpCode.Text:
|
||||
results.push({
|
||||
__raw_opCode: opCode,
|
||||
checkBit,
|
||||
type: 'Text', nodeIndex,
|
||||
text: value,
|
||||
});
|
||||
break;
|
||||
case I18nUpdateOpCode.IcuSwitch:
|
||||
tIcuIndex = __raw_opCodes[++j] as number;
|
||||
tIcu = icus ![tIcuIndex];
|
||||
let result = new I18NDebugItem(opCode, __lView, nodeIndex, 'IcuSwitch');
|
||||
result['tIcuIndex'] = tIcuIndex;
|
||||
result['checkBit'] = checkBit;
|
||||
result['mainBinding'] = value;
|
||||
result['tIcu'] = tIcu;
|
||||
results.push(result);
|
||||
break;
|
||||
case I18nUpdateOpCode.IcuUpdate:
|
||||
tIcuIndex = __raw_opCodes[++j] as number;
|
||||
tIcu = icus ![tIcuIndex];
|
||||
result = new I18NDebugItem(opCode, __lView, nodeIndex, 'IcuUpdate');
|
||||
result['tIcuIndex'] = tIcuIndex;
|
||||
result['checkBit'] = checkBit;
|
||||
result['tIcu'] = tIcu;
|
||||
results.push(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i += skipCodes;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
export interface I18nOpCodesDebug { operations: any[]; }
|
||||
|
|
|
@ -13,6 +13,7 @@ import {addAllToArray} from '../util/array_utils';
|
|||
import {assertDefined, assertEqual, assertGreaterThan} from '../util/assert';
|
||||
|
||||
import {attachPatchData} from './context_discovery';
|
||||
import {attachI18nOpCodesDebug} from './debug';
|
||||
import {elementAttribute, load, textBinding} from './instructions/all';
|
||||
import {allocExpando, createNodeAtIndex} from './instructions/shared';
|
||||
import {LContainer, NATIVE} from './interfaces/container';
|
||||
|
@ -457,6 +458,10 @@ function i18nStartFirstPass(
|
|||
|
||||
allocExpando(viewData, i18nVarsCount);
|
||||
|
||||
ngDevMode &&
|
||||
attachI18nOpCodesDebug(
|
||||
createOpCodes, updateOpCodes, icuExpressions.length ? icuExpressions : null, viewData);
|
||||
|
||||
// NOTE: local var needed to properly assert the type of `TI18n`.
|
||||
const tI18n: TI18n = {
|
||||
vars: i18nVarsCount,
|
||||
|
@ -464,6 +469,7 @@ function i18nStartFirstPass(
|
|||
update: updateOpCodes,
|
||||
icus: icuExpressions.length ? icuExpressions : null,
|
||||
};
|
||||
|
||||
tView.data[index + HEADER_OFFSET] = tI18n;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import {AttributeMarker} from '../../src/render3/interfaces/node';
|
|||
import {getNativeByIndex, getTNode} from '../../src/render3/util/view_utils';
|
||||
import {NgForOf, NgIf} from './common_with_def';
|
||||
import {allocHostVars, element, elementEnd, elementStart, template, text, nextContext, bind, elementProperty, projectionDef, projection, elementContainerStart, elementContainerEnd, textBinding} from '../../src/render3/instructions/all';
|
||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nUpdateOpCode, I18nUpdateOpCodes, TI18n} from '../../src/render3/interfaces/i18n';
|
||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nUpdateOpCode, I18nUpdateOpCodes, TI18n, IcuType} from '../../src/render3/interfaces/i18n';
|
||||
import {HEADER_OFFSET, LView, TVIEW} from '../../src/render3/interfaces/view';
|
||||
import {ComponentFixture, TemplateFixture} from './render_util';
|
||||
|
||||
|
@ -81,6 +81,17 @@ describe('Runtime i18n', () => {
|
|||
const index = 0;
|
||||
const opCodes = getOpCodes(() => { i18nStart(index, MSG_DIV); }, null, nbConsts, index);
|
||||
|
||||
|
||||
// Check debug
|
||||
const debugOps = (opCodes as any).create.debug !.operations;
|
||||
expect(debugOps[0].__raw_opCode).toBe('simple text');
|
||||
expect(debugOps[0].type).toBe('Create Text Node');
|
||||
expect(debugOps[0].nodeIndex).toBe(1);
|
||||
expect(debugOps[0].text).toBe('simple text');
|
||||
expect(debugOps[1].__raw_opCode).toBe(1);
|
||||
expect(debugOps[1].type).toBe('AppendChild');
|
||||
expect(debugOps[1].nodeIndex).toBe(0);
|
||||
|
||||
expect(opCodes).toEqual({
|
||||
vars: 1,
|
||||
create: [
|
||||
|
@ -138,6 +149,10 @@ describe('Runtime i18n', () => {
|
|||
const index = 1;
|
||||
const opCodes = getOpCodes(() => { i18nStart(index, MSG_DIV); }, null, nbConsts, index);
|
||||
|
||||
expect((opCodes as any).update.debug.operations).toEqual([
|
||||
{__raw_opCode: 8, checkBit: 1, type: 'Text', nodeIndex: 2, text: 'Hello <20>0<EFBFBD>!'}
|
||||
]);
|
||||
|
||||
expect(opCodes).toEqual({
|
||||
vars: 1,
|
||||
create:
|
||||
|
@ -282,6 +297,79 @@ describe('Runtime i18n', () => {
|
|||
const innerTextNode = index + 4;
|
||||
const lastTextNode = index + 5;
|
||||
|
||||
const debugOps = (opCodes as any).update.debug.operations;
|
||||
expect(debugOps[0].__raw_opCode).toBe(6);
|
||||
expect(debugOps[0].checkBit).toBe(1);
|
||||
expect(debugOps[0].type).toBe('IcuSwitch');
|
||||
expect(debugOps[0].nodeIndex).toBe(1);
|
||||
expect(debugOps[0].tIcuIndex).toBe(0);
|
||||
expect(debugOps[0].mainBinding).toBe('<27>0<EFBFBD>');
|
||||
|
||||
expect(debugOps[1].__raw_opCode).toBe(7);
|
||||
expect(debugOps[1].checkBit).toBe(3);
|
||||
expect(debugOps[1].type).toBe('IcuUpdate');
|
||||
expect(debugOps[1].nodeIndex).toBe(1);
|
||||
expect(debugOps[1].tIcuIndex).toBe(0);
|
||||
|
||||
const icuDebugOps = (opCodes as any).icus[0].create[0].debug.operations;
|
||||
let op: any;
|
||||
let i = 0;
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe('no ');
|
||||
expect(op.type).toBe('Create Text Node');
|
||||
expect(op.nodeIndex).toBe(2);
|
||||
expect(op.text).toBe('no ');
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe(131073);
|
||||
expect(op.type).toBe('AppendChild');
|
||||
expect(op.nodeIndex).toBe(1);
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toEqual({marker: 'element'});
|
||||
expect(op.type).toBe('ELEMENT_MARKER');
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe('b');
|
||||
expect(op.type).toBe('Create Text Node');
|
||||
expect(op.nodeIndex).toBe(3);
|
||||
expect(op.text).toBe('b');
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe(131073);
|
||||
expect(op.type).toBe('AppendChild');
|
||||
expect(op.nodeIndex).toBe(1);
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe(28);
|
||||
expect(op.type).toBe('Attr');
|
||||
expect(op.nodeIndex).toBe(3);
|
||||
expect(op.attrName).toBe('title');
|
||||
expect(op.attrValue).toBe('none');
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe('emails');
|
||||
expect(op.type).toBe('Create Text Node');
|
||||
expect(op.nodeIndex).toBe(4);
|
||||
expect(op.text).toBe('emails');
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe(393217);
|
||||
expect(op.type).toBe('AppendChild');
|
||||
expect(op.nodeIndex).toBe(3);
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe('!');
|
||||
expect(op.type).toBe('Create Text Node');
|
||||
expect(op.nodeIndex).toBe(5);
|
||||
expect(op.text).toBe('!');
|
||||
|
||||
op = icuDebugOps[i++];
|
||||
expect(op.__raw_opCode).toBe(131073);
|
||||
expect(op.type).toBe('AppendChild');
|
||||
expect(op.nodeIndex).toBe(1);
|
||||
|
||||
expect(opCodes).toEqual({
|
||||
vars: 5,
|
||||
create: [
|
||||
|
@ -2168,5 +2256,4 @@ describe('Runtime i18n', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue