diff --git a/src/Microsoft.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts b/src/Microsoft.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts index 3264572e4f..28f536d6c3 100644 --- a/src/Microsoft.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Microsoft.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts @@ -107,17 +107,22 @@ export const monoPlatform: Platform = { return address as any as Pointer; }, - getHeapObjectFieldsPtr: function getHeapObjectFieldsPtr(heapObject: System_Object): Pointer { + getObjectFieldsBaseAddress: function getObjectFieldsBaseAddress(referenceTypedObject: System_Object): Pointer { // The first two int32 values are internal Mono data - return (heapObject as any as number + 8) as any as Pointer; + return (referenceTypedObject as any as number + 8) as any as Pointer; }, - readHeapInt32: function readHeapInt32(address: Pointer, offset?: number): number { - return Module.getValue((address as any as number) + (offset || 0), 'i32'); + readInt32Field: function readHeapInt32(baseAddress: Pointer, fieldOffset?: number): number { + return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i32'); }, - readHeapObject: function readHeapObject(address: Pointer, offset?: number): System_Object { - return monoPlatform.readHeapInt32(address, offset) as any as System_Object; + readObjectField: function readHeapObject(baseAddress: Pointer, fieldOffset?: number): System_Object { + return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i32') as any as System_Object; + }, + + readStringField: function readHeapObject(baseAddress: Pointer, fieldOffset?: number): string | null { + const fieldValue = Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i32'); + return fieldValue === 0 ? null : monoPlatform.toJavaScriptString(fieldValue as any as System_String); }, }; diff --git a/src/Microsoft.Blazor.Browser.JS/src/Platform/Platform.ts b/src/Microsoft.Blazor.Browser.JS/src/Platform/Platform.ts index 8f4b610504..032defcdd9 100644 --- a/src/Microsoft.Blazor.Browser.JS/src/Platform/Platform.ts +++ b/src/Microsoft.Blazor.Browser.JS/src/Platform/Platform.ts @@ -11,10 +11,10 @@ getArrayLength(array: System_Array): number; getArrayEntryPtr(array: System_Array, index: number, itemSize: number): Pointer; - getHeapObjectFieldsPtr(heapObject: System_Object): Pointer; - - readHeapInt32(address: Pointer, offset?: number): number; - readHeapObject(address: Pointer, offset?: number): System_Object; + getObjectFieldsBaseAddress(referenceTypedObject: System_Object): Pointer; + readInt32Field(baseAddress: Pointer, fieldOffset?: number): number; + readObjectField(baseAddress: Pointer, fieldOffset?: number): System_Object; + readStringField(baseAddress: Pointer, fieldOffset?: number): string | null; } // We don't actually instantiate any of these at runtime. For perf it's preferable to diff --git a/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts b/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts index b6375efb77..9ef3d50dcd 100644 --- a/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts +++ b/src/Microsoft.Blazor.Browser.JS/src/Rendering/RenderTreeNode.ts @@ -12,15 +12,15 @@ export function getTreeNodePtr(renderTreeEntries: System_Array, index: number): export const renderTreeNode = { // The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeNode.cs - nodeType: (node: RenderTreeNodePointer) => _readInt32Property(node, 0) as NodeType, - elementName: (node: RenderTreeNodePointer) => _readStringProperty(node, 4), - descendantsEndIndex: (node: RenderTreeNodePointer) => _readInt32Property(node, 8) as NodeType, - textContent: (node: RenderTreeNodePointer) => _readStringProperty(node, 12), - attributeName: (node: RenderTreeNodePointer) => _readStringProperty(node, 16), - attributeValue: (node: RenderTreeNodePointer) => _readStringProperty(node, 20), - attributeEventHandlerValue: (node: RenderTreeNodePointer) => _readObjectProperty(node, 24), - componentId: (node: RenderTreeNodePointer) => _readInt32Property(node, 28), - component: (node: RenderTreeNodePointer) => _readObjectProperty(node, 32), + nodeType: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 0) as NodeType, + elementName: (node: RenderTreeNodePointer) => platform.readStringField(node, 4), + descendantsEndIndex: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 8) as NodeType, + textContent: (node: RenderTreeNodePointer) => platform.readStringField(node, 12), + attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 16), + attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 20), + attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 24), + componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 28), + component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 32), }; export enum NodeType { @@ -31,19 +31,6 @@ export enum NodeType { component = 4, } -function _readInt32Property(baseAddress: Pointer, offsetBytes: number) { - return platform.readHeapInt32(baseAddress, offsetBytes); -} - -function _readObjectProperty(baseAddress: Pointer, offsetBytes: number) { - return platform.readHeapObject(baseAddress, offsetBytes); -} - -function _readStringProperty(baseAddress: Pointer, offsetBytes: number) { - var managedString = platform.readHeapObject(baseAddress, offsetBytes) as System_String; - return platform.toJavaScriptString(managedString); -} - // Nominal type to ensure only valid pointers are passed to the renderTreeNode functions. // At runtime the values are just numbers. export interface RenderTreeNodePointer extends Pointer { RenderTreeNodePointer__DO_NOT_IMPLEMENT: any } diff --git a/src/Microsoft.Blazor.Browser.JS/src/Rendering/Renderer.ts b/src/Microsoft.Blazor.Browser.JS/src/Rendering/Renderer.ts index c331bc7c22..5d051bb9c0 100644 --- a/src/Microsoft.Blazor.Browser.JS/src/Rendering/Renderer.ts +++ b/src/Microsoft.Blazor.Browser.JS/src/Rendering/Renderer.ts @@ -25,13 +25,13 @@ export function attachComponentToElement(browserRendererId: number, elementSelec } export function renderRenderTree(renderComponentArgs: Pointer) { - const browserRendererId = platform.readHeapInt32(renderComponentArgs, 0); + const browserRendererId = platform.readInt32Field(renderComponentArgs, 0); const browserRenderer = browserRenderers[browserRendererId]; if (!browserRenderer) { throw new Error(`There is no browser renderer with ID ${browserRendererId}.`); } - const componentId = platform.readHeapInt32(renderComponentArgs, 4); + const componentId = platform.readInt32Field(renderComponentArgs, 4); const element = browserRenderer[componentId]; if (!element) { throw new Error(`No element is currently associated with component ${componentId}`); @@ -39,8 +39,8 @@ export function renderRenderTree(renderComponentArgs: Pointer) { clearElement(element); - const tree = platform.readHeapObject(renderComponentArgs, 8) as System_Array; - const treeLength = platform.readHeapInt32(renderComponentArgs, 12); + const tree = platform.readObjectField(renderComponentArgs, 8) as System_Array; + const treeLength = platform.readInt32Field(renderComponentArgs, 12); insertNodeRange(browserRendererId, componentId, element, tree, 0, treeLength - 1); } @@ -97,7 +97,7 @@ function insertComponent(browserRendererId: number, intoDomElement: Element, nod } function insertElement(browserRendererId: number, componentId: number, intoDomElement: Element, tree: System_Array, elementNode: RenderTreeNodePointer, elementNodeIndex: number) { - const tagName = renderTreeNode.elementName(elementNode); + const tagName = renderTreeNode.elementName(elementNode)!; const newDomElement = document.createElement(tagName); intoDomElement.appendChild(newDomElement); @@ -117,7 +117,7 @@ function insertElement(browserRendererId: number, componentId: number, intoDomEl } function applyAttribute(browserRendererId: number, componentId: number, toDomElement: Element, attributeNode: RenderTreeNodePointer, attributeNodeIndex: number) { - const attributeName = renderTreeNode.attributeName(attributeNode); + const attributeName = renderTreeNode.attributeName(attributeNode)!; switch (attributeName) { case 'onclick': @@ -136,7 +136,7 @@ function applyAttribute(browserRendererId: number, componentId: number, toDomEle // Treat as a regular string-valued attribute toDomElement.setAttribute( attributeName, - renderTreeNode.attributeValue(attributeNode) + renderTreeNode.attributeValue(attributeNode)! ); break; } @@ -163,7 +163,7 @@ function raiseEvent(browserRendererId: number, componentId: number, renderTreeNo } function insertText(intoDomElement: Element, textNode: RenderTreeNodePointer) { - const textContent = renderTreeNode.textContent(textNode); + const textContent = renderTreeNode.textContent(textNode)!; const newDomTextNode = document.createTextNode(textContent); intoDomElement.appendChild(newDomTextNode); }