Example #1
0
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
  Func* f = (m_pce == NULL)
    ? m_ue.newFunc(this, unit, m_id, m_line1, m_line2, m_base,
                   m_past, m_name, m_attrs, m_top, m_docComment,
                   m_params.size())
    : m_ue.newFunc(this, unit, preClass, m_line1, m_line2, m_base,
                   m_past, m_name, m_attrs, m_top, m_docComment,
                   m_params.size());
  f->shared()->m_info = m_info;
  for (unsigned i = 0; i < m_params.size(); ++i) {
    Func::ParamInfo pi;
    pi.setFuncletOff(m_params[i].funcletOff());
    pi.setDefaultValue(m_params[i].defaultValue());
    pi.setPhpCode(m_params[i].phpCode());
    pi.setTypeConstraint(m_params[i].typeConstraint());
    f->appendParam(m_params[i].ref(), pi);
  }
  f->shared()->m_localNames.create(m_localNames);
  f->shared()->m_numLocals = m_numLocals;
  f->shared()->m_numIterators = m_numIterators;
  f->m_maxStackCells = m_maxStackCells;
  ASSERT(m_maxStackCells > 0 && "You probably didn't set m_maxStackCells");
  f->shared()->m_staticVars = m_staticVars;
  f->shared()->m_ehtab = m_ehtab;
  f->shared()->m_fpitab = m_fpitab;
  f->shared()->m_isClosureBody = m_isClosureBody;
  f->shared()->m_isGenerator = m_isGenerator;
  f->shared()->m_isGeneratorFromClosure = m_isGeneratorFromClosure;
  f->shared()->m_userAttributes = m_userAttributes;
  f->shared()->m_builtinFuncPtr = m_builtinFuncPtr;
  return f;
}
Example #2
0
File: func.cpp Project: BwRy/hhvm
void Func::appendParam(bool ref, const Func::ParamInfo& info,
                       std::vector<ParamInfo>& pBuilder) {
  auto numParams = pBuilder.size();

  // When called by FuncEmitter, the least significant bit of m_paramCounts
  // are not yet being used as a variadic flag, so numParams() cannot be
  // used
  int qword = numParams / kBitsPerQword;
  int bit   = numParams % kBitsPerQword;
  assert(!info.isVariadic() || (m_attrs & AttrVariadicParam));
  uint64_t* refBits = &m_refBitVal;
  // Grow args, if necessary.
  if (qword) {
    if (bit == 0) {
      shared()->m_refBitPtr = (uint64_t*)
        realloc(shared()->m_refBitPtr, qword * sizeof(uint64_t));
    }
    refBits = shared()->m_refBitPtr + qword - 1;
  }

  if (bit == 0) {
    // The new word is either zerod or set to 1, depending on whether
    // we are one of the special builtins that takes variadic
    // reference arguments.  This is for use in the translator.
    *refBits = (m_attrs & AttrVariadicByRef) ? -1ull : 0;
  }

  assert(!(*refBits & (uint64_t(1) << bit)) == !(m_attrs & AttrVariadicByRef));
  *refBits &= ~(1ull << bit);
  *refBits |= uint64_t(ref) << bit;
  pBuilder.push_back(info);
}
Example #3
0
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
  Attr attrs = m_attrs;
  if (attrs & AttrPersistent &&
      (RuntimeOption::EvalJitEnableRenameFunction ||
       (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
    attrs = Attr(attrs & ~AttrPersistent);
  }

  Func* f = (m_pce == NULL)
    ? m_ue.newFunc(this, unit, m_id, m_line1, m_line2, m_base,
                   m_past, m_name, attrs, m_top, m_docComment,
                   m_params.size())
    : m_ue.newFunc(this, unit, preClass, m_line1, m_line2, m_base,
                   m_past, m_name, attrs, m_top, m_docComment,
                   m_params.size());
  f->shared()->m_info = m_info;
  f->shared()->m_returnType = m_returnType;
  std::vector<Func::ParamInfo> pBuilder;
  for (unsigned i = 0; i < m_params.size(); ++i) {
    Func::ParamInfo pi;
    pi.setFuncletOff(m_params[i].funcletOff());
    pi.setDefaultValue(m_params[i].defaultValue());
    pi.setPhpCode(m_params[i].phpCode());
    pi.setTypeConstraint(m_params[i].typeConstraint());
    pi.setUserAttributes(m_params[i].userAttributes());
    pi.setBuiltinType(m_params[i].builtinType());
    f->appendParam(m_params[i].ref(), pi, pBuilder);
  }
  f->shared()->m_params = pBuilder;
  f->shared()->m_localNames.create(m_localNames);
  f->shared()->m_numLocals = m_numLocals;
  f->shared()->m_numIterators = m_numIterators;
  f->m_maxStackCells = m_maxStackCells;
  ASSERT(m_maxStackCells > 0 && "You probably didn't set m_maxStackCells");
  f->shared()->m_staticVars = m_staticVars;
  f->shared()->m_ehtab = m_ehtab;
  f->shared()->m_fpitab = m_fpitab;
  f->shared()->m_isClosureBody = m_isClosureBody;
  f->shared()->m_isGenerator = m_isGenerator;
  f->shared()->m_isGeneratorFromClosure = m_isGeneratorFromClosure;
  f->shared()->m_userAttributes = m_userAttributes;
  f->shared()->m_builtinFuncPtr = m_builtinFuncPtr;
  f->shared()->m_nativeFuncPtr = m_nativeFuncPtr;
  return f;
}
Example #4
0
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
  bool isGenerated = isdigit(name->data()[0]) || needsStripInOut(name);

  Attr attrs = this->attrs;
  if (preClass && preClass->attrs() & AttrInterface) {
    attrs |= AttrAbstract;
  }
  if (!RuntimeOption::RepoAuthoritative) {
    if (RuntimeOption::EvalJitEnableRenameFunction) {
      attrs |= AttrInterceptable;
    } else {
      attrs = Attr(attrs & ~AttrInterceptable);
    }
  }
  if (attrs & AttrPersistent && !preClass) {
    if ((RuntimeOption::EvalJitEnableRenameFunction ||
         attrs & AttrInterceptable ||
         (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
      if (attrs & AttrBuiltin) {
        SystemLib::s_anyNonPersistentBuiltins = true;
      }
      attrs = Attr(attrs & ~AttrPersistent);
    }
  } else {
    assertx(preClass || !(attrs & AttrBuiltin));
  }
  if (!RuntimeOption::RepoAuthoritative) {
    // In non-RepoAuthoritative mode, any function could get a VarEnv because
    // of evalPHPDebugger.
    attrs |= AttrMayUseVV;
  } else if ((attrs & AttrInterceptable) &&
             !name->empty() &&
             !Func::isSpecial(name) &&
             !isClosureBody) {
    // intercepted functions need to pass all args through
    // to the interceptee
    attrs |= AttrMayUseVV;
  }
  if (isVariadic()) {
    attrs |= AttrVariadicParam;
    if (isVariadicByRef()) {
      attrs |= AttrVariadicByRef;
    }
  }

  assertx(!m_pce == !preClass);
  auto f = m_ue.newFunc(this, unit, name, attrs, params.size());

  f->m_isPreFunc = !!preClass;

  bool const needsExtendedSharedData =
    isNative ||
    line2 - line1 >= Func::kSmallDeltaLimit ||
    past - base >= Func::kSmallDeltaLimit ||
    m_numClsRefSlots > 3;

  f->m_shared.reset(
    needsExtendedSharedData
      ? new Func::ExtendedSharedData(preClass, base, past, line1, line2,
                                     top, !containsCalls, docComment)
      : new Func::SharedData(preClass, base, past,
                             line1, line2, top, !containsCalls, docComment)
  );

  f->init(params.size());

  if (auto const ex = f->extShared()) {
    ex->m_hasExtendedSharedData = true;
    ex->m_arFuncPtr = nullptr;
    ex->m_nativeFuncPtr = nullptr;
    ex->m_line2 = line2;
    ex->m_past = past;
    ex->m_returnByValue = false;
    ex->m_isMemoizeWrapper = false;
    ex->m_isMemoizeWrapperLSB = false;
    ex->m_actualNumClsRefSlots = m_numClsRefSlots;
  }

  std::vector<Func::ParamInfo> fParams;
  for (unsigned i = 0; i < params.size(); ++i) {
    Func::ParamInfo pi = params[i];
    if (pi.isVariadic()) {
      pi.builtinType = RuntimeOption::EvalHackArrDVArrs
        ? KindOfVec : KindOfArray;
    }
    f->appendParam(params[i].byRef, pi, fParams);
  }

  auto const originalFullName =
    (!originalFilename ||
     !RuntimeOption::RepoAuthoritative ||
     FileUtil::isAbsolutePath(originalFilename->slice())) ?
    originalFilename :
    makeStaticString(RuntimeOption::SourceRoot +
                     originalFilename->toCppString());

  f->shared()->m_localNames.create(m_localNames);
  f->shared()->m_numLocals = m_numLocals;
  f->shared()->m_numIterators = m_numIterators;
  f->m_maxStackCells = maxStackCells;
  f->shared()->m_staticVars = staticVars;
  f->shared()->m_ehtab = ehtab;
  f->shared()->m_fpitab = fpitab;
  f->shared()->m_isClosureBody = isClosureBody;
  f->shared()->m_isAsync = isAsync;
  f->shared()->m_isGenerator = isGenerator;
  f->shared()->m_isPairGenerator = isPairGenerator;
  f->shared()->m_userAttributes = userAttributes;
  f->shared()->m_retTypeConstraint = retTypeConstraint;
  f->shared()->m_retUserType = retUserType;
  f->shared()->m_originalFilename = originalFullName;
  f->shared()->m_isGenerated = isGenerated;
  f->shared()->m_repoReturnType = repoReturnType;
  f->shared()->m_repoAwaitedReturnType = repoAwaitedReturnType;
  f->shared()->m_isMemoizeWrapper = isMemoizeWrapper;
  f->shared()->m_isMemoizeWrapperLSB = isMemoizeWrapperLSB;
  f->shared()->m_numClsRefSlots = m_numClsRefSlots;

  if (isNative) {
    auto const ex = f->extShared();

    ex->m_hniReturnType = hniReturnType;

    auto const info = getNativeInfo();

    Attr dummy = AttrNone;
    auto nativeAttributes = parseNativeAttributes(dummy);
    Native::getFunctionPointers(
      info,
      nativeAttributes,
      ex->m_arFuncPtr,
      ex->m_nativeFuncPtr
    );
    ex->m_takesNumArgs = !!(nativeAttributes & Native::AttrTakesNumArgs);

    if (ex->m_nativeFuncPtr) {
      if (info.sig.ret == Native::NativeSig::Type::MixedTV) {
        ex->m_returnByValue = true;
      }
      int extra =
        (nativeAttributes & Native::AttrTakesNumArgs ? 1 : 0) +
        (isMethod() ? 1 : 0);
      assertx(info.sig.args.size() == params.size() + extra);
      for (auto i = params.size(); i--; ) {
        switch (info.sig.args[extra + i]) {
          case Native::NativeSig::Type::ObjectArg:
          case Native::NativeSig::Type::StringArg:
          case Native::NativeSig::Type::ArrayArg:
          case Native::NativeSig::Type::ResourceArg:
          case Native::NativeSig::Type::OutputArg:
          case Native::NativeSig::Type::MixedTV:
            fParams[i].nativeArg = true;
            break;
          default:
            break;
        }
      }
    }
  }

  f->finishedEmittingParams(fParams);
  return f;
}
Example #5
0
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
  bool isGenerated = isdigit(name->data()[0]) ||
    ParserBase::IsClosureName(name->toCppString());

  Attr attrs = this->attrs;
  if (preClass && preClass->attrs() & AttrInterface) {
    attrs |= AttrAbstract;
  }
  if (attrs & AttrPersistent &&
      ((RuntimeOption::EvalJitEnableRenameFunction && !isGenerated) ||
       (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited) ||
       attrs & AttrInterceptable)) {
    if (attrs & AttrBuiltin) {
      SystemLib::s_anyNonPersistentBuiltins = true;
    }
    attrs = Attr(attrs & ~AttrPersistent);
  }
  if (!RuntimeOption::RepoAuthoritative) {
    // In non-RepoAuthoritative mode, any function could get a VarEnv because
    // of evalPHPDebugger.
    attrs |= AttrMayUseVV;
  } else if (RuntimeOption::EvalJitEnableRenameFunction &&
      !name->empty() &&
      !Func::isSpecial(name) &&
      !isClosureBody) {
    // intercepted functions need to pass all args through
    // to the interceptee
    attrs |= AttrMayUseVV;
  }
  if (isVariadic()) { attrs |= AttrVariadicParam; }

  if (!containsCalls) { attrs |= AttrPhpLeafFn; }

  assert(!m_pce == !preClass);
  auto f = m_ue.newFunc(this, unit, name, attrs, params.size());

  f->m_isPreFunc = !!preClass;

  bool const needsExtendedSharedData =
    m_info ||
    m_builtinFuncPtr ||
    m_nativeFuncPtr ||
    (attrs & AttrNative) ||
    line2 - line1 >= Func::kSmallDeltaLimit ||
    past - base >= Func::kSmallDeltaLimit;

  f->m_shared.reset(
    needsExtendedSharedData
      ? new Func::ExtendedSharedData(preClass, base, past, line1, line2,
                                     top, docComment)
      : new Func::SharedData(preClass, base, past,
                             line1, line2, top, docComment)
  );

  f->init(params.size());

  if (auto const ex = f->extShared()) {
    ex->m_hasExtendedSharedData = true;
    ex->m_builtinFuncPtr = m_builtinFuncPtr;
    ex->m_nativeFuncPtr = m_nativeFuncPtr;
    ex->m_info = m_info;
    ex->m_line2 = line2;
    ex->m_past = past;
    ex->m_returnByValue = false;
  }

  std::vector<Func::ParamInfo> fParams;
  for (unsigned i = 0; i < params.size(); ++i) {
    Func::ParamInfo pi = params[i];
    if (pi.isVariadic()) {
      pi.builtinType = KindOfArray;
    }
    f->appendParam(params[i].byRef, pi, fParams);
  }

  f->shared()->m_returnType = returnType;
  f->shared()->m_localNames.create(m_localNames);
  f->shared()->m_numLocals = m_numLocals;
  f->shared()->m_numIterators = m_numIterators;
  f->m_maxStackCells = maxStackCells;
  f->shared()->m_staticVars = staticVars;
  f->shared()->m_ehtab = toFixed(ehtab);
  f->shared()->m_fpitab = fpitab;
  f->shared()->m_isClosureBody = isClosureBody;
  f->shared()->m_isAsync = isAsync;
  f->shared()->m_isGenerator = isGenerator;
  f->shared()->m_isPairGenerator = isPairGenerator;
  f->shared()->m_userAttributes = userAttributes;
  f->shared()->m_retTypeConstraint = retTypeConstraint;
  f->shared()->m_retUserType = retUserType;
  f->shared()->m_originalFilename = originalFilename;
  f->shared()->m_isGenerated = isGenerated;

  if (attrs & AttrNative) {
    auto const ex = f->extShared();

    auto const& info = Native::GetBuiltinFunction(
      name,
      m_pce ? m_pce->name() : nullptr,
      f->isStatic()
    );

    Attr dummy = AttrNone;
    auto nativeAttributes = parseNativeAttributes(dummy);
    Native::getFunctionPointers(
      info,
      nativeAttributes,
      ex->m_builtinFuncPtr,
      ex->m_nativeFuncPtr
    );

    if (ex->m_nativeFuncPtr &&
        !(nativeAttributes & Native::AttrZendCompat)) {
      if (info.sig.ret == Native::NativeSig::Type::MixedTV) {
        ex->m_returnByValue = true;
      }
      int extra =
        (attrs & AttrNumArgs ? 1 : 0) +
        (isMethod() ? 1 : 0);
      assert(info.sig.args.size() == params.size() + extra);
      for (auto i = params.size(); i--; ) {
        switch (info.sig.args[extra + i]) {
          case Native::NativeSig::Type::ObjectArg:
          case Native::NativeSig::Type::StringArg:
          case Native::NativeSig::Type::ArrayArg:
          case Native::NativeSig::Type::ResourceArg:
          case Native::NativeSig::Type::OutputArg:
          case Native::NativeSig::Type::MixedTV:
            fParams[i].nativeArg = true;
            break;
          default:
            break;
        }
      }
    }
  }

  f->finishedEmittingParams(fParams);
  return f;
}
Example #6
0
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
  bool isGenerated = isdigit(m_name->data()[0]) ||
    ParserBase::IsClosureName(m_name->toCPPString()) || m_isGenerator;

  Attr attrs = m_attrs;
  if (attrs & AttrPersistent &&
      ((RuntimeOption::EvalJitEnableRenameFunction && !isGenerated) ||
       (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited))) {
    attrs = Attr(attrs & ~AttrPersistent);
  }
  if (RuntimeOption::EvalJitEnableRenameFunction &&
      !m_name->empty() &&
      !Func::isSpecial(m_name) &&
      !m_isClosureBody &&
      !m_isGenerator) {
    // intercepted functions need to pass all args through
    // to the interceptee
    attrs = attrs | AttrMayUseVV;
  }

  if (!m_containsCalls) attrs = Attr(attrs | AttrPhpLeafFn);

  Func* f = (m_pce == nullptr)
    ? m_ue.newFunc(this, unit, m_id, m_line1, m_line2, m_base,
                   m_past, m_name, attrs, m_top, m_docComment,
                   m_params.size(), m_isClosureBody | m_isGeneratorFromClosure,
                   m_isGenerator)
    : m_ue.newFunc(this, unit, preClass, m_line1, m_line2, m_base,
                   m_past, m_name, attrs, m_top, m_docComment,
                   m_params.size(), m_isClosureBody | m_isGeneratorFromClosure,
                   m_isGenerator);
  f->shared()->m_info = m_info;
  f->shared()->m_returnType = m_returnType;
  std::vector<Func::ParamInfo> pBuilder;
  for (unsigned i = 0; i < m_params.size(); ++i) {
    Func::ParamInfo pi;
    pi.setFuncletOff(m_params[i].funcletOff());
    pi.setDefaultValue(m_params[i].defaultValue());
    pi.setPhpCode(m_params[i].phpCode());
    pi.setTypeConstraint(m_params[i].typeConstraint());
    pi.setUserAttributes(m_params[i].userAttributes());
    pi.setBuiltinType(m_params[i].builtinType());
    pi.setUserType(m_params[i].userType());
    f->appendParam(m_params[i].ref(), pi, pBuilder);
  }
  if (!m_params.size()) {
    assert(!f->m_refBitVal && !f->shared()->m_refBitPtr);
    f->m_refBitVal = attrs & AttrVariadicByRef ? -1uLL : 0uLL;
  }

  f->shared()->m_params = pBuilder;
  f->shared()->m_localNames.create(m_localNames);
  f->shared()->m_numLocals = m_numLocals;
  f->shared()->m_numIterators = m_numIterators;
  f->m_maxStackCells = m_maxStackCells;
  f->shared()->m_staticVars = m_staticVars;
  f->shared()->m_ehtab = m_ehtab;
  f->shared()->m_fpitab = m_fpitab;
  f->shared()->m_isClosureBody = m_isClosureBody;
  f->shared()->m_isGenerator = m_isGenerator;
  f->shared()->m_isGeneratorFromClosure = m_isGeneratorFromClosure;
  f->shared()->m_isPairGenerator = m_isPairGenerator;
  f->shared()->m_hasGeneratorAsBody = m_hasGeneratorAsBody;
  f->shared()->m_userAttributes = m_userAttributes;
  f->shared()->m_builtinFuncPtr = m_builtinFuncPtr;
  f->shared()->m_nativeFuncPtr = m_nativeFuncPtr;
  f->shared()->m_retTypeConstraint = m_retTypeConstraint;
  f->shared()->m_originalFilename = m_originalFilename;
  f->shared()->m_isGenerated = isGenerated;
  return f;
}
Example #7
0
Func* FuncEmitter::create(Unit& unit, PreClass* preClass /* = NULL */) const {
  bool isGenerated = isdigit(name->data()[0]) ||
    ParserBase::IsClosureName(name->toCppString());

  Attr attrs = this->attrs;
  if (preClass && preClass->attrs() & AttrInterface) {
    attrs |= AttrAbstract;
  }
  if (attrs & AttrPersistent &&
      ((RuntimeOption::EvalJitEnableRenameFunction && !isGenerated) ||
       (!RuntimeOption::RepoAuthoritative && SystemLib::s_inited) ||
       attrs & AttrInterceptable)) {
    if (attrs & AttrBuiltin) {
      SystemLib::s_anyNonPersistentBuiltins = true;
    }
    attrs = Attr(attrs & ~AttrPersistent);
  }
  if (!RuntimeOption::RepoAuthoritative) {
    // In non-RepoAuthoritative mode, any function could get a VarEnv because
    // of evalPHPDebugger.
    attrs |= AttrMayUseVV;
  } else if (RuntimeOption::EvalJitEnableRenameFunction &&
      !name->empty() &&
      !Func::isSpecial(name) &&
      !isClosureBody) {
    // intercepted functions need to pass all args through
    // to the interceptee
    attrs |= AttrMayUseVV;
  }
  if (isVariadic()) { attrs |= AttrVariadicParam; }

  if (!containsCalls) { attrs |= AttrPhpLeafFn; }

  assert(!m_pce == !preClass);
  auto f = m_ue.newFunc(this, unit, name, attrs, params.size());

  f->m_isPreFunc = !!preClass;

  bool const needsExtendedSharedData =
    m_info ||
    m_builtinFuncPtr ||
    m_nativeFuncPtr ||
    (attrs & AttrNative) ||
    line2 - line1 >= Func::kSmallDeltaLimit ||
    past - base >= Func::kSmallDeltaLimit;

  f->m_shared.reset(
    needsExtendedSharedData
      ? new Func::ExtendedSharedData(preClass, base, past, line1, line2,
                                     top, docComment)
      : new Func::SharedData(preClass, base, past,
                             line1, line2, top, docComment)
  );

  f->init(params.size());

  if (auto const ex = f->extShared()) {
    ex->m_hasExtendedSharedData = true;
    ex->m_builtinFuncPtr = m_builtinFuncPtr;
    ex->m_nativeFuncPtr = m_nativeFuncPtr;
    ex->m_info = m_info;
    ex->m_line2 = line2;
    ex->m_past = past;
  }

  std::vector<Func::ParamInfo> fParams;
  bool usesDoubles = false, variadic = false;
  for (unsigned i = 0; i < params.size(); ++i) {
    Func::ParamInfo pi = params[i];
    if (pi.builtinType == KindOfDouble) usesDoubles = true;
    if (pi.isVariadic()) variadic = true;
    f->appendParam(params[i].byRef, pi, fParams);
  }

  f->shared()->m_returnType = returnType;
  f->shared()->m_localNames.create(m_localNames);
  f->shared()->m_numLocals = m_numLocals;
  f->shared()->m_numIterators = m_numIterators;
  f->m_maxStackCells = maxStackCells;
  f->shared()->m_staticVars = staticVars;
  f->shared()->m_ehtab = toFixed(ehtab);
  f->shared()->m_fpitab = fpitab;
  f->shared()->m_isClosureBody = isClosureBody;
  f->shared()->m_isAsync = isAsync;
  f->shared()->m_isGenerator = isGenerator;
  f->shared()->m_isPairGenerator = isPairGenerator;
  f->shared()->m_userAttributes = userAttributes;
  f->shared()->m_retTypeConstraint = retTypeConstraint;
  f->shared()->m_retUserType = retUserType;
  f->shared()->m_originalFilename = originalFilename;
  f->shared()->m_isGenerated = isGenerated;

  f->finishedEmittingParams(fParams);

  if (attrs & AttrNative) {
    auto const ex = f->extShared();

    auto const& info = Native::GetBuiltinFunction(
      name,
      m_pce ? m_pce->name() : nullptr,
      f->isStatic()
    );

    auto const nif = info.ptr;
    if (nif) {
      Attr dummy = AttrNone;
      int nativeAttrs = parseNativeAttributes(dummy);
      if (nativeAttrs & Native::AttrZendCompat) {
        ex->m_nativeFuncPtr = nif;
        ex->m_builtinFuncPtr = zend_wrap_func;
      } else {
        if (parseNativeAttributes(dummy) & Native::AttrActRec) {
          ex->m_builtinFuncPtr = nif;
          ex->m_nativeFuncPtr = nullptr;
        } else {
          ex->m_nativeFuncPtr = nif;
          ex->m_builtinFuncPtr =
            Native::getWrapper(m_pce, usesDoubles, variadic);
        }
      }
    } else {
      ex->m_builtinFuncPtr = Native::unimplementedWrapper;
    }
  }

  return f;
}