// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a new instance of a user-defined class. /// </summary> /// <param name="prototype"> The next object in the prototype chain. </param> /// <param name="name"> The name of the class. Can be <c>null</c> if none were supplied. </param> /// <param name="instancePrototype"> The value of the 'prototype' property. </param> /// <param name="constructor"> A function that represents the constructor, if the class has /// one, or <c>null</c> otherwise. </param> /// <remarks> /// A class that doesn't extend looks like this: /// new ClassFunction(engine.Function.InstancePrototype, name, engine.Object.Construct(), constructor) /// /// A class that extends A looks like this: /// new ClassFunction(A, name, ObjectInstance.CreateRawObject(A.InstancePrototype), constructor) /// /// A class that extends null looks like this: /// new ClassFunction(engine.Function.InstancePrototype, name, ObjectInstance.CreateRawObject(null), constructor) /// </remarks> public ClassFunction(ObjectInstance prototype, string name, ObjectInstance instancePrototype, UserDefinedFunction constructor) : base(prototype) { if (instancePrototype == null) { throw new ArgumentNullException(nameof(instancePrototype)); } this.constructor = constructor; // Initialize the instance prototype. instancePrototype.InitializeProperties(new List <PropertyNameAndValue> { new PropertyNameAndValue("constructor", this, PropertyAttributes.NonEnumerable) }); // Now add properties to this object. int length = constructor == null ? 0 : constructor.Length; InitializeProperties(new List <PropertyNameAndValue>() { new PropertyNameAndValue("name", name ?? string.Empty, PropertyAttributes.Configurable), new PropertyNameAndValue("length", length, PropertyAttributes.Configurable), new PropertyNameAndValue("prototype", instancePrototype, PropertyAttributes.Writable), }); }
/// <summary> /// Costructor /// </summary> /// <param name="userDefinedFunction">The watched function</param> public UserDefinedFunctionDebugView(UserDefinedFunction userDefinedFunction) { this.userDefinedFunction = userDefinedFunction; }
// 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); } }
/// <summary> /// Sets the value of a object literal property to a setter. If the value already has a /// getter then it will be retained. /// </summary> /// <param name="obj"> The object to set the property on. </param> /// <param name="key"> The property key (can be a string or a symbol).</param> /// <param name="setter"> The setter function. </param> public static void SetObjectLiteralSetter(ObjectInstance obj, object key, UserDefinedFunction setter) { var descriptor = obj.GetOwnPropertyDescriptor(key); if (descriptor.Exists == false || !descriptor.IsAccessor) obj.DefineProperty(key, new PropertyDescriptor(null, setter, Library.PropertyAttributes.FullAccess), throwOnError: false); else obj.DefineProperty(key, new PropertyDescriptor(descriptor.Getter, setter, Library.PropertyAttributes.FullAccess), throwOnError: false); }