Delete 2 unused IROPs.
[hiphop-php.git] / hphp / runtime / base / builtin-functions.cpp
blobb56db640d777e03178e895984b34faf226b7bec7
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 #include "hphp/runtime/base/builtin-functions.h"
19 #include "hphp/runtime/base/backtrace.h"
20 #include "hphp/runtime/base/code-coverage.h"
21 #include "hphp/runtime/base/container-functions.h"
22 #include "hphp/runtime/base/execution-context.h"
23 #include "hphp/runtime/base/file-util.h"
24 #include "hphp/runtime/base/request-injection-data.h"
25 #include "hphp/runtime/base/runtime-option.h"
26 #include "hphp/runtime/base/unit-cache.h"
27 #include "hphp/runtime/base/variable-serializer.h"
28 #include "hphp/runtime/base/variable-unserializer.h"
30 #include "hphp/runtime/debugger/debugger.h"
32 #include "hphp/runtime/ext/std/ext_std_closure.h"
33 #include "hphp/runtime/ext/std/ext_std_function.h"
34 #include "hphp/runtime/ext/string/ext_string.h"
36 #include "hphp/runtime/vm/event-hook.h"
37 #include "hphp/runtime/vm/func.h"
38 #include "hphp/runtime/vm/jit/translator-inline.h"
39 #include "hphp/runtime/vm/jit/translator.h"
40 #include "hphp/runtime/vm/method-lookup.h"
41 #include "hphp/runtime/vm/repo.h"
42 #include "hphp/runtime/vm/reified-generics.h"
43 #include "hphp/runtime/vm/type-constraint.h"
44 #include "hphp/runtime/vm/unit-util.h"
45 #include "hphp/runtime/vm/unit.h"
47 #include "hphp/system/systemlib.h"
49 #include "hphp/util/logger.h"
50 #include "hphp/util/process.h"
51 #include "hphp/util/string-vsnprintf.h"
52 #include "hphp/util/text-util.h"
54 #include <folly/Format.h>
56 #include <algorithm>
58 namespace HPHP {
60 using std::string;
62 ///////////////////////////////////////////////////////////////////////////////
63 // static strings
65 const StaticString
66 s_offsetExists("offsetExists"),
67 s___invoke("__invoke"),
68 s_self("self"),
69 s_parent("parent"),
70 s_static("static");
72 const StaticString s_cmpWithCollection(
73 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
74 "a collection with an integer, double, string, array, or object"
76 const StaticString s_cmpWithVec(
77 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
78 "a vec with a non-vec"
80 const StaticString s_cmpWithDict(
81 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
82 "dicts"
84 const StaticString s_cmpWithKeyset(
85 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
86 "keysets"
88 const StaticString s_cmpWithRecord(
89 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
90 "records"
92 const StaticString s_cmpWithClsMeth(
93 "Cannot use relational comparison operators (<, <=, >, >=, <=>) to compare "
94 "a clsmeth with a non-clsmeth"
96 const StaticString s_cmpWithNonRecord(
97 "Cannot compare records with non-records"
100 ///////////////////////////////////////////////////////////////////////////////
102 bool array_is_valid_callback(const Array& arr) {
103 if (!arr.isPHPArray() && !arr.isVecArray() && !arr.isDict()) return false;
104 if (arr.size() != 2 || !arr.exists(int64_t(0)) || !arr.exists(int64_t(1))) {
105 return false;
107 auto const elem0 = arr.rval(0);
108 if (!isStringType(elem0.type()) && !isObjectType(elem0.type()) &&
109 !isClassType(elem0.type())) {
110 return false;
112 auto const elem1 = arr.rval(1);
113 if (!isStringType(elem1.type()) && !isFuncType(elem1.type())) {
114 return false;
116 return true;
119 const StaticString
120 s__invoke("__invoke"),
121 s_Closure__invoke("Closure::__invoke"),
122 s_colon2("::");
124 bool is_callable(const Variant& v) {
125 CallCtx ctx;
126 ctx.invName = nullptr;
127 vm_decode_function(v, ctx, DecodeFlags::LookupOnly);
128 if (ctx.invName != nullptr) {
129 decRefStr(ctx.invName);
131 return ctx.func != nullptr && !ctx.func->isAbstract();
134 bool is_callable(const Variant& v, bool syntax_only, Variant* name) {
135 bool ret = true;
136 if (LIKELY(!syntax_only)) {
137 ret = is_callable(v);
138 if (LIKELY(!name)) return ret;
141 auto const tv_func = v.asTypedValue();
142 if (isFuncType(tv_func->m_type)) {
143 auto func_name = tv_func->m_data.pfunc->fullName();
144 if (name) *name = Variant{func_name, Variant::PersistentStrInit{}};
145 return true;
148 if (isClsMethType(tv_func->m_type)) {
149 auto const clsmeth = tv_func->m_data.pclsmeth;
150 if (name) {
151 *name = concat3(
152 clsmeth->getCls()->nameStr(), "::", clsmeth->getFunc()->nameStr());
154 return true;
157 if (isStringType(tv_func->m_type)) {
158 if (name) *name = v;
159 return ret;
162 if (isArrayType(tv_func->m_type) ||
163 isVecType(tv_func->m_type) ||
164 isDictType(tv_func->m_type)) {
165 auto const arr = Array(tv_func->m_data.parr);
166 auto const clsname = arr.rval(int64_t(0));
167 auto const mthname = arr.rval(int64_t(1));
169 if (arr.size() != 2 ||
170 clsname.is_dummy() ||
171 (!isStringType(mthname.type()) && !isFuncType(mthname.type()))) {
172 if (name) *name = array_string;
173 return false;
176 StringData* clsString = nullptr;
177 if (isObjectType(clsname.type())) {
178 clsString = clsname.val().pobj->getClassName().get();
179 } else if (isStringType(clsname.type())) {
180 clsString = clsname.val().pstr;
181 } else if (isClassType(clsname.type())) {
182 clsString = const_cast<StringData*>(clsname.val().pclass->name());
183 } else {
184 if (name) *name = array_string;
185 return false;
188 if (isFuncType(mthname.type())) {
189 if (name) {
190 *name = Variant{mthname.val().pfunc->fullName(),
191 Variant::PersistentStrInit{}};
193 return true;
196 if (name) {
197 *name = concat3(String{clsString},
198 s_colon2,
199 String{mthname.val().pstr});
201 return ret;
204 if (tv_func->m_type == KindOfObject) {
205 ObjectData *d = tv_func->m_data.pobj;
206 const Func* invoke = d->getVMClass()->lookupMethod(s__invoke.get());
207 if (name) {
208 if (d->instanceof(c_Closure::classof())) {
209 // Hack to stop the mangled name from showing up
210 *name = s_Closure__invoke;
211 } else {
212 *name = d->getClassName().asString() + "::__invoke";
215 return invoke != nullptr;
218 return false;
221 namespace {
222 Class* vm_decode_class_from_name(
223 const String& clsName,
224 const String& funcName,
225 bool nameContainsClass,
226 ActRec* ar,
227 bool& forwarding,
228 Class* ctx,
229 DecodeFlags flags) {
230 Class* cls = nullptr;
231 if (clsName.get()->isame(s_self.get())) {
232 if (ctx) {
233 cls = ctx;
235 if (!nameContainsClass) {
236 forwarding = true;
238 } else if (clsName.get()->isame(s_parent.get())) {
239 if (ctx && ctx->parent()) {
240 cls = ctx->parent();
242 if (!nameContainsClass) {
243 forwarding = true;
245 } else if (clsName.get()->isame(s_static.get())) {
246 if (ar && ar->func()->cls()) {
247 if (ar->hasThis()) {
248 cls = ar->getThis()->getVMClass();
249 } else {
250 cls = ar->getClass();
252 if (flags != DecodeFlags::NoWarn && cls) {
253 if (RuntimeOption::EvalWarnOnSkipFrameLookup) {
254 raise_warning(
255 "vm_decode_function() used to decode a LSB class "
256 "method on %s",
257 cls->name()->data()
262 } else {
263 if (flags == DecodeFlags::Warn && nameContainsClass) {
264 String nameClass = funcName.substr(0, funcName.find("::"));
265 if (nameClass.get()->isame(s_self.get()) ||
266 nameClass.get()->isame(s_static.get())) {
267 raise_warning("behavior of call_user_func(array('%s', '%s')) "
268 "is undefined", clsName.data(), funcName.data());
271 cls = Unit::loadClass(clsName.get());
273 return cls;
276 const Func* vm_decode_func_from_name(
277 const String& funcName,
278 ActRec* ar,
279 bool forwarding,
280 ObjectData*& this_,
281 Class*& cls,
282 Class* ctx,
283 Class* cc,
284 StringData*& invName,
285 DecodeFlags flags) {
286 CallType lookupType = this_ ? CallType::ObjMethod : CallType::ClsMethod;
287 auto f = lookupMethodCtx(cc, funcName.get(), ctx, lookupType);
288 if (f && (f->attrs() & AttrStatic)) {
289 // If we found a method and its static, null out this_
290 this_ = nullptr;
291 } else {
292 if (!this_ && ar && ar->func()->cls() && ar->hasThis()) {
293 // If we did not find a static method AND this_ is null AND there is a
294 // frame ar, check if the current instance from ar is compatible
295 auto const obj = ar->getThis();
296 if (obj->instanceof(cls)) {
297 this_ = obj;
298 cls = obj->getVMClass();
300 if (flags != DecodeFlags::NoWarn && this_) {
301 if (RuntimeOption::EvalWarnOnSkipFrameLookup) {
302 raise_warning(
303 "vm_decode_function() used to decode a method on $this, an "
304 "instance of %s, from the caller, %s",
305 cls->name()->data(),
306 ar->func()->fullName()->data()
311 if (!f) {
312 if (this_) {
313 // If this_ is non-null AND we could not find a method, try
314 // looking up __call in cls's method table
315 f = cls->lookupMethod(s___call.get());
316 assertx(!f || !(f->attrs() & AttrStatic));
318 if (!f && lookupType == CallType::ClsMethod) {
319 this_ = nullptr;
321 if (f && (cc == cls || cc->lookupMethod(f->name()))) {
322 // We found __call!
323 // Stash the original name into invName.
324 invName = funcName.get();
325 invName->incRefCount();
326 } else {
327 // Bail out if we couldn't find the method or __call
328 if (flags == DecodeFlags::Warn) {
329 raise_invalid_argument_warning("function: method '%s' not found",
330 funcName.data());
332 return nullptr;
337 if (!this_ && !f->isStaticInPrologue()) {
338 if (flags != DecodeFlags::LookupOnly) throw_missing_this(f);
341 assertx(f && f->preClass());
342 // If this_ is non-NULL, then this_ is the current instance and cls is
343 // the class of the current instance.
344 assertx(!this_ || this_->getVMClass() == cls);
345 // If we are doing a forwarding call and this_ is null, set cls
346 // appropriately to propagate the current late bound class.
347 if (!this_ && forwarding && ar && ar->func()->cls()) {
348 auto const fwdCls = ar->hasThis() ?
349 ar->getThis()->getVMClass() : ar->getClass();
351 // Only forward the current late bound class if it is the same or
352 // a descendent of cls
353 if (fwdCls->classof(cls)) {
354 cls = fwdCls;
357 if (flags != DecodeFlags::NoWarn && fwdCls) {
358 if (RuntimeOption::EvalWarnOnSkipFrameLookup) {
359 raise_warning(
360 "vm_decode_function() forwarded the calling context, %s",
361 fwdCls->name()->data()
367 if (flags != DecodeFlags::NoWarn && !f->isPublic()) {
368 if (RuntimeOption::EvalWarnOnSkipFrameLookup) {
369 raise_warning(
370 "vm_decode_function() used to decode a %s method: %s",
371 f->attrs() & AttrPrivate ? "private" : "protected",
372 f->fullName()->data()
376 return f;
380 std::pair<Class*, Func*> decode_for_clsmeth(
381 const String& clsName,
382 const String& funcName,
383 ActRec* ar,
384 StringData*& invName,
385 DecodeFlags flags /* = DecodeFlags::Warn */) {
386 int pos = funcName.find("::");
387 bool nameContainsClass =
388 (pos != 0 && pos != String::npos && pos + 2 < funcName.size());
389 bool forwarding = false;
390 HPHP::Class* ctx = nullptr;
391 if (ar) ctx = arGetContextClass(ar);
392 auto cls = vm_decode_class_from_name(
393 clsName, funcName, nameContainsClass, ar, forwarding, ctx, flags);
394 if (!cls) {
395 if (flags == DecodeFlags::Warn) {
396 raise_invalid_argument_warning("function: class not found");
398 return std::make_pair(nullptr, nullptr);
401 // For clsmeth, we want to return the class user gave,
402 // not the class where func is associated with
403 auto c = cls;
404 ObjectData* thiz = nullptr;
405 auto const func = vm_decode_func_from_name(
406 funcName, ar, forwarding, thiz, c, ctx, c, invName, flags);
407 return std::make_pair(cls, const_cast<Func*>(func));
410 const HPHP::Func*
411 vm_decode_function(const_variant_ref function,
412 ActRec* ar,
413 ObjectData*& this_,
414 HPHP::Class*& cls,
415 StringData*& invName,
416 bool& dynamic,
417 DecodeFlags flags /* = DecodeFlags::Warn */,
418 bool genericsAlreadyGiven /* = false */) {
419 bool forwarding = false;
420 invName = nullptr;
421 dynamic = true;
423 if (function.isFunc()) {
424 dynamic = false;
425 this_ = nullptr;
426 cls = nullptr;
427 return function.toFuncVal();
430 if (function.isClsMeth()) {
431 dynamic = false;
432 this_ = nullptr;
433 auto const clsmeth = function.toClsMethVal();
434 cls = clsmeth->getCls();
435 return clsmeth->getFunc();
438 if (function.isString() || function.isArray()) {
439 HPHP::Class* ctx = nullptr;
440 if (ar) ctx = arGetContextClass(ar);
441 // Decode the 'function' parameter into this_, cls, name, pos, and
442 // nameContainsClass.
443 this_ = nullptr;
444 cls = nullptr;
445 String name;
446 int pos = String::npos;
447 bool nameContainsClass = false;
448 if (function.isString()) {
449 // If 'function' is a string we simply assign it to name and
450 // leave this_ and cls set to NULL.
451 name = function.toString();
452 pos = name.find("::");
453 nameContainsClass =
454 (pos != 0 && pos != String::npos && pos + 2 < name.size());
455 } else {
456 // If 'function' is an array with exactly two indices 0 and 1, we
457 // assign the value at index 1 to name and we use the value at index
458 // 0 to populate cls (if the value is a string) or this_ and cls (if
459 // the value is an object).
460 assertx(function.isArray());
461 Array arr = function.toArray();
462 if (!array_is_valid_callback(arr)) {
463 if (flags == DecodeFlags::Warn) {
464 raise_invalid_argument_warning("function: not a valid callback array");
466 return nullptr;
469 Variant elem0 = arr[0];
470 Variant elem1 = arr[1];
471 if (elem1.isFunc()) {
472 if (elem0.isObject()) {
473 this_ = elem0.getObjectData();
474 cls = this_->getVMClass();
475 } else if (elem0.isClass()) {
476 cls = elem0.toClassVal();
477 } else {
478 raise_error("calling an ill-formed array without resolved "
479 "object/class pointer");
480 return nullptr;
482 return elem1.toFuncVal();
485 assertx(elem1.isString());
486 name = elem1.toString();
487 pos = name.find("::");
488 nameContainsClass =
489 (pos != 0 && pos != String::npos && pos + 2 < name.size());
490 if (elem0.isString()) {
491 cls = vm_decode_class_from_name(
492 elem0.toString(), name, nameContainsClass, ar, forwarding, ctx,
493 flags);
494 if (!cls) {
495 if (flags == DecodeFlags::Warn) {
496 raise_invalid_argument_warning("function: class not found");
498 return nullptr;
500 } else if (elem0.isClass()) {
501 cls = elem0.toClassVal();
502 } else {
503 assertx(elem0.isObject());
504 this_ = elem0.getObjectData();
505 cls = this_->getVMClass();
509 HPHP::Class* cc = cls;
510 if (nameContainsClass) {
511 String c = name.substr(0, pos);
512 name = name.substr(pos + 2);
513 if (c.get()->isame(s_self.get())) {
514 if (cls) {
515 cc = cls;
516 } else if (ctx) {
517 cc = ctx;
519 if (!this_) {
520 forwarding = true;
522 } else if (c.get()->isame(s_parent.get())) {
523 if (cls) {
524 cc = cls->parent();
525 } else if (ctx && ctx->parent()) {
526 cc = ctx->parent();
528 if (!this_) {
529 forwarding = true;
531 } else if (c.get()->isame(s_static.get())) {
532 if (ar && ar->func()->cls()) {
533 if (ar->hasThis()) {
534 cc = ar->getThis()->getVMClass();
535 } else {
536 cc = ar->getClass();
539 if (flags != DecodeFlags::NoWarn && cc) {
540 if (RuntimeOption::EvalWarnOnSkipFrameLookup) {
541 raise_warning(
542 "vm_decode_function() used to decode a LSB class "
543 "method on %s",
544 cc->name()->data()
548 } else {
549 cc = Unit::loadClass(c.get());
551 if (!cc) {
552 if (flags == DecodeFlags::Warn) {
553 raise_invalid_argument_warning("function: class not found");
555 return nullptr;
557 if (cls) {
558 if (!cls->classof(cc)) {
559 if (flags == DecodeFlags::Warn) {
560 raise_warning("call_user_func expects parameter 1 to be a valid "
561 "callback, class '%s' is not a subclass of '%s'",
562 cls->preClass()->name()->data(),
563 cc->preClass()->name()->data());
565 return nullptr;
568 // If there is not a current instance, cc trumps cls.
569 if (!this_) {
570 cls = cc;
574 if (!cls) {
575 HPHP::Func* f = HPHP::Unit::loadFunc(name.get());
576 if (!f) {
577 if (flags == DecodeFlags::Warn) {
578 raise_invalid_argument_warning("function: method '%s' not found",
579 name.data());
581 return nullptr;
583 assertx(f && f->preClass() == nullptr);
584 if (f->hasReifiedGenerics() && !genericsAlreadyGiven) {
585 raise_invalid_argument_warning("You may not call the reified function '%s' "
586 "without reified arguments",
587 f->fullName()->data());
588 return nullptr;
590 return f;
592 assertx(cls);
593 return vm_decode_func_from_name(
594 name, ar, forwarding, this_, cls, ctx, cc, invName, flags);
596 if (function.isObject()) {
597 this_ = function.asCObjRef().get();
598 cls = nullptr;
599 dynamic = false;
600 const HPHP::Func *f = this_->getVMClass()->lookupMethod(s___invoke.get());
601 if (f != nullptr && f->isStaticInPrologue()) {
602 // If __invoke is static, invoke it as such
603 cls = this_->getVMClass();
604 this_ = nullptr;
606 if (flags == DecodeFlags::Warn && f == nullptr) {
607 raise_warning("call_user_func() expects parameter 1 to be a valid "
608 "callback, object of class %s given (no __invoke "
609 "method found)", this_->getVMClass()->name()->data());
611 return f;
613 if (flags == DecodeFlags::Warn) {
614 raise_invalid_argument_warning("function: not string, closure, or array");
616 return nullptr;
619 Variant vm_call_user_func(const_variant_ref function, const Variant& params,
620 bool checkRef /* = false */,
621 bool allowDynCallNoPointer /* = false */) {
622 CallCtx ctx;
623 vm_decode_function(function, ctx);
624 if (ctx.func == nullptr || (!isContainer(params) && !params.isNull())) {
625 return uninit_null();
627 auto ret = Variant::attach(
628 g_context->invokeFunc(ctx.func, params, ctx.this_, ctx.cls,
629 ctx.invName, ctx.dynamic, checkRef,
630 allowDynCallNoPointer)
632 return ret;
635 Variant
636 invoke(const String& function, const Variant& params,
637 bool allowDynCallNoPointer /* = false */) {
638 Func* func = Unit::loadFunc(function.get());
639 if (func && (isContainer(params) || params.isNull())) {
640 auto ret = Variant::attach(
641 g_context->invokeFunc(func, params, nullptr, nullptr, nullptr, true,
642 false, allowDynCallNoPointer)
645 return ret;
647 throw ExtendedException("(1) call the function without enough arguments OR "
648 "(2) Unable to find function \"%s\" OR "
649 "(3) function was not in invoke table OR "
650 "(4) function was renamed to something else.",
651 function.c_str());
654 Variant invoke_static_method(const String& s, const String& method,
655 const Variant& params, bool fatal /* = true */) {
656 HPHP::Class* class_ = Unit::lookupClass(s.get());
657 if (class_ == nullptr) {
658 o_invoke_failed(s.data(), method.data(), fatal);
659 return uninit_null();
661 const HPHP::Func* f = class_->lookupMethod(method.get());
662 if (f == nullptr || !f->isStaticInPrologue() ||
663 (!isContainer(params) && !params.isNull())) {
664 o_invoke_failed(s.data(), method.data(), fatal);
665 return uninit_null();
667 auto ret = Variant::attach(
668 g_context->invokeFunc(f, params, nullptr, class_)
670 return ret;
673 Variant o_invoke_failed(const char *cls, const char *meth,
674 bool fatal /* = true */) {
675 if (fatal) {
676 string msg = "Unknown method ";
677 msg += cls;
678 msg += "::";
679 msg += meth;
680 raise_fatal_error(msg.c_str());
681 } else {
682 raise_warning("call_user_func to non-existent method %s::%s", cls, meth);
683 return false;
687 void throw_has_this_need_static(const Func* f) {
688 auto const msg = folly::sformat(
689 "Static method {}() cannot be called on instance",
690 f->fullName()->data()
692 SystemLib::throwBadMethodCallExceptionObject(msg);
695 void throw_missing_this(const Func* f) {
696 auto const msg = folly::sformat(
697 "Non-static method {}() cannot be called statically",
698 f->fullName()->data()
700 SystemLib::throwBadMethodCallExceptionObject(msg);
703 void NEVER_INLINE throw_invalid_property_name(const String& name) {
704 if (!name.size()) {
705 raise_error("Cannot access empty property");
707 raise_error("Cannot access property started with '\\0'");
710 void throw_instance_method_fatal(const char *name) {
711 raise_error("Non-static method %s() cannot be called statically", name);
714 void throw_call_reified_func_without_generics(const Func* f) {
715 auto const msg = folly::sformat(
716 "Cannot call the reified function '{}' without the reified generics",
717 f->fullName()->data()
719 SystemLib::throwBadMethodCallExceptionObject(msg);
722 void throw_iterator_not_valid() {
723 SystemLib::throwInvalidOperationExceptionObject(
724 "Iterator is not valid");
727 void throw_collection_property_exception() {
728 SystemLib::throwInvalidOperationExceptionObject(
729 "Cannot access a property on a collection");
732 void throw_invalid_collection_parameter() {
733 SystemLib::throwInvalidArgumentExceptionObject(
734 "Parameter must be an array or an instance of Traversable");
737 void throw_invalid_operation_exception(StringData* str) {
738 SystemLib::throwInvalidOperationExceptionObject(Variant{str});
741 void throw_division_by_zero_exception() {
742 SystemLib::throwDivisionByZeroExceptionObject();
745 void throw_collection_compare_exception() {
746 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithCollection);
749 void throw_vec_compare_exception() {
750 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithVec);
753 void throw_dict_compare_exception() {
754 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithDict);
757 void throw_record_compare_exception() {
758 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithRecord);
761 void throw_rec_non_rec_compare_exception() {
762 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithNonRecord);
765 void throw_keyset_compare_exception() {
766 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithKeyset);
769 void throw_clsmeth_compare_exception() {
770 SystemLib::throwInvalidOperationExceptionObject(s_cmpWithClsMeth);
773 void throw_param_is_not_container() {
774 static const string msg("Parameter must be an array or collection");
775 SystemLib::throwInvalidArgumentExceptionObject(msg);
778 void throw_invalid_inout_base() {
779 SystemLib::throwInvalidArgumentExceptionObject(
780 "Parameters marked inout must be contained in locals, vecs, dicts, "
781 "keysets, and arrays"
785 void throw_cannot_modify_immutable_object(const char* className) {
786 auto msg = folly::sformat(
787 "Cannot modify immutable object of type {}",
788 className
790 SystemLib::throwInvalidOperationExceptionObject(msg);
793 void throw_cannot_modify_const_object(const char* className) {
794 auto msg = folly::sformat(
795 "Cannot modify const object of type {}",
796 className
798 SystemLib::throwInvalidOperationExceptionObject(msg);
801 void throw_object_forbids_dynamic_props(const char* className) {
802 auto msg = folly::sformat(
803 "Class {} does not allow use of dynamic (non-declared) properties",
804 className
806 SystemLib::throwInvalidOperationExceptionObject(msg);
809 void throw_cannot_modify_const_prop(const char* className,
810 const char* propName)
812 auto msg = folly::sformat(
813 "Cannot modify const property {} of class {} after construction",
814 propName, className
816 SystemLib::throwInvalidOperationExceptionObject(msg);
819 void throw_cannot_modify_static_const_prop(const char* className,
820 const char* propName)
822 auto msg = folly::sformat(
823 "Cannot modify static const property {} of class {}.",
824 propName, className
826 SystemLib::throwInvalidOperationExceptionObject(msg);
829 NEVER_INLINE
830 void throw_late_init_prop(const Class* cls,
831 const StringData* propName,
832 bool isSProp) {
833 SystemLib::throwInvalidOperationExceptionObject(
834 folly::sformat(
835 "Accessing <<__LateInit>> {} '{}::{}' before initialization",
836 isSProp ? "static property" : "property",
837 cls->name(),
838 propName
843 NEVER_INLINE
844 void throw_parameter_wrong_type(TypedValue tv,
845 const Func* callee,
846 unsigned int arg_num,
847 DataType expected_type) {
848 auto msg = param_type_error_message(
849 callee->name()->data(), arg_num, expected_type,
850 type(tv));
852 if (RuntimeOption::PHP7_EngineExceptions) {
853 SystemLib::throwTypeErrorObject(msg);
855 SystemLib::throwRuntimeExceptionObject(msg);
858 void check_collection_cast_to_array() {
859 if (RuntimeOption::WarnOnCollectionToArray) {
860 raise_warning("Casting a collection to an array is an expensive operation "
861 "and should be avoided where possible. To convert a "
862 "collection to an array without raising a warning, use the "
863 "toArray() method.");
867 Object create_object_only(const String& s) {
868 return Object::attach(g_context->createObjectOnly(s.get()));
871 Object init_object(const String& s, const Array& params, ObjectData* o) {
872 return Object{g_context->initObject(s.get(), params, o)};
875 Object
876 create_object(const String& s, const Array& params, bool init /* = true */) {
877 return Object::attach(g_context->createObject(s.get(), params, init));
880 void throw_object(const Object& e) {
881 throw req::root<Object>(e);
884 #if ((__GNUC__ != 4) || (__GNUC_MINOR__ != 8))
885 void throw_object(Object&& e) {
886 throw req::root<Object>(std::move(e));
888 #endif
891 * This function is used when another thread is segfaulting---we just
892 * want to wait forever to give it a chance to write a stacktrace file
893 * (and maybe a core file).
895 void pause_forever() {
896 for (;;) sleep(300);
899 bool is_constructor_name(const char* fn) {
900 auto len = strlen(fn);
901 const char construct[] = "__construct";
902 auto clen = sizeof(construct) - 1;
904 if (len >= clen && !strcasecmp(fn + len - clen, construct)) {
905 if (len == clen || (len > clen + 2 &&
906 fn[len - clen - 1] == ':' &&
907 fn[len - clen - 2] == ':')) {
908 return true;
911 return false;
914 void throw_missing_arguments_nr(const char *fn, int expected, int got) {
915 SystemLib::throwRuntimeExceptionObject(folly::sformat(
916 "{}() expects exactly {} parameter{}, {} given",
918 expected,
919 expected == 1 ? "" : "s",
924 void raise_bad_type_warning(const char *fmt, ...) {
925 va_list ap;
926 va_start(ap, fmt);
927 std::string msg;
928 string_vsnprintf(msg, fmt, ap);
929 va_end(ap);
931 raise_warning("Invalid operand type was used: %s", msg.c_str());
934 void raise_expected_array_warning(const char* fn /*=nullptr*/) {
935 if (!fn) {
936 if (auto ar = g_context->getStackFrame()) {
937 fn = ar->m_func->name()->data();
938 } else {
939 fn = "(unknown)";
942 raise_bad_type_warning("%s expects array(s)", fn);
945 void raise_expected_array_or_collection_warning(const char* fn /*=nullptr*/) {
946 if (!fn) {
947 if (auto ar = g_context->getStackFrame()) {
948 fn = ar->m_func->name()->data();
949 } else {
950 fn = "(unknown)";
953 raise_bad_type_warning("%s expects array(s) or collection(s)", fn);
956 void raise_invalid_argument_warning(const char *fmt, ...) {
957 va_list ap;
958 va_start(ap, fmt);
959 string msg;
960 string_vsnprintf(msg, fmt, ap);
961 va_end(ap);
962 raise_warning("Invalid argument: %s", msg.c_str());
965 Variant throw_fatal_unset_static_property(const char *s, const char *prop) {
966 raise_error("Attempt to unset static property %s::$%s", s, prop);
967 return uninit_null();
970 Variant unserialize_ex(const char* str, int len,
971 VariableUnserializer::Type type,
972 const Array& options /* = null_array */) {
973 if (str == nullptr || len <= 0) {
974 return false;
977 VariableUnserializer vu(str, len, type, true, options);
978 Variant v;
979 try {
980 v = vu.unserialize();
981 } catch (FatalErrorException& e) {
982 throw;
983 } catch (InvalidAllowedClassesException& e) {
984 raise_warning(
985 "unserialize(): allowed_classes option should be array or boolean"
987 return false;
988 } catch (Exception& e) {
989 raise_notice("Unable to unserialize: [%.*s]. %s.",
990 std::min(len, 1000), str, e.getMessage().c_str());
991 return false;
993 return v;
996 Variant unserialize_ex(const String& str,
997 VariableUnserializer::Type type,
998 const Array& options /* = null_array */) {
999 return unserialize_ex(str.data(), str.size(), type, options);
1002 String concat3(const String& s1, const String& s2, const String& s3) {
1003 auto r1 = s1.slice();
1004 auto r2 = s2.slice();
1005 auto r3 = s3.slice();
1006 auto len = r1.size() + r2.size() + r3.size();
1007 auto str = String::attach(StringData::Make(len));
1008 auto const r = str.mutableData();
1009 memcpy(r, r1.data(), r1.size());
1010 memcpy(r + r1.size(), r2.data(), r2.size());
1011 memcpy(r + r1.size() + r2.size(), r3.data(), r3.size());
1012 str.setSize(len);
1013 return str;
1016 String concat4(const String& s1, const String& s2, const String& s3,
1017 const String& s4) {
1018 auto r1 = s1.slice();
1019 auto r2 = s2.slice();
1020 auto r3 = s3.slice();
1021 auto r4 = s4.slice();
1022 auto len = r1.size() + r2.size() + r3.size() + r4.size();
1023 auto str = String::attach(StringData::Make(len));
1024 auto const r = str.mutableData();
1025 memcpy(r, r1.data(), r1.size());
1026 memcpy(r + r1.size(), r2.data(), r2.size());
1027 memcpy(r + r1.size() + r2.size(), r3.data(), r3.size());
1028 memcpy(r + r1.size() + r2.size() + r3.size(), r4.data(), r4.size());
1029 str.setSize(len);
1030 return str;
1033 static bool invoke_file_impl(Variant& res, const String& path, bool once,
1034 const char *currentDir,
1035 bool callByHPHPInvoke) {
1036 bool initial;
1037 auto const u = lookupUnit(path.get(), currentDir, &initial,
1038 Native::s_noNativeFuncs, false);
1039 if (u == nullptr) return false;
1040 if (!once || initial) {
1041 *res.asTypedValue() = g_context->invokeUnit(u, callByHPHPInvoke);
1043 return true;
1046 static NEVER_INLINE Variant throw_missing_file(const char* file) {
1047 throw PhpFileDoesNotExistException(file);
1050 static Variant invoke_file(const String& s,
1051 bool once,
1052 const char *currentDir,
1053 bool callByHPHPInvoke) {
1054 Variant r;
1055 if (invoke_file_impl(r, s, once, currentDir, callByHPHPInvoke)) {
1056 return r;
1058 return throw_missing_file(s.c_str());
1061 Variant include_impl_invoke(const String& file, bool once,
1062 const char *currentDir, bool callByHPHPInvoke) {
1063 if (FileUtil::isAbsolutePath(file.toCppString())) {
1064 if (RuntimeOption::SandboxMode || !RuntimeOption::AlwaysUseRelativePath) {
1065 try {
1066 return invoke_file(file, once, currentDir, callByHPHPInvoke);
1067 } catch(PhpFileDoesNotExistException& e) {}
1070 try {
1071 String rel_path(FileUtil::relativePath(RuntimeOption::SourceRoot,
1072 string(file.data())));
1074 // Don't try/catch - We want the exception to be passed along
1075 return invoke_file(rel_path, once, currentDir, callByHPHPInvoke);
1076 } catch(PhpFileDoesNotExistException& e) {
1077 throw PhpFileDoesNotExistException(file.c_str());
1079 } else {
1080 // Don't try/catch - We want the exception to be passed along
1081 return invoke_file(file, once, currentDir, callByHPHPInvoke);
1086 * Used by include_impl. resolve_include() needs some way of checking the
1087 * existence of a file path, which for hphpc means attempting to invoke it.
1088 * This struct carries some context information needed for the invocation, as
1089 * well as a place for the return value of invoking the file.
1091 struct IncludeImplInvokeContext {
1092 bool once;
1093 const char* currentDir;
1095 Variant returnValue;
1098 static bool include_impl_invoke_context(const String& file, void* ctx) {
1099 struct IncludeImplInvokeContext* context = (IncludeImplInvokeContext*)ctx;
1100 bool invoked_file = false;
1101 try {
1102 context->returnValue = include_impl_invoke(file, context->once,
1103 context->currentDir);
1104 invoked_file = true;
1105 } catch (PhpFileDoesNotExistException& e) {
1106 context->returnValue = false;
1108 return invoked_file;
1112 * tryFile is a pointer to a function that resolve_include() will use to
1113 * determine if a path references a real file. ctx is a pointer to some context
1114 * information that will be passed through to tryFile. (It's a hacky closure)
1116 String resolve_include(const String& file, const char* currentDir,
1117 bool (*tryFile)(const String& file, void*), void* ctx) {
1118 const char* c_file = file.data();
1120 auto const getCwd = [] () -> String {
1121 if (LIKELY(!g_context.isNull())) return g_context->getCwd();
1122 return String(Process::CurrentWorkingDirectory, CopyString);
1125 if (!File::IsPlainFilePath(file)) {
1126 // URIs don't have an include path
1127 if (tryFile(file, ctx)) {
1128 return file;
1131 } else if (FileUtil::isAbsolutePath(file.toCppString())) {
1132 String can_path = FileUtil::canonicalize(file);
1134 if (tryFile(can_path, ctx)) {
1135 return can_path;
1138 } else if ((c_file[0] == '.' && (c_file[1] == '/' || (
1139 c_file[1] == '.' && c_file[2] == '/')))) {
1141 String path(String(getCwd() + "/" + file));
1142 String can_path = FileUtil::canonicalize(path);
1144 if (tryFile(can_path, ctx)) {
1145 return can_path;
1148 } else {
1149 if (!RequestInfo::s_requestInfo.isNull()) {
1150 auto const& includePaths = RID().getIncludePaths();
1152 for (auto const& includePath : includePaths) {
1153 String path("");
1154 auto const is_stream_wrapper =
1155 includePath.find("://") != std::string::npos;
1157 if (!is_stream_wrapper && !FileUtil::isAbsolutePath(includePath)) {
1158 path += (getCwd() + "/");
1161 path += includePath;
1163 if (path[path.size() - 1] != '/') {
1164 path += "/";
1167 path += file;
1169 String can_path;
1170 if (!is_stream_wrapper) {
1171 can_path = FileUtil::canonicalize(path);
1172 } else {
1173 can_path = String(path.c_str());
1176 if (tryFile(can_path, ctx)) {
1177 return can_path;
1182 if (FileUtil::isAbsolutePath(currentDir)) {
1183 String path(currentDir);
1184 path += "/";
1185 path += file;
1186 String can_path = FileUtil::canonicalize(path);
1188 if (tryFile(can_path, ctx)) {
1189 return can_path;
1191 } else {
1192 String path(getCwd() + "/" + currentDir + file);
1193 String can_path = FileUtil::canonicalize(path);
1195 if (tryFile(can_path, ctx)) {
1196 return can_path;
1201 return String();
1204 static Variant include_impl(const String& file, bool once,
1205 const char *currentDir, bool required,
1206 bool raiseNotice) {
1207 struct IncludeImplInvokeContext ctx = {once, currentDir};
1208 String can_path = resolve_include(file, currentDir,
1209 include_impl_invoke_context, (void*)&ctx);
1211 if (can_path.isNull()) {
1212 // Failure
1213 if (raiseNotice) {
1214 raise_notice("Tried to invoke %s but file not found.", file.data());
1216 if (required) {
1217 String ms = "Required file that does not exist: ";
1218 ms += file;
1219 raise_fatal_error(ms.data());
1221 return false;
1224 return ctx.returnValue;
1227 Variant require(const String& file,
1228 bool once,
1229 const char* currentDir,
1230 bool raiseNotice) {
1231 return include_impl(file, once, currentDir, true, raiseNotice);
1234 bool function_exists(const String& function_name) {
1235 auto f = Unit::lookupFunc(function_name.get());
1236 return (f != nullptr) &&
1237 (f->arFuncPtr() != Native::unimplementedWrapper);
1240 ///////////////////////////////////////////////////////////////////////////////
1241 // debugger and code coverage instrumentation
1243 void throw_exception(const Object& e) {
1244 if (!e.instanceof(SystemLib::s_ThrowableClass)) {
1245 raise_error("Exceptions must implement the Throwable interface.");
1247 DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionThrownHook(e.get()));
1248 throw req::root<Object>(e);
1251 ///////////////////////////////////////////////////////////////////////////////