Exemplo n.º 1
0
def vcall(name, args, ci):
    """
    \return the IR for a non-static Babel virtual method call
    """
    try:
        cdecl = ci.epv.find_method(name)
        epv_type = ci.epv.get_type()
        epv = ir.Get_struct_item(ci.obj, ir.Deref(args[0]),
                                 ir.Struct_item(epv_type, 'd_epv'))
    except:
        if False:  # FIXME no_contracts and no_hooks:
            return ir.Call('_'.join(ci.co.qualified_name + [name]), args)
        else:
            cdecl = ci.epv.find_static_method(name)
            epv_type = ci.epv.get_sepv_type()
            epv = '_getSEPV()'

    # this is part of an ugly hack to make sure that self is
    # dereferenced as self->d_object (by setting attr of self to the
    # unused value of 'pure')
    if ci.co.is_interface() and args:
        _, attrs, type_, id_, arguments, doc = cdecl
        _, attrs0, mode0, type0, name0 = arguments[0]
        arguments = [ir.Arg([ir.pure], mode0, type0, name0)] + arguments[1:]
        cdecl = ir.Fn_decl(attrs, type_, id_, arguments, doc)

    return ir.Call(
        ir.Deref(
            ir.Get_struct_item(
                epv_type, ir.Deref(epv),
                ir.Struct_item(ir.Pointer_type(cdecl), 'f_' + name))), args)
Exemplo n.º 2
0
def rewrite_pow(expr):
    coeff = expr.right
    base = expr.left
    if coeff == ir.Zero:
        return ir.One
    elif base == ir.Zero:
        # checking for weird errors more than anything
        if coeff.constant:
            if operator.lt(coeff.value, 0):
                # this isn't intended to catch all edge cases, just an obvious
                # one that may come up after folding
                msg = f"raises 0 to a negative power {expr}."
                raise CompilerError(msg)
            else:
                return ir.Zero
    elif coeff == ir.One:
        return expr.left
    elif coeff == ir.IntConst(-1):
        op = "/=" if expr.in_place else "/"
        return ir.BinOp(ir.One, expr.left, op)
    elif coeff == ir.IntConst(-2):
        op = "/=" if expr.in_place else "/"
        return ir.BinOp(ir.One, ir.BinOp(expr.left, expr.left, "*"), op)
    elif coeff == ir.IntConst(2):
        op = "*=" if expr.in_place else "*"
        return ir.BinOp(expr.left, expr.left, op)
    elif coeff == ir.FloatConst(0.5):
        return ir.Call("sqrt", (expr.left,), ())
    else:
        return expr
Exemplo n.º 3
0
 def visit_call_expression(self, node, target, *args):
     this = self.get_id()
     arg_ids = [self.get_id() for _ in node.args]
     compute_this = linearize([self.visit(node.obj, ir.EXPR, this)])
     compute_args = [
         linearize([self.visit(arg, ir.EXPR, trg)])
         for arg, trg in zip(node.args, arg_ids)
     ]
     if all_local(compute_this):
         compute_args = [compute_this] + compute_args
         compute_this = []
     compute_args.sort(key=total_complexity, reverse=True)
     if target == ir.EXPR:
         return compute_this + compute_args + [
             ir.Call(node.method_owner + '.' + node.method,
                     [this] + arg_ids, args[0]),
         ]
     else:
         val = self.get_id()
         return compute_this + compute_args + [
             ir.Call(node.method_owner + '.' + node.method,
                     [this] + arg_ids, val),
             ir.CJumpBool(val, args[0], args[1]),
         ]
Exemplo n.º 4
0
    def visit_Call(
            self,
            node: ast.Call) -> typing.Union[ir.ValueRef, ir.NameRef, ir.Call]:
        if isinstance(node.func, ast.Name):
            func_name = ir.NameRef(node.func.id)
        else:
            func_name = self.visit(node.func)
        args = tuple(self.visit(arg) for arg in node.args)
        keywords = tuple(
            (kw.arg, self.visit(kw.value)) for kw in node.keywords)
        # replace call should handle folding of casts

        call_ = ir.Call(func_name, args, keywords)
        # Todo: need a way to identify array creation
        repl = replace_call(call_)
        return repl
Exemplo n.º 5
0
def build_function_call(ci, cdecl, static):
    '''
    Build an IR expression that consists of the EPV lookup for a
    possibly virtual function call using Babel IOR.
    '''
    #if final:
    # final : Final methods are the opposite of virtual. While
    # they may still be inherited by child classes, they
    # cannot be overridden.

    # static call
    #The problem with this is that e.g. C++ symbols usere different names
    #We should modify Babel to generate  __attribute__ ((weak, alias ("__entry")))
    #    callee = '_'.join(['impl']+symbol_table.prefix+[ci.epv.name,Name])
    if static:
        # static : Static methods are sometimes called "class
        # methods" because they are part of a class, but do not
        # depend on an object instance. In non-OO languages, this
        # means that the typical first argument of an instance is
        # removed. In OO languages, these are mapped directly to
        # an Java or C++ static method.
        epv_type = ci.epv.get_type()
        obj_type = ci.obj
        callee = ir.Get_struct_item(
            epv_type, ir.Deref(ir.Call('_getSEPV', [])),
            ir.Struct_item(ir.Pointer_type(cdecl),
                           'f_' + ir.fn_decl_id(cdecl)))

    else:
        # dynamic virtual method call
        epv_type = ci.epv.get_type()
        obj_type = ci.obj
        callee = ir.Deref(
            ir.Get_struct_item(
                epv_type,
                ir.Deref(
                    ir.Get_struct_item(obj_type, ir.Deref('self'),
                                       ir.Struct_item(epv_type, 'd_epv'))),
                ir.Struct_item(ir.Pointer_type(cdecl),
                               'f_' + ir.fn_decl_id(cdecl))))

    return callee
Exemplo n.º 6
0
    def generate(self, node, scope=ChapelFile()):
        """
        This code generator is a bit unusual in that it accepts a
        hybrid of \c sidl and \c ir nodes.
        """
        def gen(node):
            return self.generate(node, scope)

        def new_def(s):
            return scope.new_def(s)

        def new_header_def(s):
            return scope.new_header_def(s)

        @accepts(str, list, str)
        def new_scope(prefix, body, suffix='\n'):
            '''used for things like if, while, ...'''
            comp_stmt = ChapelFile(parent=scope, relative_indent=4)
            s = str(self.generate(body, comp_stmt))
            return new_def(scope._sep.join(['', prefix + s, suffix]))

        @accepts(str, str, str)
        def new_scope1(prefix, body, suffix):
            '''used for things like enumerator'''
            return scope.new_header_def(''.join([prefix, body, suffix]) + ';')

        def gen_comma_sep(defs):
            return self.gen_in_scope(
                defs, scope.sub_scope(relative_indent=1, separator=','))

        def gen_semicolon_sep(defs):
            return self.gen_in_scope(
                defs, scope.sub_scope(relative_indent=2,
                                      separator=';\n')) + ';'

        def gen_ws_sep(defs):
            return self.gen_in_scope(
                defs, scope.sub_scope(relative_indent=0, separator=' '))

        def gen_dot_sep(defs):
            return self.gen_in_scope(
                defs, scope.sub_scope(relative_indent=0, separator='.'))

        def tmap(f, l):
            return tuple(map(f, l))

        def gen_comment(DocComment):
            return gen_doc_comment(DocComment, scope)

        def gen_attrs(attrs):
            return sep_by(' ', attrs)

        def drop_this(args):
            if args <> []:
                if args[0] == (ir.arg, [], ir.in_, ir.void_ptr, '_this'):
                    return args[1:]
            return args

        cbool = 'chpl_bool'
        int32 = 'int32_t'
        int64 = 'int64_t'
        fcomplex = '_complex64'
        dcomplex = '_complex128'

        val = self.generate_non_tuple(node, scope)
        if val <> None:
            return val

        with match(node):
            if (ir.fn_defn, Attrs, (ir.primitive_type, 'void'), Name, Args,
                    Body, DocComment):
                new_scope(
                    '%s%sproc %s(%s) {' %
                    (gen_comment(DocComment), gen_attrs(Attrs), gen(Name),
                     gen_comma_sep(drop_this(Args))), Body, '}')
                new_def('')

            elif (ir.fn_defn, Attrs, Type, Name, Args, Body, DocComment):
                new_scope(
                    '%s%sproc %s(%s): %s {' %
                    (gen_comment(DocComment), gen_attrs(Attrs), gen(Name),
                     gen_comma_sep(drop_this(Args)), gen(Type)), Body, '}')
                new_def('')

            elif (ir.fn_decl, Attrs, (ir.primitive_type, 'void'), Name, Args,
                  DocComment):
                attrs = 'extern ' if 'extern' in Attrs else ''
                new_def('%sproc %s(%s)' %
                        (attrs, gen(Name), gen_comma_sep(Args)))

            elif (ir.fn_decl, Attrs, Type, Name, Args, DocComment):
                attrs = 'extern ' if 'extern' in Attrs else ''
                new_def('%sproc %s(%s): %s' %
                        (attrs, gen(Name), gen_comma_sep(Args), gen(Type)))

            elif (ir.call, (ir.deref, (ir.get_struct_item, S, _,
                                       (ir.struct_item, _, Name))), Args):
                # We can't do a function pointer call in Chapel
                # Emit a C stub for that
                import pdb
                pdb.set_trace()
                _, s_id, _, _ = S
                retval_arg = generate_method_stub(scope, node, s_id)
                stubname = '_'.join(
                    [epv_qname(s_id),
                     re.sub('^f_', '', Name), 'stub'])
                if retval_arg:
                    scope.pre_def(gen(ir.Var_decl(retval_arg, '_retval')))
                    scope.pre_def(
                        gen(
                            ir.Assignment('_retval',
                                          ir.Call(stubname,
                                                  Args + retval_arg))))
                    return '_retval'
                else:
                    return gen(ir.Call(stubname, Args))

            elif (ir.call, (ir.get_struct_item, S, _, (ir.struct_item, _,
                                                       Name)), Args):
                # We can't do a function pointer call in Chapel
                # Emit a C stub for that
                import pdb
                pdb.set_trace()
                _, s_id, _, _ = S
                retval_arg = generate_method_stub(scope, node, s_id)
                stubname = '_'.join(
                    [epv_qname(s_id),
                     re.sub('^f_', '', Name), 'stub'])
                if retval_arg:
                    scope.pre_def(gen(ir.Var_decl(retval_arg, '_retval')))
                    scope.pre_def(
                        gen(
                            ir.Assignment('_retval',
                                          ir.Call(stubname,
                                                  Args + retval_arg))))
                    return '_retval'
                else:
                    return gen(ir.Call(stubname, Args))

            elif (ir.new, Type, Args):
                return 'new %s(%s)' % (gen(Type), gen_comma_sep(Args))

            elif (ir.const, Type):
                return '/*FIXME: CONST*/' + gen(Type)

            # Special handling of rarray types
            elif (ir.arg, Attrs, Mode, (ir.rarray, Scalar_type, Dimension,
                                        Extents), Name):
                (arg_mode, arg_name) = (gen(Mode), gen(Name))
                # rarray type will include a new domain variable definition
                arg_type = '[?%s_dom] %s' % (Name, gen(Scalar_type))
                return '%s %s: %s' % (arg_mode, arg_name, arg_type)

            elif (ir.arg, Attrs, ir.inout, Type, '_ex'):
                return '%s %s: opaque /*%s*/' % (gen(Mode), gen('_ex'),
                                                 gen(Type))

            elif (ir.arg, Attrs, Mode, Type, Name):
                return '%s %s: %s' % (gen(Mode), gen(Name), gen(Type))

            elif (sidlir.class_, (ir.scoped_id, Prefix, Name, Ext), Extends,
                  Implements, Invariants, Methods, DocComment):
                return '%sclass %s' % (gen_comment(DocComment), '.'.join(
                    Prefix + ['_'.join(Prefix + [Name + Ext])]))

            elif (sidlir.array, [], [], []):
                print '** WARNING: deprecated rule, use a sidl__array struct instead'
                return 'sidl.Array(opaque, sidl__array) /* DEPRECATED */'

            elif (sidlir.array, Scalar_type, Dimension, Orientation):
                print '** WARNING: deprecated rule, use a sidl_*__array struct instead'
                if ir.is_scoped_id(Scalar_type):
                    ctype = 'BaseInterface'
                else:
                    ctype = Scalar_type[1]
                #scope.cstub.optional.add('#include <sidl_%s_IOR.h>'%ctype)
                return 'sidl.Array(%s, sidl_%s__array) /* DEPRECATED */' % (
                    gen(Scalar_type), ctype)

            elif (ir.pointer_type, (ir.const, (ir.primitive_type, ir.char))):
                return "string"

            elif (ir.pointer_type, (ir.primitive_type, ir.void)):
                return "opaque"

            elif (ir.pointer_type, Type):
                # ignore wrongfully introduced pointers
                # -> actually I should fix generate_method_stub instead
                return gen(Type)

            elif (ir.typedef_type, cbool):
                return "bool"

            elif (ir.typedef_type, 'sidl_bool'):
                return "bool"

            elif (ir.typedef_type, int32):
                return "int(32)"

            elif (ir.typedef_type, int64):
                return "int(64)"

            elif (ir.typedef_type, fcomplex):
                return "complex(64)"

            elif (ir.typedef_type, dcomplex):
                return "complex(128)"

            elif (ir.struct, (ir.scoped_id, Prefix, Name, Ext), Items,
                  DocComment):
                #if Name[:4] =='sidl':import pdb; pdb.set_trace()
                #print 'prefix %s, name %s, ext %s' %(Prefix, Name, Ext)
                return '.'.join(
                    list(Prefix) + ['_'.join(list(Prefix) + [Name + Ext])])

            elif (ir.struct, Name, Items, DocComment):
                # special rule for handling SIDL arrays
                scalar_t = self.is_sidl_array(Name)
                if scalar_t:
                    #scope.cstub.optional.add('#include <sidl_%s_IOR.h>'%ctype)
                    return 'sidl.Array(%s, %s)' % (scalar_t, Name)

                if Name == 'sidl__array':
                    # Generic array We
                    # need to use opaque as the Chapel representation
                    # because otherwise Chapel would start copying
                    # arguments and thus mess with the invariant that
                    # genarr.d_metadata == genarr
                    return 'opaque /* array< > */'

                # some other struct
                if Name[0] == '_' and Name[1] == '_':
                    # by convention, Chapel generates structs as
                    # struct __foo { ... } foo;
                    return Name[2:]
                return Name

            elif (ir.get_struct_item, _, StructName, (ir.struct_item, _,
                                                      Item)):
                return "%s.%s" % (gen(StructName), gen(Item))

            elif (ir.set_struct_item, _, (ir.deref, StructName),
                  (ir.struct_item, _, Item), Value):
                return gen(StructName) + '.' + gen(Item) + ' = ' + gen(Value)

            elif (ir.struct_item, (sidlir.array, Scalar_type, Dimension,
                                   Extents), Name):
                return '%s: []%s' % (gen(Name), gen(Scalar_type))

            elif (ir.type_decl, (ir.struct, Name, Items, DocComment)):
                itemdecls = gen_semicolon_sep(
                    map(lambda i: ir.Var_decl(i[1], i[2]), Items))
                return gen_comment(DocComment) + str(
                    new_scope1('record %s {\n' % gen(Name), itemdecls, '\n}'))

            elif (ir.var_decl, Type, Name):
                return scope.new_header_def('var %s: %s' %
                                            (gen(Name), gen(Type)))

            elif (ir.var_decl_init, (ir.typedef_type, "inferred_type"), Name,
                  Initializer):
                return scope.new_header_def('var %s = %s' %
                                            (gen(Name), gen(Initializer)))

            elif (ir.var_decl_init, Type, Name, Initializer):
                return scope.new_header_def(
                    'var %s: %s = %s' %
                    (gen(Name), gen(Type), gen(Initializer)))

            elif (ir.enum, Name, Items, DocComment):
                return unscope(scope, Name)
            elif (ir.cast, Type, Expr):
                return '%s:%s' % (gen(Expr), gen(Type))

            elif (ir.type_decl, (ir.enum, Name, Items, DocComment)):
                # Manually transform the Items
                enum_transformed = False
                used_states = []
                for loop_item in Items:
                    if (len(loop_item) == 3):
                        used_states.append(loop_item[2])
                    else:
                        enum_transformed = True

                items_to_use = Items
                if enum_transformed:
                    # Explicitly initialize the enum values, since
                    # Chapel enums start at 1
                    new_items = []
                    avail_state = 0
                    for loop_item in Items:
                        if (len(loop_item) == 3):
                            new_items.append(loop_item)
                        else:
                            while avail_state in used_states:
                                avail_state = avail_state + 1
                            new_items.append(
                                ir.Enumerator_value(loop_item[1], avail_state))
                            used_states.append(avail_state)
                    items_to_use = new_items

                return new_scope1('enum %s {' % gen(Name),
                                  gen_comma_sep(items_to_use), '}')

            elif (ir.import_, Name):
                new_def('use %s;' % Name)

                # ignore these two -- Chapel's mode attribute handling
                # should automatically take care of doing (de)referencing
            elif (ir.deref, (ir.pointer_expr, Name)):
                return gen(Name)
            elif (ir.pointer_expr, (ir.deref, Name)):
                return gen(Name)
            elif (ir.pointer_expr, Name):
                return gen(Name)
            elif (ir.deref, Name):
                return gen(Name)

            elif (ir.float, N):
                return str(N) + ':real(32)'

            elif (ir.double, N):
                return str(N) + ':real(64)'

            elif (sidlir.custom_attribute, Id):
                return gen(Id)
            elif (sidlir.method_name, Id, Extension):
                return gen(Id) + gen(Extension)
            elif (sidlir.scoped_id, Prefix, Name, Ext):
                return '.'.join(Prefix + [Name])

            elif (ir.typedef_type, 'sidl_enum'):
                return 'int(64)'

            else:
                return super(ChapelCodeGenerator, self).generate(node, scope)
        return scope
Exemplo n.º 7
0
    def generate_server_method(self, symbol_table, method, ci):
        """
        Generate server code for a method interface.  This function
        generates a C-callable skeleton for the method and generates a
        Skeleton of Chapel code complete with splicer blocks for the
        user to fill in.
        
        \param method        s-expression of the method's SIDL declaration
        \param symbol_table  the symbol table of the SIDL file
        \param ci            a ClassInfo object
        """
        def convert_arg((arg, attrs, mode, typ, name)):
            """
            Extract name and generate argument conversions
            """
            iorname = name
            return iorname, (arg, attrs, mode, typ, name)

        # Chapel skeleton
        (Method, Type, (MName, Name, Extension), Attrs, Args, Except, From,
         Requires, Ensures, DocComment) = method

        #ior_args = drop_rarray_ext_args(Args)

        #        ci.epv.add_method((Method, Type, (MName,  Name, Extension), Attrs, ior_args,
        #                           Except, From, Requires, Ensures, DocComment))

        abstract = member_chk(sidlir.abstract, Attrs)
        static = member_chk(sidlir.static, Attrs)
        #final = member_chk(sidlir.static, Attrs)

        if abstract:
            # nothing to be done for an abstract function
            return

        decls = []
        pre_call = []
        call_args = []
        post_call = []
        ior_args = babel.lower_ir(symbol_table, Args, lower_scoped_ids=False)
        ctype = babel.lower_ir(symbol_table, Type, lower_scoped_ids=False)
        return_stmt = []
        skel = ci.chpl_skel
        opt = skel.cstub.optional
        qname = '_'.join(ci.co.qualified_name + [Name])
        callee = qname + '_impl'

        # Argument conversions
        # ---------------------

        # self
        this_arg = [] if static else [ir.Arg([], ir.in_, ir.void_ptr, '_this')]

        # IN
        map(
            lambda (arg, attr, mode, typ, name): conv.codegen(
                (strip(typ), deref(mode, typ, name)),
                ('chpl', strip(typ)), pre_call, skel, '_CHPL_' + name, typ),
            filter(incoming, ior_args))

        # OUT
        map(
            lambda (arg, attr, mode, typ, name): conv.codegen(
                (('chpl', strip(typ)), '_CHPL_' + name), strip(
                    typ), post_call, skel, '(*%s)' % name, typ),
            filter(outgoing, ior_args))

        # RETURN value type conversion -- treated just like an OUT argument
        rarg = (ir.arg, [], ir.out, ctype, '_retval')
        conv.codegen((('chpl', strip(ctype)), '_CHPL__retval'), strip(ctype),
                     post_call, skel, '_retval', ctype)
        chpl_rarg = conv.ir_arg_to_chpl(rarg)
        _, _, _, chpltype, _ = chpl_rarg
        if Type <> sidlir.void:
            decls.append(ir.Stmt(ir.Var_decl(ctype, '_retval')))

        # def pointerize_struct((arg, attr, mode, typ, name)):
        #   # FIXME: this is borked.. instead we should remove this
        #   # _and_ the code in codegenerator that strips the
        #   # pointer_type again
        #   if typ[0] == ir.struct:
        #         return (arg, attr, mode, (ir.pointer_type, typ), name)
        #   else: return (arg, attr, mode, typ, name)

        # chpl_args = map(pointerize_struct, map(conv.ir_arg_to_chpl, ior_args))
        chpl_args = map(conv.ir_arg_to_chpl, ior_args)

        # Proxy declarations / revised names of call arguments
        is_retval = True
        for (_,attrs,mode,chpl_t,name), (_,_,_,c_t,_) \
                in zip([chpl_rarg]+chpl_args, [rarg]+ior_args):

            if chpl_t <> c_t:
                is_struct = False
                proxy_t = chpl_t
                if c_t[0] == ir.pointer_type and c_t[1][0] == ir.struct:
                    # inefficient!!!
                    opt.add(str(c_gen(ir.Type_decl(chpl_t[1]))))
                    c_t = c_t[1]
                    is_struct = True
                    proxy_t = chpl_t[1]

                # FIXME see comment in chpl_to_ior
                name = '_CHPL_' + name
                decls.append(ir.Stmt(ir.Var_decl(proxy_t, name)))
                if (mode <> sidlir.in_ or is_struct
                        # TODO this should be handled by a conversion rule
                        or (mode == sidlir.in_ and
                            (c_t == ir.pt_fcomplex or c_t == ir.pt_dcomplex))):
                    name = ir.Pointer_expr(name)

            if name == 'self' and member_chk(ir.pure, attrs):
                # part of the hack for self dereferencing
                upcast = (
                    '({0}*)(((struct sidl_BaseInterface__object*)self)->d_object)'
                    .format(c_gen(c_t[1])))
                call_args.append(upcast)
            else:
                if is_retval: is_retval = False
                else: call_args.append(name)

        call_args.append('_ex')

        if not static:
            call_args = ['self->d_data'] + call_args

        # The actual function call
        if Type == sidlir.void:
            Type = ir.pt_void
            call = [ir.Stmt(ir.Call(callee, call_args))]
        else:
            if post_call:
                call = [
                    ir.Stmt(
                        ir.Assignment('_CHPL__retval',
                                      ir.Call(callee, call_args)))
                ]
                return_stmt = [ir.Stmt(ir.Return('_retval'))]
            else:
                call = [ir.Stmt(ir.Return(ir.Call(callee, call_args)))]

        #TODO: ior_args = drop_rarray_ext_args(Args)

        skeldefn = (ir.fn_defn, [], ctype, qname + '_skel',
                    babel.epv_args(Attrs, Args, ci.epv.symbol_table,
                                   ci.epv.name),
                    decls + pre_call + call + post_call + return_stmt,
                    DocComment)

        def skel_args((arg, attr, mode, typ, name)):
            # lower array args
            if typ[0] == sidlir.array:
                return arg, attr, mode, ir.pt_void, name
            # complex is always passed as a pointer since chpl 1.5
            elif mode == ir.in_ and typ[0] == ir.typedef_type and (
                    typ[1] == '_complex64' or typ[1] == '_complex128'):
                return arg, attr, mode, ir.Pointer_type(typ), name
            else:
                return arg, attr, mode, typ, name

        ex_arg = [ir.Arg([], ir.inout, babel.ir_baseinterface_type(), '_ex')]
        impl_args = this_arg + map(skel_args, chpl_args) + ex_arg
        impldecl = (ir.fn_decl, [], chpltype, callee, impl_args, DocComment)
        splicer = '.'.join(ci.epv.symbol_table.prefix + [ci.epv.name, Name])
        impldefn = (ir.fn_defn, ['export ' + callee], chpltype, Name,
                    impl_args, [
                        'set_to_null(_ex);',
                        '// DO-NOT-DELETE splicer.begin(%s)' % splicer,
                        '// DO-NOT-DELETE splicer.end(%s)' % splicer
                    ], DocComment)

        c_gen(skeldefn, ci.stub)
        c_gen(impldecl, ci.stub)
        upc_gen(impldefn, ci.impl)
Exemplo n.º 8
0
    def generate_client_method(self, symbol_table, method, ci, has_impl):
        """
        Generate client code for a method interface.
        \param method        s-expression of the method's SIDL declaration
        \param symbol_table  the symbol table of the SIDL file
        \param ci            a ClassInfo object

        This is currently a no-op, since the UPC calling convention is
        identical to the IOR.
        """
        (Method, Type, (MName, Name, Extension), Attrs, Args, Except, From,
         Requires, Ensures, DocComment) = method

        abstract = member_chk(sidlir.abstract, Attrs)
        #final = member_chk(sidlir.final, Attrs)
        static = member_chk(sidlir.static, Attrs)

        attrs = []
        if abstract:
            # we still need to output a stub for an abstract function,
            # since it might me a virtual function call through an
            # abstract interface
            pass
        if static: attrs.append(ir.static)

        ior_type = babel.lower_ir(symbol_table, Type)
        ior_args = babel.epv_args(Attrs, Args, symbol_table, ci.epv.name)
        call_args = map(lambda arg: ir.arg_id(arg), ior_args)
        cdecl = ir.Fn_decl(attrs, ior_type, Name + Extension, ior_args,
                           DocComment)
        qname = '_'.join(ci.co.qualified_name + [Name]) + Extension

        if self.server and has_impl:
            # if we are generating server code we can take a shortcut
            # and directly invoke the implementation
            modname = '_'.join(ci.co.symbol_table.prefix + ['Impl'])
            if not static:
                qname = '_'.join(ci.co.qualified_name + ['Impl'])
                # FIXME!
            callee = '_'.join([modname, ir.fn_decl_id(cdecl)])
        else:
            callee = babel.build_function_call(ci, cdecl, static)

        if Type == sidlir.void:
            call = [ir.Stmt(ir.Call(callee, call_args))]
        else:
            call = [ir.Stmt(ir.Return(ir.Call(callee, call_args)))]

        cdecl = ir.Fn_decl(attrs, ior_type, qname, ior_args, DocComment)
        cdefn = ir.Fn_defn(attrs, ior_type, qname, ior_args, call, DocComment)

        if static:
            # TODO: [performance] we would only need to put the
            # _externals variable into the _Stub.c, not necessarily
            # all the function definitions
            ci.stub.gen(cdecl)
            ci.stub.new_def('#pragma weak ' + qname)
            ci.stub.gen(cdefn)
        else:
            # FIXME: can't UPC handle the inline keyword??
            ci.stub.new_header_def('static inline')
            ci.stub.genh(cdefn)