Compute ambient coeffects and write to coeffects local
[hiphop-php.git] / hphp / runtime / vm / runtime.cpp
blob84f63d5289735714c6bb1307cc399337c10ed7f1
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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/vm/runtime.h"
17 #include "hphp/runtime/base/execution-context.h"
18 #include "hphp/runtime/base/coeffects-config.h"
19 #include "hphp/runtime/server/source-root-info.h"
20 #include "hphp/runtime/base/zend-string.h"
21 #include "hphp/runtime/base/mixed-array.h"
22 #include "hphp/runtime/base/builtin-functions.h"
23 #include "hphp/runtime/base/request-info.h"
24 #include "hphp/runtime/base/unit-cache.h"
25 #include "hphp/runtime/ext/std/ext_std_closure.h"
26 #include "hphp/runtime/ext/generator/ext_generator.h"
27 #include "hphp/runtime/vm/bytecode.h"
28 #include "hphp/runtime/vm/repo.h"
29 #include "hphp/util/trace.h"
30 #include "hphp/util/text-util.h"
31 #include "hphp/runtime/vm/jit/translator-inline.h"
32 #include "hphp/runtime/base/zend-functions.h"
33 #include "hphp/runtime/ext/string/ext_string.h"
35 #include <folly/tracing/StaticTracepoint.h>
37 namespace HPHP {
39 TRACE_SET_MOD(runtime);
41 /**
42 * print_string will decRef the string
44 void print_string(StringData* s) {
45 g_context->write(s->data(), s->size());
46 TRACE(2, "t-x64 output(str): (%p) %43s\n", s->data(),
47 escapeStringForCPP(s->data(), s->size()).data());
48 decRefStr(s);
51 void print_int(int64_t i) {
52 char intbuf[21];
53 auto const s = conv_10(i, intbuf + sizeof(intbuf));
55 g_context->write(s.data(), s.size());
58 void print_boolean(bool val) {
59 if (val) {
60 g_context->write("1", 1);
64 /**
65 * concat_ss will will incRef the output string
66 * and decref its first argument
68 StringData* concat_ss(StringData* v1, StringData* v2) {
69 if (v1->cowCheck()) {
70 FOLLY_SDT(hhvm, hhvm_cow_concat, v1->size(), v2->size());
71 StringData* ret = StringData::Make(v1, v2);
72 // Because v1 was shared, we know this won't release the string.
73 v1->decRefCount();
74 return ret;
77 auto const rhs = v2->slice();
78 UNUSED auto const lsize = v1->size();
79 FOLLY_SDT(hhvm, hhvm_mut_concat, lsize, rhs.size());
80 auto const ret = v1->append(rhs);
81 if (UNLIKELY(ret != v1)) {
82 // had to realloc even though count==1
83 assertx(v1->hasExactlyOneRef());
84 v1->release();
86 return ret;
89 /**
90 * concat_is will incRef the output string
92 StringData* concat_is(int64_t v1, StringData* v2) {
93 char intbuf[21];
94 // Convert the int to a string
95 auto const s1 = conv_10(v1, intbuf + sizeof(intbuf));
96 auto const s2 = v2->slice();
97 return StringData::Make(s1, s2);
101 * concat_si will incRef the output string
102 * and decref its first argument
104 StringData* concat_si(StringData* v1, int64_t v2) {
105 char intbuf[21];
106 auto const s2 = conv_10(v2, intbuf + sizeof(intbuf));
107 if (v1->cowCheck()) {
108 auto const s1 = v1->slice();
109 FOLLY_SDT(hhvm, hhvm_cow_concat, s1.size(), s2.size());
110 auto const ret = StringData::Make(s1, s2);
111 // Because v1 was shared, we know this won't release it.
112 v1->decRefCount();
113 return ret;
116 UNUSED auto const lsize = v1->size();
117 FOLLY_SDT(hhvm, hhvm_mut_concat, lsize, s2.size());
118 auto const ret = v1->append(s2);
119 if (UNLIKELY(ret != v1)) {
120 // had to realloc even though count==1
121 assertx(v1->hasExactlyOneRef());
122 v1->release();
124 return ret;
127 StringData* concat_s3(StringData* v1, StringData* v2, StringData* v3) {
128 if (v1->cowCheck()) {
129 auto s1 = v1->slice();
130 auto s2 = v2->slice();
131 auto s3 = v3->slice();
132 FOLLY_SDT(hhvm, hhvm_cow_concat, s1.size(), s2.size() + s3.size());
133 StringData* ret = StringData::Make(s1, s2, s3);
134 // Because v1 was shared, we know this won't release it.
135 v1->decRefCount();
136 return ret;
139 UNUSED auto const lsize = v1->size();
140 FOLLY_SDT(hhvm, hhvm_mut_concat, lsize, v2->size() + v3->size());
141 auto const ret = v1->append(v2->slice(), v3->slice());
142 if (UNLIKELY(ret != v1)) {
143 // had to realloc even though count==1
144 assertx(v1->hasExactlyOneRef());
145 v1->release();
147 return ret;
150 StringData* concat_s4(StringData* v1, StringData* v2,
151 StringData* v3, StringData* v4) {
152 if (v1->cowCheck()) {
153 auto s1 = v1->slice();
154 auto s2 = v2->slice();
155 auto s3 = v3->slice();
156 auto s4 = v4->slice();
157 FOLLY_SDT(hhvm, hhvm_cow_concat, s1.size(),
158 s2.size() + s3.size() + s4.size());
159 StringData* ret = StringData::Make(s1, s2, s3, s4);
160 // Because v1 was shared, we know this won't release it.
161 v1->decRefCount();
162 return ret;
165 UNUSED auto const lsize = v1->size();
166 FOLLY_SDT(hhvm, hhvm_mut_concat, lsize,
167 v2->size() + v3->size() + v4->size());
168 auto const ret = v1->append(v2->slice(), v3->slice(), v4->slice());
169 if (UNLIKELY(ret != v1)) {
170 // had to realloc even though count==1
171 assertx(v1->hasExactlyOneRef());
172 v1->release();
174 return ret;
177 void raiseWarning(const StringData* sd) {
178 raise_warning("%s", sd->data());
181 void raiseNotice(const StringData* sd) {
182 raise_notice("%s", sd->data());
185 void throwArrayIndexException(const ArrayData* ad, const int64_t index) {
186 throwOOBArrayKeyException(index, ad);
189 void throwArrayKeyException(const ArrayData* ad, const StringData* key) {
190 assertx(ad->isDArray() || ad->isDictType());
191 throwOOBArrayKeyException(key, ad);
194 std::string formatParamInOutMismatch(const char* fname, uint32_t index,
195 bool funcByRef) {
196 if (funcByRef) {
197 return folly::sformat(
198 "{}() expects parameter {} to be inout, but the call was "
199 "not annotated with 'inout'", fname, index + 1
201 } else {
202 return folly::sformat(
203 "{}() does not expect parameter {} to be inout, but the call was "
204 "annotated with 'inout'", fname, index + 1
209 void throwParamInOutMismatch(const Func* func, uint32_t index) {
210 SystemLib::throwInvalidArgumentExceptionObject(formatParamInOutMismatch(
211 func->fullName()->data(), index, func->isInOut(index)));
214 void throwParamInOutMismatchRange(const Func* func, unsigned firstVal,
215 uint64_t mask, uint64_t vals) {
216 for (auto i = 0; i < 64; ++i) {
217 if (mask & (1UL << i)) {
218 bool isInOut = vals & (1UL << i);
219 if (func->isInOut(firstVal + i) != isInOut) {
220 throwParamInOutMismatch(func, firstVal + i);
225 // Caller guarantees at least one parameter with inout-ness mismatch.
226 not_reached();
229 void throwInvalidUnpackArgs() {
230 SystemLib::throwInvalidArgumentExceptionObject(
231 "Only containers may be unpacked");
234 namespace {
236 std::string formatArgumentErrMsg(const Func* func, const char* amount,
237 uint32_t expected, uint32_t got) {
238 return folly::sformat(
239 "{}() expects {} {} parameter{}, {} given",
240 func->fullName()->data(),
241 amount,
242 expected,
243 expected == 1 ? "" : "s",
250 void throwMissingArgument(const Func* func, int got) {
251 auto const expected = func->numRequiredParams();
252 assertx(got < expected);
253 auto const amount = expected < func->numParams() ? "at least" : "exactly";
254 auto const errMsg = formatArgumentErrMsg(func, amount, expected, got);
255 SystemLib::throwRuntimeExceptionObject(Variant(errMsg));
258 void raiseTooManyArguments(const Func* func, int got) {
259 assertx(!func->hasVariadicCaptureParam());
261 if (!RuntimeOption::EvalWarnOnTooManyArguments && !func->isCPPBuiltin()) {
262 return;
265 auto const total = func->numNonVariadicParams();
266 assertx(got > total);
267 auto const amount = func->numRequiredParams() < total ? "at most" : "exactly";
268 auto const errMsg = formatArgumentErrMsg(func, amount, total, got);
270 if (RuntimeOption::EvalWarnOnTooManyArguments > 1 || func->isCPPBuiltin()) {
271 SystemLib::throwRuntimeExceptionObject(Variant(errMsg));
272 } else {
273 raise_warning(errMsg);
277 void raiseTooManyArgumentsPrologue(const Func* func, ArrayData* unpackArgs) {
278 SCOPE_EXIT { decRefArr(unpackArgs); };
279 if (unpackArgs->empty()) return;
280 auto const got = func->numNonVariadicParams() + unpackArgs->size();
281 raiseTooManyArguments(func, got);
284 //////////////////////////////////////////////////////////////////////
286 void raiseCoeffectsCallViolation(const Func* callee,
287 RuntimeCoeffects provided,
288 RuntimeCoeffects required) {
289 assertx(CoeffectsConfig::enabled());
290 auto const errMsg = folly::sformat(
291 "Call to {}() requires [{}] coeffects but {}() provided [{}]",
292 callee->fullNameWithClosureName(),
293 required.toString(),
294 fromLeaf([] (const ActRec* fp, Offset) {
295 return fp->func()->fullNameWithClosureName();
297 provided.toString()
300 assertx(!provided.canCall(required));
301 if (provided.canCallWithWarning(required)) {
302 raise_warning(errMsg);
303 } else {
304 SystemLib::throwBadMethodCallExceptionObject(errMsg);
308 //////////////////////////////////////////////////////////////////////
310 int64_t zero_error_level() {
311 auto& id = RequestInfo::s_requestInfo.getNoCheck()->m_reqInjectionData;
312 auto level = id.getErrorReportingLevel();
313 id.setErrorReportingLevel(0);
314 return level;
317 void restore_error_level(int64_t oldLevel) {
318 auto& id = RequestInfo::s_requestInfo.getNoCheck()->m_reqInjectionData;
319 if (id.getErrorReportingLevel() == 0) {
320 id.setErrorReportingLevel(oldLevel);
324 //////////////////////////////////////////////////////////////////////