Beispiel #1
0
    def visit_Call(self, node: Call):
        if _is_operation_call(node):
            node.func = copy_location(_wrap_in_controlled(node.func), node)
            node.args.insert(
                0,
                copy_location(Name(self._control_param_name, ctx=Load()),
                              node.args[0]))
            return node

        if _is_variant_call(node):
            functor_application = node.func
            if _identify_signature(functor_application) == 'Id':
                node.func = copy_location(_wrap_in_controlled(node.func), node)
                node.args.insert(
                    0,
                    copy_location(Name(self._control_param_name, ctx=Load()),
                                  node.args[0]))

            else:
                node.args[0] = copy_location(
                    _extend_control_data(node.args[0],
                                         self._control_param_name),
                    node.args[0])

        self.generic_visit(node)
        return node
Beispiel #2
0
    def visit_Call(self, node: ast.Call):
        orig_node_id = id(node)

        with self.attrsub_context(node):
            if isinstance(node.func, ast.Attribute):
                node.func = self.visit_Attribute(node.func, call_context=True)
            elif isinstance(node.func, ast.Subscript):
                node.func = self.visit_Subscript(node.func, call_context=True)
            else:
                node.func = self.visit(node.func)

        # TODO: need a way to rewrite ast of subscript args,
        #  and to process these separately from outer rewrite

        node.args = self._get_replacement_args(node.args, False)
        node.keywords = self._get_replacement_args(node.keywords, True)

        # in order to ensure that the args are processed with appropriate active scope,
        # we need to make sure not to use the active namespace scope on args (in the case
        # of a function call on an ast.Attribute).
        #
        # We do so by emitting an "enter argument list", whose handler pushes the current active
        # scope while we process each argument. The "end argument list" event will then restore
        # the active scope.
        #
        # This effectively rewrites function calls as follows:
        # f(a, b, ..., c) -> trace(f, 'enter argument list')(a, b, ..., c)
        with fast.location_of(node.func):
            node.func = fast.Call(
                func=self._emitter_ast(),
                args=[TraceEvent.before_call.to_ast(), self._get_copy_id_ast(orig_node_id)],
                keywords=fast.kwargs(
                    ret=node.func,
                    call_node_id=self._get_copy_id_ast(orig_node_id),
                ),
            )

        # f(a, b, ..., c) -> trace(f(a, b, ..., c), 'exit argument list')
        with fast.location_of(node):
            node = fast.Call(
                func=self._emitter_ast(),
                args=[TraceEvent.after_call.to_ast(), self._get_copy_id_ast(orig_node_id)],
                keywords=fast.kwargs(
                    ret=node,
                    call_node_id=self._get_copy_id_ast(orig_node_id),
                ),
            )

        return self._maybe_wrap_symbol_in_before_after_tracing(node, call_context=True, orig_node_id=orig_node_id)
Beispiel #3
0
    def visit_Call(self, node: ast.Call) -> Any:
        try:
            global_func = astutils.evalnode(node.func, self.globals)
            if self.resolve_functions:
                global_val = astutils.evalnode(node, self.globals)
            else:
                global_val = node
        except SyntaxError:
            return self.generic_visit(node)

        newnode = None
        if self.resolve_functions and global_val is not node:
            # Without this check, casts don't generate code
            if not isinstance(global_val, dtypes.typeclass):
                newnode = self.global_value_to_node(
                    global_val,
                    parent_node=node,
                    qualname=astutils.unparse(node),
                    recurse=True)
                if newnode is not None:
                    return newnode
        elif not isinstance(global_func, dtypes.typeclass):
            callables = not self.do_not_detect_callables
            newnode = self.global_value_to_node(
                global_func,
                parent_node=node,
                qualname=astutils.unparse(node),
                recurse=True,
                detect_callables=callables)
            if newnode is not None:
                node.func = newnode
                return self.generic_visit(node)
        return self.generic_visit(node)
Beispiel #4
0
    def visit_Call(self, node: ast.Call):
        # Only parse calls to parsed SDFGConvertibles
        if not isinstance(node.func, (ast.Num, ast.Constant)):
            return self.generic_visit(node)
        if isinstance(node.func, ast.Num):
            value = node.func.n
        else:
            value = node.func.value

        if not hasattr(value, '__sdfg__') or isinstance(value, SDFG):
            return self.generic_visit(node)

        constant_args = self._eval_args(node)

        # Resolve nested closure as necessary
        try:
            qualname = next(k for k, v in self.closure.closure_sdfgs.items()
                            if v is value)
            if hasattr(value, 'closure_resolver'):
                self.closure.nested_closures.append(
                    (qualname,
                     value.closure_resolver(constant_args, self.closure)))
            else:
                self.closure.nested_closures.append((qualname, SDFGClosure()))
        except Exception as ex:  # Parsing failed (anything can happen here)
            warnings.warn(f'Parsing SDFGConvertible {value} failed: {ex}')
            # Return old call AST instead
            node.func = node.func.oldnode.func
            return self.generic_visit(node)
Beispiel #5
0
def getAttributeFloatAst(call_node: ast.Call):
    r'''
    Return an attribute on one of the xAOD objects.
    '''
    # Get the name of the moment out
    if len(call_node.args) != 1:
        raise BaseException(
            "Calling getMomentFloat - only one argument is allowed")
    if not isinstance(call_node.args[0], ast.Str):
        raise BaseException(
            "Calling getMomentFloat - only acceptable argument is a string")

    r = cpp_ast.CPPCodeValue()
    # We don't need include files for this - just quick access
    r.args = [
        'moment_name',
    ]
    r.replacement_instance_obj = ('obj_j', call_node.func.value.id)
    r.running_code += [
        'float result = obj_j->getAttribute<float>(moment_name);'
    ]
    r.result = 'result'
    r.result_rep = lambda sc: crep.cpp_variable(
        unique_name("jet_attrib"), scope=sc, cpp_type=ctyp.terminal('float'))

    # Replace it as the function that is going to get called.
    call_node.func = r

    return call_node
Beispiel #6
0
def getAttributeVectorFloatAst(call_node: ast.Call):
    r'''
    Return a cpp ast accessing a vector of doubles for an xAOD attribute
    '''
    # Get the name of the moment out
    if len(call_node.args) != 1:
        raise BaseException(
            "Calling getMomentFloat - only one argument is allowed")
    if not isinstance(call_node.args[0], ast.Str):
        raise BaseException(
            "Calling getMomentFloat - only acceptable argument is a string")

    r = cpp_ast.CPPCodeValue()
    r.include_files += ['vector']
    r.args = [
        'moment_name',
    ]
    r.replacement_instance_obj = ('obj_j', call_node.func.value.id)
    r.running_code += [
        'auto result = obj_j->getAttribute<std::vector<double>>(moment_name);'
    ]
    r.result = 'result'
    r.result_rep = lambda sc: crep.cpp_collection(
        unique_name("jet_vec_attrib_"),
        scope=sc,
        collection_type=ctyp.collection(ctyp.terminal('double')))

    # Replace it as the function that is going to get called.
    call_node.func = r

    return call_node
Beispiel #7
0
    def visit_Call(self, node: ast.Call):
        # Only parse calls to parsed SDFGConvertibles
        if not isinstance(node.func, (ast.Num, ast.Constant)):
            self.seen_calls.add(astutils.unparse(node.func))
            return self.generic_visit(node)
        if hasattr(node.func, 'oldnode'):
            if isinstance(node.func.oldnode, ast.Call):
                self.seen_calls.add(astutils.unparse(node.func.oldnode.func))
            else:
                self.seen_calls.add(astutils.rname(node.func.oldnode))
        if isinstance(node.func, ast.Num):
            value = node.func.n
        else:
            value = node.func.value

        if not hasattr(value, '__sdfg__') or isinstance(value, SDFG):
            return self.generic_visit(node)

        constant_args = self._eval_args(node)

        # Resolve nested closure as necessary
        qualname = None
        try:
            if id(value) in self.closure.closure_sdfgs:
                qualname, _ = self.closure.closure_sdfgs[id(value)]
            elif hasattr(node.func, 'qualname'):
                qualname = node.func.qualname
            self.seen_calls.add(qualname)
            if hasattr(value, 'closure_resolver'):
                self.closure.nested_closures.append(
                    (qualname,
                     value.closure_resolver(constant_args, self.closure)))
            else:
                self.closure.nested_closures.append((qualname, SDFGClosure()))
        except DaceRecursionError:  # Parsing failed in a nested context, raise
            raise
        except Exception as ex:  # Parsing failed (anything can happen here)
            optional_qname = ''
            if qualname is not None:
                optional_qname = f' ("{qualname}")'
            warnings.warn(
                f'Preprocessing SDFGConvertible {value}{optional_qname} failed with {type(ex).__name__}: {ex}'
            )
            if Config.get_bool('frontend', 'raise_nested_parsing_errors'):
                raise
            if id(value) in self.closure.closure_sdfgs:
                del self.closure.closure_sdfgs[id(value)]
            # Return old call AST instead
            if not hasattr(node.func, 'oldnode'):
                raise
            node.func = node.func.oldnode.func

            return self.generic_visit(node)
Beispiel #8
0
    def visit_Call(self, node: ast.Call):
        is_attrsub = False
        if isinstance(node.func, (ast.Attribute, ast.Subscript)):
            is_attrsub = True
            with self.attrsub_load_context():
                node.func = self.visit_Attribute_or_Subscript(
                    node.func, call_context=True)

            # TODO: need a way to rewrite ast of attribute and subscript args,
            #  and to process these separately from outer rewrite

        node.args = self._get_replacement_args(node.args, is_attrsub, False)
        node.keywords = self._get_replacement_args(node.keywords, is_attrsub,
                                                   True)

        # in order to ensure that the args are processed with appropriate active scope,
        # we need to push current active scope before processing the args and pop after
        # (pop happens on function return as opposed to in tracer)
        node.func = ast.Call(
            func=ast.Name(self.scope_pusher, ast.Load()),
            args=[node.func],
            keywords=[],
        )

        node = ast.Call(func=ast.Name(self.scope_popper, ast.Load()),
                        args=[node, ast.NameConstant(is_attrsub)],
                        keywords=[])

        if self.inside_attrsub_load_chain or not is_attrsub:
            return node

        replacement_node = ast.Call(func=ast.Name(self.end_tracer, ast.Load()),
                                    args=[node, ast.NameConstant(True)],
                                    keywords=[])
        ast.copy_location(replacement_node, node)
        return replacement_node
Beispiel #9
0
 def _wrap_call_if_functional(cls, call: ast.Call,
                              func_map: Dict[str, ast.FunctionDef]) -> None:
     """ Wrap the function call with a cache check if it's functional """
     # TODO: Use better function identifier than name
     func_name = call.func.id
     # TODO: Parse the args and identify if all functional
     args, kws = call.args, call.keywords
     if Functional.function(func_map.get(func_name), func_map):
         _logger.debug(f'{func_name} is functional')
         # TODO: Possibly only cache if it takes longer than X seconds
         call.args.append(call.func)
         call.func = ast.Name(id='cached_func',
                              ctx=ast.Load(),
                              lineno=call.func.lineno,
                              col_offset=call.func.col_offset)
     else:
         # TODO: Recursively cache non-functional methods that are called
         _logger.debug(f'{call.func.id} is not functional')
Beispiel #10
0
    def visit_Call(self, node: ast.Call):
        # Struct initializer
        name = astutils.rname(node.func)
        if name not in self._structs:
            return self.generic_visit(node)

        # Parse name and fields
        struct = self._structs[name]
        name = struct.name
        fields = {astutils.rname(arg.arg): arg.value for arg in node.keywords}
        if tuple(sorted(fields.keys())) != tuple(sorted(struct.fields.keys())):
            raise SyntaxError('Mismatch in fields in struct definition')

        # Create custom node
        #new_node = astutils.StructInitializer(name, fields)
        #return ast.copy_location(new_node, node)

        node.func = ast.copy_location(
            ast.Name(id='__DACESTRUCT_' + name, ctx=ast.Load()), node.func)

        return node
Beispiel #11
0
    def get_collection(self, md: EventCollectionSpecification,
                       call_node: ast.Call):
        r'''
        Return a cpp ast for accessing the jet collection with the given arguments.
        '''
        # Get the name jet collection to look at.
        if len(call_node.args) != 1:
            raise ValueError(
                f"Calling {md.name} - only one argument is allowed")
        if not isinstance(call_node.args[0], ast.Str):
            raise ValueError(
                f"Calling {md.name} - only acceptable argument is a string")

        # Fill in the CPP block next.
        r = cpp_ast.CPPCodeValue()
        r.args = [
            'collection_name',
        ]
        r.include_files += md.include_files
        r.link_libraries += md.libraries

        r.running_code += self.get_running_code(md.container_type)
        r.result = 'result'

        if issubclass(type(md.container_type),
                      event_collection_collection_container):
            r.result_rep = lambda scope: crep.cpp_collection(
                unique_name(md.name.lower()),
                scope=scope,
                collection_type=md.container_type)  # type: ignore
        else:
            r.result_rep = lambda scope: crep.cpp_variable(
                unique_name(md.name.lower()),
                scope=scope,
                cpp_type=md.container_type)

        # Replace it as the function that is going to get called.
        call_node.func = r  # type: ignore

        return call_node
Beispiel #12
0
    def update_callable_after_validation(self, call: ast.Call, callable_id: str, callable_target: Callable):
        # if the arguments are not generic, include the specified method in the symbol table
        if (isinstance(callable_target, IBuiltinMethod)
                and callable_target.identifier != callable_id
                and callable_target.raw_identifier == callable_id
                and callable_target.identifier not in self.symbols):
            self.symbols[callable_target.identifier] = callable_target
            call.func = ast.Name(lineno=call.func.lineno, col_offset=call.func.col_offset,
                                 ctx=ast.Load(), id=callable_target.identifier)

        # validates if metadata matches callable's requirements
        if hasattr(callable_target, 'requires_storage') and callable_target.requires_storage:
            if not self._metadata.has_storage:
                self._log_error(
                    CompilerError.MetadataInformationMissing(
                        line=call.func.lineno, col=call.func.col_offset,
                        symbol_id=callable_target.identifier,
                        metadata_attr_id='has_storage'
                    )
                )
            else:
                self._current_method.set_storage()
Beispiel #13
0
def build_CPPCodeValue(spec: CPPCodeSpecification,
                       call_node: ast.Call) -> ast.Call:
    '''
    Given the specification for a C++ code block, invoked as a function in our AST, replace
    the call node with a cpp spec callback AST so the C++ code is properly inserted into the
    call stream.


    Args:
        spec (CPPCodeSpecification): The specification, including the code, that we should insert at this call node
        call_node (ast.Call): The call node (with arguments) that we are going to replace with this
        C++ code.

    Raises:
        ValueError: Raised if something is wrong with the call site

    Returns:
        [type]: The C++ ast that can easily be emitted as code
    '''

    if len(call_node.args) != len(spec.arguments):
        raise ValueError(
            f"The call of {spec.name}({', '.join(spec.arguments)}) has insufficient arguments ({len(call_node.args)})."
        )

    if isinstance(call_node.func,
                  ast.Attribute) and spec.method_object is None:
        raise ValueError(
            f"The {spec.name} is a function, but it is invoked like a method.")

    if isinstance(call_node.func, ast.Name) and spec.method_object is not None:
        raise ValueError(
            f"The {spec.name} is a method, but it is invoked like a function.")

    # Create an AST to hold onto all of this.
    r = CPPCodeValue()
    # We need TVector2 included here
    r.include_files += spec.include_files

    # We need all four arguments pushed through.
    r.args = spec.arguments

    # The code is three steps
    r.running_code += spec.code
    r.result = spec.result
    if spec.cpp_return_is_collection:
        r.result_rep = lambda scope: cpp_collection(
            unique_name(spec.name),
            scope=scope,
            collection_type=ctyp.collection(ctyp.terminal(spec.cpp_return_type)
                                            ))  # type: ignore
    else:
        r.result_rep = lambda scope: cpp_variable(unique_name(spec.name),
                                                  scope=scope,
                                                  cpp_type=ctyp.terminal(
                                                      spec.cpp_return_type))

    # If this is a mehtod, copy the info over to generate the obj reference.
    if spec.method_object is not None:
        r.replacement_instance_obj = (spec.method_object,
                                      call_node.func.value.id)  # type: ignore

    call_node.func = r  # type: ignore
    return call_node