2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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 <runtime/eval/ast/expression.h>
17 #include <runtime/eval/ast/method_statement.h>
18 #include <runtime/eval/ast/statement_list_statement.h>
19 #include <runtime/eval/runtime/variable_environment.h>
20 #include <runtime/eval/ast/class_statement.h>
21 #include <runtime/eval/runtime/eval_state.h>
22 #include <runtime/eval/ast/function_call_expression.h>
27 ///////////////////////////////////////////////////////////////////////////////
29 MethodStatement::MethodStatement(STATEMENT_ARGS
, const string
&name
,
30 const ClassStatement
*cls
, int modifiers
,
32 : FunctionStatement(STATEMENT_PASS
, name
, doc
), m_class(cls
),
33 m_modifiers(modifiers
),
34 m_fullName(StringData::GetStaticString(
35 string(cls
->name().c_str()) + "::" + name
)) {
36 if ((m_modifiers
& (ClassStatement::Public
| ClassStatement::Protected
|
37 ClassStatement::Private
)) == 0) {
38 m_modifiers
|= ClassStatement::Public
;
40 m_callInfo
.m_invoker
= (void*)MethInvoker
;
41 m_callInfo
.m_invokerFewArgs
= (void*)MethInvokerFewArgs
;
42 if (m_modifiers
& ClassStatement::Static
) {
43 m_callInfo
.m_flags
|= CallInfo::StaticMethod
;
45 m_callInfo
.m_flags
|= CallInfo::Method
;
47 if (m_modifiers
& ClassStatement::Protected
) {
48 m_callInfo
.m_flags
|= CallInfo::Protected
;
49 } else if (m_modifiers
& ClassStatement::Private
) {
50 m_callInfo
.m_flags
|= CallInfo::Private
;
54 void MethodStatement::setPublic() {
55 m_modifiers
= ClassStatement::Public
;
58 String
MethodStatement::fullName() const {
62 void MethodStatement::eval(VariableEnvironment
&env
) const {
63 if (env
.isGotoing()) return;
65 // register with reflection, invoke, etc.
68 LVariableTable
*MethodStatement::getStaticVars(VariableEnvironment
&env
)
70 return &RequestEvalState::getMethodStatics(this, env
.currentClass());
73 Variant
MethodStatement::invokeInstance(CObjRef obj
, CArrRef params
,
74 bool check
/* = true */) const {
75 if (getModifiers() & ClassStatement::Static
) {
76 return invokeStatic(obj
->o_getClassName(), params
, check
);
78 if (check
) attemptAccess(FrameInjection::GetClassName(false));
79 // The debug frame should have been pushed at ObjectMethodExpression
80 DECLARE_THREAD_INFO_NOINIT
81 MethScopeVariableEnvironment
env(this);
82 env
.setCurrentObject(obj
);
83 String
clsName(m_class
->name());
84 EvalFrameInjection
fi(clsName
, m_fullName
->data(), env
,
85 loc()->file
, obj
.get(), FrameInjection::ObjectMethod
);
87 return strongBind(invokeImpl(env
, params
));
89 return invokeImpl(env
, params
);
92 Variant
MethodStatement::invokeInstanceFewArgs(CObjRef obj
, int count
,
93 INVOKE_FEW_ARGS_IMPL_ARGS
, bool check
) const {
94 if (getModifiers() & ClassStatement::Static
) {
95 return invokeStaticFewArgs(obj
->o_getClassName(), count
,
96 INVOKE_FEW_ARGS_PASS_ARGS
, check
);
98 if (check
) attemptAccess(FrameInjection::GetClassName(false));
99 // The debug frame should have been pushed at ObjectMethodExpression
100 DECLARE_THREAD_INFO_NOINIT
101 MethScopeVariableEnvironment
env(this);
102 env
.setCurrentObject(obj
);
103 String
clsName(m_class
->name());
104 EvalFrameInjection
fi(clsName
, m_fullName
->data(), env
,
105 loc()->file
, obj
.get(), FrameInjection::ObjectMethod
);
107 return strongBind(invokeImplFewArgs(env
, count
, INVOKE_FEW_ARGS_PASS_ARGS
));
109 return invokeImplFewArgs(env
, count
, INVOKE_FEW_ARGS_PASS_ARGS
);
112 Variant
MethodStatement::
113 invokeInstanceDirect(CObjRef obj
, VariableEnvironment
&env
,
114 const FunctionCallExpression
*caller
,
115 bool check
/* = true */) const {
116 if (getModifiers() & ClassStatement::Static
) {
117 return invokeStaticDirect(obj
->o_getClassName(), env
,
118 caller
, false, check
);
120 if (check
) attemptAccess(FrameInjection::GetClassName(false));
121 DECLARE_THREAD_INFO_NOINIT
122 MethScopeVariableEnvironment
fenv(this);
123 directBind(env
, caller
, fenv
);
124 fenv
.setCurrentObject(obj
);
125 String
clsName(m_class
->name());
126 EvalFrameInjection
fi(clsName
, m_fullName
->data(), fenv
,
127 loc()->file
, obj
.get(), FrameInjection::ObjectMethod
);
129 return strongBind(evalBody(fenv
));
131 return evalBody(fenv
);
134 Variant
MethodStatement::invokeStatic(const char* cls
, CArrRef params
,
135 bool check
/* = true */) const {
136 if (check
) attemptAccess(FrameInjection::GetClassName(false));
137 DECLARE_THREAD_INFO_NOINIT
138 MethScopeVariableEnvironment
env(this);
139 env
.setCurrentClass(cls
);
140 String
clsName(m_class
->name());
141 EvalFrameInjection
fi(clsName
, m_fullName
->data(), env
, loc()->file
,
142 NULL
, FrameInjection::StaticMethod
);
144 return strongBind(invokeImpl(env
, params
));
146 return invokeImpl(env
, params
);
149 Variant
MethodStatement::invokeStaticFewArgs(const char* cls
, int count
,
150 INVOKE_FEW_ARGS_IMPL_ARGS
, bool check
) const {
151 if (check
) attemptAccess(FrameInjection::GetClassName(false));
152 DECLARE_THREAD_INFO_NOINIT
153 MethScopeVariableEnvironment
env(this);
154 env
.setCurrentClass(cls
);
155 String
clsName(m_class
->name());
156 EvalFrameInjection
fi(clsName
, m_fullName
->data(), env
, loc()->file
,
157 NULL
, FrameInjection::StaticMethod
);
159 return strongBind(invokeImplFewArgs(env
, count
, INVOKE_FEW_ARGS_PASS_ARGS
));
161 return invokeImplFewArgs(env
, count
, INVOKE_FEW_ARGS_PASS_ARGS
);
164 Variant
MethodStatement::
165 invokeStaticDirect(CStrRef cls
, VariableEnvironment
&env
,
166 const FunctionCallExpression
*caller
, bool sp
,
167 bool check
/* = true */)
169 if (check
) attemptAccess(FrameInjection::GetClassName(false));
170 MethScopeVariableEnvironment
fenv(this);
171 directBind(env
, caller
, fenv
);
172 fenv
.setCurrentClass(cls
.data());
173 EvalFrameInjection::EvalStaticClassNameHelper
helper(cls
, sp
);
174 DECLARE_THREAD_INFO_NOINIT
175 String
clsName(m_class
->name());
176 EvalFrameInjection
fi(clsName
, m_fullName
->data(), fenv
,
177 loc()->file
, NULL
, FrameInjection::StaticMethod
);
179 return strongBind(evalBody(fenv
));
181 return evalBody(fenv
);
184 Variant
MethodStatement::evalBody(VariableEnvironment
&env
) const {
186 raise_error("Cannot call abstract method %s()", m_fullName
->data());
189 return strongBind(FunctionStatement::evalBody(env
));
191 return FunctionStatement::evalBody(env
);
195 void MethodStatement::getInfo(ClassInfo::MethodInfo
&info
) const {
196 FunctionStatement::getInfo(info
);
197 int attr
= info
.attribute
== ClassInfo::IsNothing
? 0 : info
.attribute
;
198 if (m_modifiers
& ClassStatement::Abstract
) attr
|= ClassInfo::IsAbstract
;
199 if (m_modifiers
& ClassStatement::Final
) attr
|= ClassInfo::IsFinal
;
200 if (m_modifiers
& ClassStatement::Protected
) attr
|= ClassInfo::IsProtected
;
201 if (m_modifiers
& ClassStatement::Private
) attr
|= ClassInfo::IsPrivate
;
202 if (m_modifiers
& ClassStatement::Static
) attr
|= ClassInfo::IsStatic
;
203 if (!(attr
& ClassInfo::IsProtected
|| attr
& ClassInfo::IsPrivate
)) {
204 attr
|= ClassInfo::IsPublic
;
206 info
.attribute
= (ClassInfo::Attribute
)attr
;
209 void MethodStatement::attemptAccess(const char *context
) const {
210 if (g_context
->getDebuggerBypassCheck()) {
213 int mods
= getModifiers();
214 const ClassStatement
*cs
= getClass();
215 ClassStatement::Modifier level
= ClassStatement::Public
;
216 if (mods
& ClassStatement::Private
) level
= ClassStatement::Private
;
217 else if (mods
& ClassStatement::Protected
) level
= ClassStatement::Protected
;
219 if (level
== ClassStatement::Protected
) {
220 while (!cs
->hasAccess(context
, level
)) {
221 while ((cs
= cs
->parentStatement())) {
222 if (cs
->findMethod(m_name
->data())) {
232 access
= cs
->hasAccess(context
, level
);
235 // special case when handling parent's private constructors that are allowed
237 if (level
== ClassStatement::Private
&&
238 (strcasecmp(m_name
->data(), "__construct") == 0 ||
239 strcasecmp(m_name
->data(), getClass()->name().c_str()) == 0)) {
240 access
= cs
->hasAccess(context
, ClassStatement::Protected
);
245 const char *mod
= "protected";
246 if (level
== ClassStatement::Private
) mod
= "private";
247 throw FatalErrorException(0, "Attempt to call %s %s::%s()%s%s",
248 mod
, getClass()->name().c_str(), m_name
->data(),
249 context
[0] ? " from " : "",
250 context
[0] ? context
: "");
254 bool MethodStatement::isAbstract() const {
255 return getModifiers() & ClassStatement::Abstract
||
256 m_class
->getModifiers() & ClassStatement::Interface
;
259 Variant
MethodStatement::MethInvoker(MethodCallPackage
&mcp
, CArrRef params
) {
260 const MethodStatement
*ms
= (const MethodStatement
*)mcp
.extra
;
261 bool check
= strcasecmp(ms
->m_name
->data(), "__invoke") != 0;
262 bool isStatic
= ms
->getModifiers() & ClassStatement::Static
;
263 if (isStatic
|| !mcp
.obj
) {
265 if (UNLIKELY(!isStatic
&& mcp
.isObj
&& mcp
.obj
== NULL
)) {
266 // this is needed for continuations where
267 // we are passed the dummy object
268 cn
= ms
->getClass()->name();
270 cn
= mcp
.getClassName();
272 if (ms
->refReturn()) {
273 return strongBind(ms
->invokeStatic(cn
.c_str(), params
, check
));
275 return ms
->invokeStatic(cn
.c_str(), params
, check
);
278 if (ms
->refReturn()) {
279 return strongBind(ms
->invokeInstance(mcp
.rootObj
, params
, check
));
281 return ms
->invokeInstance(mcp
.rootObj
, params
, check
);
286 Variant
MethodStatement::MethInvokerFewArgs(MethodCallPackage
&mcp
,
287 int count
, INVOKE_FEW_ARGS_IMPL_ARGS
) {
288 const MethodStatement
*ms
= (const MethodStatement
*)mcp
.extra
;
289 bool check
= strcasecmp(ms
->m_name
->data(), "__invoke") != 0;
290 bool isStatic
= ms
->getModifiers() & ClassStatement::Static
;
291 if (isStatic
|| !mcp
.obj
) {
293 if (UNLIKELY(!isStatic
&& mcp
.isObj
&& mcp
.obj
== NULL
)) {
294 // this is needed for continuations where
295 // we are passed the dummy object
296 cn
= ms
->getClass()->name();
298 cn
= mcp
.getClassName();
300 if (ms
->refReturn()) {
301 return strongBind(ms
->invokeStaticFewArgs(cn
.c_str(), count
,
302 INVOKE_FEW_ARGS_PASS_ARGS
, check
));
304 return ms
->invokeStaticFewArgs(cn
.c_str(), count
,
305 INVOKE_FEW_ARGS_PASS_ARGS
, check
);
308 if (ms
->refReturn()) {
309 return strongBind(ms
->invokeInstanceFewArgs(mcp
.rootObj
, count
,
310 INVOKE_FEW_ARGS_PASS_ARGS
, check
));
312 return ms
->invokeInstanceFewArgs(mcp
.rootObj
, count
,
313 INVOKE_FEW_ARGS_PASS_ARGS
, check
);
318 void MethodStatement::dump(std::ostream
&out
) const {
319 ClassStatement::dumpModifiers(out
, m_modifiers
, false);
320 FunctionStatement::dump(out
);
323 ///////////////////////////////////////////////////////////////////////////////