public NullableInteropOps(Runtime outer, Type type, TypeInfo typeInfo) : base(outer, type, typeInfo) { var elemType = TypeInfo.ExplodeNullableType(type); if (elemType == null) throw new InvalidOperationException("not a nullable type"); elemInteropOps = outer.FindInteropOps(elemType); }
// ---------------------------------------------------------------------- // Imported and Exported methods // ---------------------------------------------------------------------- // Called from body of imported method to redirect to imported method. Method is (possibly an instance // of a polymorphic method) in (possibly an instance of a higher kinded) type. // If method is a constructor: // - the uninitialized managed object has already been created, but is NOT passed as first // argument in args, and is not represented in argTypes. // - function on unmanaged side does not expect any 'this' argument, but will instead create, initialize // and return the new unmanaged object. // - we return a JSContext for the newly created unamanged object // - the calling ctor must then call the best-matching 'importing constructor' to initialize the // managed object, then call CompleteConstruction to associate the managed and unmanaged objects. public object CallImportedMethod(SimpleMethodBase methodBase, string script, params object[] args) { var resType = default(Type); var argTypes = TypeInfo.ExplodeSimpleMethodBase(methodBase, out resType); var argInteropOps = new InteropOps[argTypes.Count]; for (var i = 0; i < argTypes.Count; i++) argInteropOps[i] = FindInteropOps(argTypes[i]); var resInteropOps = resType == null ? null : FindInteropOps(resType); if (args.Length != argInteropOps.Length) throw new InvalidOperationException("mismatched method arity"); // First pass: check if we need to create any Proxied or Keyed counterparts for arguments, and/or // bind any exported instance methods into unmanaged counterpart. for (var i = 0; i < args.Length; i++) { if (argInteropOps[i].NeedsCreate(args[i])) Eval(argInteropOps[i].AppendCreate, sp => argInteropOps[i].BindCreatedInstance(sp, args[i])); if (argInteropOps[i].NeedsInstanceExportsBound(args[i])) BindExportedMethodsOfType(args[i].GetType(), args[i]); } // Second pass: export args, make call, import result Action<StringBuilder, Dictionary<Type, int>> makeCall = (sb, toBeLoaded) => { sb.Append('('); sb.Append(script); sb.Append(")("); for (var i = 0; i < args.Length; i++) { if (i > 0) sb.Append(','); argInteropOps[i].AppendExport (sb, toBeLoaded, args[i]); } sb.Append(')'); if (resType == null) sb.Append(';'); }; if (resType == null) return Eval(makeCall, null); else return Eval (resInteropOps.WrapImport(makeCall), sp => { sp.SkipWS(); if (methodBase is SimpleConstructorInfo) return resInteropOps.ContextForUnmanagedInstance(sp); else return resInteropOps.Import(sp); }); }
public void BindExportedMethod(MethodBase methodBase, object obj, string script) { if (obj != null) { // Method is a monomorphic instance method of a (possibly higher-kinded) type. Use the type of // the instance to recover the type at which the higher-kinded type is instantiated, and find // the fully instantiated method base to invoke. Remember, the instance may be a subtype of the // method's declaring type. var fkObjType = obj.GetType(); var hkMethodType = methodBase.DeclaringType; while (true) { var classTypeArguments = fkObjType.GetGenericArguments(); var hkObjType = fkObjType; if (classTypeArguments.Length > 0) hkObjType = fkObjType.GetGenericTypeDefinition(); if (hkObjType.Equals(hkMethodType)) { if (classTypeArguments.Length != hkMethodType.GetGenericArguments().Length) throw new InvalidOperationException("mismatched type arities"); methodBase = TypeInfo.FindMethodBase(hkMethodType, classTypeArguments, methodBase); break; } fkObjType = fkObjType.BaseType; if (fkObjType == null) throw new InvalidOperationException ("object type is not a subtype of method's declaring type"); } } var id = default(int); if (!exportedMethodBaseToId.TryGetValue(methodBase, out id)) { id = nextObjectId++; exportedMethodBaseToId.Add(methodBase, id); idToExportedMethodBase.Add(id, methodBase); } var resType = default(Type); var argTypes = TypeInfo.ExplodeMethodBase(methodBase, out resType); var argInteropOps = new InteropOps[argTypes.Count]; for (var i = 0; i < argTypes.Count; i++) argInteropOps[i] = FindInteropOps(argTypes[i]); // First pass: check if instance object needs to have a Keyed or Proxied counterpart created if (obj != null) { if (methodBase.IsStatic || methodBase is ConstructorInfo) throw new InvalidOperationException("no instance expected for static methods and constructors"); if (argInteropOps[0].NeedsCreate(obj)) Eval(argInteropOps[0].AppendCreate, sp => argInteropOps[0].BindCreatedInstance(sp, obj)); } Eval ((sb, toBeLoaded) => { sb.Append('('); sb.Append(script); sb.Append(")("); if (obj != null) { argInteropOps[0].AppendExport(sb, toBeLoaded, obj); sb.Append(','); } sb.Append(database.RootExpression); sb.Append(".MakeExportRedirector(["); // If method is a constructor: // - function on unmanaged side will be called without the first 'this' argument, // - CallManaged (above) must create the object itself, call the constructor, and return // the object // - function on unmanaged side should return constructed object for (var i = 0; i < argTypes.Count; i++) { if (i > 0) sb.Append(','); sb.Append(TypeIndex(toBeLoaded, argTypes[i])); } sb.Append("],"); sb.Append(id); sb.Append(",false,false));"); }, null); }
public DelegateInteropOps(Runtime outer, Type type, TypeInfo typeInfo) : base(outer, type, typeInfo) { var resType = default(Type); var argTypes = TypeInfo.ExplodeDelegateType(type, out resType); if (argTypes == null) throw new InvalidOperationException("not a delegate type"); argInteropOps = new InteropOps[argTypes.Count]; for (var i = 0; i < argTypes.Count; i++) argInteropOps[i] = outer.FindInteropOps(argTypes[i]); resInteropOps = resType == null ? null : outer.FindInteropOps(resType); captureThis = typeInfo.CaptureThis; inlineParamsArray = typeInfo.InlineParamsArray; }