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
7 changes: 7 additions & 0 deletions Compilation/CompilationContext.Closures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ public partial class CompilationContext
// `callvirt Invoke` instead of the per-call $TSFunction wrapper + reflective InvokeMethodValue.
public Dictionary<string, ArrowFunction> DirectCallArrowBindings { get; set; } = [];

// Generated value-type "shape" structs for promoted object-literal locals (#862). Shared
// program-wide (populated by DefineObjectShapeTypes after analysis). EmitVarDeclaration resolves the
// struct by canonical shape key for a promoted local; the property get/set fast paths recognise a
// promoted local from its slot's CLR type via ByClrType. See TryGetObjectShapeType /
// TryGetPromotedObjectLocal.
public ObjectShapeRegistry? ObjectShapes { get; set; }

// ============================================
// Self-referential capture write-back (issue #421)
// ============================================
Expand Down
25 changes: 25 additions & 0 deletions Compilation/CompilationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,31 @@ public IReadOnlyList<string> TakePendingLoopLabels()
return null;
}

/// <summary>
/// Resolves the generated shape struct for a promoted object-literal local by its canonical shape
/// key (#862), or null if shapes are not threaded into this context / the key is unknown. Used at the
/// declaration site to pick the struct type to declare the local with.
/// </summary>
public ObjectShapeTypeInfo? TryGetObjectShapeType(string canonicalKey) =>
ObjectShapes?.ByKey.GetValueOrDefault(canonicalKey);

/// <summary>
/// If <paramref name="variableName"/> currently binds to a promoted object-literal local (a slot
/// whose CLR type is one of the generated <c>$Shape_N</c> structs, #862), returns its
/// <see cref="LocalBuilder"/> and shape info; otherwise null. The slot's CLR type is the single
/// source of truth, so this is automatically scope-correct under shadowing and never misfires for a
/// captured/object local — no other code path declares a user local with a shape-struct slot.
/// </summary>
public (LocalBuilder Local, ObjectShapeTypeInfo Shape)? TryGetPromotedObjectLocal(string variableName)
{
if (ObjectShapes == null) return null;
if (!Locals.TryGetLocal(variableName, out var local)) return null;
var slotType = Locals.GetLocalType(variableName);
if (slotType != null && ObjectShapes.ByClrType.TryGetValue(slotType, out var shape))
return (local, shape);
return null;
}

// Exception block tracking for proper return handling
public int ExceptionBlockDepth { get; set; } = 0;
public LocalBuilder? ReturnValueLocal { get; set; }
Expand Down
1 change: 1 addition & 0 deletions Compilation/ILCompiler.ArrowFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,7 @@ private void EmitArrowBody(Expr.ArrowFunction arrow, MethodBuilder method, TypeB
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
3 changes: 3 additions & 0 deletions Compilation/ILCompiler.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ private void EmitAsyncStateMachineBodies()
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -1131,6 +1132,7 @@ private void EmitAsyncMethodBody(MethodBuilder methodBuilder, Stmt.Function meth
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -1364,6 +1366,7 @@ private void EmitTopLevelAsyncArrowBodies()
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
2 changes: 2 additions & 0 deletions Compilation/ILCompiler.AsyncGenerators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ private void EmitAsyncGeneratorMoveNextAsyncBody(AsyncGeneratorStateMachineBuild
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -291,6 +292,7 @@ private void EmitAsyncGeneratorMethodBody(MethodBuilder methodBuilder, Stmt.Func
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
1 change: 1 addition & 0 deletions Compilation/ILCompiler.Classes.Accessors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ private void EmitAccessorBody(TypeBuilder typeBuilder, Stmt.Accessor accessor, M
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
1 change: 1 addition & 0 deletions Compilation/ILCompiler.Classes.ClassExpressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ private CompilationContext CreateClassExpressionContext(
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
1 change: 1 addition & 0 deletions Compilation/ILCompiler.Classes.Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ private void EmitConstructor(TypeBuilder typeBuilder, Stmt.Class classStmt, Fiel
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
2 changes: 2 additions & 0 deletions Compilation/ILCompiler.Classes.Methods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,7 @@ private void EmitPrivateMethodBody(
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -1220,6 +1221,7 @@ private void EmitMethod(TypeBuilder typeBuilder, Stmt.Function method, FieldInfo
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
3 changes: 3 additions & 0 deletions Compilation/ILCompiler.Classes.Static.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ private void EmitStaticConstructor(TypeBuilder typeBuilder, Stmt.Class classStmt
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -382,6 +383,7 @@ private void EmitStaticMethodBody(string className, Stmt.Function method)
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -625,6 +627,7 @@ private void EmitStaticAsyncMethodBody(string className, Stmt.Function method)
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
3 changes: 3 additions & 0 deletions Compilation/ILCompiler.Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ private void EmitFunctionBody(Stmt.Function funcStmt)
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -1007,6 +1008,7 @@ private void EmitDefaultEntryPoint(List<Stmt> statements)
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -1167,6 +1169,7 @@ private void EmitExeEntryPointWithUserMain(List<Stmt> statements, Stmt.Function
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
2 changes: 2 additions & 0 deletions Compilation/ILCompiler.Generators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ private void EmitGeneratorMoveNextBody(GeneratorStateMachineBuilder smBuilder, S
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down Expand Up @@ -513,6 +514,7 @@ private void EmitGeneratorMethodBody(MethodBuilder methodBuilder, Stmt.Function
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
1 change: 1 addition & 0 deletions Compilation/ILCompiler.InnerFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ private void EmitInnerFunctionBodies()
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
1 change: 1 addition & 0 deletions Compilation/ILCompiler.Modules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ private CompilationContext CreateCompilationContext(ILGenerator il)
ArrowMethods = _closures.ArrowMethods,
ConstArrowBindings = _closures.ConstArrowBindings,
DirectCallArrowBindings = _closures.DirectCallArrowBindings,
ObjectShapes = _closures.ObjectShapes,
DisplayClasses = _closures.DisplayClasses,
DisplayClassFields = _closures.DisplayClassFields,
DisplayClassConstructors = _closures.DisplayClassConstructors,
Expand Down
5 changes: 5 additions & 0 deletions Compilation/ILCompiler.State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ private sealed class ClosureCompilationState
// NonEscapingArrowLocalAnalyzer; the fast path only fires for capturing arrows whose in-scope
// local slot type matches the display class (so a same-named binding elsewhere never hits it).
public Dictionary<string, Expr.ArrowFunction> DirectCallArrowBindings { get; } = [];

// Generated value-type "shape" structs for promoted object-literal locals (#862). Populated
// after ObjectLocalPromotionAnalyzer by DefineObjectShapeTypes; the declaration site keys in by
// canonical shape key, and the property get/set fast paths key in by the local slot's CLR type.
public ObjectShapeRegistry ObjectShapes { get; } = new();
public Dictionary<Expr.ArrowFunction, TypeBuilder> DisplayClasses { get; } = new(ReferenceEqualityComparer.Instance);
public Dictionary<Expr.ArrowFunction, Dictionary<string, FieldBuilder>> DisplayClassFields { get; } = new(ReferenceEqualityComparer.Instance);
public Dictionary<Expr.ArrowFunction, ConstructorBuilder> DisplayClassConstructors { get; } = new(ReferenceEqualityComparer.Instance);
Expand Down
54 changes: 54 additions & 0 deletions Compilation/ILCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,9 @@ public void Compile(List<Stmt> statements, TypeMap typeMap, DeadCodeInfo? deadCo
Phase2_AnalyzeClosures(statements);
ArrayLocalPromotionAnalyzer.Analyze(statements, _typeMap, _closures.Analyzer);
NonEscapingArrowLocalAnalyzer.Analyze(statements, _closures.DirectCallArrowBindings, _closures.Analyzer);
ObjectLocalPromotionAnalyzer.Analyze(statements, _typeMap, _closures.Analyzer);
Phase3_CreateProgramType();
DefineObjectShapeTypes();
PreScanBuiltInModuleImports(statements);
Phase4_DefineDeclarations(statements);
Phase5_CollectArrowFunctions(statements);
Expand Down Expand Up @@ -705,13 +707,59 @@ private void Phase8_EmitEntryPoint(List<Stmt> statements)
EmitEntryPoint(statements);
}

/// <summary>
/// Defines one generated value-type <c>$Shape_N</c> struct per distinct promotable object-literal
/// shape (#862), de-duplicated by canonical key. Each field becomes a typed public field
/// (<c>number</c>→<c>double</c>, <c>boolean</c>→<c>bool</c>, <c>string</c>→<c>string</c>). The structs
/// reference only BCL types, so compiled output stays standalone (no SharpTS.dll dependency). They are
/// finalized (<c>CreateType</c>) at the top of the finalize phase, before any type that uses them.
/// </summary>
private void DefineObjectShapeTypes()
{
if (_typeMap == null) return;
foreach (var shape in _typeMap.PromotableObjectLocalShapes)
{
if (_closures.ObjectShapes.ByKey.ContainsKey(shape.CanonicalKey)) continue;

var structType = _moduleBuilder.DefineType(
$"$Shape_{_closures.ObjectShapes.Counter++}",
TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.SequentialLayout,
_types.ValueType);

var fieldBuilders = new Dictionary<string, FieldBuilder>(StringComparer.Ordinal);
foreach (var (name, kind) in shape.Fields)
{
var clr = kind switch
{
TokenType.TYPE_NUMBER => _types.Double,
TokenType.TYPE_BOOLEAN => _types.Boolean,
_ => _types.String,
};
fieldBuilders[name] = structType.DefineField(name, clr, FieldAttributes.Public);
}

var info = new ObjectShapeTypeInfo
{
ClrType = structType,
Fields = shape.Fields.Select(f => (f.Name, f.Kind)).ToList(),
FieldBuilders = fieldBuilders,
};
_closures.ObjectShapes.ByKey[shape.CanonicalKey] = info;
_closures.ObjectShapes.ByClrType[structType] = info;
}
}

/// <summary>
/// Phase 9: Finalize all types by calling CreateType().
/// </summary>
private void Phase9_FinalizeTypes()
{
_unionGenerator?.FinalizeAllUnionTypes();

// Finalize generated object-literal shape structs (#862) before any type that uses them.
foreach (var info in _closures.ObjectShapes.ByKey.Values)
((TypeBuilder)info.ClrType).CreateType();

// Finalize entry-point display class first (needed by closures)
_closures.EntryPointDisplayClass?.CreateType();

Expand Down Expand Up @@ -908,7 +956,9 @@ public void CompileModules(List<ParsedModule> modules, ModuleResolver resolver,
Phase2_AnalyzeClosures(allStatements);
ArrayLocalPromotionAnalyzer.Analyze(allStatements, _typeMap, _closures.Analyzer);
NonEscapingArrowLocalAnalyzer.Analyze(allStatements, _closures.DirectCallArrowBindings, _closures.Analyzer);
ObjectLocalPromotionAnalyzer.Analyze(allStatements, _typeMap, _closures.Analyzer);
Phase3_CreateProgramType();
DefineObjectShapeTypes();
// Scope each module's named-import bindings to that module so local
// aliases like __platform don't collide between stdlib modules.
foreach (var m in modules)
Expand Down Expand Up @@ -1167,6 +1217,10 @@ private void ModulePhase11_FinalizeTypes()

_unionGenerator?.FinalizeAllUnionTypes();

// Finalize generated object-literal shape structs (#862) before any type that uses them.
foreach (var info in _closures.ObjectShapes.ByKey.Values)
((TypeBuilder)info.ClrType).CreateType();

// Finalize entry-point display class first (needed by closures)
_closures.EntryPointDisplayClass?.CreateType();

Expand Down
Loading
Loading