示例#1
0
        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);
            }
        }
示例#2
0
        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);
            }
        }
示例#3
0
        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);
            }
        }
示例#4
0
        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);
            }
        }
示例#5
0
        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);
            }
        }
示例#6
0
        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);
        }