Skip to content

Commit 39f3f59

Browse files
committed
Introduced prototype w. p-caching
1 parent 92b8a45 commit 39f3f59

File tree

9 files changed

+348
-239
lines changed

9 files changed

+348
-239
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<Reference Include="System.Xml" />
5151
</ItemGroup>
5252
<ItemGroup>
53+
<Compile Include="DomPrototypeInstance.cs" />
5354
<Compile Include="JsApiExtensions.cs" />
5455
<Compile Include="Attributes\DomInstanceAttribute.cs" />
5556
<Compile Include="JsConfigurationExtensions.cs" />
@@ -74,6 +75,8 @@
7475
<Compile Include="Extensions.cs" />
7576
<Compile Include="JavaScriptEngine.cs" />
7677
<Compile Include="Properties\AssemblyInfo.cs" />
78+
<Compile Include="PrototypeCache.cs" />
79+
<Compile Include="ReferenceCache.cs" />
7780
<Compile Include="Services\IConsoleLogger.cs" />
7881
<Compile Include="Services\JavaScriptProvider.cs" />
7982
<Compile Include="SystemTypeConverter.cs" />

src/AngleSharp.Scripting.JavaScript/DomConstructors.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
partial class DomConstructors
88
{
9-
readonly EngineInstance _engine;
9+
private readonly EngineInstance _engine;
1010

1111
public DomConstructors(EngineInstance engine)
1212
{

src/AngleSharp.Scripting.JavaScript/DomEventInstance.cs

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,56 @@
88

99
sealed class DomEventInstance
1010
{
11-
DomEventHandler _handler;
12-
FunctionInstance _function;
11+
private readonly EngineInstance _engine;
12+
private readonly EventInfo _eventInfo;
13+
private DomEventHandler _handler;
14+
private FunctionInstance _function;
1315

14-
public DomEventInstance(DomNodeInstance node, EventInfo eventInfo = null)
16+
public DomEventInstance(EngineInstance engine, EventInfo eventInfo = null)
1517
{
16-
Getter = new ClrFunctionInstance(node.Engine, (thisObject, arguments) => _function ?? JsValue.Null);
17-
Setter = new ClrFunctionInstance(node.Engine, (thisObject, arguments) =>
18+
_engine = engine;
19+
_eventInfo = eventInfo;
20+
Getter = new ClrFunctionInstance(engine.Jint, GetEventHandler);
21+
Setter = new ClrFunctionInstance(engine.Jint, SetEventHandler);
22+
}
23+
24+
public ClrFunctionInstance Getter { get; }
25+
26+
public ClrFunctionInstance Setter { get; }
27+
28+
private JsValue GetEventHandler(JsValue thisObject, JsValue[] arguments)
29+
{
30+
return _function ?? JsValue.Null;
31+
}
32+
33+
private JsValue SetEventHandler(JsValue thisObject, JsValue[] arguments)
34+
{
35+
var node = thisObject.As<DomNodeInstance>();
36+
37+
if (node != null)
1838
{
1939
if (_handler != null)
2040
{
21-
if (eventInfo != null)
22-
{
23-
eventInfo.RemoveEventHandler(node.Value, _handler);
24-
}
25-
41+
_eventInfo?.RemoveEventHandler(node.Value, _handler);
2642
_handler = null;
2743
_function = null;
2844
}
2945

3046
if (arguments[0].Is<FunctionInstance>())
3147
{
3248
_function = arguments[0].As<FunctionInstance>();
33-
_handler = (s, ev) =>
49+
_handler = (s, ev) =>
3450
{
35-
var sender = s.ToJsValue(node.Context);
36-
var args = ev.ToJsValue(node.Context);
37-
_function.Call(sender, new [] { args });
51+
var sender = s.ToJsValue(_engine);
52+
var args = ev.ToJsValue(_engine);
53+
_function.Call(sender, new[] { args });
3854
};
3955

40-
if (eventInfo != null)
41-
{
42-
eventInfo.AddEventHandler(node.Value, _handler);
43-
}
56+
_eventInfo?.AddEventHandler(node.Value, _handler);
4457
}
58+
}
4559

46-
return arguments[0];
47-
});
48-
}
49-
50-
public ClrFunctionInstance Getter
51-
{
52-
get;
53-
private set;
54-
}
55-
56-
public ClrFunctionInstance Setter
57-
{
58-
get;
59-
private set;
60+
return arguments[0];
6061
}
6162
}
6263
}

src/AngleSharp.Scripting.JavaScript/DomFunctionInstance.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
sealed class DomFunctionInstance : FunctionInstance
1010
{
11-
readonly MethodInfo _method;
12-
readonly EngineInstance _engine;
11+
private readonly MethodInfo _method;
12+
private readonly EngineInstance _engine;
1313

1414
public DomFunctionInstance(EngineInstance engine, MethodInfo method)
1515
: base(engine.Jint, method.GetParameterNames(), null, false)
Lines changed: 11 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,22 @@
11
namespace AngleSharp.Scripting.JavaScript
22
{
3-
using AngleSharp.Attributes;
4-
using Jint.Native;
53
using Jint.Native.Object;
64
using Jint.Runtime.Descriptors;
75
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
10-
using System.Reflection;
116

127
sealed class DomNodeInstance : ObjectInstance
138
{
14-
readonly Type _type;
15-
readonly EngineInstance _engine;
16-
readonly Object _value;
17-
18-
PropertyInfo _numericIndexer;
19-
PropertyInfo _stringIndexer;
9+
private readonly EngineInstance _engine;
10+
private readonly Object _value;
2011

2112
public DomNodeInstance(EngineInstance engine, Object value)
2213
: base(engine.Jint)
2314
{
24-
_type = value.GetType();
2515
_engine = engine;
2616
_value = value;
27-
28-
SetAllMembers();
29-
SetPseudoProperties();
30-
31-
// DOM objects can have properties added dynamically
17+
3218
Extensible = true;
33-
Prototype = engine.Jint.Object;
19+
Prototype = engine.GetDomPrototype(value.GetType());
3420
}
3521

3622
public Object Value
@@ -40,180 +26,21 @@ public Object Value
4026

4127
public override String ToString()
4228
{
43-
return String.Format("[object {0}]", _type.Name);
44-
}
45-
46-
public EngineInstance Context
47-
{
48-
get { return _engine; }
29+
return Prototype.ToString();
4930
}
5031

5132
public override PropertyDescriptor GetOwnProperty(String propertyName)
5233
{
53-
// If we have a numeric indexer and the property is numeric
54-
var numericIndex = default(Int32);
34+
var prototype = Prototype as DomPrototypeInstance;
35+
var descriptor = default(PropertyDescriptor);
36+
var result = prototype?.TryGetFromIndex(_value, propertyName, out descriptor) ?? false;
5537

56-
if (_numericIndexer != null && Int32.TryParse(propertyName, out numericIndex))
38+
if (!result)
5739
{
58-
var args = new Object[] { numericIndex };
59-
60-
try
61-
{
62-
var orig = _numericIndexer.GetMethod.Invoke(_value, args);
63-
var prop = orig.ToJsValue(_engine);
64-
return new PropertyDescriptor(prop, false, false, false);
65-
}
66-
catch (TargetInvocationException ex)
67-
{
68-
if (ex.InnerException is ArgumentOutOfRangeException)
69-
{
70-
var prop = JsValue.Undefined;
71-
return new PropertyDescriptor(prop, false, false, false);
72-
}
73-
74-
throw;
75-
}
76-
}
77-
78-
// Else a string property
79-
// If we have a string indexer and no property exists for this name then use the string indexer
80-
// Jint possibly has a limitation here - if an object has a string indexer. How do we know whether to use the defined indexer or a property?
81-
// Eg. object.callMethod1() vs object['callMethod1'] is not necessarily the same if the object has a string indexer?? (I'm not an ECMA expert!)
82-
// node.attributes is one such object - has both a string and numeric indexer
83-
// This GetOwnProperty override might need an additional parameter to let us know this was called via an indexer
84-
if (_stringIndexer != null && !Properties.ContainsKey(propertyName))
85-
{
86-
var args = new Object[] { propertyName };
87-
var prop = _stringIndexer.GetMethod.Invoke(_value, args).ToJsValue(_engine);
88-
return new PropertyDescriptor(prop, false, false, false);
40+
descriptor = base.GetOwnProperty(propertyName);
8941
}
9042

91-
// Else try to return a registered property
92-
return base.GetOwnProperty(propertyName);
93-
}
94-
95-
void SetAllMembers()
96-
{
97-
var type = _type;
98-
var types = new List<Type>(type.GetTypeInfo().ImplementedInterfaces);
99-
100-
do
101-
{
102-
types.Add(type);
103-
type = type.GetTypeInfo().BaseType;
104-
}
105-
while (type != null);
106-
107-
SetMembers(types);
108-
}
109-
110-
void SetMembers(IEnumerable<Type> types)
111-
{
112-
foreach (var type in types)
113-
{
114-
var typeInfo = type.GetTypeInfo();
115-
SetProperties(typeInfo.DeclaredProperties);
116-
SetMethods(typeInfo.DeclaredMethods);
117-
SetEvents(typeInfo.DeclaredEvents);
118-
}
119-
}
120-
121-
void SetEvents(IEnumerable<EventInfo> eventInfos)
122-
{
123-
foreach (var eventInfo in eventInfos)
124-
{
125-
var names = eventInfo.GetCustomAttributes<DomNameAttribute>();
126-
127-
foreach (var name in names.Select(m => m.OfficialName))
128-
{
129-
var eventInstance = new DomEventInstance(this, eventInfo);
130-
FastSetProperty(name, new PropertyDescriptor(eventInstance.Getter, eventInstance.Setter, false, false));
131-
}
132-
}
133-
}
134-
135-
void SetProperties(IEnumerable<PropertyInfo> properties)
136-
{
137-
foreach (var property in properties)
138-
{
139-
var index = property.GetCustomAttribute<DomAccessorAttribute>();
140-
141-
if (index != null)
142-
{
143-
var indexParameters = property.GetIndexParameters();
144-
145-
if (indexParameters.Length == 1)
146-
{
147-
if (indexParameters[0].ParameterType == typeof(Int32))
148-
{
149-
_numericIndexer = property;
150-
}
151-
else if (indexParameters[0].ParameterType == typeof(String))
152-
{
153-
_stringIndexer = property;
154-
}
155-
}
156-
}
157-
158-
var names = property.GetCustomAttributes<DomNameAttribute>();
159-
160-
foreach (var name in names.Select(m => m.OfficialName))
161-
{
162-
FastSetProperty(name, new PropertyDescriptor(
163-
new DomFunctionInstance(_engine, property.GetMethod),
164-
new DomFunctionInstance(_engine, property.SetMethod), false, false));
165-
}
166-
}
167-
}
168-
169-
void SetMethods(IEnumerable<MethodInfo> methods)
170-
{
171-
foreach (var method in methods)
172-
{
173-
var names = method.GetCustomAttributes<DomNameAttribute>();
174-
175-
foreach (var name in names.Select(m => m.OfficialName))
176-
{
177-
//TODO Jint
178-
// If it already has a property with the given name (usually another method),
179-
// then convert that method to a two-layer method, which decides which one
180-
// to pick depending on the number (and probably types) of arguments.
181-
if (!Properties.ContainsKey(name))
182-
{
183-
var func = new DomFunctionInstance(_engine, method);
184-
FastAddProperty(name, func, false, false, false);
185-
}
186-
}
187-
}
188-
}
189-
190-
void SetPseudoProperties()
191-
{
192-
if (_type.GetTypeInfo().ImplementedInterfaces.Contains(typeof(AngleSharp.Dom.IElement)))
193-
{
194-
var focusInEventInstance = new DomEventInstance(this);
195-
var focusOutEventInstance = new DomEventInstance(this);
196-
var unloadEventInstance = new DomEventInstance(this);
197-
var contextMenuEventInstance = new DomEventInstance(this);
198-
199-
FastSetProperty("scrollLeft", new PropertyDescriptor(new JsValue(0.0), false, false, false));
200-
FastSetProperty("scrollTop", new PropertyDescriptor(new JsValue(0.0), false, false, false));
201-
FastSetProperty("scrollWidth", new PropertyDescriptor(new JsValue(0.0), false, false, false));
202-
FastSetProperty("scrollHeight", new PropertyDescriptor(new JsValue(0.0), false, false, false));
203-
FastSetProperty("clientLeft", new PropertyDescriptor(new JsValue(0.0), false, false, false));
204-
FastSetProperty("clientTop", new PropertyDescriptor(new JsValue(0.0), false, false, false));
205-
FastSetProperty("clientWidth", new PropertyDescriptor(new JsValue(0.0), false, false, false));
206-
FastSetProperty("clientHeight", new PropertyDescriptor(new JsValue(0.0), false, false, false));
207-
FastSetProperty("offsetLeft", new PropertyDescriptor(new JsValue(0.0), false, false, false));
208-
FastSetProperty("offsetTop", new PropertyDescriptor(new JsValue(0.0), false, false, false));
209-
FastSetProperty("offsetWidth", new PropertyDescriptor(new JsValue(0.0), false, false, false));
210-
FastSetProperty("offsetHeight", new PropertyDescriptor(new JsValue(0.0), false, false, false));
211-
212-
FastSetProperty("focusin", new PropertyDescriptor(focusInEventInstance.Getter, focusInEventInstance.Setter, false, false));
213-
FastSetProperty("focusout", new PropertyDescriptor(focusOutEventInstance.Getter, focusOutEventInstance.Setter, false, false));
214-
FastSetProperty("unload", new PropertyDescriptor(unloadEventInstance.Getter, unloadEventInstance.Setter, false, false));
215-
FastSetProperty("contextmenu", new PropertyDescriptor(contextMenuEventInstance.Getter, contextMenuEventInstance.Setter, false, false));
216-
}
43+
return descriptor;
21744
}
21845
}
21946
}

0 commit comments

Comments
 (0)