Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Jurassic/Library/ClrWrapper/ClrInstanceTypeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ public override string DebuggerDisplayType



// OBJECTINSTANCE OVERRIDES
//_________________________________________________________________________________________

/// <summary>
/// This object is a Clr Wrapper
/// </summary>
protected override bool IsClrWrapper
{
get { return true; }
}



// OBJECT OVERRIDES
//_________________________________________________________________________________________
Expand Down
7 changes: 7 additions & 0 deletions Jurassic/Library/ClrWrapper/ClrInstanceWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ public override string DebuggerDisplayType
// return base.GetPrimitiveValue(typeHint);
//}

/// <summary>
/// This object is a Clr Wrapper
/// </summary>
protected override bool IsClrWrapper
{
get { return true; }
}



Expand Down
25 changes: 24 additions & 1 deletion Jurassic/Library/ClrWrapper/ClrStaticTypeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ internal static void PopulateMembers(ObjectInstance target, Type type, BindingFl
{
// Register static methods as functions.
var methodGroups = new Dictionary<string, List<MethodBase>>();
int indexerCounter = 0;
foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.DeclaredOnly | flags))
{
switch (member.MemberType)
Expand All @@ -218,7 +219,17 @@ internal static void PopulateMembers(ObjectInstance target, Type type, BindingFl
ClrFunction getter = getMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(getMethod));
var setMethod = property.GetSetMethod();
ClrFunction setter = setMethod == null ? null : new ClrFunction(target.Engine.Function.InstancePrototype, new ClrBinder(setMethod));
target.DefineProperty(property.Name, new PropertyDescriptor(getter, setter, PropertyAttributes.NonEnumerable), false);

PropertyAttributes propertyAttributes = PropertyAttributes.NonEnumerable;
if (property.GetIndexParameters().Length > 0)
{
propertyAttributes |= PropertyAttributes.IsIndexProperty;
// Put index after name of indexer property to avoid conflict with other properties with same name and other indexers
target.DefineProperty(string.Format("{0}{1}", property.Name, indexerCounter++),
new PropertyDescriptor(getter, setter, propertyAttributes), false);
}
else
target.DefineProperty(property.Name, new PropertyDescriptor(getter, setter, propertyAttributes), false);

// Property getters and setters also show up as methods, so remove them here.
// NOTE: only works if properties are enumerated after methods.
Expand Down Expand Up @@ -254,6 +265,18 @@ internal static void PopulateMembers(ObjectInstance target, Type type, BindingFl



// OBJECTINSTANCE OVERRIDES
//_________________________________________________________________________________________

/// <summary>
/// This object is a Clr Wrapper
/// </summary>
protected override bool IsClrWrapper
{
get { return true; }
}



// OBJECT OVERRIDES
//_________________________________________________________________________________________
Expand Down
10 changes: 9 additions & 1 deletion Jurassic/Library/Object/HiddenClassSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class HiddenClassSchema
private Dictionary<object, SchemaProperty> properties;

// Transitions
private struct TransitionInfo
private struct TransitionInfo
{
public object Key;
public PropertyAttributes Attributes;
Expand Down Expand Up @@ -124,6 +124,14 @@ public SchemaProperty GetPropertyIndexAndAttributes(object key)
return propertyInfo;
}

public IEnumerable<SchemaProperty> GetIndexers()
{
if (this.properties == null)
this.properties = CreatePropertiesDictionary();
IEnumerable<SchemaProperty> result = this.properties.Values.Where(sp => sp.IsIndexer);
return result;
}

/// <summary>
/// Adds a property to the schema.
/// </summary>
Expand Down
195 changes: 190 additions & 5 deletions Jurassic/Library/Object/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,16 @@ private object GetPropertyValue(uint index, ObjectInstance thisValue)
// The property might exist in the prototype.
if (this.prototype == null)
return thisValue.GetMissingPropertyValue(index.ToString());
return this.prototype.GetPropertyValue(index, thisValue);
object result = this.prototype.GetPropertyValue(index, thisValue);
if (result == null)
{
result = GetArrayElementPropertyValue(index, thisValue);
}
if (result == null)
{
result = GetIndexerPropertyValue(index, thisValue);
}
return result;
}

/// <summary>
Expand All @@ -339,7 +348,12 @@ public object GetPropertyValue(object key)
return GetPropertyValue(arrayIndex);

// Otherwise, the property is a name.
return GetNamedPropertyValue(key, this);
object result = GetNamedPropertyValue(key, this);

// Otherwise, the property can be an indexer
if (result == null)
result = GetIndexerPropertyValue(key, this);
return result;
}

/// <summary>
Expand Down Expand Up @@ -385,7 +399,11 @@ public object GetPropertyValue(PropertyReference propertyReference)
propertyReference.ClearCache();
if (this.Prototype == null)
return this.GetMissingPropertyValue(propertyReference.Name);
return this.Prototype.GetNamedPropertyValue(propertyReference.Name, this);

var propertyValuePrototype = this.Prototype.GetNamedPropertyValue(propertyReference.Name, this);
if (propertyValuePrototype == null)
propertyValuePrototype = GetIndexerPropertyValue(propertyReference.Name, this);
return propertyValuePrototype;
}
}

Expand Down Expand Up @@ -427,6 +445,71 @@ private object GetNamedPropertyValue(object key, ObjectInstance thisValue)
return thisValue.GetMissingPropertyValue(key);
}

/// <summary>
/// Gets the value of the property using indexer.
/// </summary>
/// <param name="key"> The property key - string, int, and so on.
/// The type should be the same as the type of the indexer parameter</param>
/// <param name="thisValue"> The value of the "this" keyword inside a getter. </param>
/// <returns> The value of the property, or <c>null</c> if the property doesn't exist. </returns>
/// <remarks> The prototype chain is searched if the indexer property does not exist directly on
/// this object. </remarks>
private object GetIndexerPropertyValue(object key, ObjectInstance thisValue)
{
if (!IsClrWrapper)
return null; // This method is only for CLR clases

ObjectInstance prototypeObject = thisValue;
do
{
// Retrieve information about the property.
IEnumerable<SchemaProperty> properties = prototypeObject.schema.GetIndexers();
foreach (SchemaProperty property in properties)
{
if (property.Exists == true)
{
// The property was found!
object value = prototypeObject.propertyValues[property.Index];
try
{
return ((PropertyAccessorValue)value).GetValue(thisValue, new object[] { key });
}
catch (Exception)
{
}
}
}

// Traverse the prototype chain.
prototypeObject = prototypeObject.prototype;
} while (prototypeObject != null);

// The indexer property doesn't exist.
return null;
}

/// <summary>
/// Gets the value of the property with the given array index if current object is an Array.
/// </summary>
/// <param name="index"> The array index of the property. </param>
/// <param name="thisValue"> The value of the "this" keyword inside a getter. </param>
/// <returns> The value of the property, or <c>null</c> if the object is not an Array. </returns>
private object GetArrayElementPropertyValue(uint index, ObjectInstance thisValue)
{
if (!IsClrWrapper)
return null; //This method is only for CLR clases

object result = null;
ClrInstanceWrapper thisWrapper = thisValue as ClrInstanceWrapper;
if (thisWrapper != null && thisWrapper.WrappedInstance.GetType().IsArray)
{
Array thisArray = thisWrapper.WrappedInstance as Array;
result = thisArray.GetValue(index);
}

return result;
}

/// <summary>
/// Retrieves the value of a property which doesn't exist on the object. This method can
/// be overridden to effectively construct properties on the fly. The default behavior is
Expand Down Expand Up @@ -500,6 +583,11 @@ public virtual void SetPropertyValue(uint index, object value, bool throwOnError
string indexStr = index.ToString();
bool exists = SetPropertyValueIfExists(indexStr, value, throwOnError);
if (exists == false)
{
exists = SetArrayElementPropertyValue(index, this, value);
}
if (exists == false &&
SetIndexerPropertyValue(index, value, this) == false)
{
// The property doesn't exist - add it.
AddProperty(indexStr, value, PropertyAttributes.FullAccess, throwOnError);
Expand Down Expand Up @@ -528,7 +616,8 @@ public void SetPropertyValue(object key, object value, bool throwOnError)
}

bool exists = SetPropertyValueIfExists(key, value, throwOnError);
if (exists == false)
if (exists == false &&
SetIndexerPropertyValue(key, value, this) == false)
{
// The property doesn't exist - add it.
AddProperty(key, value, PropertyAttributes.FullAccess, throwOnError);
Expand Down Expand Up @@ -666,7 +755,95 @@ public bool SetPropertyValueIfExists(object key, object value, bool throwOnError
// The property does not exist.
return false;
}


/// <summary>
/// Sets the value of the property using indexer.
/// </summary>
/// <param name="key"> The property key - string, int, and so on.
/// The type should be the same as the type of the indexer parameter</param>
/// <param name="value"> The value to set </param>
/// <param name="thisValue"> The value of the "this" keyword inside a setter. </param>
/// <returns> <c>true</c> if the property exists; <c>false</c> otherwise. </returns>
/// <remarks> The prototype chain is searched if the indexer property does not exist directly on
/// this object. </remarks>
private bool SetIndexerPropertyValue(object key, object value, ObjectInstance thisValue)
{
if (!IsClrWrapper)
return false; // This method is only for CLR clases

ObjectInstance prototypeObject = thisValue;
do
{
// Retrieve information about the property.
IEnumerable<SchemaProperty> properties = prototypeObject.schema.GetIndexers();
foreach (SchemaProperty property in properties)
{
if (property.Exists == true)
{
// The property was found!
object propertyAccessor = prototypeObject.propertyValues[property.Index];
try
{
((PropertyAccessorValue)propertyAccessor).SetValue(thisValue, key, value);
return true;
}
catch (Exception)
{
}
}
}

// Traverse the prototype chain.
prototypeObject = prototypeObject.prototype;
} while (prototypeObject != null);

// The indexer property doesn't exist.
return false;
}

/// <summary>
/// Sets the value of the property with the given array index if current object is an Array
/// </summary>
/// <param name="index"> The array index of the property to set. </param>
/// <param name="thisValue"> The value of the "this" keyword inside a setter. </param>
/// <param name="value"> The value to set </param>
/// <returns> <c>true</c> if the value is set; <c>false</c> otherwise. </returns>
private bool SetArrayElementPropertyValue(uint index, ObjectInstance thisValue, object value)
{
if (!IsClrWrapper)
return false; // This method is only for CLR clases

ClrInstanceWrapper thisWrapper = thisValue as ClrInstanceWrapper;
if (thisWrapper != null && thisWrapper.WrappedInstance.GetType().IsArray)
{
Array thisArray = thisWrapper.WrappedInstance as Array;
object unwrappedValue = value;
if (value is ClrStaticTypeWrapper)
{
unwrappedValue = (value as ClrStaticTypeWrapper).WrappedType;
}
else if (value is ClrInstanceTypeWrapper)
{
unwrappedValue = (value as ClrInstanceTypeWrapper).WrappedType;
}
else if (value is ClrInstanceWrapper)
{
unwrappedValue = (value as ClrInstanceWrapper).WrappedInstance;
}

// Convert to element type if possible
if (unwrappedValue != null && unwrappedValue is IConvertible)
{
Type elementType = thisArray.GetType().GetElementType();
unwrappedValue = Convert.ChangeType(unwrappedValue, elementType);
}
thisArray.SetValue(unwrappedValue, (long)index);
return true;
}

return false;
}

/// <summary>
/// Deletes the property with the given array index.
/// </summary>
Expand Down Expand Up @@ -1053,6 +1230,14 @@ public override string ToString()
}
}

/// <summary>
/// Returns true when JavaScript object is a Clr Wrapper
/// </summary>
protected virtual bool IsClrWrapper
{
get { return false; }
}



// JAVASCRIPT FUNCTIONS
Expand Down
11 changes: 6 additions & 5 deletions Jurassic/Library/Object/PropertyAccessorValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,25 @@ public FunctionInstance Setter
/// Gets the property value by calling the getter, if one is present.
/// </summary>
/// <param name="thisObject"> The value of the "this" keyword inside the getter. </param>
/// <param name="argumentValues"></param>
/// <returns> The property value returned by the getter. </returns>
public object GetValue(ObjectInstance thisObject)
public object GetValue(ObjectInstance thisObject, params object[] argumentValues)
{
if (this.getter == null)
return Undefined.Value;
return this.getter.CallLateBound(thisObject);
return this.getter.CallLateBound(thisObject, argumentValues);
}

/// <summary>
/// Sets the property value by calling the setter, if one is present.
/// </summary>
/// <param name="thisObject"> The value of the "this" keyword inside the setter. </param>
/// <param name="value"> The desired value. </param>
public void SetValue(ObjectInstance thisObject, object value)
/// <param name="argumentValues"> The values as arguments list. </param>
public void SetValue(ObjectInstance thisObject, params object[] argumentValues)
{
if (this.setter == null)
return;
this.setter.CallLateBound(thisObject, value);
this.setter.CallLateBound(thisObject, argumentValues);
}

/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions Jurassic/Library/Object/PropertyAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,10 @@ public enum PropertyAttributes
/// Indicates the property is the "magic" length property (only found on arrays).
/// </summary>
IsLengthProperty = 16,

/// <summary>
/// Indicates the property is index property
/// </summary>
IsIndexProperty = 32,
}
}
Loading