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 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 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); } }