PageRenderTime 220ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/4.0/Source/Core/Utilities/GlobalScope.cs

#
C# | 505 lines | 338 code | 81 blank | 86 comment | 19 complexity | ae2e72b82004433761876831868c6e90 MD5 | raw file
Possible License(s): CPL-1.0, GPL-2.0, CC-BY-SA-3.0, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Dynamic;
  6. using System.Linq.Expressions;
  7. using PHP.Core.Reflection;
  8. using PHP.Core.Binders;
  9. using PHP.Core.Emit;
  10. using System.Diagnostics;
  11. namespace PHP.Core.Utilities
  12. {
  13. #region BaseScope
  14. /// <summary>
  15. /// Base class for all the convenience dynamic objects for accessing global elements
  16. /// </summary>
  17. public abstract class BaseScope : DynamicObject
  18. {
  19. private ScriptContext context;
  20. private string ns;
  21. private string nsSlash;
  22. protected const string ConstID = "const";
  23. protected const string NamespaceID = "namespace";
  24. protected const string ClassID = "class";
  25. public string Namespace
  26. {
  27. get { return ns; }
  28. }
  29. public bool UseNamespaces
  30. {
  31. get { return ns != null; }
  32. }
  33. public ScriptContext Context
  34. {
  35. get { return context; }
  36. }
  37. internal BaseScope(ScriptContext context)
  38. {
  39. this.context = context;
  40. }
  41. internal BaseScope(ScriptContext context, string namespaceName)
  42. :this(context)
  43. {
  44. this.ns = namespaceName;
  45. this.nsSlash = this.ns + QualifiedName.Separator;
  46. }
  47. /// <summary>
  48. /// Transforms given name to be full name including namespaces
  49. /// </summary>
  50. public string GetFullName(string name)
  51. {
  52. if (UseNamespaces)
  53. return nsSlash + name;
  54. return name;
  55. }
  56. /// <summary>
  57. /// Wrap all arguments to Phalanger objects only if the type is not primitive
  58. /// </summary>
  59. /// <param name="args"></param>
  60. /// <returns></returns>
  61. protected static object[] wrapArgs(Object[] args)
  62. {
  63. object[] wrappedArgs = new object[args.Length];
  64. for (int i = 0; i < args.Length; ++i)
  65. {
  66. Debug.Assert(!(args[i] is PhpReference));
  67. wrappedArgs[i] = ClrObject.WrapDynamic(args[i]);
  68. }
  69. return wrappedArgs;
  70. }
  71. }
  72. #endregion
  73. #region GlobalScope
  74. /// <summary>
  75. /// Convenience class for accessing global functions and global variables
  76. /// </summary>
  77. public class GlobalScope : BaseScope
  78. {
  79. private ClassesScope classes;
  80. private NamespaceScope namespaces;
  81. private ConstsScope consts;
  82. /// <summary>
  83. /// Gets ClassesScope dynamic object that represents types defined in global scope
  84. /// </summary>
  85. protected ClassesScope Classes
  86. {
  87. get
  88. {
  89. if (classes == null)
  90. {
  91. if (UseNamespaces)
  92. classes = new ClassesScope(this.Context, Namespace);
  93. else
  94. classes = new ClassesScope(this.Context);
  95. }
  96. return classes;
  97. }
  98. }
  99. /// <summary>
  100. /// Gets ConstsScope dynamic object that represents types defined in global scope
  101. /// </summary>
  102. protected ConstsScope Consts
  103. {
  104. get
  105. {
  106. if (consts == null)
  107. {
  108. if (UseNamespaces)
  109. consts = new ConstsScope(this.Context, Namespace);
  110. else
  111. consts = new ConstsScope(this.Context);
  112. }
  113. return consts;
  114. }
  115. }
  116. /// <summary>
  117. /// Gets NamespaceScope dynamic object that represents namespaces defined in global scope
  118. /// </summary>
  119. private NamespaceScope Namespaces
  120. {
  121. get
  122. {
  123. if (namespaces == null)
  124. namespaces = new NamespaceScope(this.Context);
  125. return namespaces;
  126. }
  127. }
  128. /// <summary>
  129. /// Initialize GlobalScope object
  130. /// </summary>
  131. /// <param name="currentContext"></param>
  132. internal GlobalScope(ScriptContext currentContext)
  133. : base(currentContext)
  134. {
  135. }
  136. /// <summary>
  137. /// Initialize GlobalScope object with namespace specified
  138. /// </summary>
  139. /// <param name="currentContext"></param>
  140. /// <param name="namespaceName"></param>
  141. protected GlobalScope(ScriptContext currentContext, string namespaceName)
  142. : base(currentContext, namespaceName)
  143. {
  144. }
  145. #region DynamicObject
  146. /// <summary>
  147. /// Specifies dynamic behavior for invoke operation for global function
  148. /// </summary>
  149. public override bool TryInvokeMember(
  150. InvokeMemberBinder binder,
  151. Object[] args,
  152. out Object result
  153. )
  154. {
  155. return TryInvokeMember(binder.Name, args, out result);
  156. }
  157. /// <summary>
  158. /// Specifies dynamic behavior for get operation for global variable
  159. /// </summary>
  160. public override bool TryGetMember(
  161. GetMemberBinder binder,
  162. out Object result
  163. )
  164. {
  165. switch (binder.Name)
  166. {
  167. case ClassID:
  168. result = Classes;
  169. return true;
  170. case ConstID:
  171. result = Consts;
  172. return true;
  173. case NamespaceID:
  174. result = Namespaces;
  175. return true;
  176. }
  177. result = PhpVariable.Unwrap(Operators.GetVariable(Context, null, binder.Name));
  178. return true;
  179. }
  180. /// <summary>
  181. /// Specifies dynamic behavior for set operation for global function
  182. /// </summary>
  183. public override bool TrySetMember(
  184. SetMemberBinder binder,
  185. Object value
  186. )
  187. {
  188. return TrySetMember(binder.Name, value);
  189. }
  190. #endregion
  191. /// <summary>
  192. /// Specifies dynamic behavior for invoke operation for global function
  193. /// </summary>
  194. public bool TryInvokeMember(
  195. string memberName,
  196. Object[] args,
  197. out Object result
  198. )
  199. {
  200. result = PhpVariable.Unwrap(PhpVariable.Dereference(Context.Call(memberName, null, null, wrapArgs(args))));
  201. return true;
  202. }
  203. public bool TrySetMember(
  204. string memberName,
  205. Object value)
  206. {
  207. Operators.SetVariable(Context, null, memberName, ClrObject.WrapDynamic(value));
  208. return true;
  209. }
  210. }
  211. #endregion
  212. #region NamespaceScope
  213. /// <summary>
  214. /// Dynamic Obeject for representing PHP namespaces
  215. /// </summary>
  216. public class NamespaceScope : GlobalScope
  217. {
  218. internal NamespaceScope(ScriptContext context)
  219. : base(context)
  220. {
  221. }
  222. private NamespaceScope(ScriptContext context, string namespaceName)
  223. : base(context, namespaceName)
  224. {
  225. }
  226. /// <summary>
  227. /// Specifies dynamic behavior for invoke operation for global function
  228. /// </summary>
  229. public override bool TryInvokeMember(
  230. InvokeMemberBinder binder,
  231. Object[] args,
  232. out Object result
  233. )
  234. {
  235. return TryInvokeMember(GetFullName(binder.Name), args, out result);
  236. }
  237. /// <summary>
  238. /// Specifies dynamic behavior for get operation for global variable in namespace
  239. /// </summary>
  240. public override bool TryGetMember(
  241. GetMemberBinder binder,
  242. out Object result
  243. )
  244. {
  245. switch (binder.Name)
  246. {
  247. case ClassID:
  248. result = Classes;
  249. return true;
  250. case ConstID:
  251. result = Consts;
  252. return true;
  253. }
  254. result = new NamespaceScope(Context, GetFullName(binder.Name));
  255. return true;
  256. }
  257. /// <summary>
  258. /// Specifies dynamic behavior for set operation for global function in namespace
  259. /// </summary>
  260. public override bool TrySetMember(
  261. SetMemberBinder binder,
  262. Object value
  263. )
  264. {
  265. TrySetMember(GetFullName(binder.Name),value);
  266. return true;
  267. }
  268. }
  269. #endregion
  270. #region ClassScope
  271. /// <summary>
  272. /// Dynamic scope for reprensenting static members of class
  273. /// </summary>
  274. public class ClassScope : BaseScope
  275. {
  276. private DTypeDesc type;
  277. internal ClassScope(ScriptContext context, DTypeDesc type)
  278. : base(context)
  279. {
  280. this.type = type;
  281. }
  282. /// <summary>
  283. /// Specifies dynamic behavior for invoke operation for static method
  284. /// </summary>
  285. public override bool TryInvokeMember(
  286. InvokeMemberBinder binder,
  287. Object[] args,
  288. out Object result
  289. )
  290. {
  291. Context.Stack.AddFrame(wrapArgs(args));
  292. result = PhpVariable.Unwrap(PhpVariable.Dereference(Operators.InvokeStaticMethod(type, binder.Name, null, null, Context)));
  293. return true;
  294. }
  295. /// <summary>
  296. /// Specifies dynamic behavior for get operation for static variable
  297. /// </summary>
  298. public override bool TryGetMember(
  299. GetMemberBinder binder,
  300. out Object result
  301. )
  302. {
  303. if (binder.Name == ConstID)
  304. {
  305. result = new ConstsScope(Context,type);
  306. return true;
  307. }
  308. result = PhpVariable.Unwrap(PhpVariable.Dereference(Operators.GetStaticProperty(type, binder.Name, null, Context, false)));
  309. return true;
  310. }
  311. /// <summary>
  312. /// Specifies dynamic behavior for set operation for static variable
  313. /// </summary>
  314. public override bool TrySetMember(
  315. SetMemberBinder binder,
  316. Object value
  317. )
  318. {
  319. Operators.SetStaticProperty(type, binder.Name, ClrObject.WrapDynamic(value), null, Context);
  320. return true;
  321. }
  322. }
  323. #endregion
  324. #region ClassesScope
  325. /// <summary>
  326. /// Dynamic Object for representing PHP classes
  327. /// </summary>
  328. public class ClassesScope : BaseScope
  329. {
  330. internal ClassesScope(ScriptContext context)
  331. : base(context)
  332. {
  333. }
  334. internal ClassesScope(ScriptContext context, string namespaceName)
  335. : base(context, namespaceName)
  336. {
  337. }
  338. private DTypeDesc ResolveType(string name)
  339. {
  340. return Context.ResolveType(GetFullName(name), null, null, null, ResolveTypeFlags.UseAutoload | ResolveTypeFlags.ThrowErrors);
  341. }
  342. /// <summary>
  343. /// Creates new instance of specified PHP class
  344. /// </summary>
  345. public override bool TryInvokeMember(
  346. InvokeMemberBinder binder,
  347. Object[] args,
  348. out Object result
  349. )
  350. {
  351. Context.Stack.AddFrame(wrapArgs(args));
  352. DTypeDesc type = ResolveType(binder.Name);
  353. result = Operators.New(type, null, Context, null);
  354. return true;
  355. }
  356. /// <summary>
  357. /// Gets dynamic object representing classes
  358. /// </summary>
  359. public override bool TryGetMember(
  360. GetMemberBinder binder,
  361. out Object result
  362. )
  363. {
  364. DTypeDesc resType = ResolveType(binder.Name);
  365. result = new ClassScope(Context, resType);
  366. return true;
  367. }
  368. }
  369. #endregion
  370. #region ConstsScope
  371. /// <summary>
  372. /// Dynamic Object for representing PHP constants
  373. /// </summary>
  374. public class ConstsScope : BaseScope
  375. {
  376. private DTypeDesc type;
  377. internal ConstsScope(ScriptContext context)
  378. : base(context)
  379. {
  380. }
  381. internal ConstsScope(ScriptContext context, string namespaceName)
  382. : base(context, namespaceName)
  383. {
  384. }
  385. internal ConstsScope(ScriptContext context, DTypeDesc type)
  386. : base(context)
  387. {
  388. this.type = type;
  389. }
  390. /// <summary>
  391. /// Specifies dynamic behavior for get operation for a constant
  392. /// </summary>
  393. public override bool TryGetMember(
  394. GetMemberBinder binder,
  395. out Object result
  396. )
  397. {
  398. if (type != null)
  399. {
  400. result = Operators.GetClassConstant(type, binder.Name, null, Context);
  401. return true;
  402. }
  403. result = Context.GetConstantValue(GetFullName(binder.Name), false, false);
  404. return true;
  405. }
  406. /// <summary>
  407. /// Specifies dynamic behavior for set operation for a constant
  408. /// </summary>
  409. public override bool TrySetMember(
  410. SetMemberBinder binder,
  411. Object value
  412. )
  413. {
  414. if (type != null)
  415. {
  416. PhpException.Throw(PhpError.Error, String.Format( PHP.Core.Localizations.Strings.constant_redefined, type.MakeFullName() + Name.ClassMemberSeparator + binder.Name));
  417. return true;
  418. }
  419. Context.DefineConstant(GetFullName(binder.Name), value);
  420. return true;
  421. }
  422. }
  423. #endregion
  424. }