invokeFunction(fn, args, content) {
    var block = new nodes.Block(fn.block.parent);

    // Clone the function body
    // to prevent mutation of subsequent calls
    var body = fn.block.clone(block);

    // mixin block
    var mixinBlock = this.stack.currentFrame.block;

    // new block scope
    this.stack.push(new Frame(block));
    var scope = this.currentScope;

    // normalize arguments
    if ('arguments' != args.nodeName) {
      var expr = new nodes.Expression;
      expr.push(args);
      args = nodes.Arguments.fromExpression(expr);
    }

    // arguments local
    scope.add(new nodes.Ident('arguments', args));

    // mixin scope introspection
    scope.add(new nodes.Ident('mixin', this.return
      ? nodes.false
      : new nodes.String(mixinBlock.nodeName)));

    // current property
    if (this.property) {
      var prop = this.propertyExpression(this.property, fn.name);
      scope.add(new nodes.Ident('current-property', prop));
    } else {
      scope.add(new nodes.Ident('current-property', nodes.null));
    }

    // current call stack
    var expr = new nodes.Expression;
    for (var i = this.calling.length - 1; i--;) {
      expr.push(new nodes.Literal(this.calling[i]));
    };
    scope.add(new nodes.Ident('called-from', expr));

    // inject arguments as locals
    var i = 0
      , len = args.nodes.length;
    fn.params.nodes.forEach(function (node) {
      // rest param support
      if (node.rest) {
        node.val = new nodes.Expression;
        for (; i < len; ++i) node.val.push(args.nodes[i]);
        node.val.preserve = true;
        node.val.isList = args.isList;
        // argument default support
      } else {
        var arg = args.map[node.name] || args.nodes[i++];
        node = node.clone();
        if (arg) {
          arg.isEmpty ? args.nodes[i - 1] = this.visit(node) : node.val = arg;
        } else {
          args.push(node.val);
        }

        // required argument not satisfied
        if (node.val.isNull) {
          throw new Error('argument "' + node + '" required for ' + fn);
        }
      }

      scope.add(node);
    }, this);

    // mixin block
    if (content) scope.add(new nodes.Ident('block', content, true));

    // invoke
    return this.invoke(body, true, fn.filename);
  }
Invoke `fn` with `args`.

@param {Function} fn
@param {Array} args
@return {Node}
@api private