Skip to content

Commit

Permalink
PHP7 Engine Exceptions
Browse files Browse the repository at this point in the history
Summary:
Adds PHP 7 engine exception classes and flag to enable their use. When the
flag is set they are auto-imported from the __SystemLib namespace.

Fixes #6012

Reviewed By: jwatzman

Differential Revision: D2674480

fb-gh-sync-id: 71923c98e005742739cdb8c061d1ca400f4da82e
  • Loading branch information
paulbiss authored and hhvm-bot committed Dec 4, 2015
1 parent b20f260 commit b44b798
Show file tree
Hide file tree
Showing 59 changed files with 597 additions and 334 deletions.
13 changes: 13 additions & 0 deletions hphp/compiler/parser/parser.cpp
Expand Up @@ -2428,6 +2428,10 @@ Parser::AliasFlags Parser::getAliasFlags() {
flags = flags | AliasFlags::PHP7_ScalarTypes;
}

if (RuntimeOption::PHP7_EngineExceptions) {
flags = flags | AliasFlags::PHP7_EngineExceptions;
}

return flags;
}

Expand All @@ -2453,6 +2457,8 @@ Parser::AutoAliasMap getAutoAliasedClassesHelper() {
#define HH_ALIAS(alias, name) \
ALIAS(#alias, "HH\\" #name, AliasFlags::HH)
#define SCALAR_TYPE(name) HH_TYPE(name, AliasFlags::PHP7_ScalarTypes)
#define PHP7_TYPE(name, option) \
ALIAS(#name, "__SystemLib\\" #name, AliasFlags::option)
Parser::AutoAliasMap aliases {
HH_ONLY_TYPE(AsyncIterator),
HH_ONLY_TYPE(AsyncKeyedIterator),
Expand Down Expand Up @@ -2520,7 +2526,14 @@ Parser::AutoAliasMap getAutoAliasedClassesHelper() {
HH_ALIAS(integer, int),
HH_ALIAS(double, float),
HH_ALIAS(real, float),

// Engine exception classes
PHP7_TYPE(Throwable, PHP7_EngineExceptions),
PHP7_TYPE(Error, PHP7_EngineExceptions),
PHP7_TYPE(ParseError, PHP7_EngineExceptions),
PHP7_TYPE(TypeError, PHP7_EngineExceptions),
};
#undef PHP7_TYPE
#undef HH_ALIAS
#undef SCALAR_TYPE
#undef HH_ONLY_TYPE
Expand Down
1 change: 1 addition & 0 deletions hphp/compiler/parser/parser.h
Expand Up @@ -519,6 +519,7 @@ class Parser : public ParserBase {
None = 0,
HH = 0x1,
PHP7_ScalarTypes = 0x2,
PHP7_EngineExceptions = 0x4,
};

struct AutoAlias {
Expand Down
4 changes: 2 additions & 2 deletions hphp/hhbbc/interp.cpp
Expand Up @@ -52,7 +52,7 @@ namespace HPHP { namespace HHBBC {

namespace {

const StaticString s_Exception("Exception");
const StaticString s_Throwable("__SystemLib\\Throwable");
const StaticString s_empty("");
const StaticString s_construct("__construct");
const StaticString s_86ctor("86ctor");
Expand Down Expand Up @@ -715,7 +715,7 @@ void in(ISS& env, const bc::Throw& op) { popC(env); }

void in(ISS& env, const bc::Catch&) {
nothrow(env);
return push(env, subObj(env.index.builtin_class(s_Exception.get())));
return push(env, subObj(env.index.builtin_class(s_Throwable.get())));
}

void in(ISS& env, const bc::NativeImpl&) {
Expand Down
16 changes: 15 additions & 1 deletion hphp/hhvm/process-init.cpp
Expand Up @@ -52,6 +52,14 @@ SYSTEMLIB_CLASSES(SYSTEM_CLASS_STRING)
#undef pinitSentinel
#undef STRINGIZE_CLASS_NAME

namespace {
const StaticString s_Throwable("\\__SystemLib\\Throwable");
const StaticString s_BaseException("\\__SystemLib\\BaseException");
const StaticString s_Error("\\__SystemLib\\Error");
const StaticString s_ParseError("\\__SystemLib\\ParseError");
const StaticString s_TypeError("\\__SystemLib\\TypeError");
}

void tweak_variant_dtors();
void ProcessInit() {
// Create the global mcg object
Expand Down Expand Up @@ -152,11 +160,17 @@ void ProcessInit() {

#define INIT_SYSTEMLIB_CLASS_FIELD(cls) \
{ \
Class *cls = NamedEntity::get(s_##cls.get())->clsList(); \
Class *cls = NamedEntity::get(s_##cls.get())->clsList(); \
assert(!hhbc_ext_class_count || cls); \
SystemLib::s_##cls##Class = cls; \
}

INIT_SYSTEMLIB_CLASS_FIELD(Throwable)
INIT_SYSTEMLIB_CLASS_FIELD(BaseException)
INIT_SYSTEMLIB_CLASS_FIELD(Error)
INIT_SYSTEMLIB_CLASS_FIELD(ParseError)
INIT_SYSTEMLIB_CLASS_FIELD(TypeError)

// Stash a pointer to the VM Classes for stdclass, Exception,
// pinitSentinel and resource
SYSTEMLIB_CLASSES(INIT_SYSTEMLIB_CLASS_FIELD)
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/apc-local-array.cpp
Expand Up @@ -220,7 +220,7 @@ ArrayData* APCLocalArray::Copy(const ArrayData* ad) {
}

ArrayData* APCLocalArray::CopyWithStrongIterators(const ArrayData*) {
throw FatalErrorException(
raise_fatal_error(
"Unimplemented ArrayData::copyWithStrongIterators");
}

Expand Down
6 changes: 3 additions & 3 deletions hphp/runtime/base/array-data.cpp
Expand Up @@ -92,15 +92,15 @@ ArrayData* ArrayData::GetScalarArray(ArrayData* arr,
//////////////////////////////////////////////////////////////////////

static ArrayData* ZSetIntThrow(ArrayData* ad, int64_t k, RefData* v) {
throw FatalErrorException("Unimplemented ArrayData::ZSetInt");
raise_fatal_error("Unimplemented ArrayData::ZSetInt");
}

static ArrayData* ZSetStrThrow(ArrayData* ad, StringData* k, RefData* v) {
throw FatalErrorException("Unimplemented ArrayData::ZSetStr");
raise_fatal_error("Unimplemented ArrayData::ZSetStr");
}

static ArrayData* ZAppendThrow(ArrayData* ad, RefData* v, int64_t* key_ptr) {
throw FatalErrorException("Unimplemented ArrayData::ZAppend");
raise_fatal_error("Unimplemented ArrayData::ZAppend");
}

//////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/array-iterator.cpp
Expand Up @@ -415,7 +415,7 @@ Variant ArrayIter::second() {

const Variant& ArrayIter::secondRef() {
if (!hasArrayData()) {
throw FatalErrorException("taking reference on iterator objects");
raise_fatal_error("taking reference on iterator objects");
}
assert(hasArrayData());
const ArrayData* ad = getArrayData();
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/array-util.cpp
Expand Up @@ -452,7 +452,7 @@ static void create_miter_for_walk(folly::Optional<MArrayIter>& miter,
bool isIterable;
Object iterable = odata->iterableObject(isIterable);
if (isIterable) {
throw FatalErrorException("An iterator cannot be used with "
raise_fatal_error("An iterator cannot be used with "
"foreach by reference");
}
Array properties = iterable->o_toIterArray(null_string,
Expand Down
12 changes: 8 additions & 4 deletions hphp/runtime/base/autoload-handler.cpp
Expand Up @@ -44,6 +44,7 @@ const StaticString
s_failure("failure"),
s_autoload("__autoload"),
s_exception("exception"),
s_error("SystemLib\\Error"),
s_previous("previous");

using CufIterPtr = req::unique_ptr<CufIter>;
Expand Down Expand Up @@ -420,17 +421,20 @@ bool AutoloadHandler::autoloadClassPHP5Impl(const String& className,
try {
vm_call_user_func_cufiter(*hb.m_cufIter, params);
} catch (Object& ex) {
assert(ex.instanceof(SystemLib::s_ExceptionClass));
assert(ex.instanceof(SystemLib::s_ThrowableClass));
if (autoloadException.isNull()) {
autoloadException = ex;
} else {
Object cur = ex;
Variant next = cur->o_get(s_previous, false, s_exception);
auto const ctx = cur->instanceof(SystemLib::s_ExceptionClass)
? s_exception
: s_error;
Variant next = cur->o_get(s_previous, false, ctx);
while (next.isObject()) {
cur = next.toObject();
next = cur->o_get(s_previous, false, s_exception);
next = cur->o_get(s_previous, false, ctx);
}
cur->o_set(s_previous, autoloadException, s_exception);
cur->o_set(s_previous, autoloadException, ctx);
autoloadException = ex;
}
}
Expand Down
9 changes: 4 additions & 5 deletions hphp/runtime/base/builtin-functions.cpp
Expand Up @@ -512,7 +512,7 @@ Variant o_invoke_failed(const char *cls, const char *meth,
msg += cls;
msg += "::";
msg += meth;
throw FatalErrorException(msg.c_str());
raise_fatal_error(msg.c_str());
} else {
raise_warning("call_user_func to non-existent method %s::%s", cls, meth);
return false;
Expand Down Expand Up @@ -977,7 +977,7 @@ static Variant include_impl(const String& file, bool once,
if (required) {
String ms = "Required file that does not exist: ";
ms += file;
throw FatalErrorException(ms.data());
raise_fatal_error(ms.data());
}
return false;
}
Expand All @@ -1002,9 +1002,8 @@ bool function_exists(const String& function_name) {
// debugger and code coverage instrumentation

void throw_exception(const Object& e) {
if (!e.instanceof(SystemLib::s_ExceptionClass)) {
raise_error("Exceptions must be valid objects derived from the "
"Exception base class");
if (!e.instanceof(SystemLib::s_ThrowableClass)) {
raise_error("Exceptions must implement the Throwable interface.");
}
DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e.get()));
throw e;
Expand Down
21 changes: 21 additions & 0 deletions hphp/runtime/base/exceptions.cpp
Expand Up @@ -15,9 +15,12 @@
*/
#include "hphp/runtime/base/exceptions.h"

#include "hphp/system/systemlib.h"
#include "hphp/runtime/base/execution-context.h"
#include "hphp/runtime/base/backtrace.h"
#include "hphp/runtime/base/imarker.h"
#include "hphp/runtime/base/type-variant.h"
#include "hphp/runtime/vm/vm-regs.h"

namespace HPHP {

Expand Down Expand Up @@ -159,5 +162,23 @@ void throw_not_supported(const char* feature, const char* reason) {
throw ExtendedException("%s is not supported: %s", feature, reason);
}

///////////////////////////////////////////////////////////////////////////////

ATTRIBUTE_NORETURN
void raise_fatal_error(const char* msg,
const Array& bt /* = null_array */,
bool recoverable /* = false */,
bool silent /* = false */,
bool throws /* = true */) {
if (RuntimeOption::PHP7_EngineExceptions && throws) {
VMRegAnchor _;
SystemLib::throwErrorObject(Variant(msg));
}
auto ex = bt.isNull() && !recoverable
? FatalErrorException(msg)
: FatalErrorException(msg, bt, recoverable);
ex.setSilent(silent);
throw ex;
}
///////////////////////////////////////////////////////////////////////////////
}
5 changes: 5 additions & 0 deletions hphp/runtime/base/exceptions.h
Expand Up @@ -105,6 +105,11 @@ struct FatalErrorException : ExtendedException {
bool m_recoverable{false};
};

ATTRIBUTE_NORETURN
void raise_fatal_error(const char* msg, const Array& bt = null_array,
bool recoverable = false, bool silent = false,
bool throws = true);

//////////////////////////////////////////////////////////////////////

struct ResourceExceededException : FatalErrorException {
Expand Down
8 changes: 4 additions & 4 deletions hphp/runtime/base/execution-context.cpp
Expand Up @@ -733,9 +733,9 @@ void ExecutionContext::handleError(const std::string& msg,
DEBUGGER_ATTACHED_ONLY(phpDebuggerErrorHook(ee, errnum, msg));
bool isRecoverable =
errnum == static_cast<int>(ErrorMode::RECOVERABLE_ERROR);
auto exn = FatalErrorException(msg, ee.getBacktrace(), isRecoverable);
exn.setSilent(!errorNeedsLogging(errnum));
throw exn;
raise_fatal_error(msg.c_str(), ee.getBacktrace(), isRecoverable,
!errorNeedsLogging(errnum) /* silent */);
not_reached();
}
if (!handled) {
VMRegAnchor _;
Expand Down Expand Up @@ -876,7 +876,7 @@ bool ExecutionContext::onUnhandledException(Object e) {
Logger::Error("\nFatal error: Uncaught %s", err.data());
}

if (e.instanceof(SystemLib::s_ExceptionClass)) {
if (e.instanceof(SystemLib::s_ThrowableClass)) {
// user thrown exception
if (!m_userExceptionHandlers.empty()) {
if (!same(vm_call_user_func
Expand Down
8 changes: 4 additions & 4 deletions hphp/runtime/base/mem-file.cpp
Expand Up @@ -70,7 +70,7 @@ bool MemFile::open(const String& filename, const String& mode) {
assert(RuntimeOption::EnableOnDemandUncompress);
data = gzdecode(data, len);
if (data == nullptr) {
throw FatalErrorException("cannot unzip compressed data");
raise_fatal_error("cannot unzip compressed data");
}
m_data = data;
m_malloced = true;
Expand Down Expand Up @@ -173,12 +173,12 @@ bool MemFile::rewind() {
}

int64_t MemFile::writeImpl(const char *buffer, int64_t length) {
throw FatalErrorException((std::string("cannot write a mem stream: ") +
raise_fatal_error((std::string("cannot write a mem stream: ") +
getName()).c_str());
}

bool MemFile::flush() {
throw FatalErrorException((std::string("cannot flush a mem stream: ") +
raise_fatal_error((std::string("cannot flush a mem stream: ") +
getName()).c_str());
}

Expand All @@ -200,7 +200,7 @@ void MemFile::unzip() {
int len = m_len;
char *data = gzdecode(m_data, len);
if (data == nullptr) {
throw FatalErrorException((std::string("cannot unzip mem stream: ") +
raise_fatal_error((std::string("cannot unzip mem stream: ") +
getName()).c_str());
}
m_data = data;
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/memory-manager.cpp
Expand Up @@ -1451,7 +1451,7 @@ void* ContiguousHeap::heapAlloc(size_t nbytes, size_t &cap) {
"Heap address space exhausted\nbase:{}\nend:{}\nused{}",
m_base, m_end, m_used);
// Throw exception when t4840214 is fixed
// throw FatalErrorException("Request heap out of memory");
// raise_fatal_error("Request heap out of memory");
} else if (UNLIKELY(m_used > m_OOMMarker)) {
setSurpriseFlag(MemExceededFlag);
}
Expand Down
4 changes: 2 additions & 2 deletions hphp/runtime/base/output-file.cpp
Expand Up @@ -29,7 +29,7 @@ const StaticString s_output("Output");

OutputFile::OutputFile(const String& filename): File(true, s_php, s_output) {
if (filename != s_php_output) {
throw FatalErrorException("not a php://output file ");
raise_fatal_error("not a php://output file ");
}
setIsLocal(true);
}
Expand All @@ -44,7 +44,7 @@ void OutputFile::sweep() {
}

bool OutputFile::open(const String& filename, const String& mode) {
throw FatalErrorException("cannot open a php://output file ");
raise_fatal_error("cannot open a php://output file ");
}

bool OutputFile::close() {
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/program-functions.cpp
Expand Up @@ -1610,7 +1610,7 @@ static int execute_program_impl(int argc, char** argv) {
Array bt = createBacktrace(BacktraceArgs()
.withSelf()
.setParserFrame(&parserFrame));
throw FatalErrorException(msg->data(), bt);
raise_fatal_error(msg->data(), bt);
}
} catch (FileOpenException &e) {
Logger::Error("%s", e.getMessage().c_str());
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/proxy-array.cpp
Expand Up @@ -435,7 +435,7 @@ void ProxyArray::proxyAppend(void* data, uint32_t data_size, void** dest) {
void ProxyArray::proxyInit(uint32_t nSize,
DtorFunc pDestructor, bool persistent) {
if (persistent) {
throw FatalErrorException("zend_hash_init: \"persistent\" is \
raise_fatal_error("zend_hash_init: \"persistent\" is \
unimplemented");
}
if (nSize) {
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/base/resource-data.cpp
Expand Up @@ -71,7 +71,7 @@ const String& ResourceData::o_getClassName() const {
}

const String& ResourceData::o_getClassNameHook() const {
throw FatalErrorException("Resource did not provide a name");
raise_fatal_error("Resource did not provide a name");
}

const String& ResourceData::o_getResourceName() const {
Expand Down
9 changes: 9 additions & 0 deletions hphp/runtime/base/runtime-error.cpp
Expand Up @@ -20,6 +20,7 @@
#include "hphp/runtime/base/runtime-option.h"
#include "hphp/runtime/vm/repo.h"
#include "hphp/runtime/vm/repo-global-data.h"
#include "hphp/runtime/vm/vm-regs.h"
#include "hphp/util/logger.h"
#include "hphp/util/string-vsnprintf.h"

Expand Down Expand Up @@ -81,13 +82,21 @@ void raise_recoverable_error_without_first_frame(const std::string &msg) {
}

void raise_typehint_error(const std::string& msg) {
if (RuntimeOption::PHP7_EngineExceptions) {
VMRegAnchor _;
SystemLib::throwTypeErrorObject(msg);
}
raise_recoverable_error(msg);
if (RuntimeOption::RepoAuthoritative && Repo::global().HardTypeHints) {
raise_error("Error handler tried to recover from typehint violation");
}
}

void raise_return_typehint_error(const std::string& msg) {
if (RuntimeOption::PHP7_EngineExceptions) {
VMRegAnchor _;
SystemLib::throwTypeErrorObject(msg);
}
raise_recoverable_error(msg);
if (RuntimeOption::EvalCheckReturnTypeHints >= 3 ||
(RuntimeOption::RepoAuthoritative &&
Expand Down

0 comments on commit b44b798

Please sign in to comment.