| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-01-14 21:18:24 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2026-01-14 21:18:24 UTC |
| parent | 8641b75288ccacf2df7d3a067a3111c1dc2816ae |
| HaxeCBridge.hx | +67 | -16 |
| HaxeSwiftBridge.hx | +207 | -176 |
| borogove/Form.hx | +1 | -0 |
| borogove/Persistence.hx | +14 | -1 |
| borogove/calls/Session.hx | +11 | -1 |
| borogove/persistence/KeyValueStore.hx | +5 | -0 |
| borogove/persistence/MediaStore.hx | +5 | -0 |
diff --git a/HaxeCBridge.hx b/HaxeCBridge.hx index 65359f5..e25104a 100644 --- a/HaxeCBridge.hx +++ b/HaxeCBridge.hx @@ -96,7 +96,7 @@ class HaxeCBridge { static public function expose(?namespace: String) { var clsRef = Context.getLocalClass(); var cls = clsRef.get(); - var fields = Context.getBuildFields(); + var buildFields = Context.getBuildFields(); if (libName == null) { // if we cannot determine a libName from --main or -D, we use the first exposed class @@ -107,10 +107,27 @@ class HaxeCBridge { } } - queuedClasses.push({ - cls: clsRef, - namespace: namespace - }); + final companionFields = []; + final companion: TypeDefinition = { + pack: cls.pack, + name: cls.name + "__Companion", + pos: cls.pos, + kind: TDClass(null, [], false, true, false), + meta: [ + { pos: cls.pos, name: ":keep" }, + { pos: cls.pos, name: "HaxeCBridge.name", params: [macro $v{cls.pack.join("_") + "_" + cls.name}]} + ], + fields: companionFields, + }; + final fields = if (cls.isInterface) { + companionFields; + } else { + queuedClasses.push({ + cls: clsRef, + namespace: namespace + }); + buildFields; + } // add @:keep cls.meta.add(':keep', [], Context.currentPos()); @@ -175,7 +192,7 @@ class HaxeCBridge { firstRun = false; } - final forloop = fields.slice(0); + final forloop = buildFields.slice(0); var insertTo = 0; for (field in forloop) { insertTo++; @@ -306,14 +323,22 @@ class HaxeCBridge { if (wrapper.doc != null) wrapper.doc = ~/@returns Promise resolving to/.replace(wrapper.doc, "@param handler which receives"); default: } - if (wrap) { - if (outPtr) { - wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro { final out = $i{field.name}($a{passArgs}); if (outPtr != null) { cpp.Pointer.fromRaw(outPtr).set_ref(out); } return out.length; }, args: args}); + if (wrap || cls.isInterface) { + final pth = if (cls.isInterface) { + wrapper.access.push(AStatic); + args.insert(0, { name: "self", type: TPath({ pack: cls.pack, name: cls.name }) }); + ["self", field.name]; + } else { + [field.name]; + } + final expr = if (outPtr) { + macro { final out = $p{pth}($a{passArgs}); if (outPtr != null) { cpp.Pointer.fromRaw(outPtr).set_ref(out); } return out.length; }; } else if (promisify.length > 0) { - wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro if (handler == null) $i{field.name}($a{passArgs}); else $i{field.name}($a{passArgs}).then(v->handler($a{promisify}), e->handler($a{promisifyE})), args: args}); + macro if (handler == null) $p{pth}($a{passArgs}); else $p{pth}($a{passArgs}).then(v->handler($a{promisify}), e->handler($a{promisifyE})); } else { - wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro return $i{field.name}($a{passArgs}), args: args}); + macro return $p{pth}($a{passArgs}); } + wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: expr, args: args}); fields.insert(insertTo, wrapper); insertTo++; field.meta.push({name: "HaxeCBridge.noemit", params: [{ pos: field.pos, expr: EConst(CString("wrapped")) }], pos: field.pos}); @@ -361,14 +386,16 @@ class HaxeCBridge { }); insertTo++; default: + final selfArg = cls.isInterface ? [{ name: "__self", type: TPath({ pack: cls.pack, name: cls.name }) }] : []; + final pth = cls.isInterface ? ["__self", field.name] : [field.name]; if (get != "null" && get != "never") { fields.insert(insertTo, { name: field.name + "__fromC", doc: field.doc, meta: [{name: "HaxeCBridge.wrapper", params: [], pos: field.pos}], - access: field.access, + access: field.access.concat(cls.isInterface ? [AStatic] : []), pos: field.pos, - kind: FFun({ret: t, params: [], args: [], expr: macro { return $i{field.name} }}) + kind: FFun({ret: t, params: [], args: selfArg, expr: macro { return $p{pth} }}) }); insertTo++; } @@ -379,7 +406,7 @@ class HaxeCBridge { meta: [{name: "HaxeCBridge.wrapper", params: [], pos: field.pos}], access: field.access, pos: field.pos, - kind: FFun({ret: TPath({name: "Void", pack: []}), params: [], args: [{name: "value", type: t}], expr: macro $i{field.name} = value}) + kind: FFun({ret: TPath({name: "Void", pack: []}), params: [], args: selfArg.concat([{name: "value", type: t}]), expr: macro $p{pth} = value}) }); insertTo++; } @@ -441,7 +468,31 @@ class HaxeCBridge { } } - return fields; + if (cls.isInterface) { + // An interface can't contain methods so we make a companion object + // Haxe already makes one in C++... but this lets us customize it + // and also works with the rest of the codegen without more changes + final fullPath = companion.pack.join(".") + "." + companion.name; + Context.defineModule( + fullPath, + [companion], + [ + { path: [{ pos: companion.pos, name: "HaxeCBridge" }], mode: INormal }, + { path: cls.module.split(".").map(p -> { pos: companion.pos, name: p }), mode: INormal } + ] + ); + final ct = Context.getType(fullPath); + switch (ct) { + case TInst(ref, _): + queuedClasses.push({ + cls: ref, + namespace: namespace + }); + default: Context.error("Comanion must be a class", companion.pos); + } + } + + return buildFields; } static function convertSecondaryTP(tp: TypeParam) { @@ -530,7 +581,7 @@ class HaxeCBridge { function convertFunction(f: ClassField, kind: FunctionInfoKind) { var isConvertibleMethod = f.isPublic && !f.isAbstract && !f.isExtern && !f.meta.has("HaxeCBridge.noemit") && switch f.kind { case FVar(_), FMethod(MethMacro): false; // skip macro methods - case FMethod(_): true; + case FMethod(_): f.expr() != null; } if (!isConvertibleMethod) return; diff --git a/HaxeSwiftBridge.hx b/HaxeSwiftBridge.hx index 6b225c8..e09750a 100644 --- a/HaxeSwiftBridge.hx +++ b/HaxeSwiftBridge.hx @@ -288,6 +288,200 @@ class HaxeSwiftBridge { } } + static function buildVar(f: ClassField, libName: String, cFuncNameGet: String, cFuncNameSet: String, read: VarAccess, write: VarAccess, isStatic: Bool, builder: hx.strings.StringBuilder, genBody: Bool, genAccess: Bool) { + builder.add("\t"); + if (genAccess) builder.add("public "); + builder.add("var "); + builder.add(f.name); + builder.add(": "); + builder.add(getSwiftType(f.type)); + builder.add(" {\n"); + if (read == AccNormal || read == AccCall) { + builder.add("\t\tget"); + if (genBody) { + builder.add(" {\n\t\t\t"); + builder.add(castToSwift('c_${libName}.${cFuncNameGet}(${isStatic ? '' : 'o'})', f.type, false, true)); + builder.add("\n\t\t}"); + } + builder.add("\n"); + } + if (write == AccNormal || write == AccCall) { + builder.add("\t\tset"); + if (genBody) { + builder.add(" {\n\t\t\tc_"); + builder.add(libName); + builder.add("."); + builder.add(cFuncNameSet); + builder.add("(" + (isStatic ? "" : "o, ")); + builder.add(castToC("newValue", f.type)); + switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(f.type), Context.currentPos()), false) { + case TInst(_.get().name => "Array", [param]): + builder.add(", "); + builder.add("newValue.count"); + default: + } + builder.add(")\n\t\t}"); + } + builder.add("\n"); + } + builder.add("\t}\n\n"); + } + + static function mkSwiftAsync(ibuilder: hx.strings.StringBuilder, ret: haxe.macro.Type) { + ibuilder.add("{ ("); + ibuilder.add("a"); + switch (ret) { + case TInst(_.get().name => "Array", params): + ibuilder.add(", a_length"); + default: + } + ibuilder.add(", ctx"); + ibuilder.add(") in\n\t\t\t\tlet cont = Unmanaged<AnyObject>.fromOpaque(ctx!).takeRetainedValue() as! UnsafeContinuation<"); + ibuilder.add(getSwiftType(ret)); + ibuilder.add(", Never>\n\t\t\t\t"); + final cbuilder = new hx.strings.StringBuilder("cont.resume"); + cbuilder.add("(returning: "); + cbuilder.add(castToSwift("a", ret)); + cbuilder.add(")"); + ibuilder.add(cbuilder.toString()); + ibuilder.add("\n\t\t\t},\n\t\t\t__"); + ibuilder.add("cont_ptr"); + } + + static function buildMember(funcName: String, cFuncName: String, fld: Field, targs: Array<{ t: Type, opt: Bool, name: String }>, tret: Type, builder: hx.strings.StringBuilder, genBody: Bool, genAccess: Bool) { + var finalTret = tret; + builder.add("\t"); + if (genAccess) builder.add("public "); + builder.add("func "); + builder.add(funcName); + builder.add("("); + convertArgs(builder, targs, fld?.kind); + builder.add(") "); + switch tret { + case TAbstract(_.get().name => "Promise", params): + builder.add("async "); + default: + } + builder.add("-> "); + switch tret { + case TAbstract(_.get().name => "Promise", params): + builder.add(getSwiftType(params[0])); + default: + builder.add(getSwiftType(tret)); + } + if (genBody) { + builder.add(" {\n\t\t"); + switch tret { + case TAbstract(_.get().name => "Promise", params): + builder.add("return await withUnsafeContinuation { cont in\n\t\t"); + builder.add("let __cont_ptr = UnsafeMutableRawPointer(Unmanaged.passRetained(cont as AnyObject).toOpaque())\n\t\t"); + default: + } + for (arg in targs) { + switch (arg.t) { + case TFun(fargs, fret): + builder.add("let __"); + builder.add(arg.name); + builder.add("_ptr = UnsafeMutableRawPointer(Unmanaged.passRetained("); + builder.add(arg.name); + builder.add(" as AnyObject).toOpaque())\n\t\t"); + default: + } + } + for (arg in targs) { + final allowNull = switch arg.t { + case TAbstract(_.get().name => "Null", [param]): true; + default: false; + }; + switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(arg.t), Context.currentPos()), false) { + case TInst(_.get().name => "Array", [TInst(_.get().name => "String", _)]): + builder.add("with" + (allowNull ? "Optional" : "") + "ArrayOfCStrings(" + arg.name + ") { __" + arg.name + " in "); + default: + } + } + final ibuilder = new hx.strings.StringBuilder("c_"); + ibuilder.add(libName); + ibuilder.add("."); + ibuilder.add(cFuncName); + ibuilder.add("(\n\t\t\tself.o"); + for (arg in targs) { + ibuilder.add(",\n\t\t\t"); + final allowNull = switch arg.t { + case TAbstract(_.get().name => "Null", [param]): true; + default: false; + }; + switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(arg.t), Context.currentPos()), false) { + case TFun(fargs, fret): + ibuilder.add("{ ("); + for (i => farg in fargs) { + if (i > 0) ibuilder.add(", "); + ibuilder.add("a" + i); + switch (farg.t) { + case TInst(_.get().name => "Array", params): + ibuilder.add(", a" + i + "_length"); + default: + } + } + if (fargs.length > 0) ibuilder.add(", "); + ibuilder.add("ctx"); + // TODO unretained vs retained + ibuilder.add(") in\n\t\t\t\tlet "); + ibuilder.add(arg.name); + ibuilder.add(" = Unmanaged<AnyObject>.fromOpaque(ctx!).takeUnretainedValue() as! "); + ibuilder.add(getSwiftType(arg.t)); + ibuilder.add("\n\t\t\t\t"); + final cbuilder = new hx.strings.StringBuilder(arg.name); + cbuilder.add("("); + for (i => farg in fargs) { + if (i > 0) cbuilder.add(", "); + cbuilder.add(castToSwift("a" + i, farg.t)); + } + cbuilder.add(")"); + ibuilder.add("return "); + ibuilder.add(castToC(cbuilder.toString(), fret, false)); + ibuilder.add("\n\t\t\t},\n\t\t\t__"); + ibuilder.add(arg.name); + ibuilder.add("_ptr"); + case TInst(_.get().name => "Array", [TInst(_.get().name => "String", _)]): + ibuilder.add("__"); + ibuilder.add(arg.name); + ibuilder.add(", "); + ibuilder.add(arg.name + (allowNull ? "?" : "") + ".count" + (allowNull ? " ?? 0" : "")); + case TInst(_.get().name => "Array", [param]): + ibuilder.add(castToC(arg.name, arg.t)); + ibuilder.add(", "); + ibuilder.add(arg.name + (allowNull ? "?" : "") + ".count" + (allowNull ? " ?? 0" : "")); + default: + ibuilder.add(castToC(arg.name, arg.t)); + } + } + switch tret { + case TAbstract(_.get().name => "Promise", params): + ibuilder.add(",\n\t\t\t"); + mkSwiftAsync(ibuilder, params[0]); + finalTret = Context.resolveType(TPath({name: "Void", pack: []}), Context.currentPos()); + default: + } + ibuilder.add("\n\t\t)"); + builder.add("return "); + builder.add(castToSwift(ibuilder.toString(), finalTret, false, true)); + for (arg in targs) { + switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(arg.t), Context.currentPos()), false) { + case TInst(_.get().name => "Array", [TInst(_.get().name => "String", _)]): + builder.add("}"); + default: + } + } + switch tret { + case TAbstract(_.get().name => "Promise", params): + builder.add("\n\t\t}"); + default: + } + builder.add("\n\t}"); + } + builder.add("\n\n"); + } + static function convertQueuedClass(clsRef: Ref<ClassType>, namespace: String, fields: Array<Field>) { var cls = clsRef.get(); @@ -305,6 +499,7 @@ class HaxeSwiftBridge { .concat(safeIdent(classPrefix.join('.')) != libName ? classPrefix : []) .filter(s -> s != ''); + final extensionBuilder = new hx.strings.StringBuilder("public extension " + cls.name + " {\n"); final builder = new hx.strings.StringBuilder(cls.isInterface ? "public protocol " : "public class "); builder.add(cls.name); final superClass = if (cls.superClass == null) { @@ -346,7 +541,6 @@ class HaxeSwiftBridge { final noemit = f.meta.extract("HaxeCBridge.noemit")[0]; var isConvertibleMethod = f.isPublic && !f.isExtern && (noemit == null || (noemit.params != null && noemit.params.length > 0)); if (!isConvertibleMethod) return; - if (cls.isInterface) return; if (read != AccNormal && read != AccCall) return; // Swift doesn't allow write-only final cNameMeta = getCNameMeta(f.meta); @@ -357,60 +551,15 @@ class HaxeSwiftBridge { final cleanDoc = f.doc != null ? StringTools.trim(removeIndentation(f.doc)) : null; if (cleanDoc != null) builder.add('\t/**\n${cleanDoc.split('\n').map(l -> '\t ' + l).join('\n')}\n\t */\n'); - builder.add("\tpublic var "); - builder.add(f.name); - builder.add(": "); - builder.add(getSwiftType(f.type)); - builder.add(" {\n"); - if (read == AccNormal || read == AccCall) { - builder.add("\t\tget {\n\t\t\t"); - builder.add(castToSwift('c_${libName}.${cFuncNameGet}(${isStatic ? '' : 'o'})', f.type, false, true)); - builder.add("\n\t\t}\n"); - } - if (write == AccNormal || write == AccCall) { - builder.add("\t\tset {\n\t\t\tc_"); - builder.add(libName); - builder.add("."); - builder.add(cFuncNameSet); - builder.add("(" + (isStatic ? "" : "o, ")); - builder.add(castToC("newValue", f.type)); - switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(f.type), Context.currentPos()), false) { - case TInst(_.get().name => "Array", [param]): - builder.add(", "); - builder.add("newValue.count"); - default: - } - builder.add(")\n\t\t}\n"); - } - builder.add("\t}\n\n"); - } - function mkSwiftAsync(ibuilder: hx.strings.StringBuilder, ret: haxe.macro.Type) { - ibuilder.add("{ ("); - ibuilder.add("a"); - switch (ret) { - case TInst(_.get().name => "Array", params): - ibuilder.add(", a_length"); - default: - } - ibuilder.add(", ctx"); - ibuilder.add(") in\n\t\t\t\tlet cont = Unmanaged<AnyObject>.fromOpaque(ctx!).takeRetainedValue() as! UnsafeContinuation<"); - ibuilder.add(getSwiftType(ret)); - ibuilder.add(", Never>\n\t\t\t\t"); - final cbuilder = new hx.strings.StringBuilder("cont.resume"); - cbuilder.add("(returning: "); - cbuilder.add(castToSwift("a", ret)); - cbuilder.add(")"); - ibuilder.add(cbuilder.toString()); - ibuilder.add("\n\t\t\t},\n\t\t\t__"); - ibuilder.add("cont_ptr"); + buildVar(f, libName, cFuncNameGet, cFuncNameSet, read, write, isStatic, builder, !cls.isInterface, !cls.isInterface); + if (cls.isInterface) buildVar(f, libName, cFuncNameGet, cFuncNameSet, read, write, isStatic, extensionBuilder, true, false); } function convertFunction(f: ClassField, kind: SwiftFunctionInfoKind, ?fld: Field) { final noemit = f.meta.extract("HaxeCBridge.noemit")[0]; var isConvertibleMethod = f.isPublic && !f.isExtern && !f.meta.has("HaxeCBridge.wrapper") && (noemit == null || (noemit.params != null && noemit.params.length > 0)); if (!isConvertibleMethod) return; - if (cls.isInterface) return; switch f.type { case TFun(targs, tret): final cNameMeta = getCNameMeta(f.meta); @@ -463,131 +612,8 @@ class HaxeSwiftBridge { } builder.add("))\n\t}\n\n"); case Member: - builder.add("\tpublic func "); - builder.add(funcName); - builder.add("("); - convertArgs(builder, targs, fld?.kind); - builder.add(") "); - switch tret { - case TAbstract(_.get().name => "Promise", params): - builder.add("async "); - default: - } - builder.add("-> "); - switch tret { - case TAbstract(_.get().name => "Promise", params): - builder.add(getSwiftType(params[0])); - default: - builder.add(getSwiftType(tret)); - } - builder.add(" {\n\t\t"); - switch tret { - case TAbstract(_.get().name => "Promise", params): - builder.add("return await withUnsafeContinuation { cont in\n\t\t"); - builder.add("let __cont_ptr = UnsafeMutableRawPointer(Unmanaged.passRetained(cont as AnyObject).toOpaque())\n\t\t"); - default: - } - for (arg in targs) { - switch (arg.t) { - case TFun(fargs, fret): - builder.add("let __"); - builder.add(arg.name); - builder.add("_ptr = UnsafeMutableRawPointer(Unmanaged.passRetained("); - builder.add(arg.name); - builder.add(" as AnyObject).toOpaque())\n\t\t"); - default: - } - } - for (arg in targs) { - final allowNull = switch arg.t { - case TAbstract(_.get().name => "Null", [param]): true; - default: false; - }; - switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(arg.t), Context.currentPos()), false) { - case TInst(_.get().name => "Array", [TInst(_.get().name => "String", _)]): - builder.add("with" + (allowNull ? "Optional" : "") + "ArrayOfCStrings(" + arg.name + ") { __" + arg.name + " in "); - default: - } - } - final ibuilder = new hx.strings.StringBuilder("c_"); - ibuilder.add(libName); - ibuilder.add("."); - ibuilder.add(cFuncName); - ibuilder.add("(\n\t\t\tself.o"); - for (arg in targs) { - ibuilder.add(",\n\t\t\t"); - final allowNull = switch arg.t { - case TAbstract(_.get().name => "Null", [param]): true; - default: false; - }; - switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(arg.t), Context.currentPos()), false) { - case TFun(fargs, fret): - ibuilder.add("{ ("); - for (i => farg in fargs) { - if (i > 0) ibuilder.add(", "); - ibuilder.add("a" + i); - switch (farg.t) { - case TInst(_.get().name => "Array", params): - ibuilder.add(", a" + i + "_length"); - default: - } - } - if (fargs.length > 0) ibuilder.add(", "); - ibuilder.add("ctx"); - // TODO unretained vs retained - ibuilder.add(") in\n\t\t\t\tlet "); - ibuilder.add(arg.name); - ibuilder.add(" = Unmanaged<AnyObject>.fromOpaque(ctx!).takeUnretainedValue() as! "); - ibuilder.add(getSwiftType(arg.t)); - ibuilder.add("\n\t\t\t\t"); - final cbuilder = new hx.strings.StringBuilder(arg.name); - cbuilder.add("("); - for (i => farg in fargs) { - if (i > 0) cbuilder.add(", "); - cbuilder.add(castToSwift("a" + i, farg.t)); - } - cbuilder.add(")"); - ibuilder.add("return "); - ibuilder.add(castToC(cbuilder.toString(), fret, false)); - ibuilder.add("\n\t\t\t},\n\t\t\t__"); - ibuilder.add(arg.name); - ibuilder.add("_ptr"); - case TInst(_.get().name => "Array", [TInst(_.get().name => "String", _)]): - ibuilder.add("__"); - ibuilder.add(arg.name); - ibuilder.add(", "); - ibuilder.add(arg.name + (allowNull ? "?" : "") + ".count" + (allowNull ? " ?? 0" : "")); - case TInst(_.get().name => "Array", [param]): - ibuilder.add(castToC(arg.name, arg.t)); - ibuilder.add(", "); - ibuilder.add(arg.name + (allowNull ? "?" : "") + ".count" + (allowNull ? " ?? 0" : "")); - default: - ibuilder.add(castToC(arg.name, arg.t)); - } - } - switch tret { - case TAbstract(_.get().name => "Promise", params): - ibuilder.add(",\n\t\t\t"); - mkSwiftAsync(ibuilder, params[0]); - finalTret = Context.resolveType(TPath({name: "Void", pack: []}), Context.currentPos()); - default: - } - ibuilder.add("\n\t\t)"); - builder.add("return "); - builder.add(castToSwift(ibuilder.toString(), finalTret, false, true)); - for (arg in targs) { - switch TypeTools.followWithAbstracts(Context.resolveType(Context.toComplexType(arg.t), Context.currentPos()), false) { - case TInst(_.get().name => "Array", [TInst(_.get().name => "String", _)]): - builder.add("}"); - default: - } - } - switch tret { - case TAbstract(_.get().name => "Promise", params): - builder.add("\n\t\t}"); - default: - } - builder.add("\n\t}\n\n"); + buildMember(funcName, cFuncName, fld, targs, tret, builder, !cls.isInterface, !cls.isInterface); + if (cls.isInterface) buildMember(funcName, cFuncName, fld, targs, tret, extensionBuilder, true, false); case Static: builder.add("\tpublic static func "); builder.add(funcName); @@ -682,7 +708,9 @@ class HaxeSwiftBridge { for (f in cls.fields.get()) { switch (f.kind) { case FMethod(MethMacro): - case FMethod(_): convertFunction(f, Member, fields.find(fld -> f.name == fld.name)); + case FMethod(_): + final fld = fields.find(fld -> f.name == fld.name); + if (fld != null) convertFunction(f, Member, fld); case FVar(read, write): convertVar(f, read, write); } } @@ -698,7 +726,10 @@ class HaxeSwiftBridge { builder.add("}\n"); if (cls.isInterface) { - // TODO: extension with defaults for all exposed methods + extensionBuilder.add("}\n"); + builder.add("\n"); + builder.add(extensionBuilder.toString()); + builder.add("\npublic class Any"); builder.add(cls.name); builder.add(": "); @@ -757,7 +788,7 @@ class HaxeSwiftBridge { c_' + libName + '.' + libName + '_stop(wait) } - public protocol SDKObject { + public protocol SDKObject: Sendable { var o: UnsafeMutableRawPointer {get} } diff --git a/borogove/Form.hx b/borogove/Form.hx index 143b863..05b3662 100644 --- a/borogove/Form.hx +++ b/borogove/Form.hx @@ -8,6 +8,7 @@ import HaxeCBridge; @:expose #if cpp +@:build(HaxeCBridge.expose()) @:build(HaxeSwiftBridge.expose()) #end interface FormSection { diff --git a/borogove/Persistence.hx b/borogove/Persistence.hx index e089d4f..f167df7 100644 --- a/borogove/Persistence.hx +++ b/borogove/Persistence.hx @@ -6,24 +6,31 @@ import borogove.ChatMessage; import borogove.Message; import thenshim.Promise; +#if cpp +import HaxeCBridge; +#end + #if !NO_OMEMO import borogove.OMEMO; using borogove.SignalProtocol; #end #if cpp +@:build(HaxeCBridge.expose()) @:build(HaxeSwiftBridge.expose()) #end interface Persistence { public function lastId(accountId: String, chatId: Null<String>): Promise<Null<String>>; public function storeChats(accountId: String, chats: Array<Chat>):Void; + @HaxeCBridge.noemit public function getChats(accountId: String): Promise<Array<SerializedChat>>; @HaxeCBridge.noemit public function getChatsUnreadDetails(accountId: String, chats: Array<Chat>): Promise<Array<{ chatId: String, message: ChatMessage, unreadCount: Int }>>; + @HaxeCBridge.noemit public function storeReaction(accountId: String, update: ReactionUpdate): Promise<Null<ChatMessage>>; public function storeMessages(accountId: String, message: Array<ChatMessage>): Promise<Array<ChatMessage>>; public function updateMessage(accountId: String, message: ChatMessage):Void; - public function updateMessageStatus(accountId: String, localId: String, status:MessageStatus, statusText: Null<String>): Promise<ChatMessage>; + public function updateMessageStatus(accountId: String, localId: String, status:borogove.Message.MessageStatus, statusText: Null<String>): Promise<ChatMessage>; public function getMessage(accountId: String, chatId: String, serverId: Null<String>, localId: Null<String>): Promise<Null<ChatMessage>>; public function getMessagesBefore(accountId: String, chatId: String, beforeId: Null<String>, beforeTime: Null<String>): Promise<Array<ChatMessage>>; public function getMessagesAfter(accountId: String, chatId: String, afterId: Null<String>, afterTime: Null<String>): Promise<Array<ChatMessage>>; @@ -31,14 +38,20 @@ interface Persistence { public function hasMedia(hashAlgorithm:String, hash:BytesData): Promise<Bool>; public function storeMedia(mime:String, bytes:BytesData): Promise<Bool>; public function removeMedia(hashAlgorithm:String, hash:BytesData):Void; + @HaxeCBridge.noemit public function storeCaps(caps:Caps):Void; + @HaxeCBridge.noemit public function getCaps(ver:String): Promise<Null<Caps>>; public function storeLogin(login:String, clientId:String, displayName:String, token:Null<String>):Void; + @HaxeCBridge.noemit public function getLogin(login:String): Promise<{ clientId:Null<String>, token:Null<String>, fastCount: Int, displayName:Null<String> }>; public function removeAccount(accountId: String, completely:Bool):Void; public function listAccounts(): Promise<Array<String>>; + @HaxeCBridge.noemit public function storeStreamManagement(accountId:String, data:Null<BytesData>):Void; + @HaxeCBridge.noemit public function getStreamManagement(accountId:String): Promise<Null<BytesData>>; + @HaxeCBridge.noemit public function storeService(accountId:String, serviceId:String, name:Null<String>, node:Null<String>, caps:Caps):Void; @HaxeCBridge.noemit public function findServicesWithFeature(accountId:String, feature:String): Promise<Array<{serviceId:String, name:Null<String>, node:Null<String>, caps: Caps}>>; diff --git a/borogove/calls/Session.hx b/borogove/calls/Session.hx index 6d8b0a5..ad61083 100644 --- a/borogove/calls/Session.hx +++ b/borogove/calls/Session.hx @@ -23,6 +23,7 @@ enum abstract CallStatus(Int) { } #if cpp +@:build(HaxeCBridge.expose()) @:build(HaxeSwiftBridge.expose()) #end @:expose @@ -47,7 +48,7 @@ interface Session { public function callStatus():CallStatus; public function audioTracks():Array<MediaStreamTrack>; public function videoTracks():Array<MediaStreamTrack>; - public function dtmf():Null<DTMFSender>; + public function dtmf():Null<borogove.calls.PeerConnection.DTMFSender>; } private function mkCallMessage(to: JID, from: JID, event: Stanza) { @@ -327,7 +328,9 @@ class OutgoingProposedSession implements Session { #end @:expose class InitiatedSession implements Session { + @HaxeCBridge.noemit public var sid (get, null): String; + @HaxeCBridge.noemit public var chatId (get, null): String; private final client: Client; private final counterpart: JID; @@ -383,6 +386,7 @@ class InitiatedSession implements Session { trace("Tried to retract session in wrong state: " + sid, this); } + @HaxeCBridge.noemit public function accept() { if (accepted || remoteDescription == null) return; accepted = true; @@ -391,6 +395,7 @@ class InitiatedSession implements Session { client.trigger("call/media", { session: this, audio: audio, video: video }); } + @HaxeCBridge.noemit public function hangup() { client.sendStanza( new Stanza("iq", { to: counterpart.asString(), type: "set", id: ID.medium() }) @@ -504,6 +509,7 @@ class InitiatedSession implements Session { })).then((_) -> {}); } + @HaxeCBridge.noemit public function addMedia(streams: Array<MediaStream>): Void { if (pc == null) throw "tried to add media before PeerConnection exists"; @@ -517,6 +523,7 @@ class InitiatedSession implements Session { setupLocalDescription("content-add", oldMids, true); } + @HaxeCBridge.noemit public function callStatus() { return if (pc == null || pc.connectionState == "connecting" || pc.connectionState == "new") { Connecting; @@ -527,6 +534,7 @@ class InitiatedSession implements Session { } } + @HaxeCBridge.noemit public function audioTracks(): Array<MediaStreamTrack> { if (pc == null) return []; return pc.getTransceivers() @@ -534,6 +542,7 @@ class InitiatedSession implements Session { .map((t) -> t.receiver.track); } + @HaxeCBridge.noemit public function videoTracks(): Array<MediaStreamTrack> { if (pc == null) return []; return pc.getTransceivers() @@ -541,6 +550,7 @@ class InitiatedSession implements Session { .map((t) -> t.receiver.track); } + @HaxeCBridge.noemit public function dtmf() { if (pc == null) return null; final transceiver = pc.getTransceivers().find((t) -> t.sender != null && t.sender.track != null && t.sender.track.kind == "audio" && !t.sender.track.muted); diff --git a/borogove/persistence/KeyValueStore.hx b/borogove/persistence/KeyValueStore.hx index 84bd59c..05eb9d0 100644 --- a/borogove/persistence/KeyValueStore.hx +++ b/borogove/persistence/KeyValueStore.hx @@ -3,6 +3,11 @@ package borogove.persistence; import thenshim.Promise; #if cpp +import HaxeCBridge; +#end + +#if cpp +@:build(HaxeCBridge.expose()) @:build(HaxeSwiftBridge.expose()) #end interface KeyValueStore { diff --git a/borogove/persistence/MediaStore.hx b/borogove/persistence/MediaStore.hx index 368020d..65b45dd 100644 --- a/borogove/persistence/MediaStore.hx +++ b/borogove/persistence/MediaStore.hx @@ -4,6 +4,11 @@ import thenshim.Promise; import haxe.io.BytesData; #if cpp +import HaxeCBridge; +#end + +#if cpp +@:build(HaxeCBridge.expose()) @:build(HaxeSwiftBridge.expose()) #end interface MediaStore {