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("}"); } } }
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 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};"); } }