Ejemplo n.º 1
0
        private void EmitFunctionDefinitionNode(ImperativeNode node, ref ProtoCore.Type inferedType)
        {
            bool parseGlobalFunctionSig = null == localProcedure && ProtoCore.DSASM.ImperativeCompilePass.kGlobalFuncSig == compilePass;
            bool parseGlobalFunctionBody = null == localProcedure && ProtoCore.DSASM.ImperativeCompilePass.kGlobalFuncBody == compilePass;

            FunctionDefinitionNode funcDef = node as FunctionDefinitionNode;
            localFunctionDefNode = funcDef;

            ProtoCore.DSASM.CodeBlockType originalBlockType = codeBlock.blockType;
            codeBlock.blockType = ProtoCore.DSASM.CodeBlockType.kFunction;
            if (parseGlobalFunctionSig)
            {
                Debug.Assert(null == localProcedure);

                // TODO jun: Add semantics for checking overloads (different parameter types)
                localProcedure = new ProtoCore.DSASM.ProcedureNode();
                localProcedure.name = funcDef.Name;
                localProcedure.pc = pc;
                localProcedure.localCount = funcDef.localVars;
                localProcedure.returntype.UID = compileStateTracker.TypeSystem.GetType(funcDef.ReturnType.Name);
                if (localProcedure.returntype.UID == (int)PrimitiveType.kInvalidType)
                {
                    string message = String.Format(ProtoCore.BuildData.WarningMessage.kReturnTypeUndefined, funcDef.ReturnType.Name, funcDef.Name);
                    buildStatus.LogWarning(ProtoCore.BuildData.WarningID.kTypeUndefined, message, null, funcDef.line, funcDef.col);
                    localProcedure.returntype.UID = (int)PrimitiveType.kTypeVar;
                }
                localProcedure.returntype.IsIndexable = funcDef.ReturnType.IsIndexable;
                localProcedure.returntype.rank = funcDef.ReturnType.rank;
                localProcedure.runtimeIndex = codeBlock.codeBlockId;
                globalProcIndex = codeBlock.procedureTable.Append(localProcedure);
                compileStateTracker.ProcNode = localProcedure;

                // Append arg symbols
                if (null != funcDef.Signature)
                {
                    foreach (VarDeclNode argNode in funcDef.Signature.Arguments)
                    {
                        IdentifierNode paramNode = null;
                        bool aIsDefault = false;
                        ProtoCore.AST.Node aDefaultExpression = null;
                        if (argNode.NameNode is IdentifierNode)
                        {
                            paramNode = argNode.NameNode as IdentifierNode;
                        }
                        else if (argNode.NameNode is BinaryExpressionNode)
                        {
                            BinaryExpressionNode bNode = argNode.NameNode as BinaryExpressionNode;
                            paramNode = bNode.LeftNode as IdentifierNode;
                            aIsDefault = true;
                            aDefaultExpression = bNode;
                            //buildStatus.LogSemanticError("Defualt parameters are not supported");
                            //throw new BuildHaltException();
                        }
                        else
                        {
                            Debug.Assert(false, "Check generated AST");
                        }

                        ProtoCore.Type argType = BuildArgumentTypeFromVarDeclNode(argNode);
                        int symbolIndex = AllocateArg(paramNode.Value, localProcedure.procId, argType);
                        if (ProtoCore.DSASM.Constants.kInvalidIndex == symbolIndex)
                        {
                            throw new BuildHaltException("26384684");
                        }

                        localProcedure.argTypeList.Add(argType);
                        ProtoCore.DSASM.ArgumentInfo argInfo = new ProtoCore.DSASM.ArgumentInfo { isDefault = aIsDefault, defaultExpression = aDefaultExpression };
                        localProcedure.argInfoList.Add(argInfo);
                    }
                }

                // TODO Jun: Remove this once agree that alltest cases assume the default assoc block is block 0
                // NOTE: Only affects mirror, not actual execution
                if (null == codeBlock.parent && pc <= 0)
                {
                    // The first node in the top level block is a function
                    compileStateTracker.DSExecutable.isSingleAssocBlock = false;
                }
            #if ENABLE_EXCEPTION_HANDLING
                core.ExceptionHandlingManager.Register(codeBlock.codeBlockId, globalProcIndex, globalClassIndex);
            #endif
            }
            else if (parseGlobalFunctionBody)
            {
                EmitCompileLogFunctionStart(GetFunctionSignatureString(funcDef.Name, funcDef.ReturnType, funcDef.Signature));

                // Build arglist for comparison
                List<ProtoCore.Type> argList = new List<ProtoCore.Type>();
                if (null != funcDef.Signature)
                {
                    foreach (VarDeclNode argNode in funcDef.Signature.Arguments)
                    {
                        ProtoCore.Type argType = BuildArgumentTypeFromVarDeclNode(argNode);
                        argList.Add(argType);
                    }
                }

                // Get the exisitng procedure that was added on the previous pass
                globalProcIndex = codeBlock.procedureTable.IndexOfExact(funcDef.Name, argList);
                localProcedure = codeBlock.procedureTable.procList[globalProcIndex];

                Debug.Assert(null != localProcedure);
                localProcedure.Attributes = PopulateAttributes(funcDef.Attributes);
                // Its only on the parse body pass where the real pc is determined. Update this procedures' pc
                //Debug.Assert(ProtoCore.DSASM.Constants.kInvalidIndex == localProcedure.pc);
                localProcedure.pc = pc;

                // Copy the active function to the core so nested language blocks can refer to it
                compileStateTracker.ProcNode = localProcedure;

                // Arguments have been allocated, update the baseOffset
                localProcedure.localCount = compileStateTracker.BaseOffset;

                ProtoCore.FunctionEndPoint fep = null;

                //Traverse default argument
                emitDebugInfo = false;
                foreach (ProtoCore.DSASM.ArgumentInfo argNode in localProcedure.argInfoList)
                {
                    if (!argNode.isDefault)
                    {
                        continue;
                    }
                    BinaryExpressionNode bNode = argNode.defaultExpression as BinaryExpressionNode;

                    // build a temporay node for statement : temp = defaultarg;
                    var iNodeTemp = nodeBuilder.BuildIdentfier(Constants.kTempDefaultArg);
                    BinaryExpressionNode bNodeTemp = nodeBuilder.BuildBinaryExpression(iNodeTemp, bNode.LeftNode) as BinaryExpressionNode;
                    EmitBinaryExpressionNode(bNodeTemp, ref inferedType);

                    //duild an inline conditional node for statement: defaultarg = (temp == DefaultArgNode) ? defaultValue : temp;
                    InlineConditionalNode icNode = new InlineConditionalNode();
                    icNode.ConditionExpression = nodeBuilder.BuildBinaryExpression(iNodeTemp, new DefaultArgNode(), Operator.eq);
                    icNode.TrueExpression = bNode.RightNode;
                    icNode.FalseExpression = iNodeTemp;
                    bNodeTemp.LeftNode = bNode.LeftNode;
                    bNodeTemp.RightNode = icNode;
                    EmitBinaryExpressionNode(bNodeTemp, ref inferedType);
                }
                emitDebugInfo = true;

                // Traverse definition
                bool hasReturnStatement = false;
                foreach (ImperativeNode bnode in funcDef.FunctionBody.Body)
                {
                    DfsTraverse(bnode, ref inferedType);
                    if (ProtoCore.Utils.NodeUtils.IsReturnExpressionNode(bnode))
                    {
                        hasReturnStatement = true;
                    }

                    if (bnode is FunctionCallNode)
                    {
                        EmitSetExpressionUID(compileStateTracker.ExpressionUID++);
                    }
                }

                // All locals have been stack allocated, update the local count of this function
                localProcedure.localCount = compileStateTracker.BaseOffset;

                // Update the param stack indices of this function
                foreach (ProtoCore.DSASM.SymbolNode symnode in codeBlock.symbolTable.symbolList.Values)
                {
                    if (symnode.functionIndex == localProcedure.procId && symnode.isArgument)
                    {
                        symnode.index -= localProcedure.localCount;
                    }
                }

                ProtoCore.Lang.JILActivationRecord record = new ProtoCore.Lang.JILActivationRecord();
                record.pc = localProcedure.pc;
                record.locals = localProcedure.localCount;
                record.classIndex = ProtoCore.DSASM.Constants.kInvalidIndex;
                record.funcIndex = localProcedure.procId;
                fep = new ProtoCore.Lang.JILFunctionEndPoint(record);

                // Construct the fep arguments
                fep.FormalParams = new ProtoCore.Type[localProcedure.argTypeList.Count];
                fep.BlockScope = codeBlock.codeBlockId;
                fep.procedureNode = localProcedure;
                localProcedure.argTypeList.CopyTo(fep.FormalParams, 0);

                // TODO Jun: 'classIndexAtCallsite' is the class index as it is stored at the callsite function tables
                // Determine whether this still needs to be aligned to the actual 'classIndex' variable
                // The factors that will affect this is whether the 2 function tables (compiler and callsite) need to be merged
                int classIndexAtCallsite = ProtoCore.DSASM.Constants.kInvalidIndex + 1;
                compileStateTracker.FunctionTable.AddFunctionEndPointer(classIndexAtCallsite, funcDef.Name, fep);

                if (!hasReturnStatement)
                {
                    if (!compileStateTracker.Options.SuppressFunctionResolutionWarning)
                    {
                        string message = String.Format(ProtoCore.BuildData.WarningMessage.kFunctionNotReturnAtAllCodePaths, localProcedure.name);
                        compileStateTracker.BuildStatus.LogWarning(ProtoCore.BuildData.WarningID.kMissingReturnStatement, message, compileStateTracker.CurrentDSFileName, funcDef.line, funcDef.col);
                    }

                    EmitReturnNull();
                }

                EmitCompileLogFunctionEnd();
                //Fuqiang: return is already done in traversing the function body
                //// function return
                //EmitInstrConsole(ProtoCore.DSASM.kw.ret);
                //EmitReturn();
            }

            compileStateTracker.ProcNode = localProcedure = null;
            globalProcIndex = ProtoCore.DSASM.Constants.kGlobalScope;
            argOffset = 0;
            compileStateTracker.BaseOffset = 0;
            codeBlock.blockType = originalBlockType;
            localFunctionDefNode = null;
        }
Ejemplo n.º 2
0
        private void EmitFunctionDefinitionNode(ImperativeNode node, ref ProtoCore.Type inferedType)
        {
            bool parseGlobalFunctionSig = null == localProcedure && ProtoCore.CompilerDefinitions.Imperative.CompilePass.kGlobalFuncSig == compilePass;
            bool parseGlobalFunctionBody = null == localProcedure && ProtoCore.CompilerDefinitions.Imperative.CompilePass.kGlobalFuncBody == compilePass;

            FunctionDefinitionNode funcDef = node as FunctionDefinitionNode;
            localFunctionDefNode = funcDef;

            ProtoCore.DSASM.CodeBlockType originalBlockType = codeBlock.blockType;
            codeBlock.blockType = ProtoCore.DSASM.CodeBlockType.kFunction;
            if (parseGlobalFunctionSig)
            {
                Validity.Assert(null == localProcedure);


                // TODO jun: Add semantics for checking overloads (different parameter types)
                localProcedure = new ProtoCore.DSASM.ProcedureNode();
                localProcedure.Name = funcDef.Name;
                localProcedure.PC = pc;
                localProcedure.LocalCount = funcDef.localVars;
                var returnType = new ProtoCore.Type();
                returnType.UID = core.TypeSystem.GetType(funcDef.ReturnType.Name);
                if (returnType.UID == (int)PrimitiveType.kInvalidType)
                {
                    string message = String.Format(ProtoCore.Properties.Resources.kReturnTypeUndefined, funcDef.ReturnType.Name, funcDef.Name);
                    buildStatus.LogWarning(ProtoCore.BuildData.WarningID.kTypeUndefined, message, null, funcDef.line, funcDef.col, firstSSAGraphNode);
                    returnType.UID = (int)PrimitiveType.kTypeVar;
                }
                returnType.rank = funcDef.ReturnType.rank;
                localProcedure.ReturnType = returnType;
                localProcedure.RuntimeIndex = codeBlock.codeBlockId;
                globalProcIndex = codeBlock.procedureTable.Append(localProcedure);
                core.ProcNode = localProcedure;


                // Append arg symbols
                if (null != funcDef.Signature)
                {
                    foreach (VarDeclNode argNode in funcDef.Signature.Arguments)
                    {
                        IdentifierNode paramNode = null;
                        ProtoCore.AST.Node aDefaultExpression = null;
                        if (argNode.NameNode is IdentifierNode)
                        {
                            paramNode = argNode.NameNode as IdentifierNode;
                        }
                        else if (argNode.NameNode is BinaryExpressionNode)
                        {
                            BinaryExpressionNode bNode = argNode.NameNode as BinaryExpressionNode;
                            paramNode = bNode.LeftNode as IdentifierNode;
                            aDefaultExpression = bNode;
                        }
                        else
                        {
                            Validity.Assert(false, "Check generated AST");
                        }

                        ProtoCore.Type argType = BuildArgumentTypeFromVarDeclNode(argNode, firstSSAGraphNode);
                        int symbolIndex = AllocateArg(paramNode.Value, localProcedure.ID, argType);
                        if (ProtoCore.DSASM.Constants.kInvalidIndex == symbolIndex)
                        {
                            throw new BuildHaltException("26384684");
                        }

                        localProcedure.ArgumentTypes.Add(argType);
                        ProtoCore.DSASM.ArgumentInfo argInfo = new ProtoCore.DSASM.ArgumentInfo { DefaultExpression = aDefaultExpression };
                        localProcedure.ArgumentInfos.Add(argInfo);
                    }
                }         
            }
            else if (parseGlobalFunctionBody)
            {
                EmitCompileLogFunctionStart(GetFunctionSignatureString(funcDef.Name, funcDef.ReturnType, funcDef.Signature));

                // Build arglist for comparison
                List<ProtoCore.Type> argList = new List<ProtoCore.Type>();
                if (null != funcDef.Signature)
                {
                    foreach (VarDeclNode argNode in funcDef.Signature.Arguments)
                    {
                        ProtoCore.Type argType = BuildArgumentTypeFromVarDeclNode(argNode, firstSSAGraphNode);
                        argList.Add(argType);
                    }
                }

                // Get the exisitng procedure that was added on the previous pass
                globalProcIndex = codeBlock.procedureTable.IndexOfExact(funcDef.Name, argList, false);
                localProcedure = codeBlock.procedureTable.procList[globalProcIndex];


                Validity.Assert(null != localProcedure);
                localProcedure.Attributes = PopulateAttributes(funcDef.Attributes);
                // Its only on the parse body pass where the real pc is determined. Update this procedures' pc
                //Validity.Assert(ProtoCore.DSASM.Constants.kInvalidIndex == localProcedure.pc);
                localProcedure.PC = pc;

                // Copy the active function to the core so nested language blocks can refer to it
                core.ProcNode = localProcedure;

                // Arguments have been allocated, update the baseOffset
                localProcedure.LocalCount = core.BaseOffset;


                ProtoCore.FunctionEndPoint fep = null;
                                
                //Traverse default argument
                emitDebugInfo = false;
                foreach (ProtoCore.DSASM.ArgumentInfo argNode in localProcedure.ArgumentInfos)
                {
                    if (!argNode.IsDefault)
                    {
                        continue;
                    }
                    BinaryExpressionNode bNode = argNode.DefaultExpression as BinaryExpressionNode;

                    // build a temporay node for statement : temp = defaultarg;
                    var iNodeTemp = nodeBuilder.BuildIdentfier(Constants.kTempDefaultArg);
                    BinaryExpressionNode bNodeTemp = nodeBuilder.BuildBinaryExpression(iNodeTemp, bNode.LeftNode) as BinaryExpressionNode;
                    EmitBinaryExpressionNode(bNodeTemp, ref inferedType);

                    //duild an inline conditional node for statement: defaultarg = (temp == DefaultArgNode) ? defaultValue : temp;
                    InlineConditionalNode icNode = new InlineConditionalNode();
                    icNode.ConditionExpression = nodeBuilder.BuildBinaryExpression(iNodeTemp, new DefaultArgNode(), Operator.eq);
                    icNode.TrueExpression = bNode.RightNode;
                    icNode.FalseExpression = iNodeTemp;
                    bNodeTemp.LeftNode = bNode.LeftNode;
                    bNodeTemp.RightNode = icNode;
                    EmitBinaryExpressionNode(bNodeTemp, ref inferedType);
                }
                emitDebugInfo = true;

                // Traverse definition
                bool hasReturnStatement = false;
                foreach (ImperativeNode bnode in funcDef.FunctionBody.Body)
                {
                    DfsTraverse(bnode, ref inferedType);
                    if (ProtoCore.Utils.NodeUtils.IsReturnExpressionNode(bnode))
                    {
                        hasReturnStatement = true;
                    }

                    if (bnode is FunctionCallNode)
                    {
                        EmitSetExpressionUID(core.ExpressionUID++);
                    }
                }

                // All locals have been stack allocated, update the local count of this function
                localProcedure.LocalCount = core.BaseOffset;

                // Update the param stack indices of this function
                foreach (ProtoCore.DSASM.SymbolNode symnode in codeBlock.symbolTable.symbolList.Values)
                {
                    if (symnode.functionIndex == localProcedure.ID && symnode.isArgument)
                    {
                        symnode.index -= localProcedure.LocalCount;
                    }
                }

                ProtoCore.Lang.JILActivationRecord record = new ProtoCore.Lang.JILActivationRecord();
                record.pc = localProcedure.PC;
                record.locals = localProcedure.LocalCount;
                record.classIndex = ProtoCore.DSASM.Constants.kInvalidIndex;
                record.funcIndex = localProcedure.ID;
                fep = new ProtoCore.Lang.JILFunctionEndPoint(record);



                // Construct the fep arguments
                fep.FormalParams = new ProtoCore.Type[localProcedure.ArgumentTypes.Count];
                fep.BlockScope = codeBlock.codeBlockId;
                fep.procedureNode = localProcedure;
                localProcedure.ArgumentTypes.CopyTo(fep.FormalParams, 0);

                // TODO Jun: 'classIndexAtCallsite' is the class index as it is stored at the callsite function tables
                // Determine whether this still needs to be aligned to the actual 'classIndex' variable
                // The factors that will affect this is whether the 2 function tables (compiler and callsite) need to be merged
                int classIndexAtCallsite = ProtoCore.DSASM.Constants.kInvalidIndex + 1;
                if (!core.FunctionTable.GlobalFuncTable.ContainsKey(classIndexAtCallsite))
                {
                    Dictionary<string, FunctionGroup> funcList = new Dictionary<string, FunctionGroup>();
                    core.FunctionTable.GlobalFuncTable.Add(classIndexAtCallsite, funcList);
                }

                Dictionary<string, FunctionGroup> fgroup = core.FunctionTable.GlobalFuncTable[classIndexAtCallsite];
                if (!fgroup.ContainsKey(funcDef.Name))
                {
                    // Create a new function group in this class
                    ProtoCore.FunctionGroup funcGroup = new ProtoCore.FunctionGroup();
                    funcGroup.FunctionEndPoints.Add(fep);

                    // Add this group to the class function tables
                    core.FunctionTable.GlobalFuncTable[classIndexAtCallsite].Add(funcDef.Name, funcGroup);
                }
                else
                {
                    // Add this fep into the exisitng function group
                    core.FunctionTable.GlobalFuncTable[classIndexAtCallsite][funcDef.Name].FunctionEndPoints.Add(fep);
                }

                if (!hasReturnStatement)
                {
                    if (!core.Options.SuppressFunctionResolutionWarning)
                    {
                        string message = String.Format(ProtoCore.Properties.Resources.kFunctionNotReturnAtAllCodePaths, localProcedure.Name);
                        core.BuildStatus.LogWarning(ProtoCore.BuildData.WarningID.kMissingReturnStatement, message, core.CurrentDSFileName, funcDef.line, funcDef.col, firstSSAGraphNode);
                    }

                    EmitReturnNull();
                }

                EmitCompileLogFunctionEnd();
                //Fuqiang: return is already done in traversing the function body
                //// function return
                //EmitInstrConsole(ProtoCore.DSASM.kw.ret);
                //EmitReturn();
            }

            core.ProcNode = localProcedure = null;
            globalProcIndex = ProtoCore.DSASM.Constants.kGlobalScope;
            argOffset = 0;
            core.BaseOffset = 0;
            codeBlock.blockType = originalBlockType;
            localFunctionDefNode = null;
        }