Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement import attributes #1710

Merged
merged 2 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.11" />
<PackageVersion Include="Esprima" Version="3.0.3" />
<PackageVersion Include="Esprima" Version="3.0.4" />
<PackageVersion Include="Flurl.Http.Signed" Version="3.2.4" />
<PackageVersion Include="Jurassic" Version="3.2.7" />
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.132" />
Expand Down
3 changes: 2 additions & 1 deletion Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"Atomics",
"generators",
"import-assertions",
"import-attributes",
"iterator-helpers",
"json-modules",
"regexp-duplicate-named-groups",
"regexp-lookbehind",
"regexp-unicode-property-escapes",
Expand Down Expand Up @@ -97,6 +97,7 @@
"language/**/*-yield-*.js",
"language/module-code/instn-local-bndng-gen.js",
"language/module-code/instn-local-bndng-export-gen.js",
"language/expressions/dynamic-import/import-attributes/2nd-param-evaluation-abrupt-return.js",

// Esprima problem
"language/expressions/object/let-non-strict-access.js",
Expand Down
4 changes: 2 additions & 2 deletions Jint/Engine.Modules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal ModuleRecord LoadModule(string? referencingModuleLocation, string speci
}
else
{
module = LoaderFromModuleLoader(moduleResolution);
module = LoadFromModuleLoader(moduleResolution);
}

if (module is SourceTextModuleRecord sourceTextModule)
Expand All @@ -59,7 +59,7 @@ private BuilderModuleRecord LoadFromBuilder(string specifier, ModuleBuilder modu
return module;
}

private SourceTextModuleRecord LoaderFromModuleLoader(ResolvedSpecifier moduleResolution)
private SourceTextModuleRecord LoadFromModuleLoader(ResolvedSpecifier moduleResolution)
{
var parsedModule = ModuleLoader.LoadModule(this, moduleResolution);
var module = new SourceTextModuleRecord(this, Realm, parsedModule, moduleResolution.Uri?.LocalPath, false);
Expand Down
10 changes: 5 additions & 5 deletions Jint/EsprimaExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,11 @@ internal static void PrivateBoundIdentifiers(this Node parameter, HashSet<Privat
return new Record(propKey, closure);
}

internal static void GetImportEntries(this ImportDeclaration import, List<ImportEntry> importEntries, HashSet<string> requestedModules)
internal static void GetImportEntries(this ImportDeclaration import, List<ImportEntry> importEntries, HashSet<ModuleRequest> requestedModules)
{
var source = import.Source.StringValue!;
var specifiers = import.Specifiers;
requestedModules.Add(source);
requestedModules.Add(new ModuleRequest(source, []));

foreach (var specifier in specifiers)
{
Expand All @@ -349,7 +349,7 @@ internal static void GetImportEntries(this ImportDeclaration import, List<Import
}
}

internal static void GetExportEntries(this ExportDeclaration export, List<ExportEntry> exportEntries, HashSet<string> requestedModules)
internal static void GetExportEntries(this ExportDeclaration export, List<ExportEntry> exportEntries, HashSet<ModuleRequest> requestedModules)
{
switch (export)
{
Expand All @@ -358,7 +358,7 @@ internal static void GetExportEntries(this ExportDeclaration export, List<Export
break;
case ExportAllDeclaration allDeclaration:
//Note: there is a pending PR for Esprima to support exporting an imported modules content as a namespace i.e. 'export * as ns from "mod"'
requestedModules.Add(allDeclaration.Source.StringValue!);
requestedModules.Add(new ModuleRequest(allDeclaration.Source.StringValue!, []));
exportEntries.Add(new(allDeclaration.Exported?.GetModuleKey(), allDeclaration.Source.StringValue, "*", null));
break;
case ExportNamedDeclaration namedDeclaration:
Expand All @@ -385,7 +385,7 @@ internal static void GetExportEntries(this ExportDeclaration export, List<Export

if (namedDeclaration.Source is not null)
{
requestedModules.Add(namedDeclaration.Source.StringValue!);
requestedModules.Add(new ModuleRequest(namedDeclaration.Source.StringValue!, []));
}

break;
Expand Down
22 changes: 15 additions & 7 deletions Jint/HoistingScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public static HoistingScope GetFunctionLevelDeclarations(bool strict, IFunction

public static void GetImportsAndExports(
Module module,
out HashSet<string> requestedModules,
out HashSet<ModuleRequest> requestedModules,
out List<ImportEntry>? importEntries,
out List<ExportEntry> localExportEntries,
out List<ExportEntry> indirectExportEntries,
Expand All @@ -133,7 +133,7 @@ public static HoistingScope GetFunctionLevelDeclarations(bool strict, IFunction
treeWalker.Visit(module);

importEntries = treeWalker._importEntries;
requestedModules = treeWalker._requestedModules ?? new(StringComparer.Ordinal);
requestedModules = treeWalker._requestedModules ?? [];
var importedBoundNames = new HashSet<string>(StringComparer.Ordinal);

if (importEntries != null)
Expand Down Expand Up @@ -287,27 +287,35 @@ public void Visit(Node node, Node? parent)
}
}

private sealed class ModuleRequestRecordComparer : IComparer<ModuleRequest>
{
public int Compare(ModuleRequest x, ModuleRequest y)
{
return string.Compare(x.Specifier, y.Specifier, StringComparison.Ordinal);
}
}

private sealed class ModuleWalker
{
internal List<ImportEntry>? _importEntries;
internal List<ExportEntry>? _exportEntries;
internal HashSet<string>? _requestedModules;
internal HashSet<ModuleRequest>? _requestedModules;

internal void Visit(Node node)
{
foreach (var childNode in node.ChildNodes)
{
if (childNode.Type == Nodes.ImportDeclaration)
{
_importEntries ??= new();
_requestedModules ??= new(StringComparer.Ordinal);
_importEntries ??= [];
_requestedModules ??= [];
var import = (ImportDeclaration) childNode;
import.GetImportEntries(_importEntries, _requestedModules);
}
else if (childNode.Type is Nodes.ExportAllDeclaration or Nodes.ExportDefaultDeclaration or Nodes.ExportNamedDeclaration)
{
_exportEntries ??= new();
_requestedModules ??= new(StringComparer.Ordinal);
_exportEntries ??= [];
_requestedModules ??= [];
var export = (ExportDeclaration) childNode;
export.GetExportEntries(_exportEntries, _requestedModules);
}
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Json/JsonInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ private static JsValue InternalizeJSONProperty(JsValue holder, JsValue name, ICa
}
else
{
var keys = val.EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind.Key);
var keys = val.EnumerableOwnProperties(EnumerableOwnPropertyNamesKind.Key);
foreach (var p in keys)
{
var newElement = InternalizeJSONProperty(val, p, reviver);
Expand Down
6 changes: 3 additions & 3 deletions Jint/Native/Object/ObjectConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private JsValue Assign(JsValue thisObject, JsValue[] arguments)
private JsValue Entries(JsValue thisObject, JsValue[] arguments)
{
var obj = TypeConverter.ToObject(_realm, arguments.At(0));
var nameList = obj.EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind.KeyValue);
var nameList = obj.EnumerableOwnProperties(EnumerableOwnPropertyNamesKind.KeyValue);
return nameList;
}

Expand Down Expand Up @@ -516,7 +516,7 @@ private static JsValue IsExtensible(JsValue thisObject, JsValue[] arguments)
private JsValue Keys(JsValue thisObject, JsValue[] arguments)
{
var o = TypeConverter.ToObject(_realm, arguments.At(0));
return o.EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind.Key);
return o.EnumerableOwnProperties(EnumerableOwnPropertyNamesKind.Key);
}

/// <summary>
Expand All @@ -525,7 +525,7 @@ private JsValue Keys(JsValue thisObject, JsValue[] arguments)
private JsValue Values(JsValue thisObject, JsValue[] arguments)
{
var o = TypeConverter.ToObject(_realm, arguments.At(0));
return o.EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind.Value);
return o.EnumerableOwnProperties(EnumerableOwnPropertyNamesKind.Value);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Object/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,7 @@ internal static JsObject OrdinaryObjectCreate(Engine engine, ObjectInstance? pro
}
}

internal JsArray EnumerableOwnPropertyNames(EnumerableOwnPropertyNamesKind kind)
internal JsArray EnumerableOwnProperties(EnumerableOwnPropertyNamesKind kind)
{
var ownKeys = GetOwnPropertyKeys(Types.String);

Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/ShadowRealm/ShadowRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ private static void CopyNameAndLength(WrappedFunction f, ObjectInstance target,
// 4. If runningContext is not already suspended, suspend runningContext.

_engine.EnterExecutionContext(_executionContext);
_engine._host.ImportModuleDynamically(null, specifierString, innerCapability);
_engine._host.LoadImportedModule(null, new ModuleRequest(specifierString, new List<KeyValuePair<string, JsValue>>()), innerCapability);
_engine.LeaveExecutionContext();

var onFulfilled = new StepsFunction(_engine, callerRealm, exportNameString);
Expand Down
34 changes: 20 additions & 14 deletions Jint/Runtime/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Jint.Runtime
public class Host
{
private Engine? _engine;
private readonly List<string> _supportedImportAttributes = ["type"];

protected Engine Engine
{
Expand Down Expand Up @@ -115,62 +116,62 @@ public virtual void EnsureCanCompileStrings(Realm callerRealm, Realm evalRealm)
}

/// <summary>
/// https://tc39.es/ecma262/#sec-hostresolveimportedmodule
/// https://tc39.es/ecma262/#sec-GetImportedModule
/// </summary>
internal virtual ModuleRecord ResolveImportedModule(IScriptOrModule? referencingScriptOrModule, string specifier)
internal virtual ModuleRecord GetImportedModule(IScriptOrModule? referrer, string specifier)
{
return Engine.LoadModule(referencingScriptOrModule?.Location, specifier);
return Engine.LoadModule(referrer?.Location, specifier);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-hostimportmoduledynamically
/// https://tc39.es/ecma262/#sec-HostLoadImportedModule
/// </summary>
internal virtual void ImportModuleDynamically(IScriptOrModule? referencingModule, string specifier, PromiseCapability promiseCapability)
internal virtual void LoadImportedModule(IScriptOrModule? referrer, ModuleRequest moduleRequest, PromiseCapability payload)
{
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);
Engine.ImportModule(moduleRequest.Specifier, referrer?.Location);
promise.Resolve(JsValue.Undefined);
}
catch (JavaScriptException ex)
{
promise.Reject(ex.Error);
}

FinishDynamicImport(referencingModule, specifier, promiseCapability, (JsPromise) promise.Promise);
FinishLoadingImportedModule(referrer, moduleRequest, payload, (JsPromise) promise.Promise);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-finishdynamicimport
/// https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
/// </summary>
internal virtual void FinishDynamicImport(IScriptOrModule? referencingModule, string specifier, PromiseCapability promiseCapability, JsPromise innerPromise)
internal virtual void FinishLoadingImportedModule(IScriptOrModule? referrer, ModuleRequest moduleRequest, PromiseCapability payload, JsPromise result)
{
var onFulfilled = new ClrFunctionInstance(Engine, "", (thisObj, args) =>
{
var moduleRecord = ResolveImportedModule(referencingModule, specifier);
var moduleRecord = GetImportedModule(referrer, moduleRequest.Specifier);
try
{
var ns = ModuleRecord.GetModuleNamespace(moduleRecord);
promiseCapability.Resolve.Call(JsValue.Undefined, new JsValue[] { ns });
payload.Resolve.Call(JsValue.Undefined, new JsValue[] { ns });
}
catch (JavaScriptException ex)
{
promiseCapability.Reject.Call(JsValue.Undefined, new [] { ex.Error });
payload.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 });
payload.Reject.Call(JsValue.Undefined, new [] { error });
return JsValue.Undefined;
}, 0, PropertyFlag.Configurable);

PromiseOperations.PerformPromiseThen(Engine, innerPromise, onFulfilled, onRejected, promiseCapability);
PromiseOperations.PerformPromiseThen(Engine, result, onFulfilled, onRejected, payload);
}

/// <summary>
Expand Down Expand Up @@ -210,6 +211,11 @@ internal void HostEnqueuePromiseJob(Action job, Realm realm)
{
Engine.AddToEventLoop(job);
}

internal virtual List<string> GetSupportedImportAttributes()
{
return _supportedImportAttributes;
}
}

internal sealed record JobCallback(ICallable Callback, object? HostDefined);
Expand Down