hphp/system/systemlib.cpp (341 lines of code) (raw):

/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-present 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/system/systemlib.h" #include "hphp/runtime/base/array-init.h" #include "hphp/runtime/base/builtin-functions.h" #include "hphp/runtime/base/coeffects-config.h" #include "hphp/runtime/base/execution-context.h" #include "hphp/runtime/base/init-fini-node.h" #include "hphp/runtime/base/object-data.h" #include "hphp/runtime/base/type-object.h" #include "hphp/runtime/base/type-string.h" #include "hphp/runtime/base/type-variant.h" #include "hphp/runtime/base/types.h" #include "hphp/runtime/vm/class.h" #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/vm/unit-emitter.h" #include <memory> #include <vector> namespace HPHP::SystemLib { ///////////////////////////////////////////////////////////////////////////// namespace { const StaticString s_message("message"); const StaticString s_code("code"); const Slot s_messageIdx{0}; const Slot s_codeIdx{2}; DEBUG_ONLY bool throwable_has_expected_props() { auto const erCls = s_ErrorClass; auto const exCls = s_ExceptionClass; if (erCls->lookupDeclProp(s_message.get()) != s_messageIdx || exCls->lookupDeclProp(s_message.get()) != s_messageIdx || erCls->lookupDeclProp(s_code.get()) != s_codeIdx || exCls->lookupDeclProp(s_code.get()) != s_codeIdx) { return false; } // Check that we have the expected type-hints on these props so we don't need // to verify anything. return erCls->declPropTypeConstraint(s_messageIdx).isString() && exCls->declPropTypeConstraint(s_messageIdx).isString() && erCls->declPropTypeConstraint(s_codeIdx).isInt() && exCls->declPropTypeConstraint(s_codeIdx).isInt(); } ALWAYS_INLINE Object createAndConstruct(Class* cls, const Array& args) { Object inst{cls}; tvDecRefGen(g_context->invokeFunc(cls->getCtor(), args, inst.get())); return inst; } /** * Fast path for Errors and Exceptions that do not override the default * constructor or message property initializer. Does not reenter VM. */ ALWAYS_INLINE Object createAndConstructThrowable(Class* cls, const Variant& message) { assertx(throwable_has_expected_props()); assertx(cls->getCtor() == s_ErrorClass->getCtor() || cls->getCtor() == s_ExceptionClass->getCtor()); Object inst{cls}; if (debug) { DEBUG_ONLY auto const code_prop = inst->propRvalAtOffset(s_codeIdx); assertx(isIntType(code_prop.type())); assertx(code_prop.val().num == 0); } auto const message_prop = inst->propLvalAtOffset(s_messageIdx); assertx(isStringType(message_prop.type())); assertx(message_prop.val().pstr == staticEmptyString()); tvDup(*message.asTypedValue(), message_prop); return inst; } } bool s_inited = false; bool s_anyNonPersistentBuiltins = false; std::string s_source; Unit* s_unit = nullptr; Unit* s_hhas_unit = nullptr; Func* s_nullFunc = nullptr; Func* s_singleArgNullFunc = nullptr; Func* s_nullCtor = nullptr; ///////////////////////////////////////////////////////////////////////////// #define DEFINE_SYSTEMLIB_CLASS(cls) \ Class* s_ ## cls ## Class = nullptr; SYSTEMLIB_CLASSES(DEFINE_SYSTEMLIB_CLASS) #undef DEFINE_SYSTEMLIB_CLASS #define DEFINE_SYSTEMLIB_HH_CLASS(cls) \ Class* s_HH_ ## cls ## Class = nullptr; SYSTEMLIB_HH_CLASSES(DEFINE_SYSTEMLIB_HH_CLASS) #undef DEFINE_SYSTEMLIB_HH_CLASS Class* s_ThrowableClass; Class* s_BaseExceptionClass; Class* s_ErrorClass; Class* s_ArithmeticErrorClass; Class* s_ArgumentCountErrorClass; Class* s_AssertionErrorClass; Class* s_DivisionByZeroErrorClass; Class* s_ParseErrorClass; Class* s_TypeErrorClass; Class* s_MethCallerHelperClass; Class* s_DynMethCallerHelperClass; Object AllocStdClassObject() { return Object{s_stdclassClass}; } Object AllocPinitSentinel() { return Object{s_pinitSentinelClass}; } Object AllocExceptionObject(const Variant& message) { return createAndConstructThrowable(s_ExceptionClass, message); } Object AllocErrorObject(const Variant& message) { return createAndConstructThrowable(s_ErrorClass, message); } Object AllocArithmeticErrorObject(const Variant& message) { return createAndConstructThrowable(s_ArithmeticErrorClass, message); } Object AllocArgumentCountErrorObject(const Variant& message) { return createAndConstructThrowable(s_ArgumentCountErrorClass, message); } Object AllocDivisionByZeroErrorObject(const Variant& message) { return createAndConstructThrowable(s_DivisionByZeroErrorClass, message); } Object AllocParseErrorObject(const Variant& message) { return createAndConstructThrowable(s_ParseErrorClass, message); } Object AllocTypeErrorObject(const Variant& message) { return createAndConstructThrowable(s_TypeErrorClass, message); } Object AllocBadMethodCallExceptionObject(const Variant& message) { return createAndConstructThrowable(s_BadMethodCallExceptionClass, message); } Object AllocInvalidArgumentExceptionObject(const Variant& message) { return createAndConstructThrowable(s_InvalidArgumentExceptionClass, message); } Object AllocTypeAssertionExceptionObject(const Variant& message) { return createAndConstructThrowable(s_TypeAssertionExceptionClass, message); } Object AllocRuntimeExceptionObject(const Variant& message) { return createAndConstructThrowable(s_RuntimeExceptionClass, message); } Object AllocOutOfBoundsExceptionObject(const Variant& message) { return createAndConstructThrowable(s_OutOfBoundsExceptionClass, message); } Object AllocInvalidOperationExceptionObject(const Variant& message) { return createAndConstructThrowable(s_InvalidOperationExceptionClass, message); } Object AllocDOMExceptionObject(const Variant& message) { return createAndConstructThrowable(s_DOMExceptionClass, message); } Object AllocDivisionByZeroExceptionObject() { return createAndConstructThrowable(s_DivisionByZeroExceptionClass, Strings::DIVISION_BY_ZERO); } Object AllocInvalidForeachArgumentExceptionObject() { return createAndConstructThrowable(s_InvalidForeachArgumentExceptionClass, Strings::INVALID_ARGUMENT_FOREACH); } Object AllocUndefinedPropertyExceptionObject(const Variant& message) { return createAndConstructThrowable(s_UndefinedPropertyExceptionClass, message); } Object AllocSoapFaultObject(const Variant& code, const Variant& message, const Variant& actor /* = uninit_variant */, const Variant& detail /* = uninit_variant */, const Variant& name /* = uninit_variant */, const Variant& header /* = uninit_variant */) { return createAndConstruct( s_SoapFaultClass, make_vec_array(code, message, actor, detail, name, header) ); } Object AllocLazyKVZipIterableObject(const Variant& mp) { return createAndConstruct(s_LazyKVZipIterableClass, make_vec_array(mp)); } Object AllocLazyIterableViewObject(const Variant& iterable) { return createAndConstruct(s_LazyIterableViewClass, make_vec_array(iterable)); } Object AllocLazyKeyedIterableViewObject(const Variant& iterable) { return createAndConstruct(s_LazyKeyedIterableViewClass, make_vec_array(iterable)); } Object AllocUndefinedVariableExceptionObject(const Variant& message) { return createAndConstructThrowable(s_UndefinedVariableExceptionClass, message); } Object AllocTypecastExceptionObject(const Variant& message) { return createAndConstructThrowable(s_TypecastExceptionClass, message); } Object AllocReadonlyViolationExceptionObject(const Variant& message) { return createAndConstructThrowable(s_ReadonlyViolationExceptionClass, message); } Object AllocCoeffectViolationExceptionObject(const Variant& message) { return createAndConstructThrowable(s_CoeffectViolationExceptionClass, message); } Object AllocModuleBoundaryViolationExceptionObject(const Variant& message) { return createAndConstructThrowable(s_ModuleBoundaryViolationExceptionClass, message); } void throwExceptionObject(const Variant& message) { throw_object(AllocExceptionObject(message)); } void throwErrorObject(const Variant& message) { throw_object(AllocErrorObject(message)); } void throwArithmeticErrorObject(const Variant& message) { throw_object(AllocArithmeticErrorObject(message)); } void throwArgumentCountErrorObject(const Variant& message) { throw_object(AllocArgumentCountErrorObject(message)); } void throwDivisionByZeroErrorObject(const Variant& message) { throw_object(AllocDivisionByZeroErrorObject(message)); } void throwParseErrorObject(const Variant& message) { throw_object(AllocParseErrorObject(message)); } void throwTypeErrorObject(const Variant& message) { throw_object(AllocTypeErrorObject(message)); } void throwBadMethodCallExceptionObject(const Variant& message) { throw_object(AllocBadMethodCallExceptionObject(message)); } void throwInvalidArgumentExceptionObject(const Variant& message) { throw_object(AllocInvalidArgumentExceptionObject(message)); } void throwTypeAssertionExceptionObject(const Variant& message) { throw_object(AllocTypeAssertionExceptionObject(message)); } void throwRuntimeExceptionObject(const Variant& message) { throw_object(AllocRuntimeExceptionObject(message)); } void throwOutOfBoundsExceptionObject(const Variant& message) { throw_object(AllocOutOfBoundsExceptionObject(message)); } void throwInvalidOperationExceptionObject(const Variant& message) { throw_object(AllocInvalidOperationExceptionObject(message)); } void throwDOMExceptionObject(const Variant& message) { throw_object(AllocDOMExceptionObject(message)); } void throwDivisionByZeroExceptionObject() { throw_object(AllocDivisionByZeroExceptionObject()); } void throwSoapFaultObject(const Variant& code, const Variant& message, const Variant& actor /* = uninit_variant */, const Variant& detail /* = uninit_variant */, const Variant& name /* = uninit_variant */, const Variant& header /* = uninit_variant */) { throw_object(Object{AllocSoapFaultObject(code, message, actor, detail, name, header)}); } void throwInvalidForeachArgumentExceptionObject() { throw_object(AllocInvalidForeachArgumentExceptionObject()); } void throwUndefinedPropertyExceptionObject(const Variant& message) { throw_object(AllocUndefinedPropertyExceptionObject(message)); } void throwUndefinedVariableExceptionObject(const Variant& message) { throw_object(AllocUndefinedVariableExceptionObject(message)); } void throwTypecastExceptionObject(const Variant& message) { throw_object(AllocTypecastExceptionObject(message)); } void throwReadonlyViolationExceptionObject(const Variant& message) { throw_object(AllocReadonlyViolationExceptionObject(message)); } void throwCoeffectViolationExceptionObject(const Variant& message) { throw_object(AllocCoeffectViolationExceptionObject(message)); } void throwModuleBoundaryViolationExceptionObject(const Variant& message) { throw_object(AllocModuleBoundaryViolationExceptionObject(message)); } #define ALLOC_OBJECT_STUB(name) \ Object Alloc##name##Object() { \ return Object{s_##name##Class}; \ } ALLOC_OBJECT_STUB(Directory); ALLOC_OBJECT_STUB(PDOException); #undef ALLOC_OBJECT_STUB ///////////////////////////////////////////////////////////////////////////// static std::vector<Unit*> s_persistent_units; /* To be called during process startup ONLY, before threads are spun up. * Typically this will be called by HPHP::Extension::moduleInit to load an * extension-specific systemlib file, or to load the main systemlib. */ void addPersistentUnit(Unit* unit) { s_persistent_units.push_back(unit); } /* Typically called between requests in non-RepoAuthoritative mode * when function renaming is enabled. */ void mergePersistentUnits() { for (auto unit : s_persistent_units) { unit->merge(); } } namespace { Func* setupNullClsMethod(Func* f, Class* cls, StringData* name) { assertx(f && f->isPhpLeafFn()); auto clone = f->clone(cls, name); clone->setAttrs(static_cast<Attr>( AttrPublic | AttrNoInjection | AttrDynamicallyCallable)); clone->setRequiredCoeffects(RuntimeCoeffects::pure()); return clone; } Func* setup86ctorMethod(Class* cls) { if (!s_nullFunc) { s_nullFunc = Func::lookup(makeStaticString("__SystemLib\\__86null")); } return setupNullClsMethod(s_nullFunc, cls, s_86ctor.get()); } Func* setup86ReifiedInitMethod(Class* cls) { if (!s_singleArgNullFunc) { s_singleArgNullFunc = Func::lookup(makeStaticString("__SystemLib\\__86single_arg_null")); } return setupNullClsMethod(s_singleArgNullFunc, cls, s_86reifiedinit.get()); } } // namespace void setupNullCtor(Class* cls) { assertx(!s_nullCtor); s_nullCtor = setup86ctorMethod(cls); s_nullCtor->setHasForeignThis(true); } Func* getNull86reifiedinit(Class* cls) { auto f = setup86ReifiedInitMethod(cls); f->setBaseCls(cls); f->setGenerated(true); return f; } ///////////////////////////////////////////////////////////////////////////// namespace { struct Registered { std::atomic<bool> m_keep{false}; std::mutex m_lock; std::vector<std::unique_ptr<UnitEmitter>> m_ues; }; Registered& registered() { static Registered r; return r; } } void keepRegisteredUnitEmitters(bool b) { registered().m_keep.store(b); } void registerUnitEmitter(std::unique_ptr<UnitEmitter> ue) { assertx(ue->m_filepath->data()[0] == '/' && ue->m_filepath->data()[1] == ':'); auto& r = registered(); if (!r.m_keep) return; std::scoped_lock<std::mutex> _{r.m_lock}; r.m_ues.emplace_back(std::move(ue)); } std::vector<std::unique_ptr<UnitEmitter>> claimRegisteredUnitEmitters() { auto& r = registered(); std::scoped_lock<std::mutex> _{r.m_lock}; auto out = std::move(r.m_ues); assertx(r.m_ues.empty()); return out; } ///////////////////////////////////////////////////////////////////////////// } // namespace HPHP::SystemLib