public JurassicJsEngine(object global) { _typeConverter = new ClrTypeConverter(new ScriptEngine(), () => _global); if (global != null) { _global = global; Engine.Global.DefineProperties(_typeConverter, global.GetType(), global); foreach (var method in _global.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)) { var ctor = method.GetCustomAttribute<JsCtorAttribute>(); if(ctor == null) continue; var paramConverter = ClrTypeConverter.GetParamsConverter(_typeConverter, method.GetParameters()); var prototype = _typeConverter.GetPrototype(method.ReturnType); var jsCtor = new ClrCtor(_typeConverter, method.ReturnType, prototype, ctor.Name, args => { var clrArgs = paramConverter(Engine.Global, args); return method.Invoke(global, clrArgs); }); _typeConverter.Engine.SetGlobalValue(ctor.Name, jsCtor); } } }
public override object CallLateBound(object thisObject, params object[] argumentValues) { var clrObject = thisObject is Undefined ? _owner : _ctx.ConvertToClr(thisObject); var methodIndex = _methods.IndexOf(x => x.GetParameters().IsAppropriate(argumentValues)); if (methodIndex < 0) { return(null); //or throw exception; } var method = _methods[methodIndex]; var converter = _paramConverters[methodIndex] ?? (_paramConverters[methodIndex] = ClrTypeConverter.GetParamsConverter(_ctx, method.GetParameters())); var clrArguments = converter(thisObject, argumentValues); try { var clrResult = method.Invoke(clrObject, clrArguments); return(_ctx.ConvertToJs(clrResult)); } catch (Exception e) { throw new JavaScriptException(_ctx.Engine, ErrorType.Error, e.Message, e); } }
public ClrPrototype(ClrTypeConverter ctx, Type type) : base(ctx.Engine, ctx.GetPrototype(type.BaseType)) { var name = type.GetCustomAttribute<JsNameAttribute>()?.Name ?? type.Name; this[ctx.Engine.Symbol.ToStringTag] = name; this.DefineProperties(ctx, type); Name = name; }
public static bool IsAppropriate(this ParameterInfo[] pars, object[] argumentValues) { var isParamArray = pars.Length > 0 && pars.Last().GetCustomAttribute <ParamArrayAttribute>() != null; var checkLength = isParamArray ? pars.Length - 1 : pars.Length; for (var idx = 0; idx < checkLength; idx++) { var par = pars[idx]; if (idx >= argumentValues.Length && !par.HasDefaultValue) { return(false); } if (!ClrTypeConverter.CanConvert(argumentValues[idx], par.ParameterType)) { return(false); } } if (isParamArray && argumentValues.Length > checkLength) { var elementType = pars.Last().ParameterType.GetElementType(); return(argumentValues.Skip(checkLength).All(x => ClrTypeConverter.CanConvert(x, elementType))); } return(true); }
public ClrCtor(ClrTypeConverter ctx, Type type, ObjectInstance prototype, string name, Func<object[], object> creator) : base(ctx.Engine.Function.InstancePrototype, name, prototype) { _ctx = ctx; _creator = creator; this.DefineStaticProperties(ctx, type); }
public object CallMethod(ClrTypeConverter ctx, MethodInfo method, object thisObject, object[] argumentValues, object clrObject) { if (thisObject is ClrPrototype proto) { if (method.Name == "ToString") { return($"[object {proto.Name}]"); } throw new JavaScriptException(ctx.Engine, ErrorType.Error, "Illegal method invocation."); } var clrArguments = _paramsConverter(thisObject, argumentValues); try { var clrResult = method.Invoke(clrObject, clrArguments); return(ctx.ConvertToJs(clrResult)); } catch (Exception e) { throw new JavaScriptException(ctx.Engine, ErrorType.Error, e.Message, e); } }
public ClrOverloadMethodInstance(ClrTypeConverter ctx, object owner, MethodInfo[] methods, string name) : base(ctx.Engine) { _ctx = ctx; _owner = owner; _methods = methods.OrderByDescending(x => x.GetParameters().Length).ToArray(); _paramConverters = new Func <object, object[], object[]> [methods.Length]; SetPropertyValue("name", name, false); }
public ClrMethodInstance(ClrTypeConverter ctx, object owner, MethodInfo method, string name) : base(ctx.Engine) { _ctx = ctx; _owner = owner; _method = method; _paramsConverter = ClrTypeConverter.GetParamsConverter(_ctx, _method.GetParameters()); SetPropertyValue("name", name, false); }
public static Func <object, object[], object[]> GetParamsConverter(ClrTypeConverter ctx, ParameterInfo[] methodParameters) { if (methodParameters.Length > 0) { var lastParameter = methodParameters.Last(); //have a deal with 'param' if (lastParameter.GetCustomAttribute <ParamArrayAttribute>() != null) { return((thisObject, argumentValues) => { if (argumentValues.Length < methodParameters.Length) { return ctx.ConvertParametersToClr(methodParameters, thisObject, argumentValues); } var part1Types = methodParameters.Take(methodParameters.Length - 1).ToArray(); var clrArgumentsPart1 = ctx.ConvertParametersToClr(part1Types, thisObject, argumentValues); var part2Types = Enumerable .Repeat(typeof(object), argumentValues.Length - part1Types.Length) .ToArray(); var clrArgumentsPart2 = ctx.ConvertParametersToClr(part2Types, thisObject, argumentValues.Skip(part1Types.Length).ToArray()); var clrArguments = new object[methodParameters.Length]; Array.Copy(clrArgumentsPart1, clrArguments, clrArgumentsPart1.Length); clrArguments[clrArguments.Length - 1] = clrArgumentsPart2; return clrArguments; }); } var expands = methodParameters .Select(parameterInfo => parameterInfo.GetCustomAttribute <JsExpandArrayAttribute>() != null) .ToArray(); var hasExpands = expands.Any(x => x); if (hasExpands || methodParameters.Any(x => !x.ParameterType.IsValueType)) { return((thisObject, argumentValues) => ctx.ConvertParametersToClr(methodParameters, thisObject, argumentValues)); } //Simple case with value parameters. var converters = methodParameters.Select(x => ctx.GetConverter(x.ParameterType, x.GetDefaultValue())); return((thisObject, args) => converters.Select((c, i) => c(i < args.Length ? args[i]: null)) .ToArray()); } return((_, __) => new object[0]); }
public static void DefineStaticProperties(this ObjectInstance jsObject, ClrTypeConverter ctx, Type type) { foreach (var staticField in type.GetFields(BindingFlags.Public | BindingFlags.Static) .Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null)) { var clrValue = staticField.GetValue(null); var prop = new PropertyDescriptor(ctx.ConvertToJs(clrValue), PropertyAttributes.Sealed); var name = staticField.GetCustomAttribute <JsNameAttribute>()?.Name ?? staticField.Name; jsObject.DefineProperty(name, prop, false); } }
public static object[] ConvertParametersToClr(this ClrTypeConverter ctx, ParameterInfo[] methodParameters, object thisObject, object[] argumentValues) { var clrArguments = new object[methodParameters.Length]; for (var idx = 0; idx < clrArguments.Length; idx++) { var par = methodParameters[idx]; if (idx < argumentValues.Length) { //todo: optimize var expand = par.GetCustomAttribute <JsExpandArrayAttribute>() != null; var jsArgument = argumentValues[idx]; if (!expand && (par.ParameterType.IsValueType)) { clrArguments[idx] = ctx.GetConverter(par.ParameterType, par.GetDefaultValue())(jsArgument); } else { clrArguments[idx] = ctx.ConvertToClr( jsArgument, par.ParameterType, thisObject, expand); } } else { if (par.GetCustomAttribute <ParamArrayAttribute>() != null) { clrArguments[idx] = Activator.CreateInstance(par.ParameterType, 0); } else if (par.HasDefaultValue) { clrArguments[idx] = par.DefaultValue; } } } return(clrArguments); }
public static object[] ConvertParametersToClr(this ClrTypeConverter ctx, Type[] methodParameters, object thisObject, object[] argumentValues) { var clrArguments = new object[methodParameters.Length]; for (var idx = 0; idx < clrArguments.Length; idx++) { if (idx < argumentValues.Length) { var jsArgument = argumentValues[idx]; clrArguments[idx] = ctx.ConvertToClr(jsArgument, methodParameters[idx], thisObject); } } return(clrArguments); }
public ClrObjectInstance(ClrTypeConverter ctx, object obj, ObjectInstance prototype) : base(prototype) { var type = obj.GetType(); this[ctx.Engine.Symbol.ToStringTag] = type.GetCustomAttribute <JsNameAttribute>()?.Name ?? type.Name; _ctx = ctx; Target = obj; if (Target is Array array) { _array = array; } else { _indexProperties = Target.GetType() .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(x => x.GetIndexParameters().Length == 1) .ToArray(); } }
/// <summary> /// Adds properties to the specified js object from the specified clr object. /// </summary> public static void DefineProperties(this ObjectInstance jsObject, ClrTypeConverter ctx, Type type, object clrOwner = null) { var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; //register properties foreach (var property in type.GetProperties(bindingFlags).Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null)) { var name = property.GetName(); //todo: compile delegate instead of reflection, research what is faster. var jsGetter = property.GetMethod != null && property.GetMethod.IsPublic ? new FuncInst(ctx, clrThis => property.GetValue(clrThis)) : null; var jsSetter = property.SetMethod != null && property.SetMethod.IsPublic ? new ActInst(ctx, (clrThis, objects) => property.SetValue(clrThis, ctx.ConvertToClr(objects[0], property.PropertyType, ctx.ConvertToJs(clrThis)))) : null; var jsProperty = new PropertyDescriptor(jsGetter, jsSetter, PropertyAttributes.Configurable); jsObject.DefineProperty(name, jsProperty, false); } //register methods foreach (var methods in type .GetMethods(bindingFlags) .Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null && p.GetCustomAttribute <JsCtorAttribute>() == null) .GroupBy(p => p.GetName())) { var methodsArray = methods.ToArray(); var firstMethod = methodsArray[0]; if (firstMethod.IsSpecialName) { continue; } if (firstMethod.DeclaringType == typeof(object)) { continue; } var name = methods.Key; var jsMethod = methodsArray.Length == 1 ? new ClrMethodInstance(ctx, clrOwner, firstMethod, name) : (FunctionInstance) new ClrOverloadMethodInstance(ctx, clrOwner, methodsArray, name); //todo: check the PropertyAttributes var jsProperty = new PropertyDescriptor(jsMethod, PropertyAttributes.Sealed); jsObject.DefineProperty(name, jsProperty, false); } //register events foreach (var evt in type.GetEvents(bindingFlags) .Where(p => p.GetCustomAttribute <JsHiddenAttribute>() == null)) { var name = evt.GetName().ToLowerInvariant(); var jsGetter = new FuncInst(ctx, clrThis => { var attachedEvents = ctx.GetAttachedEventsFor(clrThis); return(attachedEvents[evt]); }, false); var jsSetter = new ActInst(ctx, (clrThis, args) => { var jsThis = ctx.ConvertToJs(clrThis); var attachedEvents = ctx.GetAttachedEventsFor(clrThis); if (attachedEvents.TryGetValue(evt, out var existHandler)) { var clrHandler = ctx.ConvertToClr(existHandler, evt.EventHandlerType, jsThis); evt.RemoveMethod.Invoke(clrThis, new [] { clrHandler }); attachedEvents.Remove(evt); } if (args.Length != 0 && args[0] is FunctionInstance functionInstance) { var clrHandler = ctx.ConvertToClr(args[0], evt.EventHandlerType, jsThis); evt.AddMethod.Invoke(clrThis, new[] { clrHandler }); attachedEvents[evt] = functionInstance; } }); var jsProperty = new PropertyDescriptor(jsGetter, jsSetter, PropertyAttributes.Configurable); jsObject.DefineProperty(name, jsProperty, false); } //Register static fields DefineStaticProperties(jsObject, ctx, type); }
public ActInst(ClrTypeConverter ctx, Action <object, object[]> act) : base(ctx.Engine) { _ctx = ctx; _act = act; }
public FuncInst(ClrTypeConverter ctx, Func <object, object> func, bool doConvert = true) : base(ctx.Engine) { _ctx = ctx; _func = func; _doConvert = doConvert; }