AssemblyDescriptor.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. using System;
  2. #if NETSTANDARD || NETCOREAPP
  3. using FastReport.Code.CodeDom.Compiler;
  4. #else
  5. using System.CodeDom.Compiler;
  6. #endif
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Collections.Specialized;
  10. using System.Drawing;
  11. using System.IO;
  12. using System.Reflection;
  13. using System.Security.Cryptography;
  14. using System.Text;
  15. using System.Text.RegularExpressions;
  16. using System.Collections.Concurrent;
  17. using FastReport.Data;
  18. using FastReport.Engine;
  19. using FastReport.Utils;
  20. #if SKIA
  21. using HMACSHA1 = FastReport.Utils.DetravHMACSHA1;
  22. #endif
  23. namespace FastReport.Code
  24. {
  25. partial class AssemblyDescriptor
  26. {
  27. private static readonly ConcurrentDictionary<string, Assembly> FAssemblyCache;
  28. private readonly FastString scriptText;
  29. private readonly List<SourcePosition> sourcePositions;
  30. private int insertLine;
  31. private int insertPos;
  32. private bool needCompile;
  33. private const string shaKey = "FastReportCode";
  34. private readonly static object compileLocker;
  35. private readonly string currentFolder;
  36. private const int RECOMPILE_COUNT = 1;
  37. public Assembly Assembly { get; private set; }
  38. public object Instance { get; private set; }
  39. public Report Report { get; }
  40. public Hashtable Expressions { get; }
  41. private void InsertItem(string text, string objName)
  42. {
  43. string[] lines = text.Split('\r');
  44. scriptText.Insert(insertPos, text);
  45. SourcePosition pos = new SourcePosition(objName, insertLine, insertLine + lines.Length - 2);
  46. sourcePositions.Add(pos);
  47. insertLine += lines.Length - 1;
  48. insertPos += text.Length;
  49. }
  50. private void InitField(string name, object c)
  51. {
  52. FieldInfo info = Instance.GetType().GetField(name,
  53. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  54. info.SetValue(Instance, c);
  55. }
  56. private void InitFields()
  57. {
  58. InitField("Report", Report);
  59. InitField("Engine", Report.Engine);
  60. ObjectCollection allObjects = Report.AllObjects;
  61. foreach (Base c in allObjects)
  62. {
  63. if (!String.IsNullOrEmpty(c.Name))
  64. InitField(c.Name, c);
  65. }
  66. }
  67. private string GetErrorObjectName(int errorLine)
  68. {
  69. foreach (SourcePosition pos in sourcePositions)
  70. {
  71. if (errorLine >= pos.start && errorLine <= pos.end)
  72. {
  73. return pos.sourceObject;
  74. }
  75. }
  76. return "";
  77. }
  78. private int GetScriptLine(int errorLine)
  79. {
  80. int start = sourcePositions[0].start;
  81. int end = sourcePositions[sourcePositions.Count - 1].end;
  82. if (errorLine >= start && errorLine <= end)
  83. return -1;
  84. if (errorLine > end)
  85. return errorLine - (end - start + 1);
  86. return errorLine;
  87. }
  88. private string ReplaceDataItems(string expression)
  89. {
  90. FindTextArgs args = new FindTextArgs();
  91. args.Text = new FastString(expression);
  92. args.OpenBracket = "[";
  93. args.CloseBracket = "]";
  94. while (args.StartIndex < args.Text.Length)
  95. {
  96. expression = CodeUtils.GetExpression(args, true);
  97. if (expression == null)
  98. break;
  99. if (DataHelper.IsValidColumn(Report.Dictionary, expression))
  100. {
  101. Type type = DataHelper.GetColumnType(Report.Dictionary, expression);
  102. expression = Report.CodeHelper.ReplaceColumnName(expression, type);
  103. }
  104. else if (DataHelper.IsValidParameter(Report.Dictionary, expression))
  105. {
  106. expression = Report.CodeHelper.ReplaceParameterName(DataHelper.GetParameter(Report.Dictionary, expression));
  107. }
  108. else if (DataHelper.IsValidTotal(Report.Dictionary, expression))
  109. {
  110. expression = Report.CodeHelper.ReplaceTotalName(expression);
  111. }
  112. else
  113. {
  114. expression = "[" + ReplaceDataItems(expression) + "]";
  115. }
  116. args.Text = args.Text.Remove(args.StartIndex, args.EndIndex - args.StartIndex);
  117. args.Text = args.Text.Insert(args.StartIndex, expression);
  118. args.StartIndex += expression.Length;
  119. }
  120. return args.Text.ToString();
  121. }
  122. private bool ContainsAssembly(IEnumerable assemblies, string assembly)
  123. {
  124. string asmName = Path.GetFileName(assembly);
  125. foreach (string a in assemblies)
  126. {
  127. string asmName1 = Path.GetFileName(a);
  128. if (String.Compare(asmName, asmName1, true) == 0)
  129. return true;
  130. }
  131. return false;
  132. }
  133. private void AddFastReportAssemblies(IList assemblies)
  134. {
  135. foreach (Assembly assembly in RegisteredObjects.Assemblies)
  136. {
  137. string aLocation = assembly.Location;
  138. #if CROSSPLATFORM || COREWIN
  139. if (aLocation == "")
  140. {
  141. // try fix SFA in FastReport.Compat
  142. string fixedReference = CodeDomProvider.TryFixReferenceInSingeFileApp(assembly);
  143. if (!string.IsNullOrEmpty(fixedReference))
  144. aLocation = fixedReference;
  145. }
  146. #endif
  147. if (!ContainsAssembly(assemblies, aLocation))
  148. assemblies.Add(aLocation);
  149. }
  150. }
  151. private void AddReferencedAssemblies(IList assemblies, string defaultPath)
  152. {
  153. for (int i = 0; i < Report.ReferencedAssemblies.Length; i++)
  154. {
  155. string s = Report.ReferencedAssemblies[i];
  156. #if CROSSPLATFORM
  157. if (s == "System.Windows.Forms.dll")
  158. s = "FastReport.Compat";
  159. #endif
  160. // fix for old reports with "System.Windows.Forms.DataVisualization" in referenced assemblies
  161. if (s.IndexOf("System.Windows.Forms.DataVisualization") != -1)
  162. s = "FastReport.DataVisualization";
  163. #if SKIA
  164. if (s.IndexOf("FastReport.Compat") != -1)
  165. s = "FastReport.Compat.Skia";
  166. if (s.IndexOf("FastReport.DataVisualization") != -1)
  167. s = "FastReport.DataVisualization.Skia";
  168. #endif
  169. AddReferencedAssembly(assemblies, defaultPath, s);
  170. }
  171. #if SKIA
  172. AddReferencedAssembly(assemblies, defaultPath, "FastReport.SkiaDrawing");
  173. #endif
  174. // these two required for "dynamic" type support
  175. AddReferencedAssembly(assemblies, defaultPath, "System.Core");
  176. AddReferencedAssembly(assemblies, defaultPath, "Microsoft.CSharp");
  177. }
  178. private void AddReferencedAssembly(IList assemblies, string defaultPath, string assemblyName)
  179. {
  180. string location = GetFullAssemblyReference(assemblyName, defaultPath);
  181. if (location != "" && !ContainsAssembly(assemblies, location))
  182. assemblies.Add(location);
  183. }
  184. private string GetFullAssemblyReference(string relativeReference, string defaultPath)
  185. {
  186. // in .NET Core we get the AssemblyReference in FR.Compat
  187. #if !(CROSSPLATFORM || COREWIN)
  188. if (relativeReference == null || relativeReference.Trim() == "")
  189. return "";
  190. // Strip off any trailing ".dll" ".exe" if present.
  191. string dllName = relativeReference;
  192. if (string.Compare(relativeReference.Substring(relativeReference.Length - 4), ".dll", true) == 0 ||
  193. string.Compare(relativeReference.Substring(relativeReference.Length - 4), ".exe", true) == 0)
  194. dllName = relativeReference.Substring(0, relativeReference.Length - 4);
  195. // See if the required assembly is already present in our current AppDomain
  196. foreach (Assembly currAssembly in AppDomain.CurrentDomain.GetAssemblies())
  197. {
  198. if (string.Compare(currAssembly.GetName().Name, dllName, true) == 0)
  199. {
  200. // Found it, return the location as the full reference.
  201. return currAssembly.Location;
  202. }
  203. }
  204. // See if the required assembly is present in the ReferencedAssemblies but not yet loaded
  205. foreach (AssemblyName assemblyName in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
  206. {
  207. if (string.Compare(assemblyName.Name, dllName, true) == 0)
  208. {
  209. // Found it, try to load assembly and return the location as the full reference.
  210. try
  211. {
  212. return Assembly.ReflectionOnlyLoad(assemblyName.FullName).Location;
  213. }
  214. catch { }
  215. }
  216. }
  217. // See if the required assembly is present locally
  218. string path = Path.Combine(defaultPath, relativeReference);
  219. if (File.Exists(path))
  220. return path;
  221. #endif
  222. return relativeReference;
  223. }
  224. private void AddExpression(string expression, Base source, bool forceSimpleItems)
  225. {
  226. if (expression.Trim() == "" || Expressions.ContainsKey(expression))
  227. return;
  228. string expr = expression;
  229. if (expr.StartsWith("[") && expr.EndsWith("]"))
  230. expr = expr.Substring(1, expr.Length - 2);
  231. // skip simple items. Report.Calc does this.
  232. if (!forceSimpleItems)
  233. {
  234. if (DataHelper.IsSimpleColumn(Report.Dictionary, expr) ||
  235. DataHelper.IsValidParameter(Report.Dictionary, expr) ||
  236. DataHelper.IsValidTotal(Report.Dictionary, expr))
  237. return;
  238. }
  239. // handle complex expressions, relations
  240. ExpressionDescriptor descriptor = new ExpressionDescriptor(this);
  241. Expressions.Add(expression, descriptor);
  242. descriptor.MethodName = "CalcExpression";
  243. if (DataHelper.IsValidColumn(Report.Dictionary, expr))
  244. expr = "[" + expr + "]";
  245. else
  246. expr = expression;
  247. string expressionCode = ReplaceDataItems(expr);
  248. InsertItem(Report.CodeHelper.AddExpression(expression, expressionCode), source == null ? "" : source.Name);
  249. needCompile = true;
  250. }
  251. public void AddObjects()
  252. {
  253. ObjectCollection allObjects = Report.AllObjects;
  254. SortedList<string, Base> objects = new SortedList<string, Base>();
  255. // add all report objects
  256. InsertItem(Report.CodeHelper.AddField(typeof(Report), "Report") +
  257. Report.CodeHelper.AddField(typeof(ReportEngine), "Engine"), "Report");
  258. foreach (Base c in allObjects)
  259. {
  260. if (!String.IsNullOrEmpty(c.Name) && !objects.ContainsKey(c.Name))
  261. objects.Add(c.Name, c);
  262. }
  263. foreach (Base c in objects.Values)
  264. {
  265. InsertItem(Report.CodeHelper.AddField(c.GetType(), c.Name), c.Name);
  266. }
  267. // add custom script
  268. string processedCode = "";
  269. foreach (Base c in objects.Values)
  270. {
  271. string customCode = c.GetCustomScript();
  272. // avoid custom script duplicates
  273. if (!String.IsNullOrEmpty(customCode) && processedCode.IndexOf(customCode) == -1)
  274. {
  275. InsertItem(customCode, c.Name);
  276. processedCode += customCode;
  277. needCompile = true;
  278. }
  279. }
  280. }
  281. public void AddSingleExpression(string expression)
  282. {
  283. InsertItem(Report.CodeHelper.BeginCalcExpression(), "");
  284. AddExpression(expression, null, true);
  285. InsertItem(Report.CodeHelper.EndCalcExpression(), "");
  286. needCompile = true;
  287. }
  288. public void AddExpressions()
  289. {
  290. // speed up the case: lot of report objects (> 1000) and lot of data columns in the dictionary (> 10000).
  291. Report.Dictionary.CacheAllObjects = true;
  292. InsertItem(Report.CodeHelper.BeginCalcExpression(), "");
  293. ObjectCollection allObjects = Report.AllObjects;
  294. ObjectCollection l = Report.Dictionary.AllObjects;
  295. foreach (Base c in l)
  296. {
  297. allObjects.Add(c);
  298. }
  299. foreach (Base c in allObjects)
  300. {
  301. string[] expressions = c.GetExpressions();
  302. if (expressions != null)
  303. {
  304. foreach (string expr in expressions)
  305. {
  306. AddExpression(expr, c, false);
  307. }
  308. }
  309. }
  310. InsertItem(Report.CodeHelper.EndCalcExpression(), "");
  311. Report.Dictionary.CacheAllObjects = false;
  312. }
  313. public void AddFunctions()
  314. {
  315. List<FunctionInfo> list = new List<FunctionInfo>();
  316. RegisteredObjects.Functions.EnumItems(list);
  317. foreach (FunctionInfo info in list)
  318. {
  319. if (info.Function != null)
  320. {
  321. InsertItem(Report.CodeHelper.GetMethodSignatureAndBody(info.Function), "Function");
  322. }
  323. }
  324. }
  325. public string GenerateReportClass(string className)
  326. {
  327. InsertItem(Report.CodeHelper.GenerateInitializeMethod(), "");
  328. return Report.CodeHelper.ReplaceClassName(scriptText.ToString(), className);
  329. }
  330. public void Compile()
  331. {
  332. if (needCompile)
  333. {
  334. lock (compileLocker)
  335. {
  336. if (needCompile)
  337. InternalCompile();
  338. }
  339. }
  340. }
  341. private void InternalCompile()
  342. {
  343. // configure compiler options
  344. CompilerParameters cp = new CompilerParameters();
  345. AddFastReportAssemblies(cp.ReferencedAssemblies); // 2
  346. AddReferencedAssemblies(cp.ReferencedAssemblies, currentFolder); // 9
  347. ReviewReferencedAssemblies(cp.ReferencedAssemblies);
  348. cp.GenerateInMemory = true;
  349. // sometimes the system temp folder is not accessible...
  350. if (Config.TempFolder != null)
  351. cp.TempFiles = new TempFileCollection(Config.TempFolder, false);
  352. if (Config.WebMode &&
  353. Config.EnableScriptSecurity &&
  354. Config.ScriptSecurityProps.AddStubClasses)
  355. AddStubClasses();
  356. string errors = string.Empty;
  357. CompilerResults cr;
  358. bool exception = !InternalCompile(cp, out cr);
  359. for (int i = 0; exception && i < RECOMPILE_COUNT; i++)
  360. {
  361. exception = !HandleCompileErrors(cp, cr, out errors);
  362. }
  363. if (exception && errors != string.Empty)
  364. throw new CompilerException(errors);
  365. }
  366. private string GetAssemblyHash(CompilerParameters cp)
  367. {
  368. StringBuilder assemblyHashSB = new StringBuilder();
  369. foreach (string a in cp.ReferencedAssemblies)
  370. assemblyHashSB.Append(a);
  371. var script = scriptText.ToString();
  372. assemblyHashSB.Append(script);
  373. byte[] hash;
  374. using (HMACSHA1 hMACSHA1 = new HMACSHA1(Encoding.ASCII.GetBytes(shaKey)))
  375. {
  376. hash = hMACSHA1.ComputeHash(Encoding.Unicode.GetBytes(assemblyHashSB.ToString()));
  377. }
  378. return Convert.ToBase64String(hash);
  379. }
  380. /// <summary>
  381. /// Returns true, if compilation is successful
  382. /// </summary>
  383. private bool InternalCompile(CompilerParameters cp, out CompilerResults cr)
  384. {
  385. // find assembly in cache
  386. string assemblyHash = GetAssemblyHash(cp);
  387. Assembly cachedAssembly;
  388. if (FAssemblyCache.TryGetValue(assemblyHash, out cachedAssembly))
  389. {
  390. Assembly = cachedAssembly;
  391. var reportScript = Assembly.CreateInstance("FastReport.ReportScript");
  392. InitInstance(reportScript);
  393. cr = null;
  394. return true;
  395. }
  396. // compile report scripts
  397. using (CodeDomProvider provider = Report.CodeHelper.GetCodeProvider())
  398. {
  399. ScriptSecurityEventArgs ssea = new ScriptSecurityEventArgs(Report, scriptText.ToString(), Report.ReferencedAssemblies);
  400. Config.OnScriptCompile(ssea);
  401. #if CROSSPLATFORM || COREWIN
  402. provider.BeforeEmitCompilation += Config.OnBeforeScriptCompilation;
  403. #endif
  404. cr = provider.CompileAssemblyFromSource(cp, scriptText.ToString());
  405. Assembly = null;
  406. Instance = null;
  407. if (cr.Errors.Count != 0) // Compile errors
  408. return false;
  409. FAssemblyCache.TryAdd(assemblyHash, cr.CompiledAssembly);
  410. Assembly = cr.CompiledAssembly;
  411. var reportScript = Assembly.CreateInstance("FastReport.ReportScript");
  412. InitInstance(reportScript);
  413. return true;
  414. }
  415. }
  416. private string ReplaceExpression(string error, TextObjectBase text)
  417. {
  418. string result = text.Text;
  419. string[] parts = error.Split('\"');
  420. if (parts.Length == 3)
  421. {
  422. string[] expressions = (text as Base).GetExpressions();
  423. foreach (string expr in expressions)
  424. {
  425. if (expr.Contains(parts[1]))
  426. {
  427. if (!DataHelper.IsValidColumn(Report.Dictionary, expr))
  428. {
  429. string replaceString = text.Brackets[0] + expr + text.Brackets[2];
  430. if (Config.CompilerSettings.ExceptionBehaviour == CompilerExceptionBehaviour.ShowExceptionMessage ||
  431. Config.CompilerSettings.ExceptionBehaviour == CompilerExceptionBehaviour.ReplaceExpressionWithPlaceholder)
  432. {
  433. result = result.Replace(replaceString, Config.CompilerSettings.Placeholder);
  434. }
  435. else if (Config.CompilerSettings.ExceptionBehaviour == CompilerExceptionBehaviour.ReplaceExpressionWithExceptionMessage)
  436. {
  437. result = result.Replace(replaceString, error);
  438. }
  439. }
  440. }
  441. }
  442. }
  443. return result;
  444. }
  445. /// <summary>
  446. /// Handle compile errors
  447. /// </summary>
  448. /// <returns>Returns <b>true</b> if all errors were handled</returns>
  449. private bool HandleCompileErrors(CompilerParameters cp, CompilerResults cr, out string errors)
  450. {
  451. errors = string.Empty;
  452. List<string> additionalAssemblies = new List<string>(4);
  453. Regex regex;
  454. if (Config.WebMode && Config.EnableScriptSecurity)
  455. {
  456. for (int i = 0; i < cr.Errors.Count;)
  457. {
  458. CompilerError ce = cr.Errors[i];
  459. if (ce.ErrorNumber == "CS1685") // duplicate class
  460. {
  461. cr.Errors.Remove(ce);
  462. continue;
  463. }
  464. else if (ce.ErrorNumber == "CS0436") // user using a forbidden type
  465. {
  466. const string pattern = "[\"'](\\S+)[\"']";
  467. regex = new Regex(pattern, RegexOptions.Compiled);
  468. string typeName = regex.Match(ce.ErrorText).Value;
  469. const string res = "Web,ScriptSecurity,ForbiddenType";
  470. string message = Res.TryGet(res);
  471. if (string.Equals(res, message))
  472. message = "Please don't use the type " + typeName;
  473. else
  474. message = message.Replace("{typeName}", typeName); //$"Please don't use the type {typeName}";
  475. ce.ErrorText = message;
  476. }
  477. else if (ce.ErrorNumber == "CS0117") // user using a forbidden method
  478. {
  479. const string pattern = "[\"'](\\S+)[\"']";
  480. regex = new Regex(pattern, RegexOptions.Compiled);
  481. MatchCollection mathes = regex.Matches(ce.ErrorText);
  482. if (mathes.Count > 1)
  483. {
  484. string methodName = mathes[1].Value;
  485. const string res = "Web,ScriptSecurity,ForbiddenMethod";
  486. string message = Res.TryGet(res);
  487. if (string.Equals(res, message))
  488. message = "Please don't use the method " + methodName;
  489. else
  490. message = message.Replace("{methodName}", methodName); //$"Please don't use the method {methodName}";
  491. ce.ErrorText = message;
  492. }
  493. }
  494. i++;
  495. }
  496. }
  497. foreach (CompilerError ce in cr.Errors)
  498. {
  499. if (ce.ErrorNumber == "CS0012") // missing reference on assembly
  500. {
  501. // try to add reference
  502. try
  503. {
  504. // in .Net Core compiler will return other quotes
  505. #if CROSSPLATFORM || COREWIN
  506. const string quotes = "\'";
  507. #else
  508. const string quotes = "\"";
  509. #endif
  510. const string pattern = quotes + @"(\S{1,}),";
  511. regex = new Regex(pattern, RegexOptions.Compiled);
  512. string assemblyName = regex.Match(ce.ErrorText).Groups[1].Value; // Groups[1] include string without quotes and , symbols
  513. if (!additionalAssemblies.Contains(assemblyName))
  514. additionalAssemblies.Add(assemblyName);
  515. continue;
  516. }
  517. catch { }
  518. }
  519. int line = GetScriptLine(ce.Line);
  520. // error is inside own items
  521. if (line == -1)
  522. {
  523. string errObjName = GetErrorObjectName(ce.Line);
  524. if (Config.CompilerSettings.ExceptionBehaviour != CompilerExceptionBehaviour.Default)
  525. {
  526. // handle errors when name does not exist in the current context
  527. if (ce.ErrorNumber == "CS0103")
  528. {
  529. TextObjectBase text = Report.FindObject(errObjName) as TextObjectBase;
  530. text.Text = ReplaceExpression(ce.ErrorText, text);
  531. if (Config.CompilerSettings.ExceptionBehaviour == CompilerExceptionBehaviour.ShowExceptionMessage)
  532. System.Windows.Forms.MessageBox.Show(ce.ErrorText);
  533. continue;
  534. }
  535. }
  536. // handle division by zero errors
  537. if (ce.ErrorNumber == "CS0020")
  538. {
  539. TextObjectBase text = Report.FindObject(errObjName) as TextObjectBase;
  540. text.CanGrow = true;
  541. text.FillColor = Color.Red;
  542. text.Text = "DIVISION BY ZERO!";
  543. continue;
  544. }
  545. else
  546. {
  547. errors += String.Format("({0}): " + Res.Get("Messages,Error") + " {1}: {2}", new object[] { errObjName, ce.ErrorNumber, ce.ErrorText }) + "\r\n";
  548. ErrorMsg(errObjName, ce);
  549. }
  550. }
  551. else
  552. {
  553. errors += String.Format("({0},{1}): " + Res.Get("Messages,Error") + " {2}: {3}", new object[] { line, ce.Column, ce.ErrorNumber, ce.ErrorText }) + "\r\n";
  554. ErrorMsg(ce, line);
  555. }
  556. }
  557. if (additionalAssemblies.Count > 0) // need recompile
  558. return ReCompile(cp, cr, additionalAssemblies);
  559. return false;
  560. }
  561. /// <summary>
  562. /// Returns true if recompilation is successful
  563. /// </summary>
  564. private bool ReCompile(CompilerParameters cp, CompilerResults cr, IList additionalAssemblies)
  565. {
  566. // try to load missing assemblies
  567. foreach (string assemblyName in additionalAssemblies)
  568. {
  569. AddReferencedAssembly(cp.ReferencedAssemblies, currentFolder, assemblyName);
  570. }
  571. return InternalCompile(cp, out cr);
  572. }
  573. public void InitInstance(object instance)
  574. {
  575. this.Instance = instance;
  576. InitFields();
  577. }
  578. public bool ContainsExpression(string expr)
  579. {
  580. return Expressions.ContainsKey(expr);
  581. }
  582. public object CalcExpression(string expr, Variant value)
  583. {
  584. FastReport.Code.ExpressionDescriptor expressionDescriptor = Expressions[expr] as FastReport.Code.ExpressionDescriptor;
  585. if (expressionDescriptor != null)
  586. return expressionDescriptor.Invoke(new object[] { expr, value });
  587. else
  588. return null;
  589. }
  590. public object InvokeMethod(string name, object[] parms)
  591. {
  592. if (String.IsNullOrEmpty(name))
  593. return null;
  594. string exprName = "method_" + name;
  595. if (!ContainsExpression(exprName))
  596. {
  597. ExpressionDescriptor descriptor = new ExpressionDescriptor(this);
  598. Expressions.Add(exprName, descriptor);
  599. descriptor.MethodName = name;
  600. }
  601. try
  602. {
  603. return (Expressions[exprName] as ExpressionDescriptor).Invoke(parms);
  604. }
  605. catch (TargetInvocationException ex)
  606. {
  607. throw (ex.InnerException); // ex now stores the original exception
  608. }
  609. }
  610. public AssemblyDescriptor(Report report, string scriptText)
  611. {
  612. this.Report = report;
  613. this.scriptText = new FastString(scriptText);
  614. Expressions = new Hashtable();
  615. sourcePositions = new List<SourcePosition>();
  616. insertPos = Report.CodeHelper.GetPositionToInsertOwnItems(scriptText);
  617. if (insertPos == -1)
  618. {
  619. string msg = Res.Get("Messages,ClassError");
  620. ErrorMsg(msg);
  621. throw new CompilerException(msg);
  622. }
  623. else
  624. {
  625. string[] lines = scriptText.Substring(0, insertPos).Split('\r');
  626. insertLine = lines.Length;
  627. if (scriptText != Report.CodeHelper.EmptyScript())
  628. needCompile = true;
  629. }
  630. // set the current folder
  631. currentFolder = Config.ApplicationFolder;
  632. if (Config.WebMode)
  633. {
  634. try
  635. {
  636. string bin_directory = Path.Combine(currentFolder, "Bin");
  637. if (Directory.Exists(bin_directory))
  638. currentFolder = bin_directory;
  639. }
  640. catch
  641. {
  642. }
  643. }
  644. // Commented by Samuray
  645. //Directory.SetCurrentDirectory(currentFolder);
  646. }
  647. static AssemblyDescriptor()
  648. {
  649. FAssemblyCache = new ConcurrentDictionary<string, Assembly>();
  650. compileLocker = new object();
  651. }
  652. private class SourcePosition
  653. {
  654. public readonly string sourceObject;
  655. public readonly int start;
  656. public readonly int end;
  657. public SourcePosition(string obj, int start, int end)
  658. {
  659. sourceObject = obj;
  660. this.start = start;
  661. this.end = end;
  662. }
  663. }
  664. }
  665. }