// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new instance of a user-defined function. /// </summary> /// <param name="targetFunction"> The function that was bound. </param> /// <param name="boundThis"> The value of the "this" parameter when the target function is called. </param> /// <param name="boundArguments"> Zero or more bound argument values. </param> internal BoundFunction(FunctionInstance targetFunction, object boundThis, object[] boundArguments) : base(targetFunction.Prototype) { if (targetFunction == null) { throw new ArgumentNullException(nameof(targetFunction)); } if (boundArguments == null) { boundArguments = new object[0]; } this.TargetFunction = targetFunction; this.BoundThis = boundThis; this.BoundArguments = boundArguments; // Add function properties. this.FastSetProperty("name", targetFunction.Name, PropertyAttributes.Configurable); this.FastSetProperty("length", Math.Max(targetFunction.Length - boundArguments.Length, 0), PropertyAttributes.Configurable); this.FastSetProperty("prototype", this.Engine.Object.Construct(), PropertyAttributes.Writable); this.InstancePrototype.FastSetProperty("constructor", this, PropertyAttributes.NonEnumerable); // Caller and arguments cannot be accessed. var thrower = new ThrowTypeErrorFunction(this.Engine.Function, "The 'caller' or 'arguments' properties cannot be accessed on a bound function."); var accessor = new PropertyAccessorValue(thrower, thrower); this.FastSetProperty("caller", accessor, PropertyAttributes.IsAccessorProperty, overwriteAttributes: true); this.FastSetProperty("arguments", accessor, PropertyAttributes.IsAccessorProperty, overwriteAttributes: true); }
// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new instance of a user-defined function. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="targetFunction"> The function that was bound. </param> /// <param name="boundThis"> The value of the "this" parameter when the target function is called. </param> /// <param name="boundArguments"> Zero or more bound argument values. </param> internal BoundFunction(FunctionInstance targetFunction, object boundThis, object[] boundArguments) : base(targetFunction.Prototype) { if (targetFunction == null) throw new ArgumentNullException("targetFunction"); if (boundArguments == null) boundArguments = new object[0]; this.TargetFunction = targetFunction; this.BoundThis = boundThis; this.BoundArguments = boundArguments; // Add function properties. this.FastSetProperty("name", targetFunction.Name); this.FastSetProperty("length", Math.Max(targetFunction.Length - boundArguments.Length, 0)); this.FastSetProperty("prototype", this.Engine.Object.Construct(), PropertyAttributes.Writable); this.InstancePrototype.FastSetProperty("constructor", this, PropertyAttributes.NonEnumerable); // Caller and arguments cannot be accessed. var thrower = new ThrowTypeErrorFunction(this.Engine.Function, "The 'caller' or 'arguments' properties cannot be accessed on a bound function."); var accessor = new PropertyAccessorValue(thrower, thrower); this.FastSetProperty("caller", accessor, PropertyAttributes.IsAccessorProperty, overwriteAttributes: true); this.FastSetProperty("arguments", accessor, PropertyAttributes.IsAccessorProperty, overwriteAttributes: true); }
// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new Arguments instance. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="callee"> The function that was called. </param> /// <param name="bindings"> The bindings to allow modification. </param> /// <param name="argumentValues"> The argument values that were passed to the function. </param> internal ArgumentsInstance(ObjectInstance prototype, UserDefinedFunction callee, RuntimeScope bindings, object[] argumentValues) : base(prototype) { if (callee == null) { throw new ArgumentNullException(nameof(callee)); } if (bindings == null) { throw new ArgumentNullException(nameof(bindings)); } if (argumentValues == null) { throw new ArgumentNullException(nameof(argumentValues)); } this.callee = callee; this.bindings = bindings; InitializeProperties(GetDeclarativeProperties(Engine)); this.FastSetProperty("length", argumentValues.Length, PropertyAttributes.NonEnumerable); if (this.callee.StrictMode == false) { this.FastSetProperty("callee", callee, PropertyAttributes.NonEnumerable); // Create an array mappedArguments where mappedArguments[i] = true means a mapping is // maintained between arguments[i] and the corresponding variable. this.mappedArguments = new bool[argumentValues.Length]; var mappedNames = new Dictionary <string, int>(); // maps argument name -> index for (int i = 0; i < argumentValues.Length; i++) { if (i < callee.ArgumentNames.Count) { // Check if the argument name appeared previously in the argument list. int previousIndex; if (mappedNames.TryGetValue(callee.ArgumentNames[i], out previousIndex) == true) { // The argument name has appeared before. Remove the getter/setter. this.DefineProperty(previousIndex.ToString(), new PropertyDescriptor(argumentValues[previousIndex], PropertyAttributes.FullAccess), false); // The argument is no longer mapped. this.mappedArguments[previousIndex] = false; } // Add the argument name and index to the hashtable. mappedNames[callee.ArgumentNames[i]] = i; // The argument is mapped by default. this.mappedArguments[i] = true; // Define a getter and setter so that the property value reflects that of the argument. var getter = new UserDefinedFunction(this.Engine.Function.InstancePrototype, "ArgumentGetter", new string[0], this.bindings, "return " + callee.ArgumentNames[i], ArgumentGetter, true); getter["argumentIndex"] = i; var setter = new UserDefinedFunction(this.Engine.Function.InstancePrototype, "ArgumentSetter", new string[] { "value" }, this.bindings, callee.ArgumentNames[i] + " = value", ArgumentSetter, true); setter["argumentIndex"] = i; this.DefineProperty(i.ToString(), new PropertyDescriptor(getter, setter, PropertyAttributes.FullAccess), false); } else { // This argument is unnamed - no mapping needs to happen. this[(uint)i] = argumentValues[i]; } } } else { // In strict mode, arguments items are not connected to the variables. for (int i = 0; i < argumentValues.Length; i++) { this[(uint)i] = argumentValues[i]; } // In strict mode, accessing caller or callee is illegal. var throwErrorFunction = new ThrowTypeErrorFunction(this.Engine.Function.InstancePrototype); this.DefineProperty("caller", new PropertyDescriptor(throwErrorFunction, throwErrorFunction, PropertyAttributes.Sealed), false); this.DefineProperty("callee", new PropertyDescriptor(throwErrorFunction, throwErrorFunction, PropertyAttributes.Sealed), false); } }
// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new Arguments instance. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="callee"> The function that was called. </param> /// <param name="scope"> The function scope. </param> /// <param name="argumentValues"> The argument values that were passed to the function. </param> public ArgumentsInstance(ObjectInstance prototype, UserDefinedFunction callee, DeclarativeScope scope, object[] argumentValues) : base(prototype) { if (callee == null) throw new ArgumentNullException("callee"); if (scope == null) throw new ArgumentNullException("scope"); if (argumentValues == null) throw new ArgumentNullException("argumentValues"); this.callee = callee; this.scope = scope; this.FastSetProperty("length", argumentValues.Length, PropertyAttributes.NonEnumerable); if (this.callee.StrictMode == false) { this.FastSetProperty("callee", callee, PropertyAttributes.NonEnumerable); // Create an array mappedArguments where mappedArguments[i] = true means a mapping is // maintained between arguments[i] and the corresponding variable. this.mappedArguments = new bool[argumentValues.Length]; var mappedNames = new Dictionary<string, int>(); // maps argument name -> index for (int i = 0; i < argumentValues.Length; i++) { if (i < callee.ArgumentNames.Count) { // Check if the argument name appeared previously in the argument list. int previousIndex; if (mappedNames.TryGetValue(callee.ArgumentNames[i], out previousIndex) == true) { // The argument name has appeared before. Remove the getter/setter. this.DefineProperty(previousIndex.ToString(), new PropertyDescriptor(argumentValues[previousIndex], PropertyAttributes.FullAccess), false); // The argument is no longer mapped. this.mappedArguments[previousIndex] = false; } // Add the argument name and index to the hashtable. mappedNames[callee.ArgumentNames[i]] = i; // The argument is mapped by default. this.mappedArguments[i] = true; // Define a getter and setter so that the property value reflects that of the argument. var getter = new UserDefinedFunction(this.Engine.Function.InstancePrototype, "ArgumentGetter", new string[0], this.scope, "return " + callee.ArgumentNames[i], ArgumentGetter, true); getter.SetPropertyValue("argumentIndex", i, false); var setter = new UserDefinedFunction(this.Engine.Function.InstancePrototype, "ArgumentSetter", new string[] { "value" }, this.scope, callee.ArgumentNames[i] + " = value", ArgumentSetter, true); setter.SetPropertyValue("argumentIndex", i, false); this.DefineProperty(i.ToString(), new PropertyDescriptor(getter, setter, PropertyAttributes.FullAccess), false); } else { // This argument is unnamed - no mapping needs to happen. this[(uint)i] = argumentValues[i]; } } } else { // In strict mode, arguments items are not connected to the variables. for (int i = 0; i < argumentValues.Length; i++) this[(uint)i] = argumentValues[i]; // In strict mode, accessing caller or callee is illegal. var throwErrorFunction = new ThrowTypeErrorFunction(this.Engine.Function.InstancePrototype); this.DefineProperty("caller", new PropertyDescriptor(throwErrorFunction, throwErrorFunction, PropertyAttributes.Sealed), false); this.DefineProperty("callee", new PropertyDescriptor(throwErrorFunction, throwErrorFunction, PropertyAttributes.Sealed), false); } }