ShadowRealm.cs 10 KB

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