void EmitDisposeAndClean(CodeGenerator cg)
        {
            // enumerator.Dispose()
            if (_disposeMethod != null)
            {
                // TODO: if (enumerator != null)

                if (_enumeratorLoc.Type.IsValueType)
                {
                    _enumeratorLoc.EmitLoadAddress(cg.Builder);
                }
                else
                {
                    _enumeratorLoc.EmitLoad(cg.Builder);
                }

                cg.EmitCall(ILOpCode.Callvirt, _disposeMethod)
                .Expect(SpecialType.System_Void);
            }

            //// enumerator = null;
            //if (!_enumeratorLoc.Type.IsValueType)
            //{
            //    cg.Builder.EmitNullConstant();
            //    cg.Builder.EmitLocalStore(_enumeratorLoc);
            //}

            //
            if (_aliasedValueLoc != null)
            {
                cg.ReturnTemporaryLocal(_aliasedValueLoc);
                _aliasedValueLoc = null;
            }

            cg.ReturnTemporaryLocal(_enumeratorLoc);
            _enumeratorLoc = null;

            // unbind
            _moveNextMethod = null;
            _disposeMethod  = null;
            _currentValue   = null;
            _currentKey     = null;
            _current        = null;
            _iterator_next  = null;
        }
Exemple #2
0
        internal override void Generate(CodeGenerator cg)
        {
            Debug.Assert(this.Enumeree != null);

            // get the enumerator,
            // bind actual MoveNext() and CurrentValue and CurrentKey

            // Template: using(
            // a) enumerator = enumeree.GetEnumerator()
            // b) enumerator = Operators.GetEnumerator(enumeree)
            // ) ...

            cg.EmitSequencePoint(this.Enumeree.PhpSyntax);

            var enumereeType = cg.Emit(this.Enumeree);

            Debug.Assert(enumereeType.SpecialType != SpecialType.System_Void);

            var getEnumeratorMethod = enumereeType.LookupMember <MethodSymbol>(WellKnownMemberNames.GetEnumeratorMethodName);

            TypeSymbol enumeratorType;

            if (enumereeType.IsOfType(cg.CoreTypes.PhpArray))
            {
                cg.Builder.EmitBoolConstant(_aliasedValues);

                // PhpArray.GetForeachtEnumerator(bool)
                enumeratorType = cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.PhpArray.GetForeachEnumerator_Boolean);  // TODO: IPhpArray
            }
            else if (enumereeType.IsOfType(cg.CoreTypes.IPhpEnumerable))
            {
                var GetForeachEnumerator_Bool_RuntimeTypeHandle = cg.CoreTypes.IPhpEnumerable.Method("GetForeachEnumerator", cg.CoreTypes.Boolean, cg.CoreTypes.RuntimeTypeHandle);

                // enumeree.GetForeachEnumerator(bool aliasedValues, RuntimeTypeHandle caller)
                cg.Builder.EmitBoolConstant(_aliasedValues);
                cg.EmitCallerTypeHandle();
                enumeratorType = cg.EmitCall(ILOpCode.Callvirt, GetForeachEnumerator_Bool_RuntimeTypeHandle);
            }
            else if (enumereeType.IsOfType(cg.CoreTypes.Iterator))
            {
                enumeratorType = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetForeachEnumerator_Iterator);
            }
            // TODO: IPhpArray
            else if (getEnumeratorMethod != null && getEnumeratorMethod.ParameterCount == 0 && enumereeType.IsReferenceType)
            {
                // enumeree.GetEnumerator()
                enumeratorType = cg.EmitCall(CallOpCode(getEnumeratorMethod, enumereeType), getEnumeratorMethod);
            }
            else
            {
                cg.EmitConvertToPhpValue(enumereeType, 0);
                cg.Builder.EmitBoolConstant(_aliasedValues);
                cg.EmitCallerTypeHandle();

                // Operators.GetForeachEnumerator(PhpValue, bool, RuntimeTypeHandle)
                enumeratorType = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetForeachEnumerator_PhpValue_Bool_RuntimeTypeHandle);
            }

            //
            _current       = enumeratorType.LookupMember <PropertySymbol>(WellKnownMemberNames.CurrentPropertyName); // TODO: Err if no Current
            _currentValue  = enumeratorType.LookupMember <PropertySymbol>(_aliasedValues ? "CurrentValueAliased" : "CurrentValue");
            _currentKey    = enumeratorType.LookupMember <PropertySymbol>("CurrentKey");
            _disposeMethod = enumeratorType.LookupMember <MethodSymbol>("Dispose", m => m.ParameterCount == 0 && !m.IsStatic);

            //
            _enumeratorLoc = cg.GetTemporaryLocal(enumeratorType, longlive: true, immediateReturn: false);
            _enumeratorLoc.EmitStore();

            // bind methods
            _moveNextMethod = enumeratorType.LookupMember <MethodSymbol>(WellKnownMemberNames.MoveNextMethodName);    // TODO: Err if there is no MoveNext()
            Debug.Assert(_moveNextMethod.ReturnType.SpecialType == SpecialType.System_Boolean);
            Debug.Assert(_moveNextMethod.IsStatic == false);

            if (_disposeMethod != null &&
                cg.GeneratorStateMachineMethod == null)     // Temporary workaround allowing "yield" inside foreach. Yield cannot be inside TRY block, so we don't generate TRY for state machines. Remove this condition once we manage to bind try/catch/yield somehow
            {
                /* Template: try { body } finally { enumerator.Dispose }
                 */

                // try {
                cg.Builder.AssertStackEmpty();
                cg.Builder.OpenLocalScope(ScopeType.TryCatchFinally);
                cg.Builder.OpenLocalScope(ScopeType.Try);

                //
                EmitBody(cg);

                // }
                cg.Builder.CloseLocalScope();   // /Try

                // finally {
                cg.Builder.OpenLocalScope(ScopeType.Finally);

                // enumerator.Dispose() & cleanup
                EmitDisposeAndClean(cg);

                // }
                cg.Builder.CloseLocalScope();   // /Finally
                cg.Builder.CloseLocalScope();   // /TryCatchFinally
            }
            else
            {
                EmitBody(cg);
                EmitDisposeAndClean(cg);
            }
        }
        internal override void Generate(CodeGenerator cg)
        {
            Debug.Assert(this.Enumeree != null);

            // get the enumerator,
            // bind actual MoveNext() and CurrentValue and CurrentKey

            // Template: using(
            // a) enumerator = enumeree.GetEnumerator()
            // b) enumerator = Operators.GetEnumerator(enumeree)
            // ) ...

            cg.EmitSequencePoint(this.Enumeree.PhpSyntax);

            var enumereeType = cg.Emit(this.Enumeree);

            Debug.Assert(enumereeType.SpecialType != SpecialType.System_Void);

            var getEnumeratorMethod = enumereeType.LookupMember <MethodSymbol>(WellKnownMemberNames.GetEnumeratorMethodName);

            TypeSymbol enumeratorType;

            if (enumereeType.IsOfType(cg.CoreTypes.PhpArray))
            {
                cg.Builder.EmitBoolConstant(_aliasedValues);

                // TODO: FastEnumerator if possible (addref on PhpArray ion readonly mode, not in generator, .. ) ?

                // PhpArray.GetForeachEnumerator(bool)
                enumeratorType = cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.PhpArray.GetForeachEnumerator_Boolean);  // TODO: IPhpArray
            }
            else if (enumereeType.IsOfType(cg.CoreTypes.IPhpEnumerable))
            {
                var GetForeachEnumerator_Bool_RuntimeTypeHandle = cg.CoreTypes.IPhpEnumerable.Method("GetForeachEnumerator", cg.CoreTypes.Boolean, cg.CoreTypes.RuntimeTypeHandle);

                // enumeree.GetForeachEnumerator(bool aliasedValues, RuntimeTypeHandle caller)
                cg.Builder.EmitBoolConstant(_aliasedValues);
                cg.EmitCallerTypeHandle();
                enumeratorType = cg.EmitCall(ILOpCode.Callvirt, GetForeachEnumerator_Bool_RuntimeTypeHandle);
            }
            else if (enumereeType.IsOfType(cg.CoreTypes.Iterator))
            {
                // use Iterator directly,
                // do not allocate additional wrappers
                enumeratorType = cg.CoreTypes.Iterator; // cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetForeachEnumerator_Iterator);
            }
            // TODO: IPhpArray
            else if (getEnumeratorMethod != null && getEnumeratorMethod.ParameterCount == 0 && enumereeType.IsReferenceType)
            {
                // enumeree.GetEnumerator()
                enumeratorType = cg.EmitCall(ILOpCode.Callvirt, getEnumeratorMethod);
            }
            else
            {
                cg.EmitConvertToPhpValue(enumereeType, 0);
                cg.Builder.EmitBoolConstant(_aliasedValues);
                cg.EmitCallerTypeHandle();

                // Operators.GetForeachEnumerator(PhpValue, bool, RuntimeTypeHandle)
                enumeratorType = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetForeachEnumerator_PhpValue_Bool_RuntimeTypeHandle);
            }

            // store the enumerator:
            _enumeratorLoc = cg.GetTemporaryLocal(enumeratorType, longlive: true, immediateReturn: false);
            _enumeratorLoc.EmitStore();

            //
            if (enumeratorType == cg.CoreTypes.Iterator)
            {
                // if iterator is null, goto end
                _enumeratorLoc.EmitLoad(cg.Builder);
                cg.Builder.EmitBranch(ILOpCode.Brfalse, NextBlock.NextEdge.NextBlock);

                // Template: iterator.rewind() : void
                _enumeratorLoc.EmitLoad(cg.Builder);
                cg.EmitPop(cg.EmitCall(ILOpCode.Callvirt, enumeratorType.LookupMember <MethodSymbol>("rewind")));

                // bind methods
                _iterator_next  = enumeratorType.LookupMember <MethodSymbol>("next");                    // next()
                _current        = _currentValue = enumeratorType.LookupMember <MethodSymbol>("current"); // current()
                _currentKey     = enumeratorType.LookupMember <MethodSymbol>("key");                     // key()
                _moveNextMethod = enumeratorType.LookupMember <MethodSymbol>("valid");                   // valid() // use it as the loop's control expression (MoveNext)
            }
            else
            {
                // bind methods
                _current       = enumeratorType.LookupMember <PropertySymbol>(WellKnownMemberNames.CurrentPropertyName)?.GetMethod; // TODO: Err if no Current
                _currentValue  = enumeratorType.LookupMember <PropertySymbol>(_aliasedValues ? "CurrentValueAliased" : "CurrentValue")?.GetMethod;
                _currentKey    = enumeratorType.LookupMember <PropertySymbol>("CurrentKey")?.GetMethod;
                _disposeMethod = enumeratorType.LookupMember <MethodSymbol>("Dispose", m => m.ParameterCount == 0 && !m.IsStatic);

                _moveNextMethod = enumeratorType.LookupMember <MethodSymbol>(WellKnownMemberNames.MoveNextMethodName);    // TODO: Err if there is no MoveNext()
                Debug.Assert(_moveNextMethod.ReturnType.SpecialType == SpecialType.System_Boolean);
                Debug.Assert(_moveNextMethod.IsStatic == false);
            }

            if (_disposeMethod != null &&
                cg.GeneratorStateMachineMethod == null)     // Temporary workaround allowing "yield" inside foreach. Yield cannot be inside TRY block, so we don't generate TRY for state machines. Remove this condition once we manage to bind try/catch/yield somehow
            {
                /* Template: try { body } finally { enumerator.Dispose }
                 */

                // try {
                cg.Builder.AssertStackEmpty();
                cg.Builder.OpenLocalScope(ScopeType.TryCatchFinally);
                cg.Builder.OpenLocalScope(ScopeType.Try);

                //
                EmitBody(cg);

                // }
                cg.Builder.CloseLocalScope();   // /Try

                // finally {
                cg.Builder.OpenLocalScope(ScopeType.Finally);

                // enumerator.Dispose() & cleanup
                EmitDisposeAndClean(cg);

                // }
                cg.Builder.CloseLocalScope();   // /Finally
                cg.Builder.CloseLocalScope();   // /TryCatchFinally
            }
            else
            {
                EmitBody(cg);
                EmitDisposeAndClean(cg);
            }
        }