diff --git a/hphp/compiler/analysis/emitter.cpp b/hphp/compiler/analysis/emitter.cpp index 6f734f6ca..ee0cb343e 100644 --- a/hphp/compiler/analysis/emitter.cpp +++ b/hphp/compiler/analysis/emitter.cpp @@ -90,6 +90,7 @@ #include "hphp/runtime/base/program_functions.h" #include "hphp/runtime/base/file_repository.h" #include "hphp/runtime/ext_hhvm/ext_hhvm.h" +#include "hphp/runtime/vm/preclass-emit.h" #include "hphp/system/systemlib.h" diff --git a/hphp/compiler/analysis/peephole.cpp b/hphp/compiler/analysis/peephole.cpp index 7ad5b0bf2..85fb3e1d8 100644 --- a/hphp/compiler/analysis/peephole.cpp +++ b/hphp/compiler/analysis/peephole.cpp @@ -16,6 +16,7 @@ #include "hphp/compiler/analysis/peephole.h" #include "hphp/compiler/analysis/emitter.h" +#include "hphp/runtime/vm/preclass-emit.h" namespace HPHP { namespace Compiler { diff --git a/hphp/runtime/base/complex_types.h b/hphp/runtime/base/complex_types.h index ef4577893..00d9d7a0e 100644 --- a/hphp/runtime/base/complex_types.h +++ b/hphp/runtime/base/complex_types.h @@ -27,6 +27,7 @@ #include "hphp/runtime/base/tv_helpers.h" #include "hphp/runtime/base/type_variant.h" #include "hphp/runtime/base/array/array_inline.h" +#include "hphp/runtime/base/array/array_init.h" #undef incl_HPHP_INSIDE_HPHP_COMPLEX_TYPES_H_ diff --git a/hphp/runtime/base/file/file.cpp b/hphp/runtime/base/file/file.cpp index bb0ae110f..f79f53b67 100644 --- a/hphp/runtime/base/file/file.cpp +++ b/hphp/runtime/base/file/file.cpp @@ -23,6 +23,7 @@ #include "hphp/runtime/base/server/virtual_host.h" #include "hphp/runtime/base/runtime_option.h" #include "hphp/runtime/base/runtime_error.h" +#include "hphp/runtime/base/array/array_init.h" #include "hphp/util/logger.h" #include "hphp/util/process.h" #include "hphp/util/util.h" diff --git a/hphp/runtime/base/file/file_stream_wrapper.cpp b/hphp/runtime/base/file/file_stream_wrapper.cpp index 433dc3324..de469582a 100644 --- a/hphp/runtime/base/file/file_stream_wrapper.cpp +++ b/hphp/runtime/base/file/file_stream_wrapper.cpp @@ -15,6 +15,7 @@ */ #include "hphp/runtime/base/file/file_stream_wrapper.h" +#include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/base/file/plain_file.h" #include "hphp/runtime/base/server/static_content_cache.h" #include diff --git a/hphp/runtime/base/file/php_stream_wrapper.cpp b/hphp/runtime/base/file/php_stream_wrapper.cpp index e4fa83239..a5ee2b8a4 100644 --- a/hphp/runtime/base/file/php_stream_wrapper.cpp +++ b/hphp/runtime/base/file/php_stream_wrapper.cpp @@ -15,6 +15,7 @@ */ #include "hphp/runtime/base/file/php_stream_wrapper.h" +#include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/base/file/plain_file.h" #include "hphp/runtime/base/file/temp_file.h" #include "hphp/runtime/base/file/mem_file.h" diff --git a/hphp/runtime/base/file/ssl_socket.cpp b/hphp/runtime/base/file/ssl_socket.cpp index 0c0dda248..9916c3bd2 100644 --- a/hphp/runtime/base/file/ssl_socket.cpp +++ b/hphp/runtime/base/file/ssl_socket.cpp @@ -16,6 +16,7 @@ #include "hphp/runtime/base/file/ssl_socket.h" #include "hphp/runtime/base/complex_types.h" +#include "hphp/runtime/base/runtime_error.h" #include "hphp/util/util.h" #include diff --git a/hphp/runtime/base/file/user_file.cpp b/hphp/runtime/base/file/user_file.cpp index 3257596f9..2cb81c715 100644 --- a/hphp/runtime/base/file/user_file.cpp +++ b/hphp/runtime/base/file/user_file.cpp @@ -15,8 +15,8 @@ */ #include "hphp/runtime/base/file/user_file.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/ext/ext_function.h" -#include "hphp/runtime/vm/instance.h" #include "hphp/runtime/vm/jit/translator-inline.h" namespace HPHP { diff --git a/hphp/runtime/base/object_data.cpp b/hphp/runtime/base/object_data.cpp index 6d442d26a..542b449fd 100644 --- a/hphp/runtime/base/object_data.cpp +++ b/hphp/runtime/base/object_data.cpp @@ -20,6 +20,7 @@ #include "hphp/runtime/base/externals.h" #include "hphp/runtime/base/variable_serializer.h" #include "hphp/runtime/base/execution_context.h" +#include "hphp/runtime/base/runtime_error.h" #include "hphp/util/lock.h" #include "hphp/runtime/base/class_info.h" #include "hphp/runtime/ext/ext_closure.h" @@ -27,6 +28,10 @@ #include "hphp/runtime/ext/ext_collections.h" #include "hphp/runtime/ext/ext_simplexml.h" #include "hphp/runtime/vm/class.h" +#include "hphp/runtime/vm/member_operations.h" +#include "hphp/runtime/vm/object_allocator_sizes.h" +#include "hphp/runtime/vm/jit/translator-inline.h" +#include "hphp/system/systemlib.h" namespace HPHP { /////////////////////////////////////////////////////////////////////////////// @@ -118,6 +123,10 @@ bool ObjectData::o_instanceof(CStrRef s) const { return m_cls->classof(cls); } +void ObjectData::raiseObjToIntNotice(const char* clsName) { + raise_notice("Object of class %s could not be converted to int", clsName); +} + bool ObjectData::o_toBooleanImpl() const noexcept { not_reached(); } @@ -833,4 +842,810 @@ size_t object_alloc_index_to_size(int idx) { } /////////////////////////////////////////////////////////////////////////////// + +static StaticString s___get(LITSTR_INIT("__get")); +static StaticString s___set(LITSTR_INIT("__set")); +static StaticString s___isset(LITSTR_INIT("__isset")); +static StaticString s___unset(LITSTR_INIT("__unset")); + +TRACE_SET_MOD(runtime); + +int HPHP::Instance::ObjAllocatorSizeClassCount = + HPHP::InitializeAllocators(); + +void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData, + size_t nProps) { + auto* dst = propVec; + auto* src = propData; + for (; src != propData + nProps; ++src, ++dst) { + *dst = *src; + // m_aux.u_deepInit is true for properties that need "deep" initialization + if (src->deepInit()) { + tvIncRef(dst); + collectionDeepCopyTV(dst); + } + } } + +void Instance::raiseAbstractClassError(Class* cls) { + Attr attrs = cls->attrs(); + raise_error("Cannot instantiate %s %s", + (attrs & AttrInterface) ? "interface" : + (attrs & AttrTrait) ? "trait" : "abstract class", + cls->preClass()->name()->data()); +} + +TypedValue* Instance::propVec() { + uintptr_t ret = (uintptr_t)this + sizeof(ObjectData) + builtinPropSize(); + // TODO(#1432007): some builtins still do not have TypedValue-aligned sizes. + assert(ret % sizeof(TypedValue) == builtinPropSize() % sizeof(TypedValue)); + return (TypedValue*) ret; +} + +const TypedValue* Instance::propVec() const { + return const_cast(this)->propVec(); +} + +Instance* Instance::callCustomInstanceInit() { + static StringData* sd_init = StringData::GetStaticString("__init__"); + const Func* init = m_cls->lookupMethod(sd_init); + if (init != nullptr) { + TypedValue tv; + // We need to incRef/decRef here because we're still a new (_count + // == 0) object and invokeFunc is going to expect us to have a + // reasonable refcount. + try { + incRefCount(); + g_vmContext->invokeFuncFew(&tv, init, this); + decRefCount(); + assert(!IS_REFCOUNTED_TYPE(tv.m_type)); + } catch (...) { + this->setNoDestruct(); + decRefObj(this); + throw; + } + } + return this; +} + +void Instance::operator delete(void* p) { + Instance* this_ = (Instance*)p; + Class* cls = this_->getVMClass(); + size_t nProps = cls->numDeclProperties(); + // cppext classes have their own implementation of delete + assert(this_->builtinPropSize() == 0); + TypedValue* propVec = (TypedValue *)((uintptr_t)this_ + sizeof(ObjectData)); + for (unsigned i = 0; i < nProps; ++i) { + TypedValue* prop = &propVec[i]; + tvRefcountedDecRef(prop); + } + DELETEOBJSZ(sizeForNProps(nProps))(this_); +} + +HOT_FUNC_VM +Instance* Instance::newInstanceRaw(Class* cls, int idx) { + Instance* obj = (Instance*)ALLOCOBJIDX(idx); + new (obj) Instance(cls, NoInit::noinit); + return obj; +} + +void Instance::invokeUserMethod(TypedValue* retval, const Func* method, + CArrRef params) { + g_vmContext->invokeFunc(retval, method, params, this); +} + +Object Instance::FromArray(ArrayData *properties) { + Instance* retval = Instance::newInstance(SystemLib::s_stdclassClass); + retval->initDynProps(); + HphpArray* props = static_cast(retval->o_properties.get()); + for (ssize_t pos = properties->iter_begin(); pos != ArrayData::invalid_index; + pos = properties->iter_advance(pos)) { + TypedValue* value = properties->nvGetValueRef(pos); + TypedValue key; + properties->nvGetKey(&key, pos); + if (key.m_type == KindOfInt64) { + props->set(key.m_data.num, tvAsCVarRef(value), false); + } else { + assert(IS_STRING_TYPE(key.m_type)); + StringData* strKey = key.m_data.pstr; + props->set(strKey, tvAsCVarRef(value), false); + decRefStr(strKey); + } + } + return retval; +} + +void Instance::initDynProps(int numDynamic /* = 0 */) { + // Create o_properties with room for numDynamic + o_properties.asArray() = ArrayData::Make(numDynamic); +} + +Slot Instance::declPropInd(TypedValue* prop) const { + // Do an address range check to determine whether prop physically resides + // in propVec. + const TypedValue* pv = propVec(); + if (prop >= pv && prop < &pv[m_cls->numDeclProperties()]) { + return prop - pv; + } else { + return kInvalidSlot; + } +} + +template +TypedValue* Instance::getPropImpl(Class* ctx, const StringData* key, + bool& visible, bool& accessible, + bool& unset) { + TypedValue* prop = nullptr; + unset = false; + Slot propInd = m_cls->getDeclPropIndex(ctx, key, accessible); + visible = (propInd != kInvalidSlot); + if (propInd != kInvalidSlot) { + // We found a visible property, but it might not be accessible. + // No need to check if there is a dynamic property with this name. + prop = &propVec()[propInd]; + if (prop->m_type == KindOfUninit) { + unset = true; + } + } else { + assert(!visible && !accessible); + // We could not find a visible property. We need to check for a + // dynamic property with this name if declOnly = false. + if (!declOnly && o_properties.get()) { + prop = static_cast(o_properties.get())->nvGet(key); + if (prop) { + // o_properties.get()->nvGet() returned a non-declared property, + // we know that it is visible and accessible (since all + // dynamic properties are), and we know it is not unset + // (since unset dynamic properties don't appear in o_properties.get()). + visible = true; + accessible = true; + } + } + } + return prop; +} + +TypedValue* Instance::getProp(Class* ctx, const StringData* key, + bool& visible, bool& accessible, bool& unset) { + return getPropImpl(ctx, key, visible, accessible, unset); +} + +TypedValue* Instance::getDeclProp(Class* ctx, const StringData* key, + bool& visible, bool& accessible, + bool& unset) { + return getPropImpl(ctx, key, visible, accessible, unset); +} + +void Instance::invokeSet(TypedValue* retval, const StringData* key, + TypedValue* val) { + AttributeClearer a(UseSet, this); + const Func* meth = m_cls->lookupMethod(s___set.get()); + assert(meth); + invokeUserMethod(retval, meth, + CREATE_VECTOR2(CStrRef(key), tvAsVariant(val))); +} + +#define MAGIC_PROP_BODY(name, attr) \ + AttributeClearer a((attr), this); \ + const Func* meth = m_cls->lookupMethod(name); \ + assert(meth); \ + invokeUserMethod(retval, meth, CREATE_VECTOR1(CStrRef(key))); \ + +void Instance::invokeGet(TypedValue* retval, const StringData* key) { + MAGIC_PROP_BODY(s___get.get(), UseGet); +} + +void Instance::invokeIsset(TypedValue* retval, const StringData* key) { + MAGIC_PROP_BODY(s___isset.get(), UseIsset); +} + +void Instance::invokeUnset(TypedValue* retval, const StringData* key) { + MAGIC_PROP_BODY(s___unset.get(), UseUnset); +} + +void Instance::invokeGetProp(TypedValue*& retval, TypedValue& tvRef, + const StringData* key) { + invokeGet(&tvRef, key); + retval = &tvRef; +} + +template +void Instance::propImpl(TypedValue*& retval, TypedValue& tvRef, + Class* ctx, + const StringData* key) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + + if (visible) { + if (accessible) { + if (unset) { + if (getAttribute(UseGet)) { + invokeGetProp(retval, tvRef, key); + } else { + if (warn) { + raiseUndefProp(key); + } + if (define) { + retval = propVal; + } else { + retval = (TypedValue*)&init_null_variant; + } + } + } else { + retval = propVal; + } + } else { + if (getAttribute(UseGet)) { + invokeGetProp(retval, tvRef, key); + } else { + // No need to check hasProp since visible is true + // Visibility is either protected or private since accessible is false + Slot propInd = m_cls->lookupDeclProp(key); + bool priv = m_cls->declProperties()[propInd].m_attrs & AttrPrivate; + + raise_error("Cannot access %s property %s::$%s", + priv ? "private" : "protected", + m_cls->m_preClass->name()->data(), + key->data()); + } + } + } else if (UNLIKELY(!*key->data())) { + throw_invalid_property_name(StrNR(key)); + } else { + if (getAttribute(UseGet)) { + invokeGetProp(retval, tvRef, key); + } else { + if (warn) { + raiseUndefProp(key); + } + if (define) { + if (o_properties.get() == nullptr) { + initDynProps(); + } + o_properties.get()->createLvalPtr(*(const String*)&key, + *(Variant**)(&retval), false); + } else { + retval = (TypedValue*)&init_null_variant; + } + } + } +} + +void Instance::prop(TypedValue*& retval, TypedValue& tvRef, + Class* ctx, const StringData* key) { + propImpl(retval, tvRef, ctx, key); +} + +void Instance::propD(TypedValue*& retval, TypedValue& tvRef, + Class* ctx, const StringData* key) { + propImpl(retval, tvRef, ctx, key); +} + +void Instance::propW(TypedValue*& retval, TypedValue& tvRef, + Class* ctx, const StringData* key) { + propImpl(retval, tvRef, ctx, key); +} + +void Instance::propWD(TypedValue*& retval, TypedValue& tvRef, + Class* ctx, const StringData* key) { + propImpl(retval, tvRef, ctx, key); +} + +bool Instance::propIsset(Class* ctx, const StringData* key) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + if (visible && accessible && !unset) { + return isset(tvAsCVarRef(propVal)); + } + if (!getAttribute(UseIsset)) { + return false; + } + TypedValue tv; + tvWriteUninit(&tv); + invokeIsset(&tv, key); + tvCastToBooleanInPlace(&tv); + return tv.m_data.num; +} + +bool Instance::propEmpty(Class* ctx, const StringData* key) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + if (visible && accessible && !unset) { + return empty(tvAsCVarRef(propVal)); + } + if (!getAttribute(UseIsset)) { + return true; + } + TypedValue tv; + tvWriteUninit(&tv); + invokeIsset(&tv, key); + tvCastToBooleanInPlace(&tv); + if (!tv.m_data.num) { + return true; + } + if (getAttribute(UseGet)) { + invokeGet(&tv, key); + bool emptyResult = empty(tvAsCVarRef(&tv)); + tvRefcountedDecRef(&tv); + return emptyResult; + } + return false; +} + +TypedValue* Instance::setProp(Class* ctx, const StringData* key, + TypedValue* val, + bool bindingAssignment /* = false */) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + if (visible && accessible) { + assert(propVal); + if (unset && getAttribute(UseSet)) { + TypedValue ignored; + invokeSet(&ignored, key, val); + tvRefcountedDecRef(&ignored); + } else { + if (UNLIKELY(bindingAssignment)) { + tvBind(val, propVal); + } else { + tvSet(*val, *propVal); + } + } + // Return a pointer to the property if it's a declared property + return declPropInd(propVal) != kInvalidSlot ? propVal : nullptr; + } + assert(!accessible); + if (visible) { + assert(propVal); + if (!getAttribute(UseSet)) { + raise_error("Cannot access protected property"); + } + // Fall through to the last case below + } else if (UNLIKELY(!*key->data())) { + throw_invalid_property_name(StrNR(key)); + } else if (!getAttribute(UseSet)) { + if (o_properties.get() == nullptr) { + initDynProps(); + } + // when seting a dynamic property, do not write + // directly to the TypedValue in the HphpArray, since + // its _count field is used to store the string hash of + // the property name. Instead, call the appropriate + // setters (set() or setRef()). + if (UNLIKELY(bindingAssignment)) { + o_properties.get()->setRef(const_cast(key), + tvAsCVarRef(val), false); + } else { + o_properties.get()->set(const_cast(key), + tvAsCVarRef(val), false); + } + return nullptr; + } + assert(!accessible); + assert(getAttribute(UseSet)); + TypedValue ignored; + invokeSet(&ignored, key, val); + tvRefcountedDecRef(&ignored); + return nullptr; +} + +TypedValue* Instance::setOpProp(TypedValue& tvRef, Class* ctx, + unsigned char op, const StringData* key, + Cell* val) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + if (visible && accessible) { + assert(propVal); + if (unset && getAttribute(UseGet)) { + TypedValue tvResult; + tvWriteUninit(&tvResult); + invokeGet(&tvResult, key); + SETOP_BODY(&tvResult, op, val); + if (getAttribute(UseSet)) { + assert(tvRef.m_type == KindOfUninit); + memcpy(&tvRef, &tvResult, sizeof(TypedValue)); + TypedValue ignored; + invokeSet(&ignored, key, &tvRef); + tvRefcountedDecRef(&ignored); + propVal = &tvRef; + } else { + memcpy(propVal, &tvResult, sizeof(TypedValue)); + } + } else { + SETOP_BODY(propVal, op, val); + } + return propVal; + } + assert(!accessible); + if (visible) { + assert(propVal); + if (!getAttribute(UseGet) || !getAttribute(UseSet)) { + raise_error("Cannot access protected property"); + } + // Fall through to the last case below + } else if (UNLIKELY(!*key->data())) { + throw_invalid_property_name(StrNR(key)); + } else if (!getAttribute(UseGet)) { + if (o_properties.get() == nullptr) { + initDynProps(); + } + o_properties.get()->createLvalPtr(*(const String*)&key, + *(Variant**)(&propVal), false); + // don't write propVal->_count because it holds data + // owned by the HphpArray + propVal->m_type = KindOfNull; + SETOP_BODY(propVal, op, val); + return propVal; + } else if (!getAttribute(UseSet)) { + TypedValue tvResult; + tvWriteUninit(&tvResult); + invokeGet(&tvResult, key); + SETOP_BODY(&tvResult, op, val); + if (o_properties.get() == nullptr) { + initDynProps(); + } + o_properties.get()->createLvalPtr(*(const String*)&key, + *(Variant**)(&propVal), false); + // don't write propVal->_count because it holds data + // owned by the HphpArray + propVal->m_data.num = tvResult.m_data.num; + propVal->m_type = tvResult.m_type; + return propVal; + } + assert(!accessible); + assert(getAttribute(UseGet) && getAttribute(UseSet)); + invokeGet(&tvRef, key); + SETOP_BODY(&tvRef, op, val); + TypedValue ignored; + invokeSet(&ignored, key, &tvRef); + tvRefcountedDecRef(&ignored); + propVal = &tvRef; + return propVal; +} + +template +void Instance::incDecPropImpl(TypedValue& tvRef, Class* ctx, + unsigned char op, const StringData* key, + TypedValue& dest) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + if (visible && accessible) { + assert(propVal); + if (unset && getAttribute(UseGet)) { + TypedValue tvResult; + tvWriteUninit(&tvResult); + invokeGet(&tvResult, key); + IncDecBody(op, &tvResult, &dest); + if (getAttribute(UseSet)) { + TypedValue ignored; + invokeSet(&ignored, key, &tvResult); + tvRefcountedDecRef(&ignored); + propVal = &tvResult; + } else { + memcpy((void *)propVal, (void *)&tvResult, sizeof(TypedValue)); + } + } else { + IncDecBody(op, propVal, &dest); + } + return; + } + assert(!accessible); + if (visible) { + assert(propVal); + if (!getAttribute(UseGet) || !getAttribute(UseSet)) { + raise_error("Cannot access protected property"); + } + // Fall through to the last case below + } else if (UNLIKELY(!*key->data())) { + throw_invalid_property_name(StrNR(key)); + } else if (!getAttribute(UseGet)) { + if (o_properties.get() == nullptr) { + initDynProps(); + } + o_properties.get()->createLvalPtr(*(const String*)&key, + *(Variant**)(&propVal), false); + // don't write propVal->_count because it holds data + // owned by the HphpArray + propVal->m_type = KindOfNull; + IncDecBody(op, propVal, &dest); + return; + } else if (!getAttribute(UseSet)) { + TypedValue tvResult; + tvWriteUninit(&tvResult); + invokeGet(&tvResult, key); + IncDecBody(op, &tvResult, &dest); + if (o_properties.get() == nullptr) { + initDynProps(); + } + o_properties.get()->createLvalPtr(*(const String*)&key, + *(Variant**)(&propVal), false); + // don't write propVal->_count because it holds data + // owned by the HphpArray + propVal->m_data.num = tvResult.m_data.num; + propVal->m_type = tvResult.m_type; + return; + } + assert(!accessible); + assert(getAttribute(UseGet) && getAttribute(UseSet)); + invokeGet(&tvRef, key); + IncDecBody(op, &tvRef, &dest); + TypedValue ignored; + invokeSet(&ignored, key, &tvRef); + tvRefcountedDecRef(&ignored); + propVal = &tvRef; +} + +template <> +void Instance::incDecProp(TypedValue& tvRef, Class* ctx, + unsigned char op, const StringData* key, + TypedValue& dest) { + incDecPropImpl(tvRef, ctx, op, key, dest); +} + +template <> +void Instance::incDecProp(TypedValue& tvRef, Class* ctx, + unsigned char op, const StringData* key, + TypedValue& dest) { + incDecPropImpl(tvRef, ctx, op, key, dest); +} + +void Instance::unsetProp(Class* ctx, const StringData* key) { + bool visible, accessible, unset; + TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); + if (visible && accessible) { + Slot propInd = declPropInd(propVal); + if (propInd != kInvalidSlot) { + // Declared property. + tvSetIgnoreRef(*null_variant.asTypedValue(), *propVal); + } else { + // Dynamic property. + assert(o_properties.get() != nullptr); + o_properties.get()->remove(CStrRef(key), false); + } + } else if (UNLIKELY(!*key->data())) { + throw_invalid_property_name(StrNR(key)); + } else { + assert(!accessible); + if (getAttribute(UseUnset)) { + TypedValue ignored; + invokeUnset(&ignored, key); + tvRefcountedDecRef(&ignored); + } else if (visible) { + raise_error("Cannot unset inaccessible property"); + } + } +} + +void Instance::raiseUndefProp(const StringData* key) { + raise_notice("Undefined property: %s::$%s", + m_cls->name()->data(), key->data()); +} + +void Instance::getProp(const Class* klass, bool pubOnly, + const PreClass::Prop* prop, + Array& props, + std::vector& inserted) const { + if (prop->attrs() & AttrStatic) { + return; + } + + Slot propInd = klass->lookupDeclProp(prop->name()); + assert(propInd != kInvalidSlot); + const TypedValue* propVal = &propVec()[propInd]; + + if ((!pubOnly || (prop->attrs() & AttrPublic)) && + propVal->m_type != KindOfUninit && + !inserted[propInd]) { + inserted[propInd] = true; + props.lvalAt(CStrRef(klass->declProperties()[propInd].m_mangledName)) + .setWithRef(tvAsCVarRef(propVal)); + } +} + +void Instance::getProps(const Class* klass, bool pubOnly, + const PreClass* pc, + Array& props, + std::vector& inserted) const { + PreClass::Prop const* propVec = pc->properties(); + size_t count = pc->numProperties(); + for (size_t i = 0; i < count; ++i) { + getProp(klass, pubOnly, &propVec[i], props, inserted); + } +} + +Variant Instance::t___destruct() { + static StringData* sd__destruct = StringData::GetStaticString("__destruct"); + const Func* method = m_cls->lookupMethod(sd__destruct); + if (method) { + Variant v; + g_vmContext->invokeFuncFew(v.asTypedValue(), method, this); + return v; + } else { + return uninit_null(); + } +} + +Variant Instance::t___call(Variant v_name, Variant v_arguments) { + static StringData* sd__call = StringData::GetStaticString("__call"); + const Func* method = m_cls->lookupMethod(sd__call); + if (method) { + Variant v; + TypedValue args[2]; + tvDup(*v_name.asTypedValue(), args[0]); + tvDup(*v_arguments.asTypedValue(), args[1]); + g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 2, + args); + return v; + } else { + return uninit_null(); + } +} + +Variant Instance::t___set(Variant v_name, Variant v_value) { + const Func* method = m_cls->lookupMethod(s___set.get()); + if (method) { + Variant v; + g_vmContext->invokeFunc(v.asTypedValue(), method, + Array(ArrayInit(2).set(v_name).set(withRefBind(v_value)).create()), + this); + return v; + } else { + return uninit_null(); + } +} + +Variant Instance::t___get(Variant v_name) { + const Func* method = m_cls->lookupMethod(s___get.get()); + if (method) { + Variant v; + TypedValue args[1]; + tvDup(*v_name.asTypedValue(), args[0]); + g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, + args); + return v; + } else { + return uninit_null(); + } +} + +bool Instance::t___isset(Variant v_name) { + const Func* method = m_cls->lookupMethod(s___isset.get()); + if (method) { + Variant v; + TypedValue args[1]; + tvDup(*v_name.asTypedValue(), args[0]); + g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, + args); + return v.toBoolean(); + } else { + return false; + } +} + +Variant Instance::t___unset(Variant v_name) { + const Func* method = m_cls->lookupMethod(s___unset.get()); + if (method) { + Variant v; + TypedValue args[1]; + tvDup(*v_name.asTypedValue(), args[0]); + g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, + args); + return v; + } else { + return uninit_null(); + } +} + +Variant Instance::t___sleep() { + static StringData* sd__sleep = StringData::GetStaticString("__sleep"); + const Func *method = m_cls->lookupMethod(sd__sleep); + if (method) { + TypedValue tv; + g_vmContext->invokeFuncFew(&tv, method, this); + return tvAsVariant(&tv); + } else { + clearAttribute(HasSleep); + return uninit_null(); + } +} + +Variant Instance::t___wakeup() { + static StringData* sd__wakeup = StringData::GetStaticString("__wakeup"); + const Func *method = m_cls->lookupMethod(sd__wakeup); + if (method) { + TypedValue tv; + g_vmContext->invokeFuncFew(&tv, method, this); + return tvAsVariant(&tv); + } else { + return uninit_null(); + } +} + +Variant Instance::t___set_state(Variant v_properties) { + static StringData* sd__set_state = StringData::GetStaticString("__set_state"); + const Func* method = m_cls->lookupMethod(sd__set_state); + if (method) { + Variant v; + TypedValue args[1]; + tvDup(*v_properties.asTypedValue(), args[0]); + g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, + args); + return v; + } else { + return false; + } +} + +String Instance::t___tostring() { + const Func *method = m_cls->getToString(); + if (method) { + TypedValue tv; + g_vmContext->invokeFuncFew(&tv, method, this); + if (!IS_STRING_TYPE(tv.m_type)) { + void (*notify_user)(const char *, ...) = &raise_error; + if (hphpiCompat) { + tvCastToStringInPlace(&tv); + notify_user = &raise_warning; + } + notify_user("Method %s::__toString() must return a string value", + m_cls->m_preClass->name()->data()); + } + return tv.m_data.pstr; + } else { + std::string msg = m_cls->m_preClass->name()->data(); + msg += "::__toString() was not defined"; + throw BadTypeConversionException(msg.c_str()); + } +} + +Variant Instance::t___clone() { + static StringData* sd__clone = StringData::GetStaticString("__clone"); + const Func *method = m_cls->lookupMethod(sd__clone); + if (method) { + TypedValue tv; + g_vmContext->invokeFuncFew(&tv, method, this); + return false; + } else { + return false; + } +} + +void Instance::cloneSet(ObjectData* clone) { + Instance* iclone = static_cast(clone); + Slot nProps = m_cls->numDeclProperties(); + TypedValue* iclonePropVec = (TypedValue *)((uintptr_t)iclone + + sizeof(ObjectData) + builtinPropSize()); + for (Slot i = 0; i < nProps; i++) { + tvRefcountedDecRef(&iclonePropVec[i]); + tvDupFlattenVars(&propVec()[i], &iclonePropVec[i], nullptr); + } + if (o_properties.get()) { + iclone->initDynProps(); + ssize_t iter = o_properties.get()->iter_begin(); + while (iter != HphpArray::ElmIndEmpty) { + auto props = static_cast(o_properties.get()); + TypedValue key; + props->nvGetKey(&key, iter); + assert(tvIsString(&key)); + StringData* strKey = key.m_data.pstr; + TypedValue *val = props->nvGet(strKey); + TypedValue *retval; + auto cloneProps = iclone->o_properties.get(); + cloneProps->createLvalPtr(strKey, *(Variant**)&retval, false); + tvDupFlattenVars(val, retval, cloneProps); + iter = o_properties.get()->iter_advance(iter); + decRefStr(strKey); + } + } +} + +ObjectData* Instance::cloneImpl() { + Instance* obj = Instance::newInstance(m_cls); + cloneSet(obj); + obj->incRefCount(); + obj->t___clone(); + return obj; +} + + } // HPHP::VM + + diff --git a/hphp/runtime/base/object_data.h b/hphp/runtime/base/object_data.h index 06d03efd0..cb1e25bec 100644 --- a/hphp/runtime/base/object_data.h +++ b/hphp/runtime/base/object_data.h @@ -21,9 +21,8 @@ #include "hphp/runtime/base/util/smart_ptr.h" #include "hphp/runtime/base/types.h" #include "hphp/runtime/base/macros.h" -#include "hphp/runtime/base/runtime_error.h" +#include "hphp/runtime/vm/class.h" #include "hphp/system/systemlib.h" - #include #include @@ -107,9 +106,6 @@ class ObjectData : public CountableNF { // Return the upper 3 bits of o_attribute return (Collection::Type)((uint16_t)(o_attribute >> 13) & 7); } - bool supportsUnsetElem() const { - return isCollection() || instanceof(SystemLib::s_ArrayAccessClass); - } bool implementsIterator() { return (instanceof(SystemLib::s_IteratorClass)); @@ -150,14 +146,13 @@ class ObjectData : public CountableNF { return true; } + static void raiseObjToIntNotice(const char*); + int64_t o_toInt64() const { if (getAttribute(CallToImpl)) { return o_toInt64Impl(); } - raise_notice( - "Object of class %s could not be converted to int", - o_getClassName().data() - ); + raiseObjToIntNotice(o_getClassName().data()); return 1; } @@ -416,6 +411,226 @@ ALWAYS_INLINE inline void decRefObj(ObjectData* obj) { } /////////////////////////////////////////////////////////////////////////////// + +void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData, + size_t nProps); + +class Instance : public ObjectData { + // Do not declare any fields directly in Instance; instead embed them in + // ObjectData, so that a property vector can always reside immediately past + // the end of an object. + + private: + // This constructor is used for all pure classes that are not + // descendents of cppext classes + explicit Instance(Class* cls) : ObjectData(false, cls) { + instanceInit(cls); + } + + enum class NoInit { noinit }; + explicit Instance(Class* cls, NoInit) : ObjectData(false, cls) {} + + public: + // This constructor is used for all cppext classes (including resources) + // and their descendents. + Instance(Class* cls, bool isResource) + : ObjectData(isResource, cls) { + instanceInit(cls); + } + + virtual ~Instance() {} + + static int ObjAllocatorSizeClassCount; + + static void raiseAbstractClassError(Class* cls); + + // Call newInstance() to instantiate an Instance + static Instance* newInstance(Class* cls) { + if (cls->m_InstanceCtor) { + return cls->m_InstanceCtor(cls); + } + Attr attrs = cls->attrs(); + if (UNLIKELY(attrs & (AttrAbstract | AttrInterface | AttrTrait))) { + raiseAbstractClassError(cls); + } + size_t nProps = cls->numDeclProperties(); + size_t size = sizeForNProps(nProps); + Instance* obj = (Instance*)ALLOCOBJSZ(size); + new (obj) Instance(cls); + if (UNLIKELY(cls->callsCustomInstanceInit())) { + /* + This must happen after the constructor finishes, + because it can leak references to obj AND it can + throw exceptions. If we have this in the Instance + constructor, and it throws, obj will be partially + destroyed (ie ~ObjectData will be called, resetting + the vtable pointer) leaving dangling references + to the object (eg in backtraces). + */ + obj->callCustomInstanceInit(); + } + return obj; + } + + // Given a Class that is assumed to be a concrete, regular (not a + // trait or interface), pure PHP class, and an allocator index, + // return a new, uninitialized object of that class. + static Instance* newInstanceRaw(Class* cls, int idx); + + private: + void instanceInit(Class* cls) { + setAttributes(cls->getODAttrs()); + size_t nProps = cls->numDeclProperties(); + if (cls->needInitialization()) { + cls->initialize(); + } + if (nProps > 0) { + if (cls->pinitVec().size() > 0) { + const Class::PropInitVec* propInitVec = m_cls->getPropData(); + assert(propInitVec != nullptr); + assert(nProps == propInitVec->size()); + if (!cls->hasDeepInitProps()) { + memcpy(propVec(), &(*propInitVec)[0], nProps * sizeof(TypedValue)); + } else { + deepInitHelper(propVec(), &(*propInitVec)[0], nProps); + } + } else { + assert(nProps == cls->declPropInit().size()); + memcpy(propVec(), &cls->declPropInit()[0], nProps * sizeof(TypedValue)); + } + } + } + + protected: + TypedValue* propVec(); + const TypedValue* propVec() const; + + public: + Instance* callCustomInstanceInit(); + + void operator delete(void* p); + + //============================================================================ + // Virtual ObjectData methods that we need to override + + public: + virtual Variant t___destruct(); + virtual Variant t___call(Variant v_name, Variant v_arguments); + virtual Variant t___set(Variant v_name, Variant v_value); + virtual Variant t___get(Variant v_name); + virtual bool t___isset(Variant v_name); + virtual Variant t___unset(Variant v_name); + virtual Variant t___sleep(); + virtual Variant t___wakeup(); + virtual Variant t___set_state(Variant v_properties); + virtual String t___tostring(); + virtual Variant t___clone(); + + //============================================================================ + // Miscellaneous. + + void cloneSet(ObjectData* clone); + ObjectData* cloneImpl(); + + void invokeUserMethod(TypedValue* retval, const Func* method, + CArrRef params); + + const Func* methodNamed(const StringData* sd) const { + return getVMClass()->lookupMethod(sd); + } + + static size_t sizeForNProps(Slot nProps) { + size_t sz = sizeof(Instance) + (sizeof(TypedValue) * nProps); + assert((sz & (sizeof(TypedValue) - 1)) == 0); + return sz; + } + + static Object FromArray(ArrayData *properties); + + //============================================================================ + // Properties. + public: + int builtinPropSize() const { + return m_cls->builtinPropSize(); + } + + // public for ObjectData access + void initDynProps(int numDynamic = 0); + Slot declPropInd(TypedValue* prop) const; + private: + template + TypedValue* getPropImpl(Class* ctx, const StringData* key, bool& visible, + bool& accessible, bool& unset); + public: + TypedValue* getProp(Class* ctx, const StringData* key, bool& visible, + bool& accessible, bool& unset); + TypedValue* getDeclProp(Class* ctx, const StringData* key, bool& visible, + bool& accessible, bool& unset); + private: + template + void propImpl(TypedValue*& retval, TypedValue& tvRef, Class* ctx, + const StringData* key); + void invokeSet(TypedValue* retval, const StringData* key, TypedValue* val); + void invokeGet(TypedValue* retval, const StringData* key); + void invokeGetProp(TypedValue*& retval, TypedValue& tvRef, + const StringData* key); + void invokeIsset(TypedValue* retval, const StringData* key); + void invokeUnset(TypedValue* retval, const StringData* key); + void getProp(const Class* klass, bool pubOnly, const PreClass::Prop* prop, + Array& props, std::vector& inserted) const; + void getProps(const Class* klass, bool pubOnly, const PreClass* pc, + Array& props, std::vector& inserted) const; + public: + void prop(TypedValue*& retval, TypedValue& tvRef, Class* ctx, + const StringData* key); + void propD(TypedValue*& retval, TypedValue& tvRef, Class* ctx, + const StringData* key); + void propW(TypedValue*& retval, TypedValue& tvRef, Class* ctx, + const StringData* key); + void propWD(TypedValue*& retval, TypedValue& tvRef, Class* ctx, + const StringData* key); + bool propIsset(Class* ctx, const StringData* key); + bool propEmpty(Class* ctx, const StringData* key); + + TypedValue* setProp(Class* ctx, const StringData* key, TypedValue* val, + bool bindingAssignment = false); + TypedValue* setOpProp(TypedValue& tvRef, Class* ctx, unsigned char op, + const StringData* key, Cell* val); + private: + template + void incDecPropImpl(TypedValue& tvRef, Class* ctx, unsigned char op, + const StringData* key, TypedValue& dest); + public: + template + void incDecProp(TypedValue& tvRef, Class* ctx, unsigned char op, + const StringData* key, TypedValue& dest); + void unsetProp(Class* ctx, const StringData* key); + + void raiseUndefProp(const StringData* name); + + friend class ObjectData; +}; + +inline Instance* instanceFromTv(TypedValue* tv) { + assert(dynamic_cast(tv->m_data.pobj)); + return static_cast(tv->m_data.pobj); } -#endif // incl_HPHP_OBJECT_DATA_H_ +class ExtObjectData : public HPHP::Instance { + public: + explicit ExtObjectData(HPHP::Class* cls) + : HPHP::Instance(cls, false) { + assert(!m_cls->callsCustomInstanceInit()); + } +}; + +template class ExtObjectDataFlags : public ExtObjectData { + public: + explicit ExtObjectDataFlags(HPHP::Class* cb) : ExtObjectData(cb) { + ObjectData::setAttributes(flags); + } +}; + +} // HPHP + +#endif diff --git a/hphp/runtime/base/resource_data.h b/hphp/runtime/base/resource_data.h index 5e23b9538..b1e62bfba 100644 --- a/hphp/runtime/base/resource_data.h +++ b/hphp/runtime/base/resource_data.h @@ -19,7 +19,6 @@ #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/memory/sweepable.h" -#include "hphp/runtime/vm/instance.h" namespace HPHP { /////////////////////////////////////////////////////////////////////////////// diff --git a/hphp/runtime/base/string_data.h b/hphp/runtime/base/string_data.h index 3d81848ea..09a9f80ad 100644 --- a/hphp/runtime/base/string_data.h +++ b/hphp/runtime/base/string_data.h @@ -429,6 +429,26 @@ ALWAYS_INLINE inline void decRefStr(StringData* s) { if (s->decRefCount() == 0) s->release(); } +struct string_data_hash { + size_t operator()(const StringData *s) const { + return s->hash(); + } +}; + +struct string_data_same { + bool operator()(const StringData *s1, const StringData *s2) const { + assert(s1 && s2); + return s1->same(s2); + } +}; + +struct string_data_isame { + bool operator()(const StringData *s1, const StringData *s2) const { + assert(s1 && s2); + return s1->isame(s2); + } +}; + /////////////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/base/time/datetime.cpp b/hphp/runtime/base/time/datetime.cpp index 3fc7771c5..f6b367bce 100644 --- a/hphp/runtime/base/time/datetime.cpp +++ b/hphp/runtime/base/time/datetime.cpp @@ -21,6 +21,7 @@ #include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/base/type_conversions.h" #include "hphp/runtime/base/builtin_functions.h" +#include "hphp/runtime/base/array/array_init.h" namespace HPHP { diff --git a/hphp/runtime/base/time/timestamp.cpp b/hphp/runtime/base/time/timestamp.cpp index 0b726afdb..403bf77e0 100644 --- a/hphp/runtime/base/time/timestamp.cpp +++ b/hphp/runtime/base/time/timestamp.cpp @@ -17,6 +17,7 @@ #include "hphp/runtime/base/time/timestamp.h" #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/time/datetime.h" +#include "hphp/runtime/base/array/array_init.h" #include namespace HPHP { diff --git a/hphp/runtime/base/time/timezone.cpp b/hphp/runtime/base/time/timezone.cpp index 1619c0ac8..5f926972c 100644 --- a/hphp/runtime/base/time/timezone.cpp +++ b/hphp/runtime/base/time/timezone.cpp @@ -21,6 +21,7 @@ #include "hphp/runtime/base/type_conversions.h" #include "hphp/runtime/base/builtin_functions.h" #include "hphp/runtime/base/runtime_error.h" +#include "hphp/runtime/base/array/array_init.h" #include "hphp/util/logger.h" namespace HPHP { diff --git a/hphp/runtime/base/tv_arith.cpp b/hphp/runtime/base/tv_arith.cpp index 410b40673..65280a8fa 100644 --- a/hphp/runtime/base/tv_arith.cpp +++ b/hphp/runtime/base/tv_arith.cpp @@ -18,6 +18,7 @@ #include #include +#include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/base/tv_conversions.h" namespace HPHP { diff --git a/hphp/runtime/base/tv_comparisons.h b/hphp/runtime/base/tv_comparisons.h index c505536d4..226636dca 100644 --- a/hphp/runtime/base/tv_comparisons.h +++ b/hphp/runtime/base/tv_comparisons.h @@ -16,7 +16,7 @@ #ifndef incl_HPHP_TV_COMPARISONS_H_ #define incl_HPHP_TV_COMPARISONS_H_ -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" namespace HPHP { diff --git a/hphp/runtime/base/type_string.h b/hphp/runtime/base/type_string.h index 1ab6bdb17..bb0aaf371 100644 --- a/hphp/runtime/base/type_string.h +++ b/hphp/runtime/base/type_string.h @@ -442,26 +442,6 @@ extern const String null_string; /////////////////////////////////////////////////////////////////////////////// -struct string_data_hash { - size_t operator()(const StringData *s) const { - return s->hash(); - } -}; - -struct string_data_same { - bool operator()(const StringData *s1, const StringData *s2) const { - assert(s1 && s2); - return s1->same(s2); - } -}; - -struct string_data_isame { - bool operator()(const StringData *s1, const StringData *s2) const { - assert(s1 && s2); - return s1->isame(s2); - } -}; - struct string_data_lt { bool operator()(const StringData *s1, const StringData *s2) const { int len1 = s1->size(); diff --git a/hphp/runtime/base/type_variant.cpp b/hphp/runtime/base/type_variant.cpp index af0b52e45..c4fcb7cb2 100644 --- a/hphp/runtime/base/type_variant.cpp +++ b/hphp/runtime/base/type_variant.cpp @@ -26,7 +26,6 @@ #include "hphp/runtime/base/array/array_iterator.h" #include "hphp/util/parser/hphp.tab.hpp" #include "hphp/runtime/vm/runtime.h" -#include "hphp/runtime/vm/instance.h" #include "hphp/system/systemlib.h" #include "hphp/runtime/ext/ext_collections.h" #include "hphp/runtime/base/tv_arith.h" diff --git a/hphp/runtime/base/types.h b/hphp/runtime/base/types.h index 14c8591eb..0851b5602 100644 --- a/hphp/runtime/base/types.h +++ b/hphp/runtime/base/types.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include namespace HPHP { /////////////////////////////////////////////////////////////////////////////// @@ -423,6 +425,116 @@ public: #define ACCESSPARAMS_DECL AccessFlags::Type flags = AccessFlags::None #define ACCESSPARAMS_IMPL AccessFlags::Type flags +/* + * Non-enumerated version of type for referring to opcodes or the + * bytecode stream. (Use the enum Op in hhbc.h for an enumerated + * version.) + */ +typedef uint8_t Opcode; + +/* + * Program counters in the bytecode interpreter. + * + * Normally points to an Opcode, but has type const uchar* because + * during a given instruction it is incremented while decoding + * immediates and may point to arbitrary bytes. + */ +typedef const uchar* PC; + +/* + * Id type for various components of a unit that have to have unique + * identifiers. For example, function ids, class ids, string literal + * ids. + */ +typedef int Id; +const Id kInvalidId = Id(-1); + +// Bytecode offsets. Used for both absolute offsets and relative +// offsets. +typedef int32_t Offset; +constexpr Offset kInvalidOffset = std::numeric_limits::max(); + +/* + * Various fields in the VM's runtime have indexes that are addressed + * using this "slot" type. For example: methods, properties, class + * constants. + * + * No slot value greater than or equal to kInvalidSlot will actually + * be used for one of these. + */ +typedef uint32_t Slot; +const Slot kInvalidSlot = Slot(-1); + +/* + * Special types that are not relevant to the runtime as a whole. + * The order for public/protected/private matters in numerous places. + * + * Attr unions are directly stored as integers in .hhbc repositories, so + * incompatible changes here require a schema version bump. + * + * AttrTrait on a method means that the method is NOT a constructor, + * even though it may look like one + * + * AttrNoOverride (WholeProgram only) on a class means its not extended + * and on a method means that no extending class defines the method. + * + * AttrVariadicByRef indicates a function is a builtin that takes + * variadic arguments, where the arguments are either by ref or + * optionally by ref. (It is equivalent to having ClassInfo's + * (RefVariableArguments | MixedVariableArguments).) + * + * AttrMayUseVV indicates that a function may need to use a VarEnv or + * varargs (aka extraArgs) at run time. + * + * AttrPhpLeafFn indicates a function does not make any explicit calls + * to other php functions. It may still call other user-level + * functions via re-entry (e.g. for destructors or autoload), and it + * may make calls to builtins using FCallBuiltin. + * + * AttrBuiltin is set on builtin functions - whether c++ or php + * + * AttrAllowOverride is set on builtin functions that can be replaced + * by user implementations + * + * AttrSkipFrame is set to indicate that the frame should be ignored + * when searching for the context (eg array_map evaluates its + * callback in the context of its caller). + */ +enum Attr { + AttrNone = 0, // class property method // + AttrReference = (1 << 0), // X // + AttrPublic = (1 << 1), // X X // + AttrProtected = (1 << 2), // X X // + AttrPrivate = (1 << 3), // X X // + AttrStatic = (1 << 4), // X X // + AttrAbstract = (1 << 5), // X X // + AttrFinal = (1 << 6), // X X // + AttrInterface = (1 << 7), // X // + AttrPhpLeafFn = (1 << 7), // X // + AttrTrait = (1 << 8), // X X // + AttrNoInjection = (1 << 9), // X // + AttrUnique = (1 << 10), // X X // + AttrDynamicInvoke = (1 << 11), // X // + AttrNoExpandTrait = (1 << 12), // X // + AttrNoOverride= (1 << 13), // X X // + AttrClone = (1 << 14), // X // + AttrVariadicByRef = (1 << 15), // X // + AttrMayUseVV = (1 << 16), // X // + AttrPersistent= (1 << 17), // X X // + AttrDeepInit = (1 << 18), // X // + AttrHot = (1 << 19), // X // + AttrBuiltin = (1 << 20), // X // + AttrAllowOverride = (1 << 21), // X // + AttrSkipFrame = (1 << 22), // X // +}; + +inline Attr operator|(Attr a, Attr b) { return Attr((int)a | (int)b); } + +inline const char* attrToVisibilityStr(Attr attr) { + return (attr & AttrPrivate) ? "private" : + (attr & AttrProtected) ? "protected" : "public"; +} + /////////////////////////////////////////////////////////////////////////////// } diff --git a/hphp/runtime/vm/as.cpp b/hphp/runtime/vm/as.cpp index ea58fd8c2..01dfd402d 100644 --- a/hphp/runtime/vm/as.cpp +++ b/hphp/runtime/vm/as.cpp @@ -91,6 +91,7 @@ #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/vm/hhbc.h" +#include "hphp/runtime/vm/preclass-emit.h" #include "hphp/runtime/base/builtin_functions.h" #include "hphp/system/systemlib.h" diff --git a/hphp/runtime/vm/backup_gc.cpp b/hphp/runtime/vm/backup_gc.cpp index ea0647e77..9a88f422c 100644 --- a/hphp/runtime/vm/backup_gc.cpp +++ b/hphp/runtime/vm/backup_gc.cpp @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ #include "hphp/runtime/vm/backup_gc.h" +#include "hphp/runtime/base/runtime_error.h" #include #include diff --git a/hphp/runtime/vm/bytecode.h b/hphp/runtime/vm/bytecode.h index af8e6daa6..47035b404 100644 --- a/hphp/runtime/vm/bytecode.h +++ b/hphp/runtime/vm/bytecode.h @@ -18,17 +18,13 @@ #define incl_HPHP_VM_BYTECODE_H_ #include - #include #include "hphp/util/util.h" #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/class_info.h" #include "hphp/runtime/base/array/array_iterator.h" - -#include "hphp/runtime/vm/core_types.h" #include "hphp/runtime/vm/class.h" -#include "hphp/runtime/vm/instance.h" #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/vm/func.h" #include "hphp/runtime/vm/name_value_table.h" diff --git a/hphp/runtime/vm/class.cpp b/hphp/runtime/vm/class.cpp index 0a3fa8f7b..713b8cdaa 100644 --- a/hphp/runtime/vm/class.cpp +++ b/hphp/runtime/vm/class.cpp @@ -13,19 +13,13 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ -#include "hphp/runtime/vm/class.h" - -#include "hphp/runtime/vm/class.h" -#include "hphp/runtime/base/base_includes.h" +#include "hphp/runtime/base/complex_types.h" +#include "hphp/runtime/base/comparisons.h" #include "hphp/runtime/base/array/hphp_array.h" #include "hphp/util/util.h" #include "hphp/util/debug.h" -#include "hphp/runtime/vm/core_types.h" -#include "hphp/runtime/vm/hhbc.h" -#include "hphp/runtime/vm/repo.h" #include "hphp/runtime/vm/jit/targetcache.h" #include "hphp/runtime/vm/jit/translator.h" -#include "hphp/runtime/vm/blob_helper.h" #include "hphp/runtime/vm/treadmill.h" #include "hphp/runtime/vm/name_value_table.h" #include "hphp/runtime/vm/name_value_table_wrapper.h" @@ -54,9 +48,9 @@ Class::InstanceBitsMap Class::s_instanceBits; ReadWriteMutex Class::s_instanceBitsLock(RankInstanceBits); std::atomic Class::s_instanceBitsInit{false}; -static const StringData* manglePropName(const StringData* className, - const StringData* propName, - Attr attrs) { +const StringData* PreClass::manglePropName(const StringData* className, + const StringData* propName, + Attr attrs) { switch (attrs & (AttrPublic|AttrProtected|AttrPrivate)) { case AttrPublic: { return propName; @@ -198,315 +192,6 @@ void PreClass::prettyPrint(std::ostream &out) const { } } -//============================================================================= -// PreClassEmitter::Prop. - -PreClassEmitter::Prop::Prop(const PreClassEmitter* pce, - const StringData* n, - Attr attrs, - const StringData* typeConstraint, - const StringData* docComment, - TypedValue* val, - DataType hphpcType) - : m_name(n) - , m_attrs(attrs) - , m_typeConstraint(typeConstraint) - , m_docComment(docComment) - , m_hphpcType(hphpcType) -{ - m_mangledName = manglePropName(pce->name(), n, attrs); - memcpy(&m_val, val, sizeof(TypedValue)); -} - -PreClassEmitter::Prop::~Prop() { -} - -//============================================================================= -// PreClassEmitter. - -PreClassEmitter::PreClassEmitter(UnitEmitter& ue, - Id id, - const StringData* n, - PreClass::Hoistable hoistable) - : m_ue(ue) - , m_name(n) - , m_id(id) - , m_hoistable(hoistable) - , m_InstanceCtor(nullptr) - , m_builtinPropSize(0) -{} - -void PreClassEmitter::init(int line1, int line2, Offset offset, Attr attrs, - const StringData* parent, - const StringData* docComment) { - m_line1 = line1; - m_line2 = line2; - m_offset = offset; - m_attrs = attrs; - m_parent = parent; - m_docComment = docComment; -} - -PreClassEmitter::~PreClassEmitter() { - for (MethodVec::const_iterator it = m_methods.begin(); - it != m_methods.end(); ++it) { - delete *it; - } -} - -void PreClassEmitter::addInterface(const StringData* n) { - m_interfaces.push_back(n); -} - -bool PreClassEmitter::addMethod(FuncEmitter* method) { - MethodMap::const_iterator it = m_methodMap.find(method->name()); - if (it != m_methodMap.end()) { - return false; - } - m_methods.push_back(method); - m_methodMap[method->name()] = method; - return true; -} - -bool PreClassEmitter::addProperty(const StringData* n, Attr attrs, - const StringData* typeConstraint, - const StringData* docComment, - TypedValue* val, - DataType hphpcType) { - PropMap::Builder::const_iterator it = m_propMap.find(n); - if (it != m_propMap.end()) { - return false; - } - PreClassEmitter::Prop prop(this, n, attrs, typeConstraint, docComment, val, - hphpcType); - m_propMap.add(prop.name(), prop); - return true; -} - -const PreClassEmitter::Prop& -PreClassEmitter::lookupProp(const StringData* propName) const { - PropMap::Builder::const_iterator it = m_propMap.find(propName); - assert(it != m_propMap.end()); - Slot idx = it->second; - return m_propMap[idx]; -} - -bool PreClassEmitter::addConstant(const StringData* n, - const StringData* typeConstraint, - TypedValue* val, - const StringData* phpCode) { - ConstMap::Builder::const_iterator it = m_constMap.find(n); - if (it != m_constMap.end()) { - return false; - } - PreClassEmitter::Const const_(n, typeConstraint, val, phpCode); - m_constMap.add(const_.name(), const_); - return true; -} - -void PreClassEmitter::addUsedTrait(const StringData* traitName) { - m_usedTraits.push_back(traitName); -} - -void PreClassEmitter::addTraitPrecRule( - const PreClass::TraitPrecRule &rule) { - m_traitPrecRules.push_back(rule); -} - -void PreClassEmitter::addTraitAliasRule( - const PreClass::TraitAliasRule &rule) { - m_traitAliasRules.push_back(rule); -} - -void PreClassEmitter::addUserAttribute(const StringData* name, TypedValue tv) { - m_userAttributes[name] = tv; -} - -void PreClassEmitter::commit(RepoTxn& txn) const { - Repo& repo = Repo::get(); - PreClassRepoProxy& pcrp = repo.pcrp(); - int repoId = m_ue.repoId(); - int64_t usn = m_ue.sn(); - pcrp.insertPreClass(repoId) - .insert(*this, txn, usn, m_id, m_name, m_hoistable); - - for (MethodVec::const_iterator it = m_methods.begin(); - it != m_methods.end(); ++it) { - (*it)->commit(txn); - } -} - -void PreClassEmitter::setBuiltinClassInfo(const ClassInfo* info, - BuiltinCtorFunction ctorFunc, - int sz) { - if (info->getAttribute() & ClassInfo::IsFinal) { - m_attrs = m_attrs | AttrFinal; - } - if (info->getAttribute() & ClassInfo::IsAbstract) { - m_attrs = m_attrs | AttrAbstract; - } - if (info->getAttribute() & ClassInfo::IsTrait) { - m_attrs = m_attrs | AttrTrait; - } - m_attrs = m_attrs | AttrUnique; - m_InstanceCtor = ctorFunc; - m_builtinPropSize = sz - sizeof(ObjectData); -} - -PreClass* PreClassEmitter::create(Unit& unit) const { - Attr attrs = m_attrs; - if (attrs & AttrPersistent && - !RuntimeOption::RepoAuthoritative && SystemLib::s_inited) { - attrs = Attr(attrs & ~AttrPersistent); - } - PreClass* pc = new PreClass(&unit, m_line1, m_line2, m_offset, m_name, - attrs, m_parent, m_docComment, m_id, - m_hoistable); - pc->m_InstanceCtor = m_InstanceCtor; - pc->m_builtinPropSize = m_builtinPropSize; - pc->m_interfaces = m_interfaces; - pc->m_usedTraits = m_usedTraits; - pc->m_traitPrecRules = m_traitPrecRules; - pc->m_traitAliasRules = m_traitAliasRules; - pc->m_userAttributes = m_userAttributes; - - PreClass::MethodMap::Builder methodBuild; - for (MethodVec::const_iterator it = m_methods.begin(); - it != m_methods.end(); ++it) { - Func* f = (*it)->create(unit, pc); - methodBuild.add(f->name(), f); - } - pc->m_methods.create(methodBuild); - - PreClass::PropMap::Builder propBuild; - for (unsigned i = 0; i < m_propMap.size(); ++i) { - const Prop& prop = m_propMap[i]; - propBuild.add(prop.name(), PreClass::Prop(pc, - prop.name(), - prop.attrs(), - prop.typeConstraint(), - prop.docComment(), - prop.val(), - prop.hphpcType())); - } - pc->m_properties.create(propBuild); - - PreClass::ConstMap::Builder constBuild; - for (unsigned i = 0; i < m_constMap.size(); ++i) { - const Const& const_ = m_constMap[i]; - constBuild.add(const_.name(), PreClass::Const(pc, - const_.name(), - const_.typeConstraint(), - const_.val(), - const_.phpCode())); - } - pc->m_constants.create(constBuild); - return pc; -} - -template void PreClassEmitter::serdeMetaData(SerDe& sd) { - // NOTE: name, hoistable, and a few other fields currently - // serialized outside of this. - sd(m_line1) - (m_line2) - (m_offset) - (m_attrs) - (m_parent) - (m_docComment) - - (m_interfaces) - (m_usedTraits) - (m_traitPrecRules) - (m_traitAliasRules) - (m_userAttributes) - (m_propMap) - (m_constMap) - ; -} - -//============================================================================= -// PreClassRepoProxy. - -PreClassRepoProxy::PreClassRepoProxy(Repo& repo) - : RepoProxy(repo) -#define PCRP_OP(c, o) \ - , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral) - PCRP_OPS -#undef PCRP_OP -{ -#define PCRP_OP(c, o) \ - m_##o[RepoIdLocal] = &m_##o##Local; \ - m_##o[RepoIdCentral] = &m_##o##Central; - PCRP_OPS -#undef PCRP_OP -} - -PreClassRepoProxy::~PreClassRepoProxy() { -} - -void PreClassRepoProxy::createSchema(int repoId, RepoTxn& txn) { - { - std::stringstream ssCreate; - ssCreate << "CREATE TABLE " << m_repo.table(repoId, "PreClass") - << "(unitSn INTEGER, preClassId INTEGER, name TEXT," - " hoistable INTEGER, extraData BLOB," - " PRIMARY KEY (unitSn, preClassId));"; - txn.exec(ssCreate.str()); - } -} - -void PreClassRepoProxy::InsertPreClassStmt - ::insert(const PreClassEmitter& pce, RepoTxn& txn, - int64_t unitSn, Id preClassId, - const StringData* name, - PreClass::Hoistable hoistable) { - if (!prepared()) { - std::stringstream ssInsert; - ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "PreClass") - << " VALUES(@unitSn, @preClassId, @name, @hoistable, " - "@extraData);"; - txn.prepare(*this, ssInsert.str()); - } - - BlobEncoder extraBlob; - RepoTxnQuery query(txn, *this); - query.bindInt64("@unitSn", unitSn); - query.bindId("@preClassId", preClassId); - query.bindStaticString("@name", name); - query.bindInt("@hoistable", hoistable); - const_cast(pce).serdeMetaData(extraBlob); - query.bindBlob("@extraData", extraBlob, /* static */ true); - query.exec(); -} - -void PreClassRepoProxy::GetPreClassesStmt - ::get(UnitEmitter& ue) { - RepoTxn txn(m_repo); - if (!prepared()) { - std::stringstream ssSelect; - ssSelect << "SELECT preClassId,name,hoistable,extraData FROM " - << m_repo.table(m_repoId, "PreClass") - << " WHERE unitSn == @unitSn ORDER BY preClassId ASC;"; - txn.prepare(*this, ssSelect.str()); - } - RepoTxnQuery query(txn, *this); - query.bindInt64("@unitSn", ue.sn()); - do { - query.step(); - if (query.row()) { - Id preClassId; /**/ query.getId(0, preClassId); - StringData* name; /**/ query.getStaticString(1, name); - int hoistable; /**/ query.getInt(2, hoistable); - BlobDecoder extraBlob = /**/ query.getBlob(3); - PreClassEmitter* pce = ue.newPreClassEmitter( - name, (PreClass::Hoistable)hoistable); - pce->serdeMetaData(extraBlob); - assert(pce->id() == preClassId); - } - } while (!query.done()); - txn.commit(); -} - //============================================================================= // Class. @@ -2090,8 +1775,9 @@ void Class::importTraitInstanceProp(ClassPtr trait, prop.m_class = this; // set current class as the first declaring prop // private props' mangled names contain the class name, so regenerate them if (prop.m_attrs & AttrPrivate) { - prop.m_mangledName = manglePropName(m_preClass->name(), prop.m_name, - prop.m_attrs); + prop.m_mangledName = PreClass::manglePropName(m_preClass->name(), + prop.m_name, + prop.m_attrs); } curPropMap.add(prop.m_name, prop); m_declPropInit.push_back(traitPropVal); @@ -2547,6 +2233,12 @@ void Class::getClassInfo(ClassInfoVM* ci) { } } +size_t Class::declPropOffset(Slot index) const { + assert(index >= 0); + return sizeof(ObjectData) + m_builtinPropSize + + index * sizeof(TypedValue); +} + Class::PropInitVec::~PropInitVec() { if (!m_smart) free(m_data); } diff --git a/hphp/runtime/vm/class.h b/hphp/runtime/vm/class.h index ddcd28b8a..5419b5a9e 100644 --- a/hphp/runtime/vm/class.h +++ b/hphp/runtime/vm/class.h @@ -21,15 +21,11 @@ #include "tbb/concurrent_hash_map.h" #include -#include "hphp/runtime/vm/core_types.h" -#include "hphp/runtime/vm/repo_helpers.h" -#include "hphp/runtime/base/runtime_option.h" -#include "hphp/util/parser/location.h" +#include "hphp/runtime/base/types.h" #include "hphp/util/fixed_vector.h" #include "hphp/util/range.h" #include "hphp/runtime/vm/fixed_string_map.h" #include "hphp/runtime/vm/indexed_string_map.h" -#include "hphp/runtime/vm/named_entity.h" namespace HPHP { @@ -319,6 +315,10 @@ class PreClass : public AtomicCountable { NamedEntity* namedEntity() const { return m_namedEntity; } + static const StringData* manglePropName(const StringData* className, + const StringData* propName, + Attr attrs); + private: typedef IndexedStringMap MethodMap; typedef IndexedStringMap PropMap; @@ -354,194 +354,6 @@ private: // use AtomicSmartPtr's to enforce this invariant. typedef AtomicSmartPtr PreClassPtr; -class PreClassEmitter { - public: - typedef std::vector MethodVec; - - class Prop { - public: - Prop() - : m_name(0) - , m_mangledName(0) - , m_attrs(AttrNone) - , m_typeConstraint(0) - , m_docComment(0) - , m_hphpcType(KindOfInvalid) - {} - - Prop(const PreClassEmitter* pce, - const StringData* n, - Attr attrs, - const StringData* typeConstraint, - const StringData* docComment, - TypedValue* val, - DataType hphpcType); - ~Prop(); - - const StringData* name() const { return m_name; } - const StringData* mangledName() const { return m_mangledName; } - Attr attrs() const { return m_attrs; } - const StringData* typeConstraint() const { return m_typeConstraint; } - const StringData* docComment() const { return m_docComment; } - const TypedValue& val() const { return m_val; } - DataType hphpcType() const { return m_hphpcType; } - - template void serde(SerDe& sd) { - sd(m_name) - (m_mangledName) - (m_attrs) - (m_typeConstraint) - (m_docComment) - (m_val) - (m_hphpcType) - ; - } - - private: - const StringData* m_name; - const StringData* m_mangledName; - Attr m_attrs; - const StringData* m_typeConstraint; - const StringData* m_docComment; - TypedValue m_val; - DataType m_hphpcType; - }; - - class Const { - public: - Const() - : m_name(0) - , m_typeConstraint(0) - , m_phpCode(0) - {} - Const(const StringData* n, const StringData* typeConstraint, - TypedValue* val, const StringData* phpCode) - : m_name(n), m_typeConstraint(typeConstraint), m_phpCode(phpCode) { - memcpy(&m_val, val, sizeof(TypedValue)); - } - ~Const() {} - - const StringData* name() const { return m_name; } - const StringData* typeConstraint() const { return m_typeConstraint; } - const TypedValue& val() const { return m_val; } - const StringData* phpCode() const { return m_phpCode; } - - template void serde(SerDe& sd) { - sd(m_name)(m_val)(m_phpCode); - } - - private: - const StringData* m_name; - const StringData* m_typeConstraint; - TypedValue m_val; - const StringData* m_phpCode; - }; - - PreClassEmitter(UnitEmitter& ue, Id id, const StringData* n, - PreClass::Hoistable hoistable); - ~PreClassEmitter(); - - void init(int line1, int line2, Offset offset, Attr attrs, - const StringData* parent, const StringData* docComment); - - UnitEmitter& ue() const { return m_ue; } - const StringData* name() const { return m_name; } - Attr attrs() const { return m_attrs; } - void setHoistable(PreClass::Hoistable h) { m_hoistable = h; } - Id id() const { return m_id; } - const MethodVec& methods() const { return m_methods; } - - void addInterface(const StringData* n); - bool addMethod(FuncEmitter* method); - bool addProperty(const StringData* n, - Attr attrs, - const StringData* typeConstraint, - const StringData* docComment, - TypedValue* val, - DataType hphpcType); - const Prop& lookupProp(const StringData* propName) const; - bool addConstant(const StringData* n, const StringData* typeConstraint, - TypedValue* val, const StringData* phpCode); - void addUsedTrait(const StringData* traitName); - void addTraitPrecRule(const PreClass::TraitPrecRule &rule); - void addTraitAliasRule(const PreClass::TraitAliasRule &rule); - void addUserAttribute(const StringData* name, TypedValue tv); - void commit(RepoTxn& txn) const; - - void setBuiltinClassInfo(const ClassInfo* info, - BuiltinCtorFunction ctorFunc, - int sz); - - PreClass* create(Unit& unit) const; - - template void serdeMetaData(SerDe&); - - private: - typedef IndexedStringMap PropMap; - typedef IndexedStringMap ConstMap; - typedef hphp_hash_map MethodMap; - - UnitEmitter& m_ue; - int m_line1; - int m_line2; - Offset m_offset; - const StringData* m_name; - Attr m_attrs; - const StringData* m_parent; - const StringData* m_docComment; - Id m_id; - PreClass::Hoistable m_hoistable; - BuiltinCtorFunction m_InstanceCtor; - int m_builtinPropSize; - - std::vector m_interfaces; - std::vector m_usedTraits; - std::vector m_traitPrecRules; - std::vector m_traitAliasRules; - PreClass::UserAttributeMap m_userAttributes; - MethodVec m_methods; - MethodMap m_methodMap; - PropMap::Builder m_propMap; - ConstMap::Builder m_constMap; -}; - -class PreClassRepoProxy : public RepoProxy { - friend class PreClass; - friend class PreClassEmitter; - public: - explicit PreClassRepoProxy(Repo& repo); - ~PreClassRepoProxy(); - void createSchema(int repoId, RepoTxn& txn); - -#define PCRP_IOP(o) PCRP_OP(Insert##o, insert##o) -#define PCRP_GOP(o) PCRP_OP(Get##o, get##o) -#define PCRP_OPS \ - PCRP_IOP(PreClass) \ - PCRP_GOP(PreClasses) - class InsertPreClassStmt : public RepoProxy::Stmt { - public: - InsertPreClassStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {} - void insert(const PreClassEmitter& pce, RepoTxn& txn, int64_t unitSn, - Id preClassId, const StringData* name, - PreClass::Hoistable hoistable); - }; - class GetPreClassesStmt : public RepoProxy::Stmt { - public: - GetPreClassesStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {} - void get(UnitEmitter& ue); - }; -#define PCRP_OP(c, o) \ - public: \ - c##Stmt& o(int repoId) { return *m_##o[repoId]; } \ - private: \ - c##Stmt m_##o##Local; \ - c##Stmt m_##o##Central; \ - c##Stmt* m_##o[RepoIdCount]; - PCRP_OPS -#undef PCRP_OP -}; - /* * Class represents the full definition of a user class in a given * request context. @@ -832,11 +644,7 @@ public: void getClassInfo(ClassInfoVM* ci); - size_t declPropOffset(Slot index) const { - assert(index >= 0); - return sizeof(ObjectData) + m_builtinPropSize - + index * sizeof(TypedValue); - } + size_t declPropOffset(Slot index) const; DataType declPropHphpcType(Slot index) const { auto& prop = m_declProperties[index]; diff --git a/hphp/runtime/vm/core_types.h b/hphp/runtime/vm/core_types.h deleted file mode 100644 index bdb420b39..000000000 --- a/hphp/runtime/vm/core_types.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | HipHop for PHP | - +----------------------------------------------------------------------+ - | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ -*/ - -#ifndef incl_HPHP_VM_CORE_TYPES_H_ -#define incl_HPHP_VM_CORE_TYPES_H_ - -#include - -#include "hphp/runtime/base/complex_types.h" - -namespace HPHP { -/////////////////////////////////////////////////////////////////////////////// - -/* - * Non-enumerated version of type for referring to opcodes or the - * bytecode stream. (Use the enum Op in hhbc.h for an enumerated - * version.) - */ -typedef uint8_t Opcode; - -/* - * Program counters in the bytecode interpreter. - * - * Normally points to an Opcode, but has type const uchar* because - * during a given instruction it is incremented while decoding - * immediates and may point to arbitrary bytes. - */ -typedef const uchar* PC; - -/* - * Id type for various components of a unit that have to have unique - * identifiers. For example, function ids, class ids, string literal - * ids. - */ -typedef int Id; -const Id kInvalidId = Id(-1); - -// Bytecode offsets. Used for both absolute offsets and relative -// offsets. -typedef int32_t Offset; -constexpr Offset kInvalidOffset = std::numeric_limits::max(); - -/* - * Various fields in the VM's runtime have indexes that are addressed - * using this "slot" type. For example: methods, properties, class - * constants. - * - * No slot value greater than or equal to kInvalidSlot will actually - * be used for one of these. - */ -typedef uint32_t Slot; -const Slot kInvalidSlot = Slot(-1); - -/* - * Special types that are not relevant to the runtime as a whole. - * The order for public/protected/private matters in numerous places. - * - * Attr unions are directly stored as integers in .hhbc repositories, so - * incompatible changes here require a schema version bump. - * - * AttrTrait on a method means that the method is NOT a constructor, - * even though it may look like one - * - * AttrNoOverride (WholeProgram only) on a class means its not extended - * and on a method means that no extending class defines the method. - * - * AttrVariadicByRef indicates a function is a builtin that takes - * variadic arguments, where the arguments are either by ref or - * optionally by ref. (It is equivalent to having ClassInfo's - * (RefVariableArguments | MixedVariableArguments).) - * - * AttrMayUseVV indicates that a function may need to use a VarEnv or - * varargs (aka extraArgs) at run time. - * - * AttrPhpLeafFn indicates a function does not make any explicit calls - * to other php functions. It may still call other user-level - * functions via re-entry (e.g. for destructors or autoload), and it - * may make calls to builtins using FCallBuiltin. - * - * AttrBuiltin is set on builtin functions - whether c++ or php - * - * AttrAllowOverride is set on builtin functions that can be replaced - * by user implementations - * - * AttrSkipFrame is set to indicate that the frame should be ignored - * when searching for the context (eg array_map evaluates its - * callback in the context of its caller). - */ -enum Attr { - AttrNone = 0, // class property method // - AttrReference = (1 << 0), // X // - AttrPublic = (1 << 1), // X X // - AttrProtected = (1 << 2), // X X // - AttrPrivate = (1 << 3), // X X // - AttrStatic = (1 << 4), // X X // - AttrAbstract = (1 << 5), // X X // - AttrFinal = (1 << 6), // X X // - AttrInterface = (1 << 7), // X // - AttrPhpLeafFn = (1 << 7), // X // - AttrTrait = (1 << 8), // X X // - AttrNoInjection = (1 << 9), // X // - AttrUnique = (1 << 10), // X X // - AttrDynamicInvoke = (1 << 11), // X // - AttrNoExpandTrait = (1 << 12), // X // - AttrNoOverride= (1 << 13), // X X // - AttrClone = (1 << 14), // X // - AttrVariadicByRef = (1 << 15), // X // - AttrMayUseVV = (1 << 16), // X // - AttrPersistent= (1 << 17), // X X // - AttrDeepInit = (1 << 18), // X // - AttrHot = (1 << 19), // X // - AttrBuiltin = (1 << 20), // X // - AttrAllowOverride = (1 << 21), // X // - AttrSkipFrame = (1 << 22), // X // -}; - -inline Attr operator|(Attr a, Attr b) { return Attr((int)a | (int)b); } - -inline const char * attrToVisibilityStr(Attr attr) { - return (attr & AttrPrivate) ? "private" : - (attr & AttrProtected) ? "protected" : "public"; -} - -} - -#endif diff --git a/hphp/runtime/vm/fixed_string_map.cpp b/hphp/runtime/vm/fixed_string_map.cpp index 64b15d737..9803cc07f 100644 --- a/hphp/runtime/vm/fixed_string_map.cpp +++ b/hphp/runtime/vm/fixed_string_map.cpp @@ -15,7 +15,7 @@ */ #include "hphp/runtime/vm/fixed_string_map.h" -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/base/macros.h" namespace HPHP { diff --git a/hphp/runtime/vm/func.cpp b/hphp/runtime/vm/func.cpp index 07213e2a5..150832702 100644 --- a/hphp/runtime/vm/func.cpp +++ b/hphp/runtime/vm/func.cpp @@ -23,7 +23,7 @@ #include "hphp/util/trace.h" #include "hphp/util/debug.h" #include "hphp/runtime/base/strings.h" -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/vm/runtime.h" #include "hphp/runtime/vm/repo.h" #include "hphp/runtime/vm/jit/targetcache.h" diff --git a/hphp/runtime/vm/func.h b/hphp/runtime/vm/func.h index 682b90f8b..31a328736 100644 --- a/hphp/runtime/vm/func.h +++ b/hphp/runtime/vm/func.h @@ -29,6 +29,7 @@ const int kNumFixedPrologues = 6; struct ActRec; typedef TypedValue*(*BuiltinFunction)(ActRec* ar); +class PreClassEmitter; /* * Unique identifier for a Func*. diff --git a/hphp/runtime/vm/funcdict.cpp b/hphp/runtime/vm/funcdict.cpp index 3c3d4ec11..7010e2bea 100644 --- a/hphp/runtime/vm/funcdict.cpp +++ b/hphp/runtime/vm/funcdict.cpp @@ -20,6 +20,7 @@ #include "hphp/util/base.h" #include "hphp/runtime/base/execution_context.h" +#include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/ext_hhvm/ext_hhvm.h" #include "hphp/runtime/vm/jit/translator.h" #include "hphp/runtime/vm/jit/targetcache.h" diff --git a/hphp/runtime/vm/hhbc.h b/hphp/runtime/vm/hhbc.h index 7455981d3..7884f2b8a 100644 --- a/hphp/runtime/vm/hhbc.h +++ b/hphp/runtime/vm/hhbc.h @@ -17,7 +17,7 @@ #ifndef incl_HPHP_VM_HHBC_H_ #define incl_HPHP_VM_HHBC_H_ -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" namespace HPHP { diff --git a/hphp/runtime/vm/instance.cpp b/hphp/runtime/vm/instance.cpp deleted file mode 100644 index 549461c62..000000000 --- a/hphp/runtime/vm/instance.cpp +++ /dev/null @@ -1,816 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | HipHop for PHP | - +----------------------------------------------------------------------+ - | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ -*/ - -#include "hphp/runtime/vm/instance.h" -#include "hphp/runtime/base/base_includes.h" -#include "hphp/runtime/base/variable_serializer.h" -#include "hphp/runtime/vm/core_types.h" -#include "hphp/runtime/vm/member_operations.h" -#include "hphp/runtime/vm/hhbc.h" -#include "hphp/runtime/vm/class.h" -#include "hphp/runtime/vm/object_allocator_sizes.h" -#include "hphp/runtime/vm/jit/translator-inline.h" -#include "hphp/runtime/ext/ext_collections.h" -#include "hphp/system/systemlib.h" - -namespace HPHP { - -static StaticString s___get(LITSTR_INIT("__get")); -static StaticString s___set(LITSTR_INIT("__set")); -static StaticString s___isset(LITSTR_INIT("__isset")); -static StaticString s___unset(LITSTR_INIT("__unset")); -static StaticString s___call(LITSTR_INIT("__call")); -static StaticString s___callStatic(LITSTR_INIT("__callStatic")); - -TRACE_SET_MOD(runtime); - -int HPHP::Instance::ObjAllocatorSizeClassCount = - HPHP::InitializeAllocators(); - -void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData, - size_t nProps) { - auto* dst = propVec; - auto* src = propData; - for (; src != propData + nProps; ++src, ++dst) { - *dst = *src; - // m_aux.u_deepInit is true for properties that need "deep" initialization - if (src->deepInit()) { - tvIncRef(dst); - collectionDeepCopyTV(dst); - } - } -} - -TypedValue* Instance::propVec() { - uintptr_t ret = (uintptr_t)this + sizeof(ObjectData) + builtinPropSize(); - // TODO(#1432007): some builtins still do not have TypedValue-aligned sizes. - assert(ret % sizeof(TypedValue) == builtinPropSize() % sizeof(TypedValue)); - return (TypedValue*) ret; -} - -const TypedValue* Instance::propVec() const { - return const_cast(this)->propVec(); -} - -Instance* Instance::callCustomInstanceInit() { - static StringData* sd_init = StringData::GetStaticString("__init__"); - const Func* init = m_cls->lookupMethod(sd_init); - if (init != nullptr) { - TypedValue tv; - // We need to incRef/decRef here because we're still a new (_count - // == 0) object and invokeFunc is going to expect us to have a - // reasonable refcount. - try { - incRefCount(); - g_vmContext->invokeFuncFew(&tv, init, this); - decRefCount(); - assert(!IS_REFCOUNTED_TYPE(tv.m_type)); - } catch (...) { - this->setNoDestruct(); - decRefObj(this); - throw; - } - } - return this; -} - -HOT_FUNC_VM -Instance* Instance::newInstanceRaw(Class* cls, int idx) { - Instance* obj = (Instance*)ALLOCOBJIDX(idx); - new (obj) Instance(cls, NoInit::noinit); - return obj; -} - -void Instance::invokeUserMethod(TypedValue* retval, const Func* method, - CArrRef params) { - g_vmContext->invokeFunc(retval, method, params, this); -} - -Object Instance::FromArray(ArrayData *properties) { - Instance* retval = Instance::newInstance(SystemLib::s_stdclassClass); - retval->initDynProps(); - HphpArray* props = static_cast(retval->o_properties.get()); - for (ssize_t pos = properties->iter_begin(); pos != ArrayData::invalid_index; - pos = properties->iter_advance(pos)) { - TypedValue* value = properties->nvGetValueRef(pos); - TypedValue key; - properties->nvGetKey(&key, pos); - if (key.m_type == KindOfInt64) { - props->set(key.m_data.num, tvAsCVarRef(value), false); - } else { - assert(IS_STRING_TYPE(key.m_type)); - StringData* strKey = key.m_data.pstr; - props->set(strKey, tvAsCVarRef(value), false); - decRefStr(strKey); - } - } - return retval; -} - -void Instance::initDynProps(int numDynamic /* = 0 */) { - // Create o_properties with room for numDynamic - o_properties.asArray() = ArrayData::Make(numDynamic); -} - -Slot Instance::declPropInd(TypedValue* prop) const { - // Do an address range check to determine whether prop physically resides - // in propVec. - const TypedValue* pv = propVec(); - if (prop >= pv && prop < &pv[m_cls->numDeclProperties()]) { - return prop - pv; - } else { - return kInvalidSlot; - } -} - -template -TypedValue* Instance::getPropImpl(Class* ctx, const StringData* key, - bool& visible, bool& accessible, - bool& unset) { - TypedValue* prop = nullptr; - unset = false; - Slot propInd = m_cls->getDeclPropIndex(ctx, key, accessible); - visible = (propInd != kInvalidSlot); - if (propInd != kInvalidSlot) { - // We found a visible property, but it might not be accessible. - // No need to check if there is a dynamic property with this name. - prop = &propVec()[propInd]; - if (prop->m_type == KindOfUninit) { - unset = true; - } - } else { - assert(!visible && !accessible); - // We could not find a visible property. We need to check for a - // dynamic property with this name if declOnly = false. - if (!declOnly && o_properties.get()) { - prop = static_cast(o_properties.get())->nvGet(key); - if (prop) { - // o_properties.get()->nvGet() returned a non-declared property, - // we know that it is visible and accessible (since all - // dynamic properties are), and we know it is not unset - // (since unset dynamic properties don't appear in o_properties.get()). - visible = true; - accessible = true; - } - } - } - return prop; -} - -TypedValue* Instance::getProp(Class* ctx, const StringData* key, - bool& visible, bool& accessible, bool& unset) { - return getPropImpl(ctx, key, visible, accessible, unset); -} - -TypedValue* Instance::getDeclProp(Class* ctx, const StringData* key, - bool& visible, bool& accessible, - bool& unset) { - return getPropImpl(ctx, key, visible, accessible, unset); -} - -void Instance::invokeSet(TypedValue* retval, const StringData* key, - TypedValue* val) { - AttributeClearer a(UseSet, this); - const Func* meth = m_cls->lookupMethod(s___set.get()); - assert(meth); - invokeUserMethod(retval, meth, - CREATE_VECTOR2(CStrRef(key), tvAsVariant(val))); -} - -#define MAGIC_PROP_BODY(name, attr) \ - AttributeClearer a((attr), this); \ - const Func* meth = m_cls->lookupMethod(name); \ - assert(meth); \ - invokeUserMethod(retval, meth, CREATE_VECTOR1(CStrRef(key))); \ - -void Instance::invokeGet(TypedValue* retval, const StringData* key) { - MAGIC_PROP_BODY(s___get.get(), UseGet); -} - -void Instance::invokeIsset(TypedValue* retval, const StringData* key) { - MAGIC_PROP_BODY(s___isset.get(), UseIsset); -} - -void Instance::invokeUnset(TypedValue* retval, const StringData* key) { - MAGIC_PROP_BODY(s___unset.get(), UseUnset); -} - -void Instance::invokeGetProp(TypedValue*& retval, TypedValue& tvRef, - const StringData* key) { - invokeGet(&tvRef, key); - retval = &tvRef; -} - -template -void Instance::propImpl(TypedValue*& retval, TypedValue& tvRef, - Class* ctx, - const StringData* key) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - - if (visible) { - if (accessible) { - if (unset) { - if (getAttribute(UseGet)) { - invokeGetProp(retval, tvRef, key); - } else { - if (warn) { - raiseUndefProp(key); - } - if (define) { - retval = propVal; - } else { - retval = (TypedValue*)&init_null_variant; - } - } - } else { - retval = propVal; - } - } else { - if (getAttribute(UseGet)) { - invokeGetProp(retval, tvRef, key); - } else { - // No need to check hasProp since visible is true - // Visibility is either protected or private since accessible is false - Slot propInd = m_cls->lookupDeclProp(key); - bool priv = m_cls->declProperties()[propInd].m_attrs & AttrPrivate; - - raise_error("Cannot access %s property %s::$%s", - priv ? "private" : "protected", - m_cls->m_preClass->name()->data(), - key->data()); - } - } - } else if (UNLIKELY(!*key->data())) { - throw_invalid_property_name(StrNR(key)); - } else { - if (getAttribute(UseGet)) { - invokeGetProp(retval, tvRef, key); - } else { - if (warn) { - raiseUndefProp(key); - } - if (define) { - if (o_properties.get() == nullptr) { - initDynProps(); - } - o_properties.get()->createLvalPtr(*(const String*)&key, - *(Variant**)(&retval), false); - } else { - retval = (TypedValue*)&init_null_variant; - } - } - } -} - -void Instance::prop(TypedValue*& retval, TypedValue& tvRef, - Class* ctx, const StringData* key) { - propImpl(retval, tvRef, ctx, key); -} - -void Instance::propD(TypedValue*& retval, TypedValue& tvRef, - Class* ctx, const StringData* key) { - propImpl(retval, tvRef, ctx, key); -} - -void Instance::propW(TypedValue*& retval, TypedValue& tvRef, - Class* ctx, const StringData* key) { - propImpl(retval, tvRef, ctx, key); -} - -void Instance::propWD(TypedValue*& retval, TypedValue& tvRef, - Class* ctx, const StringData* key) { - propImpl(retval, tvRef, ctx, key); -} - -bool Instance::propIsset(Class* ctx, const StringData* key) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - if (visible && accessible && !unset) { - return isset(tvAsCVarRef(propVal)); - } - if (!getAttribute(UseIsset)) { - return false; - } - TypedValue tv; - tvWriteUninit(&tv); - invokeIsset(&tv, key); - tvCastToBooleanInPlace(&tv); - return tv.m_data.num; -} - -bool Instance::propEmpty(Class* ctx, const StringData* key) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - if (visible && accessible && !unset) { - return empty(tvAsCVarRef(propVal)); - } - if (!getAttribute(UseIsset)) { - return true; - } - TypedValue tv; - tvWriteUninit(&tv); - invokeIsset(&tv, key); - tvCastToBooleanInPlace(&tv); - if (!tv.m_data.num) { - return true; - } - if (getAttribute(UseGet)) { - invokeGet(&tv, key); - bool emptyResult = empty(tvAsCVarRef(&tv)); - tvRefcountedDecRef(&tv); - return emptyResult; - } - return false; -} - -TypedValue* Instance::setProp(Class* ctx, const StringData* key, - TypedValue* val, - bool bindingAssignment /* = false */) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - if (visible && accessible) { - assert(propVal); - if (unset && getAttribute(UseSet)) { - TypedValue ignored; - invokeSet(&ignored, key, val); - tvRefcountedDecRef(&ignored); - } else { - if (UNLIKELY(bindingAssignment)) { - tvBind(val, propVal); - } else { - tvSet(*val, *propVal); - } - } - // Return a pointer to the property if it's a declared property - return declPropInd(propVal) != kInvalidSlot ? propVal : nullptr; - } - assert(!accessible); - if (visible) { - assert(propVal); - if (!getAttribute(UseSet)) { - raise_error("Cannot access protected property"); - } - // Fall through to the last case below - } else if (UNLIKELY(!*key->data())) { - throw_invalid_property_name(StrNR(key)); - } else if (!getAttribute(UseSet)) { - if (o_properties.get() == nullptr) { - initDynProps(); - } - // when seting a dynamic property, do not write - // directly to the TypedValue in the HphpArray, since - // its _count field is used to store the string hash of - // the property name. Instead, call the appropriate - // setters (set() or setRef()). - if (UNLIKELY(bindingAssignment)) { - o_properties.get()->setRef(const_cast(key), - tvAsCVarRef(val), false); - } else { - o_properties.get()->set(const_cast(key), - tvAsCVarRef(val), false); - } - return nullptr; - } - assert(!accessible); - assert(getAttribute(UseSet)); - TypedValue ignored; - invokeSet(&ignored, key, val); - tvRefcountedDecRef(&ignored); - return nullptr; -} - -TypedValue* Instance::setOpProp(TypedValue& tvRef, Class* ctx, - unsigned char op, const StringData* key, - Cell* val) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - if (visible && accessible) { - assert(propVal); - if (unset && getAttribute(UseGet)) { - TypedValue tvResult; - tvWriteUninit(&tvResult); - invokeGet(&tvResult, key); - SETOP_BODY(&tvResult, op, val); - if (getAttribute(UseSet)) { - assert(tvRef.m_type == KindOfUninit); - memcpy(&tvRef, &tvResult, sizeof(TypedValue)); - TypedValue ignored; - invokeSet(&ignored, key, &tvRef); - tvRefcountedDecRef(&ignored); - propVal = &tvRef; - } else { - memcpy(propVal, &tvResult, sizeof(TypedValue)); - } - } else { - SETOP_BODY(propVal, op, val); - } - return propVal; - } - assert(!accessible); - if (visible) { - assert(propVal); - if (!getAttribute(UseGet) || !getAttribute(UseSet)) { - raise_error("Cannot access protected property"); - } - // Fall through to the last case below - } else if (UNLIKELY(!*key->data())) { - throw_invalid_property_name(StrNR(key)); - } else if (!getAttribute(UseGet)) { - if (o_properties.get() == nullptr) { - initDynProps(); - } - o_properties.get()->createLvalPtr(*(const String*)&key, - *(Variant**)(&propVal), false); - // don't write propVal->_count because it holds data - // owned by the HphpArray - propVal->m_type = KindOfNull; - SETOP_BODY(propVal, op, val); - return propVal; - } else if (!getAttribute(UseSet)) { - TypedValue tvResult; - tvWriteUninit(&tvResult); - invokeGet(&tvResult, key); - SETOP_BODY(&tvResult, op, val); - if (o_properties.get() == nullptr) { - initDynProps(); - } - o_properties.get()->createLvalPtr(*(const String*)&key, - *(Variant**)(&propVal), false); - // don't write propVal->_count because it holds data - // owned by the HphpArray - propVal->m_data.num = tvResult.m_data.num; - propVal->m_type = tvResult.m_type; - return propVal; - } - assert(!accessible); - assert(getAttribute(UseGet) && getAttribute(UseSet)); - invokeGet(&tvRef, key); - SETOP_BODY(&tvRef, op, val); - TypedValue ignored; - invokeSet(&ignored, key, &tvRef); - tvRefcountedDecRef(&ignored); - propVal = &tvRef; - return propVal; -} - -template -void Instance::incDecPropImpl(TypedValue& tvRef, Class* ctx, - unsigned char op, const StringData* key, - TypedValue& dest) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - if (visible && accessible) { - assert(propVal); - if (unset && getAttribute(UseGet)) { - TypedValue tvResult; - tvWriteUninit(&tvResult); - invokeGet(&tvResult, key); - IncDecBody(op, &tvResult, &dest); - if (getAttribute(UseSet)) { - TypedValue ignored; - invokeSet(&ignored, key, &tvResult); - tvRefcountedDecRef(&ignored); - propVal = &tvResult; - } else { - memcpy((void *)propVal, (void *)&tvResult, sizeof(TypedValue)); - } - } else { - IncDecBody(op, propVal, &dest); - } - return; - } - assert(!accessible); - if (visible) { - assert(propVal); - if (!getAttribute(UseGet) || !getAttribute(UseSet)) { - raise_error("Cannot access protected property"); - } - // Fall through to the last case below - } else if (UNLIKELY(!*key->data())) { - throw_invalid_property_name(StrNR(key)); - } else if (!getAttribute(UseGet)) { - if (o_properties.get() == nullptr) { - initDynProps(); - } - o_properties.get()->createLvalPtr(*(const String*)&key, - *(Variant**)(&propVal), false); - // don't write propVal->_count because it holds data - // owned by the HphpArray - propVal->m_type = KindOfNull; - IncDecBody(op, propVal, &dest); - return; - } else if (!getAttribute(UseSet)) { - TypedValue tvResult; - tvWriteUninit(&tvResult); - invokeGet(&tvResult, key); - IncDecBody(op, &tvResult, &dest); - if (o_properties.get() == nullptr) { - initDynProps(); - } - o_properties.get()->createLvalPtr(*(const String*)&key, - *(Variant**)(&propVal), false); - // don't write propVal->_count because it holds data - // owned by the HphpArray - propVal->m_data.num = tvResult.m_data.num; - propVal->m_type = tvResult.m_type; - return; - } - assert(!accessible); - assert(getAttribute(UseGet) && getAttribute(UseSet)); - invokeGet(&tvRef, key); - IncDecBody(op, &tvRef, &dest); - TypedValue ignored; - invokeSet(&ignored, key, &tvRef); - tvRefcountedDecRef(&ignored); - propVal = &tvRef; -} - -// Actualize template method so that the method can be defined in instance.cpp -// (rather than instance.h), but still be invoked elsewhere. -template <> -void Instance::incDecProp(TypedValue& tvRef, Class* ctx, - unsigned char op, const StringData* key, - TypedValue& dest) { - incDecPropImpl(tvRef, ctx, op, key, dest); -} - -template <> -void Instance::incDecProp(TypedValue& tvRef, Class* ctx, - unsigned char op, const StringData* key, - TypedValue& dest) { - incDecPropImpl(tvRef, ctx, op, key, dest); -} - -void Instance::unsetProp(Class* ctx, const StringData* key) { - bool visible, accessible, unset; - TypedValue* propVal = getProp(ctx, key, visible, accessible, unset); - if (visible && accessible) { - Slot propInd = declPropInd(propVal); - if (propInd != kInvalidSlot) { - // Declared property. - tvSetIgnoreRef(*null_variant.asTypedValue(), *propVal); - } else { - // Dynamic property. - assert(o_properties.get() != nullptr); - o_properties.get()->remove(CStrRef(key), false); - } - } else if (UNLIKELY(!*key->data())) { - throw_invalid_property_name(StrNR(key)); - } else { - assert(!accessible); - if (getAttribute(UseUnset)) { - TypedValue ignored; - invokeUnset(&ignored, key); - tvRefcountedDecRef(&ignored); - } else if (visible) { - raise_error("Cannot unset inaccessible property"); - } - } -} - -void Instance::raiseUndefProp(const StringData* key) { - raise_notice("Undefined property: %s::$%s", - m_cls->name()->data(), key->data()); -} - -void Instance::getProp(const Class* klass, bool pubOnly, - const PreClass::Prop* prop, - Array& props, - std::vector& inserted) const { - if (prop->attrs() & AttrStatic) { - return; - } - - Slot propInd = klass->lookupDeclProp(prop->name()); - assert(propInd != kInvalidSlot); - const TypedValue* propVal = &propVec()[propInd]; - - if ((!pubOnly || (prop->attrs() & AttrPublic)) && - propVal->m_type != KindOfUninit && - !inserted[propInd]) { - inserted[propInd] = true; - props.lvalAt(CStrRef(klass->declProperties()[propInd].m_mangledName)) - .setWithRef(tvAsCVarRef(propVal)); - } -} - -void Instance::getProps(const Class* klass, bool pubOnly, - const PreClass* pc, - Array& props, - std::vector& inserted) const { - PreClass::Prop const* propVec = pc->properties(); - size_t count = pc->numProperties(); - for (size_t i = 0; i < count; ++i) { - getProp(klass, pubOnly, &propVec[i], props, inserted); - } -} - -Variant Instance::t___destruct() { - static StringData* sd__destruct = StringData::GetStaticString("__destruct"); - const Func* method = m_cls->lookupMethod(sd__destruct); - if (method) { - Variant v; - g_vmContext->invokeFuncFew(v.asTypedValue(), method, this); - return v; - } else { - return uninit_null(); - } -} - -Variant Instance::t___call(Variant v_name, Variant v_arguments) { - static StringData* sd__call = StringData::GetStaticString("__call"); - const Func* method = m_cls->lookupMethod(sd__call); - if (method) { - Variant v; - TypedValue args[2]; - tvDup(*v_name.asTypedValue(), args[0]); - tvDup(*v_arguments.asTypedValue(), args[1]); - g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 2, - args); - return v; - } else { - return uninit_null(); - } -} - -Variant Instance::t___set(Variant v_name, Variant v_value) { - const Func* method = m_cls->lookupMethod(s___set.get()); - if (method) { - Variant v; - g_vmContext->invokeFunc(v.asTypedValue(), method, - Array(ArrayInit(2).set(v_name).set(withRefBind(v_value)).create()), - this); - return v; - } else { - return uninit_null(); - } -} - -Variant Instance::t___get(Variant v_name) { - const Func* method = m_cls->lookupMethod(s___get.get()); - if (method) { - Variant v; - TypedValue args[1]; - tvDup(*v_name.asTypedValue(), args[0]); - g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, - args); - return v; - } else { - return uninit_null(); - } -} - -bool Instance::t___isset(Variant v_name) { - const Func* method = m_cls->lookupMethod(s___isset.get()); - if (method) { - Variant v; - TypedValue args[1]; - tvDup(*v_name.asTypedValue(), args[0]); - g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, - args); - return v.toBoolean(); - } else { - return false; - } -} - -Variant Instance::t___unset(Variant v_name) { - const Func* method = m_cls->lookupMethod(s___unset.get()); - if (method) { - Variant v; - TypedValue args[1]; - tvDup(*v_name.asTypedValue(), args[0]); - g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, - args); - return v; - } else { - return uninit_null(); - } -} - -Variant Instance::t___sleep() { - static StringData* sd__sleep = StringData::GetStaticString("__sleep"); - const Func *method = m_cls->lookupMethod(sd__sleep); - if (method) { - TypedValue tv; - g_vmContext->invokeFuncFew(&tv, method, this); - return tvAsVariant(&tv); - } else { - clearAttribute(HasSleep); - return uninit_null(); - } -} - -Variant Instance::t___wakeup() { - static StringData* sd__wakeup = StringData::GetStaticString("__wakeup"); - const Func *method = m_cls->lookupMethod(sd__wakeup); - if (method) { - TypedValue tv; - g_vmContext->invokeFuncFew(&tv, method, this); - return tvAsVariant(&tv); - } else { - return uninit_null(); - } -} - -Variant Instance::t___set_state(Variant v_properties) { - static StringData* sd__set_state = StringData::GetStaticString("__set_state"); - const Func* method = m_cls->lookupMethod(sd__set_state); - if (method) { - Variant v; - TypedValue args[1]; - tvDup(*v_properties.asTypedValue(), args[0]); - g_vmContext->invokeFuncFew(v.asTypedValue(), method, this, nullptr, 1, - args); - return v; - } else { - return false; - } -} - -String Instance::t___tostring() { - const Func *method = m_cls->getToString(); - if (method) { - TypedValue tv; - g_vmContext->invokeFuncFew(&tv, method, this); - if (!IS_STRING_TYPE(tv.m_type)) { - void (*notify_user)(const char *, ...) = &raise_error; - if (hphpiCompat) { - tvCastToStringInPlace(&tv); - notify_user = &raise_warning; - } - notify_user("Method %s::__toString() must return a string value", - m_cls->m_preClass->name()->data()); - } - return tv.m_data.pstr; - } else { - std::string msg = m_cls->m_preClass->name()->data(); - msg += "::__toString() was not defined"; - throw BadTypeConversionException(msg.c_str()); - } -} - -Variant Instance::t___clone() { - static StringData* sd__clone = StringData::GetStaticString("__clone"); - const Func *method = m_cls->lookupMethod(sd__clone); - if (method) { - TypedValue tv; - g_vmContext->invokeFuncFew(&tv, method, this); - return false; - } else { - return false; - } -} - -void Instance::cloneSet(ObjectData* clone) { - Instance* iclone = static_cast(clone); - Slot nProps = m_cls->numDeclProperties(); - TypedValue* iclonePropVec = (TypedValue *)((uintptr_t)iclone + - sizeof(ObjectData) + builtinPropSize()); - for (Slot i = 0; i < nProps; i++) { - tvRefcountedDecRef(&iclonePropVec[i]); - tvDupFlattenVars(&propVec()[i], &iclonePropVec[i], nullptr); - } - if (o_properties.get()) { - iclone->initDynProps(); - ssize_t iter = o_properties.get()->iter_begin(); - while (iter != HphpArray::ElmIndEmpty) { - auto props = static_cast(o_properties.get()); - TypedValue key; - props->nvGetKey(&key, iter); - assert(tvIsString(&key)); - StringData* strKey = key.m_data.pstr; - TypedValue *val = props->nvGet(strKey); - TypedValue *retval; - auto cloneProps = iclone->o_properties.get(); - cloneProps->createLvalPtr(strKey, *(Variant**)&retval, false); - tvDupFlattenVars(val, retval, cloneProps); - iter = o_properties.get()->iter_advance(iter); - decRefStr(strKey); - } - } -} - -ObjectData* Instance::cloneImpl() { - Instance* obj = Instance::newInstance(m_cls); - cloneSet(obj); - obj->incRefCount(); - obj->t___clone(); - return obj; -} - - } // HPHP::VM diff --git a/hphp/runtime/vm/instance.h b/hphp/runtime/vm/instance.h deleted file mode 100644 index 7d4fd94f1..000000000 --- a/hphp/runtime/vm/instance.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | HipHop for PHP | - +----------------------------------------------------------------------+ - | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ -*/ - -#ifndef incl_HPHP_VM_INSTANCE_H_ -#define incl_HPHP_VM_INSTANCE_H_ - -#include "hphp/runtime/base/complex_types.h" -#include "hphp/runtime/base/memory/smart_allocator.h" -#include "hphp/runtime/base/array/array_init.h" -#include "hphp/runtime/base/runtime_option.h" -#include "hphp/runtime/base/array/hphp_array.h" -#include "hphp/runtime/vm/class.h" -#include "hphp/runtime/vm/unit.h" - -namespace HPHP { - -void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData, - size_t nProps); - -class Instance : public ObjectData { - // Do not declare any fields directly in Instance; instead embed them in - // ObjectData, so that a property vector can always reside immediately past - // the end of an object. - - private: - // This constructor is used for all pure classes that are not - // descendents of cppext classes - explicit Instance(Class* cls) : ObjectData(false, cls) { - instanceInit(cls); - } - - enum class NoInit { noinit }; - explicit Instance(Class* cls, NoInit) : ObjectData(false, cls) {} - - public: - // This constructor is used for all cppext classes (including resources) - // and their descendents. - Instance(Class* cls, bool isResource) - : ObjectData(isResource, cls) { - instanceInit(cls); - } - - virtual ~Instance() {} - - static int ObjAllocatorSizeClassCount; - - // Call newInstance() to instantiate an Instance - static Instance* newInstance(Class* cls) { - if (cls->m_InstanceCtor) { - return cls->m_InstanceCtor(cls); - } - Attr attrs = cls->attrs(); - if (UNLIKELY(attrs & (AttrAbstract | AttrInterface | AttrTrait))) { - raise_error("Cannot instantiate %s %s", - (attrs & AttrInterface) ? "interface" : - (attrs & AttrTrait) ? "trait" : "abstract class", - cls->preClass()->name()->data()); - } - size_t nProps = cls->numDeclProperties(); - size_t size = sizeForNProps(nProps); - Instance* obj = (Instance*)ALLOCOBJSZ(size); - new (obj) Instance(cls); - if (UNLIKELY(cls->callsCustomInstanceInit())) { - /* - This must happen after the constructor finishes, - because it can leak references to obj AND it can - throw exceptions. If we have this in the Instance - constructor, and it throws, obj will be partially - destroyed (ie ~ObjectData will be called, resetting - the vtable pointer) leaving dangling references - to the object (eg in backtraces). - */ - obj->callCustomInstanceInit(); - } - return obj; - } - - // Given a Class that is assumed to be a concrete, regular (not a - // trait or interface), pure PHP class, and an allocator index, - // return a new, uninitialized object of that class. - static Instance* newInstanceRaw(Class* cls, int idx); - - private: - void instanceInit(Class* cls) { - setAttributes(cls->getODAttrs()); - size_t nProps = cls->numDeclProperties(); - if (cls->needInitialization()) { - cls->initialize(); - } - if (nProps > 0) { - if (cls->pinitVec().size() > 0) { - const Class::PropInitVec* propInitVec = m_cls->getPropData(); - assert(propInitVec != nullptr); - assert(nProps == propInitVec->size()); - if (!cls->hasDeepInitProps()) { - memcpy(propVec(), &(*propInitVec)[0], nProps * sizeof(TypedValue)); - } else { - deepInitHelper(propVec(), &(*propInitVec)[0], nProps); - } - } else { - assert(nProps == cls->declPropInit().size()); - memcpy(propVec(), &cls->declPropInit()[0], nProps * sizeof(TypedValue)); - } - } - } - - protected: - TypedValue* propVec(); - const TypedValue* propVec() const; - - public: - Instance* callCustomInstanceInit(); - - void operator delete(void* p) { - Instance* this_ = (Instance*)p; - Class* cls = this_->getVMClass(); - size_t nProps = cls->numDeclProperties(); - // cppext classes have their own implementation of delete - assert(this_->builtinPropSize() == 0); - TypedValue* propVec = (TypedValue *)((uintptr_t)this_ + sizeof(ObjectData)); - for (unsigned i = 0; i < nProps; ++i) { - TypedValue* prop = &propVec[i]; - tvRefcountedDecRef(prop); - } - DELETEOBJSZ(sizeForNProps(nProps))(this_); - } - - //============================================================================ - // Virtual ObjectData methods that we need to override - - public: - virtual Variant t___destruct(); - virtual Variant t___call(Variant v_name, Variant v_arguments); - virtual Variant t___set(Variant v_name, Variant v_value); - virtual Variant t___get(Variant v_name); - virtual bool t___isset(Variant v_name); - virtual Variant t___unset(Variant v_name); - virtual Variant t___sleep(); - virtual Variant t___wakeup(); - virtual Variant t___set_state(Variant v_properties); - virtual String t___tostring(); - virtual Variant t___clone(); - - //============================================================================ - // Miscellaneous. - - void cloneSet(ObjectData* clone); - ObjectData* cloneImpl(); - - void invokeUserMethod(TypedValue* retval, const Func* method, - CArrRef params); - - const Func* methodNamed(const StringData* sd) const { - return getVMClass()->lookupMethod(sd); - } - - static size_t sizeForNProps(Slot nProps) { - size_t sz = sizeof(Instance) + (sizeof(TypedValue) * nProps); - assert((sz & (sizeof(TypedValue) - 1)) == 0); - return sz; - } - - static Object FromArray(ArrayData *properties); - - //============================================================================ - // Properties. - public: - int builtinPropSize() const { - return m_cls->builtinPropSize(); - } - - // public for ObjectData access - void initDynProps(int numDynamic = 0); - Slot declPropInd(TypedValue* prop) const; - private: - template - TypedValue* getPropImpl(Class* ctx, const StringData* key, bool& visible, - bool& accessible, bool& unset); - public: - TypedValue* getProp(Class* ctx, const StringData* key, bool& visible, - bool& accessible, bool& unset); - TypedValue* getDeclProp(Class* ctx, const StringData* key, bool& visible, - bool& accessible, bool& unset); - private: - template - void propImpl(TypedValue*& retval, TypedValue& tvRef, Class* ctx, - const StringData* key); - void invokeSet(TypedValue* retval, const StringData* key, TypedValue* val); - void invokeGet(TypedValue* retval, const StringData* key); - void invokeGetProp(TypedValue*& retval, TypedValue& tvRef, - const StringData* key); - void invokeIsset(TypedValue* retval, const StringData* key); - void invokeUnset(TypedValue* retval, const StringData* key); - void getProp(const Class* klass, bool pubOnly, const PreClass::Prop* prop, - Array& props, std::vector& inserted) const; - void getProps(const Class* klass, bool pubOnly, const PreClass* pc, - Array& props, std::vector& inserted) const; - public: - void prop(TypedValue*& retval, TypedValue& tvRef, Class* ctx, - const StringData* key); - void propD(TypedValue*& retval, TypedValue& tvRef, Class* ctx, - const StringData* key); - void propW(TypedValue*& retval, TypedValue& tvRef, Class* ctx, - const StringData* key); - void propWD(TypedValue*& retval, TypedValue& tvRef, Class* ctx, - const StringData* key); - bool propIsset(Class* ctx, const StringData* key); - bool propEmpty(Class* ctx, const StringData* key); - - TypedValue* setProp(Class* ctx, const StringData* key, TypedValue* val, - bool bindingAssignment = false); - TypedValue* setOpProp(TypedValue& tvRef, Class* ctx, unsigned char op, - const StringData* key, Cell* val); - private: - template - void incDecPropImpl(TypedValue& tvRef, Class* ctx, unsigned char op, - const StringData* key, TypedValue& dest); - public: - template - void incDecProp(TypedValue& tvRef, Class* ctx, unsigned char op, - const StringData* key, TypedValue& dest); - void unsetProp(Class* ctx, const StringData* key); - - void raiseUndefProp(const StringData* name); - - friend class ObjectData; -}; - -inline Instance* instanceFromTv(TypedValue* tv) { - assert(dynamic_cast(tv->m_data.pobj)); - return static_cast(tv->m_data.pobj); -} - - } // HPHP::VM - -namespace HPHP { - -class ExtObjectData : public HPHP::Instance { - public: - explicit ExtObjectData(HPHP::Class* cls) - : HPHP::Instance(cls, false) { - assert(!m_cls->callsCustomInstanceInit()); - } -}; - -template class ExtObjectDataFlags : public ExtObjectData { - public: - explicit ExtObjectDataFlags(HPHP::Class* cb) : ExtObjectData(cb) { - ObjectData::setAttributes(flags); - } -}; - -} // HPHP - -#endif diff --git a/hphp/runtime/vm/member_operations.h b/hphp/runtime/vm/member_operations.h index 4ddf94463..c949f596e 100644 --- a/hphp/runtime/vm/member_operations.h +++ b/hphp/runtime/vm/member_operations.h @@ -21,7 +21,7 @@ #include "hphp/runtime/base/strings.h" #include "hphp/system/systemlib.h" #include "hphp/runtime/base/builtin_functions.h" -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/vm/runtime.h" #include "hphp/runtime/ext/ext_collections.h" #include "hphp/runtime/base/tv_conversions.h" diff --git a/hphp/runtime/vm/name_value_table_wrapper.cpp b/hphp/runtime/vm/name_value_table_wrapper.cpp index f15d59f9b..2083e0fed 100644 --- a/hphp/runtime/vm/name_value_table_wrapper.cpp +++ b/hphp/runtime/vm/name_value_table_wrapper.cpp @@ -15,6 +15,7 @@ */ #include "hphp/runtime/vm/name_value_table_wrapper.h" +#include "hphp/runtime/base/runtime_error.h" #include "hphp/runtime/base/array/array_iterator.h" #include "hphp/runtime/base/array/array_init.h" diff --git a/hphp/runtime/vm/named_entity.h b/hphp/runtime/vm/named_entity.h index f59477015..c075cf33c 100644 --- a/hphp/runtime/vm/named_entity.h +++ b/hphp/runtime/vm/named_entity.h @@ -20,8 +20,6 @@ #include "tbb/concurrent_unordered_map.h" #include -#include "hphp/runtime/base/complex_types.h" - #include "hphp/util/atomic.h" #include "folly/AtomicHashMap.h" diff --git a/hphp/runtime/vm/preclass-emit.cpp b/hphp/runtime/vm/preclass-emit.cpp new file mode 100644 index 000000000..b5fb4ae21 --- /dev/null +++ b/hphp/runtime/vm/preclass-emit.cpp @@ -0,0 +1,331 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ +#include "hphp/runtime/vm/preclass-emit.h" +#include "hphp/runtime/vm/repo.h" +#include "hphp/runtime/vm/blob_helper.h" + +namespace HPHP { + +//============================================================================= +// PreClassEmitter::Prop. + +PreClassEmitter::Prop::Prop(const PreClassEmitter* pce, + const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType) + : m_name(n) + , m_attrs(attrs) + , m_typeConstraint(typeConstraint) + , m_docComment(docComment) + , m_hphpcType(hphpcType) +{ + m_mangledName = PreClass::manglePropName(pce->name(), n, attrs); + memcpy(&m_val, val, sizeof(TypedValue)); +} + +PreClassEmitter::Prop::~Prop() { +} + +//============================================================================= +// PreClassEmitter. + +PreClassEmitter::PreClassEmitter(UnitEmitter& ue, + Id id, + const StringData* n, + PreClass::Hoistable hoistable) + : m_ue(ue) + , m_name(n) + , m_id(id) + , m_hoistable(hoistable) + , m_InstanceCtor(nullptr) + , m_builtinPropSize(0) +{} + +void PreClassEmitter::init(int line1, int line2, Offset offset, Attr attrs, + const StringData* parent, + const StringData* docComment) { + m_line1 = line1; + m_line2 = line2; + m_offset = offset; + m_attrs = attrs; + m_parent = parent; + m_docComment = docComment; +} + +PreClassEmitter::~PreClassEmitter() { + for (MethodVec::const_iterator it = m_methods.begin(); + it != m_methods.end(); ++it) { + delete *it; + } +} + +void PreClassEmitter::addInterface(const StringData* n) { + m_interfaces.push_back(n); +} + +bool PreClassEmitter::addMethod(FuncEmitter* method) { + MethodMap::const_iterator it = m_methodMap.find(method->name()); + if (it != m_methodMap.end()) { + return false; + } + m_methods.push_back(method); + m_methodMap[method->name()] = method; + return true; +} + +bool PreClassEmitter::addProperty(const StringData* n, Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType) { + PropMap::Builder::const_iterator it = m_propMap.find(n); + if (it != m_propMap.end()) { + return false; + } + PreClassEmitter::Prop prop(this, n, attrs, typeConstraint, docComment, val, + hphpcType); + m_propMap.add(prop.name(), prop); + return true; +} + +const PreClassEmitter::Prop& +PreClassEmitter::lookupProp(const StringData* propName) const { + PropMap::Builder::const_iterator it = m_propMap.find(propName); + assert(it != m_propMap.end()); + Slot idx = it->second; + return m_propMap[idx]; +} + +bool PreClassEmitter::addConstant(const StringData* n, + const StringData* typeConstraint, + TypedValue* val, + const StringData* phpCode) { + ConstMap::Builder::const_iterator it = m_constMap.find(n); + if (it != m_constMap.end()) { + return false; + } + PreClassEmitter::Const const_(n, typeConstraint, val, phpCode); + m_constMap.add(const_.name(), const_); + return true; +} + +void PreClassEmitter::addUsedTrait(const StringData* traitName) { + m_usedTraits.push_back(traitName); +} + +void PreClassEmitter::addTraitPrecRule( + const PreClass::TraitPrecRule &rule) { + m_traitPrecRules.push_back(rule); +} + +void PreClassEmitter::addTraitAliasRule( + const PreClass::TraitAliasRule &rule) { + m_traitAliasRules.push_back(rule); +} + +void PreClassEmitter::addUserAttribute(const StringData* name, TypedValue tv) { + m_userAttributes[name] = tv; +} + +void PreClassEmitter::commit(RepoTxn& txn) const { + Repo& repo = Repo::get(); + PreClassRepoProxy& pcrp = repo.pcrp(); + int repoId = m_ue.repoId(); + int64_t usn = m_ue.sn(); + pcrp.insertPreClass(repoId) + .insert(*this, txn, usn, m_id, m_name, m_hoistable); + + for (MethodVec::const_iterator it = m_methods.begin(); + it != m_methods.end(); ++it) { + (*it)->commit(txn); + } +} + +void PreClassEmitter::setBuiltinClassInfo(const ClassInfo* info, + BuiltinCtorFunction ctorFunc, + int sz) { + if (info->getAttribute() & ClassInfo::IsFinal) { + m_attrs = m_attrs | AttrFinal; + } + if (info->getAttribute() & ClassInfo::IsAbstract) { + m_attrs = m_attrs | AttrAbstract; + } + if (info->getAttribute() & ClassInfo::IsTrait) { + m_attrs = m_attrs | AttrTrait; + } + m_attrs = m_attrs | AttrUnique; + m_InstanceCtor = ctorFunc; + m_builtinPropSize = sz - sizeof(ObjectData); +} + +PreClass* PreClassEmitter::create(Unit& unit) const { + Attr attrs = m_attrs; + if (attrs & AttrPersistent && + !RuntimeOption::RepoAuthoritative && SystemLib::s_inited) { + attrs = Attr(attrs & ~AttrPersistent); + } + PreClass* pc = new PreClass(&unit, m_line1, m_line2, m_offset, m_name, + attrs, m_parent, m_docComment, m_id, + m_hoistable); + pc->m_InstanceCtor = m_InstanceCtor; + pc->m_builtinPropSize = m_builtinPropSize; + pc->m_interfaces = m_interfaces; + pc->m_usedTraits = m_usedTraits; + pc->m_traitPrecRules = m_traitPrecRules; + pc->m_traitAliasRules = m_traitAliasRules; + pc->m_userAttributes = m_userAttributes; + + PreClass::MethodMap::Builder methodBuild; + for (MethodVec::const_iterator it = m_methods.begin(); + it != m_methods.end(); ++it) { + Func* f = (*it)->create(unit, pc); + methodBuild.add(f->name(), f); + } + pc->m_methods.create(methodBuild); + + PreClass::PropMap::Builder propBuild; + for (unsigned i = 0; i < m_propMap.size(); ++i) { + const Prop& prop = m_propMap[i]; + propBuild.add(prop.name(), PreClass::Prop(pc, + prop.name(), + prop.attrs(), + prop.typeConstraint(), + prop.docComment(), + prop.val(), + prop.hphpcType())); + } + pc->m_properties.create(propBuild); + + PreClass::ConstMap::Builder constBuild; + for (unsigned i = 0; i < m_constMap.size(); ++i) { + const Const& const_ = m_constMap[i]; + constBuild.add(const_.name(), PreClass::Const(pc, + const_.name(), + const_.typeConstraint(), + const_.val(), + const_.phpCode())); + } + pc->m_constants.create(constBuild); + return pc; +} + +template void PreClassEmitter::serdeMetaData(SerDe& sd) { + // NOTE: name, hoistable, and a few other fields currently + // serialized outside of this. + sd(m_line1) + (m_line2) + (m_offset) + (m_attrs) + (m_parent) + (m_docComment) + + (m_interfaces) + (m_usedTraits) + (m_traitPrecRules) + (m_traitAliasRules) + (m_userAttributes) + (m_propMap) + (m_constMap) + ; +} + +//============================================================================= +// PreClassRepoProxy. + +PreClassRepoProxy::PreClassRepoProxy(Repo& repo) + : RepoProxy(repo) +#define PCRP_OP(c, o) \ + , m_##o##Local(repo, RepoIdLocal), m_##o##Central(repo, RepoIdCentral) + PCRP_OPS +#undef PCRP_OP +{ +#define PCRP_OP(c, o) \ + m_##o[RepoIdLocal] = &m_##o##Local; \ + m_##o[RepoIdCentral] = &m_##o##Central; + PCRP_OPS +#undef PCRP_OP +} + +PreClassRepoProxy::~PreClassRepoProxy() { +} + +void PreClassRepoProxy::createSchema(int repoId, RepoTxn& txn) { + { + std::stringstream ssCreate; + ssCreate << "CREATE TABLE " << m_repo.table(repoId, "PreClass") + << "(unitSn INTEGER, preClassId INTEGER, name TEXT," + " hoistable INTEGER, extraData BLOB," + " PRIMARY KEY (unitSn, preClassId));"; + txn.exec(ssCreate.str()); + } +} + +void PreClassRepoProxy::InsertPreClassStmt + ::insert(const PreClassEmitter& pce, RepoTxn& txn, + int64_t unitSn, Id preClassId, + const StringData* name, + PreClass::Hoistable hoistable) { + if (!prepared()) { + std::stringstream ssInsert; + ssInsert << "INSERT INTO " << m_repo.table(m_repoId, "PreClass") + << " VALUES(@unitSn, @preClassId, @name, @hoistable, " + "@extraData);"; + txn.prepare(*this, ssInsert.str()); + } + + BlobEncoder extraBlob; + RepoTxnQuery query(txn, *this); + query.bindInt64("@unitSn", unitSn); + query.bindId("@preClassId", preClassId); + query.bindStaticString("@name", name); + query.bindInt("@hoistable", hoistable); + const_cast(pce).serdeMetaData(extraBlob); + query.bindBlob("@extraData", extraBlob, /* static */ true); + query.exec(); +} + +void PreClassRepoProxy::GetPreClassesStmt + ::get(UnitEmitter& ue) { + RepoTxn txn(m_repo); + if (!prepared()) { + std::stringstream ssSelect; + ssSelect << "SELECT preClassId,name,hoistable,extraData FROM " + << m_repo.table(m_repoId, "PreClass") + << " WHERE unitSn == @unitSn ORDER BY preClassId ASC;"; + txn.prepare(*this, ssSelect.str()); + } + RepoTxnQuery query(txn, *this); + query.bindInt64("@unitSn", ue.sn()); + do { + query.step(); + if (query.row()) { + Id preClassId; /**/ query.getId(0, preClassId); + StringData* name; /**/ query.getStaticString(1, name); + int hoistable; /**/ query.getInt(2, hoistable); + BlobDecoder extraBlob = /**/ query.getBlob(3); + PreClassEmitter* pce = ue.newPreClassEmitter( + name, (PreClass::Hoistable)hoistable); + pce->serdeMetaData(extraBlob); + assert(pce->id() == preClassId); + } + } while (!query.done()); + txn.commit(); +} + +} // HPHP diff --git a/hphp/runtime/vm/preclass-emit.h b/hphp/runtime/vm/preclass-emit.h new file mode 100644 index 000000000..d7d513a18 --- /dev/null +++ b/hphp/runtime/vm/preclass-emit.h @@ -0,0 +1,215 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#ifndef incl_HPHP_VM_CLASS_EMIT_H_ +#define incl_HPHP_VM_CLASS_EMIT_H_ + +#include "hphp/runtime/base/complex_types.h" +#include "hphp/runtime/vm/repo_helpers.h" + +namespace HPHP { + +class PreClassEmitter { + public: + typedef std::vector MethodVec; + + class Prop { + public: + Prop() + : m_name(0) + , m_mangledName(0) + , m_attrs(AttrNone) + , m_typeConstraint(0) + , m_docComment(0) + , m_hphpcType(KindOfInvalid) + {} + + Prop(const PreClassEmitter* pce, + const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType); + ~Prop(); + + const StringData* name() const { return m_name; } + const StringData* mangledName() const { return m_mangledName; } + Attr attrs() const { return m_attrs; } + const StringData* typeConstraint() const { return m_typeConstraint; } + const StringData* docComment() const { return m_docComment; } + const TypedValue& val() const { return m_val; } + DataType hphpcType() const { return m_hphpcType; } + + template void serde(SerDe& sd) { + sd(m_name) + (m_mangledName) + (m_attrs) + (m_typeConstraint) + (m_docComment) + (m_val) + (m_hphpcType) + ; + } + + private: + const StringData* m_name; + const StringData* m_mangledName; + Attr m_attrs; + const StringData* m_typeConstraint; + const StringData* m_docComment; + TypedValue m_val; + DataType m_hphpcType; + }; + + class Const { + public: + Const() + : m_name(0) + , m_typeConstraint(0) + , m_phpCode(0) + {} + Const(const StringData* n, const StringData* typeConstraint, + TypedValue* val, const StringData* phpCode) + : m_name(n), m_typeConstraint(typeConstraint), m_phpCode(phpCode) { + memcpy(&m_val, val, sizeof(TypedValue)); + } + ~Const() {} + + const StringData* name() const { return m_name; } + const StringData* typeConstraint() const { return m_typeConstraint; } + const TypedValue& val() const { return m_val; } + const StringData* phpCode() const { return m_phpCode; } + + template void serde(SerDe& sd) { + sd(m_name)(m_val)(m_phpCode); + } + + private: + const StringData* m_name; + const StringData* m_typeConstraint; + TypedValue m_val; + const StringData* m_phpCode; + }; + + PreClassEmitter(UnitEmitter& ue, Id id, const StringData* n, + PreClass::Hoistable hoistable); + ~PreClassEmitter(); + + void init(int line1, int line2, Offset offset, Attr attrs, + const StringData* parent, const StringData* docComment); + + UnitEmitter& ue() const { return m_ue; } + const StringData* name() const { return m_name; } + Attr attrs() const { return m_attrs; } + void setHoistable(PreClass::Hoistable h) { m_hoistable = h; } + Id id() const { return m_id; } + const MethodVec& methods() const { return m_methods; } + + void addInterface(const StringData* n); + bool addMethod(FuncEmitter* method); + bool addProperty(const StringData* n, + Attr attrs, + const StringData* typeConstraint, + const StringData* docComment, + TypedValue* val, + DataType hphpcType); + const Prop& lookupProp(const StringData* propName) const; + bool addConstant(const StringData* n, const StringData* typeConstraint, + TypedValue* val, const StringData* phpCode); + void addUsedTrait(const StringData* traitName); + void addTraitPrecRule(const PreClass::TraitPrecRule &rule); + void addTraitAliasRule(const PreClass::TraitAliasRule &rule); + void addUserAttribute(const StringData* name, TypedValue tv); + void commit(RepoTxn& txn) const; + + void setBuiltinClassInfo(const ClassInfo* info, + BuiltinCtorFunction ctorFunc, + int sz); + + PreClass* create(Unit& unit) const; + + template void serdeMetaData(SerDe&); + + private: + typedef IndexedStringMap PropMap; + typedef IndexedStringMap ConstMap; + typedef hphp_hash_map MethodMap; + + UnitEmitter& m_ue; + int m_line1; + int m_line2; + Offset m_offset; + const StringData* m_name; + Attr m_attrs; + const StringData* m_parent; + const StringData* m_docComment; + Id m_id; + PreClass::Hoistable m_hoistable; + BuiltinCtorFunction m_InstanceCtor; + int m_builtinPropSize; + + std::vector m_interfaces; + std::vector m_usedTraits; + std::vector m_traitPrecRules; + std::vector m_traitAliasRules; + PreClass::UserAttributeMap m_userAttributes; + MethodVec m_methods; + MethodMap m_methodMap; + PropMap::Builder m_propMap; + ConstMap::Builder m_constMap; +}; + +class PreClassRepoProxy : public RepoProxy { + friend class PreClass; + friend class PreClassEmitter; + public: + explicit PreClassRepoProxy(Repo& repo); + ~PreClassRepoProxy(); + void createSchema(int repoId, RepoTxn& txn); + +#define PCRP_IOP(o) PCRP_OP(Insert##o, insert##o) +#define PCRP_GOP(o) PCRP_OP(Get##o, get##o) +#define PCRP_OPS \ + PCRP_IOP(PreClass) \ + PCRP_GOP(PreClasses) + class InsertPreClassStmt : public RepoProxy::Stmt { + public: + InsertPreClassStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {} + void insert(const PreClassEmitter& pce, RepoTxn& txn, int64_t unitSn, + Id preClassId, const StringData* name, + PreClass::Hoistable hoistable); + }; + class GetPreClassesStmt : public RepoProxy::Stmt { + public: + GetPreClassesStmt(Repo& repo, int repoId) : Stmt(repo, repoId) {} + void get(UnitEmitter& ue); + }; +#define PCRP_OP(c, o) \ + public: \ + c##Stmt& o(int repoId) { return *m_##o[repoId]; } \ + private: \ + c##Stmt m_##o##Local; \ + c##Stmt m_##o##Central; \ + c##Stmt* m_##o[RepoIdCount]; + PCRP_OPS +#undef PCRP_OP +}; + +} // HPHP + +#endif diff --git a/hphp/runtime/vm/repo.h b/hphp/runtime/vm/repo.h index 7ddfd1451..565fdd8e2 100644 --- a/hphp/runtime/vm/repo.h +++ b/hphp/runtime/vm/repo.h @@ -19,6 +19,7 @@ #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/vm/class.h" +#include "hphp/runtime/vm/preclass-emit.h" #include "hphp/runtime/vm/func.h" #include diff --git a/hphp/runtime/vm/repo_helpers.h b/hphp/runtime/vm/repo_helpers.h index 74bf7aa7b..e6310a0d5 100644 --- a/hphp/runtime/vm/repo_helpers.h +++ b/hphp/runtime/vm/repo_helpers.h @@ -21,7 +21,7 @@ #include #include "hphp/runtime/base/md5.h" -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" namespace HPHP { diff --git a/hphp/runtime/vm/runtime.cpp b/hphp/runtime/vm/runtime.cpp index 7986c5dfc..8e041b825 100644 --- a/hphp/runtime/vm/runtime.cpp +++ b/hphp/runtime/vm/runtime.cpp @@ -22,7 +22,6 @@ #include "hphp/runtime/ext/ext_closure.h" #include "hphp/runtime/ext/ext_continuation.h" #include "hphp/runtime/ext/ext_collections.h" -#include "hphp/runtime/vm/core_types.h" #include "hphp/runtime/vm/bytecode.h" #include "hphp/runtime/vm/repo.h" #include "hphp/util/trace.h" diff --git a/hphp/runtime/vm/srckey.h b/hphp/runtime/vm/srckey.h index d7c599c82..1a66affea 100644 --- a/hphp/runtime/vm/srckey.h +++ b/hphp/runtime/vm/srckey.h @@ -20,7 +20,7 @@ #include #include "hphp/runtime/vm/func.h" -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" namespace HPHP { diff --git a/hphp/runtime/vm/treadmill.cpp b/hphp/runtime/vm/treadmill.cpp index 4b9533650..0c16d647e 100644 --- a/hphp/runtime/vm/treadmill.cpp +++ b/hphp/runtime/vm/treadmill.cpp @@ -14,6 +14,8 @@ +----------------------------------------------------------------------+ */ +#include "hphp/runtime/vm/treadmill.h" + #include #include #include @@ -24,8 +26,7 @@ #include "hphp/util/base.h" #include "hphp/util/rank.h" #include "hphp/runtime/base/macros.h" -#include "hphp/runtime/vm/class.h" -#include "hphp/runtime/vm/treadmill.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/vm/jit/translator-x64.h" namespace HPHP { namespace Treadmill { diff --git a/hphp/runtime/vm/unit.h b/hphp/runtime/vm/unit.h index 21e2570fc..ecba45442 100644 --- a/hphp/runtime/vm/unit.h +++ b/hphp/runtime/vm/unit.h @@ -20,7 +20,7 @@ // Expects that runtime/vm/core_types.h is already included. #include "hphp/runtime/base/runtime_option.h" #include "hphp/runtime/vm/hhbc.h" -#include "hphp/runtime/vm/class.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/vm/repo_helpers.h" #include "hphp/runtime/vm/named_entity.h" #include "hphp/runtime/base/array/hphp_array.h" @@ -39,6 +39,7 @@ class FuncEmitter; class Repo; class FuncDict; class Unit; +class PreClassEmitter; enum class UnitOrigin { File = 0, diff --git a/hphp/runtime/vm/unwind.cpp b/hphp/runtime/vm/unwind.cpp index 952a641ca..0926d85a4 100644 --- a/hphp/runtime/vm/unwind.cpp +++ b/hphp/runtime/vm/unwind.cpp @@ -20,7 +20,7 @@ #include "folly/ScopeGuard.h" #include "hphp/util/trace.h" -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/vm/bytecode.h" #include "hphp/runtime/vm/func.h" #include "hphp/runtime/vm/unit.h" diff --git a/hphp/runtime/vm/verifier/pretty.h b/hphp/runtime/vm/verifier/pretty.h index a7ee5e9fd..b6be24985 100644 --- a/hphp/runtime/vm/verifier/pretty.h +++ b/hphp/runtime/vm/verifier/pretty.h @@ -19,7 +19,7 @@ #include -#include "hphp/runtime/vm/core_types.h" +#include "hphp/runtime/base/complex_types.h" namespace HPHP { diff --git a/hphp/system/systemlib.cpp b/hphp/system/systemlib.cpp index a2ff3e58c..64524210e 100644 --- a/hphp/system/systemlib.cpp +++ b/hphp/system/systemlib.cpp @@ -19,7 +19,6 @@ #include "hphp/runtime/base/complex_types.h" #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/vm/class.h" -#include "hphp/runtime/vm/instance.h" namespace HPHP { ///////////////////////////////////////////////////////////////////////////////