Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
parser.add_argument(
"--language",
default="kotlin",
choices=['kotlin', 'groovy', 'java', 'scala'],
choices=['kotlin', 'groovy', 'java', 'scala','swift'],
help="Select specific language"
)
parser.add_argument(
Expand Down
4 changes: 3 additions & 1 deletion src/compilers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
from src.compilers.groovy import GroovyCompiler
from src.compilers.java import JavaCompiler
from src.compilers.scala import ScalaCompiler
from src.compilers.swift import SwiftCompiler


COMPILERS = {
'kotlin': KotlinCompiler,
'groovy': GroovyCompiler,
'java': JavaCompiler,
'scala': ScalaCompiler
'scala': ScalaCompiler,
'swift': SwiftCompiler
}
33 changes: 33 additions & 0 deletions src/compilers/swift.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import re
import os

from src.compilers.base import BaseCompiler


class SwiftCompiler(BaseCompiler):
ERROR_REGEX = re.compile(
r'([a-zA-Z0-9\/_]+.swift):((\d+:\d+): error:[ ]+.*)(.*?(?=\n{1,}))')


CRASH_REGEX = re.compile(r'^compile command failed.*')

def __init__(self, input_name, filter_patterns=None,
library_path=None):
input_name = os.path.join(input_name, '*', '*.swift')
super().__init__(input_name, filter_patterns, library_path)

@classmethod
def get_compiler_version(cls):
return ['swiftc', '-version']

def get_compiler_cmd(self):
extra_options = []
if self.library_path:
extra_options = ["-cp", self.library_path]
return ['swiftc'] + extra_options + [self.input_name]

def get_filename(self, match):
return match[0]

def get_error_msg(self, match):
return match[1]
108 changes: 89 additions & 19 deletions src/generators/api/api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class APIGenerator(Generator):
"kotlin": builder.KotlinAPIGraphBuilder,
"groovy": builder.JavaAPIGraphBuilder,
"scala": builder.ScalaAPIGraphBuilder,
"swift": builder.SwiftAPIGraphBuilder,
}

def __init__(self, api_docs, options={}, language=None, logger=None):
Expand Down Expand Up @@ -121,7 +122,7 @@ def produce_test_case(self, expr: ast.Expr,
type_parameters.extend(self.test_case_type_params)
main_func = ast.FunctionDeclaration(
self.TEST_CASE_NAME,
params=[],
params=[] if self.language != 'swift' else [ast.ParameterDeclaration(str(t.name).lower(),t) for t in type_parameters], #TODO add parameters
type_parameters=type_parameters,
ret_type=self.bt_factory.get_void_type(),
body=ast.Block(body),
Expand Down Expand Up @@ -170,7 +171,7 @@ def wrap_types_with_type_parameter(self, types: List[tp.TypeParameter],
is_blacklisted = (
upper_bound.name in blacklisted
or (upper_bound.name == "Nullable"
and upper_bound.type_args[0].name in blacklisted)
and upper_bound.type_args[0].name in blacklisted) or (upper_bound.name == "Reference" and upper_bound.type_args[0].name in blacklisted)
)
if upper_bound.is_type_constructor() or is_blacklisted:
return types
Expand Down Expand Up @@ -335,7 +336,7 @@ def compute_programs(self):
def generate_expr_from_node(self, node: tp.Type,
func_ref: bool,
constraints: dict = None,
depth: int = 1) -> ExprRes:
depth: int = 1,is_mutating=False) -> ExprRes:
is_func = func_ref and self.api_graph.get_functional_type(
node) is not None
res = (
Expand All @@ -346,7 +347,8 @@ def generate_expr_from_node(self, node: tp.Type,
else self._generate_expr_from_node(
node, depth, {} if func_ref else (constraints or {}))
)
if node and utils.random.bool(prob=cfg.prob.local_variable_prob):
# Generate a local variable for the expression, required for mutaing methods and inout parameters in Swift
if node and (utils.random.bool(prob=cfg.prob.local_variable_prob) or is_mutating or str(node).startswith('Reference')):
var_name = gu.gen_identifier("lower")
if node.is_type_constructor():
node = node.new(node.type_parameters)
Expand All @@ -367,11 +369,11 @@ def generate_expr_from_node(self, node: tp.Type,
def generate_expr_from_nodes(self, nodes: List[tp.Type],
constraints: dict,
func_ref: bool = False,
depth: int = 1) -> ExprRes:
depth: int = 1,is_mutating=False) -> ExprRes:
if len(nodes) == 1:
return self.generate_expr_from_node(
nodes[0], func_ref=func_ref, constraints=constraints,
depth=depth)
depth=depth,is_mutating=is_mutating)
cond = self.generate_expr(self.bt_factory.get_boolean_type())
cond_type = functools.reduce(
lambda acc, x: acc if x.is_subtype(acc) else x,
Expand All @@ -398,19 +400,38 @@ def generate_expr_from_nodes(self, nodes: List[tp.Type],
def generate_from_type_combination(self, api, receiver, parameters,
return_type, type_map) -> ast.Expr:
type_var_map = copy(type_map)
is_mutating = False
if isinstance(api, ag.Method):
is_mutating = api.metadata.get('is_mutating',False)

receiver, _, _ = self.generate_expr_from_nodes(
receiver, type_var_map, func_ref=False)
receiver, type_var_map, func_ref=False, is_mutating=is_mutating)
exp_parameters = [p.t for p in getattr(api, "parameters", [])]
args = self._generate_args(exp_parameters, parameters,
depth=1, type_var_map=type_var_map)
var_type = return_type
self.type_eraser.with_target(return_type)
if isinstance(api, ag.Method):
call_args = [ast.CallArgument(arg.expr) for arg in args]
parameter_list = api.parameters

names = []
"""
extract names of the parameters, named parameters are default in Swift
"""
if parameter_list:
names = [param.name if isinstance(param,ag.NamedParameter) else None for param in parameter_list]


if self.language == 'swift' and len(args)==len(names):
call_args = [ast.CallArgument(args[i].expr,names[i],inout=str(parameter_list[i].t).startswith('Reference'))
for i in range(len(args))]
else:
call_args = [ast.CallArgument(args[i].expr,inout=str(parameter_list[i].t).startswith('Reference')) for i in range(len(args))]

type_args = self.substitute_types(api.type_parameters,
type_var_map)
expr = ast.FunctionCall(api.name, args=call_args,
receiver=receiver, type_args=type_args)
receiver=receiver, type_args=type_args,names=names,throws=api.metadata.get('throws',False))
if api.type_parameters and self.type_erasure_mode:
self.type_eraser.erase_types(expr, api, args)
elif isinstance(api, ag.Constructor):
Expand All @@ -423,8 +444,16 @@ def _instantiate_type_con(t: tp.Type):
con_type = self.api_graph.get_type_by_name(
cls_name) or self.parse_builtin_type(cls_name)
con_type = _instantiate_type_con(con_type)
call_args = [arg.expr for arg in args]
expr = ast.New(con_type, call_args, receiver=receiver)
parameter_list = api.parameters
"""
extract names of the parameters, named parameters are default in Swift
"""
names = []
if parameter_list:
names = [param.name if isinstance(param,ag.NamedParameter) else None for param in parameter_list]

call_args = [arg.expr for arg in args]
expr = ast.New(con_type, call_args, receiver=receiver,names=names)
if con_type.is_parameterized() and self.type_erasure_mode:
self.type_eraser.erase_types(expr, api, args)
else:
Expand Down Expand Up @@ -536,7 +565,8 @@ def generate_func_ref(self, expr_type: tp.Type, type_var_map: dict,
self.api_graph.get_functional_type(expr_type), type_var_map)
return ast.FunctionReference(api_name, receiver=rec,
signature=expr_type,
function_type=func_type)
function_type=func_type,named_parameters = None if len(api.parameters)==0 else [e.name if isinstance(e, ag.NamedParameter) else '' for e in api.parameters]
)

def generate_expr(self,
expr_type: tp.Type = None,
Expand Down Expand Up @@ -647,7 +677,7 @@ def _generate_expr_from_node(self, node, depth=1, constraints=None):
return ExprRes(expr, type_var_map, path)

def _generate_args(self, parameters, actual_types, depth,
type_var_map):
type_var_map,is_mutating=False):
if not parameters:
return []
args = []
Expand All @@ -665,7 +695,7 @@ def _generate_args(self, parameters, actual_types, depth,
rec_bound_handler=self.api_graph.get_instantiations_of_recursive_bound)
expr = self.generate_expr_from_nodes(param_types, {},
func_ref=True,
depth=depth)
depth=depth,is_mutating=is_mutating)
self.type_eraser.reset_target_type()
args.append(expr)
return args
Expand All @@ -688,15 +718,42 @@ def _instantiate_type_con(t: tp.Type):
depth, type_var_map)
self.type_eraser.reset_target_type()
if isinstance(elem, ag.Method):
is_mutating = elem.metadata.get('is_mutating',False)
parameters = [param.t for param in elem.parameters]
parameter_list = elem.parameters
"""
extract names of the parameters, named parameters are default in Swift
"""
names = []
if parameter_list:
names = [param.name if isinstance(param,ag.NamedParameter) else None for param in parameter_list]

args = self._generate_args(parameters,
[[p] for p in parameters],
depth + 1, type_var_map)
call_args = [ast.CallArgument(arg.expr)
for arg in args]
depth + 1, type_var_map,is_mutating=is_mutating)
if is_mutating:
var_name = gu.gen_identifier("lower")
last_elem = receiver_path[-1]
if isinstance(last_elem,tp.Type):
var_type = tp.substitute_type(last_elem, type_var_map)
if var_type.is_type_constructor():
var_type = var_type.new([type_var_map[t] for t in var_type.type_parameters])
else:
var_type = tp.substitute_type(self.api_graph.get_output_type(last_elem), type_var_map)
mut_var = ast.VariableDeclaration(var_name,receiver,var_type=var_type)
receiver = ast.Variable(var_name)
self._add_node_to_parent(self.namespace, mut_var)
if names and self.language == 'swift' and len(args)==len(names):


call_args = [ast.CallArgument(args[i].expr,names[i],inout=str(parameter_list[i].t).startswith('Reference')) #NAMED ARGUMENTS TODO
for i in range(len(args))]
else :

call_args = [ast.CallArgument(args[i].expr,inout=str(parameter_list[i].t).startswith('Reference')) for i in range(len(args))]
type_args = [type_var_map[tpa] for tpa in elem.type_parameters]
expr = ast.FunctionCall(elem.name, args=call_args,
receiver=receiver, type_args=type_args)
receiver=receiver, type_args=type_args,names=names,throws=elem.metadata.get('throws',False))
if elem.type_parameters and self.type_erasure_mode:
self.type_eraser.erase_types(expr, elem, args)
elif isinstance(elem, ag.Field):
Expand All @@ -710,7 +767,20 @@ def _instantiate_type_con(t: tp.Type):
args = self._generate_args(parameters, [[p] for p in parameters],
depth + 1, type_var_map)
call_args = [arg.expr for arg in args]
expr = ast.New(con_type, call_args, receiver=receiver)
parameter_list = elem.parameters
"""
extract names of the parameters, named parameters are default in Swift
"""
names = []
if parameter_list:
names = [param.name if isinstance(param,ag.NamedParameter) else None for param in parameter_list]


if names and self.language == 'swift':
expr = ast.New(con_type, call_args, receiver=receiver, names=names)
else:
expr = ast.New(con_type, call_args, receiver=receiver)

if con_type.is_parameterized() and self.type_erasure_mode:
self.type_eraser.erase_types(expr, elem, args)
elif isinstance(elem, ag.Variable):
Expand Down
Loading