Skip to content

Commit dd5bfb9

Browse files
committed
Numerical indices should return undefined instead of null fixes #17
1 parent 809b571 commit dd5bfb9

File tree

5 files changed

+117
-41
lines changed

5 files changed

+117
-41
lines changed

AngleSharp.Scripting.JavaScript.Tests/ScriptingTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ public async Task GetPropertyOfDocument()
3535
Assert.AreEqual("#document", result);
3636
}
3737

38+
[Test]
39+
public async Task GetNodeTypeOfDocument()
40+
{
41+
var type = await EvaluateSimpleScriptAsync("typeof document.nodeType");
42+
var value = await EvaluateSimpleScriptAsync("document.nodeType");
43+
Assert.AreEqual("number", type);
44+
Assert.AreEqual("9", value);
45+
}
46+
3847
[Test]
3948
public async Task GetChildNodesLengthOfDocument()
4049
{

AngleSharp.Scripting.JavaScript/DomFunctionInstance.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ private JsValue ToString(JsValue thisObj, JsValue[] arguments)
4848
var func = thisObj.TryCast<FunctionInstance>();
4949

5050
if (func == null)
51+
{
5152
throw new JavaScriptException(Engine.TypeError, "Function object expected.");
53+
}
5254

5355
var officialName = _method.GetOfficialName();
5456
return string.Format("function {0} () {{ [native code] }}", officialName);

AngleSharp.Scripting.JavaScript/DomNodeInstance.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace AngleSharp.Scripting.JavaScript
22
{
33
using AngleSharp.Attributes;
4+
using Jint.Native;
45
using Jint.Native.Object;
56
using Jint.Runtime.Descriptors;
67
using System;
@@ -38,7 +39,12 @@ public override PropertyDescriptor GetOwnProperty(String propertyName)
3839
var numericIndex = default(Int32);
3940

4041
if (_numericIndexer != null && Int32.TryParse(propertyName, out numericIndex))
41-
return new PropertyDescriptor(_numericIndexer.GetMethod.Invoke(_value, new Object[] { numericIndex }).ToJsValue(_engine), false, false, false);
42+
{
43+
var args = new Object[] { numericIndex };
44+
var orig = _numericIndexer.GetMethod.Invoke(_value, args);
45+
var prop = orig != null ? orig.ToJsValue(_engine) : JsValue.Undefined;
46+
return new PropertyDescriptor(prop, false, false, false);
47+
}
4248

4349
// Else a string property
4450
// If we have a string indexer and no property exists for this name then use the string indexer
@@ -47,7 +53,11 @@ public override PropertyDescriptor GetOwnProperty(String propertyName)
4753
// node.attributes is one such object - has both a string and numeric indexer
4854
// This GetOwnProperty override might need an additional parameter to let us know this was called via an indexer
4955
if (_stringIndexer != null && !Properties.ContainsKey(propertyName))
50-
return new PropertyDescriptor(_stringIndexer.GetMethod.Invoke(_value, new Object[] { propertyName }).ToJsValue(_engine), false, false, false);
56+
{
57+
var args = new Object[] { propertyName };
58+
var prop = _stringIndexer.GetMethod.Invoke(_value, args).ToJsValue(_engine);
59+
return new PropertyDescriptor(prop, false, false, false);
60+
}
5161

5262
// Else try to return a registered property
5363
return base.GetOwnProperty(propertyName);
@@ -104,9 +114,13 @@ void SetProperties(IEnumerable<PropertyInfo> properties)
104114
if (indexParameters.Length == 1)
105115
{
106116
if (indexParameters[0].ParameterType == typeof(Int32))
117+
{
107118
_numericIndexer = property;
119+
}
108120
else if (indexParameters[0].ParameterType == typeof(String))
121+
{
109122
_stringIndexer = property;
123+
}
110124
}
111125
}
112126

@@ -133,10 +147,11 @@ void SetMethods(IEnumerable<MethodInfo> methods)
133147
// If it already has a property with the given name (usually another method),
134148
// then convert that method to a two-layer method, which decides which one
135149
// to pick depending on the number (and probably types) of arguments.
136-
if (Properties.ContainsKey(name))
137-
continue;
138-
139-
FastAddProperty(name, new DomFunctionInstance(_engine, method), false, false, false);
150+
if (!Properties.ContainsKey(name))
151+
{
152+
var func = new DomFunctionInstance(_engine, method);
153+
FastAddProperty(name, func, false, false, false);
154+
}
140155
}
141156
}
142157
}

AngleSharp.Scripting.JavaScript/EngineInstance.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ public EngineInstance(IWindow window, IDictionary<String, Object> assignments)
2222
_engine.SetValue("console", new ConsoleInstance(_engine));
2323

2424
foreach (var assignment in assignments)
25+
{
2526
_engine.SetValue(assignment.Key, assignment.Value);
27+
}
2628

2729
_window = GetDomNode(window);
2830
_lexicals = LexicalEnvironment.NewObjectEnvironment(_engine, _window, _engine.ExecutionContext.LexicalEnvironment, true);
@@ -64,8 +66,10 @@ public DomNodeInstance GetDomNode(Object obj)
6466
{
6567
var domNodeInstance = default(DomNodeInstance);
6668

67-
if (_objects.TryGetValue(obj, out domNodeInstance) == false)
69+
if (!_objects.TryGetValue(obj, out domNodeInstance))
70+
{
6871
_objects.Add(obj, domNodeInstance = new DomNodeInstance(this, obj));
72+
}
6973

7074
return domNodeInstance;
7175
}

AngleSharp.Scripting.JavaScript/Extensions.cs

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,41 @@ static class Extensions
1818
{
1919
public static JsValue ToJsValue(this Object obj, EngineInstance engine)
2020
{
21-
if (obj == null)
22-
return JsValue.Null;
23-
24-
if (obj is String)
25-
return new JsValue((String)obj);
26-
else if (obj is Int32)
27-
return new JsValue((Int32)obj);
28-
else if (obj is UInt32)
29-
return new JsValue((UInt32)obj);
30-
else if (obj is Double)
31-
return new JsValue((Double)obj);
32-
else if (obj is Single)
33-
return new JsValue((Single)obj);
34-
else if (obj is Boolean)
35-
return new JsValue((Boolean)obj);
36-
else if (obj is Enum)
37-
return new JsValue(Convert.ToInt32(obj));
38-
39-
return engine.GetDomNode(obj);
21+
if (obj != null)
22+
{
23+
if (obj is String)
24+
{
25+
return new JsValue((String)obj);
26+
}
27+
else if (obj is Int32)
28+
{
29+
return new JsValue((Int32)obj);
30+
}
31+
else if (obj is UInt32)
32+
{
33+
return new JsValue((UInt32)obj);
34+
}
35+
else if (obj is Double)
36+
{
37+
return new JsValue((Double)obj);
38+
}
39+
else if (obj is Single)
40+
{
41+
return new JsValue((Single)obj);
42+
}
43+
else if (obj is Boolean)
44+
{
45+
return new JsValue((Boolean)obj);
46+
}
47+
else if (obj is Enum)
48+
{
49+
return new JsValue(Convert.ToInt32(obj));
50+
}
51+
52+
return engine.GetDomNode(obj);
53+
}
54+
55+
return JsValue.Null;
4056
}
4157

4258
public static ClrFunctionInstance AsValue(this Engine engine, Func<JsValue, JsValue[], JsValue> func)
@@ -74,7 +90,9 @@ public static Object FromJsValue(this JsValue val)
7490
var node = obj as DomNodeInstance;
7591

7692
if (node != null)
93+
{
7794
return node.Value;
95+
}
7896

7997
return obj;
8098
case Types.Undefined:
@@ -88,25 +106,35 @@ public static Object FromJsValue(this JsValue val)
88106

89107
public static Object As(this Object value, Type targetType, EngineInstance engine)
90108
{
91-
if (value == null)
92-
return value;
109+
if (value != null)
110+
{
111+
var sourceType = value.GetType();
93112

94-
var sourceType = value.GetType();
113+
if (sourceType == targetType || sourceType.IsSubclassOf(targetType) || targetType.IsInstanceOfType(value) || targetType.IsAssignableFrom(sourceType))
114+
{
115+
return value;
116+
}
117+
else if (sourceType == typeof(Double) && targetType == typeof(Int32))
118+
{
119+
return (Int32)(Double)value;
120+
}
95121

96-
if (sourceType == targetType || sourceType.IsSubclassOf(targetType) || targetType.IsInstanceOfType(value) || targetType.IsAssignableFrom(sourceType))
97-
return value;
98-
else if (sourceType == typeof(Double) && targetType == typeof(Int32))
99-
return (Int32)(Double)value;
122+
if (targetType.IsSubclassOf(typeof(Delegate)) && value is FunctionInstance)
123+
{
124+
return targetType.ToDelegate((FunctionInstance)value, engine);
125+
}
100126

101-
if (targetType.IsSubclassOf(typeof(Delegate)) && value is FunctionInstance)
102-
return targetType.ToDelegate((FunctionInstance)value, engine);
127+
var method = sourceType.PrepareConvert(targetType);
103128

104-
var method = sourceType.PrepareConvert(targetType);
129+
if (method == null)
130+
{
131+
throw new JavaScriptException("[Internal] Could not find corresponding cast target.");
132+
}
105133

106-
if (method != null)
107134
return method.Invoke(value, null);
135+
}
108136

109-
throw new JavaScriptException("[Internal] Could not find corresponding cast target.");
137+
return value;
110138
}
111139

112140
public static Object GetDefaultValue(this Type type)
@@ -136,36 +164,50 @@ public static Object[] BuildArgs(this EngineInstance context, MethodBase method,
136164
var offset = 0;
137165

138166
if (parameters.Length > 0 && parameters[0].ParameterType == typeof(IWindow))
167+
{
139168
args[offset++] = context.Window.Value;
169+
}
140170

141171
if (max > 0 && parameters[max - 1].GetCustomAttribute<ParamArrayAttribute>() != null)
172+
{
142173
max--;
174+
}
143175

144176
var n = Math.Min(arguments.Length, max - offset);
145177

146-
for (int i = 0; i < n; i++)
178+
for (var i = 0; i < n; i++)
147179
{
148180
if (parameters[i].IsOptional && arguments[i].IsUndefined())
181+
{
149182
args[i + offset] = parameters[i].DefaultValue;
183+
}
150184
else
185+
{
151186
args[i + offset] = arguments[i].FromJsValue().As(parameters[i].ParameterType, context);
187+
}
152188
}
153189

154-
for (int i = n + offset; i < max; i++)
190+
for (var i = n + offset; i < max; i++)
155191
{
156192
if (parameters[i].IsOptional)
193+
{
157194
args[i] = parameters[i].DefaultValue;
195+
}
158196
else
197+
{
159198
args[i] = parameters[i].ParameterType.GetDefaultValue();
199+
}
160200
}
161201

162202

163203
if (max != parameters.Length)
164204
{
165205
var array = Array.CreateInstance(parameters[max].ParameterType.GetElementType(), Math.Max(0, arguments.Length - max));
166206

167-
for (int i = max; i < arguments.Length; i++)
207+
for (var i = max; i < arguments.Length; i++)
208+
{
168209
array.SetValue(arguments[i].FromJsValue(), i - max);
210+
}
169211

170212
args[max] = array;
171213
}
@@ -181,13 +223,17 @@ public static String[] GetParameterNames(this MethodInfo method)
181223
public static void AddConstructors(this EngineInstance engine, ObjectInstance obj, Type type)
182224
{
183225
foreach (var exportedType in type.Assembly.ExportedTypes)
226+
{
184227
engine.AddConstructor(obj, exportedType);
228+
}
185229
}
186230

187231
public static void AddInstances(this EngineInstance engine, ObjectInstance obj, Type type)
188232
{
189233
foreach (var exportedType in type.Assembly.ExportedTypes)
234+
{
190235
engine.AddInstance(obj, exportedType);
236+
}
191237
}
192238

193239
public static void AddConstructor(this EngineInstance engine, ObjectInstance obj, Type type)

0 commit comments

Comments
 (0)