public bool TryCreatePrototypeFunction(BaristaContext context, Type typeToConvert, out JsFunction ctor) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (typeToConvert == null) { throw new ArgumentNullException(nameof(typeToConvert)); } if (m_prototypes.ContainsKey(typeToConvert)) { ctor = m_prototypes[typeToConvert]; return(true); } var reflector = new ObjectReflector(typeToConvert); JsFunction superCtor = null; var baseType = reflector.GetBaseType(); if (baseType != null && !baseType.IsSameOrSubclass(typeof(JsValue)) && TryCreatePrototypeFunction(context, baseType, out JsFunction fnSuper)) { superCtor = fnSuper; } var objectName = BaristaObjectAttribute.GetBaristaObjectNameFromType(typeToConvert); //Get all the property descriptors for the specified type. var staticPropertyDescriptors = context.CreateObject(); var instancePropertyDescriptors = context.CreateObject(); //Get static and instance properties. ProjectProperties(context, staticPropertyDescriptors, reflector.GetProperties(false)); ProjectProperties(context, instancePropertyDescriptors, reflector.GetProperties(true)); //Get static and instance indexer properties. ProjectIndexerProperties(context, staticPropertyDescriptors, reflector.GetIndexerProperties(false)); ProjectIndexerProperties(context, instancePropertyDescriptors, reflector.GetIndexerProperties(true)); //Get static and instance methods. ProjectMethods(context, staticPropertyDescriptors, reflector, reflector.GetUniqueMethodsByName(false)); ProjectMethods(context, instancePropertyDescriptors, reflector, reflector.GetUniqueMethodsByName(true)); //Get static and instance events. ProjectEvents(context, staticPropertyDescriptors, reflector, reflector.GetEventTable(false)); ProjectEvents(context, instancePropertyDescriptors, reflector, reflector.GetEventTable(true)); //Get the [[iterator]] property. ProjectIEnumerable(context, instancePropertyDescriptors, reflector); JsFunction fnCtor; var publicConstructors = reflector.GetConstructors(); if (publicConstructors.Any()) { fnCtor = context.CreateFunction(new BaristaFunctionDelegate((calleeObj, isConstructCall, thisObj, args) => { if (thisObj == null) { var ex = context.CreateTypeError($"Failed to construct '{objectName}': 'this' must be specified."); context.CurrentScope.SetException(ex); return(context.Undefined); } if (superCtor != null) { superCtor.Call(thisObj); } context.Object.DefineProperties(thisObj, instancePropertyDescriptors); //If this isn't a construct call, don't attempt to set the bean if (!isConstructCall) { return(thisObj); } //Set our native object. JsExternalObject externalObject = null; //!!Special condition -- if there's exactly one argument, and if it matches the enclosing type, //don't invoke the type's constructor, rather, just wrap the object with the JsObject. if (args.Length == 1 && args[0].GetType() == typeToConvert) { externalObject = context.CreateExternalObject(args[0]); } else { try { var bestConstructor = reflector.GetConstructorBestMatch(args); if (bestConstructor == null) { var ex = context.CreateTypeError($"Failed to construct '{objectName}': Could not find a matching constructor for the provided arguments."); context.CurrentScope.SetException(ex); return(context.Undefined); } //Convert the args into the native args of the constructor. var constructorParams = bestConstructor.GetParameters(); var convertedArgs = ConvertArgsToParamTypes(context, args, constructorParams); var newObj = bestConstructor.Invoke(convertedArgs); externalObject = context.CreateExternalObject(newObj); } catch (Exception ex) { context.CurrentScope.SetException(context.CreateError(ex.Message)); return(context.Undefined); } } thisObj.SetBean(externalObject); return(thisObj); }), objectName); } else { fnCtor = context.CreateFunction(new BaristaFunctionDelegate((calleeObj, isConstructCall, thisObj, args) => { var ex = context.CreateTypeError($"Failed to construct '{objectName}': This object cannot be constructed."); context.CurrentScope.SetException(ex); return(context.Undefined); }), objectName); } //We've got everything we need. fnCtor.Prototype = context.Object.Create(superCtor == null ? context.Object.Prototype : superCtor.Prototype); context.Object.DefineProperties(fnCtor, staticPropertyDescriptors); m_prototypes.Add(typeToConvert, fnCtor); ctor = fnCtor; return(true); }
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(), }); }