/// <summary> /// Binds all the fields for the instance. /// </summary> private void BindFields(JsBinding binding, IHostType hostType, object instance) { // TODO: Seems like we can hoist this into JsInterop, which would allow us refactor // TODO: out the builder class completely. var fields = hostType.FieldNames; for (int i = 0; i < fields.Count; ++i) { var fieldName = fields[i]; binding.AddProperty( fieldName, (v, s, args, argLength, data) => { var fieldInfo = hostType.FieldFor(fieldName).Field; var result = fieldInfo.GetValue(instance); var returnType = JsConversions.TypeFor(result, fieldInfo.FieldType); return(_interop.ToJsObject(result, returnType)); }, (v, s, args, argLength, data) => { var fieldInfo = hostType.FieldFor(fieldName).Field; var fieldType = fieldInfo.FieldType; var value = _interop.ToHostObject(args[1], fieldType); fieldInfo.SetValue(instance, value); return(JavaScriptValue.Invalid); }); } }
/// <summary> /// Binds all public properties for the instance. /// </summary> private void BindProperties(JsBinding binding, IHostType hostType, object instance) { // TODO: Seems like we can hoist this into JsInterop, which would allow us refactor // TODO: out the builder class completely. var properties = hostType.PropertyNames; for (int i = 0; i < properties.Count; ++i) { var propertyName = properties[i]; binding.AddProperty( propertyName, (v, s, args, argLength, data) => { var get = hostType.PropertyFor(propertyName).Getter; var result = get.Invoke(instance, EmptyParameters); var returnType = JsConversions.TypeFor(result, get.ReturnType); return(_interop.ToJsObject(result, returnType)); }, (v, s, args, argLength, data) => { var hostProperty = hostType.PropertyFor(propertyName); var propType = hostProperty.PropertyType; var set = hostProperty.Setter; var value = _interop.ToHostObject(args[1], propType); set.Invoke(instance, new[] { value }); return(JavaScriptValue.Invalid); }); } }
/// <summary> /// Creates the <see cref="JsBinding"/> instance representing the new bound JavaScript object. /// </summary> public JsBinding Build() { return(_scope.Run(() => { // Create a host object binding or new JS Object binding var jsValue = null != _boundTo ? _binder.BindObject(_boundTo) : JavaScriptValue.CreateObject(); var binding = new JsBinding(_scope, _binder, _interop, jsValue); // Bind Host Object Methods, Properties, and Fields if bound to host object if (null != _boundTo && null != _hostType) { BindMethods(binding, _hostType, _boundTo); BindProperties(binding, _hostType, _boundTo); BindFields(binding, _hostType, _boundTo); } // Set custom binding values if (null != _values) { for (int i = 0; i < _values.Count; ++i) { var valueDescriptor = _values[i]; binding.SetValue(valueDescriptor.Name, valueDescriptor.Value, valueDescriptor.ValueType); } } return binding; })); }
/// <summary> /// Adds function bindings for all methods in the <see cref="IHostType"/>. /// </summary> private void BindMethods(JsBinding binding, IHostType hostType, object instance) { // TODO: Seems like we can hoist this into JsInterop, which would allow us refactor // TODO: out the builder class completely. var methods = hostType.MethodNames; for (int i = 0; i < methods.Count; ++i) { var methodName = methods[i]; binding.AddFunction( methodName, (v, s, args, argLength, data) => { var totalParameters = argLength - 1; var hostMethods = hostType.MethodsFor(methodName, totalParameters); if (hostMethods.Count == 0) { var message = $"Calling host function that does not exist: [Method: {methodName}, Instance: {instance}]"; JsErrorHelper.SetJsException(message); return(JavaScriptValue.Invalid); } HostMethod hostMethodInfo; // Only a single method returned. Call this optimistically if (hostMethods.Count == 1) { hostMethodInfo = hostMethods[0]; } else { // Get the Parameter Key, and look up cached invocation var invokeKey = JsConversions.ToInvokeKey(methodName, args, argLength); if (!hostType.TryGetInvocation(invokeKey, out hostMethodInfo)) { // Otherwise, locate best method for these argument, and cache hostMethodInfo = FindBestMethod(hostMethods, args, argLength); hostType.CacheInvocation(invokeKey, hostMethodInfo); } } if (null == hostMethodInfo) { LogMethodSelectionFailure(hostMethods, args, argLength); JsErrorHelper.SetJsException( $"Calling host function that does not exist: [Method: {methodName}, Instance: {instance}]"); return(JavaScriptValue.Invalid); } try { var realParams = ToParameters(hostMethodInfo, args, argLength); var result = hostMethodInfo.Method.Invoke(instance, realParams); var resultType = hostMethodInfo.ReturnType; if (resultType == typeof(void)) { return(JavaScriptValue.Invalid); } resultType = JsConversions.TypeFor(result, resultType); return(_interop.ToJsObject(result, resultType)); } catch (Exception e) { LogMethodInvocationInfo(hostMethodInfo, instance); throw; } }); } }