protected override bool ParseCilInstructionInternal(Instruction instruction, ProgramState state) { switch (instruction.OpCode.Code) { case Code.Newarr: Typ arrayContentType; // The array is one-dimensional. if (instruction.Operand is TypeReference instructionType) { arrayContentType = Typ.FromTypeReference(instructionType); } // Then the content type of the array is an array (it is multidimensional). else if (instruction.Operand is ArrayType instructionArrayType) { // Creates a SIL representation of the array content type. arrayContentType = CreateArrayType( Typ.FromTypeReference(instructionArrayType.GetElementType()), instructionArrayType.Rank, state); } else { Log.WriteParserWarning(instruction.Operand, instruction, state); return(false); } var arrayIdentifier = state.GetIdentifier(Identifier.IdentKind.Normal); (var arrayLength, _) = state.Pop(); var arrayLengthSizeofExp = new SizeofExpression( new Tarray(arrayContentType), SizeofExpression.SizeofExpressionKind.exact, arrayLength); var arrayTypeWithPtr = new Tptr(Tptr.PtrKind.Pk_pointer, new Tarray(arrayContentType)); var args = new List <Call.CallArg> { new Call.CallArg(arrayLengthSizeofExp, arrayTypeWithPtr) }; // Represents memory allocation. var callInstr = new Call(returnId: arrayIdentifier, returnType: arrayTypeWithPtr, functionExpression: new ConstExpression( ProcedureName.BuiltIn__new_array), args: args, flags: new Call.CallFlags(), location: state.CurrentLocation); var newNode = AddMethodBodyInstructionsToCfg(state, callInstr); state.PushExpr(new VarExpression(arrayIdentifier), arrayTypeWithPtr); state.PushInstruction(instruction.Next, newNode); return(true); default: return(false); } }
/// <summary> /// Creates a local variable from a given method and variable index; creates the address of /// a local variable if the appropriate parameter value is specified. /// </summary> /// <param name="index">An identifier for a local variable. A compiled local variable loses /// its variable name, which is replaced instead by an index.</param> /// <param name="method">The method in which the local variable is initialized.</param> /// <returns>The local variable expression (or address) and its associated type.</returns> protected (LvarExpression, Typ) CreateLocal(int index, MethodDefinition method) { var name = LocalName(index); // If the variable is by reference, its corresponding type will have an &, which we do // not want. var variableType = method.Body.Variables[index].VariableType; return(new LvarExpression(new LocalVariable(name, method)), Typ.FromTypeReference(variableType)); }
protected override bool ParseCilInstructionInternal(Instruction instruction, ProgramState state) { switch (instruction.OpCode.Code) { // Object construction in SIL is represented as follows: // n${i} = _fun___new (sizeof (ExampleClass)) // n${i+1} = _fun_ExampleClass.ctor(ni) // x = n${i}, where x is the program variable case Code.Newobj: var constructorMethod = instruction.Operand as MethodReference; var objectTypeReference = constructorMethod.DeclaringType; (var memoryAllocationCall, var objectVariable) = CreateMemoryAllocationCall( objectTypeReference, state); state.PushExpr(objectVariable, Typ.FromTypeReference(objectTypeReference)); // Represents constructor call; we discard the return var as it's not needed. CreateMethodCall(state, false, constructorMethod, out _, out _, out _, out var constructorCall, isConstructorCall: true); var newNode = new StatementNode(location: state.CurrentLocation, kind: StatementNode.StatementNodeKind.Call, proc: state.ProcDesc, comment: constructorMethod .GetCompatibleFullName()); newNode.Instructions.Add(memoryAllocationCall); newNode.Instructions.Add(constructorCall); RegisterNode(state, newNode); // The first copy of this stack item was popped in the invocation of the // constructor, so we push another on. state.PushExpr(objectVariable, Typ.FromTypeReference(objectTypeReference)); state.PushInstruction(instruction.Next, newNode); // Append the next instruction (which should be stloc, for representing // storage of the constructed object into a local variable) to this new node. state.AppendToPreviousNode = true; return(true); default: return(false); } }
protected override bool ParseCilInstructionInternal(Instruction instruction, ProgramState state) { switch (instruction.OpCode.Code) { case Code.Ldnull: state.PushExpr( new ConstExpression(new IntRepresentation(0, false, true)), Typ.FromTypeReference(state.Method.Module.TypeSystem.Object)); state.PushInstruction(instruction.Next); return(true); default: return(false); } }
/// <summary> /// Initializes a new instance of the <see cref="ProcedureDescription"/> class. /// </summary> /// <param name="methodDefinition">The <see cref="MethodDefinition"/> from which to create /// the description.</param> /// <param name="cfg">The CFG with which this procedure description is associated.</param> public ProcedureDescription(MethodDefinition methodDefinition, Cfg cfg) { PdId = NextId; var parameters = methodDefinition.Parameters.Select( p => new VariableDescription(p.Name, Typ.FromTypeReference(p.ParameterType))); if (!methodDefinition.IsStatic) { parameters = parameters.Prepend( new VariableDescription(Identifier.ThisIdentifier, Typ.FromTypeReference( methodDefinition.DeclaringType))); } var location = Location.FromSequencePoint( methodDefinition.DebugInformation.SequencePoints.FirstOrDefault()); PdAttributes = new ProcedureAttributes() { Access = methodDefinition.IsPublic ? ProcedureAttributes.ProcedureAccessKind.Public : methodDefinition.IsPrivate ? ProcedureAttributes.ProcedureAccessKind.Private : ProcedureAttributes.ProcedureAccessKind.Default, Formals = parameters.ToList(), RetType = Typ.FromTypeReference(methodDefinition.ReturnType), Loc = location, ProcName = new ProcedureName(methodDefinition) }; Nodes = new List <CfgNode>(); StartNode = new StartNode(location, this); ExitNode = new ExitNode(Location.FromSequencePoint(methodDefinition .DebugInformation .SequencePoints .FirstOrDefault()), this); ExceptionSinkNode = new StatementNode(location, StatementNode.StatementNodeKind.ExceptionsSink, proc: this); cfg.RegisterNode(StartNode); cfg.RegisterNode(ExitNode); cfg.RegisterNode(ExceptionSinkNode); }
/// <summary> /// Creates an entry node for representing exceptional control flow into an exception /// handler; in it, the return value is nullified and the unwrap exception function is /// applied to it, which causes the exception to be stored in a catch variable. /// </summary> /// <param name="state">The state.</param> /// <param name="handler">The exception handler for which the node is being /// created.</param> /// <returns>The created entry node, as well as the identifier in which the exception is /// stored.</returns> private static (CfgNode, Identifier) CreateExceptionEntryNode(ProgramState state, ExceptionHandler handler) { var handlerStartLocation = GetHandlerStartLocation(state, handler); var returnIdentifier = state.GetIdentifier(Identifier.IdentKind.Normal); var returnExpression = new LvarExpression( new LocalVariable(Identifier.ReturnIdentifier, state.Method)); var returnType = Typ.FromTypeReference(state.Method.ReturnType); var getReturnValue = new Load(returnIdentifier, returnExpression, returnType, handlerStartLocation); var deactivateException = new Store(returnExpression, new ConstExpression( new IntRepresentation(0, false, true)), returnType, handlerStartLocation); var exceptionIdentifier = state.GetIdentifier(Identifier.IdentKind.Normal); var unwrapReturnValue = new Call(exceptionIdentifier, returnType, new ConstExpression( ProcedureName.BuiltIn__unwrap_exception), new List <Call.CallArg> { new Call.CallArg( new VarExpression(returnIdentifier), returnType) }, new Call.CallFlags(), handlerStartLocation); var node = new StatementNode(handlerStartLocation, StatementNode.StatementNodeKind.ExceptionHandler, state.ProcDesc); node.Instructions = new List <SilInstruction> { getReturnValue, deactivateException, unwrapReturnValue }; state.Cfg.RegisterNode(node); return(node, exceptionIdentifier); }
/// <summary> /// Creates and returns a <see cref="Call"/> instruction indicating object memory /// allocation and the temporary identifier for the new object. Examples of CIL /// instructions for which this method is used include initobj and newobj. /// </summary> /// <param name="newObjectReference">The type to be allocated.</param> /// <param name="state">Current program state.</param> /// <returns>Instruction representing the memory allocation, as well as the identifier for /// the new object.</returns> protected static (Call, VarExpression) CreateMemoryAllocationCall( TypeReference newObjectReference, ProgramState state) { var type = Typ.FromTypeReference(newObjectReference); var newObjectIdentifier = state.GetIdentifier(Identifier.IdentKind.Normal); var callFlags = new Call.CallFlags(isVirtual: false, noReturn: false, isObjCBlock: false); var args = new List <Call.CallArg> { new Call.CallArg(new SizeofExpression(type.StripPointer(), "exact"), type) }; return(new Call(newObjectIdentifier, type, new ConstExpression(ProcedureName.BuiltIn__new), args, callFlags, state.CurrentLocation), new VarExpression(newObjectIdentifier)); }
/// <summary> /// Creates a node for returning an exceptional value; does not register the node in the /// CFG. /// </summary> /// <param name="state">The state.</param> /// <param name="returnValue">The exceptional value to be returned.</param> /// <param name="location">The location.</param> /// <returns></returns> protected static CfgNode CreateExceptionReturnNode(ProgramState state, Expression returnValue, Location location) { var retType = state.Method.ReturnType.GetElementType(); var retNode = new StatementNode(location, StatementNode.StatementNodeKind.ReturnStmt, state.ProcDesc); var returnVariable = new LvarExpression(new LocalVariable(Identifier.ReturnIdentifier, state.Method)); var retInstr = new Store(returnVariable, new ExnExpression(returnValue), Typ.FromTypeReference(retType), location); retNode.Instructions.Add(retInstr); retNode.Successors = new List <CfgNode> { state.ProcDesc.ExitNode }; return(retNode); }
/// <summary> /// Creates a method call returned via out parameter. /// </summary> /// <param name="state">Current program state.</param> /// <param name="isVirtual">True if method call is virtual, false otherwise.</param> /// <param name="calledMethod">The method being called.</param> /// <param name="returnType">The return type of the method being called.</param> /// <param name="returnVariable">Identifies the variable returned by the method.</param> /// <param name="callArgs">The method arguments.</param> /// <param name="methodCall">The Call SIL instruction.</param> /// <param name="isConstructorCall"><c>true</c> if the call is for a constructor, /// <c>false</c> otherwise.</param> protected static void CreateMethodCall(ProgramState state, bool isVirtual, MethodReference calledMethod, out TypeReference returnType, out Identifier returnVariable, out List <Call.CallArg> callArgs, out Call methodCall, bool isConstructorCall = false) { callArgs = new List <Call.CallArg>(); returnType = calledMethod.ReturnType; var paramCount = calledMethod.Parameters.Count; if (calledMethod.HasThis) { paramCount++; } if (isConstructorCall) { // In this case, the "this" argument of the constructor is located at the top of // the stack; we remove it and place it at the front of the argument list. (var thisExpr, var thisType) = state.Pop(); callArgs.Add(new Call.CallArg(thisExpr, thisType)); paramCount--; } var funcExp = new ConstExpression(new ProcedureName(calledMethod)); callArgs.AddRange(state.PopMany(paramCount) .Select(p => new Call.CallArg(p.Item1, p.Item2)) .ToList()); var callFlags = new Call.CallFlags(isVirtual, false, false); returnVariable = state.GetIdentifier(Identifier.IdentKind.Normal); methodCall = new Call(returnId: returnVariable, returnType: Typ.FromTypeReference(returnType), functionExpression: funcExp, args: callArgs, flags: callFlags, location: state.CurrentLocation); }
protected override bool ParseCilInstructionInternal(Instruction instruction, ProgramState state) { switch (instruction.OpCode.Code) { case Code.Ret: var retType = state.Method.ReturnType.GetElementType(); if (retType == state.Method.Module.TypeSystem.Void) { state.PreviousNode.Successors.Add(state.ProcDesc.ExitNode); } else { (var returnValue, _) = state.Pop(); Expression returnVariable = new LvarExpression( new LocalVariable(Identifier.ReturnIdentifier, state.Method)); var retInstr = new Store(lvalue: returnVariable, rvalue: returnValue, type: Typ.FromTypeReference(retType), location: state.CurrentLocation); var retNode = new StatementNode(state.CurrentLocation, StatementNode.StatementNodeKind.ReturnStmt, state.ProcDesc); retNode.Instructions.Add(retInstr); retNode.Successors = new List <CfgNode> { state.ProcDesc.ExitNode }; RegisterNode(state, retNode); } return(true); default: return(false); } }
/// <summary> /// Creates the argument variable. /// </summary> /// <param name="index">The argument's index.</param> /// <param name="method">The method with which the argument is associated.</param> /// <returns>Expression representing the argument and its type.</returns> protected (LvarExpression, Typ) CreateArg(int index, MethodDefinition method) { TypeReference type; var name = ArgumentName(index, method); if (method.HasThis) { if (index == 0) { type = method.DeclaringType; } else { type = method.Parameters[index - 1].ParameterType; } } else { type = method.Parameters[index].ParameterType; } return(new LvarExpression(new LocalVariable(name, method)), Typ.FromTypeReference(type)); }
protected override bool ParseCilInstructionInternal(Instruction instruction, ProgramState state) { bool isVirtual; switch (instruction.OpCode.Code) { case Code.Call: isVirtual = false; break; case Code.Callvirt: isVirtual = true; break; default: return(false); } var instrs = new List <SilInstruction>(); var calledMethod = instruction.Operand as MethodReference; if (calledMethod.GetCompatibleFullName() .Contains("System.Void System.Threading.Monitor::Enter")) { state.ProcDesc.PdAttributes.IsCSharpSynchronizedMethod = true; instrs.Add(CreateLockedAttributeCall(true, calledMethod.Parameters.Count, state)); } else if (calledMethod.GetCompatibleFullName() .Contains("System.Void System.Threading.Monitor::Exit")) { instrs.Add(CreateLockedAttributeCall(false, calledMethod.Parameters.Count, state)); } else { CreateMethodCall(state, isVirtual, calledMethod, out var retTypeRef, out var retId, out var callArgs, out var callInstr); instrs.Add(callInstr); // Deref on the object calling in the case of instance method, for null // validation on it by Infer. Object "this" is the first argument in the argument list // for instance methods. if (calledMethod.HasThis && calledMethod.Name != Identifier.ConstructorIdentifier) { var thisArg = callArgs.First(); if (thisArg.Expression is VarExpression varExpression && !varExpression.FromThis) { instrs.Insert(0, CreateDereference(varExpression, thisArg.Type, state)); } } var returnType = Typ.FromTypeReference(retTypeRef); if (!(returnType is Tvoid)) { state.PushExpr(new VarExpression(retId), returnType); } } var callNode = new StatementNode(location: state.CurrentLocation, kind: StatementNode.StatementNodeKind.Call, proc: state.ProcDesc, comment: calledMethod.GetCompatibleFullName()); callNode.Instructions.AddRange(instrs); RegisterNode(state, callNode); state.PushInstruction(instruction.Next, callNode); state.AppendToPreviousNode = true; return(true); }
protected override bool ParseCilInstructionInternal(Instruction instruction, ProgramState state) { switch (instruction.OpCode.Code) { case Code.Ret: Store retInstr; var retType = state.Method.ReturnType.GetElementType(); var retNode = new StatementNode(state.CurrentLocation, StatementNode.StatementNodeKind.ReturnStmt, state.ProcDesc); if (retType == state.Method.Module.TypeSystem.Void) { state.PreviousNode.Successors.Add(state.ProcDesc.ExitNode); } else { (var returnValue, _) = state.Pop(); Expression returnVariable = new LvarExpression( new LocalVariable(Identifier.ReturnIdentifier, state.Method)); if (returnValue is BinopExpression) { // We see that for the auto-generated method op_Inequality in records, // an equality expression is pushed directly onto the stack and // returned. However, return of an expression is not valid in the SIL -- // we must inline a variable store and load of the value prior to // subsequently returning it. var inlineReturn = new LocalVariable("inlineReturn", state.Method); var inlineIdentifier = state.GetIdentifier(Identifier.IdentKind.Normal); var storeInlineReturn = new Store(new LvarExpression(inlineReturn), returnValue, Typ.FromTypeReference(retType), state.CurrentLocation); AddMethodBodyInstructionsToCfg(state, storeInlineReturn); var loadInlineReturn = new Load(inlineIdentifier, new LvarExpression(inlineReturn), Typ.FromTypeReference(retType), state.CurrentLocation); AddMethodBodyInstructionsToCfg(state, loadInlineReturn); retInstr = new Store(returnVariable, new VarExpression(inlineIdentifier), Typ.FromTypeReference(retType), state.CurrentLocation); } else { retInstr = new Store(returnVariable, returnValue, Typ.FromTypeReference(retType), state.CurrentLocation); } retNode.Instructions.Add(retInstr); retNode.Successors = new List <CfgNode> { state.ProcDesc.ExitNode }; RegisterNode(state, retNode); } return(true); default: return(false); } }
private TypeEntry RegisterCilType(TypeDefinition type, TypeEnvironment typeEnvironment) { var typeFullName = type.GetCompatibleFullName(); if (typeEnvironment.ContainsType(typeFullName)) { return(typeEnvironment[typeFullName]); } // Gets all the classes from which the input class inherits. var baseClasses = type.Interfaces.Select(i => i.InterfaceType).Append(type.BaseType); var baseInstanceFields = new List <FieldIdentifier>(); var baseStaticFields = new List <FieldIdentifier>(); var baseSupers = new List <string>(); var baseTypes = new List <string>(); // Aggregates the instance and static fields, the super foreach (var baseClass in baseClasses) { if (baseClass != null) { try { var resolvedBaseClass = baseClass.Resolve(); if (resolvedBaseClass != null) { var baseTypeEntry = RegisterCilType(resolvedBaseClass, typeEnvironment); baseInstanceFields.AddRange(baseTypeEntry.TypeStruct.InstanceFields); baseStaticFields.AddRange(baseTypeEntry.TypeStruct.StaticFields); baseSupers.AddRange(baseTypeEntry.TypeStruct.Supers.Select(s => s.Name)); baseTypes.Add(baseClass.GetCompatibleFullName()); } } catch { continue; } } } var allFields = type.Fields.Select( f => (f.IsStatic, Field: new FieldIdentifier(f.GetCompatibleFullName(), Typ.FromTypeReference(f.FieldType)))); var instanceFields = allFields .Where(f => !f.IsStatic) .Select(f => f.Field) .Concat(baseInstanceFields); var staticFields = allFields .Where(f => f.IsStatic) .Select(f => f.Field) .Concat(baseStaticFields); var procNames = type.Methods.Select(m => new ProcedureName(m)); var typeStruct = new Struct(instanceFields, staticFields, baseSupers.Concat(baseTypes), procNames); var typeEntry = new TypeEntry { TypeName = TypeName.FromTypeReference(type), TypeStruct = typeStruct, }; typeEnvironment[typeFullName] = typeEntry; return(typeEntry); }
protected static (CfgNode, CfgNode) CreateExceptionTypeCheckBranchNodes( ProgramState state, ExceptionHandler handler, Identifier exceptionIdentifier) { var handlerStartLocation = GetHandlerStartLocation(state, handler); var exceptionExpression = new VarExpression(exceptionIdentifier); var isInstIdentifier = state.GetIdentifier(Identifier.IdentKind.Normal); var isInstArgs = new List <Call.CallArg> { new Call.CallArg(exceptionExpression, Typ.FromTypeReference(handler.CatchType)), new Call.CallArg( new SizeofExpression( Typ.FromTypeReferenceNoPointer(handler.CatchType), SizeofExpression.SizeofExpressionKind.exact), new Tvoid()) }; // We don't mark the function output as an isinst output, as there is no load or store // of it. var isInstCall = new Call(isInstIdentifier, new Tint(Tint.IntKind.IBool), new ConstExpression(ProcedureName.BuiltIn__instanceof), isInstArgs, new Call.CallFlags(), handlerStartLocation); var isInstOutputExpression = new VarExpression(isInstIdentifier); var pruneTrueInstruction = new Prune(isInstOutputExpression, true, Prune.IfKind.Ik_switch, handlerStartLocation); var pruneFalseInstruction = new Prune(new UnopExpression(UnopExpression.UnopKind.LNot, isInstOutputExpression, null), false, Prune.IfKind.Ik_switch, handlerStartLocation); var setCatchVarInstruction = new Store(GetHandlerCatchVar(state, handler), exceptionExpression, Typ.FromTypeReference(state.Method.ReturnType), handlerStartLocation); var pruneTrueNode = new PruneNode(handlerStartLocation, true, PruneNode.PruneNodeKind.ExceptionHandler, Prune.IfKind.Ik_switch, state.ProcDesc); var pruneFalseNode = new PruneNode(handlerStartLocation, false, PruneNode.PruneNodeKind.ExceptionHandler, Prune.IfKind.Ik_switch, state.ProcDesc); pruneTrueNode.Instructions.AddRange(new List <SilInstruction> { isInstCall, pruneTrueInstruction, setCatchVarInstruction }); pruneFalseNode.Instructions.AddRange(new List <SilInstruction> { isInstCall, pruneFalseInstruction }); pruneTrueNode.BlockEndOffset = state.MethodExceptionHandlers .GetBlockEndOffsetFromOffset( state.CurrentInstruction.Offset); pruneFalseNode.BlockEndOffset = state.MethodExceptionHandlers .GetBlockEndOffsetFromOffset( state.CurrentInstruction.Offset); state.Cfg.RegisterNode(pruneTrueNode); state.Cfg.RegisterNode(pruneFalseNode); return(pruneTrueNode, pruneFalseNode); }
protected static void CreateCatchHandlerEntryBlock(ProgramState state, ExceptionHandlerNode handlerNode, CfgNode handlerEntryPredecessor, Identifier exceptionIdentifier) { (var trueBranch, var falseBranch) = CreateExceptionTypeCheckBranchNodes( state, handlerNode.ExceptionHandler, exceptionIdentifier); handlerEntryPredecessor.Successors.Add(trueBranch); handlerEntryPredecessor.Successors.Add(falseBranch); if (!state.ExceptionHandlerToCatchVarNode.ContainsKey(handlerNode.ExceptionHandler)) { state.ExceptionHandlerToCatchVarNode[handlerNode.ExceptionHandler] = CreateLoadCatchVarNode(state, handlerNode.ExceptionHandler); // The CIL specification dictates that the exception object is on top of // the stack when the catch handler is entered; the first instruction of // the catch handler will handle the object pushed onto the stack. state.PushExpr(new VarExpression(exceptionIdentifier), new Tptr(Tptr.PtrKind.Pk_pointer, new Tstruct("System.Object"))); state.PushInstruction( handlerNode.ExceptionHandler.HandlerStart, state.ExceptionHandlerToCatchVarNode[handlerNode.ExceptionHandler].node); } (var loadCatchVarNode, _) = GetHandlerCatchVarNode( state, handlerNode.ExceptionHandler); trueBranch.Successors.Add(loadCatchVarNode); if (handlerNode.NextCatchBlock != null) { // Continues translation with catch handler's first instruction from // the handler's catch variable load node. CreateCatchHandlerEntryBlock(state, handlerNode.NextCatchBlock, falseBranch, exceptionIdentifier); } // Last catch handler of set; need to route control flow through the false // exception type-matching node. else { if (handlerNode.FinallyBlock != null) { var finallyBranchNode = CreateFinallyExceptionBranchNode( state, handlerNode.ExceptionHandler); falseBranch.Successors .Add(finallyBranchNode); (var finallyLoadCatchVar, _) = GetHandlerCatchVarNode( state, handlerNode.FinallyBlock); finallyBranchNode.Successors.Add(finallyLoadCatchVar); } else { var returnVariable = new LvarExpression( new LocalVariable(Identifier.ReturnIdentifier, state.Method)); var retType = state.Method.ReturnType.GetElementType(); var retInstr = new Store( returnVariable, new ExnExpression(new VarExpression(exceptionIdentifier)), Typ.FromTypeReference(retType), GetHandlerStartLocation(state, handlerNode.ExceptionHandler)); falseBranch.Instructions .Add(retInstr); falseBranch.Successors .Add(state.ProcDesc.ExceptionSinkNode); } } }