123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- using System.Diagnostics.CodeAnalysis;
- using Jint.Native.Object;
- using Jint.Native.Promise;
- using Jint.Runtime;
- using Jint.Runtime.Descriptors;
- using Jint.Runtime.Environments;
- using Jint.Runtime.Interop;
- using Jint.Runtime.Interpreter;
- using Jint.Runtime.Interpreter.Statements;
- using Jint.Runtime.Modules;
- using Environment = Jint.Runtime.Environments.Environment;
- namespace Jint.Native.ShadowRealm;
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-properties-of-shadowrealm-instances
- /// </summary>
- #pragma warning disable MA0049
- public sealed class ShadowRealm : ObjectInstance
- #pragma warning restore MA0049
- {
- internal readonly Realm _shadowRealm;
- private readonly ExecutionContext _executionContext;
- internal ShadowRealm(Engine engine, in ExecutionContext executionContext, Realm shadowRealm) : base(engine)
- {
- _executionContext = executionContext;
- _shadowRealm = shadowRealm;
- }
- public JsValue Evaluate(string sourceText, ScriptParsingOptions? parsingOptions = null)
- {
- var callerRealm = _engine.Realm;
- var parserOptions = parsingOptions?.GetParserOptions() ?? _engine.GetActiveParserOptions();
- var parser = _engine.GetParserFor(parserOptions);
- return PerformShadowRealmEval(sourceText, parserOptions, parser, callerRealm);
- }
- public JsValue Evaluate(in Prepared<Script> preparedScript)
- {
- if (!preparedScript.IsValid)
- {
- ExceptionHelper.ThrowInvalidPreparedScriptArgumentException(nameof(preparedScript));
- }
- var callerRealm = _engine.Realm;
- return PerformShadowRealmEval(preparedScript, callerRealm);
- }
- public JsValue ImportValue(string specifier, string exportName)
- {
- var callerRealm = _engine.Realm;
- var value = ShadowRealmImportValue(specifier, exportName, callerRealm);
- _engine.RunAvailableContinuations();
- return value;
- }
- [RequiresUnreferencedCode("User supplied delegate")]
- public ShadowRealm SetValue(string name, Delegate value)
- {
- _shadowRealm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(_engine, value), PropertyFlag.NonEnumerable));
- return this;
- }
- public ShadowRealm SetValue(string name, string value)
- {
- return SetValue(name, JsString.Create(value));
- }
- public ShadowRealm SetValue(string name, double value)
- {
- return SetValue(name, JsNumber.Create(value));
- }
- public ShadowRealm SetValue(string name, int value)
- {
- return SetValue(name, JsNumber.Create(value));
- }
- public ShadowRealm SetValue(string name, bool value)
- {
- return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
- }
- public ShadowRealm SetValue(string name, JsValue value)
- {
- _shadowRealm.GlobalObject.Set(name, value);
- return this;
- }
- public ShadowRealm SetValue(string name, object obj)
- {
- var value = obj is Type t
- ? TypeReference.CreateTypeReference(_engine, t)
- : JsValue.FromObject(_engine, obj);
- return SetValue(name, value);
- }
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-performshadowrealmeval
- /// </summary>
- internal JsValue PerformShadowRealmEval(string sourceText, ParserOptions parserOptions, Parser parser, Realm callerRealm)
- {
- var evalRealm = _shadowRealm;
- _engine._host.EnsureCanCompileStrings(callerRealm, evalRealm);
- Script script;
- try
- {
- script = parser.ParseScriptGuarded(callerRealm, sourceText, strict: _engine._isStrict);
- }
- catch (ParseErrorException e)
- {
- if (string.Equals(e.Error.Code, "InvalidLhsInAssignment", StringComparison.Ordinal))
- {
- ExceptionHelper.ThrowReferenceError(callerRealm, e.Description);
- }
- else
- {
- ExceptionHelper.ThrowSyntaxError(callerRealm, e.Message);
- }
- return default;
- }
- return PerformShadowRealmEvalInternal(new Prepared<Script>(script, parserOptions), callerRealm);
- }
- internal JsValue PerformShadowRealmEval(in Prepared<Script> preparedScript, Realm callerRealm)
- {
- var evalRealm = _shadowRealm;
- _engine._host.EnsureCanCompileStrings(callerRealm, evalRealm);
- return PerformShadowRealmEvalInternal(preparedScript, callerRealm);
- }
- internal JsValue PerformShadowRealmEvalInternal(in Prepared<Script> preparedScript, Realm callerRealm)
- {
- var evalRealm = _shadowRealm;
- var script = preparedScript.Program!;
- ref readonly var body = ref script.Body;
- if (body.Count == 0)
- {
- return Undefined;
- }
- var validator = new ShadowScriptValidator(callerRealm);
- validator.Visit(script);
- var strictEval = script.Strict;
- var runningContext = _engine.ExecutionContext;
- var lexEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, evalRealm.GlobalEnv);
- Environment varEnv = evalRealm.GlobalEnv;
- if (strictEval)
- {
- varEnv = lexEnv;
- }
- // If runningContext is not already suspended, suspend runningContext.
- var evalContext = new ExecutionContext(null, lexEnv, varEnv, null, evalRealm, null, parserOptions: preparedScript.ParserOptions);
- _engine.EnterExecutionContext(evalContext);
- Completion result;
- try
- {
- _engine.EvalDeclarationInstantiation(script, varEnv, lexEnv, privateEnv: null, strictEval);
- using (new StrictModeScope(strictEval, force: true))
- {
- // _activeEvaluationContext must be set or e.g. a nested eval could lead to NullReferenceException...
- // TODO: is this correct or should we join the current EvaluationContext if any?
- var originalEvaluationContext = _engine._activeEvaluationContext;
- _engine._activeEvaluationContext = new EvaluationContext(_engine);
- try
- {
- result = new JintScript(script).Execute(_engine._activeEvaluationContext);
- }
- finally
- {
- _engine._activeEvaluationContext = originalEvaluationContext;
- }
- }
- if (result.Type == CompletionType.Throw)
- {
- ThrowCrossRealmError(callerRealm, result.GetValueOrDefault().ToString());
- }
- }
- finally
- {
- _engine.LeaveExecutionContext();
- }
- return GetWrappedValue(callerRealm, callerRealm, result.Value);
- }
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-getwrappedvalue
- /// </summary>
- private static JsValue GetWrappedValue(Realm throwerRealm, Realm callerRealm, JsValue value)
- {
- if (value is ObjectInstance oi)
- {
- if (!oi.IsCallable)
- {
- ThrowCrossRealmError(throwerRealm, "Result is not callable");
- }
- return WrappedFunctionCreate(throwerRealm, callerRealm, oi);
- }
- return value;
- }
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-wrappedfunctioncreate
- /// </summary>
- private static WrappedFunction WrappedFunctionCreate(Realm throwerRealm, Realm callerRealm, ObjectInstance target)
- {
- var wrapped = new WrappedFunction(callerRealm.GlobalEnv._engine, callerRealm, target);
- try
- {
- CopyNameAndLength(wrapped, target);
- }
- catch (JavaScriptException ex)
- {
- ThrowCrossRealmError(throwerRealm, ex.Message);
- }
- return wrapped;
- }
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-copynameandlength
- /// </summary>
- private static void CopyNameAndLength(WrappedFunction f, ObjectInstance target, string? prefix = null, int argCount = 0)
- {
- var L = JsNumber.PositiveZero;
- var targetHasLength = target.HasOwnProperty("length");
- if (targetHasLength)
- {
- var targetLen = target.Get("length");
- if (targetLen is JsNumber number)
- {
- if (number.IsPositiveInfinity())
- {
- L = number;
- }
- else if (number.IsNegativeInfinity())
- {
- L = JsNumber.PositiveZero;
- }
- else
- {
- var targetLenAsInt = TypeConverter.ToIntegerOrInfinity(targetLen);
- L = JsNumber.Create(System.Math.Max(targetLenAsInt - argCount, 0));
- }
- }
- }
- f.SetFunctionLength(L);
- var targetName = target.Get(CommonProperties.Name);
- if (!targetName.IsString())
- {
- targetName = JsString.Empty;
- }
- f.SetFunctionName(targetName, prefix);
- }
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-shadowrealmimportvalue
- /// </summary>
- internal JsValue ShadowRealmImportValue(
- string specifierString,
- string exportNameString,
- Realm callerRealm)
- {
- var innerCapability = PromiseConstructor.NewPromiseCapability(_engine, _engine.Realm.Intrinsics.Promise);
- // var runningContext = _engine.ExecutionContext;
- // 4. If runningContext is not already suspended, suspend runningContext.
- _engine.EnterExecutionContext(_executionContext);
- _engine._host.LoadImportedModule(null, new ModuleRequest(specifierString, []), innerCapability);
- _engine.LeaveExecutionContext();
- var onFulfilled = new StepsFunction(_engine, callerRealm, exportNameString);
- var promiseCapability = PromiseConstructor.NewPromiseCapability(_engine, _engine.Realm.Intrinsics.Promise);
- var value = PromiseOperations.PerformPromiseThen(_engine, (JsPromise) innerCapability.PromiseInstance, onFulfilled, callerRealm.Intrinsics.ThrowTypeError, promiseCapability);
- return value;
- }
- private sealed class StepsFunction : Function.Function
- {
- private readonly string _exportNameString;
- public StepsFunction(Engine engine, Realm realm, string exportNameString) : base(engine, realm, JsString.Empty)
- {
- _exportNameString = exportNameString;
- SetFunctionLength(JsNumber.PositiveOne);
- }
- protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
- {
- var exports = (ModuleNamespace) arguments.At(0);
- var f = this;
- var s = _exportNameString;
- var hasOwn = exports.HasOwnProperty(s);
- if (!hasOwn)
- {
- ExceptionHelper.ThrowTypeError(_realm, $"export name {s} missing");
- }
- var value = exports.Get(s);
- var realm = f._realm;
- return GetWrappedValue(_engine.Realm, realm, value);
- }
- }
- private static ShadowRealm ValidateShadowRealmObject(Realm callerRealm, JsValue thisObj)
- {
- var instance = thisObj as ShadowRealm;
- if (instance is null)
- {
- ExceptionHelper.ThrowTypeError(callerRealm, "object must be a ShadowRealm");
- }
- return instance;
- }
- private static void ThrowCrossRealmError(Realm callerRealm, string message)
- {
- ExceptionHelper.ThrowTypeError(callerRealm, "Cross-Realm Error: " + message);
- }
- private sealed class WrappedFunction : Function.Function
- {
- private readonly ObjectInstance _wrappedTargetFunction;
- public WrappedFunction(
- Engine engine,
- Realm callerRealm,
- ObjectInstance wrappedTargetFunction) : base(engine, callerRealm, null)
- {
- _wrappedTargetFunction = wrappedTargetFunction;
- _prototype = callerRealm.Intrinsics.Function.PrototypeObject;
- }
- /// <summary>
- /// https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist
- /// </summary>
- protected internal override JsValue Call(JsValue thisArgument, JsValue[] arguments)
- {
- var target = _wrappedTargetFunction;
- var targetRealm = GetFunctionRealm(target);
- var callerRealm = GetFunctionRealm(this);
- var wrappedArgs = new JsValue[arguments.Length];
- for (var i = 0; i < arguments.Length; i++)
- {
- wrappedArgs[i] = GetWrappedValue(callerRealm, targetRealm, arguments[i]);
- }
- var wrappedThisArgument = GetWrappedValue(callerRealm, targetRealm, thisArgument);
- JsValue result;
- try
- {
- result = target.Call(wrappedThisArgument, wrappedArgs);
- }
- catch (JavaScriptException ex)
- {
- ThrowCrossRealmError(_realm, ex.Message);
- return default!;
- }
- return GetWrappedValue(callerRealm, callerRealm, result);
- }
- }
- /// <summary>
- /// If body Contains NewTarget is true, throw a SyntaxError exception.
- /// If body Contains SuperProperty is true, throw a SyntaxError exception.
- /// If body Contains SuperCall is true, throw a SyntaxError exception.
- /// </summary>
- private sealed class ShadowScriptValidator : AstVisitor
- {
- private readonly Realm _realm;
- public ShadowScriptValidator(Realm realm)
- {
- _realm = realm;
- }
- protected override object? VisitSuper(Super super)
- {
- ExceptionHelper.ThrowTypeError(_realm, "Shadow realm code cannot contain super");
- return null;
- }
- }
- }
|