예제 #1
0
        private SBExpressionData ResolveDotIdentifier(SBExpressionData left, SBExpressionData right)
        {
            SBExpressionData result = null;

            switch (left.ReferencedType)
            {
                case SBExpressionType.Namespace:
                    result = this.ResolveDotIdentifierNamespace(left, right);
                    break;
                case SBExpressionType.GlobalVariableReference:
                    result = this.ResolveDotIdentifierGlobalVariableReference(left, right);
                    break;
                case SBExpressionType.Identifier:
                //throw new NotSupportedException("Don't think this is supposed to be called ever.");
                //result = this.ResolveDotIdentifierIdentifier(left, right);
                //break;
                case SBExpressionType.Constant:
                case SBExpressionType.Expression:
                case SBExpressionType.Indexing:
                case SBExpressionType.LocalVariableReference:
                case SBExpressionType.PropertyReference:
                case SBExpressionType.TestListReference:
                    result = this.ResolveDotIdentifierInstanceReference(left, right, true);
                    break;
                case SBExpressionType.TypeReference:
                    result = this.ResolveDotIdentifierTypeReference(left, right);
                    break;
                case SBExpressionType.ScriptNamespace:
                case SBExpressionType.ProcedureReference:
                    result = this.ResolveDotIdentifierInstanceReference(left, right, true);
                    break;
                default:
                    throw new Exception("Illegal left side of dot-operator; " + left.ToString());
            }

            return result;
        }
        public void HandleParensExpression(
            Antlr4.Runtime.ParserRuleContext context,
            bool isCallStatement,
            SBExpressionData left,
            Stack <SBExpressionData> argumentStack,
            SBExpressionData assignmentTarget,
            PropertyBlock propertyBlock)
        {
            var leftType = left.DataType?.Type;
            List <SBExpressionData> arguments = new List <SBExpressionData>();

            if (argumentStack.Count > 0)
            {
                while (argumentStack.Count > 0)
                {
                    arguments.Insert(0, this.ResolveIfIdentifier(argumentStack.Pop(), true));                             // First pushed first in list.
                }
            }

            var           matchingMethods = new List <Tuple <MethodInfo, int> >();
            var           suggestedAssignmentsForMatchingMethods = new List <List <SBExpressionData> >();
            Expression    instance       = null;
            var           callType       = ParansExpressionType.MethodCall;
            var           firstParIsThis = false; // Whether the first parameter of procedure (or method?) is a 'this' reference.
            TypeReference returnType     = null;

            #region Identify call type and list methods with matching name

            IEnumerable <MethodInfo> methods = null;
            // Find the list of methods matching the given arguments.
            switch (left.ReferencedType)
            {
            case SBExpressionType.Namespace:
            case SBExpressionType.Constant:
            case SBExpressionType.GlobalVariableReference:
            case SBExpressionType.TypeReference:
                m_errors.SymanticError(context.Start.Line, -1, false, $"\"{left.ToString()}\" is not a method, procedure or delegate.");
                return;

            case SBExpressionType.Identifier:
                m_errors.SymanticError(left.Token.Line, left.Token.Column, false, $"\"{left.ToString()}\" is unresolved.");
                return;

            case SBExpressionType.Expression:
            case SBExpressionType.PropertyReference:
            case SBExpressionType.LocalVariableReference:
                if (left.DataType.Type.IsDelegate())
                {
                    methods = new MethodInfo[] { leftType.GetMethod("Invoke") };
                }
                else if (typeof(IProcedureReference).IsAssignableFrom(leftType))
                {
                    if (leftType.IsGenericType && leftType.GetGenericTypeDefinition() == typeof(IProcedureReference <>))
                    {
                        var m = leftType.GetGenericArguments()[0].GetMethod("Invoke");
                        System.Diagnostics.Debug.Assert(m != null);
                        methods  = new MethodInfo[] { m };
                        callType = isCallStatement ? ParansExpressionType.ProcedureCall : ParansExpressionType.FunctionCall;
                        if (left.DataType.HaveProcedureReference)
                        {
                            var proc = left.DataType.DynamicType as FileProcedure;
                            firstParIsThis = proc.IsFirstParameterThisReference;
                            instance       = left.InstanceCode;
                        }
                    }
                    else
                    {
                        // Make rest of this method treat this as a method call rather than a procedure call.
                        instance = left.ExpressionCode;
                        callType = isCallStatement ? ParansExpressionType.DynamicProcedureCall : ParansExpressionType.DynamicFunctionCall;
                    }
                }
                else
                {
                    m_errors.SymanticError(context.Start.Line, -1, false, $"\"{left.ToString()}\" is not a procedure reference or a delegate.");
                    return;
                }
                break;

            case SBExpressionType.MethodReference:
                instance = left.ExpressionCode;
                methods  = (IEnumerable <MethodInfo>)left.Value;
                break;

            case SBExpressionType.ProcedureReference:
            {
                callType = isCallStatement ? ParansExpressionType.ProcedureCall : ParansExpressionType.FunctionCall;
                // Note: never anonymous procedure here.
                var procedure = (left.Value as IProcedureReference).ProcedureData;
                if (!isCallStatement && (procedure.Flags & ProcedureFlags.IsFunction) == ProcedureFlags.None)
                {
                    m_errors.SymanticError(context.Start.Line, -1, false, "Only procedures marked as 'function' can be called from expressions.");
                }
                if (procedure.DataType != null && procedure.DataType.Type != typeof(UnresolvedProcedureType))
                {
                    var m = procedure.DataType.Type.GetGenericArguments()[0].GetMethod("Invoke");
                    System.Diagnostics.Debug.Assert(m != null);
                    methods = new MethodInfo[] { m };
                }
                else
                {
                    m_errors.SymanticError(context.Start.Line, -1, false, "Unresolved signature for procedure.");
                }
            }
            break;

            //case SBExpressionType.DynamicObjectProperty:
            //case SBExpressionType.DynamicObjectPropertyReadonly:
            //    m_errors.SymanticError(context.Start.Line, context.Start.Column, false, "Left side is a dynamic property, not a dynamic procedure.");
            //    return;

            //case SBExpressionType.DynamicObjectProcedure:
            //    {
            //        callType = ParansExpressionType.DynamicObjectMethodCall;
            //    }
            //    break;

            case SBExpressionType.DynamicObjectMember:
            case SBExpressionType.DynamicAsyncObjectMember:
            {
                instance = left.InstanceCode;
                var name = left.Value as string;
                SBExpressionData sequencialFirstArguments = null, namedArguments = null, sequencialLastArguments = null;
                if (this.CreateArgumentsForDynamicCall(
                        arguments,
                        ref sequencialFirstArguments,
                        ref namedArguments,
                        ref sequencialLastArguments))
                {
                    MethodInfo dynamicExecutor = (left.ReferencedType == SBExpressionType.DynamicObjectMember) ? s_ExecuteDynamicObjectMethod : s_ExecuteDynamicAsyncObjectMethod;
                    Expression dynamicCall     = Expression.Call(
                        dynamicExecutor,
                        m_currentProcedure?.ContextReferenceInternal,
                        Expression.Convert(instance, (left.ReferencedType == SBExpressionType.DynamicObjectMember) ? typeof(IDynamicStepBroObject) : typeof(IDynamicAsyncStepBroObject)),
                        Expression.Constant(name),
                        sequencialFirstArguments.ExpressionCode);

                    returnType = (left.ReferencedType == SBExpressionType.DynamicObjectMember) ? TypeReference.TypeObject : TypeReference.TypeAsyncObject;
                    if (m_callAssignmentAwait)
                    {
                        dynamicCall           = this.MakeAwaitOperation(dynamicCall, context, true, m_callAssignmentTarget?.DataType.Type);
                        returnType            = (TypeReference)dynamicCall.Type; //new TypeReference(dynamicCall.Type);
                        m_callAssignmentAwait = false;
                    }

                    if (isCallStatement)
                    {
                        m_scopeStack.Peek().AddStatementCode(dynamicCall);
                    }
                    else
                    {
                        m_expressionData.Push(
                            new SBExpressionData(
                                HomeType.Immediate,
                                SBExpressionType.Expression,
                                returnType,
                                dynamicCall,
                                automaticTypeConvert: true));
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }
                return;             // All done for now.
            }

            case SBExpressionType.OperationError:
            case SBExpressionType.UnsupportedOperation:
            case SBExpressionType.UnknownIdentifier:
                return;

            default:
                m_errors.InternalError(context.Start.Line, -1, $"Unhandled expression type: \"{left.ReferencedType}\".");
                break;
            }

            #endregion

            MethodInfo selectedMethod = null;
            List <SBExpressionData> methodArguments = null;

            #region Dynamic Procedure Call

            if (callType == ParansExpressionType.DynamicProcedureCall)
            {
                selectedMethod = s_DynamicProcedureCall;

                SBExpressionData sequencialFirstArguments = null, namedArguments = null, sequencialLastArguments = null;
                if (this.CreateArgumentsForDynamicCall(
                        arguments,
                        ref sequencialFirstArguments,
                        ref namedArguments,
                        ref sequencialLastArguments))
                {
                    //this IProcedureReference procedure,
                    //IScriptCallContext context,
                    //PropertyBlock propertyBlock,
                    //object[] sequencialFirstArguments,
                    //ArgumentList namedArguments,
                    //object[] sequencialLastArguments

                    m_scopeStack.Peek().AddStatementCode(
                        Expression.Call(
                            s_DynamicProcedureCall,
                            instance,
                            m_currentProcedure?.ContextReferenceInternal,
                            Expression.Constant(null, typeof(PropertyBlock)),
                            sequencialFirstArguments.ExpressionCode,
                            namedArguments.ExpressionCode,
                            sequencialLastArguments.ExpressionCode));
                }
                return;     // All done.
            }
            else if (callType == ParansExpressionType.DynamicFunctionCall)
            {
                selectedMethod = s_DynamicFunctionCall;
                throw new NotImplementedException("DYNAMIC FUNCTION CALL");
            }

            #endregion

            // Fall through: Not a dynamic call

            if (methods == null)
            {
                throw new Exception("No methods identified; should be handled and reported earlier.");
            }

            #region Find matching method and setup arguments

            foreach (var m in methods)
            {
                var constructedMethod = m;
                var extensionInstance = m.IsExtension() ? instance : null;
                List <SBExpressionData> suggestedAssignments = new List <SBExpressionData>();
                int score = this.CheckMethodArguments(
                    ref constructedMethod,
                    callType == ParansExpressionType.ProcedureCall || callType == ParansExpressionType.FunctionCall,
                    firstParIsThis,
                    instance,
                    m_currentProcedure?.ContextReferenceInternal,
                    extensionInstance,
                    arguments,
                    suggestedAssignments);
                if (score > 0)
                {
                    matchingMethods.Add(new Tuple <MethodInfo, int>(constructedMethod, score));
                    suggestedAssignmentsForMatchingMethods.Add(suggestedAssignments);
                }
            }

            int bestMatch = -1;
            if (matchingMethods.Count == 1)
            {
                bestMatch = 0;
            }
            else if (matchingMethods.Count > 1)
            {
                int max      = matchingMethods[0].Item2;
                int maxAt    = 0;
                int numAtMax = 1;
                for (var i = 1; i < matchingMethods.Count; i++)
                {
                    if (matchingMethods[i].Item2 > max)
                    {
                        max      = matchingMethods[i].Item2;
                        maxAt    = i;
                        numAtMax = 1;
                    }
                    else if (matchingMethods[i].Item2 == max)
                    {
                        numAtMax++;
                    }
                }
                if (numAtMax == 1)
                {
                    bestMatch = maxAt;
                }
            }

            if (bestMatch >= 0)
            {
                selectedMethod = matchingMethods[bestMatch].Item1;
                var suggestedAssignments = suggestedAssignmentsForMatchingMethods[bestMatch];

                returnType = new TypeReference(selectedMethod.ReturnType);
                if (selectedMethod.IsExtension())
                {
                    instance = null;
                }
                methodArguments = new List <SBExpressionData>();
                int i = 0;
                foreach (var p in selectedMethod.GetParameters())
                {
                    SBExpressionData argument = suggestedAssignments[i];
                    if (argument == null || argument.ExpressionCode == null)
                    {
                        throw new NotImplementedException();    // Implicit argument must be found and added.
                    }
                    else
                    {
                        methodArguments.Add(argument);
                    }
                    i++;
                }
            }
            else
            {
                // Handle none or more than one alternative
                throw new NotImplementedException();
            }

            #endregion

            #region Call the target procedure or method

            if (callType == ParansExpressionType.ProcedureCall || callType == ParansExpressionType.FunctionCall)
            {
                Expression procedureReferenceExp  = null;
                Type       delegateType           = null;
                Type       procedureReferenceType = null;
                Expression theDelegate            = null;

                switch (left.ReferencedType)
                {
                case SBExpressionType.Expression:
                case SBExpressionType.PropertyReference:
                case SBExpressionType.LocalVariableReference:
                {
                    procedureReferenceExp = left.ExpressionCode;

                    if (leftType.IsGenericType && leftType.GetGenericTypeDefinition() == typeof(IProcedureReference <>))
                    {
                        delegateType           = leftType.GetGenericArguments()[0];
                        procedureReferenceType = leftType;         //typeof(IProcedureReference<>).MakeGenericType(delegateType);
                    }
                    else
                    {
                        if (leftType == typeof(IProcedureReference))
                        {
                            // Anonymous procedure type; do a dynamic call !!!
                            throw new NotImplementedException("Dynamic call needs to be implemented.");
                        }
                        else
                        {
                            throw new ArgumentException("Not a procedure reference");
                        }
                    }
                }
                break;

                case SBExpressionType.ProcedureReference:
                {
                    var procRef   = left.Value as IProcedureReference;
                    var procedure = procRef.ProcedureData as FileProcedure;
                    returnType             = procedure.ReturnType;
                    delegateType           = procedure.DelegateType;
                    procedureReferenceType = procedure.ProcedureReferenceType;

                    if (delegateType != null)
                    {
                        var getStaticProcedureReferenceTyped = s_GetProcedureTyped.MakeGenericMethod(delegateType);
                        int fileID = Object.ReferenceEquals(procedure.ParentFile, m_file) ? -1 : ((ScriptFile)procedure.ParentFile).UniqueID;

                        procedureReferenceExp = Expression.Call(
                            getStaticProcedureReferenceTyped,
                            m_currentProcedure.ContextReferenceInternal,
                            Expression.Constant(fileID),
                            Expression.Constant(procedure.UniqueID));
                    }
                    else
                    {
                        int fileID = Object.ReferenceEquals(procedure.ParentFile, m_file) ? -1 : ((ScriptFile)procedure.ParentFile).UniqueID;

                        procedureReferenceExp = Expression.Call(
                            s_GetProcedure,
                            m_currentProcedure.ContextReferenceInternal,
                            Expression.Constant(fileID),
                            Expression.Constant(procedure.UniqueID));
                    }
                }
                break;

                default:
                    break;
                }

                theDelegate = Expression.Property(procedureReferenceExp, "RuntimeProcedure");

                if (isCallStatement)
                {
                    // C# version of the generated code:
                    // var callcontext = context.EnterNewScriptContext(MySecond, ContextLogOption.Normal).Disposer())
                    // try { MySecondProcedure(callcontext.Value, v1, v2); }
                    // finally { callcontext.Dispose(); }

                    var procRefVar = Expression.Variable(procedureReferenceType, "procRef");
                    var subCallContextContainer = Expression.Variable(typeof(InternalDisposer <IScriptCallContext>), "subCallContextContainer");
                    var subCallContext          = Expression.Property(subCallContextContainer, "Value");
                    // Replace the callContext argument with new context reference.
                    methodArguments[0] = new SBExpressionData(subCallContext);
                    var assignProcRefVar = Expression.Assign(procRefVar, procedureReferenceExp);

                    var createSubContext = Expression.New(
                        typeof(InternalDisposer <IScriptCallContext>).GetConstructor(new Type[] { typeof(IScriptCallContext) }),
                        Expression.Call(
                            m_currentProcedure.ContextReferenceInternal,
                            typeof(IScriptCallContext).GetMethod(nameof(IScriptCallContext.EnterNewScriptContext), new Type[] { typeof(IProcedureReference), typeof(ContextLogOption), typeof(bool) }),
                            procRefVar,
                            Expression.Constant(ContextLogOption.Normal),
                            Expression.Constant(false)));

                    var invokeMethod = delegateType.GetMethod("Invoke");

                    Expression callProcedure = Expression.Call(
                        Expression.Property(
                            procRefVar,
                            "RuntimeProcedure"),
                        invokeMethod,
                        methodArguments.Select(a => a.ExpressionCode).ToArray());
                    if (m_callAssignmentAwait)
                    {
                        callProcedure = this.MakeAwaitOperation(callProcedure, context, true, m_callAssignmentTarget?.DataType.Type);
                        returnType    = new TypeReference(callProcedure.Type);
                    }
                    if (m_callAssignmentTarget != null)
                    {
                        callProcedure = Expression.Assign(m_callAssignmentTarget.ExpressionCode, callProcedure);
                    }

                    var disposeSubContext = Expression.Call(
                        subCallContextContainer,
                        typeof(InternalDisposer <IScriptCallContext>).GetMethod("Dispose"));

                    var completeProcedureCall = Expression.Block(
                        new ParameterExpression[] { procRefVar, subCallContextContainer },
                        Expression.TryCatchFinally(
                            Expression.Block(
                                assignProcRefVar,
                                Expression.Assign(subCallContextContainer, createSubContext),
                                callProcedure,
                                Expression.Condition(
                                    Expression.Call(s_PostProcedureCallResultHandling, m_currentProcedure.ContextReferenceInternal, subCallContext),
                                    Expression.Return(m_currentProcedure.ReturnLabel, Expression.Default(m_currentProcedure.ReturnType.Type)),  // When told to exit the procedure now.
                                    Expression.Empty())
                                ),
                            disposeSubContext,
                            Expression.Catch(
                                typeof(Exception),
                                Expression.Rethrow())
                            ));

                    m_scopeStack.Peek().AddStatementCode(completeProcedureCall);
                }
                else
                {
                    var subCallContext = Expression.New(
                        typeof(FunctionCallContextWrapper).GetConstructor(
                            new Type[] { typeof(IScriptCallContext),
                                         typeof(int),
                                         typeof(int) }),
                        m_currentProcedure.ContextReferenceInternal,
                        Expression.Constant(context.Start.Line),
                        Expression.Constant(-1) /* TODO: get line and column from the 'left' parameter */);

                    methodArguments[0] = new SBExpressionData(subCallContext);

                    m_expressionData.Push(
                        new SBExpressionData(
                            HomeType.Immediate,
                            SBExpressionType.Expression,
                            returnType,
                            Expression.Call(
                                theDelegate,
                                delegateType.GetMethod("Invoke"),
                                methodArguments.Select(a => a.ExpressionCode).ToArray()),
                            null));
                }
            }
            else    // Not a procedure or function; a method.
            {
                switch (left.ReferencedType)
                {
                case SBExpressionType.Expression:
                case SBExpressionType.PropertyReference:
                case SBExpressionType.LocalVariableReference:
                    if (leftType.IsDelegate())
                    {
                        m_expressionData.Push(
                            new SBExpressionData(
                                HomeType.Immediate,
                                SBExpressionType.Expression,
                                (TypeReference)(leftType.GetMethod("Invoke").ReturnType),
                                Expression.Invoke(left.ExpressionCode, methodArguments.Select(a => a.ExpressionCode).ToArray()),
                                null));
                    }
                    else
                    {
                        throw new NotImplementedException("Can we end up here?");
                    }
                    break;

                case SBExpressionType.MethodReference:
                {
                    Expression callMethod = Expression.Call(
                        instance,
                        selectedMethod,
                        methodArguments.Select(a => a.ExpressionCode).ToArray());
                    if (isCallStatement)
                    {
                        if (m_callAssignmentAwait)
                        {
                            callMethod = this.MakeAwaitOperation(callMethod, context, true, m_callAssignmentTarget?.DataType.Type);
                            returnType = new TypeReference(callMethod.Type);
                        }
                        if (m_callAssignmentTarget != null)
                        {
                            callMethod = Expression.Assign(m_callAssignmentTarget.ExpressionCode, callMethod);
                        }
                        m_scopeStack.Peek().AddStatementCode(callMethod);
                    }
                    else
                    {
                        m_expressionData.Push(
                            new SBExpressionData(
                                HomeType.Immediate,
                                SBExpressionType.Expression,
                                returnType,
                                callMethod,
                                null));
                    }
                }
                break;

                default:
                    break;
                }
            }

            #endregion
        }