diff --git a/test/link/macho.zig b/test/link/macho.zig index 18548cd6da..d8d338d212 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -83,6 +83,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { macho_step.dependOn(testDeadStripDylibs(b, .{ .target = b.host })); macho_step.dependOn(testHeaderpad(b, .{ .target = b.host })); macho_step.dependOn(testLinkDirectlyCppTbd(b, .{ .target = b.host })); + macho_step.dependOn(testMergeLiteralsObjc(b, .{ .target = b.host })); macho_step.dependOn(testNeededFramework(b, .{ .target = b.host })); macho_step.dependOn(testObjc(b, .{ .target = b.host })); macho_step.dependOn(testObjcpp(b, .{ .target = b.host })); @@ -1125,6 +1126,96 @@ fn testMergeLiterals2(b: *Build, opts: Options) *Step { return test_step; } +fn testMergeLiteralsObjc(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "merge-literals-objc", opts); + + const main_o = addObject(b, opts, .{ .name = "main", .objc_source_bytes = + \\#import ; + \\ + \\extern void foo(); + \\ + \\int main() { + \\ NSString *thing = @"aaa"; + \\ + \\ SEL sel = @selector(lowercaseString); + \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO"); + \\ NSLog (@"Responds to lowercaseString: %@", lower); + \\ if ([thing respondsToSelector:sel]) //(lower == @"YES") + \\ NSLog(@"lowercaseString is: %@", [thing lowercaseString]); + \\ + \\ foo(); + \\} + }); + + const a_o = addObject(b, opts, .{ .name = "a", .objc_source_bytes = + \\#import ; + \\ + \\void foo() { + \\ NSString *thing = @"aaa"; + \\ SEL sel = @selector(lowercaseString); + \\ NSString *lower = (([thing respondsToSelector:sel]) ? @"YES" : @"NO"); + \\ NSLog (@"Responds to lowercaseString in foo(): %@", lower); + \\ if ([thing respondsToSelector:sel]) //(lower == @"YES") + \\ NSLog(@"lowercaseString in foo() is: %@", [thing lowercaseString]); + \\ SEL sel2 = @selector(uppercaseString); + \\ NSString *upper = (([thing respondsToSelector:sel2]) ? @"YES" : @"NO"); + \\ NSLog (@"Responds to uppercaseString in foo(): %@", upper); + \\ if ([thing respondsToSelector:sel2]) //(upper == @"YES") + \\ NSLog(@"uppercaseString in foo() is: %@", [thing uppercaseString]); + \\} + }); + + const runWithChecks = struct { + fn runWithChecks(step: *Step, exe: *Compile) void { + const builder = step.owner; + const run = addRunArtifact(exe); + run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString: YES") }); + run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString is: aaa") }); + run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to lowercaseString in foo(): YES") }); + run.addCheck(.{ .expect_stderr_match = builder.dupe("lowercaseString in foo() is: aaa") }); + run.addCheck(.{ .expect_stderr_match = builder.dupe("Responds to uppercaseString in foo(): YES") }); + run.addCheck(.{ .expect_stderr_match = builder.dupe("uppercaseString in foo() is: AAA") }); + step.dependOn(&run.step); + + const check = exe.checkObject(); + check.dumpSection("__TEXT,__objc_methname"); + check.checkContains("lowercaseString\x00"); + check.dumpSection("__TEXT,__objc_methname"); + check.checkContains("uppercaseString\x00"); + step.dependOn(&check.step); + } + }.runWithChecks; + + { + const exe = addExecutable(b, opts, .{ .name = "main1" }); + exe.addObject(main_o); + exe.addObject(a_o); + exe.root_module.linkFramework("Foundation", .{}); + runWithChecks(test_step, exe); + } + + { + const exe = addExecutable(b, opts, .{ .name = "main2" }); + exe.addObject(a_o); + exe.addObject(main_o); + exe.root_module.linkFramework("Foundation", .{}); + runWithChecks(test_step, exe); + } + + { + const b_o = addObject(b, opts, .{ .name = "b" }); + b_o.addObject(a_o); + b_o.addObject(main_o); + + const exe = addExecutable(b, opts, .{ .name = "main3" }); + exe.addObject(b_o); + exe.root_module.linkFramework("Foundation", .{}); + runWithChecks(test_step, exe); + } + + return test_step; +} + fn testMhExecuteHeader(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "mh-execute-header", opts);