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); } }
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); }
private void ProjectProperties(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable<PropertyInfo> properties) { foreach (var prop in properties) { if (prop.GetIndexParameters().Length > 0) throw new NotSupportedException("Index properties not supported for projecting CLR to JavaScript objects."); JavaScriptFunction jsGet = null, jsSet = null; if (prop.GetMethod != null) { jsGet = engine.CreateFunction((eng, ctor, thisObj, args) => { var @this = thisObj as JavaScriptObject; if (@this == null) { eng.SetException(eng.CreateTypeError("Could not retrieve property '" + prop.Name + "' because there was an invalid 'this' context.")); return eng.UndefinedValue; } try { return FromObject(prop.GetValue(@this.ExternalObject)); } catch (Exception ex) { eng.SetException(FromObject(ex)); return eng.UndefinedValue; } }, owningTypeName + "." + prop.Name + ".get"); } if (prop.SetMethod != null) { jsSet = engine.CreateFunction((eng, ctor, thisObj, args) => { var @this = thisObj as JavaScriptObject; if (@this == null) { eng.SetException(eng.CreateTypeError("Could not retrieve property '" + prop.Name + "' because there was an invalid 'this' context.")); return eng.UndefinedValue; } try { var val = ToObject(args.First()); if (prop.PropertyType == typeof(int)) { val = (int)(double)val; } prop.SetValue(@this.ExternalObject, val); return eng.UndefinedValue; } catch (Exception ex) { eng.SetException(FromObject(ex)); return eng.UndefinedValue; } }, owningTypeName + "." + prop.Name + ".set"); } var descriptor = engine.CreateObject(); if (jsGet != null) descriptor.SetPropertyByName("get", jsGet); if (jsSet != null) descriptor.SetPropertyByName("set", jsSet); descriptor.SetPropertyByName("enumerable", engine.TrueValue); target.DefineProperty(prop.Name, descriptor); } }
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); } }
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); }
private void ProjectProperties(string owningTypeName, JavaScriptObject target, JavaScriptEngine engine, IEnumerable <PropertyInfo> properties) { foreach (var prop in properties) { if (prop.GetIndexParameters().Length > 0) { throw new NotSupportedException("Index properties not supported for projecting CLR to JavaScript objects."); } JavaScriptFunction jsGet = null, jsSet = null; if (prop.GetMethod != null) { jsGet = engine.CreateFunction((eng, ctor, thisObj, args) => { var @this = thisObj as JavaScriptObject; if (@this == null) { eng.SetException(eng.CreateTypeError("Could not retrieve property '" + prop.Name + "' because there was an invalid 'this' context.")); return(eng.UndefinedValue); } object external = null; if (!prop.GetMethod.IsStatic) { external = @this.ExternalObject; if (external == null) { external = @this.Prototype.ExternalObject; } if (external == null) { throw new Exception("this object not found for method invocation. Perhaps it has been garbage collected."); } } try { return(FromObject(prop.GetValue(external))); } catch (Exception ex) { eng.SetException(FromObject(ex), ex); return(eng.UndefinedValue); } }, owningTypeName + "." + prop.Name + ".get"); } if (prop.SetMethod != null) { jsSet = engine.CreateFunction((eng, ctor, thisObj, args) => { var @this = thisObj as JavaScriptObject; if (@this == null) { eng.SetException(eng.CreateTypeError("Could not retrieve property '" + prop.Name + "' because there was an invalid 'this' context.")); return(eng.UndefinedValue); } try { var val = ToObject(args.First()); if (prop.PropertyType == typeof(int)) { val = (int)(double)val; } var obj = @this.ExternalObject; if (obj == null && @this.Prototype.ExternalObject == engine.GlobalObject.Prototype.ExternalObject) { obj = @this.Prototype.ExternalObject; } prop.SetValue(obj, val); return(eng.UndefinedValue); } catch (Exception ex) { eng.SetException(FromObject(ex), ex); return(eng.UndefinedValue); } }, owningTypeName + "." + prop.Name + ".set"); } var descriptor = engine.CreateObject(); if (jsGet != null) { descriptor.SetPropertyByName("get", jsGet); } if (jsSet != null) { descriptor.SetPropertyByName("set", jsSet); } descriptor.SetPropertyByName("enumerable", engine.TrueValue); target.DefineProperty(prop.Name, descriptor); } }