/*
 * The CreateCl opcode is specified as not being allowed before the
 * class it creates exists, and closure classes are always unique.
 *
 * This means even if we're not in RepoAuthoritative mode, as long as
 * this code is reachable it will always use the same closure Class*,
 * so we can just burn it into the TC without using RDS.
 */
void emitCreateCl(HTS& env, int32_t numParams, const StringData* clsName) {
  auto const cls = Unit::lookupClassOrUniqueClass(clsName);
  auto const invokeFunc = cls->lookupMethod(s_uuinvoke.get());
  auto const clonedFunc = invokeFunc->cloneAndSetClass(
    const_cast<Class*>(curClass(env))
  );
  assert(cls && (cls->attrs() & AttrUnique));

  auto const closure = allocObjFast(env, cls);
  gen(env, IncRef, closure);

  auto const ctx = [&]{
    if (!curClass(env)) return cns(env, nullptr);
    auto const ldctx = gen(env, LdCtx, fp(env));
    if (invokeFunc->attrs() & AttrStatic) {
      return gen(env, ConvClsToCctx, gen(env, LdClsCtx, ldctx));
    }
    gen(env, IncRefCtx, ldctx);
    return ldctx;
  }();
  gen(env, StClosureCtx, closure, ctx);
  gen(env, StClosureFunc, FuncData(clonedFunc), closure);

  SSATmp* args[numParams];
  for (int32_t i = 0; i < numParams; ++i) {
    args[numParams - i - 1] = popF(env);
  }

  int32_t propId = 0;
  for (; propId < numParams; ++propId) {
    gen(
      env,
      StClosureArg,
      PropByteOffset(cls->declPropOffset(propId)),
      closure,
      args[propId]
    );
  }

  // Closure static variables are per instance, and need to start
  // uninitialized.  After numParams use vars, the remaining instance
  // properties hold any static locals.
  assert(cls->numDeclProperties() ==
         clonedFunc->numStaticLocals() + numParams);
  for (int32_t numDeclProperties = cls->numDeclProperties();
      propId < numDeclProperties;
      ++propId) {
    gen(
      env,
      StClosureArg,
      PropByteOffset(cls->declPropOffset(propId)),
      closure,
      cns(env, Type::Uninit)
    );
  }

  push(env, closure);
}
Exemple #2
0
void c_Closure::init(int numArgs, ActRec* ar, TypedValue* sp) {
  auto const cls = getVMClass();
  auto const invokeFunc = getInvokeFunc();

  if (invokeFunc->cls()) {
    setThisOrClass(ar->getThisOrClass());
    if (invokeFunc->isStatic()) {
      if (!hasClass()) {
        setClass(getThisUnchecked()->getVMClass());
      }
    } else if (!hasClass()) {
      getThisUnchecked()->incRefCount();
    }
  } else {
    hdr()->ctx = nullptr;
  }

  /*
   * Copy the use vars to instance variables, and initialize any
   * instance properties that are for static locals to KindOfUninit.
   */
  auto const numDeclProperties = cls->numDeclProperties();
  assertx(numDeclProperties - numArgs == getInvokeFunc()->numStaticLocals());
  auto beforeCurUseVar = sp + numArgs;
  auto curProperty = getUseVars();
  int i = 0;
  assertx(numArgs <= numDeclProperties);
  for (; i < numArgs; i++) {
    // teleport the references in here so we don't incref
    tvCopy(*--beforeCurUseVar, *curProperty++);
  }
  for (; i < numDeclProperties; ++i) {
    tvWriteUninit(*curProperty++);
  }
}
Exemple #3
0
static Array HHVM_METHOD(Closure, __debugInfo) {
  auto closure = c_Closure::fromObject(this_);

  Array ret = Array::Create();

  // Serialize 'use' parameters.
  if (auto useVars = closure->getUseVars()) {
    Array use;

    auto cls = this_->getVMClass();
    auto propsInfo = cls->declProperties();
    auto nProps = cls->numDeclProperties();
    for (size_t i = 0; i < nProps; ++i) {
      auto value = &useVars[i];
      use.setWithRef(Variant(StrNR(propsInfo[i].name)), tvAsCVarRef(value));
    }

    if (!use.empty()) {
      ret.set(s_static, use);
    }
  }

  auto const func = closure->getInvokeFunc();

  // Serialize function parameters.
  if (auto nParams = func->numParams()) {
   Array params;

   auto lNames = func->localNames();
   for (int i = 0; i < nParams; ++i) {
      auto str = String::attach(
        StringData::Make(s_varprefix.get(), lNames[i])
      );

      bool optional = func->params()[i].phpCode;
      if (auto mi = func->methInfo()) {
        optional = optional || mi->parameters[i]->valueText;
      }

      params.set(str, optional ? s_optional : s_required);
    }

    ret.set(s_parameter, params);
  }

  // Serialize 'this' object.
  if (closure->hasThis()) {
    ret.set(s_this, Object(closure->getThis()));
  }

  return ret;
}
Exemple #4
0
void cgNewInstanceRaw(IRLS& env, const IRInstruction* inst) {
  auto const dst = dstLoc(env, inst, 0).reg();
  auto const cls = inst->extra<NewInstanceRaw>()->cls;
  auto const size = ObjectData::sizeForNProps(cls->numDeclProperties());

  auto const callSpec = size <= kMaxSmallSize
    ? CallSpec::direct(ObjectData::newInstanceRaw)
    : CallSpec::direct(ObjectData::newInstanceRawBig);

  auto const args = argGroup(env, inst)
    .imm(reinterpret_cast<uintptr_t>(cls))
    .imm(size);

  cgCallHelper(vmain(env), env, callSpec,
               callDest(dst), SyncOptions::Sync, args);
}
Exemple #5
0
void cgInitObjProps(IRLS& env, const IRInstruction* inst) {
  auto const cls = inst->extra<InitObjProps>()->cls;
  auto const obj = srcLoc(env, inst, 0).reg();
  auto& v = vmain(env);

  // Set the attributes, if any.
  auto const odAttrs = cls->getODAttrs();
  if (odAttrs) {
    static_assert(sizeof(ObjectData::Attribute) == 2,
                  "Codegen expects 2-byte ObjectData attributes");
    assertx(!(odAttrs & 0xffff0000));
    v << orwim{odAttrs, obj[ObjectData::attributeOff()], v.makeReg()};
  }

  // Initialize the properties.
  auto const nprops = cls->numDeclProperties();
  if (nprops > 0) {
    if (cls->pinitVec().size() == 0) {
      // If the Class has no 86pinit property-initializer functions, we can
      // just copy the initial values from a data member on the Class.
      implInitObjPropsFast(v, env, inst, obj, cls, nprops);
    } else {
      // Load the Class's propInitVec from the target cache.  We know it's
      // already been initialized as a pre-condition on this op.
      auto const propHandle = cls->propHandle();
      assertx(rds::isNormalHandle(propHandle));

      auto const propInitVec = v.makeReg();
      auto const propData = v.makeReg();
      v << load{Vreg(rvmtl())[propHandle], propInitVec};
      v << load{propInitVec[Class::PropInitVec::dataOff()], propData};

      auto const propsOff = sizeof(ObjectData) + cls->builtinODTailSize();
      auto args = argGroup(env, inst)
        .addr(obj, safe_cast<int32_t>(propsOff))
        .reg(propData);

      if (!cls->hasDeepInitProps()) {
        cgCallHelper(v, env, CallSpec::direct(memcpy), kVoidDest,
                     SyncOptions::None, args.imm(cellsToBytes(nprops)));
      } else {
        cgCallHelper(v, env, CallSpec::direct(deepInitHelper),
                     kVoidDest, SyncOptions::None, args.imm(nprops));
      }
    }
  }
}
Exemple #6
0
Object APCObject::createObject() const {
  auto cls = m_cls.left();
  assert(cls != nullptr);

  auto obj = Object::attach(
    m_fast_init ? ObjectData::newInstanceNoPropInit(const_cast<Class*>(cls))
                : ObjectData::newInstance(const_cast<Class*>(cls))
  );

  auto const numProps = cls->numDeclProperties();
  auto const objProp = obj->propVec();
  auto const apcProp = persistentProps();

  if (m_fast_init) {
    // re-entry is possible while we're executing toLocal() on each
    // property, so heap inspectors may see partially initid objects
    // not yet exposed to PHP.
    unsigned i = 0;
    try {
      for (; i < numProps; ++i) {
        new (objProp + i) Variant(apcProp[i]->toLocal());
      }
    } catch (...) {
      for (; i < numProps; ++i) {
        new (objProp + i) Variant();
      }
      throw;
    }
  } else {
    for (unsigned i = 0; i < numProps; ++i) {
      tvAsVariant(&objProp[i]) = apcProp[i]->toLocal();
    }
  }

  if (UNLIKELY(numProps < m_propCount)) {
    auto dynProps = apcProp[numProps];
    assert(dynProps->kind() == APCKind::StaticArray ||
           dynProps->kind() == APCKind::UncountedArray ||
           dynProps->kind() == APCKind::SharedArray);
    obj->setDynPropArray(dynProps->toLocal().asCArrRef());
  }

  if (!m_no_wakeup) obj->invokeWakeup();
  return obj;
}
Exemple #7
0
Object APCObject::createObject() const {
  auto cls = m_cls.left();
  assert(cls != nullptr);

  auto obj = Object::attach(
    m_fast_init ? ObjectData::newInstanceNoPropInit(const_cast<Class*>(cls))
                : ObjectData::newInstance(const_cast<Class*>(cls))
  );

  auto const numProps = cls->numDeclProperties();
  auto const objProp = obj->propVec();
  auto const apcProp = persistentProps();

  if (m_fast_init) {
    unsigned i = 0;
    try {
      while (i < numProps) {
        new (objProp + i) Variant(apcProp[i]->toLocal());
        ++i;
      }
    } catch (...) {
      while (i < numProps) {
        new (objProp + i) Variant();
        ++i;
      }
      throw;
    }
  } else {
    for (unsigned i = 0; i < numProps; ++i) {
      tvAsVariant(&objProp[i]) = apcProp[i]->toLocal();
    }
  }

  if (UNLIKELY(numProps < m_propCount)) {
    auto dynProps = apcProp[numProps];
    assert(dynProps->kind() == APCKind::StaticArray ||
           dynProps->kind() == APCKind::UncountedArray ||
           dynProps->kind() == APCKind::SharedArray);
    obj->setDynPropArray(dynProps->toLocal().asCArrRef());
  }

  if (!m_no_wakeup) obj->invokeWakeup();
  return obj;
}
Exemple #8
0
ObjectData* c_Closure::clone() {
  auto const cls = getVMClass();
  auto ret = c_Closure::fromObject(closureInstanceCtorRepoAuth(cls));

  ret->hdr()->ctx = hdr()->ctx;
  if (auto t = getThis()) {
    t->incRefCount();
  }

  auto src  = getUseVars();
  auto dest = ret->getUseVars();
  auto const nProps = cls->numDeclProperties();
  auto const stop = src + nProps;
  for (; src != stop; ++src, ++dest) {
    tvDup(*src, *dest);
  }

  return ret;
}