static bool MethodMatch(MethodReference candidate, MethodReference method) { var candidateResolved = candidate.Resolve(); // Check overrides if (candidateResolved.HasOverrides) { foreach (var @override in candidateResolved.Overrides) { var resolvedOverride = ResolveGenericsVisitor.Process(candidate, @override); return MemberEqualityComparer.Default.Equals(resolvedOverride, method); } } if (!candidateResolved.IsVirtual) return false; if (candidate.Name != method.Name) return false; if (!TypeMatch(ResolveGenericsVisitor.Process(candidate, candidate.ReturnType), ResolveGenericsVisitor.Process(method, method.ReturnType))) return false; if (candidate.Parameters.Count != method.Parameters.Count) return false; for (int i = 0; i < candidate.Parameters.Count; i++) if (!TypeMatch(ResolveGenericsVisitor.Process(candidate, candidate.Parameters[i].ParameterType), ResolveGenericsVisitor.Process(method, method.Parameters[i].ParameterType))) return false; return true; }
private static bool DoesMatchMethod(MethodReference mInternal, MethodReference m) { var mInternalDef = mInternal.Resolve(); if (mInternalDef.GetCustomAttribute<JsRedirectAttribute>() != null) { // Can never return a redirected method return false; } // Look for methods with custom signatures var detailsAttr = mInternalDef.GetCustomAttribute<JsDetailAttribute>(true); if (detailsAttr != null) { var signature = detailsAttr.Properties.FirstOrDefault(x => x.Name == "Signature"); if (signature.Name != null) { if (mInternal.Name != m.Name) { return false; } var sigTypes = ((CustomAttributeArgument[])signature.Argument.Value) .Select(x => ((TypeReference)x.Value).FullResolve(m)) .ToArray(); var mReturnType = m.ReturnType.FullResolve(m); if (!mReturnType.IsSame(sigTypes[0])) { return false; } for (int i = 0; i < m.Parameters.Count; i++) { var mParameterType = m.Parameters[i].ParameterType.FullResolve(m); if (!mParameterType.IsSame(sigTypes[i + 1])) { return false; } } return true; } } // Look for C# method that matches with custom 'this' Func<bool> isFakeThis = () => { if (mInternal.HasThis) { return false; } if (mInternal.Name != m.Name) { return false; } if (mInternal.Parameters.Count != m.Parameters.Count + 1) { return false; } if (mInternal.Parameters[0].GetCustomAttribute<JsFakeThisAttribute>() == null) { return false; } if (!mInternal.ReturnType.IsSame(m.ReturnType)) { return false; } for (int i = 1; i < mInternal.Parameters.Count; i++) { if (!mInternal.Parameters[i].ParameterType.IsSame(m.Parameters[i - 1].ParameterType)) { return false; } } return true; }; if (isFakeThis()) { return true; } // Look for C# method that match signature return mInternal.MatchMethodOnly(m); }
void FindGetLogger(TypeDefinition typeDefinition) { if (!typeDefinition.IsPublic) { var message = $"The logger factory type '{typeDefinition.FullName}' needs to be public."; throw new WeavingException(message); } GetLoggerMethod = typeDefinition .Methods .FirstOrDefault(x => x.Name == "GetLogger" && x.IsStatic && x.HasGenericParameters && x.GenericParameters.Count == 1 && x.Parameters.Count == 0); if (GetLoggerMethod == null) { throw new WeavingException("Found 'LoggerFactory' but it did not have a static 'GetLogger' method that takes 'string' as a parameter"); } if (!GetLoggerMethod.Resolve().IsPublic) { var message = $"The logger factory method '{GetLoggerMethod.FullName}' needs to be public."; throw new WeavingException(message); } }
public bool IsSetup(MethodReference target) { if (!IsInFixture(target)) return false; var field = MSpecTranslator.TranslateGeneratedMethod(target.Resolve()); if (field == null) return false; return field.FieldType.Name == "Establish" || field.FieldType.Name == "Because"; }
public Function(Type declaringType, MethodReference methodReference, TypeRef functionType, ValueRef generatedValue, FunctionSignature signature) { Signature = signature; DeclaringType = declaringType; MethodReference = methodReference; FunctionType = functionType; GeneratedValue = generatedValue; VirtualSlot = -1; MethodDefinition = methodReference.Resolve(); ParameterTypes = signature.ParameterTypes.Select(x => x.Type).ToArray(); // Generate function type when being called from vtable/IMT (if it applies) // If declaring type is a value type, needs to unbox "this" for virtual method if (DeclaringType.TypeDefinitionCecil.IsValueType && (MethodDefinition.Attributes & MethodAttributes.Virtual) != 0) { bool hasStructValueReturn = signature.ReturnType.ABIParameterInfo.Kind == ABIParameterInfoKind.Indirect; // Create function type with boxed "this" var argumentCount = LLVM.CountParamTypes(FunctionType); var argumentTypes = new TypeRef[argumentCount]; LLVM.GetParamTypes(FunctionType, argumentTypes); // Change first type to boxed "this" var thisIndex = hasStructValueReturn ? 1 : 0; argumentTypes[thisIndex] = LLVM.PointerType(DeclaringType.ObjectTypeLLVM, 0); VirtualFunctionType = LLVM.FunctionType(LLVM.GetReturnType(FunctionType), argumentTypes, LLVM.IsFunctionVarArg(FunctionType)); } else { VirtualFunctionType = FunctionType; } }
public Method(MethodReference methodReference, ComponentCache componentCache) { Contract.Requires<ArgumentNullException>(methodReference != null); Contract.Requires<ArgumentNullException>(componentCache != null); method = methodReference.Resolve(); cache = componentCache; }
private static void CloneMethodAttributes(MethodReference sourceMethod, MethodReference clonedMethod) { var s = sourceMethod.Resolve(); var c = clonedMethod.Resolve(); c.CallingConvention = s.CallingConvention; c.SemanticsAttributes = s.SemanticsAttributes; s.CustomAttributes.ToList().ForEach(a => c.CustomAttributes.Add(a)); s.SecurityDeclarations.ToList().ForEach(sd => c.SecurityDeclarations.Add(sd)); }
public static string Create(MethodReference mRef, Resolver resolver, ICode ast) { if (mRef.ContainsGenericParameters()) { throw new ArgumentException("Cannot create JS for method with open generic parameters"); } var mDef = mRef.Resolve(); if (mDef.IsAbstract) { throw new ArgumentException("Should never need to transcode an abstract method"); } var tRef = mRef.DeclaringType; var tDef = tRef.Resolve(); var v = new JsMethod(resolver); v.Visit(ast); var js = v.js.ToString(); var sb = new StringBuilder(); // Method declaration var methodName = resolver.MethodNames[mRef]; //var parameterNames = mRef.Parameters.Select(x => v.parameters.ValueOrDefault(x).NullThru(y => resolver.LocalVarNames[y])).ToArray(); // Match parameters, but have to do by position, as method built may be a custom method replacing a BCL method, // so parameters are not the same. var parameterNames = mRef.Parameters.Select(x => v.parameters.FirstOrDefault(y => y.Key.Sequence == x.Sequence).Value.NullThru(y => resolver.LocalVarNames[y])).ToArray(); if (!mDef.IsStatic) { var thisName = v.vars.FirstOrDefault(x => x.ExprType == Expr.NodeType.VarThis).NullThru(x => resolver.LocalVarNames[x]); parameterNames = parameterNames.Prepend(thisName).ToArray(); } var unusedParameterNameGen = new NameGenerator(); parameterNames = parameterNames.Select(x => x ?? ("_" + unusedParameterNameGen.GetNewName())).ToArray(); sb.AppendFormat("// {0}", mRef.FullName); sb.AppendLine(); sb.AppendFormat("var {0} = function({1}) {{", methodName, string.Join(", ", parameterNames)); // Variable declarations var declVars = v.vars .Select(x => new { name = resolver.LocalVarNames[x], type = x.Type }) .Where(x => !parameterNames.Contains(x.name)) .Select(x => { var name = x.name; if (x.type.IsValueType) { name += " = " + DefaultValuer.Get(x.type, resolver.FieldNames); } return name; }) .Distinct() // Bit of a hack, but works for now .ToArray(); if (declVars.Any()) { sb.AppendLine(); sb.Append(' ', tabSize); sb.AppendFormat("var {0};", string.Join(", ", declVars)); } // Method body sb.AppendLine(js); // Method ending sb.AppendLine("};"); var sbStr = sb.ToString(); return sbStr; }
private bool HasMethod(MethodReference method) { foreach (var baseMethod in _baseMethodsSet) { if (baseMethod.Class == method.DeclaringType && baseMethod.Definition == method.Resolve()) { return true; } } return false; }
public MethodDefinition GetFactoryMethod(MethodReference ctor, AssemblyDefinition assembly, IAssemblyTracker tracker) { if (!_factoryMethods.ContainsKey (ctor)) { var memberId = CecilResolver.CreateMemberID (ctor.Resolve()); AssemblyNameReference containingTrackedAssembly; _factoryMethods[ctor] = CecilResolver.ResolveToMethodDefinition (_infoProvider.GetFactoryMethodFunc (memberId), tracker, out containingTrackedAssembly); if (containingTrackedAssembly != null) tracker.TrackNewReference (assembly, containingTrackedAssembly); } return _factoryMethods[ctor]; }
protected virtual bool IsAssertMethod(MethodReference methodReference) { var resolved = methodReference.Resolve(); var name = resolved.DeclaringType.Name; if (!resolved.IsStatic || !name.EndsWith("Assert")) { return false; } else { return true; } }
public MethodDefinition CloneIntoType(MethodReference sourceMethod, TypeDefinition type) { var s = sourceMethod.Resolve(); var c = new MethodDefinition(s.Name, s.Attributes, s.ReturnType); type.Methods.Add(c); CloneMethodProperties(c, s); CloneMethodAttributes(c, s); CloneMethodParameters(s, c); CloneGenericParameters(s, c); return c; }
private MethodDefinition ResolveMethod(MethodReference aRef) { var xDef = aRef as MethodDefinition; if (xDef != null) { return xDef; } var xSpec = aRef as GenericInstanceMethod; if (xSpec != null) { throw new Exception("Queueing generic methods not yet(?) supported!"); } return aRef.Resolve(); }
public Ctx(TypeReference tRef, MethodReference mRef, Ctx fromCtx = null) { this.TRef = tRef; this.TDef = tRef.Resolve(); this.MRef = mRef; this.MDef = mRef.Resolve(); this.HasFakeThis = mRef.Parameters.FirstOrDefault().NullThru(x => x.GetCustomAttribute<JsFakeThisAttribute>() != null); this.Module = mRef.Module; this.TypeSystem = mRef.Module.TypeSystem; this.ExprGen = Expr.CreateExprGen(this); this.This = fromCtx == null ? (this.MDef.IsStatic ? null : new ExprVarThis(this, tRef)) : fromCtx.This; this.type = new Lazy<TypeReference>(() => this.Module.Import(typeof(Type))); this._int64 = new Lazy<TypeReference>(() => this.Module.Import(typeof(Cls._Int64))); this._uint64 = new Lazy<TypeReference>(() => this.Module.Import(typeof(Cls._UInt64))); }
public uint GetOrCreateIndex(MethodReference method, IMetadataCollection metadataCollection) { if (method == null) { return 0; } if (method.IsGenericInstance || method.DeclaringType.IsGenericInstance) { Il2CppGenericMethodCollectorWrite.Add(method); if (Il2CppGenericMethodCollectorRead.HasIndex(method)) { return (Il2CppGenericMethodCollectorRead.GetIndex(method) | 0xc0000000); } return 0xc0000000; } return (uint) (metadataCollection.GetMethodIndex(method.Resolve()) | 0x60000000); }
public void WalkMethod(MethodReference methodReference) { if (!AddMethod(methodReference)) { return; } var method = methodReference.Resolve(); List<Instruction> foundInstructions = (from instruction in method.Body.Instructions where method.HasBody && method.Body.Instructions != null && instruction.Operand != null select instruction).ToList(); IEnumerable<TypeReference> typesFound = from instruction in foundInstructions let tRef = instruction.Operand as TypeReference where tRef != null select tRef; IEnumerable<MethodReference> methodsFound = from instruction in foundInstructions let mRef = instruction.Operand as MethodReference where mRef != null && mRef.DeclaringType != null select mRef; IEnumerable<FieldDefinition> fieldsFound = from instruction in foundInstructions let fRef = instruction.Operand as FieldReference where fRef != null && fRef.FieldType != null let fRefResolved = fRef.Resolve() where fRefResolved != null select fRefResolved; foreach (TypeReference typeDefinition in typesFound) { AddType(typeDefinition); } foreach (FieldDefinition fieldDefinition in fieldsFound) { AddField(fieldDefinition); } foreach (MethodReference methodDefinition in methodsFound) { if (methodDefinition != method) { WalkMethod(methodDefinition); } } }
private static void CloneMethodProperties(MethodReference sourceMethod, MethodReference clonedMethod) { var s = sourceMethod.Resolve(); var c = clonedMethod.Resolve(); c.IsAbstract = s.IsAbstract; c.IsAddOn = s.IsAddOn; c.IsAssembly = s.IsAssembly; c.IsCheckAccessOnOverride = s.IsCheckAccessOnOverride; c.IsCompilerControlled = s.IsCompilerControlled; c.IsFamily = s.IsFamily; c.IsFamilyAndAssembly = s.IsFamilyAndAssembly; c.IsFamilyOrAssembly = s.IsFamilyOrAssembly; c.IsFinal = s.IsFinal; c.IsFire = s.IsFire; c.IsForwardRef = s.IsForwardRef; c.IsGetter = s.IsGetter; c.IsHideBySig = s.IsHideBySig; c.IsIL = s.IsIL; c.IsInternalCall = s.IsInternalCall; c.IsManaged = s.IsManaged; c.IsNative = s.IsNative; c.IsNewSlot = s.IsNewSlot; c.IsPInvokeImpl = s.IsPInvokeImpl; c.IsPreserveSig = s.IsPreserveSig; c.IsPrivate = s.IsPrivate; c.IsPublic = s.IsPublic; c.IsRemoveOn = s.IsRemoveOn; c.IsReuseSlot = s.IsReuseSlot; c.IsRuntime = s.IsRuntime; c.IsRuntimeSpecialName = s.IsRuntimeSpecialName; c.IsSetter = s.IsSetter; c.IsSpecialName = s.IsSpecialName; c.IsStatic = s.IsStatic; c.IsSynchronized = s.IsSynchronized; c.IsUnmanaged = s.IsUnmanaged; c.IsUnmanagedExport = s.IsUnmanagedExport; c.IsVirtual = s.IsVirtual; c.NoInlining = s.NoInlining; c.NoOptimization = s.NoOptimization; }
void AddMethodToScan(MethodReference m, MethodInfo caller) { if (m == null) return; if (m.DeclaringType.Namespace.StartsWith ("System")) return; var def = m as MethodDefinition; if (def == null) { try { def = m.Resolve (); } catch (Exception) { def = null; } } if (def != null && def.HasBody) { var key = def.FullName; if (scannedMethods.ContainsKey (key)) return; var mi = default(MethodInfo); if (!methodsToScan.TryGetValue (key, out mi)) { mi = new MethodInfo () { Def = def, Caller = caller, }; methodsToScan.Add (key, mi); } } }
private static GenericInstanceMethod ConstructGenericMethod(GenericContext context, TypeReference declaringType, MethodDefinition method, IEnumerable<TypeReference> genericArguments) { MethodReference owner = new MethodReference(method.Name, method.ReturnType, declaringType) { HasThis = method.HasThis }; foreach (GenericParameter parameter in method.GenericParameters) { owner.GenericParameters.Add(new GenericParameter(parameter.Name, owner)); } foreach (ParameterDefinition definition in method.Parameters) { owner.Parameters.Add(new ParameterDefinition(definition.Name, definition.Attributes, definition.ParameterType)); } if (owner.Resolve() == null) { throw new Exception(); } GenericInstanceMethod method2 = new GenericInstanceMethod(owner); foreach (TypeReference reference3 in genericArguments) { method2.GenericArguments.Add(InflateType(context, reference3)); } return method2; }
public void Visit(MethodReference method, string referencingEntityName) { MethodChanged = false; ParamsMethod = null; if (method.DeclaringType.Scope == SupportAssemblyReference() && TryToResolveInSupport(method)) return; if (method.Resolve() != null || method.DeclaringType.IsArray || TryToResolveInSupport(method) || ResolveManually(method) != null) return; if (IsIgnoredType(method.DeclaringType)) return; Console.WriteLine("Error: method `{0}` doesn't exist in target framework. It is referenced from {1} at {2}.", method, method.Module.Name, referencingEntityName); }
private static bool CheckTypeForMethodUsage (TypeDefinition type, MethodReference method) { if (type.HasGenericParameters) type = type.GetElementType ().Resolve (); HashSet<ulong> methods = GetCache (type); if (methods.Contains (GetToken (method))) return true; MethodDefinition md = method.Resolve (); if ((md != null) && md.HasOverrides) { foreach (MethodReference mr in md.Overrides) { if (methods.Contains (GetToken (mr))) return true; } } return false; }
/// <summary> /// Create a postfix for the name of the given method based on /// - "unsigned" parameter types: these will be mapped to their signed counterparts; /// - on 'ref' our 'out' status, as these will be mapped to arrays; /// - on generic parameters (as these will be mapped to 'object'); /// - on generic parameters of parameters, as these will be type-erased; /// - on generic method parameters, e.g. void Foo[T](), again type-erasure being the reason. /// /// See also http://stackoverflow.com/questions/8808703/method-signature-in-c-sharp /// </summary> private static string CreateSignaturePostfix(MethodReference method) { var declaringType = method.DeclaringType; if (declaringType.IsArray) return string.Empty; if ((method.Name == ".ctor") || (method.Name == ".cctor")) return string.Empty; var declType = declaringType.GetElementType().Resolve(); if ((declType != null) && (declType.IsDelegate())) return string.Empty; var methodDef = method.Resolve(); // dont handle events. if (methodDef.IsAddOn || methodDef.IsRemoveOn) return ""; var needsPostfix = false; var postfix = new StringBuilder("$$"); const bool processGenerics = true; const bool forcePostfixOnParmType = true; List<ParameterDefinition> types = new List<ParameterDefinition>(method.Parameters); if (ReturnTypeSegregatesOverloads(methodDef) || (methodDef.IsHideBySig && !method.ReturnType.IsVoid())) // TODO: check if it is ok to exlude Void here. // Note that for CLR, the return type always segregates overloads. // It doesn't for C# (and most high level languages), // and that's what we are aiming at at the moment. types.Add(new ParameterDefinition(method.ReturnType)); foreach (var parm in types) { var parmTypeId = ""; var parameterType = parm.ParameterType; if (parameterType.IsByReference) { parmTypeId += "r"; parameterType = ((ByReferenceType)parameterType).ElementType; } if (parmTypeId != "" && forcePostfixOnParmType) needsPostfix = true; var typeId = GetTypeId(parameterType, processGenerics); if (typeId != null) needsPostfix = true; else if (parmTypeId == "") typeId = "_"; postfix.Append(parmTypeId + typeId); } // add marker for generic method parameters. if (processGenerics && methodDef.HasGenericParameters) { if (!needsPostfix) { postfix.Clear(); postfix.Append("$$"); postfix.Append(methodDef.GenericParameters.Count); } else { postfix.Insert(2, methodDef.GenericParameters.Count + "$"); } needsPostfix = true; } if (needsPostfix) { return postfix.ToString(); } return string.Empty; }
/// <summary> /// Creates the function. /// </summary> /// <param name="method">The method.</param> /// <returns></returns> Function CreateFunction(MethodReference method) { Function function; if (functions.TryGetValue(method, out function)) return function; var numParams = method.Parameters.Count; if (method.HasThis) numParams++; var parameterTypes = new Type[numParams]; var parameterTypesLLVM = new TypeRef[numParams]; var declaringType = CreateType(ResolveGenericsVisitor.Process(method, method.DeclaringType)); for (int index = 0; index < numParams; index++) { TypeReference parameterTypeReference; if (method.HasThis && index == 0) { parameterTypeReference = declaringType.TypeReference; // Value type uses ByReference type for this if (parameterTypeReference.IsValueType) parameterTypeReference = parameterTypeReference.MakeByReferenceType(); } else { var parameter = method.Parameters[method.HasThis ? index - 1 : index]; parameterTypeReference = ResolveGenericsVisitor.Process(method, parameter.ParameterType); } var parameterType = CreateType(parameterTypeReference); if (parameterType.DefaultType.Value == IntPtr.Zero) throw new InvalidOperationException(); parameterTypes[index] = parameterType; parameterTypesLLVM[index] = parameterType.DefaultType; } var returnType = CreateType(ResolveGenericsVisitor.Process(method, method.ReturnType)); // Generate function global bool isExternal = method.DeclaringType.Resolve().Module.Assembly != assembly; var methodMangledName = Regex.Replace(method.FullName, @"(\W)", "_"); var functionType = LLVM.FunctionType(returnType.DefaultType, parameterTypesLLVM, false); var resolvedMethod = method.Resolve(); var hasDefinition = resolvedMethod != null && (resolvedMethod.HasBody || ((resolvedMethod.ImplAttributes & (MethodImplAttributes.InternalCall | MethodImplAttributes.Runtime)) != 0)); var functionGlobal = hasDefinition ? LLVM.AddFunction(module, methodMangledName, functionType) : LLVM.ConstPointerNull(LLVM.PointerType(functionType, 0)); function = new Function(declaringType, method, functionType, functionGlobal, returnType, parameterTypes); functions.Add(method, function); if (hasDefinition) { if (isExternal) { // External weak linkage LLVM.SetLinkage(functionGlobal, Linkage.ExternalWeakLinkage); } else { // Need to compile EmitFunction(function); } } return function; }
/// <summary> /// Compiles the given method definition. /// </summary> /// <param name="method">The method.</param> /// <param name="function">The function.</param> /// <exception cref="System.NotSupportedException"></exception> /// <exception cref="System.NotImplementedException"></exception> /// <exception cref="System.InvalidOperationException">Backward jump with a non-empty stack unknown target.</exception> private void CompileFunction(MethodReference methodReference, Function function) { var method = methodReference.Resolve(); var body = method.Body; var codeSize = body != null ? body.CodeSize : 0; var functionGlobal = function.GeneratedValue; var functionContext = new FunctionCompilerContext(function); functionContext.BasicBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Empty); LLVM.PositionBuilderAtEnd(builder, functionContext.BasicBlock); if (body == null && (method.ImplAttributes & MethodImplAttributes.Runtime) != 0) { var declaringClass = GetClass(function.DeclaringType); // Generate IL for various methods if (declaringClass.BaseType != null && declaringClass.BaseType.Type.TypeReference.FullName == typeof(MulticastDelegate).FullName) { body = GenerateDelegateMethod(method, declaringClass); if (body == null) return; codeSize = UpdateOffsets(body); } } if (body == null) return; var numParams = method.Parameters.Count; // Create stack, locals and args var stack = new List<StackValue>(body.MaxStackSize); var locals = new List<StackValue>(body.Variables.Count); var args = new List<StackValue>(numParams); var exceptionHandlers = new List<ExceptionHandlerInfo>(); var activeTryHandlers = new List<ExceptionHandlerInfo>(); functionContext.Stack = stack; functionContext.Locals = locals; functionContext.Arguments = args; functionContext.ExceptionHandlers = exceptionHandlers; functionContext.ActiveTryHandlers = activeTryHandlers; // Process locals foreach (var local in body.Variables) { // TODO: Anything to do on pinned objects? //if (local.IsPinned) // throw new NotSupportedException(); var type = CreateType(ResolveGenericsVisitor.Process(methodReference, local.VariableType)); locals.Add(new StackValue(type.StackType, type, LLVM.BuildAlloca(builder, type.DefaultType, local.Name))); } // Process args for (int index = 0; index < function.ParameterTypes.Length; index++) { var argType = function.ParameterTypes[index]; var arg = LLVM.GetParam(functionGlobal, (uint)index); // Copy argument on stack var storage = LLVM.BuildAlloca(builder, argType.DefaultType, "arg" + index.ToString()); LLVM.BuildStore(builder, arg, storage); args.Add(new StackValue(argType.StackType, argType, storage)); } // Some wasted space due to unused offsets, but we only keep one so it should be fine. // TODO: Reuse same allocated instance per thread, and grow it only if necessary var branchTargets = new bool[codeSize]; var basicBlocks = new BasicBlockRef[codeSize]; var forwardStacks = new StackValue[codeSize][]; functionContext.BasicBlocks = basicBlocks; functionContext.ForwardStacks = forwardStacks; // Find branch targets (which will require PHI node for stack merging) for (int index = 0; index < body.Instructions.Count; index++) { var instruction = body.Instructions[index]; var flowControl = instruction.OpCode.FlowControl; // Process branch targets if (flowControl == FlowControl.Cond_Branch || flowControl == FlowControl.Branch) { var targets = instruction.Operand is Instruction[] ? (Instruction[])instruction.Operand : new[] { (Instruction)instruction.Operand }; foreach (var target in targets) { // Operand Target can be reached branchTargets[target.Offset] = true; } } // Need to enforce a block to be created for the next instruction after a conditional branch // TODO: Break? if (flowControl == FlowControl.Cond_Branch) { if (instruction.Next != null) branchTargets[instruction.Next.Offset] = true; } } // Setup exception handling if (body.HasExceptionHandlers) { // Add an "ehselector.slot" i32 local, and a "exn.slot" Object reference local functionContext.ExceptionHandlerSelectorSlot = LLVM.BuildAlloca(builder, int32Type, "ehselector.slot"); functionContext.ExceptionSlot = LLVM.BuildAlloca(builder, @object.DefaultType, "exn.slot"); functionContext.EndfinallyJumpTarget = LLVM.BuildAlloca(builder, int32Type, "endfinally.jumptarget"); // Create resume exception block functionContext.ResumeExceptionBlock = LLVM.AppendBasicBlockInContext(context, functionGlobal, "eh.resume"); LLVM.PositionBuilderAtEnd(builder2, functionContext.ResumeExceptionBlock); var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, "exn"); var ehselectorValue = LLVM.BuildLoad(builder2, functionContext.ExceptionHandlerSelectorSlot, "sel"); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, intPtrType, "exn"); var landingPadValue = LLVM.BuildInsertValue(builder2, LLVM.GetUndef(caughtResultType), exceptionObject, 0, "lpad.val"); landingPadValue = LLVM.BuildInsertValue(builder2, landingPadValue, ehselectorValue, 1, "lpad.val"); LLVM.BuildResume(builder2, landingPadValue); // Exception handlers blocks are also branch targets foreach (var exceptionHandler in body.ExceptionHandlers) { branchTargets[exceptionHandler.HandlerStart.Offset] = true; } } // Create basic block // TODO: Could be done during previous pass for (int offset = 0; offset < branchTargets.Length; offset++) { // Create a basic block if this was a branch target or an instruction after a conditional branch if (branchTargets[offset]) { basicBlocks[offset] = LLVM.AppendBasicBlockInContext(context, functionGlobal, string.Format("L_{0:x4}", offset)); } } // Create catch clause stack if (body.HasExceptionHandlers) { // Exception handlers blocks are also branch targets foreach (var exceptionHandler in body.ExceptionHandlers) { exceptionHandlers.Add(new ExceptionHandlerInfo(exceptionHandler)); if (exceptionHandler.HandlerType != ExceptionHandlerType.Catch) continue; var handlerStart = exceptionHandler.HandlerStart.Offset; var catchBlock = basicBlocks[handlerStart]; var catchClass = GetClass(ResolveGenericsVisitor.Process(methodReference, exceptionHandler.CatchType)); // Extract exception LLVM.PositionBuilderAtEnd(builder2, catchBlock); var exceptionObject = LLVM.BuildLoad(builder2, functionContext.ExceptionSlot, string.Empty); exceptionObject = LLVM.BuildPointerCast(builder2, exceptionObject, catchClass.Type.DefaultType, string.Empty); // Erase exception from exn.slot (it has been handled) LLVM.BuildStore(builder2, LLVM.ConstNull(@object.DefaultType), functionContext.ExceptionSlot); LLVM.BuildStore(builder2, LLVM.ConstInt(int32Type, 0, false), functionContext.ExceptionHandlerSelectorSlot); forwardStacks[handlerStart] = new[] { new StackValue(catchClass.Type.StackType, catchClass.Type, exceptionObject) }; } } foreach (var instruction in body.Instructions) { try { // Check if any exception handlers might have changed if (body.HasExceptionHandlers) UpdateExceptionHandlers(functionContext, instruction); if (branchTargets[instruction.Offset]) UpdateBranching(functionContext, instruction); // Reset states functionContext.FlowingNextInstructionMode = FlowingNextInstructionMode.Implicit; EmitInstruction(functionContext, instruction); // If we do a jump, let's merge stack var flowControl = instruction.OpCode.FlowControl; if (flowControl == FlowControl.Cond_Branch || flowControl == FlowControl.Branch) MergeStacks(functionContext, instruction); } catch (Exception e) { throw new InvalidOperationException(string.Format("Error while processing instruction {0} of method {1}", instruction, methodReference), e); } } }
static MethodDefinition GetMethodDefinition(MethodReference method) { if (method != null && method.DeclaringType.IsGenericInstance) return method.Resolve(); else return method as MethodDefinition; }
public void Add(Mono.Cecil.MethodReference reference) { Add(reference.Resolve()); }
private bool TryToResolveInSupport(MethodReference method) { if (string.IsNullOrEmpty(Context.SupportModulePartialNamespace)) return false; method = method.GetElementMethod(); var originalType = method.DeclaringType; if (originalType.IsGenericInstance || originalType.HasGenericParameters) return false; var support = SupportAssemblyReference(); var ns = Context.SupportModulePartialNamespace; if (!string.IsNullOrEmpty(originalType.Namespace)) ns += '.' + originalType.Namespace; method.DeclaringType = new TypeReference(ns, originalType.Name, Context.TargetModule, support, originalType.IsValueType); MethodDefinition resolved = null; // We can only change declaring type like this for static methods if (!method.HasThis) { resolved = method.Resolve(); } // If our method is instance, we can have a static method in support module that has explicit "this" parameter if (resolved == null && method.HasThis) { method.HasThis = false; method.Parameters.Insert(0, new ParameterDefinition(originalType)); resolved = method.Resolve(); // Our explicit "this" parameter can be of type System.Object if (resolved == null) { method.Parameters[0] = new ParameterDefinition(method.DeclaringType.Module.TypeSystem.Object.Resolve()); resolved = method.Resolve(); } if (resolved == null) { method.HasThis = true; method.Parameters.RemoveAt(0); } } if (resolved != null) { Context.RewriteTarget = true; AddSupportReferenceIfNeeded(support); return true; } method.DeclaringType = originalType; return false; }
protected virtual string GetMethodName(MethodReference method) { MethodDefinition methodDefinition = method.Resolve(); if (methodDefinition != null && methodDefinition.Module.FilePath == this.ModuleContext.Module.FilePath) { if (this.TypeContext.MethodDefinitionToNameMap.ContainsKey(methodDefinition)) { return this.TypeContext.MethodDefinitionToNameMap[methodDefinition]; } if (this.TypeContext.GeneratedMethodDefinitionToNameMap.ContainsKey(methodDefinition)) { return this.TypeContext.GeneratedMethodDefinitionToNameMap[methodDefinition]; } return this.ModuleContext.RenamedMembersMap[methodDefinition.MetadataToken.ToUInt32()]; } return Utilities.EscapeName(GenericHelper.GetNonGenericName(method.Name), this.Language); }
//static public MethodReference HHH(this MethodDefinition @this, Dictionary<TypeReference, TypeReference> genericity, MethodReference met) //{ // if (met is GenericInstanceMethod) // { // if (met.DeclaringType is GenericInstanceType) // { // //var mmm = new MethodReference(met.Name, @this.HHH(genericity, met.ReturnType), @this.HHH(genericity, met.DeclaringType)) // //{ // // HasThis = met.HasThis, // // ExplicitThis = met.ExplicitThis, // // CallingConvention = met.CallingConvention // //}; // //foreach (var parameter in met.Parameters) // //{ // // mmm.Parameters.Add(new ParameterDefinition(@this.HHH(genericity, parameter.ParameterType))); // //} // //foreach (var generic_parameter in met.GenericParameters) // //{ // // mmm.GenericParameters.Add(new GenericParameter(generic_parameter.Name, mmm)); // //} // ////var hhhh = new GenericInstanceMethod(mmm); // ////hhhh. // //return mmm.MakeGenericMethod((met as GenericInstanceMethod).GenericArguments.Select(_Type => @this.HHH(genericity, _Type)).ToArray()); // //return mmm.MakeGenericMethod((met as GenericInstanceMethod).GenericArguments.Select(_Type => @this.HHH(genericity, _Type)).ToArray()); // return @this.Module.Import(@this.Module.Import(met.Resolve()) // .MakeGenericMethod((met as GenericInstanceMethod).GenericArguments.Select(_Type => @this.HHH(genericity, _Type)).ToArray()) // .MakeHostInstanceGeneric((@this.HHH(genericity, met.DeclaringType) as GenericInstanceType).GenericArguments.ToArray())); // ////var ggg = new GenericInstanceMethod(@this.Module.Import(met.Resolve())); // //ggg.DeclaringType = @this.HHH(genericity, met.DeclaringType); // //ggg.Parameters.Clear(); // //foreach (var _parameter in met.Parameters) { ggg.Parameters.Add(new ParameterDefinition(_parameter.Name, _parameter.Attributes, /*@this.HHH(genericity, */_parameter.ParameterType/*)*/)); } // //foreach (var _type in (met as GenericInstanceMethod).GenericArguments) { ggg.GenericArguments.Add(@this.HHH(genericity, _type)); } // //var m = @this.Module.Import(@this.Module.Import(met.Resolve()).MakeHostInstanceGeneric((@this.HHH(genericity, met.DeclaringType) as GenericInstanceType).GenericArguments.Select(_Type => @this.HHH(genericity, _Type)).ToArray()).MakeGenericMethod((met as GenericInstanceMethod).GenericArguments.Select(ppp => @this.HHH(genericity, ppp)).Select(_Type => @this.HHH(genericity, _Type)).ToArray())); // //return met; // //return ggg; // } // else // { // var m = @this.Module.Import(@this.Module.Import(met.Resolve()).MakeGenericMethod((met as GenericInstanceMethod).GenericArguments.Select(ppp => @this.HHH(genericity, ppp)).ToArray())); // return m; // } // } // else // { // if (met.DeclaringType is GenericInstanceType) // { // var m = @this.Module.Import(met.Resolve()).MakeHostInstanceGeneric((@this.HHH(genericity, met.DeclaringType) as GenericInstanceType).GenericArguments.Select(_Type => // { // var hhhhhhh = met; // return @this.HHH(genericity, _Type); // }).ToArray()); // return m; // } // else // { // return met; // } // } //} //static public TypeReference HHH(this MethodDefinition @this, Dictionary<TypeReference, TypeReference> genericity, TypeReference type) //{ // if (type.IsByReference/* || type.Name.EndsWith("&")*/) { return new ByReferenceType(@this.HHH(genericity, (type as ByReferenceType).ElementType)); } // if (genericity.TryGetValue(type, out var _type)) { return _type; } // //var _genericity = @this.DeclaringType.GenericParameters.Concat(@this.GenericParameters).ToArray(); // //if (type.Name.StartsWith("!!")) { return _genericity[int.Parse(type.Name.Substring(2)) + @this.DeclaringType.DeclaringType.GenericParameters.Count]; } // //if (type.Name.StartsWith("!")) { return _genericity[int.Parse(type.Name.Substring(1))]; } // if (type is GenericInstanceType) { return @this.Module.Import(type.Resolve()).MakeGenericType((type as GenericInstanceType).GenericArguments.Select(_Type => @this.HHH(genericity, _Type))); } // return type; //} static public MethodReference Repair(this MethodReference that, MethodReference method) { var _types = that.GenericParameterTypes().Select(_Type => _Type.Name.StartsWith("!!") ? that.GenericParameters[int.Parse(_Type.Name.Substring(2))] : (_Type.Name.StartsWith("!") ? that.DeclaringType.GenericParameters[int.Parse(_Type.Name.Substring(1))] : _Type)).ToArray(); if (_types.Length > 0) { if (method.GenericParameters.Count > 0) //TODO test it! { var dec = that.GetRealType(method.DeclaringType); if (dec is GenericInstanceType) { var _method = that.Module.Import(method.Resolve()).MakeHostInstanceGeneric((dec as GenericInstanceType).GenericArguments.ToArray()); //var _method = new MethodReference(method.Name, that.GetRealType(method.ReturnType), that.GetRealType(method.DeclaringType)); //foreach (var _parameter in method.Parameters) { _method.Parameters.Add(new ParameterDefinition(_parameter.Name, _parameter.Attributes, that.GetRealType(_parameter.ParameterType))); } return(_method.MakeGenericMethod(method.GenericParameters.Select(k => k.Name.StartsWith("!!") ? that.GenericParameters[int.Parse(k.Name.Substring(2))] : (k.Name.StartsWith("!") ? that.DeclaringType.GenericParameters[int.Parse(k.Name.Substring(1))] : k)).ToArray())); } else { return(method.MakeGenericMethod(method.GenericParameters.Select(k => k.Name.StartsWith("!!") ? that.GenericParameters[int.Parse(k.Name.Substring(2))] : (k.Name.StartsWith("!") ? that.DeclaringType.GenericParameters[int.Parse(k.Name.Substring(1))] : k)).ToArray())); } } else if (method is GenericInstanceMethod) { var dec = that.GetRealType(method.DeclaringType); if (dec is GenericInstanceType) { var _method = that.Module.Import(method.Resolve()).MakeHostInstanceGeneric((dec as GenericInstanceType).GenericArguments.ToArray()); //var _method = new MethodReference(method.Name, that.GetRealType(method.ReturnType), that.GetRealType(method.DeclaringType)); //foreach (var _parameter in method.Parameters) { _method.Parameters.Add(new ParameterDefinition(_parameter.Name, _parameter.Attributes, that.GetRealType(_parameter.ParameterType))); } return(_method.MakeGenericMethod((method as GenericInstanceMethod).GenericArguments.Select(k => k.Name.StartsWith("!!") ? that.GenericParameters[int.Parse(k.Name.Substring(2))] : (k.Name.StartsWith("!") ? that.DeclaringType.GenericParameters[int.Parse(k.Name.Substring(1))] : k)).ToArray())); } else { return(method.MakeGenericMethod((method as GenericInstanceMethod).GenericArguments.Select(k => k.Name.StartsWith("!!") ? that.GenericParameters[int.Parse(k.Name.Substring(2))] : (k.Name.StartsWith("!") ? that.DeclaringType.GenericParameters[int.Parse(k.Name.Substring(1))] : k)).ToArray())); } } else { var dec = that.GetRealType(method.DeclaringType); if (dec is GenericInstanceType) { var _method = that.Module.Import(method.Resolve()).MakeHostInstanceGeneric((dec as GenericInstanceType).GenericArguments.ToArray()); //var _method = new MethodReference(method.Name, that.GetRealType(method.ReturnType), that.GetRealType(method.DeclaringType)); //foreach (var _parameter in method.Parameters) { _method.Parameters.Add(new ParameterDefinition(_parameter.Name, _parameter.Attributes, that.GetRealType(_parameter.ParameterType))); } return(_method); } else { return(method); } } } //var met = method.Resolve(); //if (met.GenericParameters.Count > 0) //{ // var kk = method.Module.Import(met).MakeGenericMethod(_types.Reverse().Take(met.GenericParameters.Count).Reverse().ToArray()); // if (met.DeclaringType.GenericParameters.Count > 0) { kk.DeclaringType = met.DeclaringType.MakeGenericType(_types.Take(met.DeclaringType.GenericParameters.Count)); } // return kk; //} //if (met.DeclaringType.GenericParameters.Count > 0) //{ // var kk = method.Module.Import(met); // kk.DeclaringType = met.DeclaringType.MakeGenericType(_types.Take(met.DeclaringType.GenericParameters.Count)); // return kk; //} return(method); }
private bool IsPure (MethodReference mr) { MethodDefinition method = mr.Resolve (); if (method != null) { TypeDefinition type = method.DeclaringType; string type_name = type.GetFullName (); string method_name = method.Name; // getters if (method.IsGetter) return true; // System.String, System.Type, etc methods if (types_considered_pure.Contains (type_name)) return true; // Equals, GetHashCode, Contains, etc if (methods_considered_pure.Contains (method_name)) return true; // operators if (method_name.StartsWith ("op_", StringComparison.Ordinal) && method_name != "op_Implicit" && method_name != "op_Explicit") return true; // Contract methods (skip namespace) if (type_name == "System.Diagnostics.Contracts.Contract") return true; // System.Predicate<T> and System.Comparison<T> if (type_name.StartsWith ("System.Predicate`1", StringComparison.Ordinal)) return true; if (type_name.StartsWith ("System.Comparison`1", StringComparison.Ordinal)) return true; // delegate invocation if (MethodSignatures.Invoke.Matches (method)) { if (type.HasCustomAttributes) { if (HasPureAttribute (type.CustomAttributes)) { return true; } } } // PureAttribute if (method.HasCustomAttributes) { if (HasPureAttribute (method.CustomAttributes)) { return true; } } return false; } // If we can't resolve the method we have to assume it's OK to call. Log.WriteLine (this, "couldn't resolve {0} call: assuming it is pure", mr); return true; }
protected bool ShouldChangeToCallVirt(MethodReference method) { return method.DeclaringType.Resolve() == _sourceType && method.Resolve().RemainsInRoleInterface(); }
MethodDefinition ResolveMethodDefinition (MethodReference method) { MethodDefinition md = method as MethodDefinition; if (md == null) md = method.Resolve (); return md; }
private void ExtractBasicBlocksOfMethod(MethodDefinition definition) { _done.Add(definition); // Make sure definition assembly is loaded. String full_name = definition.Module.FullyQualifiedName; LoadAssembly(full_name); if (definition.Body == null) { System.Console.WriteLine("WARNING: METHOD BODY NULL! " + definition); return; } int instruction_count = definition.Body.Instructions.Count; StackQueue <Mono.Cecil.Cil.Instruction> leader_list = new StackQueue <Mono.Cecil.Cil.Instruction>(); // Each method is a leader of a block. CFGVertex v = (CFGVertex)this.AddVertex(_node_number++); v.Method = definition; v.HasReturnValue = definition.IsReuseSlot; v._entry = v; this._entries.Add(v); v._ordered_list_of_blocks = new List <CFGVertex>(); v._ordered_list_of_blocks.Add(v); for (int j = 0; j < instruction_count; ++j) { // accumulate jump to locations since these split blocks. Mono.Cecil.Cil.Instruction mi = definition.Body.Instructions[j]; //System.Console.WriteLine(mi); Inst i = Inst.Wrap(mi, this); Mono.Cecil.Cil.OpCode op = i.OpCode; Mono.Cecil.Cil.FlowControl fc = op.FlowControl; v._instructions.Add(i); // Verify that mi not owned already. CFG.CFGVertex asdfasdf; Debug.Assert(!partition_of_instructions.TryGetValue(mi, out asdfasdf)); // Update ownership. partition_of_instructions.Add(mi, v); if (fc == Mono.Cecil.Cil.FlowControl.Next) { continue; } if (fc == Mono.Cecil.Cil.FlowControl.Branch || fc == Mono.Cecil.Cil.FlowControl.Cond_Branch) { // Save leader target of branch. object o = i.Operand; // Two cases that I know of: operand is just and instruction, // or operand is an array of instructions (via a switch instruction). Mono.Cecil.Cil.Instruction oo = o as Mono.Cecil.Cil.Instruction; Mono.Cecil.Cil.Instruction[] ooa = o as Mono.Cecil.Cil.Instruction[]; if (oo != null) { leader_list.Push(oo); } else if (ooa != null) { foreach (Mono.Cecil.Cil.Instruction ins in ooa) { Debug.Assert(ins != null); leader_list.Push(ins); } } else { throw new Exception("Unknown operand type for basic block partitioning."); } } } StackQueue <int> ordered_leader_list = new StackQueue <int>(); for (int j = 0; j < instruction_count; ++j) { // Order jump targets. These denote locations // where to split blocks. However, it's ordered, // so that splitting is done from last instruction in block // to first instruction in block. Mono.Cecil.Cil.Instruction i = definition.Body.Instructions[j]; //System.Console.WriteLine("Looking for " + i); if (leader_list.Contains(i)) { ordered_leader_list.Push(j); } } // Split block at jump targets in reverse. while (ordered_leader_list.Count > 0) { int i = ordered_leader_list.Pop(); CFG.CFGVertex new_node = v.Split(i); } //this.Dump(); StackQueue <CFG.CFGVertex> stack = new StackQueue <CFG.CFGVertex>(); foreach (CFG.CFGVertex node in this.VertexNodes) { stack.Push(node); } while (stack.Count > 0) { // Split blocks at branches, not including calls, with following // instruction a leader of new block. CFG.CFGVertex node = stack.Pop(); int node_instruction_count = node._instructions.Count; for (int j = 0; j < node_instruction_count; ++j) { Inst i = node._instructions[j]; Mono.Cecil.Cil.OpCode op = i.OpCode; Mono.Cecil.Cil.FlowControl fc = op.FlowControl; if (fc == Mono.Cecil.Cil.FlowControl.Next) { continue; } if (fc == Mono.Cecil.Cil.FlowControl.Call) { continue; } if (fc == Mono.Cecil.Cil.FlowControl.Meta) { continue; } if (fc == Mono.Cecil.Cil.FlowControl.Phi) { continue; } if (j + 1 >= node_instruction_count) { continue; } CFG.CFGVertex new_node = node.Split(j + 1); stack.Push(new_node); break; } } //this.Dump(); stack = new StackQueue <CFG.CFGVertex>(); foreach (CFG.CFGVertex node in this.VertexNodes) { stack.Push(node); } while (stack.Count > 0) { // Add in all final non-fallthrough branch edges. CFG.CFGVertex node = stack.Pop(); int node_instruction_count = node._instructions.Count; Inst i = node._instructions[node_instruction_count - 1]; Mono.Cecil.Cil.OpCode op = i.OpCode; Mono.Cecil.Cil.FlowControl fc = op.FlowControl; switch (fc) { case Mono.Cecil.Cil.FlowControl.Branch: case Mono.Cecil.Cil.FlowControl.Cond_Branch: { // Two cases: i.Operand is a single instruction, or an array of instructions. if (i.Operand as Mono.Cecil.Cil.Instruction != null) { Mono.Cecil.Cil.Instruction target_instruction = i.Operand as Mono.Cecil.Cil.Instruction; CFGVertex target_node = this.VertexNodes.First( (CFGVertex x) => { if (!x._instructions.First().Instruction.Equals(target_instruction)) { return(false); } return(true); }); if (Options.Singleton.Get(Options.OptionType.DisplaySSAComputation)) { System.Console.WriteLine("Create edge a " + node.Name + " to " + target_node.Name); } this.AddEdge(node, target_node); } else if (i.Operand as Mono.Cecil.Cil.Instruction[] != null) { foreach (Mono.Cecil.Cil.Instruction target_instruction in (i.Operand as Mono.Cecil.Cil.Instruction[])) { CFGVertex target_node = this.VertexNodes.First( (CFGVertex x) => { if (!x._instructions.First().Instruction.Equals(target_instruction)) { return(false); } return(true); }); System.Console.WriteLine("Create edge a " + node.Name + " to " + target_node.Name); this.AddEdge(node, target_node); } } else { throw new Exception("Unknown operand type for conditional branch."); } break; } case Mono.Cecil.Cil.FlowControl.Break: break; case Mono.Cecil.Cil.FlowControl.Call: { // We no longer split at calls. Splitting causes // problems because interprocedural edges are // produced. That's not good because it makes // code too "messy". break; object o = i.Operand; if (o as Mono.Cecil.MethodReference != null) { Mono.Cecil.MethodReference r = o as Mono.Cecil.MethodReference; Mono.Cecil.MethodDefinition d = r.Resolve(); IEnumerable <CFGVertex> target_node_list = this.VertexNodes.Where( (CFGVertex x) => { return(x.Method.FullName == r.FullName && x._entry == x); }); int c = target_node_list.Count(); if (c >= 1) { // target_node is the entry for a method. Also get the exit. CFGVertex target_node = target_node_list.First(); CFGVertex exit_node = target_node.Exit; // check if this is a recursive call. DO NOT BOTHER!! if (node.Method == target_node.Method) { } else { // Create edges from exit to successor blocks of the call. foreach (CFGEdge e in node._Successors) { System.Console.WriteLine("Create edge c " + exit_node.Name + " to " + e.to.Name); this._interprocedure_graph.AddEdge(exit_node, e.to); } // Create edge from node to method entry. System.Console.WriteLine("Create edge b " + node.Name + " to " + target_node.Name); this._interprocedure_graph.AddEdge(node, target_node); } } } break; } case Mono.Cecil.Cil.FlowControl.Meta: break; case Mono.Cecil.Cil.FlowControl.Next: break; case Mono.Cecil.Cil.FlowControl.Phi: break; case Mono.Cecil.Cil.FlowControl.Return: break; case Mono.Cecil.Cil.FlowControl.Throw: break; } } //this.Dump(); }