/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(Jurassic.Compiler.ILGenerator generator, int argumentCount) { // Emit undefined. EmitHelpers.EmitUndefined(generator); // End the IL. generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Check for the correct number of arguments. if (argumentCount != 0) { EmitHelpers.EmitThrow(generator, ErrorType.TypeError, "Wrong number of arguments"); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } if (this.field.IsStatic == false) { generator.LoadArgument(1); ClrBinder.EmitConversionToType(generator, this.field.DeclaringType, convertToAddress: true); } generator.LoadField(this.field); ClrBinder.EmitConversionToObject(generator, this.field.FieldType); generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Check for the correct number of arguments. if (argumentCount != 1) { EmitHelpers.EmitThrow(generator, "TypeError", "Wrong number of arguments"); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } if (this.field.IsStatic == false) { generator.LoadArgument(1); ClrBinder.EmitConversionToType(generator, this.field.DeclaringType, convertToAddress: true); } generator.LoadArgument(2); generator.LoadInt32(0); generator.LoadArrayElement(typeof(object)); ClrBinder.EmitConversionToType(generator, this.field.FieldType, convertToAddress: false); generator.StoreField(this.field); EmitHelpers.EmitUndefined(generator); generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Check for the correct number of arguments. if (argumentCount != 0) { EmitHelpers.EmitThrow(generator, "TypeError", "Wrong number of arguments"); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } if (this.field.IsStatic == false) { generator.LoadArgument(1); ClrBinder.EmitConversionToType(generator, this.field.DeclaringType, convertToAddress: true); } generator.LoadField(this.field); ClrBinder.EmitConversionToObject(generator, this.field.FieldType); generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Here is what we are going to generate. //private static object SampleBinder(ScriptEngine engine, object thisObject, object[] arguments) //{ // // Target function signature: int (bool, int, string, object). // bool param1; // int param2; // string param3; // object param4; // param1 = arguments[0] != 0; // param2 = TypeConverter.ToInt32(arguments[1]); // param3 = TypeConverter.ToString(arguments[2]); // param4 = Undefined.Value; // return thisObject.targetMethod(param1, param2, param3, param4); //} // Find the target method. var binderMethod = this.buckets[Math.Min(argumentCount, this.buckets.Length - 1)]; // Constrain the number of apparent arguments to within the required bounds. int minArgumentCount = binderMethod.RequiredParameterCount; int maxArgumentCount = binderMethod.RequiredParameterCount + binderMethod.OptionalParameterCount; if (binderMethod.HasParamArray == true) maxArgumentCount = int.MaxValue; foreach (var argument in binderMethod.GenerateArguments(generator, Math.Min(Math.Max(argumentCount, minArgumentCount), maxArgumentCount))) { switch (argument.Source) { case BinderArgumentSource.ScriptEngine: // Load the "engine" parameter passed by the client. generator.LoadArgument(0); break; case BinderArgumentSource.ThisValue: // Load the "this" parameter passed by the client. generator.LoadArgument(1); bool inheritsFromObjectInstance = typeof(ObjectInstance).IsAssignableFrom(argument.Type); if (argument.Type.IsClass == true && inheritsFromObjectInstance == false && argument.Type != typeof(string) && argument.Type != typeof(object)) { // If the "this" object is an unsupported class, pass it through unmodified. generator.CastClass(argument.Type); } else { if (argument.Type != typeof(object)) { // If the target "this" object type is not of type object, throw an error if // the value is undefined or null. generator.Duplicate(); var temp = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(temp); generator.LoadArgument(0); generator.LoadVariable(temp); generator.LoadString(binderMethod.Name); generator.Call(ReflectionHelpers.TypeUtilities_VerifyThisObject); generator.ReleaseTemporaryVariable(temp); } // Convert to the target type. EmitTypeConversion(generator, typeof(object), argument.Type); if (argument.Type != typeof(ObjectInstance) && inheritsFromObjectInstance == true) { // EmitConversionToObjectInstance can emit null if the toType is derived from ObjectInstance. // Therefore, if the value emitted is null it means that the "thisObject" is a type derived // from ObjectInstance (e.g. FunctionInstance) and the value provided is a different type // (e.g. ArrayInstance). In this case, throw an exception explaining that the function is // not generic. var endOfThrowLabel = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(endOfThrowLabel); generator.LoadArgument(0); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("The method '{0}' is not generic", binderMethod.Name)); generator.DefineLabelPosition(endOfThrowLabel); } } break; case BinderArgumentSource.InputParameter: if (argument.InputParameterIndex < argumentCount) { // Load the argument onto the stack. generator.LoadArgument(2); generator.LoadInt32(argument.InputParameterIndex); generator.LoadArrayElement(typeof(object)); // Get some flags that apply to the parameter. var parameterFlags = JSParameterFlags.None; var parameterAttribute = argument.GetCustomAttribute<JSParameterAttribute>(); if (parameterAttribute != null) { if (argument.Type != typeof(ObjectInstance)) throw new NotImplementedException("[JSParameter] is only supported for arguments of type ObjectInstance."); parameterFlags = parameterAttribute.Flags; } if ((parameterFlags & JSParameterFlags.DoNotConvert) == 0) { // Convert the input parameter to the correct type. EmitTypeConversion(generator, typeof(object), argument); } else { // Don't do argument conversion. /*var endOfThrowLabel = generator.CreateLabel(); generator.IsInstance(typeof(ObjectInstance)); generator.Duplicate(); generator.BranchIfNotNull(endOfThrowLabel); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("Parameter {1} parameter of '{0}' must be an object", binderMethod.Name, argument.InputParameterIndex)); generator.DefineLabelPosition(endOfThrowLabel);*/ } } else { // The target method has more parameters than we have input values. EmitUndefined(generator, argument); } break; } } // Emit the call. binderMethod.GenerateCall(generator); // Convert the return value. if (binderMethod.ReturnType == typeof(void)) EmitHelpers.EmitUndefined(generator); else { EmitTypeConversion(generator, binderMethod.ReturnType, typeof(object)); // Convert a null return value to Null.Value or Undefined.Value. var endOfSpecialCaseLabel = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(endOfSpecialCaseLabel); generator.Pop(); if ((binderMethod.Flags & JSFunctionFlags.ConvertNullReturnValueToUndefined) != 0) EmitHelpers.EmitUndefined(generator); else EmitHelpers.EmitNull(generator); generator.DefineLabelPosition(endOfSpecialCaseLabel); } // End the IL. generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Determine the methods that have the correct number of arguments. var candidateMethods = new List<BinderMethod>(); foreach (var candidateMethod in this.targetMethods) { if (candidateMethod.IsArgumentCountCompatible(argumentCount) == true) candidateMethods.Add(candidateMethod); } // Zero candidates means no overload had the correct number of arguments. if (candidateMethods.Count == 0) { EmitHelpers.EmitThrow(generator, "TypeError", string.Format("No overload for method '{0}' takes {1} arguments", this.Name, argumentCount)); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } // Select the method to call at run time. generator.LoadInt32(candidateMethods.Count); generator.NewArray(typeof(RuntimeMethodHandle)); for (int i = 0; i < candidateMethods.Count; i ++) { generator.Duplicate(); generator.LoadInt32(i); generator.LoadToken(candidateMethods[i]); generator.StoreArrayElement(typeof(RuntimeMethodHandle)); } generator.LoadArgument(0); generator.LoadArgument(1); generator.LoadArgument(2); generator.Call(ReflectionHelpers.BinderUtilities_ResolveOverloads); var endOfMethod = generator.CreateLabel(); for (int i = 0; i < candidateMethods.Count; i++) { // Check if this is the selected method. ILLabel endOfIf = null; if (i < candidateMethods.Count - 1) { generator.Duplicate(); generator.LoadInt32(i); endOfIf = generator.CreateLabel(); generator.BranchIfNotEqual(endOfIf); } generator.Pop(); var targetMethod = candidateMethods[i]; // Convert the arguments. foreach (var argument in targetMethod.GenerateArguments(generator, argumentCount)) { // Load the input parameter value. switch (argument.Source) { case BinderArgumentSource.ScriptEngine: generator.LoadArgument(0); break; case BinderArgumentSource.ThisValue: generator.LoadArgument(1); break; case BinderArgumentSource.InputParameter: generator.LoadArgument(2); generator.LoadInt32(argument.InputParameterIndex); generator.LoadArrayElement(typeof(object)); break; } // Convert to the target type. EmitConversionToType(generator, argument.Type, convertToAddress: argument.Source == BinderArgumentSource.ThisValue); } // Call the target method. targetMethod.GenerateCall(generator); // Convert the return value. if (targetMethod.ReturnType == typeof(void)) EmitHelpers.EmitUndefined(generator); else EmitConversionToObject(generator, targetMethod.ReturnType); // Branch to the end of the method if this was the selected method. if (endOfIf != null) { generator.Branch(endOfMethod); generator.DefineLabelPosition(endOfIf); } } generator.DefineLabelPosition(endOfMethod); generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Determine the methods that have the correct number of arguments. var candidateMethods = new List <BinderMethod>(); foreach (var candidateMethod in this.targetMethods) { if (candidateMethod.IsArgumentCountCompatible(argumentCount) == true) { candidateMethods.Add(candidateMethod); } } // Zero candidates means no overload had the correct number of arguments. if (candidateMethods.Count == 0) { EmitHelpers.EmitThrow(generator, ErrorType.TypeError, string.Format("No overload for method '{0}' takes {1} arguments", this.Name, argumentCount)); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } // Select the method to call at run time. generator.LoadInt32(candidateMethods.Count); generator.NewArray(typeof(RuntimeMethodHandle)); for (int i = 0; i < candidateMethods.Count; i++) { generator.Duplicate(); generator.LoadInt32(i); generator.LoadToken(candidateMethods[i]); generator.StoreArrayElement(typeof(RuntimeMethodHandle)); } generator.LoadArgument(0); generator.LoadArgument(1); generator.LoadArgument(2); generator.Call(ReflectionHelpers.BinderUtilities_ResolveOverloads); var endOfMethod = generator.CreateLabel(); for (int i = 0; i < candidateMethods.Count; i++) { // Check if this is the selected method. ILLabel endOfIf = null; if (i < candidateMethods.Count - 1) { generator.Duplicate(); generator.LoadInt32(i); endOfIf = generator.CreateLabel(); generator.BranchIfNotEqual(endOfIf); } generator.Pop(); var targetMethod = candidateMethods[i]; // Convert the arguments. foreach (var argument in targetMethod.GenerateArguments(generator, argumentCount)) { // Load the input parameter value. switch (argument.Source) { case BinderArgumentSource.ScriptEngine: generator.LoadArgument(0); break; case BinderArgumentSource.ThisValue: generator.LoadArgument(1); break; case BinderArgumentSource.InputParameter: generator.LoadArgument(2); generator.LoadInt32(argument.InputParameterIndex); generator.LoadArrayElement(typeof(object)); break; } // Convert to the target type. EmitConversionToType(generator, argument.Type, convertToAddress: argument.Source == BinderArgumentSource.ThisValue); } // Call the target method. targetMethod.GenerateCall(generator); // Convert the return value. if (targetMethod.ReturnType == typeof(void)) { EmitHelpers.EmitUndefined(generator); } else { EmitConversionToObject(generator, targetMethod.ReturnType); } // Branch to the end of the method if this was the selected method. if (endOfIf != null) { generator.Branch(endOfMethod); generator.DefineLabelPosition(endOfIf); } } generator.DefineLabelPosition(endOfMethod); generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Here is what we are going to generate. //private static object SampleBinder(ScriptEngine engine, object thisObject, object[] arguments) //{ // // Target function signature: int (bool, int, string, object). // bool param1; // int param2; // string param3; // object param4; // param1 = arguments[0] != 0; // param2 = TypeConverter.ToInt32(arguments[1]); // param3 = TypeConverter.ToString(arguments[2]); // param4 = Undefined.Value; // return thisObject.targetMethod(param1, param2, param3, param4); //} // Find the target method. var binderMethod = this.buckets[Math.Min(argumentCount, this.buckets.Length - 1)]; // Constrain the number of apparent arguments to within the required bounds. int minArgumentCount = binderMethod.RequiredParameterCount; int maxArgumentCount = binderMethod.RequiredParameterCount + binderMethod.OptionalParameterCount; if (binderMethod.HasParamArray == true) { maxArgumentCount = int.MaxValue; } foreach (var argument in binderMethod.GenerateArguments(generator, Math.Min(Math.Max(argumentCount, minArgumentCount), maxArgumentCount))) { switch (argument.Source) { case BinderArgumentSource.ScriptEngine: // Load the "engine" parameter passed by the client. generator.LoadArgument(0); break; case BinderArgumentSource.ThisValue: // Load the "this" parameter passed by the client. generator.LoadArgument(1); bool inheritsFromObjectInstance = typeof(ObjectInstance).IsAssignableFrom(argument.Type); if (argument.Type.IsClass == true && inheritsFromObjectInstance == false && argument.Type != typeof(string) && argument.Type != typeof(object)) { // If the "this" object is an unsupported class, pass it through unmodified. generator.CastClass(argument.Type); } else { if (argument.Type != typeof(object)) { // If the target "this" object type is not of type object, throw an error if // the value is undefined or null. generator.Duplicate(); var temp = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(temp); generator.LoadArgument(0); generator.LoadVariable(temp); generator.LoadString(binderMethod.Name); generator.Call(ReflectionHelpers.TypeUtilities_VerifyThisObject); generator.ReleaseTemporaryVariable(temp); } // Convert to the target type. EmitTypeConversion(generator, typeof(object), argument.Type); if (argument.Type != typeof(ObjectInstance) && inheritsFromObjectInstance == true) { // EmitConversionToObjectInstance can emit null if the toType is derived from ObjectInstance. // Therefore, if the value emitted is null it means that the "thisObject" is a type derived // from ObjectInstance (e.g. FunctionInstance) and the value provided is a different type // (e.g. ArrayInstance). In this case, throw an exception explaining that the function is // not generic. var endOfThrowLabel = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(endOfThrowLabel); generator.LoadArgument(0); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("The method '{0}' is not generic", binderMethod.Name)); generator.DefineLabelPosition(endOfThrowLabel); } } break; case BinderArgumentSource.InputParameter: if (argument.InputParameterIndex < argumentCount) { // Load the argument onto the stack. generator.LoadArgument(2); generator.LoadInt32(argument.InputParameterIndex); generator.LoadArrayElement(typeof(object)); // Get some flags that apply to the parameter. var parameterFlags = JSParameterFlags.None; var parameterAttribute = argument.GetCustomAttribute <JSParameterAttribute>(); if (parameterAttribute != null) { if (argument.Type != typeof(ObjectInstance)) { throw new NotImplementedException("[JSParameter] is only supported for arguments of type ObjectInstance."); } parameterFlags = parameterAttribute.Flags; } if ((parameterFlags & JSParameterFlags.DoNotConvert) == 0) { // Convert the input parameter to the correct type. EmitTypeConversion(generator, typeof(object), argument); } else { // Don't do argument conversion. /*var endOfThrowLabel = generator.CreateLabel(); * generator.IsInstance(typeof(ObjectInstance)); * generator.Duplicate(); * generator.BranchIfNotNull(endOfThrowLabel); * EmitHelpers.EmitThrow(generator, "TypeError", string.Format("Parameter {1} parameter of '{0}' must be an object", binderMethod.Name, argument.InputParameterIndex)); * generator.DefineLabelPosition(endOfThrowLabel);*/ } } else { // The target method has more parameters than we have input values. EmitUndefined(generator, argument); } break; } } // Emit the call. binderMethod.GenerateCall(generator); // Convert the return value. if (binderMethod.ReturnType == typeof(void)) { EmitHelpers.EmitUndefined(generator); } else { EmitTypeConversion(generator, binderMethod.ReturnType, typeof(object)); // Convert a null return value to Null.Value or Undefined.Value. var endOfSpecialCaseLabel = generator.CreateLabel(); generator.Duplicate(); generator.BranchIfNotNull(endOfSpecialCaseLabel); generator.Pop(); if ((binderMethod.Flags & JSFunctionFlags.ConvertNullReturnValueToUndefined) != 0) { EmitHelpers.EmitUndefined(generator); } else { EmitHelpers.EmitNull(generator); } generator.DefineLabelPosition(endOfSpecialCaseLabel); } // End the IL. generator.Complete(); }
/// <summary> /// Generates a method that does type conversion and calls the bound method. /// </summary> /// <param name="generator"> The ILGenerator used to output the body of the method. </param> /// <param name="argumentCount"> The number of arguments that will be passed to the delegate. </param> /// <returns> A delegate that does type conversion and calls the method represented by this /// object. </returns> protected override void GenerateStub(ILGenerator generator, int argumentCount) { // Check for the correct number of arguments. if (argumentCount != 1) { EmitHelpers.EmitThrow(generator, ErrorType.TypeError, "Wrong number of arguments"); EmitHelpers.EmitDefaultValue(generator, PrimitiveType.Any); generator.Complete(); return; } if (this.field.IsStatic == false) { generator.LoadArgument(1); ClrBinder.EmitConversionToType(generator, this.field.DeclaringType, convertToAddress: true); } generator.LoadArgument(2); generator.LoadInt32(0); generator.LoadArrayElement(typeof(object)); ClrBinder.EmitConversionToType(generator, this.field.FieldType, convertToAddress: false); generator.StoreField(this.field); EmitHelpers.EmitUndefined(generator); generator.Complete(); }