Skip to content

Commit 885306b

Browse files
committed
Enable dynamic constructor generation #5
1 parent d3ddcca commit 885306b

File tree

4 files changed

+106
-54
lines changed

4 files changed

+106
-54
lines changed

AngleSharp.Scripting.JavaScript/AngleSharp.Scripting.JavaScript.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<ItemGroup>
5353
<Compile Include="ConfigurationExtensions.cs" />
5454
<Compile Include="ConsoleInstance.cs" />
55+
<Compile Include="DomConstructorInstance.cs" />
5556
<Compile Include="DomConstructors.cs" />
5657
<Compile Include="Dom\RequesterState.cs" />
5758
<Compile Include="Dom\XmlHttpRequest.cs" />
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace AngleSharp.Scripting.JavaScript
2+
{
3+
using Jint.Native;
4+
using Jint.Native.Function;
5+
using Jint.Native.Object;
6+
using Jint.Runtime;
7+
using System.Reflection;
8+
9+
sealed class DomConstructorInstance : FunctionInstance, IConstructor
10+
{
11+
readonly ConstructorInfo _constructor;
12+
readonly EngineInstance _engine;
13+
14+
public DomConstructorInstance(EngineInstance engine, ConstructorInfo constructor)
15+
: base(engine.Jint, null, null, false)
16+
{
17+
_engine = engine;
18+
_constructor = constructor;
19+
}
20+
21+
public override JsValue Call(JsValue thisObject, JsValue[] arguments)
22+
{
23+
throw new JavaScriptException("Only call the constructor with the new keyword.");
24+
}
25+
26+
public ObjectInstance Construct(JsValue[] arguments)
27+
{
28+
try
29+
{
30+
var parameters = _engine.BuildArgs(_constructor, arguments);
31+
var obj = _constructor.Invoke(parameters);
32+
return _engine.GetDomNode(obj);
33+
}
34+
catch
35+
{
36+
throw new JavaScriptException(_engine.Jint.Error);
37+
}
38+
39+
}
40+
}
41+
}
Lines changed: 6 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
namespace AngleSharp.Scripting.JavaScript
22
{
3-
using AngleSharp.Attributes;
43
using Jint.Native;
54
using Jint.Native.Function;
65
using Jint.Runtime;
76
using Jint.Runtime.Interop;
8-
using System;
9-
using System.Linq;
107
using System.Reflection;
118

129
sealed class DomFunctionInstance : FunctionInstance
@@ -15,7 +12,7 @@ sealed class DomFunctionInstance : FunctionInstance
1512
readonly DomNodeInstance _host;
1613

1714
public DomFunctionInstance(DomNodeInstance host, MethodInfo method)
18-
: base(host.Engine, GetParameters(method), null, false)
15+
: base(host.Engine, method.GetParameterNames(), null, false)
1916
{
2017
_host = host;
2118
_method = method;
@@ -33,7 +30,9 @@ public override JsValue Call(JsValue thisObject, JsValue[] arguments)
3330
{
3431
try
3532
{
36-
return _method.Invoke(node.Value, BuildArgs(arguments)).ToJsValue(_host.Context);
33+
var engine = _host.Context;
34+
var parameters = engine.BuildArgs(_method, arguments);
35+
return _method.Invoke(node.Value, parameters).ToJsValue(engine);
3736
}
3837
catch
3938
{
@@ -45,62 +44,15 @@ public override JsValue Call(JsValue thisObject, JsValue[] arguments)
4544
return JsValue.Undefined;
4645
}
4746

48-
Object[] BuildArgs(JsValue[] arguments)
49-
{
50-
var parameters = _method.GetParameters();
51-
var max = parameters.Length;
52-
var args = new Object[max];
53-
54-
if (max > 0 && parameters[max - 1].GetCustomAttribute<ParamArrayAttribute>() != null)
55-
max--;
56-
57-
var n = Math.Min(arguments.Length, max);
58-
59-
for (int i = 0; i < n; i++)
60-
args[i] = arguments[i].FromJsValue().As(parameters[i].ParameterType, _host.Context);
61-
62-
for (int i = n; i < max; i++)
63-
args[i] = parameters[i].IsOptional ? parameters[i].DefaultValue : parameters[i].ParameterType.GetDefaultValue();
64-
65-
if (max != parameters.Length)
66-
{
67-
var array = Array.CreateInstance(parameters[max].ParameterType.GetElementType(), Math.Max(0, arguments.Length - max));
68-
69-
for (int i = max; i < arguments.Length; i++)
70-
array.SetValue(arguments[i].FromJsValue(), i - max);
71-
72-
args[max] = array;
73-
}
74-
75-
return args;
76-
}
77-
78-
static String[] GetParameters(MethodInfo method)
79-
{
80-
if (method == null)
81-
return new String[0];
82-
83-
return method.GetParameters().Select(m => m.Name).ToArray();
84-
}
85-
8647
private JsValue ToString(JsValue thisObj, JsValue[] arguments)
8748
{
8849
var func = thisObj.TryCast<FunctionInstance>();
8950

9051
if (func == null)
91-
{
9252
throw new JavaScriptException(Engine.TypeError, "Function object expected.");
93-
}
94-
95-
var names = _method.GetCustomAttributes<DomNameAttribute>();
96-
97-
var officialName = _method.Name;
98-
var officalNameAttribute = names.FirstOrDefault();
99-
100-
if (officalNameAttribute != null)
101-
officialName = officalNameAttribute.OfficialName;
10253

103-
return string.Format("function {0}() {{ [native code] }}", officialName);
54+
var officialName = _method.GetOfficialName();
55+
return string.Format("function {0} () {{ [native code] }}", officialName);
10456
}
10557
}
10658
}

AngleSharp.Scripting.JavaScript/Extensions.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
namespace AngleSharp.Scripting.JavaScript
22
{
3+
using AngleSharp.Attributes;
34
using Jint;
45
using Jint.Native;
56
using Jint.Native.Function;
7+
using Jint.Native.Object;
68
using Jint.Runtime;
79
using Jint.Runtime.Descriptors;
810
using Jint.Runtime.Interop;
911
using System;
12+
using System.Linq;
1013
using System.Linq.Expressions;
1114
using System.Reflection;
1215

@@ -122,5 +125,60 @@ public static MethodInfo PrepareConvert(this Type fromType, Type toType)
122125
return null;
123126
}
124127
}
128+
129+
public static Object[] BuildArgs(this EngineInstance context, MethodBase method, JsValue[] arguments)
130+
{
131+
var parameters = method.GetParameters();
132+
var max = parameters.Length;
133+
var args = new Object[max];
134+
135+
if (max > 0 && parameters[max - 1].GetCustomAttribute<ParamArrayAttribute>() != null)
136+
max--;
137+
138+
var n = Math.Min(arguments.Length, max);
139+
140+
for (int i = 0; i < n; i++)
141+
args[i] = arguments[i].FromJsValue().As(parameters[i].ParameterType, context);
142+
143+
for (int i = n; i < max; i++)
144+
args[i] = parameters[i].IsOptional ? parameters[i].DefaultValue : parameters[i].ParameterType.GetDefaultValue();
145+
146+
if (max != parameters.Length)
147+
{
148+
var array = Array.CreateInstance(parameters[max].ParameterType.GetElementType(), Math.Max(0, arguments.Length - max));
149+
150+
for (int i = max; i < arguments.Length; i++)
151+
array.SetValue(arguments[i].FromJsValue(), i - max);
152+
153+
args[max] = array;
154+
}
155+
156+
return args;
157+
}
158+
159+
public static String[] GetParameterNames(this MethodInfo method)
160+
{
161+
return method != null ? method.GetParameters().Select(m => m.Name).ToArray() : null;
162+
}
163+
164+
public static void AddConstructor(this EngineInstance engine, ObjectInstance obj, Type type)
165+
{
166+
var info = type.GetConstructors().FirstOrDefault(m =>
167+
m.GetCustomAttributes<DomConstructorAttribute>().Any());
168+
169+
if (info != null)
170+
{
171+
var name = type.GetOfficialName();
172+
var constructor = new DomConstructorInstance(engine, info);
173+
obj.FastSetProperty(name, new PropertyDescriptor(constructor, false, true, false));
174+
}
175+
}
176+
177+
public static String GetOfficialName(this MemberInfo member)
178+
{
179+
var names = member.GetCustomAttributes<DomNameAttribute>();
180+
var officalNameAttribute = names.FirstOrDefault();
181+
return officalNameAttribute != null ? officalNameAttribute.OfficialName : member.Name;
182+
}
125183
}
126184
}

0 commit comments

Comments
 (0)