ShadowRealm.cs 13 KB

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