/// <summary> /// Look-up for a class method implementation given a method selector and a class. /// </summary> /// <param name="selector">Method selector to look for.</param> /// <param name="cls">Class where to start searching for the method (unless superLookupScope) is set.</param> /// <param name="superLookupScope">If set, start the lookup from the superclass of this class.</param> /// <returns>Returns the compiled method for the given selector or null if none was found.</returns> /// <remarks>If the method is not found, this functions does not searches the instance side of the class.</remarks> public static CompiledMethod LookupClassMethod(Symbol selector, ref SmalltalkClass cls, Symbol superLookupScope) { return(MethodLookupHelper.LookupMethod(ref cls, superLookupScope, c => { CompiledMethod method; if (c.ClassBehavior.TryGetValue(selector, out method)) { return method; } return null; })); }
/// <summary> /// This method is the core of the dynamic method lookup system. /// It determines the class of an object and looks-up the method implementation /// for a given method selector. /// </summary> /// <param name="runtime">Required.</param> /// <param name="selector">Required.</param> /// <param name="superLookupScope">Optional.</param> /// <param name="receiver">Optional.</param> /// <param name="self">Required.</param> /// <param name="executionContext">Required.</param> /// <param name="arguments">Required.</param> /// <param name="receiverClass">Must Return!</param> /// <param name="methodClass">Return null if missing.</param> /// <param name="restrictions">Must Return!</param> /// <param name="executableCode">Return null if missing.</param> public static void GetMethodInformation(SmalltalkRuntime runtime, Symbol selector, Symbol superLookupScope, object receiver, Expression self, Expression executionContext, IEnumerable <Expression> arguments, out SmalltalkClass receiverClass, out SmalltalkClass methodClass, out BindingRestrictions restrictions, out Expression executableCode) { restrictions = null; SmalltalkClass cls = null; // Special case for Smalltalk classes, because we want the class behavior first ... if (receiver is SmalltalkClass) { cls = (SmalltalkClass)receiver; if (cls.Runtime == runtime) { // The class of a SmalltalkClass object is a special class known by the runtime. receiverClass = runtime.NativeTypeClassMap.Class; if (receiverClass == null) { // And in case it's not set (not normal), we default to object receiverClass = runtime.NativeTypeClassMap.Object; } // Lookup method in class behavior CompiledMethod mth = MethodLookupHelper.LookupClassMethod(selector, ref cls, superLookupScope); if (mth != null) { // A class method, special restrictions methodClass = cls; restrictions = BindingRestrictions.GetInstanceRestriction(self, receiver); executableCode = mth.GetExpression(self, executionContext, arguments); if (executableCode != null) { return; } } // Not in class behavior ... fallback to instance / Object behavior cls = receiverClass; superLookupScope = null; // IMPROVE: May need to add Runtime Restrictions too. restrictions = BindingRestrictions.GetTypeRestriction(self, typeof(SmalltalkClass)); } } if ((cls == null) || (restrictions == null)) // Will not run for receivers of type SmalltalkClass { cls = MethodLookupHelper.GetClassAndRestrictions(runtime, receiver, self, arguments, out restrictions); } receiverClass = cls; // Look-up the method CompiledMethod method = MethodLookupHelper.LookupInstanceMethod(selector, ref cls, superLookupScope); methodClass = cls; if (method == null) { executableCode = null; } else { executableCode = method.GetExpression(self, executionContext, arguments); } }
/// <summary> /// Performs the binding of the dynamic operation. /// </summary> /// <param name="target">The target of the dynamic operation.</param> /// <param name="args">An array of arguments of the dynamic operation.</param> /// <returns>The System.Dynamic.DynamicMetaObject representing the result of the binding.</returns> public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) { ExecutionContext executionContext = this.GetExecutionContext(args); if (executionContext == null) { // If this is null, the binder was not used by a Smalltalk method. Or may be somebody passed null for the ExecutionContext, which is illegal too. throw new ArgumentException("The MessageSendCallSiteBinderBase can only be used in methods where the signature is (object, ExecutionContext, ...)"); } SmalltalkRuntime runtime = executionContext.Runtime; // Look-up the method implementation for the given selector // and generate an expression (runtime code) for that method. // This also returns the restriction needed for the PIC. BindingRestrictions restrictions; SmalltalkClass receiverClass; SmalltalkClass methodClass; Expression expression; string superScope = this.GetSuperLookupScope(); MethodLookupHelper.GetMethodInformation(runtime, runtime.GetSymbol(this.Selector), (superScope == null) ? null : runtime.GetSymbol(superScope), target.Value, target.Expression, args[0].Expression, args.Skip(1).Select(dmo => dmo.Expression), out receiverClass, out methodClass, out restrictions, out expression); // If no code was generated, method is missing, and we must do #doesNotUnderstand. if (expression == null) { // When the regular lookup experience a message send that the receiver does not understand, // we auto-generate an expression to send the #_doesNotUnderstand:arguments: to the receiver. // // Every object in the system must implement #_doesNotUnderstand:arguments:! // // Example: // tmp := anObject invalidMessageWith: arg1 with: arg2 with: arg3. // // We auto-generate a code to: // tmp := anObject _doesNotUnderstand: #invalidMessageWith:with:with: // arguments: (Array with: arg1 with: arg2 with: arg3). // Expression[] dnuArgs = new Expression[] { Expression.Constant(runtime.GetSymbol(this.Selector), typeof(object)), // The selector Expression.NewArrayInit(typeof(object), args.Skip(1).Select(d => Expression.Convert(d.Expression, typeof(object)))) // The remeaining args as an array }; // Look-up the #_doesNotUnderstand:arguments: method. MethodLookupHelper.GetMethodInformation(runtime, runtime.GetSymbol("_doesNotUnderstand:arguments:"), null, target.Value, target.Expression, args[0].Expression, dnuArgs, out receiverClass, out methodClass, out restrictions, out expression); // Every class is supposed to implement the #_doesNotUnderstand:arguments:, if not, throw a runtime exception if (expression == null) { throw new CodeGenerationException(RuntimeErrors.DoesNotUnderstandMissing); } } // Return a DynamicMetaObject with the generated code. // Important here are the restrictions, which ensure that as long as <self> is // of the correct type, we can freely execute the code for the method we've just found. return(new DynamicMetaObject(expression, target.Restrictions.Merge(restrictions))); }