Delete various dead C++ symbols
[hiphop-php.git] / hphp / runtime / vm / interp-helpers.h
blob337c82a4486cbb92159597c176ec9e36edef0420
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 #pragma once
19 #include "hphp/runtime/base/builtin-functions.h"
20 #include "hphp/runtime/base/coeffects-config.h"
21 #include "hphp/runtime/base/exceptions.h"
22 #include "hphp/runtime/base/object-data.h"
23 #include "hphp/runtime/base/runtime-error.h"
24 #include "hphp/runtime/base/runtime-option.h"
25 #include "hphp/runtime/base/request-info.h"
26 #include "hphp/runtime/ext/std/ext_std_closure.h"
27 #include "hphp/runtime/vm/act-rec.h"
28 #include "hphp/runtime/vm/bytecode.h"
29 #include "hphp/runtime/vm/class.h"
30 #include "hphp/runtime/vm/coeffects.h"
31 #include "hphp/runtime/vm/func.h"
32 #include "hphp/runtime/vm/hhbc.h"
33 #include "hphp/runtime/vm/reified-generics.h"
34 #include "hphp/runtime/vm/runtime.h"
35 #include "hphp/runtime/vm/type-constraint.h"
36 #include "hphp/util/text-util.h"
37 #include "hphp/util/trace.h"
39 #include <folly/Random.h>
41 namespace HPHP {
43 template<class TGetCtx>
44 void verifyParamType(const Func* func, int32_t id, tv_lval val,
45 TGetCtx getCtx) {
46 assertx(id < func->numNonVariadicParams());
47 assertx(func->numParams() == int(func->params().size()));
48 const TypeConstraint& tc = func->params()[id].typeConstraint;
49 if (tc.isCheckable()) {
50 auto const ctx = tc.isThis() ? getCtx() : nullptr;
51 tc.verifyParam(val, ctx, func, id);
53 if (func->hasParamsWithMultiUBs()) {
54 auto& ubs = const_cast<Func::ParamUBMap&>(func->paramUBs());
55 auto it = ubs.find(id);
56 if (it != ubs.end()) {
57 for (auto& ub : it->second) {
58 applyFlagsToUB(ub, tc);
59 if (ub.isCheckable()) {
60 auto const ctx = ub.isThis() ? getCtx() : nullptr;
61 ub.verifyParam(val, ctx, func, id);
69 * RAII wrapper for popping/pushing generics from/to the VM stack.
71 struct GenericsSaver {
72 explicit GenericsSaver(bool hasGenerics) : m_generics(pop(hasGenerics)) {}
73 ~GenericsSaver() { push(std::move(m_generics)); }
75 static Array pop(bool hasGenerics) {
76 if (LIKELY(!hasGenerics)) return Array();
77 assertx(tvIsVec(vmStack().topC()));
78 auto const generics = vmStack().topC()->m_data.parr;
79 vmStack().discard();
80 return Array::attach(generics);
82 static void push(Array&& generics) {
83 if (LIKELY(generics.isNull())) return;
84 vmStack().pushArrayLikeNoRc(generics.detach());
87 private:
88 Array m_generics;
92 * RAII wrapper for popping/pushing coeffects from/to the VM stack.
94 struct CoeffectsSaver {
95 explicit CoeffectsSaver(bool hasCoeffects) : m_coeffects(pop(hasCoeffects)) {}
96 ~CoeffectsSaver() { push(m_coeffects); }
98 static Optional<RuntimeCoeffects> pop(bool hasCoeffects) {
99 if (LIKELY(!hasCoeffects)) return std::nullopt;
100 assertx(tvIsInt(vmStack().topC()));
101 auto const coeffects = vmStack().topC()->m_data.num;
102 vmStack().discard();
103 return RuntimeCoeffects::fromValue(coeffects);
105 static void push(Optional<RuntimeCoeffects> coeffects) {
106 if (LIKELY(!coeffects)) return;
107 vmStack().pushInt(coeffects->value());
110 private:
111 Optional<RuntimeCoeffects> m_coeffects;
114 inline void callerDynamicCallChecks(const Func* func,
115 bool allowDynCallNoPointer = false) {
116 auto dynCallable = func->isDynamicallyCallable();
117 if (dynCallable) {
118 if (allowDynCallNoPointer) return;
119 if (!RO::EvalForbidDynamicCallsWithAttr) return;
121 auto level = func->isMethod()
122 ? (func->isStatic()
123 ? RO::EvalForbidDynamicCallsToClsMeth
124 : RO::EvalForbidDynamicCallsToInstMeth)
125 : RO::EvalForbidDynamicCallsToFunc;
126 if (level <= 0) return;
127 if (dynCallable && level < 2) return;
129 if (auto const rate = func->dynCallSampleRate()) {
130 if (folly::Random::rand32(*rate) != 0) return;
131 level = 1;
134 auto error_msg = dynCallable ?
135 Strings::FUNCTION_CALLED_DYNAMICALLY_WITH_ATTRIBUTE :
136 Strings::FUNCTION_CALLED_DYNAMICALLY_WITHOUT_ATTRIBUTE;
137 if (level >= 3 || (level >= 2 && !dynCallable)) {
138 std::string msg;
139 string_printf(msg, error_msg, func->fullName()->data());
140 throw_invalid_operation_exception(makeStaticString(msg));
142 raise_notice(error_msg, func->fullName()->data());
145 inline void callerDynamicConstructChecks(const Class* cls) {
146 auto level = RO::EvalForbidDynamicConstructs;
147 if (level <= 0 || cls->isDynamicallyConstructible()) return;
149 if (auto const rate = cls->dynConstructSampleRate()) {
150 if (folly::Random::rand32(*rate) != 0) return;
151 level = 1;
154 if (level >= 2) {
155 std::string msg;
156 string_printf(
157 msg,
158 Strings::CLASS_CONSTRUCTED_DYNAMICALLY,
159 cls->name()->data()
161 throw_invalid_operation_exception(makeStaticString(msg));
162 } else {
163 raise_notice(
164 Strings::CLASS_CONSTRUCTED_DYNAMICALLY,
165 cls->name()->data()
170 inline void calleeDynamicCallChecks(const Func* func, bool dynamicCall,
171 bool allowDynCallNoPointer = false) {
172 if (!dynamicCall) return;
173 if (func->isDynamicallyCallable() && allowDynCallNoPointer) return;
175 auto error_msg = func->isDynamicallyCallable() ?
176 Strings::FUNCTION_CALLED_DYNAMICALLY_WITH_ATTRIBUTE :
177 Strings::FUNCTION_CALLED_DYNAMICALLY_WITHOUT_ATTRIBUTE;
179 if (RuntimeOption::EvalNoticeOnBuiltinDynamicCalls && func->isBuiltin()) {
180 raise_notice(
181 error_msg,
182 func->fullName()->data()
188 * Check if the `callee` satisfies the coeffect constraints on the prologue
189 * flags. Returns true if yes, otherwise raise a warning and return false or
190 * raise an exception.
192 inline bool calleeCoeffectChecks(const Func* callee,
193 RuntimeCoeffects providedCoeffects,
194 uint32_t numArgsInclUnpack,
195 void* prologueCtx) {
196 if (!CoeffectsConfig::enabled()) {
197 if (callee->hasCoeffectsLocal()) {
198 vmStack().pushInt(RuntimeCoeffects::none().value());
200 return true;
202 auto const requiredCoeffects = [&] {
203 auto required = callee->requiredCoeffects();
204 if (!callee->hasCoeffectRules()) return required;
205 for (auto const& rule : callee->getCoeffectRules()) {
206 required |= rule.emit(callee, numArgsInclUnpack, prologueCtx,
207 providedCoeffects);
209 if (callee->hasCoeffectsLocal()) vmStack().pushInt(required.value());
210 return required;
211 }();
212 if (LIKELY(providedCoeffects.canCall(requiredCoeffects))) return true;
213 raiseCoeffectsCallViolation(callee, providedCoeffects, requiredCoeffects);
214 return false;
218 * Check for presence, count and wildcard match of generics.
220 inline void calleeGenericsChecks(const Func* callee, bool hasGenerics) {
221 if (LIKELY(!callee->hasReifiedGenerics())) {
222 if (UNLIKELY(hasGenerics)) vmStack().popC();
223 return;
226 if (!hasGenerics) {
227 if (!areAllGenericsSoft(callee->getReifiedGenericsInfo())) {
228 throw_call_reified_func_without_generics(callee);
231 raise_warning_for_soft_reified(0, true, callee->fullName());
233 // Push an empty array, as the remainder of the call setup assumes generics
234 // are on the stack.
235 auto const ad = ArrayData::CreateVec();
236 vmStack().pushArrayLikeNoRc(ad);
237 return;
240 auto const generics = vmStack().topC();
241 assertx(tvIsVec(generics));
242 checkFunReifiedGenericMismatch(callee, val(generics).parr);
246 * Check for too few or too many arguments and trim extra args.
248 inline void calleeArgumentArityChecks(const Func* callee,
249 uint32_t& numArgsInclUnpack) {
250 if (numArgsInclUnpack < callee->numRequiredParams()) {
251 throwMissingArgument(callee, numArgsInclUnpack);
254 if (numArgsInclUnpack > callee->numParams()) {
255 assertx(!callee->hasVariadicCaptureParam());
256 assertx(numArgsInclUnpack == callee->numNonVariadicParams() + 1);
257 --numArgsInclUnpack;
259 GenericsSaver gs{callee->hasReifiedGenerics()};
261 assertx(tvIsVec(vmStack().topC()));
262 auto const numUnpackArgs = vmStack().topC()->m_data.parr->size();
263 vmStack().popC();
265 if (numUnpackArgs != 0) {
266 raiseTooManyArguments(callee, numArgsInclUnpack + numUnpackArgs);
271 inline void calleeArgumentTypeChecks(const Func* callee,
272 uint32_t numArgsInclUnpack,
273 void* prologueCtx) {
274 // Builtins use a separate non-standard mechanism.
275 if (callee->isCPPBuiltin()) return;
277 auto const getCtx = [&] () -> const Class* {
278 if (!callee->cls()) return nullptr;
279 assertx(prologueCtx);
280 auto const ctx = callee->isClosureBody()
281 ? reinterpret_cast<c_Closure*>(prologueCtx)->getThisOrClass()
282 : prologueCtx;
283 return callee->isStatic()
284 ? reinterpret_cast<Class*>(ctx)
285 : reinterpret_cast<ObjectData*>(ctx)->getVMClass();
288 auto const numArgs =
289 std::min(numArgsInclUnpack, callee->numNonVariadicParams());
290 auto const firstArgIdx =
291 numArgsInclUnpack - 1 + (callee->hasReifiedGenerics() ? 1 : 0);
292 for (auto i = 0; i < numArgs; ++i) {
293 verifyParamType(callee, i, vmStack().indC(firstArgIdx - i), getCtx);
297 inline void initFuncInputs(const Func* callee, uint32_t numArgsInclUnpack) {
298 assertx(numArgsInclUnpack <= callee->numParams());
300 // All arguments already initialized. Extra arguments already popped
301 // by calleeArgumentArityChecks().
302 if (LIKELY(numArgsInclUnpack == callee->numParams())) return;
304 CoeffectsSaver cs{callee->hasCoeffectsLocal()};
305 GenericsSaver gs{callee->hasReifiedGenerics()};
306 auto const numParams = callee->numNonVariadicParams();
307 while (numArgsInclUnpack < numParams) {
308 vmStack().pushUninit();
309 ++numArgsInclUnpack;
312 if (callee->hasVariadicCaptureParam()) {
313 auto const ad = ArrayData::CreateVec();
314 vmStack().pushArrayLikeNoRc(ad);
315 ++numArgsInclUnpack;
318 assertx(numArgsInclUnpack == callee->numParams());
322 * This helper does a stack overflow check on *both* the native stack
323 * and the VM stack.
325 * In some cases for re-entry, we're checking for space other than
326 * just the callee, and `extraCells' may need to be passed with a
327 * non-zero value. (We over-check in these situations, but it's fine.)
329 ALWAYS_INLINE
330 void checkStack(Stack& stk, const Func* f, int32_t extraCells) {
332 * Check whether func's maximum stack usage would overflow the stack.
333 * Both native and VM stack overflows are independently possible.
335 * All stack checks are inflated by stackCheckPadding() to ensure
336 * there is space both for calling leaf functions /and/ for
337 * re-entry. (See kStackCheckReenterPadding and
338 * RuntimeOption::EvalStackCheckLeafPadding.)
340 auto limit = f->maxStackCells() + stackCheckPadding() + extraCells;
341 if (LIKELY(stack_in_bounds() && !stk.wouldOverflow(limit))) return;
342 TRACE_MOD(Trace::gc, 1, "Maximum stack depth exceeded.\n");
343 throw_stack_overflow();