Commit 000598acc9

Jakub Konka <kubkon@jakubkonka.com>
2024-01-15 11:16:03
test/link/macho: test objc msgsend selector stubs
1 parent 3d835de
Changed files (1)
test
test/link/macho.zig
@@ -53,6 +53,8 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step {
             macho_step.dependOn(testHeaderpad(b, .{ .target = b.host }));
             macho_step.dependOn(testNeededFramework(b, .{ .target = b.host }));
             macho_step.dependOn(testObjc(b, .{ .target = b.host }));
+            macho_step.dependOn(testObjcStubs(b, .{ .target = b.host }));
+            macho_step.dependOn(testObjcStubs2(b, .{ .target = b.host }));
             macho_step.dependOn(testWeakFramework(b, .{ .target = b.host }));
         }
     }
@@ -289,6 +291,7 @@ fn testEntryPointArchive(b: *Build, opts: Options) *Step {
         exe.root_module.addLibraryPath(lib.getEmittedBinDirectory());
 
         const run = addRunArtifact(exe);
+        run.expectExitCode(0);
         test_step.dependOn(&run.step);
     }
 
@@ -299,6 +302,7 @@ fn testEntryPointArchive(b: *Build, opts: Options) *Step {
         exe.link_gc_sections = true;
 
         const run = addRunArtifact(exe);
+        run.expectExitCode(0);
         test_step.dependOn(&run.step);
     }
 
@@ -769,6 +773,7 @@ fn testNoDeadStrip(b: *Build, opts: Options) *Step {
     test_step.dependOn(&check.step);
 
     const run = addRunArtifact(exe);
+    run.expectExitCode(0);
     test_step.dependOn(&run.step);
 
     return test_step;
@@ -859,6 +864,155 @@ fn testObjc(b: *Build, opts: Options) *Step {
     return test_step;
 }
 
+fn testObjcStubs(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "macho-objc-stubs", opts);
+
+    const exe = addExecutable(b, opts, .{
+        .name = "main",
+        .objc_source_bytes =
+        \\@import Foundation;
+        \\@interface Foo : NSObject
+        \\@property (nonatomic, assign) NSString* name;
+        \\@end
+        \\@implementation Foo
+        \\- (void)bar {
+        \\    printf("%s", [self.name UTF8String]);
+        \\}
+        \\@end
+        \\int main() {
+        \\    Foo *foo = [[Foo alloc] init];
+        \\    foo.name = @"Foo";
+        \\    [foo bar];
+        \\    return 0;
+        \\}
+        ,
+        .objc_source_flags = &.{ "-fmodules", "-fobjc-msgsend-selector-stubs" },
+    });
+    exe.root_module.linkFramework("Foundation", .{});
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("Foo");
+    test_step.dependOn(&run.step);
+
+    const check = exe.checkObject();
+    check.checkInHeaders();
+    check.checkExact("sectname __objc_stubs");
+    check.checkInHeaders();
+    check.checkExact("sectname __objc_methname");
+    check.checkInHeaders();
+    check.checkExact("sectname __objc_selrefs");
+    check.checkInSymtab();
+    check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$bar");
+    check.checkInSymtab();
+    check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$name");
+    check.checkInSymtab();
+    check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$setName");
+    test_step.dependOn(&check.step);
+
+    return test_step;
+}
+
+fn testObjcStubs2(b: *Build, opts: Options) *Step {
+    const test_step = addTestStep(b, "macho-objc-stubs-2", opts);
+
+    const all_h = all_h: {
+        const wf = WriteFile.create(b);
+        break :all_h wf.add("all.h",
+            \\#import <Foundation/Foundation.h>
+            \\
+            \\@interface Foo : NSObject
+            \\@property (nonatomic, assign) NSString* name;
+            \\- (void) foo;
+            \\@end
+            \\@interface Bar : NSObject
+            \\@property (nonatomic, assign) NSString* name;
+            \\- (void) bar;
+            \\- (void) foobar: (Foo*) foo;
+            \\@end
+        );
+    };
+
+    const foo_o = addObject(b, opts, .{
+        .name = "foo",
+        .objc_source_bytes =
+        \\#import <Foundation/Foundation.h>
+        \\#import "all.h"
+        \\@implementation Foo
+        \\- (void)foo {
+        \\    printf("%s", [self.name UTF8String]);
+        \\}
+        \\@end
+        ,
+        .objc_source_flags = &.{"-fobjc-msgsend-selector-stubs"},
+    });
+    foo_o.root_module.addIncludePath(all_h.dirname());
+
+    const bar_o = addObject(b, opts, .{
+        .name = "bar",
+        .objc_source_bytes =
+        \\#import <Foundation/Foundation.h>
+        \\#import "all.h"
+        \\@implementation Bar
+        \\- (void)bar {
+        \\    printf("%s", [self.name UTF8String]);
+        \\}
+        \\- (void)foobar: (Foo*) foo {
+        \\    printf("%s%s", [foo.name UTF8String], [self.name UTF8String]);
+        \\}
+        \\@end
+        ,
+        .objc_source_flags = &.{"-fobjc-msgsend-selector-stubs"},
+    });
+    bar_o.root_module.addIncludePath(all_h.dirname());
+
+    const main_o = addObject(b, opts, .{
+        .name = "main",
+        .objc_source_bytes =
+        \\#import <Foundation/Foundation.h>
+        \\#import "all.h"
+        \\int main() {
+        \\    Foo *foo = [[Foo alloc] init];
+        \\    foo.name = @"Foo";
+        \\    Bar *bar = [[Bar alloc] init];
+        \\    bar.name = @"Bar";
+        \\    [foo foo];
+        \\    [bar bar];
+        \\    [bar foobar:foo];
+        \\    return 0;
+        \\}
+        ,
+        .objc_source_flags = &.{"-fobjc-msgsend-selector-stubs"},
+    });
+    main_o.root_module.addIncludePath(all_h.dirname());
+
+    const exe = addExecutable(b, opts, .{ .name = "main" });
+    exe.addObject(main_o);
+    exe.addObject(foo_o);
+    exe.addObject(bar_o);
+    exe.root_module.linkFramework("Foundation", .{});
+
+    const run = addRunArtifact(exe);
+    run.expectStdOutEqual("FooBarFooBar");
+    test_step.dependOn(&run.step);
+
+    const check = exe.checkObject();
+    check.checkInHeaders();
+    check.checkExact("sectname __objc_stubs");
+    check.checkInHeaders();
+    check.checkExact("sectname __objc_methname");
+    check.checkInHeaders();
+    check.checkExact("sectname __objc_selrefs");
+    check.checkInSymtab();
+    check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$foo");
+    check.checkInSymtab();
+    check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$bar");
+    check.checkInSymtab();
+    check.checkContains("(__TEXT,__objc_stubs) (was private external) _objc_msgSend$foobar");
+    test_step.dependOn(&check.step);
+
+    return test_step;
+}
+
 fn testRelocatable(b: *Build, opts: Options) *Step {
     const test_step = addTestStep(b, "macho-relocatable", opts);
 
@@ -1647,3 +1801,4 @@ const BuildOptions = link.BuildOptions;
 const Compile = Step.Compile;
 const Options = link.Options;
 const Step = Build.Step;
+const WriteFile = Step.WriteFile;