LoadModule.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Disable warnings about XML documentation
  2. #pragma warning disable 1591
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using MoonSharp.Interpreter.Execution;
  8. namespace MoonSharp.Interpreter.CoreLib
  9. {
  10. /// <summary>
  11. /// Class implementing loading Lua functions like 'require', 'load', etc.
  12. /// </summary>
  13. [MoonSharpModule]
  14. public class LoadModule
  15. {
  16. public static void MoonSharpInit(Table globalTable, Table ioTable)
  17. {
  18. DynValue package = globalTable.Get("package");
  19. if (package.IsNil())
  20. {
  21. package = DynValue.NewTable(globalTable.OwnerScript);
  22. globalTable["package"] = package;
  23. }
  24. else if (package.Type != DataType.Table)
  25. {
  26. throw new InternalErrorException("'package' global variable was found and it is not a table");
  27. }
  28. #if PCL
  29. string cfg = "\\\n;\n?\n!\n-\n";
  30. #else
  31. string cfg = System.IO.Path.DirectorySeparatorChar + "\n;\n?\n!\n-\n";
  32. #endif
  33. package.Table.Set("config", DynValue.NewString(cfg));
  34. }
  35. // load (ld [, source [, mode [, env]]])
  36. // ----------------------------------------------------------------
  37. // Loads a chunk.
  38. //
  39. // If ld is a string, the chunk is this string.
  40. //
  41. // If there are no syntactic errors, returns the compiled chunk as a function;
  42. // otherwise, returns nil plus the error message.
  43. //
  44. // source is used as the source of the chunk for error messages and debug
  45. // information (see §4.9). When absent, it defaults to ld, if ld is a string,
  46. // or to "=(load)" otherwise.
  47. //
  48. // The string mode is ignored, and assumed to be "t";
  49. [MoonSharpModuleMethod]
  50. public static DynValue load(ScriptExecutionContext executionContext, CallbackArguments args)
  51. {
  52. return load_impl(executionContext, args, null);
  53. }
  54. // loadsafe (ld [, source [, mode [, env]]])
  55. // ----------------------------------------------------------------
  56. // Same as load, except that "env" defaults to the current environment of the function
  57. // calling load, instead of the actual global environment.
  58. [MoonSharpModuleMethod]
  59. public static DynValue loadsafe(ScriptExecutionContext executionContext, CallbackArguments args)
  60. {
  61. return load_impl(executionContext, args, GetSafeDefaultEnv(executionContext));
  62. }
  63. public static DynValue load_impl(ScriptExecutionContext executionContext, CallbackArguments args, Table defaultEnv)
  64. {
  65. try
  66. {
  67. Script S = executionContext.GetScript();
  68. DynValue ld = args[0];
  69. string script = "";
  70. if (ld.Type == DataType.Function)
  71. {
  72. while (true)
  73. {
  74. DynValue ret = executionContext.GetScript().Call(ld);
  75. if (ret.Type == DataType.String && ret.String.Length > 0)
  76. script += ret.String;
  77. else if (ret.IsNil())
  78. break;
  79. else
  80. return DynValue.NewTuple(DynValue.Nil, DynValue.NewString("reader function must return a string"));
  81. }
  82. }
  83. else if (ld.Type == DataType.String)
  84. {
  85. script = ld.String;
  86. }
  87. else
  88. {
  89. args.AsType(0, "load", DataType.Function, false);
  90. }
  91. DynValue source = args.AsType(1, "load", DataType.String, true);
  92. DynValue env = args.AsType(3, "load", DataType.Table, true);
  93. DynValue fn = S.LoadString(script,
  94. !env.IsNil() ? env.Table : defaultEnv,
  95. !source.IsNil() ? source.String : "=(load)");
  96. return fn;
  97. }
  98. catch (SyntaxErrorException ex)
  99. {
  100. return DynValue.NewTuple(DynValue.Nil, DynValue.NewString(ex.DecoratedMessage ?? ex.Message));
  101. }
  102. }
  103. // loadfile ([filename [, mode [, env]]])
  104. // ----------------------------------------------------------------
  105. // Similar to load, but gets the chunk from file filename or from the standard input,
  106. // if no file name is given. INCOMPAT: stdin not supported, mode ignored
  107. [MoonSharpModuleMethod]
  108. public static DynValue loadfile(ScriptExecutionContext executionContext, CallbackArguments args)
  109. {
  110. return loadfile_impl(executionContext, args, null);
  111. }
  112. // loadfile ([filename [, mode [, env]]])
  113. // ----------------------------------------------------------------
  114. // Same as loadfile, except that "env" defaults to the current environment of the function
  115. // calling load, instead of the actual global environment.
  116. [MoonSharpModuleMethod]
  117. public static DynValue loadfilesafe(ScriptExecutionContext executionContext, CallbackArguments args)
  118. {
  119. return loadfile_impl(executionContext, args, GetSafeDefaultEnv(executionContext));
  120. }
  121. private static DynValue loadfile_impl(ScriptExecutionContext executionContext, CallbackArguments args, Table defaultEnv)
  122. {
  123. try
  124. {
  125. Script S = executionContext.GetScript();
  126. DynValue filename = args.AsType(0, "loadfile", DataType.String, false);
  127. DynValue env = args.AsType(2, "loadfile", DataType.Table, true);
  128. DynValue fn = S.LoadFile(filename.String, env.IsNil() ? defaultEnv : env.Table);
  129. return fn;
  130. }
  131. catch (SyntaxErrorException ex)
  132. {
  133. return DynValue.NewTuple(DynValue.Nil, DynValue.NewString(ex.DecoratedMessage ?? ex.Message));
  134. }
  135. }
  136. private static Table GetSafeDefaultEnv(ScriptExecutionContext executionContext)
  137. {
  138. Table env = executionContext.CurrentGlobalEnv;
  139. if (env == null)
  140. throw new ScriptRuntimeException("current environment cannot be backtracked.");
  141. return env;
  142. }
  143. //dofile ([filename])
  144. //--------------------------------------------------------------------------------------------------------------
  145. //Opens the named file and executes its contents as a Lua chunk. When called without arguments,
  146. //dofile executes the contents of the standard input (stdin). Returns all values returned by the chunk.
  147. //In case of errors, dofile propagates the error to its caller (that is, dofile does not run in protected mode).
  148. [MoonSharpModuleMethod]
  149. public static DynValue dofile(ScriptExecutionContext executionContext, CallbackArguments args)
  150. {
  151. try
  152. {
  153. Script S = executionContext.GetScript();
  154. DynValue v = args.AsType(0, "dofile", DataType.String, false);
  155. DynValue fn = S.LoadFile(v.String);
  156. return DynValue.NewTailCallReq(fn); // tail call to dofile
  157. }
  158. catch (SyntaxErrorException ex)
  159. {
  160. throw new ScriptRuntimeException(ex);
  161. }
  162. }
  163. //require (modname)
  164. //----------------------------------------------------------------------------------------------------------------
  165. //Loads the given module. The function starts by looking into the package.loaded table to determine whether
  166. //modname is already loaded. If it is, then require returns the value stored at package.loaded[modname].
  167. //Otherwise, it tries to find a loader for the module.
  168. //
  169. //To find a loader, require is guided by the package.loaders array. By changing this array, we can change
  170. //how require looks for a module. The following explanation is based on the default configuration for package.loaders.
  171. //
  172. //First require queries package.preload[modname]. If it has a value, this value (which should be a function)
  173. //is the loader. Otherwise require searches for a Lua loader using the path stored in package.path.
  174. //If that also fails, it searches for a C loader using the path stored in package.cpath. If that also fails,
  175. //it tries an all-in-one loader (see package.loaders).
  176. //
  177. //Once a loader is found, require calls the loader with a single argument, modname. If the loader returns any value,
  178. //require assigns the returned value to package.loaded[modname]. If the loader returns no value and has not assigned
  179. //any value to package.loaded[modname], then require assigns true to this entry. In any case, require returns the
  180. //final value of package.loaded[modname].
  181. //
  182. //If there is any error loading or running the module, or if it cannot find any loader for the module, then require
  183. //signals an error.
  184. [MoonSharpModuleMethod]
  185. public static DynValue __require_clr_impl(ScriptExecutionContext executionContext, CallbackArguments args)
  186. {
  187. Script S = executionContext.GetScript();
  188. DynValue v = args.AsType(0, "__require_clr_impl", DataType.String, false);
  189. DynValue fn = S.RequireModule(v.String);
  190. return fn; // tail call to dofile
  191. }
  192. [MoonSharpModuleMethod]
  193. public const string require = @"
  194. function(modulename)
  195. if (package == nil) then package = { }; end
  196. if (package.loaded == nil) then package.loaded = { }; end
  197. local m = package.loaded[modulename];
  198. if (m ~= nil) then
  199. return m;
  200. end
  201. local func = __require_clr_impl(modulename);
  202. local res = func();
  203. package.loaded[modulename] = res;
  204. return res;
  205. end";
  206. }
  207. }