Example #1
0
 // used by InitializeProjectionForType
 private JavaScriptObject CreateObjectFor(JavaScriptEngine engine, JavaScriptProjection baseTypeProjection)
 {
     if (baseTypeProjection != null)
     {
         // todo: revisit to see if there is a better way to do this
         // Can probably get
         // ((engine.GlobalObject.GetPropertyByName("Object") as JavaScriptObject).GetPropertyByName("create") as JavaScriptFunction).Invoke(baseTypeProjection.Prototype)
         // but this is so much clearer
         dynamic          global = engine.GlobalObject;
         JavaScriptObject result = global.Object.create(baseTypeProjection.Prototype);
         return(result);
     }
     else
     {
         return(engine.CreateObject());
     }
 }
        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);
        }
 // used by InitializeProjectionForType
 private JavaScriptObject CreateObjectFor(JavaScriptEngine engine, JavaScriptProjection baseTypeProjection)
 {
     if (baseTypeProjection != null)
     {
         // todo: revisit to see if there is a better way to do this
         // Can probably get 
         // ((engine.GlobalObject.GetPropertyByName("Object") as JavaScriptObject).GetPropertyByName("create") as JavaScriptFunction).Invoke(baseTypeProjection.Prototype) 
         // but this is so much clearer
         dynamic global = engine.GlobalObject;
         JavaScriptObject result = global.Object.create(baseTypeProjection.Prototype);
         return result;
     }
     else
     {
         return engine.CreateObject();
     }
 }
Example #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);
        }
Example #5
0
        private JavaScriptProjection InitializeProjectionForType(Type t)
        {
            var eng = GetEngineAndClaimContext();

            ObjectReflector      reflector          = ObjectReflector.Create(t);
            JavaScriptProjection baseTypeProjection = null;

            if (reflector.HasBaseType)
            {
                Type baseType = reflector.GetBaseType();
                if (!projectionTypes_.TryGetValue(baseType, out baseTypeProjection))
                {
                    baseTypeProjection           = InitializeProjectionForType(baseType);
                    baseTypeProjection.RefCount += 1;
                    projectionTypes_[baseType]   = baseTypeProjection;
                }
            }

            var publicConstructors       = reflector.GetConstructors();
            var publicInstanceProperties = reflector.GetProperties(instance: true);
            var publicStaticProperties   = reflector.GetProperties(instance: false);
            var publicInstanceMethods    = reflector.GetMethods(instance: true);
            var publicStaticMethods      = reflector.GetMethods(instance: false);
            var publicInstanceEvents     = reflector.GetEvents(instance: true);
            var publicStaticEvents       = reflector.GetEvents(instance: false);

            if (AnyHaveSameArity(out var duplicateName, publicConstructors, publicInstanceMethods, publicStaticMethods, publicInstanceProperties, publicStaticProperties))
            {
                throw new InvalidOperationException("The specified type cannot be marshaled; some publicly accessible members have the same arity.  Projected methods can't differentiate only by type (e.g., Add(int, int) and Add(float, float) would cause this error): " + t.FullName + "::" + duplicateName);
            }

            JavaScriptFunction ctor;

            if (publicConstructors.Any())
            {
                // e.g. var MyObject = function() { [native code] };
                ctor = eng.CreateFunction((engine, constr, thisObj, args) =>
                {
                    // todo
                    return(FromObject(publicConstructors.First().Invoke(new object[] { })));
                }, t.FullName);
            }
            else
            {
                ctor = eng.CreateFunction((engine, constr, thisObj, args) =>
                {
                    return(eng.UndefinedValue);
                }, t.FullName);
            }

            // MyObject.prototype = Object.create(baseTypeProjection.PrototypeObject);
            var prototypeObj = CreateObjectFor(eng, baseTypeProjection);

            ctor.SetPropertyByName("prototype", prototypeObj);
            // MyObject.prototype.constructor = MyObject;
            prototypeObj.SetPropertyByName("constructor", ctor);

            // MyObject.CreateMyObject = function() { [native code] };
            ProjectMethods(t.FullName, ctor, eng, publicStaticMethods);
            // Object.defineProperty(MyObject, 'Foo', { get: function() { [native code] } });
            ProjectProperties(t.FullName, ctor, eng, publicStaticProperties);
            // MyObject.addEventListener = function() { [native code] };
            if ((baseTypeProjection?.HasStaticEvents ?? false) || publicStaticEvents.Any())
            {
                ProjectEvents(t.FullName, ctor, eng, publicStaticEvents, baseTypeProjection, instance: false);
            }

            // MyObject.prototype.ToString = function() { [native code] };
            ProjectMethods(t.FullName + ".prototype", prototypeObj, eng, publicInstanceMethods);
            // Object.defineProperty(MyObject.prototype, 'baz', { get: function() { [native code] }, set: function() { [native code] } });
            ProjectProperties(t.FullName + ".prototype", prototypeObj, eng, publicInstanceProperties);
            // MyObject.prototype.addEventListener = function() { [native code] };
            if ((baseTypeProjection?.HasInstanceEvents ?? false) || publicInstanceEvents.Any())
            {
                ProjectEvents(t.FullName + ".prototype", prototypeObj, eng, publicInstanceEvents, baseTypeProjection, instance: true);
            }

            prototypeObj.Freeze();

            return(new JavaScriptProjection
            {
                RefCount = 0,
                Constructor = ctor,
                Prototype = prototypeObj,
                HasInstanceEvents = (baseTypeProjection?.HasInstanceEvents ?? false) || publicInstanceEvents.Any(),
                HasStaticEvents = (baseTypeProjection?.HasStaticEvents ?? false) || publicStaticEvents.Any(),
            });
        }