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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user