/// <summary> /// Calls the function with the given name. The function must exist on this object or an /// exception will be thrown. /// </summary> /// <param name="functionName"> The name of the function to call. </param> /// <param name="parameters"> The parameters to pass to the function. </param> /// <returns> The result of calling the function. </returns> internal object CallMemberFunctionOn(object thisObj, string functionName, params object[] parameters) { PropertyVariable function = GetProperty(functionName); if (function == null) { throw new JavaScriptException(this.Engine, "TypeError", "Object " + ToString() + " has no method '" + functionName + "'"); } MethodBase method = function.ConstantValue as MethodBase; if (method != null) { // Invoke it: return(method.Invoke(thisObj, parameters)); } MethodGroup set = function.ConstantValue as MethodGroup; if (set != null) { // Invoke it: set.Invoke(thisObj, parameters); } throw new JavaScriptException(this.Engine, "TypeError", "Property '" + functionName + "' of object " + ToString() + " is not a function"); }
/// <summary>Enumerates all properties and accounts for any custom ones if the object defines it.</summary> public IEnumerable <string> PropertyEnumerator(object obj) { // Got a custom one? PropertyVariable pv = GetProperty("Properties"); if (pv == null) { foreach (KeyValuePair <string, PropertyVariable> kvp in Properties) { if (kvp.Value.IsEnumerable) { yield return(kvp.Key); } } } else { // Get the value: IEnumerable <string> set = pv.GetValue(obj) as IEnumerable <string>; // Iterate it: foreach (string str in set) { yield return(str); } } }
/// <summary>Enumerates all the values in the given object.</summary> public IEnumerable <object> PropertyValues(object obj) { // Got a custom one? PropertyVariable pv = GetProperty("PropertyValues"); if (pv == null) { foreach (KeyValuePair <string, PropertyVariable> kvp in Properties) { if (kvp.Value.IsEnumerable) { // Read the value: object value = kvp.Value.GetValue(obj); yield return(value); } } } else { // Get the value: IEnumerable <object> set = pv.GetValue(obj) as IEnumerable <object>; // Iterate it: foreach (object s in set) { yield return(s); } } }
/// <summary> /// Adds a property to this object, typically generating a field of the given type. /// </summary> /// <param name="name"> The name of the property to add. </param> /// <param name="attributes"> The property attributes. </param> internal PropertyVariable AddProperty(string name, Nitrassic.Library.PropertyAttributes attributes, Type valueType) { if (Properties == null) { Properties = new Dictionary <string, PropertyVariable>(); } PropertyVariable sp = new PropertyVariable(this, name, attributes, valueType); Properties[name] = sp; return(sp); }
/// <summary>Sets a generic property on the given object at runtime. Note that this can't create a property.</summary> public static void SetPropertyValue(ScriptEngine engine, object thisObj, string property, object value) { // Exists? if (thisObj == null) { throw new NullReferenceException("Attempted to set property '" + property + "' on a null reference."); } // Get the prototype: Prototype proto = engine.Prototypes.Get(thisObj.GetType()); // Get the property: PropertyVariable pv = proto.GetProperty(property); if (pv == null) { // Undefined: throw new JavaScriptException(engine, "TypeError", "Can't set dynamic properties on generic objects."); } // Set the value: pv.SetValue(thisObj, value); }
/// <summary>Pulls a generic property from the given object at runtime.</summary> public static object GetPropertyValue(ScriptEngine engine, object thisObj, string property) { // Exists? if (thisObj == null) { throw new NullReferenceException("Attempted to read property '" + property + "' from a null reference."); } // Get the prototype: Prototype proto = engine.Prototypes.Get(thisObj.GetType()); // Get the property: PropertyVariable pv = proto.GetProperty(property); if (pv == null) { // Undefined: return(Nitrassic.Undefined.Value); } // Get the value: return(pv.GetValue(thisObj)); }
/// <summary> /// Adds the given method to the prototype. /// </summary> private void AddMethod(MethodBase method, Type type, int staticMode) { // Get the properties attribute if it's got one: var attribute = (JSProperties)Attribute.GetCustomAttribute(method, typeof(JSProperties)); if (attribute != null && attribute.Hidden) { return; } // Determine the name of the method. string name; if (attribute != null && attribute.Name != null) { name = attribute.Name; } else { name = method.Name; } // Reject properties (but not constructors): if (method.IsSpecialName && name != ".ctor") { return; } ParameterInfo[] mParams = null; if (method.IsStatic && staticMode != 0) { // If it's static and it does not have a 'thisObj' property // then it's a constructor level static function, IF the object has a constructor at all. mParams = method.GetParameters(); if (mParams.Length > 0) { if (mParams[0].Name == "thisObj" || (mParams.Length > 1 && mParams[1].Name == "thisObj")) { // Not actually static. if (staticMode == 2) { return; } } else if (staticMode == 1) { return; } } } bool enumerable = true; if (name == "OnConstruct" || name == ".ctor") { name = ".make"; enumerable = false; } else if (name == "OnCall") { name = ".call"; enumerable = false; } // Special case for get_ or set_ methods - we want them to act like properties. // Chop off get_ or set_ here (so the actual name entering the proto is correct) // then when the user attempts to use it like a property, it checks then. int actLikeProperty = 0; if (name.StartsWith("get_")) { name = name.Substring(4); actLikeProperty = 1; } else if (name.StartsWith("set_")) { name = name.Substring(4); actLikeProperty = 2; } // For methods, auto == lowercase. if (attribute == null) { name = char.ToLower(name[0]) + name.Substring(1); } else { // For methods, auto == lowercase if (attribute.FirstCharacter == CaseMode.Upper) { name = char.ToUpper(name[0]) + name.Substring(1); } else if (attribute.FirstCharacter == CaseMode.Lower || attribute.FirstCharacter == CaseMode.Auto) { name = char.ToLower(name[0]) + name.Substring(1); } } if (actLikeProperty != 0) { if (name == "_Item") { // Rename! Actually got an indexer here. name = "___item"; if (mParams == null) { mParams = method.GetParameters(); } // Skip thisObj and engine if they're set: for (int i = 0; i < mParams.Length; i++) { ParameterInfo p = mParams[i]; if (i < 2) { // Skip special parameters. if (p.Name == "thisObj" || (i == 0 && p.ParameterType == typeof(ScriptEngine))) { continue; } } // Append the type name: name += "_" + p.ParameterType.Name; } } else if (name == "_Properties") { // Rename! Got the property enumerator here. name = "___properties"; } } // Check property attributes. Nitrassic.Library.PropertyAttributes descriptorAttributes = Nitrassic.Library.PropertyAttributes.Sealed; if (attribute != null && attribute.IsEnumerable && enumerable) { descriptorAttributes |= Nitrassic.Library.PropertyAttributes.Enumerable; } if (attribute == null || attribute.IsConfigurable) { descriptorAttributes |= Nitrassic.Library.PropertyAttributes.Configurable; } if (attribute == null || attribute.IsWritable) { descriptorAttributes |= Nitrassic.Library.PropertyAttributes.Writable; } // Already defined this property? PropertyVariable property = GetProperty(name); if (property == null) { // Add as a single method, optionally as a virtual property: object propertyValue; if (actLikeProperty == 0) { // Straight apply the method: propertyValue = method; } else { // Create a virtual property: VirtualProperty vProperty = new VirtualProperty(); // Apply the method: if (actLikeProperty == 1) { vProperty.GetMethod = method; vProperty.LoadMeta(); } else { vProperty.SetMethod = method; } // Apply virtual property as the value: propertyValue = vProperty; } property = AddProperty(name, propertyValue, descriptorAttributes); } else if (actLikeProperty != 0) { // Got the other method in a property. VirtualProperty vProperty = property.ConstantValue as VirtualProperty; // Apply the method: if (actLikeProperty == 1) { vProperty.GetMethod = method; vProperty.LoadMeta(); } else { vProperty.SetMethod = method; } } else { // Already a method set? MethodGroup group = property.ConstantValue as MethodGroup; if (group == null) { // Create a method group and add the current method to it: group = new MethodGroup(); // Set the attributes: group.PropertyAttributes = descriptorAttributes; // Add the method: group.Add(property.ConstantValue as MethodBase); // Force the property to change type: property.ForceChange(group); } // Add this method to the group: group.Add(method); if (group.PropertyAttributes != descriptorAttributes) { throw new InvalidOperationException("Inconsistant property attributes detected on " + method + "."); } } }