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 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_RUNTIME_VM_NATIVE_H
17 #define incl_HPHP_RUNTIME_VM_NATIVE_H
19 #include "hphp/runtime/base/type-string.h"
20 #include "hphp/runtime/base/typed-value.h"
21 #include "hphp/runtime/base/tv-mutate.h"
22 #include "hphp/runtime/base/tv-variant.h"
24 #include "hphp/runtime/vm/func.h"
25 #include "hphp/runtime/vm/class-meth-data-ref.h"
26 #include "hphp/util/abi-cxx.h"
28 #include <type_traits>
38 /* Macros related to declaring/registering internal implementations
39 * of <<__Native>> global functions.
41 * Declare a function in ext_foo.h using:
42 * ReturnType HHVM_FUNCTION(functionName, parameterList...)
44 * int64_t HHVM_FUNCTION(sum, int64_t a, int64_t b) {
48 * Then register it from your Extension's moduleLoad() hook:
49 * void moduleLoad(const IniSetting::Map& ini, Hdf config) override {
53 * To finish exposing it to PHP, add an entry to Systemlib
54 * using matching hack typehints:
57 * function sum(int $a, int $b): int;
59 ****************************************************************************
61 * If, for whatever reason, the standard declaration doesn't work,
62 * you may declare the function directly as:
63 * ReturnType localSymbolName(parameterList...)
65 * int64_t my_sum_function(int64_t a, int64_t b) {
69 * In which case you will need to use a different macro in moduleLoad()
70 * virtual moduleLoad(const IniSetting::Map& ini, Hdf config) {
71 * HHVM_NAME_FE(sum, my_sum_function)
73 * Or an explicit call to registerNativeFunc()
74 * static const StaticString s_sum("sum");
75 * virtual moduleLoad(const IniSetting::Map& ini, Hdf config) {
76 * Native::registerNativeFunc(s_sum, (void*)my_sum_function);
79 ****************************************************************************
81 * The macros HHVM_FALIAS, HHVM_MALIAS, and HHVM_STATIC_MALIAS allow
82 * giving different names to the C++ implementation and the exported
83 * C++ function. This ca be useful for creating multiple names for one
84 * function or for registering functions that live in a namespace.
87 #define HHVM_FN(fn) f_ ## fn
88 #define HHVM_FUNCTION(fn, ...) \
89 HHVM_FN(fn)(__VA_ARGS__)
90 #define HHVM_NAMED_FE_STR(fn, fimpl, functable) \
92 String name{makeStaticString(fn)}; \
93 registerExtensionFunction(name); \
94 Native::registerNativeFunc(functable, name, fimpl); \
96 #define HHVM_NAMED_FE(fn, fimpl)\
97 HHVM_NAMED_FE_STR(#fn, fimpl, nativeFuncs())
99 HHVM_NAMED_FE_STR(#fn, HHVM_FN(fn), nativeFuncs())
100 #define HHVM_FALIAS(fn, falias)\
101 HHVM_NAMED_FE_STR(#fn, HHVM_FN(falias), nativeFuncs())
103 /* Macros related to declaring/registering internal implementations
104 * of <<__Native>> class instance methods.
106 * See the definition of function macros above for general explanation.
107 * These macros only differ in the following ways:
108 * - They accept a classname in addition to the function name
109 * - The registered name of the function is "ClassName->FunctionName"
110 * - Prototypes include a prepended ObjectData* const parameter (named this_)
112 #define HHVM_MN(cn,fn) c_ ## cn ## _ni_ ## fn
113 #define HHVM_METHOD(cn, fn, ...) \
114 HHVM_MN(cn,fn)(ObjectData* const this_, ##__VA_ARGS__)
115 #define HHVM_NAMED_ME(cn,fn,mimpl) \
116 Native::registerNativeFunc(nativeFuncs(), #cn "->" #fn, mimpl)
117 #define HHVM_ME(cn,fn) HHVM_NAMED_ME(cn,fn, HHVM_MN(cn,fn))
118 #define HHVM_MALIAS(cn,fn,calias,falias) \
119 HHVM_NAMED_ME(cn,fn,HHVM_MN(calias,falias))
121 /* special case when we're registering info for a method defined in
122 * s_systemNativeFuncs, instead of the current Extension
124 #define HHVM_SYS_FE(fn)\
125 HHVM_NAMED_FE_STR(#fn, HHVM_FN(fn), Native::s_systemNativeFuncs)
126 #define HHVM_NAMED_SYS_ME(cn,fn,mimpl) Native::registerNativeFunc(\
127 Native::s_systemNativeFuncs, #cn "->" #fn, mimpl)
128 #define HHVM_SYS_ME(cn,fn) HHVM_NAMED_SYS_ME(cn,fn, HHVM_MN(cn,fn))
130 /* Macros related to declaring/registering internal implementations
131 * of <<__Native>> class static methods.
133 * See the definition of function macros above for general explanation.
134 * These macros only differ in the following ways:
135 * - They accept a classname in addition to the function name
136 * - The registered name of the function is "ClassName::FunctionName"
137 * - Prototypes include a prepended const Class* parameter (named self_)
139 #define HHVM_STATIC_MN(cn,fn) c_ ## cn ## _ns_ ## fn
140 #define HHVM_STATIC_METHOD(cn, fn, ...) \
141 HHVM_STATIC_MN(cn,fn)(const Class *self_, ##__VA_ARGS__)
142 #define HHVM_NAMED_STATIC_ME(cn,fn,mimpl) \
143 Native::registerNativeFunc(nativeFuncs(), #cn "::" #fn, mimpl)
144 #define HHVM_STATIC_ME(cn,fn) HHVM_NAMED_STATIC_ME(cn,fn,HHVM_STATIC_MN(cn,fn))
145 #define HHVM_STATIC_MALIAS(cn,fn,calias,falias) \
146 HHVM_NAMED_STATIC_ME(cn,fn,HHVM_STATIC_MN(calias,falias))
148 /* Macros related to declaring/registering constants. Note that the
149 * HHVM_RCC_* macros expect a StaticString to be present via s_##class_name.
151 #define HHVM_RC_STR(const_name, const_value) \
152 Native::registerConstant<KindOfString>( \
153 makeStaticString(#const_name), makeStaticString(const_value));
154 #define HHVM_RC_INT(const_name, const_value) \
155 Native::registerConstant<KindOfInt64>( \
156 makeStaticString(#const_name), int64_t{const_value});
157 #define HHVM_RC_DBL(const_name, const_value) \
158 Native::registerConstant<KindOfDouble>( \
159 makeStaticString(#const_name), double{const_value});
160 #define HHVM_RC_BOOL(const_name, const_value) \
161 Native::registerConstant<KindOfBoolean>( \
162 makeStaticString(#const_name), bool{const_value});
164 #define HHVM_RC_STR_SAME(const_name) \
165 Native::registerConstant<KindOfString>( \
166 makeStaticString(#const_name), makeStaticString(const_name));
167 #define HHVM_RC_INT_SAME(const_name) \
168 Native::registerConstant<KindOfInt64>( \
169 makeStaticString(#const_name), int64_t{const_name});
170 #define HHVM_RC_DBL_SAME(const_name) \
171 Native::registerConstant<KindOfDouble>( \
172 makeStaticString(#const_name), double{const_name});
173 #define HHVM_RC_BOOL_SAME(const_name) \
174 Native::registerConstant<KindOfBoolean>( \
175 makeStaticString(#const_name), bool{const_name});
177 #define HHVM_RCC_STR(class_name, const_name, const_value) \
178 Native::registerClassConstant<KindOfString>(s_##class_name.get(), \
179 makeStaticString(#const_name), makeStaticString(const_value));
180 #define HHVM_RCC_INT(class_name, const_name, const_value) \
181 Native::registerClassConstant<KindOfInt64>(s_##class_name.get(), \
182 makeStaticString(#const_name), int64_t{const_value});
183 #define HHVM_RCC_DBL(class_name, const_name, const_value) \
184 Native::registerClassConstant<KindOfDouble>(s_##class_name.get(), \
185 makeStaticString(#const_name), double{const_value});
186 #define HHVM_RCC_BOOL(class_name, const_name, const_value) \
187 Native::registerClassConstant<KindOfBoolean>(s_##class_name.get(), \
188 makeStaticString(#const_name), bool{const_value});
190 // Register a dynamic constant. This will not be optimized by hhbbc
191 #define HHVM_RC_DYNAMIC(const_name, const_value_cell) \
192 Native::registerConstant(makeStaticString(#const_name), \
193 const_value_cell, true);
195 namespace HPHP
{ namespace Native
{
196 //////////////////////////////////////////////////////////////////////////////
198 // Maximum number of args for a native function call
200 // To paraphrase you-know-who, "32 args should be enough for anybody"
202 // Note: If changing this number, update native-func-caller.h
203 // using make_native-func-caller.php
204 const int kMaxBuiltinArgs
= 32;
206 inline int maxFCallBuiltinArgs() {
207 return kMaxBuiltinArgs
;
213 // Methods whose implementation is generated by HackC.
214 AttrOpCodeImpl
= (1u << 0),
218 * Prepare function call arguments to match their expected
219 * types on a native function/method call.
221 * Uses typehints in Func and tvCast*InPlace
223 void coerceFCallArgsFromLocals(const ActRec
* fp
,
226 void coerceFCallArgsFromStack(TypedValue
* args
,
231 * Dispatches a call to the native function bound to <func>
232 * If <ctx> is not nullptr, it is prepended to <args> when
235 void callFunc(const Func
* func
,
240 bool isFCallBuiltin
);
242 #define NATIVE_TYPES \
243 /* kind arg type return type */ \
244 X(Int32, int32_t, int32_t) \
245 X(Int64, int64_t, int64_t) \
246 X(Double, double, double) \
247 X(Bool, bool, bool) \
248 X(Object, const Object&, Object) \
249 X(String, const String&, String) \
250 X(Array, const Array&, Array) \
251 X(Resource, const Resource&, Resource) \
252 X(Func, Func*, Func*) \
253 X(Class, const Class*, const Class*) \
254 X(ClsMeth, ClsMethDataRef, ClsMethDataRef) \
255 X(Mixed, const Variant&, Variant) \
256 X(ObjectArg, ObjectArg, ObjectArg) \
257 X(StringArg, StringArg, StringArg) \
258 X(ArrayArg, ArrayArg, ArrayArg) \
259 X(ResourceArg,ResourceArg, ResourceArg) \
260 X(MixedTV, TypedValue, TypedValue) \
261 X(This, ObjectData*, ObjectData*) \
262 X(Void, void, void) \
263 X(IntIO, int64_t&, int64_t&) \
264 X(DoubleIO, double&, double&) \
265 X(BoolIO, bool&, bool&) \
266 X(ObjectIO, Object&, Object&) \
267 X(StringIO, String&, String&) \
268 X(ArrayIO, Array&, Array&) \
269 X(ResourceIO, Resource&, Resource&) \
270 X(FuncIO, Func*&, Func*&) \
271 X(ClassIO, Class*&, Class*&) \
272 X(ClsMethIO, ClsMethDataRef&, ClsMethDataRef&)\
273 X(MixedIO, Variant&, Variant&) \
279 * Delete move assignment operator to make this non-copyable.
281 NativeArg
& operator=(NativeArg
&&) = delete;
282 T
* operator->() { return m_px
; }
283 T
* get() { return m_px
; }
284 bool operator!() const { return m_px
== nullptr; }
285 bool isNull() const { return m_px
== nullptr; }
288 * To be able to pass the values of this class as function parameters
289 * by value, define default copy constructor (See Itanium C++ ABI p3.1.1).
290 * Make it private to satisfy the non-copyable requirement.
292 NativeArg(const NativeArg
&) = default;
297 using ObjectArg
= Native::NativeArg
<ObjectData
>;
298 using StringArg
= Native::NativeArg
<StringData
>;
299 using ArrayArg
= Native::NativeArg
<ArrayData
>;
300 using ResourceArg
= Native::NativeArg
<ResourceData
>;
305 enum class Type
: uint8_t {
306 #define X(name, ...) name,
311 NativeSig() : ret(Type::Void
) {}
313 NativeSig(Type ret
, const std::vector
<Type
>& args
)
314 : ret(ret
), args(args
)
317 NativeSig(Type ret
, std::vector
<Type
>&& args
)
318 : ret(ret
), args(std::move(args
))
321 NativeSig(NativeSig
&&) = default;
322 NativeSig(const NativeSig
&) = default;
324 NativeSig
& operator=(const NativeSig
&) = default;
325 NativeSig
& operator=(NativeSig
&&) = default;
328 explicit NativeSig(Ret (*ptr
)());
330 template<class Ret
, class... Args
>
331 explicit NativeSig(Ret (*ptr
)(Args
...));
333 bool operator==(const NativeSig
& other
) const {
334 return ret
== other
.ret
&& args
== other
.args
;
337 std::string
toString(const char* classname
, const char* fname
) const;
340 std::vector
<Type
> args
;
345 template<class T
> struct native_arg_type
{};
346 template<class T
> struct native_ret_type
{};
347 template<class T
> struct known_native_arg
: std::false_type
{};
349 #define X(name, argTy, retTy) \
350 template<> struct native_ret_type<retTy> \
351 : std::integral_constant<NativeSig::Type,NativeSig::Type::name> \
353 template<> struct native_arg_type<argTy> \
354 : std::integral_constant<NativeSig::Type,NativeSig::Type::name> \
356 template<> struct known_native_arg<argTy> : std::true_type {};
362 template<class... Args
> struct all_known_arg_type
{};
363 template<class A
> struct all_known_arg_type
<A
> : known_native_arg
<A
> {};
365 template<class A
, class... Rest
>
366 struct all_known_arg_type
<A
,Rest
...>
367 : std::integral_constant
<
369 known_native_arg
<A
>::value
&& all_known_arg_type
<Rest
...>::value
373 template<class T
> struct understandable_sig
: std::false_type
{};
374 template<class R
> struct understandable_sig
<R (*)()> : std::true_type
{};
375 template<class R
, class... Args
>
376 struct understandable_sig
<R (*)(Args
...)>
377 : all_known_arg_type
<Args
...>
380 template<class... Args
>
381 std::vector
<NativeSig::Type
> build_args() {
383 native_arg_type
<Args
>::value
...
390 NativeSig::NativeSig(Ret (*/
*ptr*/
)())
391 : ret(detail::native_ret_type
<Ret
>::value
) {}
393 template <class Ret
, class... Args
>
394 NativeSig::NativeSig(Ret (*/
*ptr*/
)(Args
...))
395 : ret(detail::native_ret_type
<Ret
>::value
),
396 args(detail::build_args
<Args
...>()) {}
400 // NativeFunctionInfo carries around a NativeSig describing the real signature,
401 // and a type-erased NativeFunction ptr.
402 struct NativeFunctionInfo
{
403 NativeFunctionInfo() : ptr(nullptr) {}
405 // generate the signature using template magic
406 template<typename Func
>
407 explicit NativeFunctionInfo(Func f
)
409 , ptr(reinterpret_cast<NativeFunction
>(f
))
412 // trust the given signature
413 template<typename Func
>
414 NativeFunctionInfo(NativeSig sig
, Func f
)
416 , ptr(reinterpret_cast<NativeFunction
>(f
))
419 explicit operator bool() const { return ptr
!= nullptr; }
421 bool operator==(const NativeFunctionInfo
& other
) const {
422 return ptr
== other
.ptr
&& sig
== other
.sig
;
430 * Known output types for inout parameters on builtins and optional default
431 * values to be passed to builtins which use inout paramaters purely as out
432 * values, ignoring their inputs.
434 MaybeDataType
builtinOutType(const TypeConstraint
&, const UserAttributeMap
&);
435 folly::Optional
<TypedValue
> builtinInValue(const Func
* builtin
, uint32_t i
);
437 /////////////////////////////////////////////////////////////////////////////
440 * Returns a specialization of either functionWrapper or methodWrapper
442 * functionWrapper() Unpacks args and coerces types according
443 * to Func typehints. Calls C++ function in the form:
444 * ret f_foo(type arg1, type arg2, ...)
445 * Marshalls return into TypedValue.
447 * methodWrapper() behaves the same as functionWrapper(),
448 * but also prepends either an ObjectData* (instance) or Class* (static)
449 * argument to the signature. i.e.:
450 * ret c_class_ni_method(ObjectData* this_, type arg1, type arg2, ...)
451 * ret c_class_ns_method(Class* self_, type arg1, type arg2, ...)
453 void getFunctionPointers(const NativeFunctionInfo
& info
,
456 NativeFunction
& nif
);
459 * Fallback method bound to declared methods with no matching
460 * internal implementation.
462 [[noreturn
]] TypedValue
* unimplementedWrapper(ActRec
* ar
);
464 /////////////////////////////////////////////////////////////////////////////
467 * registerNativeFunc() and getNativeFunction() use a provided
468 * FuncTable that is a case insensitive map of "name" to function pointer.
470 * Extensions should generally add items to this map using the HHVM_FE/ME
471 * macros above. The function name (key) must be a static string.
475 void registerNativeFunc(FuncTable
&, const StringData
*,
476 const NativeFunctionInfo
&);
478 // Helper accepting a C-string name
479 template <class Fun
> typename
480 std::enable_if
<!std::is_member_function_pointer
<Fun
>::value
, void>::type
481 registerNativeFunc(FuncTable
& nativeFuncs
, const char* name
, Fun func
) {
483 std::is_pointer
<Fun
>::value
&&
484 std::is_function
<typename
std::remove_pointer
<Fun
>::type
>::value
,
485 "You can only register pointers to functions."
488 detail::understandable_sig
<Fun
>::value
,
489 "Arguments on builtin function were not understood types"
491 registerNativeFunc(nativeFuncs
, makeStaticString(name
),
492 NativeFunctionInfo(func
));
495 // Helper accepting a possibly nonstatic HPHP::String name
496 template <class Fun
> typename
497 std::enable_if
<!std::is_member_function_pointer
<Fun
>::value
, void>::type
498 registerNativeFunc(FuncTable
& nativeFuncs
, const String
& name
, Fun func
) {
500 std::is_pointer
<Fun
>::value
&&
501 std::is_function
<typename
std::remove_pointer
<Fun
>::type
>::value
,
502 "You can only register pointers to functions."
505 detail::understandable_sig
<Fun
>::value
,
506 "Arguments on builtin function were not understood types"
508 registerNativeFunc(nativeFuncs
, makeStaticString(name
),
509 NativeFunctionInfo(func
));
512 // Specializations of registerNativeFunc for taking pointers to member
513 // functions and making them look like HNI wrapper funcs.
515 // This allows invoking object method calls directly, but ONLY for specialized
516 // subclasses of ObjectData.
518 // This API is limited to: Closure, Asio, and Collections. Do not use it if
519 // you are not implementing one of these
520 template<class Ret
, class Cls
> typename
521 std::enable_if
<std::is_base_of
<ObjectData
, Cls
>::value
, void>::type
522 registerNativeFunc(FuncTable
& nativeFuncs
, const char* name
,
523 Ret (Cls::*func
)()) {
524 registerNativeFunc(nativeFuncs
, name
,
525 (Ret (*)(ObjectData
*))getMethodPtr(func
));
528 template<class Ret
, class Cls
, class... Args
> typename
529 std::enable_if
<std::is_base_of
<ObjectData
, Cls
>::value
, void>::type
530 registerNativeFunc(FuncTable
& nativeFuncs
, const char* name
,
531 Ret (Cls::*func
)(Args
...)) {
533 nativeFuncs
, name
, (Ret (*)(ObjectData
*, Args
...))getMethodPtr(func
)
537 template<class Ret
, class Cls
> typename
538 std::enable_if
<std::is_base_of
<ObjectData
, Cls
>::value
, void>::type
539 registerNativeFunc(FuncTable
& nativeFuncs
, const char* name
,
540 Ret (Cls::*func
)() const) {
541 registerNativeFunc(nativeFuncs
, name
,
542 (Ret (*)(ObjectData
*))getMethodPtr(func
));
545 template<class Ret
, class Cls
, class... Args
> typename
546 std::enable_if
<std::is_base_of
<ObjectData
, Cls
>::value
, void>::type
547 registerNativeFunc(FuncTable
& nativeFuncs
, const char* name
,
548 Ret (Cls::*func
)(Args
...) const) {
550 nativeFuncs
, name
, (Ret (*)(ObjectData
*, Args
...))getMethodPtr(func
)
554 /////////////////////////////////////////////////////////////////////////////
556 const char* checkTypeFunc(const NativeSig
& sig
,
557 const TypeConstraint
& retType
,
558 const FuncEmitter
* func
);
560 // NativeFunctionInfo for native funcs and methods defined under
561 // system/php, separate from normal extensions.
562 extern FuncTable s_systemNativeFuncs
;
564 // A permanently empty table, used in contexts were no native bindings
565 // are possible (most ordinary code).
566 extern const FuncTable s_noNativeFuncs
;
568 String
fullName(const StringData
* fname
, const StringData
* cname
,
571 NativeFunctionInfo
getNativeFunction(const FuncTable
& nativeFuncs
,
572 const StringData
* fname
,
573 const StringData
* cname
= nullptr,
574 bool isStatic
= false);
576 NativeFunctionInfo
getNativeFunction(const FuncTable
& nativeFuncs
,
578 const char* cname
= nullptr,
579 bool isStatic
= false);
581 //////////////////////////////////////////////////////////////////////////////
584 typedef std::map
<const StringData
*,TypedValueAux
> ConstantMap
;
585 extern ConstantMap s_constant_map
;
588 bool registerConstant(const StringData
* cnsName
, TypedValue cns
,
589 bool dynamic
= false) {
590 assertx(tvIsPlausible(cns
) && cns
.m_type
!= KindOfUninit
);
591 auto& dst
= s_constant_map
[cnsName
];
592 *static_cast<TypedValue
*>(&dst
) = cns
;
593 dst
.dynamic() = dynamic
;
594 return bindPersistentCns(cnsName
, cns
);
597 template<DataType DType
>
598 typename
std::enable_if
<
599 !std::is_same
<typename DataTypeCPPType
<DType
>::type
,void>::value
,
601 registerConstant(const StringData
* cnsName
,
602 typename DataTypeCPPType
<DType
>::type val
) {
603 return registerConstant(cnsName
, make_tv
<DType
>(val
));
606 template<DataType DType
>
607 typename
std::enable_if
<
608 std::is_same
<typename DataTypeCPPType
<DType
>::type
,void>::value
,
610 registerConstant(const StringData
* cnsName
) {
611 return registerConstant(cnsName
, make_tv
<DType
>());
615 const ConstantMap
& getConstants() {
616 return s_constant_map
;
619 using ConstantCallback
= Variant (*)(const StringData
*);
620 bool registerConstant(const StringData
*, ConstantCallback
);
622 //////////////////////////////////////////////////////////////////////////////
625 typedef hphp_hash_map
<const StringData
*, ConstantMap
,
626 string_data_hash
, string_data_isame
> ClassConstantMapMap
;
627 extern ClassConstantMapMap s_class_constant_map
;
630 bool registerClassConstant(const StringData
*clsName
,
631 const StringData
*cnsName
,
633 assertx(tvIsPlausible(cns
));
634 auto &cls
= s_class_constant_map
[clsName
];
635 assertx(cls
.find(cnsName
) == cls
.end());
636 *static_cast<TypedValue
*>(&cls
[cnsName
]) = cns
;
640 template<DataType DType
>
641 typename
std::enable_if
<
642 !std::is_same
<typename DataTypeCPPType
<DType
>::type
,void>::value
,
644 registerClassConstant(const StringData
* clsName
,
645 const StringData
* cnsName
,
646 typename DataTypeCPPType
<DType
>::type val
) {
647 return registerClassConstant(clsName
, cnsName
, make_tv
<DType
>(val
));
650 template<DataType DType
>
651 typename
std::enable_if
<
652 std::is_same
<typename DataTypeCPPType
<DType
>::type
,void>::value
,
654 registerClassConstant(const StringData
* clsName
,
655 const StringData
* cnsName
) {
656 return registerClassConstant(clsName
, cnsName
, make_tv
<DType
>());
660 const ConstantMap
* getClassConstants(const StringData
* clsName
) {
661 auto clsit
= s_class_constant_map
.find(const_cast<StringData
*>(clsName
));
662 if (clsit
== s_class_constant_map
.end()) {
665 return &clsit
->second
;
668 //////////////////////////////////////////////////////////////////////////////
669 }} // namespace HPHP::Native