using Jint.Native; using Jint.Native.Global; using Jint.Native.Object; using Jint.Native.Promise; using Jint.Runtime.Descriptors; using Jint.Runtime.Environments; using Jint.Runtime.Interop; using Jint.Runtime.Modules; namespace Jint.Runtime { public class Host { private Engine? _engine; protected Engine Engine { get { if (_engine is null) { ExceptionHelper.ThrowInvalidOperationException("Initialize has not been called"); } return _engine!; } private set => _engine = value; } /// /// Initializes the host. /// public void Initialize(Engine engine) { Engine = engine; InitializeHostDefinedRealm(); PostInitialize(); } protected virtual void PostInitialize() { } /// /// https://tc39.es/ecma262/#sec-initializehostdefinedrealm /// protected virtual void InitializeHostDefinedRealm() { var realm = CreateRealm(); var newContext = new ExecutionContext( scriptOrModule: null, lexicalEnvironment: realm.GlobalEnv, variableEnvironment: realm.GlobalEnv, privateEnvironment: null, realm: realm, function: null); Engine.EnterExecutionContext(newContext); } protected virtual GlobalEnvironmentRecord CreateGlobalEnvironment(ObjectInstance globalObject) { return JintEnvironment.NewGlobalEnvironment(Engine, globalObject, globalObject); } protected virtual ObjectInstance CreateGlobalObject(Realm realm) { var globalObject = new GlobalObject(Engine, realm); // Because the properties might need some of the built-in object // their configuration is delayed to a later step // trigger initialization globalObject.EnsureInitialized(); return globalObject; } /// /// https://tc39.es/ecma262/#sec-createrealm /// protected internal virtual Realm CreateRealm() { var realmRec = new Realm(); Engine._realmInConstruction = realmRec; CreateIntrinsics(realmRec); var globalObject = CreateGlobalObject(realmRec); var globalEnv = CreateGlobalEnvironment(globalObject); realmRec.GlobalEnv = globalEnv; realmRec.GlobalObject = globalObject; Engine._realmInConstruction = null!; return realmRec; } /// /// https://tc39.es/ecma262/#sec-createintrinsics /// protected virtual void CreateIntrinsics(Realm realmRec) { var intrinsics = new Intrinsics(Engine, realmRec); realmRec.Intrinsics = intrinsics; } /// /// https://tc39.es/ecma262/#sec-hostensurecancompilestrings /// public virtual void EnsureCanCompileStrings(Realm callerRealm, Realm evalRealm) { if (!Engine.Options.StringCompilationAllowed) { ExceptionHelper.ThrowJavaScriptException(callerRealm.Intrinsics.TypeError, "String compilation has been disabled in engine options"); } } /// /// https://tc39.es/ecma262/#sec-hostresolveimportedmodule /// internal virtual ModuleRecord ResolveImportedModule(IScriptOrModule? referencingScriptOrModule, string specifier) { return Engine.LoadModule(referencingScriptOrModule?.Location, specifier); } /// /// https://tc39.es/ecma262/#sec-hostimportmoduledynamically /// internal virtual void ImportModuleDynamically(IScriptOrModule? referencingModule, string specifier, PromiseCapability promiseCapability) { var promise = Engine.RegisterPromise(); try { // This should instead return the PromiseInstance returned by ModuleRecord.Evaluate (currently done in Engine.EvaluateModule), but until we have await this will do. Engine.ImportModule(specifier, referencingModule?.Location); promise.Resolve(JsValue.Undefined); } catch (JavaScriptException ex) { promise.Reject(ex.Error); } FinishDynamicImport(referencingModule, specifier, promiseCapability, (JsPromise) promise.Promise); } /// /// https://tc39.es/ecma262/#sec-finishdynamicimport /// internal virtual void FinishDynamicImport(IScriptOrModule? referencingModule, string specifier, PromiseCapability promiseCapability, JsPromise innerPromise) { var onFulfilled = new ClrFunctionInstance(Engine, "", (thisObj, args) => { var moduleRecord = ResolveImportedModule(referencingModule, specifier); try { var ns = ModuleRecord.GetModuleNamespace(moduleRecord); promiseCapability.Resolve.Call(JsValue.Undefined, new JsValue[] { ns }); } catch (JavaScriptException ex) { promiseCapability.Reject.Call(JsValue.Undefined, new [] { ex.Error }); } return JsValue.Undefined; }, 0, PropertyFlag.Configurable); var onRejected = new ClrFunctionInstance(Engine, "", (thisObj, args) => { var error = args.At(0); promiseCapability.Reject.Call(JsValue.Undefined, new [] { error }); return JsValue.Undefined; }, 0, PropertyFlag.Configurable); PromiseOperations.PerformPromiseThen(Engine, innerPromise, onFulfilled, onRejected, promiseCapability); } /// /// https://tc39.es/ecma262/#sec-hostgetimportmetaproperties /// public virtual List> GetImportMetaProperties(ModuleRecord moduleRecord) { return new List>(); } /// /// https://tc39.es/ecma262/#sec-hostfinalizeimportmeta /// public virtual void FinalizeImportMeta(ObjectInstance importMeta, ModuleRecord moduleRecord) { } /// /// https://tc39.es/proposal-shadowrealm/#sec-host-initialize-shadow-shadowrealm /// public virtual void InitializeShadowRealm(Realm realm) { } /// /// https://tc39.es/ecma262/#sec-hostmakejobcallback /// internal virtual JobCallback MakeJobCallBack(ICallable cleanupCallback) { return new JobCallback(cleanupCallback, null); } /// /// https://tc39.es/ecma262/#sec-hostenqueuepromisejob /// internal void HostEnqueuePromiseJob(Action job, Realm realm) { Engine.AddToEventLoop(job); } } } internal sealed record JobCallback(ICallable Callback, object? HostDefined);