Clean up how JS functions are registered and located for invocation from .NET
This commit is contained in:
parent
7bbf2b54aa
commit
0187384638
|
|
@ -1,13 +1,11 @@
|
||||||
import { platform } from './Environment'
|
import { platform } from './Environment'
|
||||||
import { registerFunction } from './RegisteredFunction';
|
import { registerFunction } from './RegisteredFunction';
|
||||||
|
|
||||||
// This file defines an export that, when the library is loaded in a browser via a
|
|
||||||
// <script> element, will be attached to the global namespace
|
|
||||||
const blazorInstance = {
|
|
||||||
platform: platform,
|
|
||||||
registerFunction: registerFunction
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window['Blazor'] = blazorInstance;
|
// When the library is loaded in a browser via a <script> element, make the
|
||||||
|
// following APIs available in global scope for invocation from JS
|
||||||
|
window['Blazor'] = {
|
||||||
|
platform,
|
||||||
|
registerFunction,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { invokeWithJsonMarshalling } from './RegisteredFunction';
|
||||||
|
import { attachComponentToElement, renderRenderTree } from './Rendering/Renderer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The definitive list of internal functions invokable from .NET code.
|
||||||
|
* These function names are treated as 'reserved' and cannot be passed to registerFunction.
|
||||||
|
*/
|
||||||
|
export const internalRegisteredFunctions = {
|
||||||
|
attachComponentToElement,
|
||||||
|
invokeWithJsonMarshalling,
|
||||||
|
renderRenderTree,
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { MethodHandle, System_Object, System_String, System_Array, Pointer, Platform } from '../Platform';
|
import { MethodHandle, System_Object, System_String, System_Array, Pointer, Platform } from '../Platform';
|
||||||
import { getAssemblyNameFromUrl } from '../DotNet';
|
import { getAssemblyNameFromUrl } from '../DotNet';
|
||||||
|
import { getRegisteredFunction } from '../../RegisteredFunction';
|
||||||
|
|
||||||
let assembly_load: (assemblyName: string) => number;
|
let assembly_load: (assemblyName: string) => number;
|
||||||
let find_class: (assemblyHandle: number, namespace: string, className: string) => number;
|
let find_class: (assemblyHandle: number, namespace: string, className: string) => number;
|
||||||
|
|
@ -120,6 +121,10 @@ export const monoPlatform: Platform = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Bypass normal type checking to add this extra function. It's only intended to be called from
|
||||||
|
// the JS code in Mono's driver.c. It's never intended to be called from TypeScript.
|
||||||
|
(monoPlatform as any).monoGetRegisteredFunction = getRegisteredFunction;
|
||||||
|
|
||||||
function addScriptTagsToDocument() {
|
function addScriptTagsToDocument() {
|
||||||
// Load either the wasm or asm.js version of the Mono runtime
|
// Load either the wasm or asm.js version of the Mono runtime
|
||||||
const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate;
|
const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import { System_String } from './Platform/Platform';
|
import { System_String } from './Platform/Platform';
|
||||||
import { platform } from './Environment';
|
import { platform } from './Environment';
|
||||||
|
import { internalRegisteredFunctions } from './InternalRegisteredFunction';
|
||||||
|
|
||||||
const registeredFunctions: { [identifier: string]: Function } = {};
|
const registeredFunctions: { [identifier: string]: Function | undefined } = {};
|
||||||
|
|
||||||
// Code in Mono 'driver.c' looks for the registered functions here
|
|
||||||
window['__blazorRegisteredFunctions'] = registeredFunctions;
|
|
||||||
|
|
||||||
export function registerFunction(identifier: string, implementation: Function) {
|
export function registerFunction(identifier: string, implementation: Function) {
|
||||||
|
if (internalRegisteredFunctions.hasOwnProperty(identifier)) {
|
||||||
|
throw new Error(`The function identifier '${identifier}' is reserved and cannot be registered.`);
|
||||||
|
}
|
||||||
|
|
||||||
if (registeredFunctions.hasOwnProperty(identifier)) {
|
if (registeredFunctions.hasOwnProperty(identifier)) {
|
||||||
throw new Error(`A function with the identifier '${identifier}' has already been registered.`);
|
throw new Error(`A function with the identifier '${identifier}' has already been registered.`);
|
||||||
}
|
}
|
||||||
|
|
@ -14,13 +16,19 @@ export function registerFunction(identifier: string, implementation: Function) {
|
||||||
registeredFunctions[identifier] = implementation;
|
registeredFunctions[identifier] = implementation;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the JSON-marshalled RegisteredFunction.Invoke calls
|
export function getRegisteredFunction(identifier: string): Function {
|
||||||
registerFunction('__blazor_InvokeJson', (identifier: System_String, ...argsJson: System_String[]) => {
|
// By prioritising the internal ones, we ensure you can't override them
|
||||||
const identifierJsString = platform.toJavaScriptString(identifier);
|
const result = internalRegisteredFunctions[identifier] || registeredFunctions[identifier];
|
||||||
if (!(registeredFunctions && registeredFunctions.hasOwnProperty(identifierJsString))) {
|
if (result) {
|
||||||
throw new Error(`Could not find registered function with name "${identifierJsString}".`);
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new Error(`Could not find registered function with name '${identifier}'.`);
|
||||||
}
|
}
|
||||||
const funcInstance = registeredFunctions[identifierJsString];
|
}
|
||||||
|
|
||||||
|
export function invokeWithJsonMarshalling(identifier: System_String, ...argsJson: System_String[]) {
|
||||||
|
const identifierJsString = platform.toJavaScriptString(identifier);
|
||||||
|
const funcInstance = getRegisteredFunction(identifierJsString);
|
||||||
const args = argsJson.map(json => JSON.parse(platform.toJavaScriptString(json)));
|
const args = argsJson.map(json => JSON.parse(platform.toJavaScriptString(json)));
|
||||||
const result = funcInstance.apply(null, args);
|
const result = funcInstance.apply(null, args);
|
||||||
if (result !== null && result !== undefined) {
|
if (result !== null && result !== undefined) {
|
||||||
|
|
@ -29,4 +37,4 @@ registerFunction('__blazor_InvokeJson', (identifier: System_String, ...argsJson:
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { registerFunction } from '../RegisteredFunction';
|
import { System_Object, System_String, System_Array, MethodHandle, Pointer } from '../Platform/Platform';
|
||||||
import { System_Object, System_String, System_Array, MethodHandle, Pointer } from '../Platform/Platform';
|
|
||||||
import { platform } from '../Environment';
|
import { platform } from '../Environment';
|
||||||
import { getTreeNodePtr, renderTreeNode, NodeType, RenderTreeNodePointer } from './RenderTreeNode';
|
import { getTreeNodePtr, renderTreeNode, NodeType, RenderTreeNodePointer } from './RenderTreeNode';
|
||||||
let raiseEventMethod: MethodHandle;
|
let raiseEventMethod: MethodHandle;
|
||||||
|
|
@ -14,10 +13,7 @@ type ComponentIdToParentElement = { [componentId: number]: Element };
|
||||||
type BrowserRendererRegistry = { [browserRendererId: number]: ComponentIdToParentElement };
|
type BrowserRendererRegistry = { [browserRendererId: number]: ComponentIdToParentElement };
|
||||||
const browserRenderers: BrowserRendererRegistry = {};
|
const browserRenderers: BrowserRendererRegistry = {};
|
||||||
|
|
||||||
registerFunction('_blazorAttachComponentToElement', attachComponentToElement);
|
export function attachComponentToElement(browserRendererId: number, elementSelector: System_String, componentId: number) {
|
||||||
registerFunction('_blazorRender', renderRenderTree);
|
|
||||||
|
|
||||||
function attachComponentToElement(browserRendererId: number, elementSelector: System_String, componentId: number) {
|
|
||||||
const elementSelectorJs = platform.toJavaScriptString(elementSelector);
|
const elementSelectorJs = platform.toJavaScriptString(elementSelector);
|
||||||
const element = document.querySelector(elementSelectorJs);
|
const element = document.querySelector(elementSelectorJs);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
|
|
@ -28,7 +24,7 @@ function attachComponentToElement(browserRendererId: number, elementSelector: Sy
|
||||||
browserRenderers[browserRendererId][componentId] = element;
|
browserRenderers[browserRendererId][componentId] = element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRenderTree(renderComponentArgs: Pointer) {
|
export function renderRenderTree(renderComponentArgs: Pointer) {
|
||||||
const browserRendererId = platform.readHeapInt32(renderComponentArgs, 0);
|
const browserRendererId = platform.readHeapInt32(renderComponentArgs, 0);
|
||||||
const browserRenderer = browserRenderers[browserRendererId];
|
const browserRenderer = browserRenderers[browserRendererId];
|
||||||
if (!browserRenderer) {
|
if (!browserRenderer) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace Microsoft.Blazor.Browser.Interop
|
||||||
// This is a low-perf convenience method that bypasses the need to deal with
|
// This is a low-perf convenience method that bypasses the need to deal with
|
||||||
// .NET memory and data structures on the JS side
|
// .NET memory and data structures on the JS side
|
||||||
var argsJson = args.Select(Json.Serialize);
|
var argsJson = args.Select(Json.Serialize);
|
||||||
var resultJson = InvokeUnmarshalled<string>("__blazor_InvokeJson",
|
var resultJson = InvokeUnmarshalled<string>("invokeWithJsonMarshalling",
|
||||||
argsJson.Prepend(identifier).ToArray());
|
argsJson.Prepend(identifier).ToArray());
|
||||||
return Json.Deserialize<TRes>(resultJson);
|
return Json.Deserialize<TRes>(resultJson);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.Blazor.Browser.Rendering
|
||||||
{
|
{
|
||||||
var componentId = AssignComponentId(component);
|
var componentId = AssignComponentId(component);
|
||||||
RegisteredFunction.InvokeUnmarshalled<int, string, int, object>(
|
RegisteredFunction.InvokeUnmarshalled<int, string, int, object>(
|
||||||
"_blazorAttachComponentToElement",
|
"attachComponentToElement",
|
||||||
_browserRendererId,
|
_browserRendererId,
|
||||||
domElementSelector,
|
domElementSelector,
|
||||||
componentId);
|
componentId);
|
||||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.Blazor.Browser.Rendering
|
||||||
ArraySegment<RenderTreeNode> renderTree)
|
ArraySegment<RenderTreeNode> renderTree)
|
||||||
{
|
{
|
||||||
RegisteredFunction.InvokeUnmarshalled<RenderComponentArgs, object>(
|
RegisteredFunction.InvokeUnmarshalled<RenderComponentArgs, object>(
|
||||||
"_blazorRender",
|
"renderRenderTree",
|
||||||
new RenderComponentArgs
|
new RenderComponentArgs
|
||||||
{
|
{
|
||||||
BrowserRendererId = _browserRendererId,
|
BrowserRendererId = _browserRendererId,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue