Skip to content

Commit

Permalink
Improve AOT usage scenarios with metadata (#1764)
Browse files Browse the repository at this point in the history
* create AOT example project
* set REPL for AOT publish
* extract reflection related helpers from TypeConverter to InteropHelper
  • Loading branch information
lahma authored Jan 27, 2024
1 parent 0946a9f commit 9fbdab3
Show file tree
Hide file tree
Showing 34 changed files with 595 additions and 406 deletions.
17 changes: 17 additions & 0 deletions Jint.AotExample/Jint.AotExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<PublishAot>true</PublishAot>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Jint\Jint.csproj" />
<TrimmerRootAssembly Include="Jint" />
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions Jint.AotExample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Jint;

var engine = new Engine(cfg => cfg
.AllowClr()
);

engine.SetValue("company", new Company());

Console.WriteLine($"Company's name: {engine.Evaluate("company.name")}");
Console.WriteLine($"Company's field: {engine.Evaluate("company.field")}");
Console.WriteLine($"Company's indexer: {engine.Evaluate("company[42]")}");
Console.WriteLine($"Company's greeting: {engine.Evaluate("company.sayHello('Mary')")}");


public class Company
{
public string Field = "public field value";
public string Name => "Jint";
public string SayHello(string name) => $"Hello {name}!";
public int this[int index] => index;
}

2 changes: 2 additions & 0 deletions Jint.Repl/Jint.Repl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
<PublishAot>true</PublishAot>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Jint\Jint.csproj" />
<TrimmerRootAssembly Include="Jint" />
</ItemGroup>
</Project>
9 changes: 5 additions & 4 deletions Jint.Repl/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Diagnostics;
using System.Reflection;
using System.Reflection;
using Esprima;
using Jint;
using Jint.Native;
using Jint.Native.Json;
using Jint.Runtime;

#pragma warning disable IL2026
#pragma warning disable IL2111

var engine = new Engine(cfg => cfg
.AllowClr()
);
Expand All @@ -30,8 +32,7 @@
}

var assembly = Assembly.GetExecutingAssembly();
var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
var version = fvi.FileVersion;
var version = assembly.GetName().Version?.ToString();

Console.WriteLine("Welcome to Jint ({0})", version);
Console.WriteLine("Type 'exit' to leave, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<LangVersion>latest</LangVersion>
<NoWarn>612</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
</PropertyGroup>


Expand Down
11 changes: 6 additions & 5 deletions Jint.Tests/Runtime/EngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -602,14 +602,15 @@ public void EvalFunctionParseAndExecuteCode()
[Fact]
public void ForInStatement()
{
RunTest(@"
var engine = new Engine();
var result = engine.Evaluate("""
var x, y, str = '';
for(var z in this) {
str += z;
str += z;
}
equal('xystrz', str);
");
return str;
""");
Assert.Equal("xystrz", result);
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion Jint.Tests/Runtime/InteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3431,7 +3431,7 @@ public void ShouldRespectConcreteGenericReturnTypes()
});

var result = new List<string>();
void Debug(object? o)
void Debug(object o)
{
result.Add($"{o?.GetType().Name ?? "null"}: {o ?? "null"}");
}
Expand Down
6 changes: 6 additions & 0 deletions Jint.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jint.AotExample", "Jint.AotExample\Jint.AotExample.csproj", "{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -59,6 +61,10 @@ Global
{70198CE9-7DFE-40CA-BBAC-1454C92C4109}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70198CE9-7DFE-40CA-BBAC-1454C92C4109}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70198CE9-7DFE-40CA-BBAC-1454C92C4109}.Release|Any CPU.Build.0 = Release|Any CPU
{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
31 changes: 26 additions & 5 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Esprima;
using Esprima.Ast;
Expand Down Expand Up @@ -227,7 +228,7 @@ internal ExecutionContext EnterExecutionContext(in ExecutionContext context)
/// </summary>
public Engine SetValue(string name, Delegate value)
{
Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), true, false, true));
Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), PropertyFlag.NonEnumerable));
return this;
}

Expand All @@ -244,23 +245,23 @@ public Engine SetValue(string name, string? value)
/// </summary>
public Engine SetValue(string name, double value)
{
return SetValue(name, JsNumber.Create(value));
return SetValue(name, (JsValue) JsNumber.Create(value));
}

/// <summary>
/// Registers an integer value as variable.
/// </summary>
public Engine SetValue(string name, int value)
{
return SetValue(name, JsNumber.Create(value));
return SetValue(name, (JsValue) JsNumber.Create(value));
}

/// <summary>
/// Registers a boolean value as variable.
/// </summary>
public Engine SetValue(string name, bool value)
{
return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
return SetValue(name, (JsValue) (value ? JsBoolean.True : JsBoolean.False));
}

/// <summary>
Expand All @@ -273,7 +274,7 @@ public Engine SetValue(string name, JsValue value)
}

/// <summary>
/// Registers an object value as variable, creates an interop wrapper when needed..
/// Registers an object value as variable, creates an interop wrapper when needed.
/// </summary>
public Engine SetValue(string name, object? obj)
{
Expand All @@ -284,6 +285,26 @@ public Engine SetValue(string name, object? obj)
return SetValue(name, value);
}

/// <summary>
/// Registers an object value as variable, creates an interop wrapper when needed.
/// </summary>
public Engine SetValue(string name, [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type)
{
#pragma warning disable IL2111
return SetValue(name, TypeReference.CreateTypeReference(this, type));
#pragma warning restore IL2111
}

/// <summary>
/// Registers an object value as variable, creates an interop wrapper when needed.
/// </summary>
public Engine SetValue<[DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>(string name, T? obj)
{
return obj is Type t
? SetValue(name, t)
: SetValue(name, JsValue.FromObject(this, obj));
}

internal void LeaveExecutionContext()
{
_executionContexts.Pop();
Expand Down
6 changes: 3 additions & 3 deletions Jint/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ internal static Type GetDefinedType(this MemberInfo memberInfo)
};
}

internal static IEnumerable<MethodInfo> GetExtensionMethods(this Type type)
internal static IEnumerable<MethodInfo> GetExtensionMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
{
return type.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(static m => m.IsExtensionMethod());
}

internal static IEnumerable<MethodInfo> GetOperatorOverloadMethods(this Type type)
internal static IEnumerable<MethodInfo> GetOperatorOverloadMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
{
return type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(static m => m.IsSpecialName);
}

private static bool IsExtensionMethod(this MethodBase methodInfo)
{
return methodInfo.IsDefined(typeof(ExtensionAttribute), true);
return methodInfo.IsDefined(typeof(ExtensionAttribute), inherit: true);
}

public static bool IsNullable(this Type type)
Expand Down
5 changes: 5 additions & 0 deletions Jint/Jint.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

<NoWarn>$(NoWarn);1591</NoWarn>

<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>

<PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute</PolySharpExcludeGeneratedTypes>
<PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>

</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' != 'net462' and '$(TargetFramework)' != 'netstandard2.0' ">
Expand Down
7 changes: 5 additions & 2 deletions Jint/Native/ShadowRealm/ShadowRealm.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Esprima;
using System.Diagnostics.CodeAnalysis;
using Esprima;
using Esprima.Ast;
using Esprima.Utils;
using Jint.Native.Object;
Expand Down Expand Up @@ -55,9 +56,11 @@ public JsValue ImportValue(string specifier, string exportName)
_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), true, false, true));
_shadowRealm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(_engine, value), PropertyFlag.NonEnumerable));
return this;
}

Expand Down
13 changes: 8 additions & 5 deletions Jint/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,20 @@ internal void Apply(Engine engine)
// add missing bits if needed
if (Interop.Enabled)
{
engine.Realm.GlobalObject.SetProperty("System",
new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
#pragma warning disable IL2026

engine.Realm.GlobalObject.SetProperty("System", new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));

engine.Realm.GlobalObject.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunction(
engine,
"importNamespace",
(thisObj, arguments) =>
new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))),
PropertyFlag.AllForbidden));
engine.Realm.GlobalObject.SetProperty("clrHelper", new PropertyDescriptor(
new ObjectWrapper(engine, new ClrHelper(Interop)),
PropertyFlag.AllForbidden));

engine.Realm.GlobalObject.SetProperty("clrHelper", new PropertyDescriptor(new ObjectWrapper(engine, new ClrHelper(Interop)), PropertyFlag.AllForbidden));

#pragma warning restore IL2026
}

if (Interop.ExtensionMethodTypes.Count > 0)
Expand Down
37 changes: 37 additions & 0 deletions Jint/Runtime/InternalTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Jint.Runtime;

[Flags]
internal enum InternalTypes
{
// should not be used, used for empty match
Empty = 0,

Undefined = 1,
Null = 2,

// primitive types range start
Boolean = 4,
String = 8,
Number = 16,
Integer = 32,
Symbol = 64,
BigInt = 128,

// primitive types range end
Object = 256,

PrivateName = 512,

// internal usage
ObjectEnvironmentRecord = 1024,
RequiresCloning = 2048,
Module = 4096,

// the object doesn't override important GetOwnProperty etc which change behavior
PlainObject = 8192,
// our native array
Array = 16384,

Primitive = Boolean | String | Number | Integer | BigInt | Symbol,
InternalFlags = ObjectEnvironmentRecord | RequiresCloning | PlainObject | Array | Module
}
14 changes: 7 additions & 7 deletions Jint/Runtime/Interop/ClrHelper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
namespace Jint.Runtime.Interop;
using Jint.Native;

using Jint.Native;
namespace Jint.Runtime.Interop;

public class ClrHelper
#pragma warning disable IL2072

internal sealed class ClrHelper
{
private readonly Options.InteropOptions _interopOptions;

Expand Down Expand Up @@ -74,10 +76,8 @@ public JsValue ObjectToType(ObjectWrapper obj)
{
return TypeReference.CreateTypeReference(obj.Engine, t);
}
else
{
ExceptionHelper.ThrowArgumentException("Must be an ObjectWrapper of Type", nameof(obj));
}

ExceptionHelper.ThrowArgumentException("Must be an ObjectWrapper of Type", nameof(obj));
return JsValue.Undefined;
}

Expand Down
Loading

0 comments on commit 9fbdab3

Please sign in to comment.