private JsValue GetPropertyValue(BaristaContext context, PropertyInfo prop, string propertyName, JsObject thisObj) { object targetObj = null; if (thisObj == null) { context.CurrentScope.SetException(context.CreateTypeError($"Could not retrieve property '{propertyName}': Invalid 'this' context.")); return(context.Undefined); } if (thisObj.TryGetBean(out JsExternalObject xoObj)) { targetObj = xoObj.Target; } try { var result = prop.GetValue(targetObj); if (context.Converter.TryFromObject(context, result, out JsValue resultValue)) { return(resultValue); } else { return(context.Undefined); } } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } }
private JsValue GetIndexerPropertyValue(BaristaContext context, PropertyInfo indexerProp, string propertyName, JsObject thisObj, object[] args) { object targetObj = null; if (thisObj == null) { context.CurrentScope.SetException(context.CreateTypeError($"Could not get indexer property '{propertyName}': Invalid 'this' context.")); return(context.Undefined); } if (args.Length < 1) { context.CurrentScope.SetException(context.CreateTypeError($"Could not get indexer property '{propertyName}': At least one index must be specified.")); return(context.Undefined); } if (thisObj.TryGetBean(out JsExternalObject xoObj)) { targetObj = xoObj.Target; } try { var indexParameters = indexerProp.GetIndexParameters(); var indexArgs = new object[indexParameters.Length]; for (int i = 0; i < indexParameters.Length; i++) { indexArgs[i] = Convert.ChangeType(args.ElementAtOrDefault(i), indexParameters[i].ParameterType); } var result = indexerProp.GetValue(targetObj, indexArgs); if (context.Converter.TryFromObject(context, result, out JsValue resultValue)) { return(resultValue); } else { return(context.Undefined); } } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } }
private JsValue SetIndexerPropertyValue(BaristaContext context, PropertyInfo indexerProp, string propertyName, JsObject thisObj, object[] args) { object targetObj = null; if (thisObj == null) { context.CurrentScope.SetException(context.CreateTypeError($"Could not set indexer property '{propertyName}': Invalid 'this' context.")); return(context.Undefined); } if (args.Length < 2) { context.CurrentScope.SetException(context.CreateTypeError($"Could not set indexer property '{propertyName}': At least one index and a value be specified.")); return(context.Undefined); } if (thisObj.TryGetBean(out JsExternalObject xoObj)) { targetObj = xoObj.Target; } try { var value = args.LastOrDefault(); var nativeArgs = args.Take(args.Length - 1).ToArray(); var indexParameters = indexerProp.GetIndexParameters(); var indexArgs = new object[indexParameters.Length]; for (int i = 0; i < indexParameters.Length; i++) { indexArgs[i] = Convert.ChangeType(nativeArgs.ElementAtOrDefault(i), indexParameters[i].ParameterType); } indexerProp.SetValue(targetObj, value, indexArgs); return(context.Undefined); } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } }
private JsValue SetPropertyValue(BaristaContext context, PropertyInfo prop, string propertyName, JsObject thisObj, object[] args) { object targetObj = null; if (thisObj == null) { context.CurrentScope.SetException(context.CreateTypeError($"Could not set property '{propertyName}': Invalid 'this' context.")); return(context.Undefined); } if (thisObj.TryGetBean(out JsExternalObject xoObj)) { targetObj = xoObj.Target; } var setPropertyValueType = prop.SetMethod.GetParameters().First().ParameterType; var argumentValue = args.ElementAtOrDefault(0); try { //If the exposed property is a JsValue, Attempt to convert and then set the property. if (typeof(JsValue).IsSameOrSubclass(setPropertyValueType) && context.Converter.TryFromObject(context, argumentValue, out JsValue jsValue) && setPropertyValueType.IsSameOrSubclass(jsValue.GetType())) { prop.SetValue(targetObj, jsValue); return(context.Undefined); } var value = Convert.ChangeType(args.ElementAtOrDefault(0), setPropertyValueType); prop.SetValue(targetObj, value); return(context.Undefined); } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } }
private void ProjectMethods(BaristaContext context, JsObject targetObject, ObjectReflector reflector, IDictionary <string, IList <MethodInfo> > methods) { foreach (var method in methods) { var methodName = method.Key; var methodInfos = method.Value; var fn = context.CreateFunction(new BaristaFunctionDelegate((calleeObj, isConstructCall, thisObj, args) => { object targetObj = null; if (thisObj == null) { context.CurrentScope.SetException(context.CreateTypeError($"Could not call function '{methodName}': Invalid 'this' context.")); return(context.Undefined); } if (thisObj.TryGetBean(out JsExternalObject xoObj)) { targetObj = xoObj.Target; } try { var bestMethod = reflector.GetMethodBestMatch(methodInfos, args); if (bestMethod == null) { var ex = context.CreateTypeError($"Failed to call function '{methodName}': Could not find a matching function for the provided arguments."); context.CurrentScope.SetException(ex); return(context.Undefined); } //Convert the args into the native args of the method. var methodParams = bestMethod.GetParameters(); var convertedArgs = ConvertArgsToParamTypes(context, args, methodParams); var result = bestMethod.Invoke(targetObj, convertedArgs); if (context.Converter.TryFromObject(context, result, out JsValue resultValue)) { return(resultValue); } else { context.CurrentScope.SetException(context.CreateTypeError($"The call to '{methodName}' was successful, but the result could not be converted into a JavaScript object.")); return(context.Undefined); } } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } })); var functionDescriptor = context.CreateObject(); if (methodInfos.All(mi => BaristaPropertyAttribute.GetAttribute(mi).Configurable)) { functionDescriptor.SetProperty("configurable", context.True); } if (methodInfos.All(mi => BaristaPropertyAttribute.GetAttribute(mi).Enumerable)) { functionDescriptor.SetProperty("enumerable", context.True); } if (methodInfos.All(mi => BaristaPropertyAttribute.GetAttribute(mi).Writable)) { functionDescriptor.SetProperty("writable", context.True); } functionDescriptor.SetProperty("value", fn); targetObject.SetProperty(context.CreateString(methodName), functionDescriptor); } }
public bool TryCreatePrototypeFunction(BaristaContext context, Type typeToConvert, out JsFunction ctor) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (typeToConvert == null) { throw new ArgumentNullException(nameof(typeToConvert)); } if (m_prototypes.ContainsKey(typeToConvert)) { ctor = m_prototypes[typeToConvert]; return(true); } var reflector = new ObjectReflector(typeToConvert); JsFunction superCtor = null; var baseType = reflector.GetBaseType(); if (baseType != null && !baseType.IsSameOrSubclass(typeof(JsValue)) && TryCreatePrototypeFunction(context, baseType, out JsFunction fnSuper)) { superCtor = fnSuper; } var objectName = BaristaObjectAttribute.GetBaristaObjectNameFromType(typeToConvert); //Get all the property descriptors for the specified type. var staticPropertyDescriptors = context.CreateObject(); var instancePropertyDescriptors = context.CreateObject(); //Get static and instance properties. ProjectProperties(context, staticPropertyDescriptors, reflector.GetProperties(false)); ProjectProperties(context, instancePropertyDescriptors, reflector.GetProperties(true)); //Get static and instance indexer properties. ProjectIndexerProperties(context, staticPropertyDescriptors, reflector.GetIndexerProperties(false)); ProjectIndexerProperties(context, instancePropertyDescriptors, reflector.GetIndexerProperties(true)); //Get static and instance methods. ProjectMethods(context, staticPropertyDescriptors, reflector, reflector.GetUniqueMethodsByName(false)); ProjectMethods(context, instancePropertyDescriptors, reflector, reflector.GetUniqueMethodsByName(true)); //Get static and instance events. ProjectEvents(context, staticPropertyDescriptors, reflector, reflector.GetEventTable(false)); ProjectEvents(context, instancePropertyDescriptors, reflector, reflector.GetEventTable(true)); //Get the [[iterator]] property. ProjectIEnumerable(context, instancePropertyDescriptors, reflector); JsFunction fnCtor; var publicConstructors = reflector.GetConstructors(); if (publicConstructors.Any()) { fnCtor = context.CreateFunction(new BaristaFunctionDelegate((calleeObj, isConstructCall, thisObj, args) => { if (thisObj == null) { var ex = context.CreateTypeError($"Failed to construct '{objectName}': 'this' must be specified."); context.CurrentScope.SetException(ex); return(context.Undefined); } if (superCtor != null) { superCtor.Call(thisObj); } context.Object.DefineProperties(thisObj, instancePropertyDescriptors); //If this isn't a construct call, don't attempt to set the bean if (!isConstructCall) { return(thisObj); } //Set our native object. JsExternalObject externalObject = null; //!!Special condition -- if there's exactly one argument, and if it matches the enclosing type, //don't invoke the type's constructor, rather, just wrap the object with the JsObject. if (args.Length == 1 && args[0].GetType() == typeToConvert) { externalObject = context.CreateExternalObject(args[0]); } else { try { var bestConstructor = reflector.GetConstructorBestMatch(args); if (bestConstructor == null) { var ex = context.CreateTypeError($"Failed to construct '{objectName}': Could not find a matching constructor for the provided arguments."); context.CurrentScope.SetException(ex); return(context.Undefined); } //Convert the args into the native args of the constructor. var constructorParams = bestConstructor.GetParameters(); var convertedArgs = ConvertArgsToParamTypes(context, args, constructorParams); var newObj = bestConstructor.Invoke(convertedArgs); externalObject = context.CreateExternalObject(newObj); } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } } thisObj.SetBean(externalObject); return(thisObj); }), objectName); } else { fnCtor = context.CreateFunction(new BaristaFunctionDelegate((calleeObj, isConstructCall, thisObj, args) => { var ex = context.CreateTypeError($"Failed to construct '{objectName}': This object cannot be constructed."); context.CurrentScope.SetException(ex); return(context.Undefined); }), objectName); } //We've got everything we need. fnCtor.Prototype = context.Object.Create(superCtor == null ? context.Object.Prototype : superCtor.Prototype); context.Object.DefineProperties(fnCtor, staticPropertyDescriptors); m_prototypes.Add(typeToConvert, fnCtor); ctor = fnCtor; return(true); }