Skip to content

Commit

Permalink
Merge pull request #31 from koculu/30-bug-the-types-of-sub-namespaces…
Browse files Browse the repository at this point in the history
…-are-not-accessible

Fix sub namespace resolution bug.
  • Loading branch information
koculu authored May 5, 2024
2 parents e81d324 + fa8990c commit 1b36108
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 17 deletions.
5 changes: 4 additions & 1 deletion src/Topaz.Benchmark/Topaz.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<RootNamespace>Tenray.Topaz</RootNamespace>
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
<EnableNETAnalyzers>False</EnableNETAnalyzers>
</PropertyGroup>

<ItemGroup>
Expand Down
78 changes: 78 additions & 0 deletions src/Topaz.Test/AddNamespaceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using NUnit.Framework;
using System.Collections;
using System.Text.RegularExpressions;
using Tenray.Topaz.API;

namespace Tenray.Topaz.Test;

public sealed class AddNamespaceTests
{
[Test]
public void TestAddNamespace1()
{
var engine = new TopazEngine();
engine.AddNamespace("System", null, true);
dynamic model = new JsObject();
engine.SetValue("model", model);
engine.ExecuteScript(@"
let sb = new System.Text.StringBuilder()
sb.Append('1234')
model.data = sb.ToString()
");
Assert.That(model.data, Is.EqualTo("1234"));
}

[Test]
public void TestAddNamespace2()
{
var engine = new TopazEngine();
engine.AddNamespace("System.Text", null, true);
dynamic model = new JsObject();
engine.SetValue("model", model);

engine.ExecuteScript(@"
let sb = new System.Text.StringBuilder()
sb.Append('1234')
model.data = sb.ToString()
model.reg = new System.Text.RegularExpressions.Regex('\w');
");
Assert.That(model.data, Is.EqualTo("1234"));
Assert.That(model.reg, Is.InstanceOf<Regex>());
}

[Test]
public void TestAddNamespace3()
{
var engine = new TopazEngine();
engine.AddNamespace("System.Text", null, false);
dynamic model = new JsObject();
engine.SetValue("model", model);

engine.ExecuteScript(@"
let sb = new System.Text.StringBuilder()
sb.Append('1234')
model.data = sb.ToString()
model.reg = System.Text.RegularExpressions.Regex;
");
Assert.That(model.data, Is.EqualTo("1234"));
Assert.That(model.reg, Is.Null);
}

[Test]
public void TestAddNamespace4()
{
var engine = new TopazEngine();
engine.AddNamespace("System.Text", null, true);
dynamic model = new JsObject();
engine.SetValue("model", model);

engine.ExecuteScript(@"
let sb = new System.Text.StringBuilder()
sb.Append('1234')
model.data = sb.ToString()
model.appDomain = System.AppDomain;
");
Assert.That(model.data, Is.EqualTo("1234"));
Assert.That(model.appDomain, Is.Null);
}
}
46 changes: 46 additions & 0 deletions src/Topaz.Test/AwaitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
Expand Down Expand Up @@ -187,6 +188,51 @@ public void CustomAwaitHandler2()
");
Assert.That(model.result2, Is.EqualTo(33));
}

public async Task<int> DelayedRunner(Func<TaskData, Task<int>> action, int milliseconds)
{
await Task.Delay(milliseconds);

var taskData = new TaskData();
var ret = await action(taskData);
return ret;
}

[Test]
public void AwaitChain1()
{
var engine = new TopazEngine(new TopazEngineSetup { AwaitExpressionHandler = new CustomAwaitExpressionHandler() });
dynamic model = new JsObject();
engine.SetValue("test", this);
engine.SetValue("model", model);
engine.ExecuteScriptAsync(@"
var handler = async function(taskdata)
{
return taskdata.TestMethod();
}
var t = await test.DelayedRunner(handler, 3);
model.result1 = t;
").Wait();
Assert.That(model.result1, Is.EqualTo(33));
engine.ExecuteScript(@"
var handler = async function(taskdata)
{
return taskdata.TestMethod();
}
var t = await test.DelayedRunner(handler, 3);
model.result2 = await t;
");
Assert.That(model.result2, Is.EqualTo(33));
}
}

public class TaskData
{
public async Task<int> TestMethod()
{
await Task.Delay(11);
return 33;
}
}

class CustomAwaitExpressionHandler : IAwaitExpressionHandler
Expand Down
2 changes: 1 addition & 1 deletion src/Topaz.Test/SwitchCaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ function f2(key) {
foreach (var item in (IEnumerable)(model.a))
Assert.IsTrue((bool)item);
}
}
}
5 changes: 4 additions & 1 deletion src/Topaz.Test/Topaz.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>

<IsPackable>false</IsPackable>
<RootNamespace>Tenray.Topaz</RootNamespace>
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
<EnableNETAnalyzers>False</EnableNETAnalyzers>
</PropertyGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/Topaz/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<Authors>Ahmed Yasin Koculu</Authors>
<PackageId>Topaz</PackageId>
<Title>Topaz</Title>
<ProductVersion>1.3.9.0</ProductVersion>
<Version>1.3.9.0</Version>
<ProductVersion>1.4.0.0</ProductVersion>
<Version>1.4.0.0</Version>
<Authors>Ahmed Yasin Koculu</Authors>
<AssemblyTitle>Topaz</AssemblyTitle>
<Description>Multithreaded Javascript Engine for .NET</Description>
Expand Down
5 changes: 2 additions & 3 deletions src/Topaz/ITopazEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,9 @@ public interface ITopazEngine
/// </summary>
/// <param name="namespace">The full name of the namespace.</param>
/// <param name="whitelist">The whitelist contains the types that are allowed.</param>
/// <param name="allowSubNamespaces">If true, subnamespaces will be accessible.</param>
/// <param name="name">The name that will be used in script to access given namespace.
/// <param name="allowSubNamespaces">If true, subnamespaces will be accessible.</param>
/// If this is not provided the script name will be equal to the namespace.</param>
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist, bool allowSubNamespaces = false, string name = null);
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist, bool allowSubNamespaces = false);

/// <summary>
/// Gets the value of the variable that is defined in the global scope.
Expand Down
55 changes: 51 additions & 4 deletions src/Topaz/Interop/Impl/NamespaceProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace Tenray.Topaz.Interop;
/// </summary>
public sealed class NamespaceProxy : ITypeProxy
{
private Dictionary<string, NamespaceProxy> SubnamespaceProxies = new();

/// <summary>
/// Namespace name.
/// eg: System.Collections
Expand Down Expand Up @@ -51,6 +53,11 @@ public sealed class NamespaceProxy : ITypeProxy
/// </summary>
public Type ProxiedType { get; }

/// <summary>
/// If true, types of the current namespace are accessible.
/// </summary>
public bool EnableTypeRetrieval { get; set; }

public NamespaceProxy(
string name,
IReadOnlySet<string> whitelist,
Expand Down Expand Up @@ -87,26 +94,34 @@ public bool TryGetStaticMember(
return false;
}

if (SubnamespaceProxies.TryGetValue(memberName, out var subProxy))
{
value = subProxy;
return true;
}

var fullname = Name + "." + memberName;
var type = FindType(fullname);
if (type == null)
{
if (AllowSubNamespaces)
{
value = new NamespaceProxy(fullname,
value = subProxy = new NamespaceProxy(fullname,
Whitelist,
true,
ValueConverter,
MemberInfoProvider,
MaxGenericTypeArgumentCount,
ProxyOptions);
subProxy.EnableTypeRetrieval = true;
SubnamespaceProxies.Add(memberName, subProxy);
return true;
}
value = null;
return false;
}

if (!type.IsPublic)
if (!EnableTypeRetrieval || !type.IsPublic)
{
value = null;
return false;
Expand All @@ -124,15 +139,28 @@ public bool TryGetStaticMember(
return true;
}

static Type SearchType(string typeName)
{
var type = Type.GetType(typeName, false, false);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName, false, false);
if (type != null)
return type;
}
return null;
}

private Type FindType(string fullname)
{
var type = Type.GetType(fullname, false, false);
var type = SearchType(fullname);
if (type != null)
return type;
// search for generic types.
for (var i = 1; i < MaxGenericTypeArgumentCount; ++i)
{
type = Type.GetType(fullname + "`" + i, false, false);
type = SearchType(fullname + "`" + i);
if (type != null)
return type;
}
Expand All @@ -152,4 +180,23 @@ public override string ToString()
{
return Name;
}

public void AddSubNameSpaces(Span<string> parts,
IReadOnlySet<string> whitelist,
bool allowSubNamespaces)
{
if (parts.Length == 0)
{
EnableTypeRetrieval = true;
return;
}
if (SubnamespaceProxies.TryGetValue(parts[0], out var proxy))
{
proxy.AddSubNameSpaces(parts.Slice(1), whitelist, allowSubNamespaces);
return;
}
var subProxy = new NamespaceProxy(Name + "." + parts[0], whitelist, allowSubNamespaces && parts.Length == 1, ValueConverter, MemberInfoProvider, MaxGenericTypeArgumentCount);
subProxy.AddSubNameSpaces(parts.Slice(1), whitelist, allowSubNamespaces);
SubnamespaceProxies.Add(parts[0], subProxy);
}
}
6 changes: 4 additions & 2 deletions src/Topaz/Topaz.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Deterministic>true</Deterministic>
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<NeutralLanguage>en-US</NeutralLanguage>
<TargetFrameworks>net7.0;net6.0</TargetFrameworks>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<RepositoryUrl>https://github.com/koculu/topaz</RepositoryUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand All @@ -16,8 +16,10 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<RootNamespace>Tenray.Topaz</RootNamespace>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<RunAnalyzersDuringLiveAnalysis>True</RunAnalyzersDuringLiveAnalysis>
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
<AnalysisMode>All</AnalysisMode>
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
<EnableNETAnalyzers>False</EnableNETAnalyzers>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
Expand Down
15 changes: 12 additions & 3 deletions src/Topaz/TopazEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,21 @@ public void AddExtensionMethods(Type type)
extensionMethodRegistry.AddType(type);
}

public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist = null, bool allowSubNamespaces = false, string name = null)
public void AddNamespace(string @namespace, IReadOnlySet<string> whitelist = null, bool allowSubNamespaces = false)
{
var parts = @namespace.Split('.');
var rootNamespace = parts[0];
if (GlobalScope.GetValue(rootNamespace) is NamespaceProxy existingProxy)
{
existingProxy.AddSubNameSpaces(parts.AsSpan(1), whitelist, allowSubNamespaces);
return;
}
var proxy = new NamespaceProxy(rootNamespace, whitelist, allowSubNamespaces && parts.Length == 1, ValueConverter, MemberInfoProvider);
GlobalScope.SetValueAndKind(
name ?? @namespace,
new NamespaceProxy(@namespace, whitelist, allowSubNamespaces, ValueConverter, MemberInfoProvider),
rootNamespace,
proxy,
VariableKind.Const);
proxy.AddSubNameSpaces(parts.AsSpan(1), whitelist, allowSubNamespaces);
}

public object GetValue(string name)
Expand Down

0 comments on commit 1b36108

Please sign in to comment.