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);