Пример #1
0
        private void ProjectMethods(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable <MethodInfo> methods)
        {
            var methodsByName = methods.GroupBy(m => m.Name);

            foreach (var group in methodsByName)
            {
                var method = engine.CreateFunction((eng, ctor, thisObj, args) =>
                {
                    var @this = thisObj as JavaScriptObject;
                    if (@this == null)
                    {
                        eng.SetException(eng.CreateTypeError("Could not call method '" + group.Key + "' because there was an invalid 'this' context."));
                        return(eng.UndefinedValue);
                    }

                    var argsArray = args.ToArray();
                    var candidate = GetBestFitMethod(group, thisObj, argsArray);
                    if (candidate == null)
                    {
                        eng.SetException(eng.CreateReferenceError("Could not find suitable method or not enough arguments to invoke '" + group.Key + "'."));
                        return(eng.UndefinedValue);
                    }

                    List <object> argsToPass = new List <object>();
                    for (int i = 0; i < candidate.GetParameters().Length; i++)
                    {
                        argsToPass.Add(ToObject(argsArray[i]));
                    }

                    try
                    {
                        return(FromObject(candidate.Invoke(@this.ExternalObject, argsToPass.ToArray())));
                    }
                    catch (Exception ex)
                    {
                        eng.SetException(FromObject(ex));
                        return(eng.UndefinedValue);
                    }
                }, owningTypeName + "." + group.Key);
                //var propDescriptor = engine.CreateObject();
                //propDescriptor.SetPropertyByName("configurable", engine.TrueValue);
                //propDescriptor.SetPropertyByName("enumerable", engine.TrueValue);
                //propDescriptor.SetPropertyByName("value", method);
                //target.DefineProperty(group.Key, propDescriptor);
                target.SetPropertyByName(group.Key, method);
            }
        }
Пример #2
0
        private void ProjectEvents(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable<EventInfo> events, JavaScriptProjection baseTypeProjection, bool instance)
        {
            var eventsArray = events.ToArray();
            var eventsLookup = eventsArray.ToDictionary(ei => ei.Name.ToLower());
            // General approach here
            // if there is a base thing, invoke that
            // for each event, register a delegate that marshals it back to JavaScript
            var add = engine.CreateFunction((eng, ctor, thisObj, args) =>
            {
                bool callBase = instance && (baseTypeProjection?.HasInstanceEvents ?? false);
                var @this = thisObj as JavaScriptObject;
                if (@this == null)
                    return eng.UndefinedValue;

                if (callBase)
                {
                    var baseObj = baseTypeProjection.Prototype;
                    var baseFn = baseObj.GetPropertyByName("addEventListener") as JavaScriptFunction;
                    if (baseFn != null)
                    {
                        baseFn.Call(@this, args);
                    }
                }

                var argsArray = args.ToArray();
                if (argsArray.Length < 2)
                    return eng.UndefinedValue;

                string eventName = argsArray[0].ToString();
                JavaScriptFunction callbackFunction = argsArray[1] as JavaScriptFunction;
                if (callbackFunction == null)
                    return eng.UndefinedValue;

                EventInfo curEvent;
                if (!eventsLookup.TryGetValue(eventName, out curEvent))
                    return eng.UndefinedValue;

                MethodInfo targetMethod = curEvent.EventHandlerType.GetMethod("Invoke");

                var paramsExpr = targetMethod.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
                int cookie = EventMarshaler.RegisterDelegate(callbackFunction, SynchronizationContext.Current);

                var marshaler = Expression.Lambda(curEvent.EventHandlerType, Expression.Block(
                    Expression.Call(
                        typeof(EventMarshaler).GetMethod(nameof(EventMarshaler.InvokeJavaScriptCallback)), 
                        Expression.Constant(cookie), 
                        Expression.NewArrayInit(typeof(string), targetMethod.GetParameters().Select(p => Expression.Constant(p.Name))),
                        Expression.NewArrayInit(typeof(object), paramsExpr))
                ), paramsExpr);

                curEvent.AddMethod.Invoke(@this.ExternalObject, new object[] { marshaler.Compile() });

                return eng.UndefinedValue;
            }, owningTypeName + ".addEventListener");
            target.SetPropertyByName("addEventListener", add);
        }
Пример #3
0
        private void ProjectMethods(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable<MethodInfo> methods)
        {
            var methodsByName = methods.GroupBy(m => m.Name);
            foreach (var group in methodsByName)
            {
                var method = engine.CreateFunction((eng, ctor, thisObj, args) =>
                {
                    var @this = thisObj as JavaScriptObject;
                    if (@this == null)
                    {
                        eng.SetException(eng.CreateTypeError("Could not call method '" + group.Key + "' because there was an invalid 'this' context."));
                        return eng.UndefinedValue;
                    }

                    var argsArray = args.ToArray();
                    var candidate = GetBestFitMethod(group, thisObj, argsArray);
                    if (candidate == null)
                    {
                        eng.SetException(eng.CreateReferenceError("Could not find suitable method or not enough arguments to invoke '" + group.Key + "'."));
                        return eng.UndefinedValue;
                    }

                    List<object> argsToPass = new List<object>();
                    for (int i = 0; i < candidate.GetParameters().Length; i++)
                    {
                        argsToPass.Add(ToObject(argsArray[i]));
                    }

                    try
                    {
                        return FromObject(candidate.Invoke(@this.ExternalObject, argsToPass.ToArray()));
                    }
                    catch (Exception ex)
                    {
                        eng.SetException(FromObject(ex));
                        return eng.UndefinedValue;
                    }
                }, owningTypeName + "." + group.Key);
                //var propDescriptor = engine.CreateObject();
                //propDescriptor.SetPropertyByName("configurable", engine.TrueValue);
                //propDescriptor.SetPropertyByName("enumerable", engine.TrueValue);
                //propDescriptor.SetPropertyByName("value", method);
                //target.DefineProperty(group.Key, propDescriptor);
                target.SetPropertyByName(group.Key, method);
            }
        }
Пример #4
0
        private void ProjectEvents(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable <EventInfo> events, JavaScriptProjection baseTypeProjection, bool instance)
        {
            var eventsArray  = events.ToArray();
            var eventsLookup = eventsArray.ToDictionary(ei => ei.Name.ToLower());
            // General approach here
            // if there is a base thing, invoke that
            // for each event, register a delegate that marshals it back to JavaScript
            var add = engine.CreateFunction((eng, ctor, thisObj, args) =>
            {
                bool callBase = instance && (baseTypeProjection?.HasInstanceEvents ?? false);
                var @this     = thisObj as JavaScriptObject;
                if (@this == null)
                {
                    return(eng.UndefinedValue);
                }

                if (callBase)
                {
                    var baseObj = baseTypeProjection.Prototype;
                    var baseFn  = baseObj.GetPropertyByName("addEventListener") as JavaScriptFunction;
                    if (baseFn != null)
                    {
                        baseFn.Call(@this, args);
                    }
                }

                var argsArray = args.ToArray();
                if (argsArray.Length < 2)
                {
                    return(eng.UndefinedValue);
                }

                string eventName = argsArray[0].ToString();
                JavaScriptFunction callbackFunction = argsArray[1] as JavaScriptFunction;
                if (callbackFunction == null)
                {
                    return(eng.UndefinedValue);
                }

                EventInfo curEvent;
                if (!eventsLookup.TryGetValue(eventName, out curEvent))
                {
                    return(eng.UndefinedValue);
                }

                MethodInfo targetMethod = curEvent.EventHandlerType.GetMethod("Invoke");

                var paramsExpr = targetMethod.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
                int cookie     = EventMarshaler.RegisterDelegate(callbackFunction, SynchronizationContext.Current);

                var marshaler = Expression.Lambda(curEvent.EventHandlerType, Expression.Block(
                                                      Expression.Call(
                                                          typeof(EventMarshaler).GetMethod(nameof(EventMarshaler.InvokeJavaScriptCallback)),
                                                          Expression.Constant(cookie),
                                                          Expression.NewArrayInit(typeof(string), targetMethod.GetParameters().Select(p => Expression.Constant(p.Name))),
                                                          Expression.NewArrayInit(typeof(object), paramsExpr))
                                                      ), paramsExpr);

                curEvent.AddMethod.Invoke(@this.ExternalObject, new object[] { marshaler.Compile() });

                return(eng.UndefinedValue);
            }, owningTypeName + ".addEventListener");

            target.SetPropertyByName("addEventListener", add);
        }
Пример #5
0
        private void ProjectMethods(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable <MethodInfo> methods)
        {
            var methodsByName = methods.GroupBy(m => m.Name);

            foreach (var group in methodsByName)
            {
                var groupKey        = group.Key;
                var groupCandidates = group.ToList();
                var method          = engine.CreateFunction((eng, ctor, thisObj, args) =>
                {
                    var @this = thisObj as JavaScriptObject;
                    if (@this == null)
                    {
                        if (thisObj.Type == JavaScriptValueType.Undefined)
                        {
                            var global = engine.GlobalObject.Prototype;
                            thisObj    = @this = global;
                        }
                        else
                        {
                            eng.SetException(eng.CreateTypeError("Could not call method '" + groupKey + "' because there was an invalid 'this' context."));
                            return(eng.UndefinedValue);
                        }
                    }

                    var argsArray = args.ToArray();
                    var candidate = GetBestFitMethod(groupCandidates, thisObj, argsArray);
                    if (candidate == null)
                    {
                        eng.SetException(eng.CreateReferenceError("Could not find suitable method or not enough arguments to invoke '" + groupKey + "'."));
                        return(eng.UndefinedValue);
                    }

                    if (!parameterCache.TryGetValue(candidate, out var parameters))
                    {
                        parameters = candidate.GetParameters();
                        parameterCache[candidate] = parameters;
                    }


                    var parameterCount = parameters.Length;
                    var argsToPass     = engine.BorrowArrayOfObjects(parameterCount);
                    for (int i = 0; i < parameterCount && i < argsArray.Length; i++)
                    {
                        var val           = ToObject(argsArray[i]);
                        var parameterType = parameters[i].ParameterType;
                        if (val != null && parameterType == typeof(string) && !(val is string))
                        {
                            val = argsArray[i].ToString();
                        }
                        if (val is Double d && parameterType != typeof(Double))
                        {
                            val = Convert.ChangeType(val, parameterType);
                        }
                        argsToPass[i] = val;
                    }

                    var t      = @this;
                    object obj = null;
                    while (obj == null && t.IsTruthy && t.Prototype != null)
                    {
                        obj = t.ExternalObject;
                        if (obj == null)
                        {
                            t = t.Prototype;
                        }
                    }

                    try
                    {
                        var result = FromObject(candidate.Invoke(obj, argsToPass));
                        engine.ReleaseArrayOfObjects(argsToPass);
                        return(result);
                    }
                    catch (Exception ex)
                    {
                        eng.SetException(FromObject(ex), ex);
                        return(eng.UndefinedValue);
                    }
                }, owningTypeName + "." + groupKey);
                //var propDescriptor = engine.CreateObject();
                //propDescriptor.SetPropertyByName("configurable", engine.TrueValue);
                //propDescriptor.SetPropertyByName("enumerable", engine.TrueValue);
                //propDescriptor.SetPropertyByName("value", method);
                //target.DefineProperty(group.Key, propDescriptor);
                target.SetPropertyByName(groupKey, method);
            }
        }