ShadowRealm.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. using System.Diagnostics.CodeAnalysis;
  2. using Esprima;
  3. using Esprima.Ast;
  4. using Esprima.Utils;
  5. using Jint.Native.Object;
  6. using Jint.Native.Promise;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Descriptors;
  9. using Jint.Runtime.Environments;
  10. using Jint.Runtime.Interop;
  11. using Jint.Runtime.Interpreter;
  12. using Jint.Runtime.Interpreter.Statements;
  13. using Jint.Runtime.Modules;
  14. using Environment = Jint.Runtime.Environments.Environment;
  15. namespace Jint.Native.ShadowRealm;
  16. /// <summary>
  17. /// https://tc39.es/proposal-shadowrealm/#sec-properties-of-shadowrealm-instances
  18. /// </summary>
  19. #pragma warning disable MA0049
  20. public sealed class ShadowRealm : ObjectInstance
  21. #pragma warning restore MA0049
  22. {
  23. private readonly JavaScriptParser _parser;
  24. internal readonly Realm _shadowRealm;
  25. private readonly ExecutionContext _executionContext;
  26. internal ShadowRealm(Engine engine, ExecutionContext executionContext, Realm shadowRealm) : base(engine)
  27. {
  28. _parser = new(new ParserOptions
  29. {
  30. Tolerant = false,
  31. RegexTimeout = engine.Options.Constraints.RegexTimeout
  32. });
  33. _executionContext = executionContext;
  34. _shadowRealm = shadowRealm;
  35. }
  36. public JsValue Evaluate(string sourceText)
  37. {
  38. var callerRealm = _engine.Realm;
  39. return PerformShadowRealmEval(sourceText, callerRealm);
  40. }
  41. public JsValue Evaluate(Script script)
  42. {
  43. var callerRealm = _engine.Realm;
  44. return PerformShadowRealmEval(script, callerRealm);
  45. }
  46. public JsValue ImportValue(string specifier, string exportName)
  47. {
  48. var callerRealm = _engine.Realm;
  49. var value = ShadowRealmImportValue(specifier, exportName, callerRealm);
  50. _engine.RunAvailableContinuations();
  51. return value;
  52. }
  53. [RequiresUnreferencedCode("User supplied delegate")]
  54. public ShadowRealm SetValue(string name, Delegate value)
  55. {
  56. _shadowRealm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(_engine, value), PropertyFlag.NonEnumerable));
  57. return this;
  58. }
  59. public ShadowRealm SetValue(string name, string value)
  60. {
  61. return SetValue(name, JsString.Create(value));
  62. }
  63. public ShadowRealm SetValue(string name, double value)
  64. {
  65. return SetValue(name, JsNumber.Create(value));
  66. }
  67. public ShadowRealm SetValue(string name, int value)
  68. {
  69. return SetValue(name, JsNumber.Create(value));
  70. }
  71. public ShadowRealm SetValue(string name, bool value)
  72. {
  73. return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
  74. }
  75. public ShadowRealm SetValue(string name, JsValue value)
  76. {
  77. _shadowRealm.GlobalObject.Set(name, value);
  78. return this;
  79. }
  80. public ShadowRealm SetValue(string name, object obj)
  81. {
  82. var value = obj is Type t
  83. ? TypeReference.CreateTypeReference(_engine, t)
  84. : JsValue.FromObject(_engine, obj);
  85. return SetValue(name, value);
  86. }
  87. /// <summary>
  88. /// https://tc39.es/proposal-shadowrealm/#sec-performshadowrealmeval
  89. /// </summary>
  90. internal JsValue PerformShadowRealmEval(string sourceText, Realm callerRealm)
  91. {
  92. var evalRealm = _shadowRealm;
  93. _engine._host.EnsureCanCompileStrings(callerRealm, evalRealm);
  94. Script script;
  95. try
  96. {
  97. script = _parser.ParseScript(sourceText, source: null, _engine._isStrict);
  98. }
  99. catch (ParserException e)
  100. {
  101. if (string.Equals(e.Description, Messages.InvalidLHSInAssignment, StringComparison.Ordinal))
  102. {
  103. ExceptionHelper.ThrowReferenceError(callerRealm, Messages.InvalidLHSInAssignment);
  104. }
  105. else
  106. {
  107. ExceptionHelper.ThrowSyntaxError(callerRealm, e.Message);
  108. }
  109. return default;
  110. }
  111. return PerformShadowRealmEvalInternal(script, callerRealm);
  112. }
  113. internal JsValue PerformShadowRealmEval(Script script, Realm callerRealm)
  114. {
  115. var evalRealm = _shadowRealm;
  116. _engine._host.EnsureCanCompileStrings(callerRealm, evalRealm);
  117. return PerformShadowRealmEvalInternal(script, callerRealm);
  118. }
  119. internal JsValue PerformShadowRealmEvalInternal(Script script, Realm callerRealm)
  120. {
  121. var evalRealm = _shadowRealm;
  122. ref readonly var body = ref script.Body;
  123. if (body.Count == 0)
  124. {
  125. return Undefined;
  126. }
  127. var validator = new ShadowScriptValidator(callerRealm);
  128. validator.Visit(script);
  129. var strictEval = script.Strict;
  130. var runningContext = _engine.ExecutionContext;
  131. var lexEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, evalRealm.GlobalEnv);
  132. Environment varEnv = evalRealm.GlobalEnv;
  133. if (strictEval)
  134. {
  135. varEnv = lexEnv;
  136. }
  137. // If runningContext is not already suspended, suspend runningContext.
  138. var evalContext = new ExecutionContext(null, lexEnv, varEnv, null, evalRealm, null);
  139. _engine.EnterExecutionContext(evalContext);
  140. Completion result;
  141. try
  142. {
  143. _engine.EvalDeclarationInstantiation(script, varEnv, lexEnv, privateEnv: null, strictEval);
  144. using (new StrictModeScope(strictEval, force: true))
  145. {
  146. result = new JintScript(script).Execute(new EvaluationContext(_engine));
  147. }
  148. if (result.Type == CompletionType.Throw)
  149. {
  150. ThrowCrossRealmError(callerRealm, result.GetValueOrDefault().ToString());
  151. }
  152. }
  153. finally
  154. {
  155. _engine.LeaveExecutionContext();
  156. }
  157. return GetWrappedValue(callerRealm, callerRealm, result.Value);
  158. }
  159. /// <summary>
  160. /// https://tc39.es/proposal-shadowrealm/#sec-getwrappedvalue
  161. /// </summary>
  162. private static JsValue GetWrappedValue(Realm throwerRealm, Realm callerRealm, JsValue value)
  163. {
  164. if (value is ObjectInstance oi)
  165. {
  166. if (!oi.IsCallable)
  167. {
  168. ThrowCrossRealmError(throwerRealm, "Result is not callable");
  169. }
  170. return WrappedFunctionCreate(throwerRealm, callerRealm, oi);
  171. }
  172. return value;
  173. }
  174. /// <summary>
  175. /// https://tc39.es/proposal-shadowrealm/#sec-wrappedfunctioncreate
  176. /// </summary>
  177. private static WrappedFunction WrappedFunctionCreate(Realm throwerRealm, Realm callerRealm, ObjectInstance target)
  178. {
  179. var wrapped = new WrappedFunction(callerRealm.GlobalEnv._engine, callerRealm, target);
  180. try
  181. {
  182. CopyNameAndLength(wrapped, target);
  183. }
  184. catch (JavaScriptException ex)
  185. {
  186. ThrowCrossRealmError(throwerRealm, ex.Message);
  187. }
  188. return wrapped;
  189. }
  190. /// <summary>
  191. /// https://tc39.es/proposal-shadowrealm/#sec-copynameandlength
  192. /// </summary>
  193. private static void CopyNameAndLength(WrappedFunction f, ObjectInstance target, string? prefix = null, int argCount = 0)
  194. {
  195. var L = JsNumber.PositiveZero;
  196. var targetHasLength = target.HasOwnProperty("length");
  197. if (targetHasLength)
  198. {
  199. var targetLen = target.Get("length");
  200. if (targetLen is JsNumber number)
  201. {
  202. if (number.IsPositiveInfinity())
  203. {
  204. L = number;
  205. }
  206. else if (number.IsNegativeInfinity())
  207. {
  208. L = JsNumber.PositiveZero;
  209. }
  210. else
  211. {
  212. var targetLenAsInt = TypeConverter.ToIntegerOrInfinity(targetLen);
  213. L = JsNumber.Create(System.Math.Max(targetLenAsInt - argCount, 0));
  214. }
  215. }
  216. }
  217. f.SetFunctionLength(L);
  218. var targetName = target.Get(CommonProperties.Name);
  219. if (!targetName.IsString())
  220. {
  221. targetName = JsString.Empty;
  222. }
  223. f.SetFunctionName(targetName, prefix);
  224. }
  225. /// <summary>
  226. /// https://tc39.es/proposal-shadowrealm/#sec-shadowrealmimportvalue
  227. /// </summary>
  228. internal JsValue ShadowRealmImportValue(
  229. string specifierString,
  230. string exportNameString,
  231. Realm callerRealm)
  232. {
  233. var innerCapability = PromiseConstructor.NewPromiseCapability(_engine, _engine.Realm.Intrinsics.Promise);
  234. // var runningContext = _engine.ExecutionContext;
  235. // 4. If runningContext is not already suspended, suspend runningContext.
  236. _engine.EnterExecutionContext(_executionContext);
  237. _engine._host.LoadImportedModule(null, new ModuleRequest(specifierString, []), innerCapability);
  238. _engine.LeaveExecutionContext();
  239. var onFulfilled = new StepsFunction(_engine, callerRealm, exportNameString);
  240. var promiseCapability = PromiseConstructor.NewPromiseCapability(_engine, _engine.Realm.Intrinsics.Promise);
  241. var value = PromiseOperations.PerformPromiseThen(_engine, (JsPromise) innerCapability.PromiseInstance, onFulfilled, callerRealm.Intrinsics.ThrowTypeError, promiseCapability);
  242. return value;
  243. }
  244. private sealed class StepsFunction : Function.Function
  245. {
  246. private readonly string _exportNameString;
  247. public StepsFunction(Engine engine, Realm realm, string exportNameString) : base(engine, realm, JsString.Empty)
  248. {
  249. _exportNameString = exportNameString;
  250. SetFunctionLength(JsNumber.PositiveOne);
  251. }
  252. protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
  253. {
  254. var exports = (ModuleNamespace) arguments.At(0);
  255. var f = this;
  256. var s = _exportNameString;
  257. var hasOwn = exports.HasOwnProperty(s);
  258. if (!hasOwn)
  259. {
  260. ExceptionHelper.ThrowTypeError(_realm, $"export name {s} missing");
  261. }
  262. var value = exports.Get(s);
  263. var realm = f._realm;
  264. return GetWrappedValue(_engine.Realm, realm, value);
  265. }
  266. }
  267. private static ShadowRealm ValidateShadowRealmObject(Realm callerRealm, JsValue thisObj)
  268. {
  269. var instance = thisObj as ShadowRealm;
  270. if (instance is null)
  271. {
  272. ExceptionHelper.ThrowTypeError(callerRealm, "object must be a ShadowRealm");
  273. }
  274. return instance;
  275. }
  276. private static void ThrowCrossRealmError(Realm callerRealm, string message)
  277. {
  278. ExceptionHelper.ThrowTypeError(callerRealm, "Cross-Realm Error: " + message);
  279. }
  280. private sealed class WrappedFunction : Function.Function
  281. {
  282. private readonly ObjectInstance _wrappedTargetFunction;
  283. public WrappedFunction(
  284. Engine engine,
  285. Realm callerRealm,
  286. ObjectInstance wrappedTargetFunction) : base(engine, callerRealm, null)
  287. {
  288. _wrappedTargetFunction = wrappedTargetFunction;
  289. _prototype = callerRealm.Intrinsics.Function.PrototypeObject;
  290. }
  291. /// <summary>
  292. /// https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist
  293. /// </summary>
  294. protected internal override JsValue Call(JsValue thisArgument, JsValue[] arguments)
  295. {
  296. var target = _wrappedTargetFunction;
  297. var targetRealm = GetFunctionRealm(target);
  298. var callerRealm = GetFunctionRealm(this);
  299. var wrappedArgs = new JsValue[arguments.Length];
  300. for (var i = 0; i < arguments.Length; i++)
  301. {
  302. wrappedArgs[i] = GetWrappedValue(callerRealm, targetRealm, arguments[i]);
  303. }
  304. var wrappedThisArgument = GetWrappedValue(callerRealm, targetRealm, thisArgument);
  305. JsValue result;
  306. try
  307. {
  308. result = target.Call(wrappedThisArgument, wrappedArgs);
  309. }
  310. catch (JavaScriptException ex)
  311. {
  312. ThrowCrossRealmError(_realm, ex.Message);
  313. return default!;
  314. }
  315. return GetWrappedValue(callerRealm, callerRealm, result);
  316. }
  317. }
  318. /// <summary>
  319. /// If body Contains NewTarget is true, throw a SyntaxError exception.
  320. /// If body Contains SuperProperty is true, throw a SyntaxError exception.
  321. /// If body Contains SuperCall is true, throw a SyntaxError exception.
  322. /// </summary>
  323. private sealed class ShadowScriptValidator : AstVisitor
  324. {
  325. private readonly Realm _realm;
  326. public ShadowScriptValidator(Realm realm)
  327. {
  328. _realm = realm;
  329. }
  330. protected override object? VisitSuper(Super super)
  331. {
  332. ExceptionHelper.ThrowTypeError(_realm, "Shadow realm code cannot contain super");
  333. return null;
  334. }
  335. }
  336. }