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 }