protected override void WriteEvent( Method eventMethod, string declaringTypeFullName, string boundTypeFullName, bool isInterfaceMember, CodeWriter code) { if (!isInterfaceMember && !eventMethod.IsAbstract) { string listenerTypeFullName = eventMethod.Parameters[0].ParameterType; Type listenerType; if (!this.listenerMap.TryGetValue(listenerTypeFullName, out listenerType)) { throw new InvalidOperationException( "Listener delegate type not found: " + listenerTypeFullName); } string boundEventTypeFullName = this.GetBoundEventTypeForListenerType(listenerType); string eventTypeName = GetTypeName(boundEventTypeFullName, declaringTypeFullName); string eventName = eventMethod.Name.Substring( "Add".Length, eventMethod.Name.Length - "AddListener".Length); string listenerMapType = "System.Collections.Generic.Dictionary<" + $"System.EventHandler<{eventTypeName}>, {listenerTypeFullName}>"; code.Code($"{(eventMethod.IsStatic ? "static " : "")}{listenerMapType} __{eventName}ListenerMap ="); code.Code($"\tnew {listenerMapType}();"); code.Code(); } base.WriteEvent(eventMethod, declaringTypeFullName, boundTypeFullName, isInterfaceMember, code); }
protected override void WriteEventAdder( string eventName, Member eventMember, string declaringTypeFullName, string forward, CodeWriter code) { Method eventMethod = eventMember as Method; Event eventEvent = eventMember as Event; bool isStatic = (eventMethod != null && eventMethod.IsStatic) || (eventEvent != null && eventEvent.IsStatic); string listenerTypeFullName = (eventMethod != null ? eventMethod.Parameters[0].ParameterType : eventEvent != null ? eventEvent.EventHandlerType : "???"); string sender = isStatic ? "typeof(" + GetTypeName(declaringTypeFullName, declaringTypeFullName) + ")" : "this"; code.Code("add"); code.Code("{"); code.Code($"\tif (__{eventName}ListenerMap.ContainsKey(value)) return;"); code.Code($"\t{listenerTypeFullName} listener = (sender, e) => {{"); // TODO: IOS event listeners currently fail to get removed at the native code layer, // because the Xamarin binding layer creates unique blocks. // This check for presence in the map is a workaround until a better solution is found. code.Code($"\t\tif (__{eventName}ListenerMap.ContainsKey(value)) value({sender}, e);"); code.Code("\t};"); code.Code($"\t{forward}.Add{eventName}Listener(listener);"); code.Code($"\t__{eventName}ListenerMap[value] = listener;"); code.Code("}"); }
void WriteTypeScriptBindingForConstructor( Type declaringType, Constructor constructor, CodeWriter code) { string parameters = String.Join(", ", constructor.Parameters.Select( p => p.Name + ": " + this.GetJavaScriptTypeName(p.ParameterType, declaringType))); code.Code($"constructor({parameters});"); }
protected override void WritePropertySetter( Property property, string mappedPropertyType, string forward, CodeWriter code) { // The Objective Sharpie binder /sometimes/ binds static property setters as normal methods. if (!property.SetMethod.Name.StartsWith("set_")) { code.Code("set"); code.Code("{"); code.Code($"\t{forward}.{property.SetMethod.Name}(" + this.CastArgument("value", mappedPropertyType, property.PropertyType) + ");"); code.Code("}"); } else { base.WritePropertySetter(property, mappedPropertyType, forward, code); } }
protected override void WriteEventRemover( string eventName, Member eventMember, string declaringTypeFullName, string forward, CodeWriter code) { Event eventEvent = (Event)eventMember; string boundEventTypeFullName = this.GetBoundEventTypeForListenerType(eventEvent.EventHandlerType); code.Code("remove"); code.Code("{"); code.Code($"\tSystem.EventHandler<{this.PluginAlias}{boundEventTypeFullName}> handler;"); code.Code($"\tif (__{eventName}ListenerMap.TryGetValue(value, out handler))"); code.Code("\t{"); code.Code($"\t\t{forward}.{eventName} -= handler;"); code.Code($"\t\t__{eventName}ListenerMap.Remove(value);"); code.Code("\t}"); code.Code("}"); }
void WriteEventListener( string eventName, string boundEventTypeFullName, string eventTypeName, Type listenerType, CodeWriter code) { string listenerMethodName = listenerType.Members.Cast <Method>().Single().Name; code.Code($"class __{eventName}Listener : global::Java.Lang.Object, {listenerType.FullName}"); code.Code("{"); code.Code("\tpublic object Sender { get; set; }"); code.Code($"\tpublic System.EventHandler<{eventTypeName}> Handler {{ get; set; }}"); code.Code($"\tpublic void {listenerMethodName}({boundEventTypeFullName} e)"); code.Code("\t{"); code.Code("\t\tHandler(Sender, e);"); code.Code("\t}"); code.Code("}"); code.Code(); }
protected override void WriteOutErrorConstructor( Constructor constructor, string declaringTypeFullName, string boundTypeFullName, CodeWriter code) { string context = this.GetImplicitContext(constructor, code); int skip = context != null ? 1 : 0; string parameters = String.Join(", ", constructor.Parameters.Skip(skip) .Take(constructor.Parameters.Count - 1 - skip).Select( (p, i) => GetParameterTypeName(p.ParameterType, declaringTypeFullName, null, i) + " " + p.Name)); string visibility = declaringTypeFullName.EndsWith("Event") ? "internal" : "public"; code.Code($"{visibility} {GetNameFromTypeFullName(declaringTypeFullName)}({parameters})"); code.Code("{"); code.Code("\tFoundation.NSError error;"); if (context != null && constructor.Parameters.Count > 1) { context += ", "; } string arguments = (context != null ? context : String.Empty) + String.Join(", ", constructor.Parameters.Skip(skip) .Take(constructor.Parameters.Count - 1 - skip).Select( (p, i) => this.CastArgument( p.Name, this.GetParameterTypeName(p.ParameterType, declaringTypeFullName, null, i), p.ParameterType))) + (constructor.Parameters.Count > 1 + skip ? ", " : "") + "out error"; code.Code($"\tforward = new {boundTypeFullName}({arguments});"); code.Code("\tif (error != null) throw new Foundation.NSErrorException(error);"); code.Code("}"); }
void WriteTypeScriptBindingForField(Type declaringType, Member fieldOrProperty, CodeWriter code) { EnumValue enumValue = fieldOrProperty as EnumValue; Field field = fieldOrProperty as Field; Property property = fieldOrProperty as Property; if (enumValue != null) { code.Code($"{Uncapitalize(enumValue.Name)} = {enumValue.Value},"); } else if (field != null) { string typeName = this.GetJavaScriptTypeName(field.FieldType, declaringType); code.Code($"{Uncapitalize(field.Name)}: {typeName};"); } else if (property != null) { string typeName = this.GetJavaScriptTypeName(property.PropertyType, declaringType); string modifier = property.SetMethod == null ? "readonly " : ""; code.Code($"{modifier}{Uncapitalize(property.Name)}: {typeName};"); } }
protected override void WriteEvent( Event eventMember, string declaringTypeFullName, string boundTypeFullName, bool isInterfaceMember, CodeWriter code) { if (!isInterfaceMember) { string boundEventTypeFullName = this.GetBoundEventTypeForListenerType(eventMember.EventHandlerType); string eventTypeName = GetTypeName(boundEventTypeFullName, declaringTypeFullName); string listenerMapType = "System.Collections.Generic.Dictionary<" + $"System.EventHandler<{eventTypeName}>, " + $"System.EventHandler<{this.PluginAlias}{boundEventTypeFullName}>>"; code.Code($"{(eventMember.IsStatic ? "static " : "")}" + $"{listenerMapType} __{eventMember.Name}ListenerMap ="); code.Code($"\tnew {listenerMapType}();"); code.Code(); } base.WriteEvent(eventMember, declaringTypeFullName, boundTypeFullName, isInterfaceMember, code); }
protected override void WriteEventRemover( string eventName, Member eventMember, string declaringTypeFullName, string forward, CodeWriter code) { Method eventMethod = eventMember as Method; Event eventEvent = eventMember as Event; string listenerTypeFullName = (eventMethod != null ? eventMethod.Parameters[0].ParameterType : eventEvent != null ? eventEvent.EventHandlerType : "???"); code.Code("remove"); code.Code("{"); code.Code($"\t{listenerTypeFullName} listener;"); code.Code($"\tif (__{eventName}ListenerMap.TryGetValue(value, out listener))"); code.Code("\t{"); code.Code($"\t\t{forward}.Remove{eventName}Listener(listener);"); code.Code($"\t\t__{eventName}ListenerMap.Remove(value);"); code.Code("\t}"); code.Code("}"); }
protected override void WriteEventAdder( string eventName, Member eventMember, string declaringTypeFullName, string forward, CodeWriter code) { Event eventEvent = (Event)eventMember; string sender = eventEvent.IsStatic ? "typeof(" + declaringTypeFullName + ")" : "this"; string boundEventTypeFullName = this.GetBoundEventTypeForListenerType(eventEvent.EventHandlerType); code.Code("add"); code.Code("{"); code.Code($"\tif (__{eventName}ListenerMap.ContainsKey(value)) return;"); code.Code($"\tSystem.EventHandler<{this.PluginAlias}{boundEventTypeFullName}> handler = "); code.Code($"\t\t(sender, e) => value({sender}, e);"); code.Code($"\t{forward}.{eventName} += handler;"); code.Code($"\t__{eventName}ListenerMap[value] = handler;"); code.Code("}"); }
protected override string GetImplicitContext(Member member, CodeWriter code) { Constructor constructor = member as Constructor; Method method = member as Method; if ((constructor != null && constructor.Parameters.Count > 0 && constructor.Parameters[0].ParameterType == "UIKit.UIApplication") || (method != null && method.Parameters.Count > 0 && method.Parameters[0].ParameterType == "UIKit.UIApplication")) { code.Code($"[{typeof(ImplicitContextAttribute).FullName}(\"{ImplicitContextAttribute.Application}\")]"); return(typeof(IOSApiAdapter).Namespace + ".PluginContext.Application"); } else if ((constructor != null && constructor.Parameters.Count > 0 && constructor.Parameters[0].ParameterType == "UIKit.UIWindow") || (method != null && method.Parameters.Count > 0 && method.Parameters[0].ParameterType == "UIKit.UIWindow")) { code.Code($"[{typeof(ImplicitContextAttribute).FullName}(\"{ImplicitContextAttribute.Window}\")]"); return(typeof(IOSApiAdapter).Namespace + ".PluginContext.CurrentWindow"); } return(null); }
protected override void WriteInnerTypes(Class outerClass, string outerClassFullName, CodeWriter code) { base.WriteInnerTypes(outerClass, outerClassFullName, code); List <Type> inferredInnerTypes; if (this.innerTypeMap.TryGetValue(outerClass.FullName, out inferredInnerTypes)) { foreach (Type innerType in inferredInnerTypes) { string innerName = innerType.Name.Substring(innerType.Name.LastIndexOf('_') + 1); code.Code(); this.WriteType(innerType, outerClassFullName + "+" + innerName, code); } } }
void WriteTypeScriptBindingsForProperty(Type declaringType, Property property, CodeWriter code) { CodeWriter body = code.Indent(); string propertyName = property.Name; if (propertyName.StartsWith("Is")) { propertyName = propertyName.Substring(2); } bool isInterfaceMember = declaringType is Interface; string simpleName = declaringType.Name.Substring(declaringType.Name.IndexOf('+') + 1); if (property.GetMethod != null) { string propertyDeclaration; string returnType = this.GetJavaScriptTypeName(property.PropertyType, declaringType); if (this.ForceAsyncAPIs) { propertyDeclaration = (property.Name.StartsWith("Is") ? Uncapitalize(property.Name) : "get" + property.Name) + "Async"; returnType = MakePromiseTypeName(returnType); } else { propertyDeclaration = "get " + Uncapitalize(property.Name); } code.Code((property.IsStatic ? "static " : String.Empty) + $"{propertyDeclaration}(): {returnType}" + (isInterfaceMember ? ";" : "{")); if (!isInterfaceMember) { string retCast = ""; if (property.PropertyType == "System.Boolean") { retCast = ".then(result => !!result)"; } else if (property.PropertyType == "System.Guid") { retCast = ".then(result => (typeof(result) === \"string\" ? " + "result.toUpperCase() : result && result.value))"; } else if (property.PropertyType == "System.Uri") { retCast = ".then(result => (typeof(result) === \"string\" ? " + "result : result && result.value))"; } if (property.IsStatic) { body.Code("return bridge.getStaticProperty(" + $"{simpleName}.type, \"{propertyName}\"){retCast};"); } else { body.Code($"return bridge.getProperty(this, \"{propertyName}\"){retCast};"); } code.Code("}"); } } if (property.SetMethod != null) { string propertyDeclaration; string returnType; string returnStatement; if (this.ForceAsyncAPIs) { propertyDeclaration = "set" + propertyName + "Async"; returnType = ": " + MakePromiseTypeName("void"); returnStatement = "return "; } else { propertyDeclaration = "set " + Uncapitalize(property.Name); returnType = String.Empty; returnStatement = String.Empty; } string typeName = this.GetJavaScriptTypeName(property.PropertyType, declaringType); code.Code((property.IsStatic ? "static " : String.Empty) + $"{propertyDeclaration}(value: {typeName}){returnType}" + (isInterfaceMember ? ";" : "{")); if (!isInterfaceMember) { string value = "value"; if (property.PropertyType == "System.Guid") { value = "(typeof(value) === \"string\" ? { \"type\": \"<uuid>\", \"value\": value } : null)"; } else if (property.PropertyType == "System.Uri") { value = "(typeof(value) === \"string\" ? { \"type\": \"<uri>\", \"value\": value } : null)"; } if (property.IsStatic) { body.Code($"{returnStatement}bridge.setStaticProperty(" + $"{simpleName}.type, \"{propertyName}\", {value});"); } else { body.Code($"{ returnStatement}bridge.setProperty(this, \"{propertyName}\", {value});"); } code.Code("}"); } } }
protected override void WriteAsyncMethod( Method method, string declaringTypeFullName, string boundTypeFullName, bool isInterfaceMember, CodeWriter code) { string methodName = method.Name; if ((method.Parameters.Count == 1 || method.Parameters.Count == 2) && method.Parameters.All(p => p.ParameterType.StartsWith("System.Action"))) { if (methodName.EndsWith("AndThen")) { methodName = methodName.Substring(0, methodName.Length - "AndThen".Length); } else if (methodName.EndsWith("Then")) { methodName = methodName.Substring(0, methodName.Length - "Then".Length); } else if (methodName.EndsWith("WithResult")) { methodName = methodName.Substring(0, methodName.Length - "WithResult".Length); } else if (methodName.EndsWith("Result")) { methodName = methodName.Substring(0, methodName.Length - "Result".Length); } } string context = this.GetImplicitContext(method, code); int skip = context != null ? 1 : 0; bool hasCatchCallback = false; int callbackParameterIndex = method.Parameters.Count - 1; if (method.Parameters[callbackParameterIndex].ParameterType == "System.Action<Foundation.NSError>") { --callbackParameterIndex; hasCatchCallback = true; } if (callbackParameterIndex < 0 || !method.Parameters[callbackParameterIndex].ParameterType.StartsWith("System.Action")) { throw new InvalidOperationException("Invalid async method signature: " + method); } bool hasReturnValue = false; string returnValueType = "void"; string returnType = "System.Threading.Tasks.Task"; string thenCallbackType = method.Parameters[callbackParameterIndex].ParameterType; string callbackParameterType = null; int ltIndex = thenCallbackType.IndexOf('<'); if (ltIndex > 0) { hasReturnValue = true; callbackParameterType = thenCallbackType.Substring(ltIndex + 1, thenCallbackType.Length - ltIndex - 2); returnValueType = GetMemberTypeName(callbackParameterType, declaringTypeFullName, method.Name); returnType += "<" + returnValueType + ">"; } bool isAbstract = isInterfaceMember || method.IsAbstract; int parameterCount = method.Parameters.Count - (hasCatchCallback ? 2 : 1) - skip; string parameters = String.Join(", ", method.Parameters.Skip(skip).Take(parameterCount).Select( (p, i) => GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i) + " " + p.Name)); code.Code($"{(isInterfaceMember ? "" : "public ")}{(method.IsStatic ? "static " : "")}" + $"{(isAbstract && !isInterfaceMember ? "abstract" : "async")} {returnType} " + $"{methodName}({parameters}){(isAbstract ? ";" : "")}"); if (!isAbstract) { code.Code("{"); if (hasReturnValue) { code.Code($"\tvar asyncResult = default({returnValueType});"); } code.Code("\tvar completion = new System.Threading.Tasks.TaskCompletionSource<bool>();"); code.Code($"\t{thenCallbackType} success = ({(hasReturnValue ? "result" : "")}) =>"); if (hasReturnValue) { code.Code("\t{"); code.Code($"\t\tasyncResult = " + this.CastArgument("result", callbackParameterType, returnValueType) + ";"); code.Code("\t\tcompletion.SetResult(true);"); code.Code("\t};"); } else { code.Code("\t\tcompletion.SetResult(true);"); } if (hasCatchCallback) { code.Code("\tSystem.Action<Foundation.NSError> failure = (error) =>"); code.Code("\t\tcompletion.SetException(new Foundation.NSErrorException(error));"); } if (context != null && method.Parameters.Count > 1) { context += ", "; } string arguments = (context != null ? context : String.Empty) + String.Join(", ", method.Parameters.Skip(skip).Select( (p, i) => this.CastArgument( p.Name, this.GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i), p.ParameterType))); string forward = (method.IsStatic ? boundTypeFullName : "Forward"); code.Code($"\t{forward}.{method.Name}({arguments});"); code.Code("\tawait completion.Task;"); if (hasReturnValue) { code.Code("\treturn asyncResult;"); } code.Code("}"); } }
protected override void WriteOutErrorMethod( Method method, string declaringTypeFullName, string boundTypeFullName, bool isInterfaceMember, bool isMarshalByValueType, CodeWriter code) { string methodName = method.Name; if (method.Parameters.Count == 1) { if (methodName.EndsWith("WithError")) { methodName = methodName.Substring(0, methodName.Length - "WithError".Length); } else if (methodName.EndsWith("Error")) { methodName = methodName.Substring(0, methodName.Length - "Error".Length); } } string context = this.GetImplicitContext(method, code); int skip = context != null ? 1 : 0; bool isAbstract = isInterfaceMember || method.IsAbstract; string parameters = String.Join(", ", method.Parameters.Skip(skip) .Take(method.Parameters.Count - 1 - skip).Select( (p, i) => GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i) + " " + p.Name)); code.Code($"{(isInterfaceMember ? "" : "public ")}{(method.IsStatic ? "static " : "")}" + $"{(isAbstract && !isInterfaceMember ? "abstract " : "")}" + $"{GetMemberTypeName(method.ReturnType, declaringTypeFullName, method.Name)} " + $"{methodName}({parameters}){(isAbstract ? ";" : "")}"); if (!isAbstract) { code.Code("{"); code.Code("\tFoundation.NSError error;"); if (isMarshalByValueType && !method.IsStatic) { code.Code($"\t{this.PluginAlias}{boundTypeFullName} Forward = " + $"new {this.PluginAlias}{boundTypeFullName}();"); code.Code("\tthis.CopyValuesTo(Forward);"); } if (context != null && method.Parameters.Count > 1) { context += ", "; } string arguments = (context != null ? context : String.Empty) + String.Join(", ", method.Parameters.Skip(skip) .Take(method.Parameters.Count - 1 - skip).Select( (p, i) => this.CastArgument( p.Name, this.GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i), p.ParameterType))) + (method.Parameters.Count > 1 + skip ? ", " : "") + "out error"; string forward = (method.IsStatic ? boundTypeFullName : "Forward"); if (method.ReturnType == "System.Void") { code.Code($"\t{forward}.{method.Name}({arguments});"); code.Code("\tif (error != null) throw new Foundation.NSErrorException(error);"); if (isMarshalByValueType && !method.IsStatic) { code.Code("\tthis.CopyValuesFrom(Forward);"); } } else { code.Code($"\tvar result = " + this.CastArgument( $"{forward}.{method.Name}({arguments})", method.ReturnType, this.GetMemberTypeName(method.ReturnType, declaringTypeFullName, method.Name)) + ";"); code.Code("\tif (error != null) throw new Foundation.NSErrorException(error);"); if (isMarshalByValueType && !method.IsStatic) { code.Code("\tthis.CopyValuesFrom(Forward);"); } code.Code("\treturn result;"); } code.Code("}"); } }
protected override void WriteAsyncMethod( Method method, string declaringTypeFullName, string boundTypeFullName, bool isInterfaceMember, CodeWriter code) { string context = this.GetImplicitContext(method, code); int skip = context != null ? 1 : 0; string asyncReturnType; string asyncTaskReturnType; if (method.ReturnType.StartsWith("Windows.Foundation.IAsyncAction")) { asyncReturnType = "void"; asyncTaskReturnType = "System.Threading.Tasks.Task"; } else { asyncReturnType = method.ReturnType.Substring(method.ReturnType.IndexOf('<') + 1); asyncReturnType = asyncReturnType.Substring(0, asyncReturnType.Length - 1); asyncTaskReturnType = "System.Threading.Tasks.Task<" + GetMemberTypeName(asyncReturnType, declaringTypeFullName, method.Name) + ">"; } bool isAbstract = isInterfaceMember || method.IsAbstract; string parameters = String.Join(", ", method.Parameters.Skip(skip).Select( (p, i) => GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i) + " " + p.Name)); code.Code($"{(isInterfaceMember ? "" : "public ")}{(method.IsStatic ? "static " : "")}" + $"{(isAbstract && !isInterfaceMember ? "abstract " : "async ")}{asyncTaskReturnType} " + $"{method.Name}({parameters}){(isAbstract ? ";" : "")}"); if (!isAbstract) { code.Code("{"); if (context != null && method.Parameters.Count > 1) { context += ", "; } string arguments = (context != null ? context : String.Empty) + String.Join(", ", method.Parameters.Skip(skip).Select( (p, i) => this.CastArgument( p.Name, this.GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i), p.ParameterType, true))); string forward = (method.IsStatic ? this.PluginAlias + boundTypeFullName : "Forward"); if (asyncReturnType == "void") { code.Code($"\tawait System.WindowsRuntimeSystemExtensions.AsTask(" + $"{forward}.{method.Name}({arguments}));"); } else { code.Code($"\tvar result = await " + "System.WindowsRuntimeSystemExtensions.AsTask(" + $"{forward}.{method.Name}({arguments}));"); code.Code("\treturn " + this.CastArgument( $"result", asyncReturnType, this.GetMemberTypeName(asyncReturnType, declaringTypeFullName, method.Name)) + ";"); } code.Code("}"); } }
void WriteTypeScriptBindingsForType( Type type, IEnumerable <Type> importTypes, CodeWriter code) { string simpleTypeName; if (!type.IsNested) { if (!(type is Enum)) { foreach (Type importType in importTypes) { code.Code($"import {importType.Name} = require(\"./{importType.Name}\");"); } if (!(type is Interface)) { code.Code(); code.Code($"import {{ bridge, NativeObject, NativeReference{(this.ES6 ? "" : ", Promise")} }} " + $"from \"{this.BridgeModuleName}\";"); } else if (!this.ES6) { code.Code(); code.Code($"import {{ Promise }} from \"{this.BridgeModuleName}\";"); } } code.Code(); simpleTypeName = type.Name; } else { // TODO: Support multi-level nesting simpleTypeName = type.Name.Replace('+', '.'); } simpleTypeName = simpleTypeName.Substring(simpleTypeName.LastIndexOf('.') + 1); PluginInfo.AssemblyClassInfo classInfo = this.PluginInfo.Assembly.Classes .FirstOrDefault(c => c.Name == type.Name || c.Name == type.FullName); bool marshalByValue = (classInfo != null && classInfo.MarshalByValue == "true"); if (type is Class || type is Struct) { string extendsTypeName = (marshalByValue ? "NativeObject" : "NativeReference"); code.Code($"{(type.IsNested ? "export " : "")}class {simpleTypeName} extends {extendsTypeName} {{"); code.Code($"\tstatic type: string = \"{type.FullName}\";"); } else if (type is Interface) { code.Code($"{(type.IsNested ? "export " : "")}interface {simpleTypeName} {{"); } else if (type is Enum) { code.Code($"{(type.IsNested ? "export " : "")}enum {simpleTypeName} {{"); } else { throw new NotSupportedException("Type type not supported: " + type.GetType().Name); } CodeWriter members = code.Indent(); Func <Property, bool> isStructProperty = p => p.GetMethod != null && !p.IsStatic; if (type is Enum) { foreach (EnumValue field in type.Members.Cast <EnumValue>().OrderBy(f => (int)f.Value)) { this.WriteTypeScriptBindingForField(type, field, members); } } else if (marshalByValue) { // Give the C3P JS marshaller hints about how to convert certain marshal-by-value fields. IEnumerable <Property> properties = type.Members.OfType <Property>().Where(isStructProperty); string[] guidFields = properties.Where(p => p.PropertyType == "System.Guid") .Select(p => p.Name).ToArray(); if (guidFields.Length > 0) { members.Code("static typeConversions: any = { " + String.Join(", ", guidFields.Select(f => $"\"{Uncapitalize(f)}\": \"uuid\"")) + " };"); } bool isFirstField = true; foreach (Property property in properties) { if (isFirstField) { members.Code(); isFirstField = false; } this.WriteTypeScriptBindingForField(type, property, members); } } if (marshalByValue) { members.Code(); members.Code( "constructor() {", $"\tsuper({simpleTypeName}.type);", "}"); } else if (type is Class) { string implicitContextArgument = null; foreach (Constructor constructor in type.Members.OfType <Constructor>()) { members.Code(); this.WriteTypeScriptBindingForConstructor(type, constructor, members); if (implicitContextArgument == null) { implicitContextArgument = GetImplicitContextArgument(constructor); } } string argsWithContext = (implicitContextArgument == null ? "args" : "[" + implicitContextArgument + "].concat(args)"); members.Code(); members.Code("constructor(handle: Promise<number>);"); members.Code(); members.Code( $"constructor(...args: any[]) {{", $"\tsuper(", $"\t\t{simpleTypeName}.type,", $"\t\t(args.length === 1 && args[0] instanceof Promise ? args[0] :", $"\t\t\tbridge.createInstance({type.Name}.type, {argsWithContext})));", $"}}"); members.Code(); members.Code("dispose(): Promise<void> {"); members.Code("\tvar releaseNativeInstance: () => Promise<void> = "); members.Code("\t\tbridge.releaseInstance.bind(undefined, this.type, this.handle);"); members.Code("\treturn super.dispose().then(function () { return releaseNativeInstance(); });"); members.Code("}"); } foreach (Property property in type.Members.OfType <Property>() .Where(p => !marshalByValue || !isStructProperty(p))) { members.Code(); this.WriteTypeScriptBindingsForProperty(type, property, members); } foreach (Event eventMember in type.Members.OfType <Event>()) { members.Code(); this.WriteTypeScriptBindingForEvent(type, eventMember, members); } foreach (Method method in type.Members.OfType <Method>()) { members.Code(); this.WriteTypeScriptBindingForMethod(type, method, members); } code.Code("}"); if (!(type is Enum) && !(type is Interface)) { code.Code($"bridge.registerType({simpleTypeName}.type, <any>{simpleTypeName});"); } if (type.Members.OfType <Type>().Count() != 0) { code.Code(); code.Code($"module {type.Name} {{"); foreach (Type nestedType in type.Members.OfType <Type>()) { code.Code(); this.WriteTypeScriptBindingsForType(nestedType, null, code.Indent()); } code.Code("}"); } if (!type.IsNested) { code.Code(); code.Code($"export = {type.Name};"); } }
void WriteTypeScriptBindingForMethod(Type declaringType, Method method, CodeWriter code) { string methodName = Uncapitalize(method.Name); string parameters = String.Join(", ", method.Parameters.Select( p => p.Name + ": " + this.GetJavaScriptTypeName(p.ParameterType, declaringType))); string returnType = this.GetJavaScriptTypeName(method.ReturnType, declaringType); if (this.ForceAsyncAPIs) { returnType = MakePromiseTypeName(returnType); if (!methodName.EndsWith("Async")) { methodName += "Async"; } } bool isInterfaceMethod = (declaringType is Interface); code.Code((method.IsStatic ? "static " : String.Empty) + $"{methodName}({parameters}): {returnType}" + (isInterfaceMethod ? ";" : " {")); if (!isInterfaceMethod) { CodeWriter body = code.Indent(); string retCast = ""; if (method.ReturnType == "System.Boolean" || method.ReturnType == "System.Threading.Tasks.Task<System.Boolean>") { retCast = ".then(result => !!result)"; } else if (method.ReturnType == "System.Void" || method.ReturnType == "System.Threading.Tasks.Task") { retCast = ".then(result => undefined)"; } else if (method.ReturnType == "System.Guid" || method.ReturnType == "System.Threading.Tasks.Task<System.Guid>") { retCast = ".then(result => (typeof(result) === \"string\" ? " + "result.toUpperCase() : result && result.value))"; } string arguments = String.Join(", ", method.Parameters.Select(p => p.ParameterType == "System.Guid" ? $"(typeof({p.Name}) === \"string\" ? {{ \"type\": \"<uuid>\", \"value\": {p.Name} }} : null)" : p.ParameterType == "System.Uri" ? $"(typeof({p.Name}) === \"string\" ? {{ \"type\": \"<uri>\", \"value\": {p.Name} }} : null)" : p.Name)); string implicitContextArgument = GetImplicitContextArgument(method); if (implicitContextArgument != null) { arguments = (arguments.Length > 0 ? implicitContextArgument + ", " + arguments : implicitContextArgument); } if (method.IsStatic) { string simpleName = declaringType.Name.Substring(declaringType.Name.IndexOf('+') + 1); body.Code("return bridge.invokeStaticMethod(" + $"{simpleName}.type, \"{method.Name}\", [{arguments}]){retCast};"); } else { body.Code($"return bridge.invokeMethod(this, \"{method.Name}\", [{arguments}]){retCast};"); } code.Code("}"); } }
void WriteTypeScriptBindingForEvent(Type declaringType, Event eventMember, CodeWriter code) { bool isInterfaceMember = (declaringType is Interface); string eventHandlerPrefix = "System.EventHandler<"; if (!eventMember.EventHandlerType.StartsWith(eventHandlerPrefix)) { throw new NotSupportedException("Unsupported event handler type: " + eventMember.EventHandlerType); } string addMethodName = $"add{eventMember.Name}Listener"; string removeMethodName = $"remove{eventMember.Name}Listener"; string returnType = "void"; if (this.ForceAsyncAPIs) { addMethodName += "Async"; removeMethodName += "Async"; returnType = "Promise<void>"; } string eventType = eventMember.EventHandlerType.Substring( eventHandlerPrefix.Length, eventMember.EventHandlerType.Length - 1 - eventHandlerPrefix.Length); code.Code((eventMember.IsStatic ? "static " : String.Empty) + $"{addMethodName}(listener: (e: {GetJavaScriptTypeName(eventType, declaringType)}) => void)" + $": {returnType}" + (isInterfaceMember ? ";" : " {")); string simpleName = declaringType.Name.Substring(declaringType.Name.IndexOf('+') + 1); if (!isInterfaceMember) { if (eventMember.IsStatic) { code.Code("\treturn bridge.addStaticEventListener(" + $"{simpleName}.type, \"{eventMember.Name}\", listener);"); } else { code.Code($"\treturn bridge.addEventListener(this, \"{eventMember.Name}\", listener);"); } code.Code("}"); } code.Code(); code.Code((eventMember.IsStatic ? "static " : String.Empty) + $"{removeMethodName}(listener: (e: {GetJavaScriptTypeName(eventType, declaringType)}) => void)" + $": {returnType}" + (isInterfaceMember ? ";" : " {")); if (!isInterfaceMember) { if (eventMember.IsStatic) { code.Code("\treturn bridge.removeStaticEventListener(" + $"{simpleName}.type, \"{eventMember.Name}\", listener);"); } else { code.Code($"\treturn bridge.removeEventListener(this, \"{eventMember.Name}\", listener);"); } code.Code("}"); } }
void GeneratePluginModule(ClrApi pluginApi, string scriptOutputPath) { using (CodeWriter code = new CodeWriter(Path.Combine(scriptOutputPath, "plugin.ts"))) { code.Code("import { Platform, NativeModules } from \"react-native\";"); code.Code(); HashSet <string> allNamespaces = new HashSet <string>(); if (this.PluginInfo.AndroidPlatform != null) { code.Code("if (Platform.OS === \"android\") {"); foreach (var namespaceMapping in this.PluginInfo.AndroidPlatform.NamespaceMappings) { code.Code("\tNativeModules.C3P.registerNamespaceMapping(" + $"\"{namespaceMapping.Namespace}\", \"{namespaceMapping.Package}\");"); allNamespaces.Add(namespaceMapping.Namespace); } code.Code("}"); } if (this.PluginInfo.IOSPlatform != null) { code.Code("if (Platform.OS === \"ios\") {"); foreach (var namespaceMapping in this.PluginInfo.IOSPlatform.NamespaceMappings) { code.Code("\tNativeModules.C3P.registerNamespaceMapping(" + $"\"{namespaceMapping.Namespace}\", \"{namespaceMapping.Prefix}\");"); allNamespaces.Add(namespaceMapping.Namespace); } code.Code("}"); } if (this.PluginInfo.WindowsPlatform != null) { code.Code("if (Platform.OS === \"windows\") {"); foreach (var namespaceInfo in this.PluginInfo.WindowsPlatform.IncludeNamespaces) { code.Code("\tNativeModules.C3P.registerNamespaceMapping(" + $"\"{namespaceInfo.Namespace}\");"); allNamespaces.Add(namespaceInfo.Namespace); } code.Code("}"); } code.Code(); foreach (var classInfo in this.PluginInfo.Assembly.Classes.Where(c => c.MarshalByValue == "true")) { code.Code($"NativeModules.C3P.registerMarshalByValueClass(\"{classInfo.Name}\");"); } string[] includeNamespaces = allNamespaces.ToArray(); code.Code(); foreach (Type pluginType in pluginApi.Assemblies.Single().Types .Where(t => includeNamespaces.Contains(t.Namespace))) { code.Code($"import {pluginType.Name} = require(\"./{pluginType.Name}\");"); } code.Code(); code.Code("export = {"); foreach (Type pluginType in pluginApi.Assemblies.Single().Types .Where(t => includeNamespaces.Contains(t.Namespace))) { code.Code($"\t{pluginType.Name}: {pluginType.Name},"); } code.Code("}"); } }
protected override void WriteAsyncMethod( Method method, string declaringTypeFullName, string boundTypeFullName, bool isInterfaceMember, CodeWriter code) { string asyncReturnType = this.FindAsyncReturnType(boundTypeFullName, method.Name, declaringTypeFullName); string mappedAsyncReturnType = this.GetMemberTypeName(asyncReturnType, declaringTypeFullName, method.Name); string xamarinReturnType = this.MapTypeName(asyncReturnType); if (asyncReturnType == "java.util.UUID") { // TODO: This special case shouldn't be necessary here. xamarinReturnType = "Java.Util.UUID"; } string castFromType = xamarinReturnType; if (xamarinReturnType == null) { if (asyncReturnType.StartsWith("java.util.List<")) { // Java lists inside a Future don't get automatically converted to // the generic IList<> by Xamarin. xamarinReturnType = "System.Collections.IList"; string itemType = asyncReturnType.Substring(asyncReturnType.IndexOf('<') + 1); itemType = itemType.Substring(0, itemType.Length - 1); castFromType = xamarinReturnType + "<" + (this.MapTypeName(itemType) ?? itemType) + ">"; } else { xamarinReturnType = mappedAsyncReturnType; castFromType = xamarinReturnType; } } string context = this.GetImplicitContext(method, code); int skip = context != null ? 1 : 0; bool isAbstract = isInterfaceMember || method.IsAbstract; string parameters = String.Join(", ", method.Parameters.Skip(skip).Select( (p, i) => GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i) + " " + p.Name)); code.Code($"{(isInterfaceMember ? "" : "public ")}{(method.IsStatic ? "static " : "")}" + $"{(isAbstract && !isInterfaceMember ? "abstract" : "async")} " + "System.Threading.Tasks.Task" + (mappedAsyncReturnType != "void" ? "<" + mappedAsyncReturnType + ">" : "") + $" {method.Name}({parameters}){(isAbstract ? ";" : "")}"); if (!isAbstract) { code.Code("{"); if (context != null && method.Parameters.Count > 1) { context += ", "; } string arguments = (context != null ? context : String.Empty) + String.Join(", ", method.Parameters.Skip(skip).Select( (p, i) => this.CastArgument( p.Name, this.GetParameterTypeName(p.ParameterType, declaringTypeFullName, method.Name, i), p.ParameterType))); string forward = (method.IsStatic ? boundTypeFullName : "Forward"); code.Code($"\tvar future = {forward}.{method.Name}({arguments});"); if (mappedAsyncReturnType == "void") { code.Code("\tawait Java.Util.Concurrent.IFutureExtensions.GetAsync(future);"); } else { code.Code($"\tvar result = ({xamarinReturnType})" + "await Java.Util.Concurrent.IFutureExtensions.GetAsync(future);"); code.Code($"\treturn " + this.CastArgument($"result", castFromType, mappedAsyncReturnType) + ";"); } code.Code("}"); } }