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; }
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; }