Implement more of php assert()

Adds support for string arguments to assert (outside of repo
authoritative mode), and support for ASSERT_QUIET_EVAL.
Esse commit está contido em:
Jordan DeLong
2013-07-16 11:13:11 -07:00
commit de Sara Golemon
commit a466faf52d
4 arquivos alterados com 86 adições e 14 exclusões
+2
Ver Arquivo
@@ -1,3 +1,5 @@
Future (next release):
- Implement assert with string arguments
"Tamale" 7/22/2013
- Implement RecursiveArrayIterator
+78 -9
Ver Arquivo
@@ -14,8 +14,12 @@
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/runtime/ext/ext_options.h"
#include "folly/ScopeGuard.h"
#include "hphp/runtime/ext/ext_misc.h"
#include "hphp/runtime/ext/ext_error.h"
#include "hphp/runtime/ext/ext_function.h"
#include "hphp/runtime/ext/extension.h"
#include "hphp/runtime/base/runtime_option.h"
@@ -26,6 +30,7 @@
#include "hphp/runtime/base/runtime_error.h"
#include "hphp/runtime/base/zend_functions.h"
#include "hphp/runtime/base/zend_string.h"
#include "hphp/runtime/vm/jit/translator-inline.h"
#include "hphp/util/process.h"
#include <sys/utsname.h>
#include <pwd.h>
@@ -41,6 +46,7 @@ public:
assertActive = RuntimeOption::AssertActive ? 1 : 0;
assertWarning = RuntimeOption::AssertWarning ? 1 : 0;
assertBail = 0;
assertQuietEval = false;
}
virtual void requestShutdown() {
@@ -50,6 +56,7 @@ public:
int assertActive;
int assertWarning;
int assertBail;
bool assertQuietEval;
Variant assertCallback;
};
@@ -76,29 +83,91 @@ Variant f_assert_options(int what, CVarRef value /* = null_variant */) {
if (!value.isNull()) s_option_data->assertCallback = value;
return oldValue;
}
if (what == k_ASSERT_QUIET_EVAL) {
bool oldValue = s_option_data->assertQuietEval;
if (!value.isNull()) s_option_data->assertQuietEval = value.toBoolean();
return Variant(oldValue);
}
throw_invalid_argument("assert option %d is not supported", what);
return false;
}
static Variant eval_for_assert(ActRec* const curFP, CStrRef codeStr) {
String prefixedCode = concat3("<?php return ", codeStr, ";");
auto const oldErrorLevel =
s_option_data->assertQuietEval ? f_error_reporting(Variant(0)) : 0;
SCOPE_EXIT {
if (s_option_data->assertQuietEval) f_error_reporting(oldErrorLevel);
};
auto const unit = g_vmContext->compileEvalString(prefixedCode.get());
if (unit == nullptr) {
raise_recoverable_error("Syntax error in assert()");
// Failure to compile the eval string doesn't count as an
// assertion failure.
return Variant(true);
}
auto const func = unit->getMain();
TypedValue retVal;
g_vmContext->invokeFunc(
&retVal,
func,
null_array,
nullptr,
nullptr,
// Zend appears to share the variable environment with the assert()
// builtin, but we deviate by having no shared env here.
nullptr /* VarEnv */,
nullptr,
VMExecutionContext::InvokePseudoMain
);
return tvAsVariant(&retVal);
}
Variant f_assert(CVarRef assertion) {
if (!s_option_data->assertActive) return true;
if (assertion.isString()) {
throw NotSupportedException(__func__,
"assert cannot take string argument");
}
if (assertion.toBoolean()) return true;
// assertion failed
Transl::CallerFrame cf;
Offset callerOffset;
auto const fp = cf(&callerOffset);
auto const passed = [&]() -> bool {
if (assertion.isString()) {
if (RuntimeOption::RepoAuthoritative) {
// We could support this with compile-time string literals,
// but it's not yet implemented.
throw NotSupportedException(__func__,
"assert with strings argument in RepoAuthoritative mode");
}
return eval_for_assert(fp, assertion.toString()).toBoolean();
}
return assertion.toBoolean();
}();
if (passed) return true;
if (!s_option_data->assertCallback.isNull()) {
f_call_user_func(1, s_option_data->assertCallback);
auto const unit = fp->m_func->unit();
ArrayInit ai(3, ArrayInit::vectorInit);
ai.set(String(unit->filepath()));
ai.set(Variant(unit->getLineNumber(callerOffset)));
ai.set(assertion.isString() ? assertion.toString() : String(""));
f_call_user_func(1, s_option_data->assertCallback, ai.toArray());
}
if (s_option_data->assertWarning) {
raise_warning("Assertion failed");
auto const str = !assertion.isString()
? String("Assertion failed")
: concat3("Assertion \"", assertion.toString(), "\" failed");
raise_warning("%s", str.data());
}
if (s_option_data->assertBail) {
throw Assertion();
}
return uninit_null();
}
-1
Ver Arquivo
@@ -2405,7 +2405,6 @@ bool VMExecutionContext::evalUnit(Unit* unit, PC& pc, int funcType) {
}
Stats::inc(Stats::PseudoMain_Executed);
ActRec* ar = m_stack.allocA();
assert((uintptr_t)&ar->m_func < (uintptr_t)&ar->m_r);
Class* cls = curClass();
+6 -4
Ver Arquivo
@@ -136,7 +136,7 @@ struct EagerVMRegAnchor {
}
};
inline ActRec* regAnchorFP() {
inline ActRec* regAnchorFP(Offset* pc = nullptr) {
// In builtins, m_fp points to the caller's frame if called
// through FCallBuiltin, else it points to the builtin's frame,
// in which case, getPrevVMState() gets the caller's frame.
@@ -144,8 +144,9 @@ inline ActRec* regAnchorFP() {
// in order to find the true context.
VMExecutionContext* context = g_vmContext;
ActRec* cur = context->getFP();
if (pc) *pc = cur->m_func->unit()->offsetOf(context->getPC());
while (cur && cur->skipFrame()) {
cur = context->getPrevVMState(cur);
cur = context->getPrevVMState(cur, pc);
}
return cur;
}
@@ -171,8 +172,9 @@ struct EagerCallerFrame : public EagerVMRegAnchor {
// VM helper to retrieve the frame pointer from the TC. This is
// a common need for extensions.
struct CallerFrame : public VMRegAnchor {
ActRec* operator()() {
return regAnchorFP();
template<class... Args>
ActRec* operator()(Args&&... args) {
return regAnchorFP(std::forward<Args>(args)...);
}
ActRec* actRecForArgs() { return regAnchorFPForArgs(); }
};