Пример #1
0
        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);
        }
Пример #2
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(),
            });
        }