Skip to content

Commit 1b36108

Browse files
authored
Merge pull request #31 from koculu/30-bug-the-types-of-sub-namespaces-are-not-accessible
Fix sub namespace resolution bug.
2 parents e81d324 + fa8990c commit 1b36108

File tree

10 files changed

+204
-17
lines changed

10 files changed

+204
-17
lines changed

src/Topaz.Benchmark/Topaz.Benchmark.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFrameworks>net7.0</TargetFrameworks>
5+
<TargetFrameworks>net8.0</TargetFrameworks>
66
<RootNamespace>Tenray.Topaz</RootNamespace>
7+
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
8+
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
9+
<EnableNETAnalyzers>False</EnableNETAnalyzers>
710
</PropertyGroup>
811

912
<ItemGroup>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using NUnit.Framework;
2+
using System.Collections;
3+
using System.Text.RegularExpressions;
4+
using Tenray.Topaz.API;
5+
6+
namespace Tenray.Topaz.Test;
7+
8+
public sealed class AddNamespaceTests
9+
{
10+
[Test]
11+
public void TestAddNamespace1()
12+
{
13+
var engine = new TopazEngine();
14+
engine.AddNamespace("System", null, true);
15+
dynamic model = new JsObject();
16+
engine.SetValue("model", model);
17+
engine.ExecuteScript(@"
18+
let sb = new System.Text.StringBuilder()
19+
sb.Append('1234')
20+
model.data = sb.ToString()
21+
");
22+
Assert.That(model.data, Is.EqualTo("1234"));
23+
}
24+
25+
[Test]
26+
public void TestAddNamespace2()
27+
{
28+
var engine = new TopazEngine();
29+
engine.AddNamespace("System.Text", null, true);
30+
dynamic model = new JsObject();
31+
engine.SetValue("model", model);
32+
33+
engine.ExecuteScript(@"
34+
let sb = new System.Text.StringBuilder()
35+
sb.Append('1234')
36+
model.data = sb.ToString()
37+
model.reg = new System.Text.RegularExpressions.Regex('\w');
38+
");
39+
Assert.That(model.data, Is.EqualTo("1234"));
40+
Assert.That(model.reg, Is.InstanceOf<Regex>());
41+
}
42+
43+
[Test]
44+
public void TestAddNamespace3()
45+
{
46+
var engine = new TopazEngine();
47+
engine.AddNamespace("System.Text", null, false);
48+
dynamic model = new JsObject();
49+
engine.SetValue("model", model);
50+
51+
engine.ExecuteScript(@"
52+
let sb = new System.Text.StringBuilder()
53+
sb.Append('1234')
54+
model.data = sb.ToString()
55+
model.reg = System.Text.RegularExpressions.Regex;
56+
");
57+
Assert.That(model.data, Is.EqualTo("1234"));
58+
Assert.That(model.reg, Is.Null);
59+
}
60+
61+
[Test]
62+
public void TestAddNamespace4()
63+
{
64+
var engine = new TopazEngine();
65+
engine.AddNamespace("System.Text", null, true);
66+
dynamic model = new JsObject();
67+
engine.SetValue("model", model);
68+
69+
engine.ExecuteScript(@"
70+
let sb = new System.Text.StringBuilder()
71+
sb.Append('1234')
72+
model.data = sb.ToString()
73+
model.appDomain = System.AppDomain;
74+
");
75+
Assert.That(model.data, Is.EqualTo("1234"));
76+
Assert.That(model.appDomain, Is.Null);
77+
}
78+
}

src/Topaz.Test/AwaitTests.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System;
33
using System.Collections;
44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.Linq;
67
using System.Reflection;
78
using System.Threading;
@@ -187,6 +188,51 @@ public void CustomAwaitHandler2()
187188
");
188189
Assert.That(model.result2, Is.EqualTo(33));
189190
}
191+
192+
public async Task<int> DelayedRunner(Func<TaskData, Task<int>> action, int milliseconds)
193+
{
194+
await Task.Delay(milliseconds);
195+
196+
var taskData = new TaskData();
197+
var ret = await action(taskData);
198+
return ret;
199+
}
200+
201+
[Test]
202+
public void AwaitChain1()
203+
{
204+
var engine = new TopazEngine(new TopazEngineSetup { AwaitExpressionHandler = new CustomAwaitExpressionHandler() });
205+
dynamic model = new JsObject();
206+
engine.SetValue("test", this);
207+
engine.SetValue("model", model);
208+
engine.ExecuteScriptAsync(@"
209+
var handler = async function(taskdata)
210+
{
211+
return taskdata.TestMethod();
212+
}
213+
var t = await test.DelayedRunner(handler, 3);
214+
model.result1 = t;
215+
").Wait();
216+
Assert.That(model.result1, Is.EqualTo(33));
217+
engine.ExecuteScript(@"
218+
var handler = async function(taskdata)
219+
{
220+
return taskdata.TestMethod();
221+
}
222+
var t = await test.DelayedRunner(handler, 3);
223+
model.result2 = await t;
224+
");
225+
Assert.That(model.result2, Is.EqualTo(33));
226+
}
227+
}
228+
229+
public class TaskData
230+
{
231+
public async Task<int> TestMethod()
232+
{
233+
await Task.Delay(11);
234+
return 33;
235+
}
190236
}
191237

192238
class CustomAwaitExpressionHandler : IAwaitExpressionHandler

src/Topaz.Test/SwitchCaseTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@ function f2(key) {
5454
foreach (var item in (IEnumerable)(model.a))
5555
Assert.IsTrue((bool)item);
5656
}
57-
}
57+
}

src/Topaz.Test/Topaz.Test.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net7.0</TargetFrameworks>
4+
<TargetFrameworks>net8.0</TargetFrameworks>
55

66
<IsPackable>false</IsPackable>
77
<RootNamespace>Tenray.Topaz</RootNamespace>
8+
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
9+
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
10+
<EnableNETAnalyzers>False</EnableNETAnalyzers>
811
</PropertyGroup>
912

1013
<ItemGroup>

src/Topaz/Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
<Authors>Ahmed Yasin Koculu</Authors>
66
<PackageId>Topaz</PackageId>
77
<Title>Topaz</Title>
8-
<ProductVersion>1.3.9.0</ProductVersion>
9-
<Version>1.3.9.0</Version>
8+
<ProductVersion>1.4.0.0</ProductVersion>
9+
<Version>1.4.0.0</Version>
1010
<Authors>Ahmed Yasin Koculu</Authors>
1111
<AssemblyTitle>Topaz</AssemblyTitle>
1212
<Description>Multithreaded Javascript Engine for .NET</Description>

src/Topaz/ITopazEngine.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,9 @@ public interface ITopazEngine
155155
/// </summary>
156156
/// <param name="namespace">The full name of the namespace.</param>
157157
/// <param name="whitelist">The whitelist contains the types that are allowed.</param>
158-
/// <param name="allowSubNamespaces">If true, subnamespaces will be accessible.</param>
159-
/// <param name="name">The name that will be used in script to access given namespace.
158+
/// <param name="allowSubNamespaces">If true, subnamespaces will be accessible.</param>
160159
/// If this is not provided the script name will be equal to the namespace.</param>
161-
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist, bool allowSubNamespaces = false, string name = null);
160+
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist, bool allowSubNamespaces = false);
162161

163162
/// <summary>
164163
/// Gets the value of the variable that is defined in the global scope.

src/Topaz/Interop/Impl/NamespaceProxy.cs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Tenray.Topaz.Interop;
99
/// </summary>
1010
public sealed class NamespaceProxy : ITypeProxy
1111
{
12+
private Dictionary<string, NamespaceProxy> SubnamespaceProxies = new();
13+
1214
/// <summary>
1315
/// Namespace name.
1416
/// eg: System.Collections
@@ -51,6 +53,11 @@ public sealed class NamespaceProxy : ITypeProxy
5153
/// </summary>
5254
public Type ProxiedType { get; }
5355

56+
/// <summary>
57+
/// If true, types of the current namespace are accessible.
58+
/// </summary>
59+
public bool EnableTypeRetrieval { get; set; }
60+
5461
public NamespaceProxy(
5562
string name,
5663
IReadOnlySet<string> whitelist,
@@ -87,26 +94,34 @@ public bool TryGetStaticMember(
8794
return false;
8895
}
8996

97+
if (SubnamespaceProxies.TryGetValue(memberName, out var subProxy))
98+
{
99+
value = subProxy;
100+
return true;
101+
}
102+
90103
var fullname = Name + "." + memberName;
91104
var type = FindType(fullname);
92105
if (type == null)
93106
{
94107
if (AllowSubNamespaces)
95108
{
96-
value = new NamespaceProxy(fullname,
109+
value = subProxy = new NamespaceProxy(fullname,
97110
Whitelist,
98111
true,
99112
ValueConverter,
100113
MemberInfoProvider,
101114
MaxGenericTypeArgumentCount,
102115
ProxyOptions);
116+
subProxy.EnableTypeRetrieval = true;
117+
SubnamespaceProxies.Add(memberName, subProxy);
103118
return true;
104119
}
105120
value = null;
106121
return false;
107122
}
108123

109-
if (!type.IsPublic)
124+
if (!EnableTypeRetrieval || !type.IsPublic)
110125
{
111126
value = null;
112127
return false;
@@ -124,15 +139,28 @@ public bool TryGetStaticMember(
124139
return true;
125140
}
126141

142+
static Type SearchType(string typeName)
143+
{
144+
var type = Type.GetType(typeName, false, false);
145+
if (type != null) return type;
146+
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
147+
{
148+
type = a.GetType(typeName, false, false);
149+
if (type != null)
150+
return type;
151+
}
152+
return null;
153+
}
154+
127155
private Type FindType(string fullname)
128156
{
129-
var type = Type.GetType(fullname, false, false);
157+
var type = SearchType(fullname);
130158
if (type != null)
131159
return type;
132160
// search for generic types.
133161
for (var i = 1; i < MaxGenericTypeArgumentCount; ++i)
134162
{
135-
type = Type.GetType(fullname + "`" + i, false, false);
163+
type = SearchType(fullname + "`" + i);
136164
if (type != null)
137165
return type;
138166
}
@@ -152,4 +180,23 @@ public override string ToString()
152180
{
153181
return Name;
154182
}
183+
184+
public void AddSubNameSpaces(Span<string> parts,
185+
IReadOnlySet<string> whitelist,
186+
bool allowSubNamespaces)
187+
{
188+
if (parts.Length == 0)
189+
{
190+
EnableTypeRetrieval = true;
191+
return;
192+
}
193+
if (SubnamespaceProxies.TryGetValue(parts[0], out var proxy))
194+
{
195+
proxy.AddSubNameSpaces(parts.Slice(1), whitelist, allowSubNamespaces);
196+
return;
197+
}
198+
var subProxy = new NamespaceProxy(Name + "." + parts[0], whitelist, allowSubNamespaces && parts.Length == 1, ValueConverter, MemberInfoProvider, MaxGenericTypeArgumentCount);
199+
subProxy.AddSubNameSpaces(parts.Slice(1), whitelist, allowSubNamespaces);
200+
SubnamespaceProxies.Add(parts[0], subProxy);
201+
}
155202
}

src/Topaz/Topaz.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Deterministic>true</Deterministic>
55
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
66
<NeutralLanguage>en-US</NeutralLanguage>
7-
<TargetFrameworks>net7.0;net6.0</TargetFrameworks>
7+
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
88
<RepositoryUrl>https://github.com/koculu/topaz</RepositoryUrl>
99
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1010
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -16,8 +16,10 @@
1616
<EmbedUntrackedSources>true</EmbedUntrackedSources>
1717
<RootNamespace>Tenray.Topaz</RootNamespace>
1818
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
19-
<RunAnalyzersDuringLiveAnalysis>True</RunAnalyzersDuringLiveAnalysis>
19+
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
2020
<AnalysisMode>All</AnalysisMode>
21+
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
22+
<EnableNETAnalyzers>False</EnableNETAnalyzers>
2123
</PropertyGroup>
2224

2325
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">

src/Topaz/TopazEngine.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,21 @@ public void AddExtensionMethods(Type type)
101101
extensionMethodRegistry.AddType(type);
102102
}
103103

104-
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist = null, bool allowSubNamespaces = false, string name = null)
104+
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist = null, bool allowSubNamespaces = false)
105105
{
106+
var parts = @namespace.Split('.');
107+
var rootNamespace = parts[0];
108+
if (GlobalScope.GetValue(rootNamespace) is NamespaceProxy existingProxy)
109+
{
110+
existingProxy.AddSubNameSpaces(parts.AsSpan(1), whitelist, allowSubNamespaces);
111+
return;
112+
}
113+
var proxy = new NamespaceProxy(rootNamespace, whitelist, allowSubNamespaces && parts.Length == 1, ValueConverter, MemberInfoProvider);
106114
GlobalScope.SetValueAndKind(
107-
name ?? @namespace,
108-
new NamespaceProxy(@namespace, whitelist, allowSubNamespaces, ValueConverter, MemberInfoProvider),
115+
rootNamespace,
116+
proxy,
109117
VariableKind.Const);
118+
proxy.AddSubNameSpaces(parts.AsSpan(1), whitelist, allowSubNamespaces);
110119
}
111120

112121
public object GetValue(string name)

0 commit comments

Comments
 (0)