Skip to content

Instantly share code, notes, and snippets.

@vsapsai
Last active September 18, 2021 00:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vsapsai/f9d3603dde95eebd23248da4d7b4f5ec to your computer and use it in GitHub Desktop.
Save vsapsai/f9d3603dde95eebd23248da4d7b4f5ec to your computer and use it in GitHub Desktop.
Synthesize test cases for `Sema::addMethodTGlobalList` performance.
#!/usr/bin/env python3
# Generates a test case with a chain of framework, each of those having
# multiple classes with `init` method. The dependency chain looks like
#
# repro.m -> SynthesizedN -> SynthesizedN-1 -> ... -> Synthesized0 -> Base
import argparse
import os
# To reuse in multiple places.
def framework_name(index):
return f"Synthesized{index}"
def write_framework(name, location, header, modulemap):
framework_path = os.path.join(location, f"{name}.framework")
os.mkdir(framework_path)
os.mkdir(os.path.join(framework_path, "Headers"))
with open(os.path.join(framework_path, "Headers", f"{name}.h"), "wt") as f:
f.write(header)
os.mkdir(os.path.join(framework_path, "Modules"))
with open(os.path.join(framework_path, "Modules", "module.modulemap"), "wt") as f:
f.write(modulemap)
def synthesize_common_code(location):
modulemap = """
framework module Base {
header "Base.h"
export *
}
"""
header = """
@interface NSObject
- (id)init;
@end
"""
write_framework("Base", location, header, modulemap)
def synthesize_framework(index, location, classes_count):
name = framework_name(index)
modulemap = """
framework module {name} {{
header "{name}.h"
export *
}}
""".format(name=name)
previous_import = ""
if index > 0:
prev_name = framework_name(index - 1)
previous_import = f"#import <{prev_name}/{prev_name}.h>"
classes = ["""
@interface {name}_{i} : NSObject
- (id)init;
@end
""".format(name=name, i=i) for i in range(classes_count)]
header = """
#import <Base/Base.h>
{previous_import}
{all_classes}
""".format(previous_import=previous_import, all_classes="\n\n".join(classes))
write_framework(name, location, header, modulemap)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--frameworks-count", type=int, default=10,
help="number of frameworks to synthesize")
parser.add_argument("--classes-per-framework", type=int, default=5,
help="number of classes to create in each framework")
parser.add_argument("--output-directory", required=True,
help="file path to store the generated files")
args = parser.parse_args()
output_dir = os.path.abspath(args.output_directory)
frameworks_dir = os.path.join(output_dir, "Frameworks")
os.mkdir(frameworks_dir)
# Write Base.framework.
synthesize_common_code(frameworks_dir)
# Write synthesized frameworks.
for index in range(args.frameworks_count):
synthesize_framework(index, frameworks_dir, args.classes_per_framework)
# Write a file triggering the imports.
last_framework_name = framework_name(args.frameworks_count - 1)
repro = """
#import <{name}/{name}.h>
""".format(name=last_framework_name)
with open(os.path.join(output_dir, "repro.m"), "wt") as f:
f.write(repro)
#!/usr/bin/env python3
# Generates a test case with a chain of shared dependencies and with multiple immediate dependencies. Each framework can have multiple classes with `init` method. The dependency chain looks like
#
# repro.m -> Immediate0 -> SharedN -> SharedN-1 -> ... Shared0 -> Base
# -> Immediate1 -> SharedN -> SharedN-1 -> ... Shared0 -> Base
# ...
# -> ImmediateM -> SharedN -> SharedN-1 -> ... Shared0 -> Base
import argparse
import os
def write_framework(name, location, header_content):
modulemap = """
framework module {name} {{
header "{name}.h"
export *
}}
""".format(name=name)
framework_path = os.path.join(location, f"{name}.framework")
os.mkdir(framework_path)
os.mkdir(os.path.join(framework_path, "Headers"))
with open(os.path.join(framework_path, "Headers", f"{name}.h"), "wt") as f:
f.write(header_content)
os.mkdir(os.path.join(framework_path, "Modules"))
with open(os.path.join(framework_path, "Modules", "module.modulemap"), "wt") as f:
f.write(modulemap)
def synthesize_root_object_framework(location):
header_content = """
@interface NSObject
- (id)init;
@end
"""
write_framework("Base", location, header_content)
def framework_import(name):
return f"#import <{name}/{name}.h>"
def simple_class(name):
return """
@interface {name} : NSObject
- (id)init;
@end
""".format(name=name)
def shared_framework_name(index):
return f"Shared{index}"
def synthesize_shared_framework(index, location, classes_count):
name = shared_framework_name(index)
previous_import = ""
if index > 0:
prev_name = shared_framework_name(index - 1)
previous_import = framework_import(prev_name)
classes = [simple_class(f"{name}_{i}") for i in range(classes_count)]
header = """
#import <Base/Base.h>
{previous_import}
{all_classes}
""".format(previous_import=previous_import, all_classes="\n".join(classes))
write_framework(name, location, header)
def immediate_framework_name(index):
return f"Immediate{index}"
def synthesize_immediate_framework(index, location, classes_count, shared_dependency_name):
name = immediate_framework_name(index)
shared_import = framework_import(shared_dependency_name)
classes = [simple_class(f"{name}_{i}") for i in range(classes_count)]
header = """
{shared_import}
{all_classes}
""".format(shared_import=shared_import, all_classes="\n".join(classes))
write_framework(name, location, header)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--shared-frameworks-count", type=int, default=50,
help="number of frameworks in the chain of shared dependencies")
parser.add_argument("--classes-per-shared-framework", type=int, default=5,
help="number of classes to create in each shared framework")
parser.add_argument("--immediate-frameworks-count", type=int, default=500,
help="number of immediate dependencies (that depend on shared themselves)")
parser.add_argument("--classes-per-immediate-framework", type=int, default=5,
help="number of classes to create in each immediate framework")
parser.add_argument("--output-directory", required=True,
help="file path to store the generated files")
args = parser.parse_args()
output_dir = os.path.abspath(args.output_directory)
frameworks_dir = os.path.join(output_dir, "Frameworks")
os.mkdir(frameworks_dir)
# Write Base.framework.
synthesize_root_object_framework(frameworks_dir)
# Write shared dependencies.
for i in range(args.shared_frameworks_count):
synthesize_shared_framework(i, frameworks_dir, args.classes_per_shared_framework)
# Write immediate dependencies.
shared_import = shared_framework_name(args.shared_frameworks_count - 1)
for i in range(args.immediate_frameworks_count):
synthesize_immediate_framework(i, frameworks_dir, args.classes_per_immediate_framework, shared_import)
# Write a file triggering the imports.
immediate_imports = [framework_import(immediate_framework_name(i)) for i in range(args.immediate_frameworks_count)]
repro = """
{imports}
{repro_class}
""".format(imports="\n".join(immediate_imports), repro_class=simple_class("Repro"))
with open(os.path.join(output_dir, "repro.m"), "wt") as f:
f.write(repro)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment