blob: 4b643764f9b879fcbf1f7cd75d12189c26cd9d75 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import datetime
import os
import platform
import subprocess
import sys
from typing import List
# Where is this script?
DIR = os.path.dirname(os.path.realpath(__file__))
# What platform are we on?
HOST_PLARFORM = "{}-{}".format(
platform.system().lower().replace("darwin", "mac"),
{
"x86_64": "x64",
"aarch64": "arm64",
}[platform.machine()],
)
# Where is gn?
GN = os.path.realpath(os.path.join(DIR, '../../../prebuilt/third_party/gn', HOST_PLARFORM, 'gn'))
ZIRCON_TOOLS_DIR = os.environ.get('ZIRCON_TOOLS_DIR')
if ZIRCON_TOOLS_DIR is None:
print('Run "fx exec %s".' % sys.argv[0])
sys.exit(1)
FIDL_FORMAT = os.path.join(ZIRCON_TOOLS_DIR, 'fidl-format')
# Define ways that identifiers may be rendered
STYLES = []
def style(func):
STYLES.append((func.__name__, func))
@style
def lower(ident):
return '_'.join(w.lower() for w in ident)
@style
def upper(ident):
return '_'.join(w.upper() for w in ident)
@style
def camel(ident):
return ''.join(w.capitalize() for w in ident)
# Define places that identifiers may appear in a FIDL library:
USES = []
def use(func):
USES.append((func.__name__.replace('_', '.'), func))
@use
def constants(f, idents):
for ident in idents:
# TODO(fxb/38124): Enable this case once we've clarified these edge cases
# and chosen a way to unambiguously reference the root library. Currently,
# "const uint32 uint32 = 1;" will fail with an includes-cycle fidlc error.
if ident == "uint32":
continue
f.write('const uint32 %s = 1;\n' % ident)
@use
def using(f, idents):
for ident in idents:
f.write('using %s = vector;\n' % ident)
# TODO(ianloic): Make this test work. It requires N libraries to import for N
# identifiers. That doesn't fit well into the model of this test.
#@use
#def using_as(f, idents):
# for ident in idents:
# f.write('using fuchsia.mem as %s;\n' % ident)
@use
def enums(f, idents):
# enums with every dangerous name
for ident in idents:
f.write('enum %s { MEMBER = 1; };\n' % ident)
# enum with every dangerous field name
f.write('enum DangerousMembers {\n')
for i, ident in enumerate(idents):
f.write(' %s = %d;\n' % (ident, i))
f.write('};\n')
@use
def struct_types(f, idents):
# structs with every dangerous name
f.write('using membertype = uint32;\n')
for ident in idents:
# TODO(FIDL-720): Having a declaration with same same name as what is
# aliased causes a cycle.
if ident == "uint32":
continue
f.write('struct %s { membertype member = 1; };\n' % ident)
# a struct with every dangerous name as the field type
f.write('struct DangerousMembers {\n')
for i, ident in enumerate(idents):
# dangerous field type
f.write(' %s f%d;\n' % (ident, i))
f.write('};\n')
@use
def struct_names(f, idents):
# a struct with every dangerous name as the field name
f.write('struct DangerousMembers {\n')
for i, ident in enumerate(idents):
f.write(' uint32 %s;\n' % ident)
f.write('};\n')
# TODO(FIDL-759)
# Temporarily disabled due to superlinear compiler time and peak memory usage.
# @use
# def union_names(f, idents):
# # unions with every dangerous name
# f.write('using membertype = uint32;\n')
# for ident in idents:
# # TODO(FIDL-720): Having a declaration with same same name as what is
# # aliased causes a cycle.
# if ident == "uint32":
# continue
# f.write('union %s { membertype member; };\n' % ident)
#
# # a union with every dangerous name as the field type
# f.write('union DangerousMembers {\n')
# for i, ident in enumerate(idents):
# # dangerous field type
# f.write(' %s f%d;\n' % (ident, i))
# f.write('};\n')
#
#
# @use
# def union_types(f, idents):
# # a union with every dangerous name as the field name
# f.write('union DangerousMembers {\n')
# for i, ident in enumerate(idents):
# f.write(' uint32 %s;\n' % (ident))
# f.write('};\n')
@use
def table_names(f, idents):
# tables with every dangerous name
f.write('using membertype = uint32;\n')
for ident in idents:
# TODO(FIDL-720): Having a declaration with same same name as what is
# aliased causes a cycle.
if ident == "uint32":
continue
f.write('table %s { 1: membertype member; };\n' % ident)
# a table with every dangerous name as the field type
f.write('table DangerousMembers {\n')
for i, ident in enumerate(idents):
# dangerous field type
f.write(' %d: %s f%d;\n' % (i + 1, ident, i))
f.write('};\n')
@use
def table_fields(f, idents):
# a table with every dangerous name as the field name
f.write('table DangerousMembers {\n')
for i, ident in enumerate(idents):
f.write(' %d: uint32 %s;\n' % (i + 1, ident))
f.write('};\n')
@use
def protocol_names(f, idents):
# a protocols with every dangerous name
for ident in idents:
f.write('protocol %s { JustOneMethod(); };\n' % ident)
@use
def method_names(f, idents):
# a protocol with every dangerous name as a method name
f.write('protocol DangerousMethods {\n');
for ident in idents:
f.write(' %s();\n' % ident)
f.write('};\n')
@use
def event_names(f, idents):
# a protocol with every dangerous name as an event name
f.write('protocol DangerousEvents {\n');
for ident in idents:
f.write(' -> %s();\n' % ident)
f.write('};\n')
@use
def method_request_arguments(f, idents):
# a protocol with every dangerous name as a request argument
f.write('using argtype = uint32;\n')
f.write('protocol DangerousRequestArguments {\n');
for i, ident in enumerate(idents):
f.write(' Method%d(argtype %s);\n' % (i, ident))
f.write('};\n')
@use
def method_response_arguments(f, idents):
# a protocol with every dangerous name as a response argument
f.write('using argtype = uint32;\n')
f.write('protocol DangerousResponseArguments {\n');
for i, ident in enumerate(idents):
f.write(' Method%d() -> (argtype %s);\n' % (i, ident))
f.write('};\n')
@use
def method_event_arguments(f, idents):
# a protocol with every dangerous name as a event argument
f.write('using argtype = uint32;\n')
f.write('protocol DangerousResponseArguments {\n');
for i, ident in enumerate(idents):
f.write(' -> Event%d(argtype %s);\n' % (i, ident))
f.write('};\n')
def generated(prefix):
"""Return a header line indicating that this is a generated file."""
return """{prefix} Copyright {year} The Fuchsia Authors. All rights reserved.
{prefix} Use of this source code is governed by a BSD-style license that can be
{prefix} found in the LICENSE file.
{prefix} Generated by {generator}.
""".format(
prefix=prefix,
year=datetime.datetime.now().year,
generator=os.path.basename(__file__))
def library_target(library_name):
return '//garnet/tests/fidl-dangerous-identifiers/fidl/:%s' % library_name
def dangerous_identifiers() -> List[List[str]]:
"""Load a list of dangerous identifiers from the source tree."""
file_path = os.path.join(DIR, 'dangerous_identifiers.txt')
dangerous = (
line.strip()
for line in open(file_path).readlines()
if not line.startswith('#'))
idents = [ident.split('_') for ident in dangerous]
assert all(
all(all(c.islower() or c.isnumeric()
for c in piece)
for piece in ident)
for ident in idents), 'All identifiers must be in lower_camel_case'
return idents
def generate_fidl(identifiers: List[str]) -> List[str]:
"""Generate FIDL libraries for the specified identifiers.
Return the list of library names.
"""
directory = os.path.join(DIR, 'fidl')
os.makedirs(directory, exist_ok=True)
prefix = 'fidl.test.dangerous'
# generate FIDL libraries
library_names = []
for style_name, style_func in STYLES:
for use_name, use_func in USES:
library_name = '%s.%s.%s' % (prefix, use_name, style_name)
fidl_file = os.path.join(directory, '%s.test.fidl' % library_name)
with open(fidl_file, 'w') as f:
f.write(generated('//'))
f.write('library %s;\n' % library_name)
use_func(f, [style_func(r) for r in identifiers])
subprocess.check_output([FIDL_FORMAT, '-i', fidl_file])
library_names.append(library_name)
# generate BUILD.gn for FIDL libraries
build_file = os.path.join(directory, 'BUILD.gn')
with open(build_file, 'w') as build_gn:
build_gn.write(generated('#'))
build_gn.write('import("//build/fidl/fidl.gni")\n\n')
build_gn.write('group("fidl") {\ndeps=[\n')
for library_name in library_names:
build_gn.write(' ":%s",\n' % library_name)
build_gn.write(']}\n')
for library_name in library_names:
build_gn.write(
'fidl("%s") {\n sources = [ "%s.test.fidl" ] }\n\n'
% (library_name, library_name))
subprocess.check_output([GN, 'format', build_file])
return library_names
def generate_cpp(identifiers: List[str], libraries: List[str]) -> None:
directory = os.path.join(DIR, 'cpp')
os.makedirs(directory, exist_ok=True)
# generate C++ test
for library_name in libraries:
with open(os.path.join(directory, '%s_test.cc' % library_name), 'w') as f:
f.write(generated('//'))
f.write('#include <%s/cpp/fidl.h>\n' % (library_name.replace('.', '/')))
f.write('\nint main() { return 0; }\n')
# generate BUILD.gn for C++ test
build_file = os.path.join(directory, 'BUILD.gn')
with open(build_file, 'w') as build_gn:
build_gn.write(generated('#'))
for library_name in libraries:
build_gn.write("""source_set("%s_cpp") {
output_name = "cpp_fidl_dangerous_identifiers_test_%s"
sources = [ "%s_test.cc" ]
deps = [
""" % (library_name, library_name, library_name))
build_gn.write(' "%s",\n' % library_target(library_name))
build_gn.write(' ]\n}\n')
build_gn.write("""group("cpp") {
deps = [""")
for library_name in libraries:
build_gn.write("""
":%s_cpp",
""" % (library_name))
build_gn.write("""
]
}""")
subprocess.check_output([GN, 'format', build_file])
if __name__ == '__main__':
identifiers = dangerous_identifiers()
library_names = generate_fidl(identifiers)
generate_cpp(identifiers, library_names)