/* @flow */ 
 | 
  
 | 
import VNode, { cloneVNode } from './vnode' 
 | 
import { createElement } from './create-element' 
 | 
import { resolveInject } from '../instance/inject' 
 | 
import { normalizeChildren } from '../vdom/helpers/normalize-children' 
 | 
import { resolveSlots } from '../instance/render-helpers/resolve-slots' 
 | 
import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots' 
 | 
import { installRenderHelpers } from '../instance/render-helpers/index' 
 | 
  
 | 
import { 
 | 
  isDef, 
 | 
  isTrue, 
 | 
  hasOwn, 
 | 
  camelize, 
 | 
  emptyObject, 
 | 
  validateProp 
 | 
} from '../util/index' 
 | 
  
 | 
export function FunctionalRenderContext ( 
 | 
  data: VNodeData, 
 | 
  props: Object, 
 | 
  children: ?Array<VNode>, 
 | 
  parent: Component, 
 | 
  Ctor: Class<Component> 
 | 
) { 
 | 
  const options = Ctor.options 
 | 
  // ensure the createElement function in functional components 
 | 
  // gets a unique context - this is necessary for correct named slot check 
 | 
  let contextVm 
 | 
  if (hasOwn(parent, '_uid')) { 
 | 
    contextVm = Object.create(parent) 
 | 
    // $flow-disable-line 
 | 
    contextVm._original = parent 
 | 
  } else { 
 | 
    // the context vm passed in is a functional context as well. 
 | 
    // in this case we want to make sure we are able to get a hold to the 
 | 
    // real context instance. 
 | 
    contextVm = parent 
 | 
    // $flow-disable-line 
 | 
    parent = parent._original 
 | 
  } 
 | 
  const isCompiled = isTrue(options._compiled) 
 | 
  const needNormalization = !isCompiled 
 | 
  
 | 
  this.data = data 
 | 
  this.props = props 
 | 
  this.children = children 
 | 
  this.parent = parent 
 | 
  this.listeners = data.on || emptyObject 
 | 
  this.injections = resolveInject(options.inject, parent) 
 | 
  this.slots = () => { 
 | 
    if (!this.$slots) { 
 | 
      normalizeScopedSlots( 
 | 
        data.scopedSlots, 
 | 
        this.$slots = resolveSlots(children, parent) 
 | 
      ) 
 | 
    } 
 | 
    return this.$slots 
 | 
  } 
 | 
  
 | 
  Object.defineProperty(this, 'scopedSlots', ({ 
 | 
    enumerable: true, 
 | 
    get () { 
 | 
      return normalizeScopedSlots(data.scopedSlots, this.slots()) 
 | 
    } 
 | 
  }: any)) 
 | 
  
 | 
  // support for compiled functional template 
 | 
  if (isCompiled) { 
 | 
    // exposing $options for renderStatic() 
 | 
    this.$options = options 
 | 
    // pre-resolve slots for renderSlot() 
 | 
    this.$slots = this.slots() 
 | 
    this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots) 
 | 
  } 
 | 
  
 | 
  if (options._scopeId) { 
 | 
    this._c = (a, b, c, d) => { 
 | 
      const vnode = createElement(contextVm, a, b, c, d, needNormalization) 
 | 
      if (vnode && !Array.isArray(vnode)) { 
 | 
        vnode.fnScopeId = options._scopeId 
 | 
        vnode.fnContext = parent 
 | 
      } 
 | 
      return vnode 
 | 
    } 
 | 
  } else { 
 | 
    this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization) 
 | 
  } 
 | 
} 
 | 
  
 | 
installRenderHelpers(FunctionalRenderContext.prototype) 
 | 
  
 | 
export function createFunctionalComponent ( 
 | 
  Ctor: Class<Component>, 
 | 
  propsData: ?Object, 
 | 
  data: VNodeData, 
 | 
  contextVm: Component, 
 | 
  children: ?Array<VNode> 
 | 
): VNode | Array<VNode> | void { 
 | 
  const options = Ctor.options 
 | 
  const props = {} 
 | 
  const propOptions = options.props 
 | 
  if (isDef(propOptions)) { 
 | 
    for (const key in propOptions) { 
 | 
      props[key] = validateProp(key, propOptions, propsData || emptyObject) 
 | 
    } 
 | 
  } else { 
 | 
    if (isDef(data.attrs)) mergeProps(props, data.attrs) 
 | 
    if (isDef(data.props)) mergeProps(props, data.props) 
 | 
  } 
 | 
  
 | 
  const renderContext = new FunctionalRenderContext( 
 | 
    data, 
 | 
    props, 
 | 
    children, 
 | 
    contextVm, 
 | 
    Ctor 
 | 
  ) 
 | 
  
 | 
  const vnode = options.render.call(null, renderContext._c, renderContext) 
 | 
  
 | 
  if (vnode instanceof VNode) { 
 | 
    return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext) 
 | 
  } else if (Array.isArray(vnode)) { 
 | 
    const vnodes = normalizeChildren(vnode) || [] 
 | 
    const res = new Array(vnodes.length) 
 | 
    for (let i = 0; i < vnodes.length; i++) { 
 | 
      res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext) 
 | 
    } 
 | 
    return res 
 | 
  } 
 | 
} 
 | 
  
 | 
function cloneAndMarkFunctionalResult (vnode, data, contextVm, options, renderContext) { 
 | 
  // #7817 clone node before setting fnContext, otherwise if the node is reused 
 | 
  // (e.g. it was from a cached normal slot) the fnContext causes named slots 
 | 
  // that should not be matched to match. 
 | 
  const clone = cloneVNode(vnode) 
 | 
  clone.fnContext = contextVm 
 | 
  clone.fnOptions = options 
 | 
  if (process.env.NODE_ENV !== 'production') { 
 | 
    (clone.devtoolsMeta = clone.devtoolsMeta || {}).renderContext = renderContext 
 | 
  } 
 | 
  if (data.slot) { 
 | 
    (clone.data || (clone.data = {})).slot = data.slot 
 | 
  } 
 | 
  return clone 
 | 
} 
 | 
  
 | 
function mergeProps (to, from) { 
 | 
  for (const key in from) { 
 | 
    to[camelize(key)] = from[key] 
 | 
  } 
 | 
} 
 |