Skip to content

Commit

Permalink
synthesize Finalize() pattern
Browse files Browse the repository at this point in the history
- Finalize() method that calls __destruct() (through synthesized IDisposable.Dispose())
- Dispose() calls __destruct() just once
- ref #489
  • Loading branch information
jakubmisek committed Sep 14, 2019
1 parent 12c537a commit 4ec4469
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 1 deletion.
68 changes: 67 additions & 1 deletion src/Peachpie.CodeAnalysis/CodeGen/Symbols/SourceTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ void EmitDisposable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
ExplicitOverride = (MethodSymbol)DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose),
ForwardedCall = __destruct,
};

module.SetMethodBody(dispose, MethodGenerator.GenerateMethodBody(module, dispose, il =>
{
var thisPlace = new ArgPlace(this, 0);
Expand All @@ -163,11 +163,77 @@ void EmitDisposable(Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
CallerType = this,
};
// private bool <>b_disposed;
var disposedField = cg.Module.SynthesizedManager.GetOrCreateSynthesizedField(this, DeclaringCompilation.CoreTypes.Boolean, WellKnownPchpNames.SynthesizedDisposedFieldName, Accessibility.Private, false, false, false);
var disposedPlace = new FieldPlace(thisPlace, disposedField, cg.Module);
// if (<>b_disposed) return;
var lblContinue = new object();
disposedPlace.EmitLoad(cg.Builder);
cg.Builder.EmitBranch(ILOpCode.Brfalse, lblContinue);
cg.EmitRet(DeclaringCompilation.CoreTypes.Void);
cg.Builder.MarkLabel(lblContinue);
// <>b_disposed = true;
disposedPlace.EmitStorePrepare(cg.Builder);
cg.Builder.EmitBoolConstant(true);
disposedPlace.EmitStore(cg.Builder);
// __destruct()
cg.EmitRet(cg.EmitForwardCall(__destruct, dispose, callvirt: true));
}, null, diagnostics, false));

module.SynthesizedManager.AddMethod(this, dispose);

//
// Finalize()
//
var finalize = new SynthesizedFinalizeSymbol(this)
{
ForwardedCall = dispose,
};

Debug.Assert(finalize.OverriddenMethod != null);

module.SetMethodBody(finalize, MethodGenerator.GenerateMethodBody(module, finalize, il =>
{
var thisPlace = new ArgPlace(this, 0);
var ctxPlace = new FieldPlace(thisPlace, this.ContextStore, module);
var cg = new CodeGenerator(il, module, diagnostics, module.Compilation.Options.OptimizationLevel, false, this, ctxPlace, thisPlace)
{
CallerType = this,
};
//
cg.Builder.OpenLocalScope(ScopeType.TryCatchFinally);
// try {
cg.Builder.OpenLocalScope(ScopeType.Try);
// Dispose()
cg.EmitPop(cg.EmitForwardCall(dispose, finalize, callvirt: false));
//if (cg.EmitPdbSequencePoints) cg.Builder.EmitOpCode(ILOpCode.Nop);
// }
cg.Builder.CloseLocalScope();
// finally {
cg.Builder.OpenLocalScope(ScopeType.Finally);
// base.Finalize()
thisPlace.EmitLoad(cg.Builder);
cg.EmitCall(ILOpCode.Call, finalize.ExplicitOverride);
//if (cg.EmitPdbSequencePoints) cg.Builder.EmitOpCode(ILOpCode.Nop);
// }
cg.Builder.CloseLocalScope();
cg.Builder.CloseLocalScope();
// .ret
cg.EmitRet(DeclaringCompilation.GetSpecialType(SpecialType.System_Void));
}, null, diagnostics, false));

module.SynthesizedManager.AddMethod(this, finalize);
}

void EmitPhpCtors(ImmutableArray<MethodSymbol> instancectors, Emit.PEModuleBuilder module, DiagnosticBag diagnostics)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using System.Diagnostics;
using Devsense.PHP.Syntax;
using System.Threading;
using Peachpie.CodeAnalysis.Utilities;

namespace Pchp.CodeAnalysis.Symbols
{
#region SynthesizedDtorSymbol // generated Finalize method

internal class SynthesizedFinalizeSymbol : SynthesizedMethodSymbol
{
public SynthesizedFinalizeSymbol(NamedTypeSymbol/*!*/container)
: base(container, WellKnownMemberNames.DestructorName, false, true, container.DeclaringCompilation.CoreTypes.Void, phphidden: true)
{
Debug.Assert(!container.IsStatic);

ExplicitOverride = (MethodSymbol)container.DeclaringCompilation
.GetSpecialType(SpecialType.System_Object)
.GetMembers(WellKnownMemberNames.DestructorName)
.Single();
}

public override bool HidesBaseMethodsByName => false;

internal override bool HasSpecialName => false;

public override Accessibility DeclaredAccessibility => Accessibility.Protected;

public sealed override bool IsAbstract => false;

public sealed override bool IsExtern => false;

public sealed override bool IsSealed => false;

public sealed override MethodKind MethodKind => MethodKind.Destructor;

internal override bool IsExplicitInterfaceImplementation => false;

internal override ObsoleteAttributeData ObsoleteAttributeData => null;

internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false;

internal override bool IsMetadataVirtual(bool ignoreInterfaceImplementationChanges = false) => true;

internal override bool IsMetadataFinal => false;

public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
throw new NotImplementedException();
}
}

public override ImmutableArray<Location> Locations
{
get
{
throw new NotImplementedException();
}
}

public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray<ParameterSymbol>.Empty;
}

#endregion
}
5 changes: 5 additions & 0 deletions src/Peachpie.CodeAnalysis/Symbols/WellKnownPchpNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ internal static class WellKnownPchpNames
/// </summary>
public const string GeneratorStateMachineNameFormatString = "<>sm_{0}";

/// <summary>
/// Field with flag whether the class's Dispose() was called already.
/// </summary>
public static string SynthesizedDisposedFieldName => "<>b_disposed";

/// <summary>
/// Name of method containing lambda method's implementation.
/// This is PHP-like name that has to be equal <c>anonymous@function</c>
Expand Down

0 comments on commit 4ec4469

Please sign in to comment.