2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_INTERP_HELPERS_H_
18 #define incl_HPHP_VM_INTERP_HELPERS_H_
20 #include "hphp/runtime/base/builtin-functions.h"
21 #include "hphp/runtime/base/exceptions.h"
22 #include "hphp/runtime/base/runtime-error.h"
23 #include "hphp/runtime/base/runtime-option.h"
24 #include "hphp/runtime/base/request-info.h"
25 #include "hphp/runtime/vm/act-rec.h"
26 #include "hphp/runtime/vm/bytecode.h"
27 #include "hphp/runtime/vm/func.h"
28 #include "hphp/runtime/vm/hhbc.h"
29 #include "hphp/runtime/vm/runtime.h"
30 #include "hphp/runtime/vm/rx.h"
31 #include "hphp/util/text-util.h"
32 #include "hphp/util/trace.h"
34 #include <folly/Random.h>
38 inline void callerInOutChecks(const Func
* func
, const FCallArgs
& fca
) {
39 for (auto i
= 0; i
< fca
.numArgs
; ++i
) {
40 auto const inout
= func
->isInOut(i
);
41 if (inout
!= fca
.isInOut(i
)) {
42 SystemLib::throwInvalidArgumentExceptionObject(
43 formatParamInOutMismatch(func
->fullName()->data(), i
, inout
));
48 inline void callerDynamicCallChecks(const Func
* func
,
49 bool allowDynCallNoPointer
= false) {
50 auto dynCallable
= func
->isDynamicallyCallable();
52 if (allowDynCallNoPointer
) return;
53 if (!RO::EvalForbidDynamicCallsWithAttr
) return;
55 auto level
= func
->isMethod()
57 ? RO::EvalForbidDynamicCallsToClsMeth
58 : RO::EvalForbidDynamicCallsToInstMeth
)
59 : RO::EvalForbidDynamicCallsToFunc
;
60 if (level
<= 0) return;
62 if (auto const rate
= func
->dynCallSampleRate()) {
63 if (folly::Random::rand32(*rate
) != 0) return;
67 auto error_msg
= dynCallable
?
68 Strings::FUNCTION_CALLED_DYNAMICALLY_WITH_ATTRIBUTE
:
69 Strings::FUNCTION_CALLED_DYNAMICALLY_WITHOUT_ATTRIBUTE
;
70 if (level
>= 3 || (level
>= 2 && !dynCallable
)) {
72 string_printf(msg
, error_msg
, func
->fullName()->data());
73 throw_invalid_operation_exception(makeStaticString(msg
));
75 raise_notice(error_msg
, func
->fullName()->data());
78 inline void callerDynamicConstructChecks(const Class
* cls
) {
79 auto level
= RO::EvalForbidDynamicConstructs
;
80 if (level
<= 0 || cls
->isDynamicallyConstructible()) return;
82 if (auto const rate
= cls
->dynConstructSampleRate()) {
83 if (folly::Random::rand32(*rate
) != 0) return;
91 Strings::CLASS_CONSTRUCTED_DYNAMICALLY
,
94 throw_invalid_operation_exception(makeStaticString(msg
));
97 Strings::CLASS_CONSTRUCTED_DYNAMICALLY
,
103 inline void calleeDynamicCallChecks(const Func
* func
, bool dynamicCall
,
104 bool allowDynCallNoPointer
= false) {
105 if (!dynamicCall
) return;
106 if (func
->isDynamicallyCallable() && allowDynCallNoPointer
) return;
108 auto error_msg
= func
->isDynamicallyCallable() ?
109 Strings::FUNCTION_CALLED_DYNAMICALLY_WITH_ATTRIBUTE
:
110 Strings::FUNCTION_CALLED_DYNAMICALLY_WITHOUT_ATTRIBUTE
;
112 if (RuntimeOption::EvalNoticeOnBuiltinDynamicCalls
&& func
->isBuiltin()) {
115 func
->fullName()->data()
121 * Check if a call from `caller` to `callee` satisfies reactivity constraints.
122 * Returns true if yes, otherwise raise a warning and return false or raise
125 inline bool callerRxChecks(const ActRec
* caller
, const Func
* callee
) {
126 if (RuntimeOption::EvalRxEnforceCalls
<= 0) return true;
127 // Conditional reactivity is not tracked yet, so assume the caller has minimum
128 // and the callee has maximum possible level of reactivity.
129 auto const callerLevel
= caller
->rxMinLevel();
130 if (!rxEnforceCallsInLevel(callerLevel
)) return true;
132 auto const minReqCalleeLevel
= rxRequiredCalleeLevel(callerLevel
);
133 if (LIKELY(callee
->rxLevel() >= minReqCalleeLevel
)) return true;
134 raiseRxCallViolation(caller
, callee
);
139 * This helper only does a stack overflow check for the native stack.
140 * Both native and VM stack overflows are independently possible.
142 inline void checkNativeStack() {
143 // Check whether we're going out of bounds of our native stack.
144 if (LIKELY(stack_in_bounds())) return;
145 TRACE_MOD(Trace::gc
, 1, "Maximum stack depth exceeded.\n");
146 throw_stack_overflow();
150 * This helper does a stack overflow check on *both* the native stack
153 * In some cases for re-entry, we're checking for space other than
154 * just the callee, and `extraCells' may need to be passed with a
155 * non-zero value. (We over-check in these situations, but it's fine.)
158 void checkStack(Stack
& stk
, const Func
* f
, int32_t extraCells
) {
160 * Check whether func's maximum stack usage would overflow the stack.
161 * Both native and VM stack overflows are independently possible.
163 * All stack checks are inflated by kStackCheckPadding to ensure
164 * there is space both for calling leaf functions /and/ for
165 * re-entry. (See kStackCheckReenterPadding and
166 * kStackCheckLeafPadding.)
168 auto limit
= f
->maxStackCells() + kStackCheckPadding
+ extraCells
;
169 if (LIKELY(stack_in_bounds() && !stk
.wouldOverflow(limit
))) return;
170 TRACE_MOD(Trace::gc
, 1, "Maximum stack depth exceeded.\n");
171 throw_stack_overflow();