PageRenderTime 639ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/mcs/eval.cs

https://bitbucket.org/danipen/mono
C# | 1165 lines | 826 code | 121 blank | 218 comment | 113 complexity | 0faa8012afc0091cab9f9e812efc276d MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // eval.cs: Evaluation and Hosting API for the C# compiler
  3. //
  4. // Authors:
  5. // Miguel de Icaza (miguel@gnome.org)
  6. // Marek Safar (marek.safar@gmail.com)
  7. //
  8. // Dual licensed under the terms of the MIT X11 or GNU GPL
  9. //
  10. // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
  11. // Copyright 2004-2011 Novell, Inc
  12. // Copyright 2011 Xamarin Inc
  13. //
  14. using System;
  15. using System.Threading;
  16. using System.Collections.Generic;
  17. using System.Reflection;
  18. using System.Reflection.Emit;
  19. using System.IO;
  20. using System.Text;
  21. using System.Linq;
  22. namespace Mono.CSharp
  23. {
  24. /// <summary>
  25. /// Evaluator: provides an API to evaluate C# statements and
  26. /// expressions dynamically.
  27. /// </summary>
  28. /// <remarks>
  29. /// This class exposes static methods to evaluate expressions in the
  30. /// current program.
  31. ///
  32. /// To initialize the evaluator with a number of compiler
  33. /// options call the Init(string[]args) method with a set of
  34. /// command line options that the compiler recognizes.
  35. ///
  36. /// To interrupt execution of a statement, you can invoke the
  37. /// Evaluator.Interrupt method.
  38. /// </remarks>
  39. public class Evaluator {
  40. enum ParseMode {
  41. // Parse silently, do not output any error messages
  42. Silent,
  43. // Report errors during parse
  44. ReportErrors,
  45. // Auto-complete, means that the tokenizer will start producing
  46. // GETCOMPLETIONS tokens when it reaches a certain point.
  47. GetCompletions
  48. }
  49. static object evaluator_lock = new object ();
  50. static volatile bool invoking;
  51. #if !STATIC
  52. static int count;
  53. #endif
  54. static Thread invoke_thread;
  55. readonly Dictionary<string, Tuple<FieldSpec, FieldInfo>> fields;
  56. Type base_class;
  57. bool inited;
  58. int startup_files;
  59. readonly CompilerContext ctx;
  60. readonly ModuleContainer module;
  61. readonly ReflectionImporter importer;
  62. readonly CompilationSourceFile source_file;
  63. public Evaluator (CompilerContext ctx)
  64. {
  65. this.ctx = ctx;
  66. module = new ModuleContainer (ctx);
  67. module.Evaluator = this;
  68. source_file = new CompilationSourceFile (module, null);
  69. module.AddTypeContainer (source_file);
  70. startup_files = ctx.SourceFiles.Count;
  71. // FIXME: Importer needs this assembly for internalsvisibleto
  72. module.SetDeclaringAssembly (new AssemblyDefinitionDynamic (module, "evaluator"));
  73. importer = new ReflectionImporter (module, ctx.BuiltinTypes);
  74. InteractiveBaseClass = typeof (InteractiveBase);
  75. fields = new Dictionary<string, Tuple<FieldSpec, FieldInfo>> ();
  76. }
  77. void Init ()
  78. {
  79. var loader = new DynamicLoader (importer, ctx);
  80. CompilerCallableEntryPoint.Reset ();
  81. RootContext.ToplevelTypes = module;
  82. //var startup_files = new List<string> ();
  83. //foreach (CompilationUnit file in Location.SourceFiles)
  84. // startup_files.Add (file.Path);
  85. loader.LoadReferences (module);
  86. ctx.BuiltinTypes.CheckDefinitions (module);
  87. module.InitializePredefinedTypes ();
  88. inited = true;
  89. }
  90. void ParseStartupFiles ()
  91. {
  92. Driver d = new Driver (ctx);
  93. Location.Initialize (ctx.SourceFiles);
  94. var parser_session = new ParserSession ();
  95. for (int i = 0; i < startup_files; ++i) {
  96. var sf = ctx.SourceFiles [i];
  97. d.Parse (sf, module, parser_session, ctx.Report);
  98. }
  99. }
  100. void Reset ()
  101. {
  102. CompilerCallableEntryPoint.PartialReset ();
  103. Location.Reset ();
  104. Location.Initialize (ctx.SourceFiles);
  105. }
  106. /// <summary>
  107. /// If true, turns type expressions into valid expressions
  108. /// and calls the describe method on it
  109. /// </summary>
  110. public bool DescribeTypeExpressions;
  111. /// <summary>
  112. /// Whether the evaluator will use terse syntax, and the semicolons at the end are optional
  113. /// </summary>
  114. public bool Terse = true;
  115. /// <summary>
  116. /// The base class for the classes that host the user generated code
  117. /// </summary>
  118. /// <remarks>
  119. ///
  120. /// This is the base class that will host the code
  121. /// executed by the Evaluator. By default
  122. /// this is the Mono.CSharp.InteractiveBase class
  123. /// which is useful for interactive use.
  124. ///
  125. /// By changing this property you can control the
  126. /// base class and the static members that are
  127. /// available to your evaluated code.
  128. /// </remarks>
  129. public Type InteractiveBaseClass {
  130. get {
  131. return base_class;
  132. }
  133. set {
  134. base_class = value;
  135. if (value != null && typeof (InteractiveBase).IsAssignableFrom (value))
  136. InteractiveBase.Evaluator = this;
  137. }
  138. }
  139. /// <summary>
  140. /// Interrupts the evaluation of an expression executing in Evaluate.
  141. /// </summary>
  142. /// <remarks>
  143. /// Use this method to interrupt long-running invocations.
  144. /// </remarks>
  145. public void Interrupt ()
  146. {
  147. if (!inited || !invoking)
  148. return;
  149. if (invoke_thread != null)
  150. invoke_thread.Abort ();
  151. }
  152. /// <summary>
  153. /// Compiles the input string and returns a delegate that represents the compiled code.
  154. /// </summary>
  155. /// <remarks>
  156. ///
  157. /// Compiles the input string as a C# expression or
  158. /// statement, unlike the Evaluate method, the
  159. /// resulting delegate can be invoked multiple times
  160. /// without incurring in the compilation overhead.
  161. ///
  162. /// If the return value of this function is null,
  163. /// this indicates that the parsing was complete.
  164. /// If the return value is a string it indicates
  165. /// that the input string was partial and that the
  166. /// invoking code should provide more code before
  167. /// the code can be successfully compiled.
  168. ///
  169. /// If you know that you will always get full expressions or
  170. /// statements and do not care about partial input, you can use
  171. /// the other Compile overload.
  172. ///
  173. /// On success, in addition to returning null, the
  174. /// compiled parameter will be set to the delegate
  175. /// that can be invoked to execute the code.
  176. ///
  177. /// </remarks>
  178. public string Compile (string input, out CompiledMethod compiled)
  179. {
  180. if (input == null || input.Length == 0){
  181. compiled = null;
  182. return null;
  183. }
  184. lock (evaluator_lock){
  185. if (!inited) {
  186. Init ();
  187. ParseStartupFiles ();
  188. } else {
  189. ctx.Report.Printer.Reset ();
  190. }
  191. bool partial_input;
  192. CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
  193. // Terse mode, try to provide the trailing semicolon automatically.
  194. if (parser == null && Terse && partial_input){
  195. bool ignore;
  196. // check if the source would compile with a block, if so, we should not
  197. // add the semicolon.
  198. var needs_block = ParseString (ParseMode.Silent, input + "{}", out ignore) != null;
  199. if (!needs_block)
  200. parser = ParseString (ParseMode.Silent, input + ";", out ignore);
  201. }
  202. if (parser == null){
  203. compiled = null;
  204. if (partial_input)
  205. return input;
  206. ParseString (ParseMode.ReportErrors, input, out partial_input);
  207. return null;
  208. }
  209. Class parser_result = parser.InteractiveResult;
  210. compiled = CompileBlock (parser_result, parser.undo, ctx.Report);
  211. return null;
  212. }
  213. }
  214. /// <summary>
  215. /// Compiles the input string and returns a delegate that represents the compiled code.
  216. /// </summary>
  217. /// <remarks>
  218. ///
  219. /// Compiles the input string as a C# expression or
  220. /// statement, unlike the Evaluate method, the
  221. /// resulting delegate can be invoked multiple times
  222. /// without incurring in the compilation overhead.
  223. ///
  224. /// This method can only deal with fully formed input
  225. /// strings and does not provide a completion mechanism.
  226. /// If you must deal with partial input (for example for
  227. /// interactive use) use the other overload.
  228. ///
  229. /// On success, a delegate is returned that can be used
  230. /// to invoke the method.
  231. ///
  232. /// </remarks>
  233. public CompiledMethod Compile (string input)
  234. {
  235. CompiledMethod compiled;
  236. // Ignore partial inputs
  237. if (Compile (input, out compiled) != null){
  238. // Error, the input was partial.
  239. return null;
  240. }
  241. // Either null (on error) or the compiled method.
  242. return compiled;
  243. }
  244. /// <summary>
  245. /// Evaluates and expression or statement and returns any result values.
  246. /// </summary>
  247. /// <remarks>
  248. /// Evaluates the input string as a C# expression or
  249. /// statement. If the input string is an expression
  250. /// the result will be stored in the result variable
  251. /// and the result_set variable will be set to true.
  252. ///
  253. /// It is necessary to use the result/result_set
  254. /// pair to identify when a result was set (for
  255. /// example, execution of user-provided input can be
  256. /// an expression, a statement or others, and
  257. /// result_set would only be set if the input was an
  258. /// expression.
  259. ///
  260. /// If the return value of this function is null,
  261. /// this indicates that the parsing was complete.
  262. /// If the return value is a string, it indicates
  263. /// that the input is partial and that the user
  264. /// should provide an updated string.
  265. /// </remarks>
  266. public string Evaluate (string input, out object result, out bool result_set)
  267. {
  268. CompiledMethod compiled;
  269. result_set = false;
  270. result = null;
  271. input = Compile (input, out compiled);
  272. if (input != null)
  273. return input;
  274. if (compiled == null)
  275. return null;
  276. //
  277. // The code execution does not need to keep the compiler lock
  278. //
  279. object retval = typeof (QuitValue);
  280. try {
  281. invoke_thread = System.Threading.Thread.CurrentThread;
  282. invoking = true;
  283. compiled (ref retval);
  284. } catch (ThreadAbortException e){
  285. Thread.ResetAbort ();
  286. Console.WriteLine ("Interrupted!\n{0}", e);
  287. } finally {
  288. invoking = false;
  289. }
  290. //
  291. // We use a reference to a compiler type, in this case
  292. // Driver as a flag to indicate that this was a statement
  293. //
  294. if (!ReferenceEquals (retval, typeof (QuitValue))) {
  295. result_set = true;
  296. result = retval;
  297. }
  298. return null;
  299. }
  300. public string [] GetCompletions (string input, out string prefix)
  301. {
  302. prefix = "";
  303. if (input == null || input.Length == 0)
  304. return null;
  305. lock (evaluator_lock){
  306. if (!inited)
  307. Init ();
  308. bool partial_input;
  309. CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
  310. if (parser == null){
  311. return null;
  312. }
  313. Class parser_result = parser.InteractiveResult;
  314. #if NET_4_0
  315. var access = AssemblyBuilderAccess.RunAndCollect;
  316. #else
  317. var access = AssemblyBuilderAccess.Run;
  318. #endif
  319. var a = new AssemblyDefinitionDynamic (module, "completions");
  320. a.Create (AppDomain.CurrentDomain, access);
  321. module.SetDeclaringAssembly (a);
  322. // Need to setup MemberCache
  323. parser_result.CreateContainer ();
  324. var method = parser_result.Members[0] as Method;
  325. BlockContext bc = new BlockContext (method, method.Block, ctx.BuiltinTypes.Void);
  326. try {
  327. method.Block.Resolve (null, bc, method);
  328. } catch (CompletionResult cr) {
  329. prefix = cr.BaseText;
  330. return cr.Result;
  331. }
  332. }
  333. return null;
  334. }
  335. /// <summary>
  336. /// Executes the given expression or statement.
  337. /// </summary>
  338. /// <remarks>
  339. /// Executes the provided statement, returns true
  340. /// on success, false on parsing errors. Exceptions
  341. /// might be thrown by the called code.
  342. /// </remarks>
  343. public bool Run (string statement)
  344. {
  345. object result;
  346. bool result_set;
  347. return Evaluate (statement, out result, out result_set) == null;
  348. }
  349. /// <summary>
  350. /// Evaluates and expression or statement and returns the result.
  351. /// </summary>
  352. /// <remarks>
  353. /// Evaluates the input string as a C# expression or
  354. /// statement and returns the value.
  355. ///
  356. /// This method will throw an exception if there is a syntax error,
  357. /// of if the provided input is not an expression but a statement.
  358. /// </remarks>
  359. public object Evaluate (string input)
  360. {
  361. object result;
  362. bool result_set;
  363. string r = Evaluate (input, out result, out result_set);
  364. if (r != null)
  365. throw new ArgumentException ("Syntax error on input: partial input");
  366. if (result_set == false)
  367. throw new ArgumentException ("The expression did not set a result");
  368. return result;
  369. }
  370. enum InputKind {
  371. EOF,
  372. StatementOrExpression,
  373. CompilationUnit,
  374. Error
  375. }
  376. //
  377. // Deambiguates the input string to determine if we
  378. // want to process a statement or if we want to
  379. // process a compilation unit.
  380. //
  381. // This is done using a top-down predictive parser,
  382. // since the yacc/jay parser can not deambiguage this
  383. // without more than one lookahead token. There are very
  384. // few ambiguities.
  385. //
  386. InputKind ToplevelOrStatement (SeekableStreamReader seekable)
  387. {
  388. Tokenizer tokenizer = new Tokenizer (seekable, source_file, new ParserSession ());
  389. // Prefer contextual block keywords over identifiers
  390. tokenizer.parsing_block++;
  391. int t = tokenizer.token ();
  392. switch (t){
  393. case Token.EOF:
  394. return InputKind.EOF;
  395. // These are toplevels
  396. case Token.EXTERN:
  397. case Token.OPEN_BRACKET:
  398. case Token.ABSTRACT:
  399. case Token.CLASS:
  400. case Token.ENUM:
  401. case Token.INTERFACE:
  402. case Token.INTERNAL:
  403. case Token.NAMESPACE:
  404. case Token.PRIVATE:
  405. case Token.PROTECTED:
  406. case Token.PUBLIC:
  407. case Token.SEALED:
  408. case Token.STATIC:
  409. case Token.STRUCT:
  410. return InputKind.CompilationUnit;
  411. // Definitely expression
  412. case Token.FIXED:
  413. case Token.BOOL:
  414. case Token.BYTE:
  415. case Token.CHAR:
  416. case Token.DECIMAL:
  417. case Token.DOUBLE:
  418. case Token.FLOAT:
  419. case Token.INT:
  420. case Token.LONG:
  421. case Token.NEW:
  422. case Token.OBJECT:
  423. case Token.SBYTE:
  424. case Token.SHORT:
  425. case Token.STRING:
  426. case Token.UINT:
  427. case Token.ULONG:
  428. return InputKind.StatementOrExpression;
  429. // These need deambiguation help
  430. case Token.USING:
  431. t = tokenizer.token ();
  432. if (t == Token.EOF)
  433. return InputKind.EOF;
  434. if (t == Token.IDENTIFIER)
  435. return InputKind.CompilationUnit;
  436. return InputKind.StatementOrExpression;
  437. // Distinguish between:
  438. // delegate opt_anonymous_method_signature block
  439. // delegate type
  440. case Token.DELEGATE:
  441. t = tokenizer.token ();
  442. if (t == Token.EOF)
  443. return InputKind.EOF;
  444. if (t == Token.OPEN_PARENS || t == Token.OPEN_BRACE)
  445. return InputKind.StatementOrExpression;
  446. return InputKind.CompilationUnit;
  447. // Distinguih between:
  448. // unsafe block
  449. // unsafe as modifier of a type declaration
  450. case Token.UNSAFE:
  451. t = tokenizer.token ();
  452. if (t == Token.EOF)
  453. return InputKind.EOF;
  454. if (t == Token.OPEN_PARENS)
  455. return InputKind.StatementOrExpression;
  456. return InputKind.CompilationUnit;
  457. // These are errors: we list explicitly what we had
  458. // from the grammar, ERROR and then everything else
  459. case Token.READONLY:
  460. case Token.OVERRIDE:
  461. case Token.ERROR:
  462. return InputKind.Error;
  463. // This catches everything else allowed by
  464. // expressions. We could add one-by-one use cases
  465. // if needed.
  466. default:
  467. return InputKind.StatementOrExpression;
  468. }
  469. }
  470. //
  471. // Parses the string @input and returns a CSharpParser if succeeful.
  472. //
  473. // if @silent is set to true then no errors are
  474. // reported to the user. This is used to do various calls to the
  475. // parser and check if the expression is parsable.
  476. //
  477. // @partial_input: if @silent is true, then it returns whether the
  478. // parsed expression was partial, and more data is needed
  479. //
  480. CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
  481. {
  482. partial_input = false;
  483. Reset ();
  484. var enc = ctx.Settings.Encoding;
  485. var s = new MemoryStream (enc.GetBytes (input));
  486. SeekableStreamReader seekable = new SeekableStreamReader (s, enc);
  487. InputKind kind = ToplevelOrStatement (seekable);
  488. if (kind == InputKind.Error){
  489. if (mode == ParseMode.ReportErrors)
  490. ctx.Report.Error (-25, "Detection Parsing Error");
  491. partial_input = false;
  492. return null;
  493. }
  494. if (kind == InputKind.EOF){
  495. if (mode == ParseMode.ReportErrors)
  496. Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
  497. partial_input = true;
  498. return null;
  499. }
  500. seekable.Position = 0;
  501. source_file.DeclarationFound = false;
  502. CSharpParser parser = new CSharpParser (seekable, source_file, new ParserSession ());
  503. if (kind == InputKind.StatementOrExpression){
  504. parser.Lexer.putback_char = Tokenizer.EvalStatementParserCharacter;
  505. parser.Lexer.parsing_block++;
  506. ctx.Settings.StatementMode = true;
  507. } else {
  508. parser.Lexer.putback_char = Tokenizer.EvalCompilationUnitParserCharacter;
  509. ctx.Settings.StatementMode = false;
  510. }
  511. if (mode == ParseMode.GetCompletions)
  512. parser.Lexer.CompleteOnEOF = true;
  513. ReportPrinter old_printer = null;
  514. if ((mode == ParseMode.Silent || mode == ParseMode.GetCompletions))
  515. old_printer = ctx.Report.SetPrinter (new StreamReportPrinter (TextWriter.Null));
  516. try {
  517. parser.parse ();
  518. } finally {
  519. if (ctx.Report.Errors != 0){
  520. if (mode != ParseMode.ReportErrors && parser.UnexpectedEOF)
  521. partial_input = true;
  522. if (parser.undo != null)
  523. parser.undo.ExecuteUndo ();
  524. parser = null;
  525. }
  526. if (old_printer != null)
  527. ctx.Report.SetPrinter (old_printer);
  528. }
  529. return parser;
  530. }
  531. CompiledMethod CompileBlock (Class host, Undo undo, Report Report)
  532. {
  533. #if STATIC
  534. throw new NotSupportedException ();
  535. #else
  536. string current_debug_name = "eval-" + count + ".dll";
  537. ++count;
  538. AssemblyDefinitionDynamic assembly;
  539. AssemblyBuilderAccess access;
  540. if (Environment.GetEnvironmentVariable ("SAVE") != null) {
  541. access = AssemblyBuilderAccess.RunAndSave;
  542. assembly = new AssemblyDefinitionDynamic (module, current_debug_name, current_debug_name);
  543. assembly.Importer = importer;
  544. } else {
  545. #if NET_4_0
  546. access = AssemblyBuilderAccess.RunAndCollect;
  547. #else
  548. access = AssemblyBuilderAccess.Run;
  549. #endif
  550. assembly = new AssemblyDefinitionDynamic (module, current_debug_name);
  551. }
  552. assembly.Create (AppDomain.CurrentDomain, access);
  553. Method expression_method;
  554. if (host != null) {
  555. var base_class_imported = importer.ImportType (base_class);
  556. var baseclass_list = new List<FullNamedExpression> (1) {
  557. new TypeExpression (base_class_imported, host.Location)
  558. };
  559. host.AddBasesForPart (baseclass_list);
  560. host.CreateContainer ();
  561. host.DefineContainer ();
  562. host.Define ();
  563. expression_method = (Method) host.Members[0];
  564. } else {
  565. expression_method = null;
  566. }
  567. module.CreateContainer ();
  568. // Disable module and source file re-definition checks
  569. module.EnableRedefinition ();
  570. source_file.EnableRedefinition ();
  571. module.Define ();
  572. if (Report.Errors != 0){
  573. if (undo != null)
  574. undo.ExecuteUndo ();
  575. return null;
  576. }
  577. if (host != null){
  578. host.EmitContainer ();
  579. }
  580. module.EmitContainer ();
  581. if (Report.Errors != 0){
  582. if (undo != null)
  583. undo.ExecuteUndo ();
  584. return null;
  585. }
  586. module.CloseContainer ();
  587. if (host != null)
  588. host.CloseContainer ();
  589. if (access == AssemblyBuilderAccess.RunAndSave)
  590. assembly.Save ();
  591. if (host == null)
  592. return null;
  593. //
  594. // Unlike Mono, .NET requires that the MethodInfo is fetched, it cant
  595. // work from MethodBuilders. Retarded, I know.
  596. //
  597. var tt = assembly.Builder.GetType (host.TypeBuilder.Name);
  598. var mi = tt.GetMethod (expression_method.MemberName.Name);
  599. //
  600. // We need to then go from FieldBuilder to FieldInfo
  601. // or reflection gets confused (it basically gets confused, and variables override each
  602. // other).
  603. //
  604. foreach (var member in host.Members) {
  605. var field = member as Field;
  606. if (field == null)
  607. continue;
  608. var fi = tt.GetField (field.Name);
  609. Tuple<FieldSpec, FieldInfo> old;
  610. // If a previous value was set, nullify it, so that we do
  611. // not leak memory
  612. if (fields.TryGetValue (field.Name, out old)) {
  613. if (old.Item1.MemberType.IsStruct) {
  614. //
  615. // TODO: Clear fields for structs
  616. //
  617. } else {
  618. try {
  619. old.Item2.SetValue (null, null);
  620. } catch {
  621. }
  622. }
  623. }
  624. fields[field.Name] = Tuple.Create (field.Spec, fi);
  625. }
  626. return (CompiledMethod) System.Delegate.CreateDelegate (typeof (CompiledMethod), mi);
  627. #endif
  628. }
  629. /// <summary>
  630. /// A sentinel value used to indicate that no value was
  631. /// was set by the compiled function. This is used to
  632. /// differentiate between a function not returning a
  633. /// value and null.
  634. /// </summary>
  635. internal static class QuitValue { }
  636. internal Tuple<FieldSpec, FieldInfo> LookupField (string name)
  637. {
  638. Tuple<FieldSpec, FieldInfo> fi;
  639. fields.TryGetValue (name, out fi);
  640. return fi;
  641. }
  642. static string Quote (string s)
  643. {
  644. if (s.IndexOf ('"') != -1)
  645. s = s.Replace ("\"", "\\\"");
  646. return "\"" + s + "\"";
  647. }
  648. public string GetUsing ()
  649. {
  650. StringBuilder sb = new StringBuilder ();
  651. // TODO:
  652. //foreach (object x in ns.using_alias_list)
  653. // sb.AppendFormat ("using {0};\n", x);
  654. foreach (var ue in source_file.Usings) {
  655. sb.AppendFormat ("using {0};", ue.ToString ());
  656. sb.Append (Environment.NewLine);
  657. }
  658. return sb.ToString ();
  659. }
  660. internal List<string> GetUsingList ()
  661. {
  662. var res = new List<string> ();
  663. foreach (var ue in source_file.Usings) {
  664. if (ue.Alias != null || ue.ResolvedExpression == null)
  665. continue;
  666. res.Add (ue.NamespaceExpression.Name);
  667. }
  668. return res;
  669. }
  670. internal string [] GetVarNames ()
  671. {
  672. lock (evaluator_lock){
  673. return new List<string> (fields.Keys).ToArray ();
  674. }
  675. }
  676. public string GetVars ()
  677. {
  678. lock (evaluator_lock){
  679. StringBuilder sb = new StringBuilder ();
  680. foreach (var de in fields){
  681. var fi = LookupField (de.Key);
  682. object value;
  683. try {
  684. value = fi.Item2.GetValue (null);
  685. if (value is string)
  686. value = Quote ((string)value);
  687. } catch {
  688. value = "<error reading value>";
  689. }
  690. sb.AppendFormat ("{0} {1} = {2}", fi.Item1.MemberType.GetSignatureForError (), de.Key, value);
  691. sb.AppendLine ();
  692. }
  693. return sb.ToString ();
  694. }
  695. }
  696. /// <summary>
  697. /// Loads the given assembly and exposes the API to the user.
  698. /// </summary>
  699. public void LoadAssembly (string file)
  700. {
  701. var loader = new DynamicLoader (importer, ctx);
  702. var assembly = loader.LoadAssemblyFile (file, false);
  703. if (assembly == null)
  704. return;
  705. lock (evaluator_lock){
  706. importer.ImportAssembly (assembly, module.GlobalRootNamespace);
  707. }
  708. }
  709. /// <summary>
  710. /// Exposes the API of the given assembly to the Evaluator
  711. /// </summary>
  712. public void ReferenceAssembly (Assembly a)
  713. {
  714. lock (evaluator_lock){
  715. importer.ImportAssembly (a, module.GlobalRootNamespace);
  716. }
  717. }
  718. }
  719. /// <summary>
  720. /// A delegate that can be used to invoke the
  721. /// compiled expression or statement.
  722. /// </summary>
  723. /// <remarks>
  724. /// Since the Compile methods will compile
  725. /// statements and expressions into the same
  726. /// delegate, you can tell if a value was returned
  727. /// by checking whether the returned value is of type
  728. /// NoValueSet.
  729. /// </remarks>
  730. public delegate void CompiledMethod (ref object retvalue);
  731. /// <summary>
  732. /// The default base class for every interaction line
  733. /// </summary>
  734. /// <remarks>
  735. /// The expressions and statements behave as if they were
  736. /// a static method of this class. The InteractiveBase class
  737. /// contains a number of useful methods, but can be overwritten
  738. /// by setting the InteractiveBaseType property in the Evaluator
  739. /// </remarks>
  740. public class InteractiveBase {
  741. /// <summary>
  742. /// Determines where the standard output of methods in this class will go.
  743. /// </summary>
  744. public static TextWriter Output = Console.Out;
  745. /// <summary>
  746. /// Determines where the standard error of methods in this class will go.
  747. /// </summary>
  748. public static TextWriter Error = Console.Error;
  749. /// <summary>
  750. /// The primary prompt used for interactive use.
  751. /// </summary>
  752. public static string Prompt = "csharp> ";
  753. /// <summary>
  754. /// The secondary prompt used for interactive use (used when
  755. /// an expression is incomplete).
  756. /// </summary>
  757. public static string ContinuationPrompt = " > ";
  758. /// <summary>
  759. /// Used to signal that the user has invoked the `quit' statement.
  760. /// </summary>
  761. public static bool QuitRequested;
  762. public static Evaluator Evaluator;
  763. /// <summary>
  764. /// Shows all the variables defined so far.
  765. /// </summary>
  766. static public void ShowVars ()
  767. {
  768. Output.Write (Evaluator.GetVars ());
  769. Output.Flush ();
  770. }
  771. /// <summary>
  772. /// Displays the using statements in effect at this point.
  773. /// </summary>
  774. static public void ShowUsing ()
  775. {
  776. Output.Write (Evaluator.GetUsing ());
  777. Output.Flush ();
  778. }
  779. /// <summary>
  780. /// Times the execution of the given delegate
  781. /// </summary>
  782. static public TimeSpan Time (Action a)
  783. {
  784. DateTime start = DateTime.Now;
  785. a ();
  786. return DateTime.Now - start;
  787. }
  788. /// <summary>
  789. /// Loads the assemblies from a package
  790. /// </summary>
  791. /// <remarks>
  792. /// Loads the assemblies from a package. This is equivalent
  793. /// to passing the -pkg: command line flag to the C# compiler
  794. /// on the command line.
  795. /// </remarks>
  796. static public void LoadPackage (string pkg)
  797. {
  798. if (pkg == null){
  799. Error.WriteLine ("Invalid package specified");
  800. return;
  801. }
  802. string pkgout = Driver.GetPackageFlags (pkg, null);
  803. string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
  804. Split (new Char [] { ' ', '\t'});
  805. foreach (string s in xargs){
  806. if (s.StartsWith ("-r:") || s.StartsWith ("/r:") || s.StartsWith ("/reference:")){
  807. string lib = s.Substring (s.IndexOf (':')+1);
  808. Evaluator.LoadAssembly (lib);
  809. continue;
  810. }
  811. }
  812. }
  813. /// <summary>
  814. /// Loads the assembly
  815. /// </summary>
  816. /// <remarks>
  817. /// Loads the specified assembly and makes its types
  818. /// available to the evaluator. This is equivalent
  819. /// to passing the -pkg: command line flag to the C#
  820. /// compiler on the command line.
  821. /// </remarks>
  822. static public void LoadAssembly (string assembly)
  823. {
  824. Evaluator.LoadAssembly (assembly);
  825. }
  826. static public void print (object obj)
  827. {
  828. Output.WriteLine (obj);
  829. }
  830. static public void print (string fmt, params object [] args)
  831. {
  832. Output.WriteLine (fmt, args);
  833. }
  834. /// <summary>
  835. /// Returns a list of available static methods.
  836. /// </summary>
  837. static public string help {
  838. get {
  839. return "Static methods:\n" +
  840. #if !NET_2_1
  841. " Describe (object); - Describes the object's type\n" +
  842. #endif
  843. " LoadPackage (package); - Loads the given Package (like -pkg:FILE)\n" +
  844. " LoadAssembly (assembly); - Loads the given assembly (like -r:ASSEMBLY)\n" +
  845. " ShowVars (); - Shows defined local variables.\n" +
  846. " ShowUsing (); - Show active using declarations.\n" +
  847. " Prompt - The prompt used by the C# shell\n" +
  848. " ContinuationPrompt - The prompt for partial input\n" +
  849. " Time (() => { }); - Times the specified code\n" +
  850. " print (obj); - Shorthand for Console.WriteLine\n" +
  851. " quit; - You'll never believe it - this quits the repl!\n" +
  852. " help; - This help text\n";
  853. }
  854. }
  855. /// <summary>
  856. /// Indicates to the read-eval-print-loop that the interaction should be finished.
  857. /// </summary>
  858. static public object quit {
  859. get {
  860. QuitRequested = true;
  861. // To avoid print null at the exit
  862. return typeof (Evaluator.QuitValue);
  863. }
  864. }
  865. /// <summary>
  866. /// Same as quit - useful in script scenerios
  867. /// </summary>
  868. static public void Quit () {
  869. QuitRequested = true;
  870. }
  871. #if !NET_2_1
  872. /// <summary>
  873. /// Describes an object or a type.
  874. /// </summary>
  875. /// <remarks>
  876. /// This method will show a textual representation
  877. /// of the object's type. If the object is a
  878. /// System.Type it renders the type directly,
  879. /// otherwise it renders the type returned by
  880. /// invoking GetType on the object.
  881. /// </remarks>
  882. static public string Describe (object x)
  883. {
  884. if (x == null)
  885. return "<null>";
  886. var type = x as Type ?? x.GetType ();
  887. StringWriter sw = new StringWriter ();
  888. new Outline (type, sw, true, false, false).OutlineType ();
  889. return sw.ToString ();
  890. }
  891. #endif
  892. }
  893. class HoistedEvaluatorVariable : HoistedVariable
  894. {
  895. public HoistedEvaluatorVariable (Field field)
  896. : base (null, field)
  897. {
  898. }
  899. protected override FieldExpr GetFieldExpression (EmitContext ec)
  900. {
  901. return new FieldExpr (field, field.Location);
  902. }
  903. }
  904. /// <summary>
  905. /// A class used to assign values if the source expression is not void
  906. ///
  907. /// Used by the interactive shell to allow it to call this code to set
  908. /// the return value for an invocation.
  909. /// </summary>
  910. class OptionalAssign : SimpleAssign {
  911. public OptionalAssign (Expression t, Expression s, Location loc)
  912. : base (t, s, loc)
  913. {
  914. }
  915. protected override Expression DoResolve (ResolveContext ec)
  916. {
  917. Expression clone = source.Clone (new CloneContext ());
  918. clone = clone.Resolve (ec);
  919. if (clone == null)
  920. return null;
  921. //
  922. // A useful feature for the REPL: if we can resolve the expression
  923. // as a type, Describe the type;
  924. //
  925. if (ec.Module.Evaluator.DescribeTypeExpressions){
  926. var old_printer = ec.Report.SetPrinter (new SessionReportPrinter ());
  927. Expression tclone;
  928. try {
  929. // Note: clone context cannot be shared otherwise block mapping would leak
  930. tclone = source.Clone (new CloneContext ());
  931. tclone = tclone.Resolve (ec, ResolveFlags.Type);
  932. if (ec.Report.Errors > 0)
  933. tclone = null;
  934. } finally {
  935. ec.Report.SetPrinter (old_printer);
  936. }
  937. if (tclone is TypeExpr) {
  938. Arguments args = new Arguments (1);
  939. args.Add (new Argument (new TypeOf ((TypeExpr) clone, Location)));
  940. return new Invocation (new SimpleName ("Describe", Location), args).Resolve (ec);
  941. }
  942. }
  943. // This means its really a statement.
  944. if (clone.Type.Kind == MemberKind.Void || clone is DynamicInvocation || clone is Assign) {
  945. return clone;
  946. }
  947. source = clone;
  948. return base.DoResolve (ec);
  949. }
  950. }
  951. public class Undo
  952. {
  953. List<Action> undo_actions;
  954. public Undo ()
  955. {
  956. }
  957. public void AddTypeContainer (TypeContainer current_container, TypeDefinition tc)
  958. {
  959. if (current_container == tc){
  960. Console.Error.WriteLine ("Internal error: inserting container into itself");
  961. return;
  962. }
  963. if (undo_actions == null)
  964. undo_actions = new List<Action> ();
  965. var existing = current_container.Containers.FirstOrDefault (l => l.Basename == tc.Basename);
  966. if (existing != null) {
  967. current_container.RemoveContainer (existing);
  968. undo_actions.Add (() => current_container.AddTypeContainer (existing));
  969. }
  970. undo_actions.Add (() => current_container.RemoveContainer (tc));
  971. }
  972. public void ExecuteUndo ()
  973. {
  974. if (undo_actions == null)
  975. return;
  976. foreach (var p in undo_actions){
  977. p ();
  978. }
  979. undo_actions = null;
  980. }
  981. }
  982. }