public abstract IsInstance ( |
||
type | The type of the class the object inherits from or the interface the /// object implements. | |
리턴 | void |
/// <summary> /// Pops the value on the stack, converts it from one type to another, then pushes the /// result onto the stack. /// </summary> /// <param name="il"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> /// <param name="toType"> The type to convert to. </param> private static void EmitTypeConversion(ILGenerator il, Type fromType, Type toType) { // If the source type equals the destination type, then there is nothing to do. if (fromType == toType) { return; } // Emit for each type of argument we support. if (toType == typeof(int)) { EmitConversion.ToInteger(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType)); } else if (typeof(ObjectInstance).IsAssignableFrom(toType)) { EmitConversion.Convert(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType), PrimitiveType.Object); if (toType != typeof(ObjectInstance)) { // Convert to null if the from type isn't compatible with the to type. // For example, if the target type is FunctionInstance and the from type is ArrayInstance, then pass null. il.IsInstance(toType); } } else { EmitConversion.Convert(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType), PrimitiveTypeUtilities.ToPrimitiveType(toType)); } }
/// <summary> /// Generates CIL for the in operator. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateIn(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit the left-hand side expression and convert it to a string. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, this.Left.ResultType); // Store the left-hand side expression in a temporary variable. var temp = generator.CreateTemporaryVariable(typeof(string)); generator.StoreVariable(temp); // Emit the right-hand side expression. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Right.ResultType); // Check the right-hand side is a javascript object - if not, throw an exception. generator.Duplicate(); generator.IsInstance(typeof(ObjectInstance)); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfTrue(endOfTypeCheck); // Throw an nicely formatted exception. var rightValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(rightValue); generator.LoadEnumValue(ErrorType.TypeError); generator.LoadString("The in operator expected an object, but found '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(rightValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(rightValue); generator.ReinterpretCast(typeof(ObjectInstance)); // Load the left-hand side expression from the temporary variable. generator.LoadVariable(temp); // Call ObjectInstance.HasProperty(object) generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); // Allow the temporary variable to be reused. generator.ReleaseTemporaryVariable(temp); }
/// <summary> /// Generates CIL for the instanceof operator. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateInstanceOf(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit the left-hand side expression and convert it to an object. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Left.ResultType); // Store the left-hand side expression in a temporary variable. var temp = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(temp); // Emit the right-hand side expression. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Right.ResultType); // Check the right-hand side is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. var rightValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(rightValue); EmitHelpers.LoadScriptEngine(generator); generator.LoadString("TypeError"); generator.LoadString("The instanceof operator expected a function, but found '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(rightValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(rightValue); // Load the left-hand side expression from the temporary variable. generator.LoadVariable(temp); // Call FunctionInstance.HasInstance(object) generator.Call(ReflectionHelpers.FunctionInstance_HasInstance); // Allow the temporary variable to be reused. generator.ReleaseTemporaryVariable(temp); }
/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Check if this is a direct call to eval(). if (this.Target is NameExpression && ((NameExpression)this.Target).Name == "eval") { GenerateEval(generator, optimizationInfo); return; } // Emit the function instance first. ILLocalVariable targetBase = null; if (this.Target is MemberAccessExpression) { // The function is a member access expression (e.g. "Math.cos()"). // Evaluate the left part of the member access expression. var baseExpression = ((MemberAccessExpression)this.Target).Base; baseExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, baseExpression.ResultType); targetBase = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetBase); // Evaluate the right part of the member access expression. var memberAccessExpression = new MemberAccessExpression(((MemberAccessExpression)this.Target).Operator); memberAccessExpression.Push(new TemporaryVariableExpression(targetBase)); memberAccessExpression.Push(((MemberAccessExpression)this.Target).GetOperand(1)); memberAccessExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } else { // Something else (e.g. "eval()"). this.Target.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } // Check the object really is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. generator.Pop(); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("'{0}' is not a function", this.Target.ToString())); generator.DefineLabelPosition(endOfTypeCheck); // Generate code to produce the "this" value. There are three cases. if (this.Target is NameExpression) { // 1. The function is a name expression (e.g. "parseInt()"). // In this case this = scope.ImplicitThisValue, if there is one, otherwise undefined. ((NameExpression)this.Target).GenerateThis(generator); } else if (this.Target is MemberAccessExpression) { // 2. The function is a member access expression (e.g. "Math.cos()"). // In this case this = Math. //var baseExpression = ((MemberAccessExpression)this.Target).Base; //baseExpression.GenerateCode(generator, optimizationInfo); //EmitConversion.ToAny(generator, baseExpression.ResultType); generator.LoadVariable(targetBase); } else { // 3. Neither of the above (e.g. "(function() { return 5 })()") // In this case this = undefined. EmitHelpers.EmitUndefined(generator); } // Emit an array containing the function arguments. GenerateArgumentsArray(generator, optimizationInfo); // Call FunctionInstance.CallLateBound(thisValue, argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_CallLateBound); // Allow reuse of the temporary variable. if (targetBase != null) { generator.ReleaseTemporaryVariable(targetBase); } }
/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Note: we use GetRawOperand() so that grouping operators are not ignored. var operand = this.GetRawOperand(0); // There is only one operand, and it can be either a reference or a function call. // We need to split the operand into a function and some arguments. // If the operand is a reference, it is equivalent to a function call with no arguments. if (operand is FunctionCallExpression) { // Emit the function instance first. var function = ((FunctionCallExpression)operand).Target; function.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, function.ResultType); } else { // Emit the function instance first. operand.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, operand.ResultType); } // Check the object really is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. var targetValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetValue); EmitHelpers.LoadScriptEngine(generator); generator.LoadString("TypeError"); generator.LoadString("The new operator requires a function, found a '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(targetValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(targetValue); if (operand is FunctionCallExpression) { // Emit an array containing the function arguments. ((FunctionCallExpression)operand).GenerateArgumentsArray(generator, optimizationInfo); } else { // Emit an empty array. generator.LoadInt32(0); generator.NewArray(typeof(object)); } // Call FunctionInstance.ConstructLateBound(argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_ConstructLateBound); }
/// <summary> /// Generates CIL for the in operator. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> private void GenerateIn(ILGenerator generator, OptimizationInfo optimizationInfo) { // Emit the left-hand side expression and convert it to a string. this.Left.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, this.Left.ResultType); // Store the left-hand side expression in a temporary variable. var temp = generator.CreateTemporaryVariable(typeof(string)); generator.StoreVariable(temp); // Emit the right-hand side expression. this.Right.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Right.ResultType); // Check the right-hand side is a javascript object - if not, throw an exception. generator.IsInstance(typeof(Library.ObjectInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. var rightValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(rightValue); EmitHelpers.LoadScriptEngine(generator); generator.LoadString("TypeError"); generator.LoadString("The in operator expected an object, but found '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(rightValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(rightValue); // Load the left-hand side expression from the temporary variable. generator.LoadVariable(temp); // Call ObjectInstance.HasProperty(object) generator.Call(ReflectionHelpers.ObjectInstance_HasProperty); // Allow the temporary variable to be reused. generator.ReleaseTemporaryVariable(temp); }
/// <summary> /// Pops the value on the stack, converts it from one type to another, then pushes the /// result onto the stack. /// </summary> /// <param name="il"> The IL generator. </param> /// <param name="fromType"> The type to convert from. </param> /// <param name="toType"> The type to convert to. </param> private static void EmitTypeConversion(ILGenerator il, Type fromType, Type toType) { // If the source type equals the destination type, then there is nothing to do. if (fromType == toType) return; // Emit for each type of argument we support. if (toType == typeof(int)) EmitConversion.ToInteger(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType)); else if (typeof(ObjectInstance).IsAssignableFrom(toType)) { EmitConversion.Convert(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType), PrimitiveType.Object); if (toType != typeof(ObjectInstance)) { // Convert to null if the from type isn't compatible with the to type. // For example, if the target type is FunctionInstance and the from type is ArrayInstance, then pass null. il.IsInstance(toType); } } else EmitConversion.Convert(il, PrimitiveTypeUtilities.ToPrimitiveType(fromType), PrimitiveTypeUtilities.ToPrimitiveType(toType)); }
/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Check if this is a direct call to eval(). if (this.Target is NameExpression && ((NameExpression)this.Target).Name == "eval") { GenerateEval(generator, optimizationInfo); return; } // Check if this is a super() call. if (this.Target is SuperExpression) { // executionContext.CallSuperClass(arguments) EmitHelpers.LoadExecutionContext(generator); GenerateArgumentsArray(generator, optimizationInfo); generator.Call(ReflectionHelpers.ExecutionContext_CallSuperClass); return; } // Emit the function instance first. ILLocalVariable targetBase = null; if (this.Target is MemberAccessExpression) { // The function is a member access expression (e.g. "Math.cos()"). // Evaluate the left part of the member access expression. var baseExpression = ((MemberAccessExpression)this.Target).Base; baseExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, baseExpression.ResultType); targetBase = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetBase); // Evaluate the right part of the member access expression. var memberAccessExpression = new MemberAccessExpression(((MemberAccessExpression)this.Target).Operator); memberAccessExpression.Push(new TemporaryVariableExpression(targetBase)); memberAccessExpression.Push(((MemberAccessExpression)this.Target).GetOperand(1)); memberAccessExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } else { // Something else (e.g. "my_func()"). this.Target.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } // Check the object really is a function - if not, throw an exception. generator.Duplicate(); generator.IsInstance(typeof(FunctionInstance)); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfTrue(endOfTypeCheck); // Throw an nicely formatted exception. generator.Pop(); EmitHelpers.EmitThrow(generator, ErrorType.TypeError, string.Format("'{0}' is not a function", this.Target.ToString()), optimizationInfo); generator.DefineLabelPosition(endOfTypeCheck); // Pass in the path, function name and line. generator.ReinterpretCast(typeof(FunctionInstance)); generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); // Generate code to produce the "this" value. There are three cases. if (this.Target is NameExpression) { // 1. The function is a name expression (e.g. "parseInt()"). // If we are inside a with() block, then there is an implicit 'this' value, // otherwise 'this' is undefined. Scope.GenerateReference(generator, optimizationInfo); generator.Call(ReflectionHelpers.RuntimeScope_ImplicitThis); } else if (this.Target is MemberAccessExpression targetMemberAccessExpression) { // 2. The function is a member access expression (e.g. "Math.cos()"). // In this case this = Math. // Unless it's a super call like super.blah(). if (targetMemberAccessExpression.Base is SuperExpression) { EmitHelpers.LoadThis(generator); } else { generator.LoadVariable(targetBase); } } else { // 3. Neither of the above (e.g. "(function() { return 5 })()") // In this case this = undefined. EmitHelpers.EmitUndefined(generator); } // Emit an array containing the function arguments. GenerateArgumentsArray(generator, optimizationInfo); // Call FunctionInstance.CallLateBound(thisValue, argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_CallWithStackTrace); // Allow reuse of the temporary variable. if (targetBase != null) { generator.ReleaseTemporaryVariable(targetBase); } }
/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Note: we use GetRawOperand() so that grouping operators are not ignored. var operand = this.GetRawOperand(0); // There is only one operand, and it can be either a reference or a function call. // We need to split the operand into a function and some arguments. // If the operand is a reference, it is equivalent to a function call with no arguments. if (operand is FunctionCallExpression) { // Emit the function instance first. var function = ((FunctionCallExpression)operand).Target; function.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, function.ResultType); } else { // Emit the function instance first. operand.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, operand.ResultType); } // Check the object really is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. var targetValue = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetValue); EmitHelpers.LoadScriptEngine(generator); generator.LoadString("TypeError"); generator.LoadString("The new operator requires a function, found a '{0}' instead"); generator.LoadInt32(1); generator.NewArray(typeof(object)); generator.Duplicate(); generator.LoadInt32(0); generator.LoadVariable(targetValue); generator.Call(ReflectionHelpers.TypeUtilities_TypeOf); generator.StoreArrayElement(typeof(object)); generator.Call(ReflectionHelpers.String_Format); generator.NewObject(ReflectionHelpers.JavaScriptException_Constructor_Error); generator.Throw(); generator.DefineLabelPosition(endOfTypeCheck); generator.ReleaseTemporaryVariable(targetValue); if (operand is FunctionCallExpression) { // Emit an array containing the function arguments. ((FunctionCallExpression)operand).GenerateArgumentsArray(generator, optimizationInfo); } else { // Emit an empty array. generator.LoadInt32(0); generator.NewArray(typeof(object)); } // Call FunctionInstance.ConstructLateBound(argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_ConstructLateBound); }
/// <summary> /// Generates CIL for the expression. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Check if this is a direct call to eval(). if (this.Target is NameExpression && ((NameExpression)this.Target).Name == "eval") { GenerateEval(generator, optimizationInfo); return; } // Emit the function instance first. ILLocalVariable targetBase = null; if (this.Target is MemberAccessExpression) { // The function is a member access expression (e.g. "Math.cos()"). // Evaluate the left part of the member access expression. var baseExpression = ((MemberAccessExpression)this.Target).Base; baseExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, baseExpression.ResultType); targetBase = generator.CreateTemporaryVariable(typeof(object)); generator.StoreVariable(targetBase); // Evaluate the right part of the member access expression. var memberAccessExpression = new MemberAccessExpression(((MemberAccessExpression)this.Target).Operator); memberAccessExpression.Push(new TemporaryVariableExpression(targetBase)); memberAccessExpression.Push(((MemberAccessExpression)this.Target).GetOperand(1)); memberAccessExpression.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } else { // Something else (e.g. "eval()"). this.Target.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Target.ResultType); } // Check the object really is a function - if not, throw an exception. generator.IsInstance(typeof(Library.FunctionInstance)); generator.Duplicate(); var endOfTypeCheck = generator.CreateLabel(); generator.BranchIfNotNull(endOfTypeCheck); // Throw an nicely formatted exception. generator.Pop(); EmitHelpers.EmitThrow(generator, "TypeError", string.Format("'{0}' is not a function", this.Target.ToString())); generator.DefineLabelPosition(endOfTypeCheck); // Pass in the path, function name and line. generator.LoadStringOrNull(optimizationInfo.Source.Path); generator.LoadStringOrNull(optimizationInfo.FunctionName); generator.LoadInt32(optimizationInfo.SourceSpan.StartLine); // Generate code to produce the "this" value. There are three cases. if (this.Target is NameExpression) { // 1. The function is a name expression (e.g. "parseInt()"). // In this case this = scope.ImplicitThisValue, if there is one, otherwise undefined. ((NameExpression)this.Target).GenerateThis(generator); } else if (this.Target is MemberAccessExpression) { // 2. The function is a member access expression (e.g. "Math.cos()"). // In this case this = Math. //var baseExpression = ((MemberAccessExpression)this.Target).Base; //baseExpression.GenerateCode(generator, optimizationInfo); //EmitConversion.ToAny(generator, baseExpression.ResultType); generator.LoadVariable(targetBase); } else { // 3. Neither of the above (e.g. "(function() { return 5 })()") // In this case this = undefined. EmitHelpers.EmitUndefined(generator); } // Emit an array containing the function arguments. GenerateArgumentsArray(generator, optimizationInfo); // Call FunctionInstance.CallLateBound(thisValue, argumentValues) generator.Call(ReflectionHelpers.FunctionInstance_CallWithStackTrace); // Allow reuse of the temporary variable. if (targetBase != null) generator.ReleaseTemporaryVariable(targetBase); }
/// <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)); // End the IL. generator.Complete(); }
/// <summary> /// Pops the value on the stack, converts it from an object to the given type, then pushes /// the result onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="toType"> The type to convert to. </param> /// <param name="convertToAddress"> <c>true</c> if the value is intended for use as an /// instance pointer; <c>false</c> otherwise. </param> internal static void EmitConversionToType(ILGenerator generator, Type toType, bool convertToAddress) { // Convert Null.Value to null if the target type is a reference type. ILLabel endOfNullCheck = null; if (toType.IsValueType == false) { var startOfElse = generator.CreateLabel(); endOfNullCheck = generator.CreateLabel(); generator.Duplicate(); EmitHelpers.EmitNull(generator); generator.BranchIfNotEqual(startOfElse); generator.Pop(); generator.LoadNull(); generator.Branch(endOfNullCheck); generator.DefineLabelPosition(startOfElse); } switch (Type.GetTypeCode(toType)) { case TypeCode.Boolean: EmitConversion.ToBool(generator, PrimitiveType.Any); break; case TypeCode.Byte: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Char: EmitConversion.ToString(generator, PrimitiveType.Any); generator.Duplicate(); generator.Call(ReflectionHelpers.String_Length); generator.LoadInt32(1); var endOfCharCheck = generator.CreateLabel(); generator.BranchIfEqual(endOfCharCheck); EmitHelpers.EmitThrow(generator, "TypeError", "Cannot convert string to char - the string must be exactly one character long"); generator.DefineLabelPosition(endOfCharCheck); generator.LoadInt32(0); generator.Call(ReflectionHelpers.String_GetChars); break; case TypeCode.DBNull: throw new NotSupportedException("DBNull is not a supported parameter type."); case TypeCode.Decimal: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.NewObject(ReflectionHelpers.Decimal_Constructor_Double); break; case TypeCode.Double: EmitConversion.ToNumber(generator, PrimitiveType.Any); break; case TypeCode.Empty: throw new NotSupportedException("Empty is not a supported return type."); case TypeCode.Int16: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Int32: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Int64: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.ConvertToInt64(); break; case TypeCode.DateTime: case TypeCode.Object: // Check if the type must be unwrapped. generator.Duplicate(); generator.IsInstance(typeof(Jurassic.Library.ClrInstanceWrapper)); var endOfUnwrapCheck = generator.CreateLabel(); generator.BranchIfFalse(endOfUnwrapCheck); // Unwrap the wrapped instance. generator.Call(ReflectionHelpers.ClrInstanceWrapper_GetWrappedInstance); generator.DefineLabelPosition(endOfUnwrapCheck); // Value types must be unboxed. if (toType.IsValueType == true) { if (convertToAddress == true) // Unbox. generator.Unbox(toType); else // Unbox and copy to the stack. generator.UnboxAny(toType); //// Calling methods on value required the address of the value type, not the value type itself. //if (argument.Source == BinderArgumentSource.ThisValue && argument.Type.IsValueType == true) //{ // var temp = generator.CreateTemporaryVariable(argument.Type); // generator.StoreVariable(temp); // generator.LoadAddressOfVariable(temp); // generator.ReleaseTemporaryVariable(temp); //} } break; case TypeCode.SByte: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Single: EmitConversion.ToNumber(generator, PrimitiveType.Any); break; case TypeCode.String: EmitConversion.ToString(generator, PrimitiveType.Any); break; case TypeCode.UInt16: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.UInt32: EmitConversion.ToUInt32(generator, PrimitiveType.Any); break; case TypeCode.UInt64: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.ConvertToUnsignedInt64(); break; } // Label the end of the null check. if (toType.IsValueType == false) generator.DefineLabelPosition(endOfNullCheck); }
/// <summary> /// Pops the value on the stack, converts it from an object to the given type, then pushes /// the result onto the stack. /// </summary> /// <param name="generator"> The IL generator. </param> /// <param name="toType"> The type to convert to. </param> /// <param name="convertToAddress"> <c>true</c> if the value is intended for use as an /// instance pointer; <c>false</c> otherwise. </param> internal static void EmitConversionToType(ILGenerator generator, Type toType, bool convertToAddress) { // Convert Null.Value to null if the target type is a reference type. ILLabel endOfNullCheck = null; if (toType.IsValueType == false) { var startOfElse = generator.CreateLabel(); endOfNullCheck = generator.CreateLabel(); generator.Duplicate(); EmitHelpers.EmitNull(generator); generator.BranchIfNotEqual(startOfElse); generator.Pop(); generator.LoadNull(); generator.Branch(endOfNullCheck); generator.DefineLabelPosition(startOfElse); } switch (Type.GetTypeCode(toType)) { case TypeCode.Boolean: EmitConversion.ToBool(generator, PrimitiveType.Any); break; case TypeCode.Byte: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Char: EmitConversion.ToString(generator, PrimitiveType.Any); generator.Duplicate(); generator.Call(ReflectionHelpers.String_Length); generator.LoadInt32(1); var endOfCharCheck = generator.CreateLabel(); generator.BranchIfEqual(endOfCharCheck); EmitHelpers.EmitThrow(generator, ErrorType.TypeError, "Cannot convert string to char - the string must be exactly one character long"); generator.DefineLabelPosition(endOfCharCheck); generator.LoadInt32(0); generator.Call(ReflectionHelpers.String_GetChars); break; case TypeCode.DBNull: throw new NotSupportedException("DBNull is not a supported parameter type."); case TypeCode.Decimal: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.NewObject(ReflectionHelpers.Decimal_Constructor_Double); break; case TypeCode.Double: EmitConversion.ToNumber(generator, PrimitiveType.Any); break; case TypeCode.Empty: throw new NotSupportedException("Empty is not a supported return type."); case TypeCode.Int16: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Int32: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Int64: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.ConvertToInt64(); break; case TypeCode.DateTime: case TypeCode.Object: // Check if the type must be unwrapped. generator.Duplicate(); generator.IsInstance(typeof(Jurassic.Library.ClrInstanceWrapper)); var endOfUnwrapCheck = generator.CreateLabel(); generator.BranchIfFalse(endOfUnwrapCheck); // Unwrap the wrapped instance. generator.Call(ReflectionHelpers.ClrInstanceWrapper_GetWrappedInstance); generator.DefineLabelPosition(endOfUnwrapCheck); // Value types must be unboxed. if (toType.IsValueType == true) { if (convertToAddress == true) { // Unbox. generator.Unbox(toType); } else { // Unbox and copy to the stack. generator.UnboxAny(toType); } //// Calling methods on value required the address of the value type, not the value type itself. //if (argument.Source == BinderArgumentSource.ThisValue && argument.Type.IsValueType == true) //{ // var temp = generator.CreateTemporaryVariable(argument.Type); // generator.StoreVariable(temp); // generator.LoadAddressOfVariable(temp); // generator.ReleaseTemporaryVariable(temp); //} } break; case TypeCode.SByte: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.Single: EmitConversion.ToNumber(generator, PrimitiveType.Any); break; case TypeCode.String: EmitConversion.ToString(generator, PrimitiveType.Any); break; case TypeCode.UInt16: EmitConversion.ToInt32(generator, PrimitiveType.Any); break; case TypeCode.UInt32: EmitConversion.ToUInt32(generator, PrimitiveType.Any); break; case TypeCode.UInt64: EmitConversion.ToNumber(generator, PrimitiveType.Any); generator.ConvertToUnsignedInt64(); break; } // Label the end of the null check. if (toType.IsValueType == false) { generator.DefineLabelPosition(endOfNullCheck); } }
/// <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)); } // End the IL. generator.Complete(); }