/// <summary> /// Generates CIL for a call to eval(). /// </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 GenerateEval(ILGenerator generator, OptimizationInfo optimizationInfo) { // engine EmitHelpers.LoadScriptEngine(generator); // code if (this.OperandCount < 2) { // No arguments were supplied. generator.LoadNull(); } else { // Take the first argument and convert it to a string. GenerateArgumentsArray(generator, optimizationInfo); generator.LoadInt32(0); generator.LoadArrayElement(typeof(object)); } // scope EmitHelpers.LoadScope(generator); // thisObject EmitHelpers.LoadThis(generator); // strictMode generator.LoadBoolean(optimizationInfo.StrictMode); // Call Global.Eval(engine, code, scope, thisValue, strictMode) generator.Call(ReflectionHelpers.Global_Eval); }
/// <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(); }
/// <summary> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { // This method generates code to retrieve the value of a variable, given the name of // variable and scope in which the variable is being referenced. The variable was // not necessary declared in this scope - it might be declared in any of the parent // scopes (together called a scope chain). The general algorithm is to start at the // head of the chain and search backwards until the variable is found. There are // two types of scopes: declarative scopes and object scopes. Object scopes are hard - // it cannot be known at compile time whether the variable exists or not so runtime // checks have to be inserted. Declarative scopes are easier - variables have to be // declared and cannot be deleted. There is one tricky bit: new variables can be // introduced into a declarative scope at runtime by a non-strict eval() statement. // Even worse, variables that were introduced by means of an eval() *can* be deleted. var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfGet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // The variable was declared in this scope. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) variable.Store = generator.DeclareVariable(typeof(object), variable.Name); // Load the value from the variable. generator.LoadVariable(variable.Store); // Ensure that we match ResultType. EmitConversion.Convert(generator, variable.Type, this.ResultType, optimizationInfo); } else { // scope.Values[index] if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadArrayElement(typeof(object)); } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Load the value of the variable. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_GetValue); generator.Branch(endOfGet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (scope.ParentScope == null) { // Global variable access // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("variable", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Get a reference to the global object. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) generator.Pop(); } else { // Gets the value of a variable in an object scope. if (scopeVariable == null) EmitHelpers.LoadScope(generator); else generator.LoadVariable(scopeVariable); generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Object); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) generator.Pop(); } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); // Release the temporary variable. if (scopeVariable != null) generator.ReleaseTemporaryVariable(scopeVariable); // Define a label at the end. generator.DefineLabelPosition(endOfGet); // Object scope references may have side-effects (because of getters) so if the value // is to be ignored we evaluate the value then pop the value from the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <summary> /// Generates IL for the script. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> protected override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Method signature: object FunctionDelegate(Compiler.Scope scope, object thisObject, Library.FunctionInstance functionObject, object[] arguments) // Initialize the scope (note: the initial scope for a function is always declarative). this.BaseScope.GenerateScopeCreation(generator, optimizationInfo); // In ES3 the "this" value must be an object. See 10.4.3 in the spec. if (this.StrictMode == false && this.MethodOptimizationHints.HasThis == true) { // context.ConvertThisToObject(); EmitHelpers.LoadExecutionContext(generator); generator.Call(ReflectionHelpers.ExecutionContext_ConvertThisToObject); } // Transfer the function name into the scope. if (Name.HasStaticName && !Name.IsGetter && !Name.IsSetter && this.Arguments.Any(a => a.Name == Name.StaticName) == false && optimizationInfo.MethodOptimizationHints.HasVariable(Name.StaticName)) { EmitHelpers.LoadFunction(generator); var functionName = new NameExpression(this.BaseScope, Name.StaticName); functionName.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } // Transfer the arguments object into the scope. if (this.MethodOptimizationHints.HasArguments == true && this.Arguments.Any(a => a.Name == "arguments") == false) { // executionContext.CreateArgumentsInstance(object[] arguments) EmitHelpers.LoadExecutionContext(generator); this.BaseScope.GenerateReference(generator, optimizationInfo); EmitHelpers.LoadArgumentsArray(generator); generator.Call(ReflectionHelpers.ExecutionContext_CreateArgumentsInstance); var arguments = new NameExpression(this.BaseScope, "arguments"); arguments.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } // Transfer the argument values into the scope. // Note: the arguments array can be smaller than expected. if (this.Arguments.Count > 0) { for (int i = 0; i < this.Arguments.Count; i++) { // Check if a duplicate argument name exists. bool duplicate = false; for (int j = i + 1; j < this.Arguments.Count; j++) { if (this.Arguments[i].Name == this.Arguments[j].Name) { duplicate = true; break; } } if (duplicate == true) { continue; } var loadDefaultValue = generator.CreateLabel(); var storeValue = generator.CreateLabel(); // Check if an array element exists. EmitHelpers.LoadArgumentsArray(generator); generator.LoadArrayLength(); generator.LoadInt32(i); generator.BranchIfLessThanOrEqual(loadDefaultValue); // Load the parameter value from the parameters array. EmitHelpers.LoadArgumentsArray(generator); generator.LoadInt32(i); generator.LoadArrayElement(typeof(object)); if (this.Arguments[i].DefaultValue == null) { // Branch to the part where it stores the value. generator.Branch(storeValue); // Load undefined. generator.DefineLabelPosition(loadDefaultValue); EmitHelpers.EmitUndefined(generator); generator.ReinterpretCast(typeof(object)); } else { // Check if it's undefined. generator.Duplicate(); EmitHelpers.EmitUndefined(generator); generator.ReinterpretCast(typeof(object)); generator.BranchIfNotEqual(storeValue); generator.Pop(); // Load the default value. generator.DefineLabelPosition(loadDefaultValue); this.Arguments[i].DefaultValue.GenerateCode(generator, optimizationInfo); EmitConversion.ToAny(generator, this.Arguments[i].DefaultValue.ResultType); } // Store the value in the scope. generator.DefineLabelPosition(storeValue); var argument = new NameExpression(this.BaseScope, this.Arguments[i].Name); argument.GenerateSet(generator, optimizationInfo, PrimitiveType.Any); } } // Initialize any declarations. this.BaseScope.GenerateHoistedDeclarations(generator, optimizationInfo); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) { generator.DefineLabelPosition(optimizationInfo.ReturnTarget); } // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) { // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); } else { // There were no return statements - return null. generator.LoadNull(); } }
/// <summary> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call Get(object) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); } }
/// <summary> /// Generates IL for the script. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> protected override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Method signature: object FunctionDelegate(Compiler.Scope scope, object thisObject, Library.FunctionInstance functionObject, object[] arguments) // Initialize the scope (note: the initial scope for a function is always declarative). this.InitialScope.GenerateScopeCreation(generator, optimizationInfo); // Verify the scope is correct. VerifyScope(generator); // In ES3 the "this" value must be an object. See 10.4.3 in the spec. if (this.StrictMode == false && this.MethodOptimizationHints.HasThis == true) { // if (thisObject == null || thisObject == Null.Value || thisObject == Undefined.Value) EmitHelpers.LoadThis(generator); generator.LoadNull(); generator.CompareEqual(); EmitHelpers.LoadThis(generator); EmitHelpers.EmitNull(generator); generator.CompareEqual(); generator.BitwiseOr(); EmitHelpers.LoadThis(generator); EmitHelpers.EmitUndefined(generator); generator.CompareEqual(); generator.BitwiseOr(); // { var startOfFalse = generator.CreateLabel(); generator.BranchIfFalse(startOfFalse); // thisObject = engine.Global; EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Global); // } else { var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); generator.DefineLabelPosition(startOfFalse); // thisObject = TypeConverter.ToObject(thisObject); EmitHelpers.LoadThis(generator); EmitConversion.ToObject(generator, PrimitiveType.Any); // } generator.DefineLabelPosition(endOfIf); EmitHelpers.StoreThis(generator); } // Transfer the function name into the scope. if (string.IsNullOrEmpty(this.Name) == false && this.ArgumentNames.Contains(this.Name) == false && optimizationInfo.MethodOptimizationHints.HasVariable(this.Name)) { EmitHelpers.LoadFunction(generator); var functionName = new NameExpression(this.InitialScope, this.Name); functionName.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } // Transfer the arguments object into the scope. if (this.MethodOptimizationHints.HasArguments == true && this.ArgumentNames.Contains("arguments") == false) { // prototype EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Object); generator.Call(ReflectionHelpers.FunctionInstance_InstancePrototype); // callee EmitHelpers.LoadFunction(generator); generator.CastClass(typeof(Library.UserDefinedFunction)); // scope EmitHelpers.LoadScope(generator); generator.CastClass(typeof(DeclarativeScope)); // argumentValues EmitHelpers.LoadArgumentsArray(generator); generator.NewObject(ReflectionHelpers.Arguments_Constructor); var arguments = new NameExpression(this.InitialScope, "arguments"); arguments.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } // Transfer the argument values into the scope. // Note: the arguments array can be smaller than expected. if (this.ArgumentNames.Count > 0) { var endOfArguments = generator.CreateLabel(); for (int i = 0; i < this.ArgumentNames.Count; i++) { // Check if a duplicate argument name exists. bool duplicate = false; for (int j = i + 1; j < this.ArgumentNames.Count; j++) if (this.ArgumentNames[i] == this.ArgumentNames[j]) { duplicate = true; break; } if (duplicate == true) continue; // Check if an array element exists. EmitHelpers.LoadArgumentsArray(generator); generator.LoadArrayLength(); generator.LoadInt32(i); generator.BranchIfLessThanOrEqual(endOfArguments); // Store the array element in the scope. EmitHelpers.LoadArgumentsArray(generator); generator.LoadInt32(i); generator.LoadArrayElement(typeof(object)); var argument = new NameExpression(this.InitialScope, this.ArgumentNames[i]); argument.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } generator.DefineLabelPosition(endOfArguments); } // Initialize any declarations. this.InitialScope.GenerateDeclarations(generator, optimizationInfo); //EmitHelpers.LoadScope(generator); //EmitConversion.ToObject(generator, PrimitiveType.Any); //generator.Pop(); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) generator.DefineLabelPosition(optimizationInfo.ReturnTarget); // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); else // There were no return statements - return null. generator.LoadNull(); }
/// <summary> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { // This method generates code to retrieve the value of a variable, given the name of // variable and scope in which the variable is being referenced. The variable was // not necessary declared in this scope - it might be declared in any of the parent // scopes (together called a scope chain). The general algorithm is to start at the // head of the chain and search backwards until the variable is found. There are // two types of scopes: declarative scopes and object scopes. Object scopes are hard - // it cannot be known at compile time whether the variable exists or not so runtime // checks have to be inserted. Declarative scopes are easier - variables have to be // declared and cannot be deleted. There is one tricky bit: new variables can be // introduced into a declarative scope at runtime by a non-strict eval() statement. // Even worse, variables that were introduced by means of an eval() *can* be deleted. var scope = this.Scope; ILLocalVariable scopeVariable = null; var endOfGet = generator.CreateLabel(); do { if (scope is DeclarativeScope) { // The variable was declared in this scope. var variable = scope.GetDeclaredVariable(this.Name); if (variable != null) { if (scope.ExistsAtRuntime == false) { // The scope has been optimized away. The value of the variable is stored // in an ILVariable. // Declare an IL local variable if no storage location has been allocated yet. if (variable.Store == null) { variable.Store = generator.DeclareVariable(typeof(object), variable.Name); } // Load the value from the variable. generator.LoadVariable(variable.Store); // Ensure that we match ResultType. EmitConversion.Convert(generator, variable.Type, this.ResultType, optimizationInfo); } else { // scope.Values[index] if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.Call(ReflectionHelpers.DeclarativeScope_Values); generator.LoadInt32(variable.Index); generator.LoadArrayElement(typeof(object)); } // The variable was found - no need to search any more parent scopes. break; } else { // The variable was not defined at compile time, but may have been // introduced by an eval() statement. if (optimizationInfo.MethodOptimizationHints.HasEval == true) { // Check the variable exists: if (scope.HasValue(variableName) == true) { if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_HasValue); var hasValueClause = generator.CreateLabel(); generator.BranchIfFalse(hasValueClause); // Load the value of the variable. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(DeclarativeScope)); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.Scope_GetValue); generator.Branch(endOfGet); // } generator.DefineLabelPosition(hasValueClause); } } } else { if (scope.ParentScope == null) { // Global variable access // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("variable", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Get a reference to the global object. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(this.Name); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) { generator.Pop(); } } else { // Gets the value of a variable in an object scope. if (scopeVariable == null) { EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.CastClass(typeof(ObjectScope)); generator.Call(ReflectionHelpers.ObjectScope_ScopeObject); generator.LoadString(this.Name); generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Object); // Check if the value is null. generator.Duplicate(); generator.BranchIfNotNull(endOfGet); if (scope.ParentScope != null) { generator.Pop(); } } } // Try the parent scope. if (scope.ParentScope != null && scope.ExistsAtRuntime == true) { if (scopeVariable == null) { scopeVariable = generator.CreateTemporaryVariable(typeof(Scope)); EmitHelpers.LoadScope(generator); } else { generator.LoadVariable(scopeVariable); } generator.Call(ReflectionHelpers.Scope_ParentScope); generator.StoreVariable(scopeVariable); } scope = scope.ParentScope; } while (scope != null); // Throw an error if the name does not exist and throwIfUnresolvable is true. if (scope == null && throwIfUnresolvable == true) { EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo); } // Release the temporary variable. if (scopeVariable != null) { generator.ReleaseTemporaryVariable(scopeVariable); } // Define a label at the end. generator.DefineLabelPosition(endOfGet); // Object scope references may have side-effects (because of getters) so if the value // is to be ignored we evaluate the value then pop the value from the stack. //if (optimizationInfo.SuppressReturnValue == true) // generator.Pop(); }
/// <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> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; TypeOfMemberAccess memberAccessType = DetermineTypeOfMemberAccess(optimizationInfo, out propertyName); if (memberAccessType == TypeOfMemberAccess.ArrayIndex) { // Array indexer // ------------- // xxx = object[index] // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (memberAccessType == TypeOfMemberAccess.Static) { // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Call Get(object) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Object); } }
/// <summary> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null && (PrimitiveTypeUtilities.IsNumeric(rhs.ResultType) || rhs.ResultType == PrimitiveType.String)) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) isArrayIndex = true; } } if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call Get(string) //generator.LoadString(propertyName); //generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call Get(string) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); } }
/// <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> /// Pushes the value of the reference onto the stack. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> /// <param name="throwIfUnresolvable"> <c>true</c> to throw a ReferenceError exception if /// the name is unresolvable; <c>false</c> to output <c>null</c> instead. </param> public void GenerateGet(ILGenerator generator, OptimizationInfo optimizationInfo, bool throwIfUnresolvable) { string propertyName = null; bool isArrayIndex = false; // Right-hand-side can be a property name (a.b) if (this.OperatorType == OperatorType.MemberAccess) { var rhs = this.GetOperand(1) as NameExpression; if (rhs == null) { throw new JavaScriptException(optimizationInfo.Engine, "SyntaxError", "Invalid member access", optimizationInfo.SourceSpan.StartLine, optimizationInfo.Source.Path, optimizationInfo.FunctionName); } propertyName = rhs.Name; } // Or a constant indexer (a['b']) if (this.OperatorType == OperatorType.Index) { var rhs = this.GetOperand(1) as LiteralExpression; if (rhs != null && (PrimitiveTypeUtilities.IsNumeric(rhs.ResultType) || rhs.ResultType == PrimitiveType.String)) { propertyName = TypeConverter.ToString(rhs.Value); // Or a array index (a[0]) if (rhs.ResultType == PrimitiveType.Int32 || (propertyName != null && Library.ArrayInstance.ParseArrayIndex(propertyName) != uint.MaxValue)) { isArrayIndex = true; } } } if (isArrayIndex == true) { // Array indexer // ------------- // xxx = object[index] // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the right-hand side and convert to a uint32. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToUInt32(generator, rhs.ResultType); // Call the indexer. generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_Int); } else if (propertyName != null) { //// Load the left-hand side and convert to an object instance. //var lhs = this.GetOperand(0); //lhs.GenerateCode(generator, optimizationInfo); //EmitConversion.ToObject(generator, lhs.ResultType); //// Call Get(string) //generator.LoadString(propertyName); //generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); // Named property access (e.g. x = y.property) // ------------------------------------------- // __object_cacheKey = null; // __object_property_cachedIndex = 0; // ... // if (__object_cacheKey != object.InlineCacheKey) // xxx = object.InlineGetPropertyValue("property", out __object_property_cachedIndex, out __object_cacheKey) // else // xxx = object.InlinePropertyValues[__object_property_cachedIndex]; // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // TODO: share these variables somehow. var cacheKey = generator.DeclareVariable(typeof(object)); var cachedIndex = generator.DeclareVariable(typeof(int)); // Store the object into a temp variable. var objectInstance = generator.DeclareVariable(PrimitiveType.Object); generator.StoreVariable(objectInstance); // if (__object_cacheKey != object.InlineCacheKey) generator.LoadVariable(cacheKey); generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey); var elseClause = generator.CreateLabel(); generator.BranchIfEqual(elseClause); // value = object.InlineGetProperty("property", out __object_property_cachedIndex, out __object_cacheKey) generator.LoadVariable(objectInstance); generator.LoadString(propertyName); generator.LoadAddressOfVariable(cachedIndex); generator.LoadAddressOfVariable(cacheKey); generator.Call(ReflectionHelpers.ObjectInstance_InlineGetPropertyValue); var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); // else generator.DefineLabelPosition(elseClause); // value = object.InlinePropertyValues[__object_property_cachedIndex]; generator.LoadVariable(objectInstance); generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues); generator.LoadVariable(cachedIndex); generator.LoadArrayElement(typeof(object)); // End of the if statement generator.DefineLabelPosition(endOfIf); } else { // Dynamic property access // ----------------------- // xxx = object.Get(x) // Load the left-hand side and convert to an object instance. var lhs = this.GetOperand(0); lhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToObject(generator, lhs.ResultType, optimizationInfo); // Load the property name and convert to a string. var rhs = this.GetOperand(1); rhs.GenerateCode(generator, optimizationInfo); EmitConversion.ToString(generator, rhs.ResultType); // Call Get(string) generator.Call(ReflectionHelpers.ObjectInstance_GetPropertyValue_String); } }
/// <summary> /// Generates IL for the script. /// </summary> /// <param name="generator"> The generator to output the CIL to. </param> /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param> protected override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo) { // Method signature: object FunctionDelegate(Compiler.Scope scope, object thisObject, Library.FunctionInstance functionObject, object[] arguments) // Initialize the scope (note: the initial scope for a function is always declarative). this.InitialScope.GenerateScopeCreation(generator, optimizationInfo); // Verify the scope is correct. VerifyScope(generator); // In ES3 the "this" value must be an object. See 10.4.3 in the spec. if (this.StrictMode == false && this.MethodOptimizationHints.HasThis == true) { // if (thisObject == null || thisObject == Null.Value || thisObject == Undefined.Value) EmitHelpers.LoadThis(generator); generator.LoadNull(); generator.CompareEqual(); EmitHelpers.LoadThis(generator); EmitHelpers.EmitNull(generator); generator.CompareEqual(); generator.BitwiseOr(); EmitHelpers.LoadThis(generator); EmitHelpers.EmitUndefined(generator); generator.CompareEqual(); generator.BitwiseOr(); // { var startOfFalse = generator.CreateLabel(); generator.BranchIfFalse(startOfFalse); // thisObject = engine.Global; EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Global); // } else { var endOfIf = generator.CreateLabel(); generator.Branch(endOfIf); generator.DefineLabelPosition(startOfFalse); // thisObject = TypeConverter.ToObject(thisObject); EmitHelpers.LoadThis(generator); EmitConversion.ToObject(generator, PrimitiveType.Any, optimizationInfo); // } generator.DefineLabelPosition(endOfIf); EmitHelpers.StoreThis(generator); } // Transfer the function name into the scope. if (string.IsNullOrEmpty(this.Name) == false && this.IncludeNameInScope == true && this.ArgumentNames.Contains(this.Name) == false && optimizationInfo.MethodOptimizationHints.HasVariable(this.Name)) { EmitHelpers.LoadFunction(generator); var functionName = new NameExpression(this.InitialScope, this.Name); functionName.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } // Transfer the arguments object into the scope. if (this.MethodOptimizationHints.HasArguments == true && this.ArgumentNames.Contains("arguments") == false) { // prototype EmitHelpers.LoadScriptEngine(generator); generator.Call(ReflectionHelpers.ScriptEngine_Object); generator.Call(ReflectionHelpers.FunctionInstance_InstancePrototype); // callee EmitHelpers.LoadFunction(generator); generator.CastClass(typeof(Library.UserDefinedFunction)); // scope EmitHelpers.LoadScope(generator); generator.CastClass(typeof(DeclarativeScope)); // argumentValues EmitHelpers.LoadArgumentsArray(generator); generator.NewObject(ReflectionHelpers.Arguments_Constructor); var arguments = new NameExpression(this.InitialScope, "arguments"); arguments.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } // Transfer the argument values into the scope. // Note: the arguments array can be smaller than expected. if (this.ArgumentNames.Count > 0) { var endOfArguments = generator.CreateLabel(); for (int i = 0; i < this.ArgumentNames.Count; i++) { // Check if a duplicate argument name exists. bool duplicate = false; for (int j = i + 1; j < this.ArgumentNames.Count; j++) { if (this.ArgumentNames[i] == this.ArgumentNames[j]) { duplicate = true; break; } } if (duplicate == true) { continue; } // Check if an array element exists. EmitHelpers.LoadArgumentsArray(generator); generator.LoadArrayLength(); generator.LoadInt32(i); generator.BranchIfLessThanOrEqual(endOfArguments); // Store the array element in the scope. EmitHelpers.LoadArgumentsArray(generator); generator.LoadInt32(i); generator.LoadArrayElement(typeof(object)); var argument = new NameExpression(this.InitialScope, this.ArgumentNames[i]); argument.GenerateSet(generator, optimizationInfo, PrimitiveType.Any, false); } generator.DefineLabelPosition(endOfArguments); } // Initialize any declarations. this.InitialScope.GenerateDeclarations(generator, optimizationInfo); //EmitHelpers.LoadScope(generator); //EmitConversion.ToObject(generator, PrimitiveType.Any); //generator.Pop(); // Generate code for the body of the function. this.AbstractSyntaxTree.GenerateCode(generator, optimizationInfo); // Define the return target - this is where the return statement jumps to. // ReturnTarget can be null if there were no return statements. if (optimizationInfo.ReturnTarget != null) { generator.DefineLabelPosition(optimizationInfo.ReturnTarget); } // Load the return value. If the variable is null, there were no return statements. if (optimizationInfo.ReturnVariable != null) { // Return the value stored in the variable. Will be null if execution hits the end // of the function without encountering any return statements. generator.LoadVariable(optimizationInfo.ReturnVariable); } else { // There were no return statements - return null. generator.LoadNull(); } }
/// <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(); }