blob: a8204b18f2d46b381a829539fb00e60cca480fea [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 subprocess
import sys
from typing import List
# Where is this script?
DIR = os.path.dirname(os.path.realpath(__file__))
# Where is gn?
GN = os.path.realpath(os.path.join(DIR, '../../../buildtools/gn'))
# 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:
f.write('const uint32 %s = 1;\n' % ident)
@use
def using(f, idents):
for ident in idents:
f.write('using %s = uint32;\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:
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')
@use
def union_names(f, idents):
# unions with every dangerous name
f.write('using membertype = uint32;\n')
for ident in idents:
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:
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)
with open(os.path.join(directory, '%s.test.fidl' % library_name), 'w') as f:
f.write(generated('//'))
f.write('library %s;\n' % library_name)
use_func(f, [style_func(r) for r in identifiers])
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 deps = [ "//zircon/public/fidl/fuchsia-mem" ] }\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
with open(os.path.join(directory, 'test.cpp'), 'w') as f:
f.write(generated('//'))
for library_name in libraries:
f.write('#include <%s/cpp/fidl.h>\n' % (library_name.replace('.', '/')))
f.write('\nint main() {\n return 0;\n}\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('#'))
build_gn.write("""source_set("cpp") {
output_name = "cpp_fidl_dangerous_identifiers_test"
sources = [ "test.cpp" ]
deps = [
""")
for library_name in libraries:
build_gn.write(' "%s",\n' % library_target(library_name))
build_gn.write(' ]\n}\n')
subprocess.check_output([GN, 'format', build_file])
if __name__ == '__main__':
identifiers = dangerous_identifiers()
library_names = generate_fidl(identifiers)
generate_cpp(identifiers, library_names)