Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Jint.Benchmark/BuiltinShapeBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,15 @@ public Engine EngineInitBuiltins()
engine.Evaluate(_initBuiltins);
return engine;
}

// Forces Temporal.Now (9 functions) to initialize — newly shape-backed here, dictionary on main.
private static readonly Prepared<Script> _initTemporalNow = Engine.PrepareScript("Temporal.Now.instant();");

[Benchmark]
public Engine EngineInitTemporalNow()
{
var engine = new Engine();
engine.Evaluate(_initTemporalNow);
return engine;
}
}
14 changes: 8 additions & 6 deletions Jint.SourceGenerators/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
8 changes: 8 additions & 0 deletions Jint.SourceGenerators/Diagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,12 @@ internal static class DiagnosticDescriptors
category: "Jint.SourceGenerators",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor UnsupportedShapeMember = new(
id: "JINT022",
title: "Built-in shape host has an unsupported member kind",
messageFormat: "Type '{0}' uses built-in shape storage (derives from BuiltinShapeObject) but declares an accessor / intrinsic reference / thrower / non-static or mutable property, which the shape path does not support; add [JsObject(UseShape = false)] or remove the member",
category: "Jint.SourceGenerators",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
44 changes: 41 additions & 3 deletions Jint.SourceGenerators/Models.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,32 @@ public IEnumerable<Diagnostic> Diagnostics
? string.Empty
: typeSymbol.ContainingNamespace.ToDisplayString();

// Read [JsObject(ExtraCapacity = N, UseShape = true)].
// Read [JsObject(ExtraCapacity = N, UseShape = ...)]. Shape storage is the default for any host
// that derives from BuiltinShapeObject (deriving from it is the opt-in); UseShape = false is the
// explicit opt-out. Hosts that don't derive from it always use the dictionary path regardless.
var extraCapacity = 0;
var useShape = false;
var useShapeRequested = true;
foreach (var attr in typeSymbol.GetAttributes())
{
if (attr.AttributeClass?.Name != "JsObjectAttribute") continue;
foreach (var named in attr.NamedArguments)
{
if (named.Key == "ExtraCapacity" && named.Value.Value is int v) extraCapacity = v;
else if (named.Key == "UseShape" && named.Value.Value is bool s) useShape = s;
else if (named.Key == "UseShape" && named.Value.Value is bool s) useShapeRequested = s;
}
}

var derivesFromBuiltinShapeObject = false;
for (var baseType = typeSymbol.BaseType; baseType is not null; baseType = baseType.BaseType)
{
if (baseType.Name == "BuiltinShapeObject")
{
derivesFromBuiltinShapeObject = true;
break;
}
}
var useShape = useShapeRequested && derivesFromBuiltinShapeObject;

var functions = new List<FunctionDefinition>();
var properties = new List<PropertyDefinition>();
var symbols = new List<SymbolDefinition>();
Expand Down Expand Up @@ -329,6 +342,31 @@ public IEnumerable<Diagnostic> Diagnostics
typeSymbol.Name));
}

// The shape path supports [JsFunction]s, static-immutable [JsProperty] constants, and symbols
// (incl. symbol-keyed functions). Accessors, intrinsic references, throwers and instance/mutable
// properties aren't representable — fail loudly rather than silently dropping them.
if (useShape)
{
var hasUnsupported = intrinsicReferences.Count > 0 || throwerAccessors.Count > 0;
foreach (var fn in functions)
{
if (fn.Registration is RegistrationKind.AccessorGet or RegistrationKind.AccessorSet
or RegistrationKind.SymbolAccessorGet or RegistrationKind.SymbolAccessorSet)
{
hasUnsupported = true;
break;
}
}
foreach (var p in properties)
{
if (!p.IsStatic || !p.IsImmutable) { hasUnsupported = true; break; }
}
if (hasUnsupported)
{
diagnostics.Add(new DiagnosticInfo(DiagnosticDescriptors.UnsupportedShapeMember, location, typeSymbol.Name));
}
}

functions.Sort(static (a, b) => string.CompareOrdinal(a.JsName, b.JsName));
properties.Sort(static (a, b) => string.CompareOrdinal(a.JsName, b.JsName));
symbols.Sort(static (a, b) => string.CompareOrdinal(a.SymbolName, b.SymbolName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ internal sealed class JsObjectAttribute : global::System.Attribute
public int ExtraCapacity { get; set; }

/// <summary>
/// Store this built-in's string-keyed own properties as a shared immutable BuiltinShape plus a
/// per-realm lazily-filled descriptor array instead of a per-realm property dictionary. The host
/// must derive from <c>BuiltinShapeObject</c>. Supported for hosts whose members are
/// [JsFunction]s, static-immutable [JsProperty] constants, and [JsSymbol]s (namespace built-ins
/// such as Math/JSON/Reflect/Atomics). See BuiltinShapeObject.
/// Built-in shape storage (a shared immutable BuiltinShape plus a per-realm lazily-filled
/// descriptor array instead of a per-realm property dictionary) is used <b>by default</b> for any
/// host that derives from <c>BuiltinShapeObject</c> — deriving from it is the opt-in. Set this to
/// <c>false</c> to opt a derived host back out to the dictionary path. Hosts that do not derive
/// from <c>BuiltinShapeObject</c> always use the dictionary path regardless of this flag.
/// Supported members: [JsFunction]s, static-immutable [JsProperty] constants, and symbols. See
/// BuiltinShapeObject.
/// </summary>
public bool UseShape { get; set; }
public bool UseShape { get; set; } = true;
}

[global::System.AttributeUsage(global::System.AttributeTargets.Method)]
Expand Down
Loading