├── src ├── Topaz │ ├── docs │ │ └── Topaz │ │ │ └── images │ │ │ └── logo1.png │ ├── Esprima │ │ ├── SourceType.cs │ │ ├── Ast │ │ │ ├── VariableDeclarationKind.cs │ │ │ ├── Declaration.cs │ │ │ ├── BindingPattern.cs │ │ │ ├── StatementListItem.cs │ │ │ ├── ExportDeclaration.cs │ │ │ ├── Statement.cs │ │ │ ├── Expression.cs │ │ │ ├── PropertyKind.cs │ │ │ ├── StaticMemberExpression.cs │ │ │ ├── ComputedMemberExpression.cs │ │ │ ├── Directive.cs │ │ │ ├── RegexValue.cs │ │ │ ├── ImportDeclarationSpecifier.cs │ │ │ ├── IClass.cs │ │ │ ├── Super.cs │ │ │ ├── Import.cs │ │ │ ├── Node.cs │ │ │ ├── DebuggerStatement.cs │ │ │ ├── EmptyStatement.cs │ │ │ ├── ThisExpression.cs │ │ │ ├── UpdateExpression.cs │ │ │ ├── ClassElement.cs │ │ │ ├── Program.cs │ │ │ ├── Module.cs │ │ │ ├── BreakStatement.cs │ │ │ ├── SpreadElement.cs │ │ │ ├── ContinueStatement.cs │ │ │ ├── ThrowStatement.cs │ │ │ ├── AwaitExpression.cs │ │ │ ├── IFunction.cs │ │ │ ├── ReturnStatement.cs │ │ │ ├── ExpressionStatement.cs │ │ │ ├── WhileStatement.cs │ │ │ ├── WithStatement.cs │ │ │ ├── DoWhileStatement.cs │ │ │ ├── MetaProperty.cs │ │ │ ├── AssignmentPattern.cs │ │ │ ├── ClassBody.cs │ │ │ ├── Script.cs │ │ │ ├── YieldExpression.cs │ │ │ ├── ExportSpecifier.cs │ │ │ ├── BlockStatement.cs │ │ │ ├── ImportDefaultSpecifier.cs │ │ │ ├── Identifier.cs │ │ │ ├── ImportNamespaceSpecifier.cs │ │ │ ├── LabeledStatement.cs │ │ │ ├── VariableDeclarator.cs │ │ │ ├── CatchClause.cs │ │ │ ├── ObjectPattern.cs │ │ │ ├── TaggedTemplateExpression.cs │ │ │ ├── ArrayPattern.cs │ │ │ ├── ChainExpression.cs │ │ │ ├── ArrayExpression.cs │ │ │ ├── ObjectExpression.cs │ │ │ ├── ExportDefaultDeclaration.cs │ │ │ ├── ImportSpecifier.cs │ │ │ ├── MethodDefinition.cs │ │ │ ├── ForInStatement.cs │ │ │ ├── RestElement.cs │ │ │ ├── SwitchCase.cs │ │ │ ├── ExportAllDeclaration.cs │ │ │ ├── TemplateElement.cs │ │ │ ├── Property.cs │ │ │ ├── TryStatement.cs │ │ │ ├── IfStatement.cs │ │ │ ├── SwitchStatement.cs │ │ │ ├── NewExpression.cs │ │ │ ├── ForOfStatement.cs │ │ │ ├── ConditionalExpression.cs │ │ │ ├── SequenceExpression.cs │ │ │ ├── ImportDeclaration.cs │ │ │ ├── VariableDeclaration.cs │ │ │ ├── CallExpression.cs │ │ │ ├── ClassExpression.cs │ │ │ ├── MemberExpression.cs │ │ │ ├── ClassDeclaration.cs │ │ │ ├── ForStatement.cs │ │ │ ├── TemplateLiteral.cs │ │ │ ├── ArrowParameterPlaceHolder.cs │ │ │ ├── ExportNamedDeclaration.cs │ │ │ ├── Range.cs │ │ │ ├── FunctionExpression.cs │ │ │ ├── ArrowFunctionExpression.cs │ │ │ ├── FunctionDeclaration.cs │ │ │ ├── GenericChildNodeYield.cs │ │ │ ├── Nodes.cs │ │ │ ├── UnaryExpression.cs │ │ │ └── Literal.cs │ │ ├── Exception.cs │ │ ├── Marker.cs │ │ ├── Comment.cs │ │ ├── IErrorHandler.cs │ │ ├── Utils │ │ │ ├── JsonWriter.cs │ │ │ └── EnumMemberAttribute.cs │ │ ├── CollectingErrorHandler.cs │ │ ├── ParserException.cs │ │ ├── ParseError.cs │ │ ├── ErrorHandler.cs │ │ ├── EsprimaExceptionHelper.cs │ │ ├── Token.cs │ │ ├── LICENSE.txt │ │ └── ParserOptions.cs │ ├── Core │ │ ├── VariableState.cs │ │ ├── ScopeType.cs │ │ ├── Variable.cs │ │ ├── ScriptExecutor.Capture.cs │ │ └── ScriptExecutor.CallFunction.cs │ ├── VariableKind.cs │ ├── Options │ │ ├── VarScopeBehavior.cs │ │ ├── SecurityPolicy.cs │ │ ├── AssignmentWithoutDefinitionBehavior.cs │ │ └── PresetOptions.cs │ ├── Interop │ │ ├── IInvokable.cs │ │ ├── IDelegateInvoker.cs │ │ ├── IAwaitExpressionHandler.cs │ │ ├── IValueConverter.cs │ │ ├── IMemberInfoProvider.cs │ │ ├── IObjectProxyRegistry.cs │ │ ├── Impl │ │ │ ├── ProxyOptions.cs │ │ │ ├── MemberInfoProvider.cs │ │ │ ├── MethodAndParameterInfo.cs │ │ │ ├── IndexedPropertyMetaGetter.cs │ │ │ ├── DictionaryObjectProxyRegistry.cs │ │ │ ├── DynamicObjectKeysGetter.cs │ │ │ ├── DefaultMemberAccessPolicy.cs │ │ │ └── InvokerUsingReflectionContext.cs │ │ ├── IMemberAccessPolicy.cs │ │ ├── IObjectProxy.cs │ │ └── ITypeProxy.cs │ ├── Wrappers │ │ ├── BreakWrapper.cs │ │ ├── ContinueWrapper.cs │ │ ├── ReturnWrapper.cs │ │ ├── ValueWrapper.cs │ │ ├── TopazArrayWrapper.cs │ │ ├── TopazObjectWrapper.cs │ │ └── TopazMemberAccessor.cs │ ├── ErrorHandling │ │ └── TopazException.cs │ ├── API │ │ ├── JsObjectConvertionOption.cs │ │ ├── array │ │ │ └── normal │ │ │ │ ├── JsArrayOptimizedMethods.cs │ │ │ │ └── JsArrayInvoker.cs │ │ ├── IJsObject.cs │ │ ├── IJsArray.cs │ │ ├── object │ │ │ ├── normal │ │ │ │ ├── JsObject.Dynamic.cs │ │ │ │ └── JsonElementJsObjectConverter.cs │ │ │ └── concurrent │ │ │ │ ├── ConcurrentJsObject.Dynamic.cs │ │ │ │ └── JsonElementConcurrentJsObjectConverter.cs │ │ ├── json │ │ │ └── JSONObject.cs │ │ └── JsObjectConverter.cs │ ├── Undefined.cs │ ├── Topaz.csproj.user │ ├── Properties │ │ └── PublishProfiles │ │ │ └── FolderProfile.pubxml.user │ ├── AstHandlers │ │ ├── Expressions │ │ │ ├── UpdateExpressionHandler.cs │ │ │ ├── ChainExpressionHandler.cs │ │ │ ├── MemberExpressionHandler.cs │ │ │ ├── FunctionExpressionHandler.cs │ │ │ ├── AssignmentPatternHandler.cs │ │ │ ├── ArrowFunctionExpressionHandler.cs │ │ │ ├── ConditionalExpressionHandler.cs │ │ │ ├── SequenceExpressionHandler.cs │ │ │ ├── TemplateLiteralHandler.cs │ │ │ ├── LiteralHandler.cs │ │ │ ├── TaggedTemplateExpressionHandler.cs │ │ │ ├── CallExpressionHandler.cs │ │ │ ├── NewExpressionHandler.cs │ │ │ ├── ArrayExpressionHandler.cs │ │ │ └── AwaitExpressionHandler.cs │ │ ├── AsyncHandlers │ │ │ ├── Expressions │ │ │ │ ├── UpdateExpressionHandler.cs │ │ │ │ ├── ChainExpressionHandler.cs │ │ │ │ ├── MemberExpressionHandler.cs │ │ │ │ ├── BinaryExpressionHandler.cs │ │ │ │ ├── AssignmentPatternHandler.cs │ │ │ │ ├── ConditionalExpressionHandler.cs │ │ │ │ ├── SequenceExpressionHandler.cs │ │ │ │ ├── TemplateLiteralHandler.cs │ │ │ │ ├── UnaryExpressionHandler.cs │ │ │ │ ├── TaggedTemplateExpressionHandler.cs │ │ │ │ ├── CallExpressionHandler.cs │ │ │ │ ├── AwaitExpressionHandler.cs │ │ │ │ ├── NewExpressionHandler.cs │ │ │ │ └── ArrayExpressionHandler.cs │ │ │ └── Statements │ │ │ │ ├── ThrowStatementHandler.cs │ │ │ │ ├── IfStatementHandler.cs │ │ │ │ └── BlockStatementHandler.cs │ │ └── Statements │ │ │ ├── ThrowStatementHandler.cs │ │ │ ├── FunctionDeclarationHandler.cs │ │ │ ├── IfStatementHandler.cs │ │ │ ├── BlockStatementHandler.cs │ │ │ └── TryStatementHandler.cs │ ├── TopazEngineSetup.cs │ ├── Directory.Build.props │ ├── ITopazFunction.cs │ ├── Collections │ │ └── ThrowHelper.cs │ └── Topaz.csproj ├── Topaz.Benchmark │ ├── Engines.cs │ ├── Topaz.Benchmark.csproj │ ├── Benchmark2.cs │ ├── Benchmark4.cs │ ├── Benchmark3.cs │ ├── Program.cs │ └── Benchmark6.cs ├── .editorconfig └── Topaz.Test │ ├── GlobalThisTests.cs │ ├── Topaz.Test.csproj │ ├── MultithreadingTests.cs │ ├── GenericListTests.cs │ ├── CancellationTests.cs │ ├── DictionaryTests.cs │ ├── AsyncFunctionTests.cs │ ├── WhileLoopTests.cs │ ├── SwitchCaseTests.cs │ ├── SpreadOperatorTests.cs │ ├── TemplateLiteralTests.cs │ ├── ConstructorTests.cs │ ├── ForLoopsTests.cs │ └── NamespaceTests.cs ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── enhancement.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── dotnet.yml │ └── publish-nuget.yml └── LICENSE /src/Topaz/docs/Topaz/images/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koculu/Topaz/HEAD/src/Topaz/docs/Topaz/images/logo1.png -------------------------------------------------------------------------------- /src/Topaz/Esprima/SourceType.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima; 2 | 3 | public enum SourceType 4 | { 5 | Module, 6 | Script 7 | } 8 | -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Engines.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Benchmark; 2 | 3 | enum Engines 4 | { 5 | Topaz, 6 | V8Engine, 7 | Jint 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Core/VariableState.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Core; 2 | 3 | internal enum VariableState 4 | { 5 | None, 6 | Captured 7 | } 8 | -------------------------------------------------------------------------------- /src/Topaz/VariableKind.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz; 2 | 3 | public enum VariableKind 4 | { 5 | Var = 0, 6 | Let = 1, 7 | Const = 2 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/VariableDeclarationKind.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public enum VariableDeclarationKind 4 | { 5 | Var, 6 | Let, 7 | Const 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Options/VarScopeBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Options; 2 | 3 | public enum VarScopeBehavior 4 | { 5 | FunctionScope, 6 | DeclarationScope 7 | } 8 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Declaration.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public abstract class Declaration : Statement 4 | { 5 | protected Declaration(Nodes type) : base(type) 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IInvokable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Tenray.Topaz.Interop; 4 | 5 | public interface IInvokable 6 | { 7 | object Invoke(IReadOnlyList args); 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/BindingPattern.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public abstract class BindingPattern : Expression 4 | { 5 | protected BindingPattern(Nodes type) : base(type) 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/StatementListItem.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public abstract class StatementListItem : Node 4 | { 5 | protected StatementListItem(Nodes type) : base(type) 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ExportDeclaration.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public abstract class ExportDeclaration : Declaration 4 | { 5 | protected ExportDeclaration(Nodes type) : base(type) 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IDelegateInvoker.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Tenray.Topaz.Interop; 4 | 5 | public interface IDelegateInvoker 6 | { 7 | object Invoke(object function, IReadOnlyList args); 8 | } 9 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/BreakWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz; 2 | 3 | internal sealed class BreakWrapper 4 | { 5 | internal static BreakWrapper Instance = new BreakWrapper(); 6 | private BreakWrapper() 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Topaz/ErrorHandling/TopazException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tenray.Topaz; 4 | 5 | public sealed class TopazException : Exception 6 | { 7 | public TopazException(string message) : base(message) 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | bin 3 | obj 4 | src/Tenray.Topaz/Tenray.Topaz.xml 5 | src/Tenray.Topaz/Tenray.Topaz.csproj.user 6 | src/Tenray.Topaz/Properties/PublishProfiles/FolderProfile.pubxml 7 | src/Tenray.Topaz/Properties/PublishProfiles/FolderProfile.pubxml.user 8 | -------------------------------------------------------------------------------- /src/Topaz/API/JsObjectConvertionOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tenray.Topaz.API; 4 | 5 | [Flags] 6 | public enum JsObjectConverterOption 7 | { 8 | None, 9 | UseLowerCasePropertyNames, 10 | CreateConcurrentJsObject 11 | } 12 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/ContinueWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz; 2 | 3 | internal sealed class ContinueWrapper 4 | { 5 | internal static ContinueWrapper Instance = new ContinueWrapper(); 6 | private ContinueWrapper() 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/ReturnWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz; 2 | 3 | internal sealed class ReturnWrapper 4 | { 5 | internal object Result { get; } 6 | 7 | internal ReturnWrapper(object result) 8 | { 9 | Result = result; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Statement.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public abstract class Statement : StatementListItem 4 | { 5 | protected Statement(Nodes type) : base(type) 6 | { 7 | } 8 | 9 | public Identifier? LabelSet { get; internal set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Expression.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | /// 4 | /// A JavaScript expression. 5 | /// 6 | public abstract class Expression : StatementListItem 7 | { 8 | protected Expression(Nodes type) : base(type) 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Exception.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima; 2 | 3 | using System; 4 | 5 | internal static class Exception where T : Exception, new() 6 | { 7 | private static string? _message; 8 | 9 | public static string DefaultMessage => _message ??= new T().Message; 10 | } 11 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IAwaitExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Tenray.Topaz.Interop; 5 | 6 | public interface IAwaitExpressionHandler 7 | { 8 | Task HandleAwaitExpression(object awaitObject, CancellationToken token); 9 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/PropertyKind.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esprima.Ast; 4 | 5 | [Flags] 6 | public enum PropertyKind 7 | { 8 | None = 0, 9 | Data = 1, 10 | Get = 2, 11 | Set = 4, 12 | Init = 8, 13 | Constructor = 16, 14 | Method = 32 15 | }; 16 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tenray.Topaz.Interop; 4 | 5 | public interface IValueConverter 6 | { 7 | bool TryConvertValue(object value, Type targetType, out object convertedValue); 8 | 9 | bool IsValueAssignableTo(object value, Type targetType); 10 | } 11 | -------------------------------------------------------------------------------- /src/Topaz/Undefined.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz; 2 | 3 | public sealed class Undefined 4 | { 5 | public static readonly Undefined Value = new (); 6 | private Undefined() 7 | { 8 | } 9 | 10 | public override string ToString() 11 | { 12 | return "undefined"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Topaz/API/array/normal/JsArrayOptimizedMethods.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.API; 2 | 3 | /// 4 | /// The methods that are optimized by avoiding reflection on common array operations. 5 | /// 6 | internal enum JsArrayOptimizedMethods 7 | { 8 | push, 9 | pop, 10 | shift 11 | } 12 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/StaticMemberExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public sealed class StaticMemberExpression : MemberExpression 4 | { 5 | public StaticMemberExpression(Expression obj, Expression property, bool optional) 6 | : base(obj, property, false, optional) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ComputedMemberExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public sealed class ComputedMemberExpression : MemberExpression 4 | { 5 | public ComputedMemberExpression(Expression obj, Expression property, bool optional) 6 | : base(obj, property, true, optional) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Directive.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public sealed class Directive : ExpressionStatement 4 | { 5 | public readonly string Directiv; 6 | 7 | public Directive(Expression expression, string directive) : base(expression) 8 | { 9 | Directiv = directive; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IMemberInfoProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Tenray.Topaz.Interop; 5 | 6 | public interface IMemberInfoProvider 7 | { 8 | MemberInfo[] GetInstanceMembers(object instance, string memberName); 9 | 10 | MemberInfo[] GetStaticMembers(Type type, string memberName); 11 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/RegexValue.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public sealed class RegexValue 4 | { 5 | public readonly string Pattern; 6 | public readonly string Flags; 7 | 8 | public RegexValue(string pattern, string flags) 9 | { 10 | Pattern = pattern; 11 | Flags = flags; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IObjectProxyRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tenray.Topaz.Interop; 4 | 5 | public interface IObjectProxyRegistry 6 | { 7 | void AddObjectProxy(Type type, IObjectProxy proxy); 8 | 9 | void RemoveObjectProxy(Type type); 10 | 11 | bool TryGetObjectProxy(object instance, out IObjectProxy proxy); 12 | } 13 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ImportDeclarationSpecifier.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public abstract class ImportDeclarationSpecifier : Declaration 4 | { 5 | protected ImportDeclarationSpecifier(Nodes type) : base(type) 6 | { 7 | } 8 | 9 | public Identifier Local => LocalId; 10 | protected abstract Identifier LocalId { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Topaz/Topaz.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <_LastSelectedProfileId>D:\code\modern\topaz\src\Tenray.Topaz\Properties\PublishProfiles\FolderProfile.pubxml 5 | 6 | -------------------------------------------------------------------------------- /src/Topaz/Core/ScopeType.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Core; 2 | 3 | internal enum ScopeType 4 | { 5 | Global, 6 | Block, 7 | /// 8 | /// Function Scope (Closure). 9 | /// This scope is frozen. 10 | /// Its variable dictionary is immutable. 11 | /// 12 | Function, 13 | FunctionInnerBlock, 14 | Custom 15 | } 16 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/IClass.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | /// 4 | /// Represents either a or an 5 | /// 6 | public interface IClass 7 | { 8 | Identifier? Id { get; } 9 | Expression? SuperClass { get; } 10 | ClassBody Body { get; } 11 | NodeCollection ChildNodes { get; } 12 | } 13 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Marker.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima; 2 | 3 | internal sealed class Marker 4 | { 5 | public int Index; 6 | public int Line; 7 | public int Column; 8 | 9 | public Marker() 10 | { 11 | } 12 | 13 | public Marker(int index, int line, int column) 14 | { 15 | Index = index; 16 | Line = line; 17 | Column = column; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Topaz/Properties/PublishProfiles/FolderProfile.pubxml.user: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | True|2022-11-13T18:30:13.4769810Z;False|2022-11-13T19:28:41.5630884+01:00;False|2022-11-13T19:28:29.0958182+01:00; 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Super.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class Super : Expression 6 | { 7 | public Super() : base(Nodes.Super) 8 | { 9 | } 10 | 11 | public override NodeCollection ChildNodes => NodeCollection.Empty; 12 | 13 | protected internal override void Accept(AstVisitor visitor) 14 | { 15 | visitor.VisitSuper(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Import.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class Import : Expression 6 | { 7 | public Import() : base(Nodes.Import) 8 | { 9 | } 10 | 11 | public override NodeCollection ChildNodes => NodeCollection.Empty; 12 | 13 | protected internal override void Accept(AstVisitor visitor) 14 | { 15 | visitor.VisitImport(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Comment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esprima; 4 | 5 | public enum CommentType 6 | { 7 | Block, 8 | Line 9 | } 10 | 11 | public sealed class Comment 12 | { 13 | public CommentType Type; 14 | public string? Value; 15 | 16 | public bool MultiLine; 17 | public int[] Slice = Array.Empty(); 18 | public int Start; 19 | public int End; 20 | public SourceLocation? Loc; 21 | } 22 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/IErrorHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima; 2 | 3 | public interface IErrorHandler 4 | { 5 | string? Source { get; set; } 6 | bool Tolerant { get; set; } 7 | void RecordError(ParserException error); 8 | void Tolerate(ParserException error); 9 | ParserException CreateError(int index, int line, int column, string message); 10 | void TolerateError(int index, int line, int column, string message); 11 | } 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement 3 | about: Suggest an improvement 4 | title: "[Enhancement] Title" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What kind of enhancement you would like to have? Please describe.** 11 | e.g. Improve code quality, performance, memory consumption, etc. 12 | 13 | **Describe the enhancement you'd like** 14 | A clear and concise description of what you want to improve. 15 | -------------------------------------------------------------------------------- /src/Topaz/API/IJsObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz.API; 5 | 6 | public interface IJsObject 7 | { 8 | bool TryGetValue(object key, out object value); 9 | 10 | IEnumerable GetObjectKeys(); 11 | 12 | void SetValue(object key, object value); 13 | 14 | internal void UnwrapObject(ScriptExecutor scriptExecutor); 15 | 16 | internal bool IsPrototypeProperty(object member); 17 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Node.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public abstract class Node 6 | { 7 | protected Node(Nodes type) 8 | { 9 | Type = type; 10 | } 11 | 12 | public readonly Nodes Type; 13 | public Range Range; 14 | public Location Location; 15 | 16 | public abstract NodeCollection ChildNodes { get; } 17 | 18 | protected internal abstract void Accept(AstVisitor visitor); 19 | } 20 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/ProxyOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tenray.Topaz.Interop; 4 | 5 | [Flags] 6 | public enum ProxyOptions 7 | { 8 | None, 9 | AllowConstructor, 10 | AllowMethod, 11 | AllowField, 12 | AllowProperty, 13 | AutomaticTypeConversion, 14 | Default = 15 | AllowMethod | 16 | AllowField | 17 | AllowProperty | 18 | AllowConstructor | 19 | AutomaticTypeConversion 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/DebuggerStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class DebuggerStatement : Statement 6 | { 7 | public DebuggerStatement() : base(Nodes.DebuggerStatement) { } 8 | 9 | public override NodeCollection ChildNodes => NodeCollection.Empty; 10 | 11 | protected internal override void Accept(AstVisitor visitor) 12 | { 13 | visitor.VisitDebuggerStatement(this); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/EmptyStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class EmptyStatement : Statement 6 | { 7 | public EmptyStatement() : base(Nodes.EmptyStatement) 8 | { 9 | } 10 | 11 | public override NodeCollection ChildNodes => NodeCollection.Empty; 12 | 13 | protected internal override void Accept(AstVisitor visitor) 14 | { 15 | visitor.VisitEmptyStatement(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/API/IJsArray.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz.API; 5 | 6 | public interface IJsArray 7 | { 8 | bool TryGetArrayValue(int index, out object value); 9 | 10 | void SetArrayValue(int index, object value); 11 | 12 | void AddArrayValue(object value); 13 | 14 | void AddArrayValues(IEnumerable enumerable); 15 | 16 | internal void UnwrapArray(ScriptExecutor scriptExecutor); 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ThisExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ThisExpression : Expression 6 | { 7 | public ThisExpression() : base(Nodes.ThisExpression) 8 | { 9 | } 10 | 11 | public override NodeCollection ChildNodes => NodeCollection.Empty; 12 | 13 | protected internal override void Accept(AstVisitor visitor) 14 | { 15 | visitor.VisitThisExpression(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/UpdateExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class UpdateExpressionHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 10 | { 11 | return UnaryExpressionHandler.Execute(scriptExecutor, expression, token); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/UpdateExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class UpdateExpression : UnaryExpression 6 | { 7 | public UpdateExpression(string? op, Expression arg, bool prefix) : base(Nodes.UpdateExpression, op, arg) 8 | { 9 | Prefix = prefix; 10 | } 11 | 12 | protected internal override void Accept(AstVisitor visitor) 13 | { 14 | visitor.VisitUpdateExpression(this); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ClassElement.cs: -------------------------------------------------------------------------------- 1 | #nullable disable 2 | 3 | namespace Esprima.Ast; 4 | 5 | public abstract class ClassProperty : Expression 6 | { 7 | public PropertyKind Kind; 8 | 9 | public Expression Key; // Identifier, Literal, '[' Expression ']' 10 | public bool Computed; 11 | public Expression Value; 12 | 13 | protected ClassProperty(Nodes type) : base(type) 14 | { 15 | } 16 | 17 | public override NodeCollection ChildNodes => new(Key, Value); 18 | } 19 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Program.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public abstract class Program : Statement 6 | { 7 | protected Program(Nodes type) : base(type) 8 | { 9 | } 10 | 11 | public abstract SourceType SourceType { get; } 12 | 13 | public abstract ref readonly NodeList Body { get; } 14 | 15 | protected internal override void Accept(AstVisitor visitor) 16 | { 17 | visitor.VisitProgram(this); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/ValueWrapper.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using Esprima.Utils; 3 | 4 | namespace Tenray.Topaz; 5 | 6 | internal sealed class ValueWrapper : Expression 7 | { 8 | public override NodeCollection ChildNodes => NodeCollection.Empty; 9 | 10 | public readonly object Value; 11 | 12 | public ValueWrapper(object value) : base(Nodes.ValueWrapper) 13 | { 14 | Value = value; 15 | } 16 | 17 | protected internal override void Accept(AstVisitor visitor) 18 | { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Module.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public sealed class Module : Program 4 | { 5 | private readonly NodeList _body; 6 | public override SourceType SourceType => SourceType.Module; 7 | 8 | public Module(in NodeList body) : base(Nodes.Program) 9 | { 10 | _body = body; 11 | } 12 | 13 | public override ref readonly NodeList Body => ref _body; 14 | 15 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Body); 16 | } 17 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/BreakStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class BreakStatement : Statement 6 | { 7 | public readonly Identifier? Label; 8 | 9 | public BreakStatement(Identifier? label) : base(Nodes.BreakStatement) 10 | { 11 | Label = label; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Label); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitBreakStatement(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/UpdateExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class UpdateExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | return await UnaryExpressionHandler.ExecuteAsync(scriptExecutor, expression, token); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/SpreadElement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class SpreadElement : Expression 6 | { 7 | public readonly Expression Argument; 8 | 9 | public SpreadElement(Expression argument) : base(Nodes.SpreadElement) 10 | { 11 | Argument = argument; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Argument); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitSpreadElement(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ContinueStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ContinueStatement : Statement 6 | { 7 | public readonly Identifier? Label; 8 | 9 | public ContinueStatement(Identifier? label) : base(Nodes.ContinueStatement) 10 | { 11 | Label = label; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Label); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitContinueStatement(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ThrowStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ThrowStatement : Statement 6 | { 7 | public readonly Expression Argument; 8 | 9 | public ThrowStatement(Expression argument) : base(Nodes.ThrowStatement) 10 | { 11 | Argument = argument; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Argument); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitThrowStatement(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/AwaitExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class AwaitExpression : Expression 6 | { 7 | public readonly Expression Argument; 8 | 9 | public AwaitExpression(Expression argument) : base(Nodes.AwaitExpression) 10 | { 11 | Argument = argument; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Argument); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitAwaitExpression(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/IFunction.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | /// 4 | /// Represents either a , a or an 5 | /// 6 | public interface IFunction 7 | { 8 | Identifier? Id { get; } 9 | ref readonly NodeList Params { get; } 10 | Node Body { get; } 11 | bool Generator { get; } 12 | bool Expression { get; } 13 | bool Strict { get; } 14 | bool Async { get; } 15 | NodeCollection ChildNodes { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ReturnStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ReturnStatement : Statement 6 | { 7 | public readonly Expression? Argument; 8 | 9 | public ReturnStatement(Expression? argument) : base(Nodes.ReturnStatement) 10 | { 11 | Argument = argument; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Argument); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitReturnStatement(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Utils/JsonWriter.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Utils; 2 | 3 | public abstract class JsonWriter 4 | { 5 | public abstract void Null(); 6 | public abstract void Number(long n); 7 | public abstract void Number(double n); 8 | public abstract void String(string? value); 9 | public abstract void Boolean(bool flag); 10 | public abstract void StartArray(); 11 | public abstract void EndArray(); 12 | public abstract void StartObject(); 13 | public abstract void Member(string name); 14 | public abstract void EndObject(); 15 | } 16 | -------------------------------------------------------------------------------- /src/Topaz/Options/SecurityPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Tenray.Topaz.Options; 4 | 5 | /// 6 | /// Security Flags. 7 | /// 8 | [Flags] 9 | public enum SecurityPolicy 10 | { 11 | /// 12 | /// Default and secure. Script cannot process 13 | /// types in the System.Reflection namespace. 14 | /// 15 | Default, 16 | 17 | /// 18 | /// Reflection API is allowed. 19 | /// Script can access everything. 20 | /// Use it with caution. 21 | /// 22 | EnableReflection 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ExpressionStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public class ExpressionStatement : Statement 6 | { 7 | public readonly Expression Expression; 8 | 9 | public ExpressionStatement(Expression expression) : base(Nodes.ExpressionStatement) 10 | { 11 | Expression = expression; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Expression); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitExpressionStatement(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/MemberInfoProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Tenray.Topaz.Interop; 5 | 6 | public class MemberInfoProvider : IMemberInfoProvider 7 | { 8 | public MemberInfo[] GetInstanceMembers(object instance, string memberName) 9 | { 10 | return instance.GetType().GetMember(memberName, BindingFlags.Public | BindingFlags.Instance); 11 | } 12 | 13 | public MemberInfo[] GetStaticMembers(Type type, string memberName) 14 | { 15 | return type.GetMember(memberName, BindingFlags.Public | BindingFlags.Static); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.cs] 2 | 3 | # CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. 4 | dotnet_diagnostic.CS8632.severity = none 5 | 6 | # CS1591: Missing XML comment for publicly visible type or member 7 | dotnet_diagnostic.CS1591.severity = none 8 | 9 | # CS1584: XML comment has syntactically incorrect cref attribute 10 | dotnet_diagnostic.CS1584.severity = none 11 | 12 | # CS1570: XML comment has badly formed XML 13 | dotnet_diagnostic.CS1570.severity = none 14 | 15 | # CS1658: Warning is overriding an error 16 | dotnet_diagnostic.CS1658.severity = none -------------------------------------------------------------------------------- /src/Topaz/Interop/IMemberAccessPolicy.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Interop; 2 | 3 | /// 4 | /// Provides member access security policy. 5 | /// 6 | public interface IMemberAccessPolicy 7 | { 8 | /// 9 | /// Returns true if 10 | /// access allowed to 11 | /// given member name on given object instance. 12 | /// 13 | /// Object instance. 14 | /// Member name. 15 | /// true if allowed, false otherwise. 16 | public bool IsObjectMemberAccessAllowed(object obj, string memberName); 17 | } -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/ChainExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class ChainExpressionHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 10 | { 11 | var expr = (ChainExpression)expression; 12 | // Possible values: 13 | // CallExpression | ComputedMemberExpression | StaticMemberExpression 14 | return scriptExecutor.ExecuteStatement(expr.Expression, token); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/WhileStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class WhileStatement : Statement 6 | { 7 | public readonly Expression Test; 8 | public readonly Statement Body; 9 | 10 | public WhileStatement(Expression test, Statement body) : base(Nodes.WhileStatement) 11 | { 12 | Test = test; 13 | Body = body; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Test, Body); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitWhileStatement(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/WithStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class WithStatement : Statement 6 | { 7 | public readonly Expression Object; 8 | public readonly Statement Body; 9 | 10 | public WithStatement(Expression obj, Statement body) : base(Nodes.WithStatement) 11 | { 12 | Object = obj; 13 | Body = body; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Object, Body); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitWithStatement(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/DoWhileStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class DoWhileStatement : Statement 6 | { 7 | public readonly Statement Body; 8 | public readonly Expression Test; 9 | 10 | public DoWhileStatement(Statement body, Expression test) : base(Nodes.DoWhileStatement) 11 | { 12 | Body = body; 13 | Test = test; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Body, Test); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitDoWhileStatement(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/MetaProperty.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class MetaProperty : Expression 6 | { 7 | public readonly Identifier Meta; 8 | public readonly Identifier Property; 9 | 10 | public MetaProperty(Identifier meta, Identifier property) : base(Nodes.MetaProperty) 11 | { 12 | Meta = meta; 13 | Property = property; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Meta, Property); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitMetaProperty(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/AssignmentPattern.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class AssignmentPattern : Expression 6 | { 7 | public readonly Expression Left; 8 | public Expression Right; 9 | 10 | public AssignmentPattern(Expression left, Expression right) : base(Nodes.AssignmentPattern) 11 | { 12 | Left = left; 13 | Right = right; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Left, Right); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitAssignmentPattern(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ClassBody.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ClassBody : Node 6 | { 7 | private readonly NodeList _body; 8 | 9 | public ClassBody(in NodeList body) : base(Nodes.ClassBody) 10 | { 11 | _body = body; 12 | } 13 | 14 | public ref readonly NodeList Body => ref _body; 15 | 16 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_body); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitClassBody(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Statements/ThrowStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System; 3 | using System.Threading; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Statements; 7 | 8 | internal static partial class ThrowStatementHandler 9 | { 10 | internal static object Execute(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 11 | { 12 | var expr = (ThrowStatement)statement; 13 | var err = scriptExecutor.ExecuteExpressionAndGetValue(expr.Argument, token); 14 | if (err is Exception e) 15 | throw e; 16 | throw new Exception(err.ToString()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Script.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public sealed class Script : Program 4 | { 5 | private readonly NodeList _body; 6 | 7 | public Script( 8 | in NodeList body, 9 | bool strict) 10 | : base(Nodes.Program) 11 | { 12 | _body = body; 13 | Strict = strict; 14 | } 15 | 16 | public override SourceType SourceType => SourceType.Script; 17 | public bool Strict { get; } 18 | 19 | public override ref readonly NodeList Body => ref _body; 20 | 21 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Body); 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/YieldExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class YieldExpression : Expression 6 | { 7 | public readonly Expression? Argument; 8 | public readonly bool Delegate; 9 | 10 | public YieldExpression(Expression? argument, bool delgate) : base(Nodes.YieldExpression) 11 | { 12 | Argument = argument; 13 | Delegate = delgate; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Argument); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitYieldExpression(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ExportSpecifier.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ExportSpecifier : Statement 6 | { 7 | public readonly Identifier Exported; 8 | public readonly Identifier Local; 9 | 10 | public ExportSpecifier(Identifier local, Identifier exported) : base(Nodes.ExportSpecifier) 11 | { 12 | Exported = exported; 13 | Local = local; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Exported, Local); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitExportSpecifier(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/BlockStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class BlockStatement : Statement 6 | { 7 | private readonly NodeList _body; 8 | 9 | public BlockStatement(in NodeList body) : base(Nodes.BlockStatement) 10 | { 11 | _body = body; 12 | } 13 | 14 | public ref readonly NodeList Body => ref _body; 15 | 16 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_body); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitBlockStatement(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ImportDefaultSpecifier.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ImportDefaultSpecifier : ImportDeclarationSpecifier 6 | { 7 | public new readonly Identifier Local; 8 | protected override Identifier LocalId => Local; 9 | 10 | public ImportDefaultSpecifier(Identifier local) : base(Nodes.ImportDefaultSpecifier) 11 | { 12 | Local = local; 13 | } 14 | 15 | public override NodeCollection ChildNodes => new(Local); 16 | 17 | protected internal override void Accept(AstVisitor visitor) 18 | { 19 | visitor.VisitImportDefaultSpecifier(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Identifier.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | using Tenray.Topaz; 3 | 4 | namespace Esprima.Ast; 5 | 6 | public sealed class Identifier : Expression 7 | { 8 | public readonly string? Name; 9 | internal TopazIdentifier TopazIdentifier; 10 | 11 | public Identifier(string? name) : base(Nodes.Identifier) 12 | { 13 | Name = name; 14 | TopazIdentifier = new TopazIdentifier(Name); 15 | } 16 | 17 | public override NodeCollection ChildNodes => NodeCollection.Empty; 18 | 19 | protected internal override void Accept(AstVisitor visitor) 20 | { 21 | visitor.VisitIdentifier(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ImportNamespaceSpecifier.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ImportNamespaceSpecifier : ImportDeclarationSpecifier 6 | { 7 | public new readonly Identifier Local; 8 | protected override Identifier LocalId => Local; 9 | 10 | public ImportNamespaceSpecifier(Identifier local) : base(Nodes.ImportNamespaceSpecifier) 11 | { 12 | Local = local; 13 | } 14 | 15 | public override NodeCollection ChildNodes => new(Local); 16 | 17 | protected internal override void Accept(AstVisitor visitor) 18 | { 19 | visitor.VisitImportNamespaceSpecifier(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/MemberExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class MemberExpressionHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 10 | { 11 | var expr = (MemberExpression)expression; 12 | var obj = scriptExecutor.ExecuteStatement(expr.Object, token); 13 | var prop = scriptExecutor.ExecuteStatement(expr.Property, token); 14 | return new TopazMemberAccessor(obj, prop, expr.Computed, expr.Optional); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/LabeledStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class LabeledStatement : Statement 6 | { 7 | public readonly Identifier Label; 8 | public readonly Statement Body; 9 | 10 | public LabeledStatement(Identifier label, Statement body) : base(Nodes.LabeledStatement) 11 | { 12 | Label = label; 13 | Body = body; 14 | body.LabelSet = label; 15 | } 16 | 17 | public override NodeCollection ChildNodes => new(Label, Body); 18 | 19 | protected internal override void Accept(AstVisitor visitor) 20 | { 21 | visitor.VisitLabeledStatement(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/VariableDeclarator.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class VariableDeclarator : Node 6 | { 7 | public readonly Expression Id; // BindingIdentifier | BindingPattern; 8 | public Expression? Init; 9 | 10 | public VariableDeclarator(Expression id, Expression? init) : 11 | base(Nodes.VariableDeclarator) 12 | { 13 | Id = id; 14 | Init = init; 15 | } 16 | 17 | public override NodeCollection ChildNodes => new(Id, Init); 18 | 19 | protected internal override void Accept(AstVisitor visitor) 20 | { 21 | visitor.VisitVariableDeclarator(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/CatchClause.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class CatchClause : Statement 6 | { 7 | public readonly Expression? Param; // BindingIdentifier | BindingPattern | null; 8 | public readonly BlockStatement Body; 9 | 10 | public CatchClause(Expression? param, BlockStatement body) : 11 | base(Nodes.CatchClause) 12 | { 13 | Param = param; 14 | Body = body; 15 | } 16 | 17 | public override NodeCollection ChildNodes => new(Param, Body); 18 | 19 | protected internal override void Accept(AstVisitor visitor) 20 | { 21 | visitor.VisitCatchClause(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ObjectPattern.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ObjectPattern : BindingPattern 6 | { 7 | private readonly NodeList _properties; 8 | 9 | public ObjectPattern(in NodeList properties) : base(Nodes.ObjectPattern) 10 | { 11 | _properties = properties; 12 | } 13 | 14 | public ref readonly NodeList Properties => ref _properties; 15 | 16 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_properties); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitObjectPattern(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/TaggedTemplateExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class TaggedTemplateExpression : Expression 6 | { 7 | public readonly Expression Tag; 8 | public readonly TemplateLiteral Quasi; 9 | 10 | public TaggedTemplateExpression(Expression tag, TemplateLiteral quasi) : base(Nodes.TaggedTemplateExpression) 11 | { 12 | Tag = tag; 13 | Quasi = quasi; 14 | } 15 | 16 | public override NodeCollection ChildNodes => new(Tag, Quasi); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitTaggedTemplateExpression(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ArrayPattern.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ArrayPattern : BindingPattern 6 | { 7 | private readonly NodeList _elements; 8 | 9 | public ArrayPattern(in NodeList elements) : base(Nodes.ArrayPattern) 10 | { 11 | _elements = elements; 12 | } 13 | 14 | public ref readonly NodeList Elements => ref _elements; 15 | 16 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_elements!); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitArrayPattern(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/ChainExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class ChainExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var expr = (ChainExpression)expression; 13 | // Possible values: 14 | // CallExpression | ComputedMemberExpression | StaticMemberExpression 15 | return await scriptExecutor.ExecuteStatementAsync(expr.Expression, token); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ChainExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ChainExpression : Expression 6 | { 7 | /// 8 | /// CallExpression | ComputedMemberExpression | StaticMemberExpression 9 | /// 10 | public readonly Expression Expression; 11 | 12 | public ChainExpression(Expression expression) : base(Nodes.ChainExpression) 13 | { 14 | Expression = expression; 15 | } 16 | 17 | public override NodeCollection ChildNodes => NodeCollection.Empty; 18 | 19 | protected internal override void Accept(AstVisitor visitor) 20 | { 21 | visitor.VisitChainExpression(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ArrayExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ArrayExpression : Expression 6 | { 7 | private readonly NodeList _elements; 8 | 9 | public ArrayExpression(in NodeList elements) : base(Nodes.ArrayExpression) 10 | { 11 | _elements = elements; 12 | } 13 | 14 | public ref readonly NodeList Elements => ref _elements; 15 | 16 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_elements!); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitArrayExpression(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Statements/ThrowStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Tenray.Topaz.Core; 6 | 7 | namespace Tenray.Topaz.Statements; 8 | 9 | internal static partial class ThrowStatementHandler 10 | { 11 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 12 | { 13 | var expr = (ThrowStatement)statement; 14 | var err = await scriptExecutor.ExecuteStatementAsync(expr.Argument, token); 15 | if (err is Exception e) 16 | throw e; 17 | throw new Exception(err.ToString()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ObjectExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ObjectExpression : Expression 6 | { 7 | private readonly NodeList _properties; 8 | 9 | public ObjectExpression(in NodeList properties) : base(Nodes.ObjectExpression) 10 | { 11 | _properties = properties; 12 | } 13 | 14 | public ref readonly NodeList Properties => ref _properties; 15 | 16 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_properties); 17 | 18 | protected internal override void Accept(AstVisitor visitor) 19 | { 20 | visitor.VisitObjectExpression(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/CollectingErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Esprima; 4 | 5 | /// 6 | /// Error handler that collects errors that have been seen during the parsing. 7 | /// 8 | /// 9 | /// If you reuse this instance memory usage can grow during process lifetime when errors 10 | /// are gathered. 11 | /// 12 | public sealed class CollectingErrorHandler : ErrorHandler 13 | { 14 | private readonly List _errors = new(); 15 | 16 | public IReadOnlyCollection Errors => _errors; 17 | 18 | public override void RecordError(ParserException error) 19 | { 20 | _errors.Add(error); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ExportDefaultDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ExportDefaultDeclaration : ExportDeclaration 6 | { 7 | public readonly StatementListItem Declaration; //: BindingIdentifier | BindingPattern | ClassDeclaration | Expression | FunctionDeclaration; 8 | 9 | public ExportDefaultDeclaration(StatementListItem declaration) : base(Nodes.ExportDefaultDeclaration) 10 | { 11 | Declaration = declaration; 12 | } 13 | 14 | public override NodeCollection ChildNodes => new(Declaration); 15 | 16 | protected internal override void Accept(AstVisitor visitor) 17 | { 18 | visitor.VisitExportDefaultDeclaration(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/FunctionExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz.Expressions; 5 | 6 | internal static partial class FunctionExpressionHandler 7 | { 8 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression) 9 | { 10 | var expr = (FunctionExpression)expression; 11 | var identifier = expr.Id; 12 | var name = identifier?.Name ?? string.Empty; 13 | var function = new TopazFunction( 14 | scriptExecutor.NewFunctionScope(), 15 | name, 16 | expr); 17 | scriptExecutor.DefineVariable(identifier, function, VariableKind.Var); 18 | return function; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Statements/FunctionDeclarationHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz.Statements; 5 | 6 | internal static partial class FunctionDeclarationHandler 7 | { 8 | internal static object Execute(ScriptExecutor scriptExecutor, Node statement) 9 | { 10 | var expr = (FunctionDeclaration)statement; 11 | var identifier = expr.Id; 12 | var name = identifier?.Name ?? string.Empty; 13 | var function = new TopazFunction( 14 | scriptExecutor.NewFunctionScope(), 15 | name, 16 | expr); 17 | scriptExecutor.DefineVariable(identifier, function, VariableKind.Var); 18 | return function; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ImportSpecifier.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ImportSpecifier : ImportDeclarationSpecifier 6 | { 7 | public new readonly Identifier Local; 8 | protected override Identifier LocalId => Local; 9 | 10 | public readonly Identifier Imported; 11 | 12 | public ImportSpecifier(Identifier local, Identifier imported) : base(Nodes.ImportSpecifier) 13 | { 14 | Local = local; 15 | Imported = imported; 16 | } 17 | 18 | public override NodeCollection ChildNodes => new(Local, Imported); 19 | 20 | protected internal override void Accept(AstVisitor visitor) 21 | { 22 | visitor.VisitImportSpecifier(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE] Feature request title." 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/AssignmentPatternHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class AssignmentPatternHandler 8 | { 9 | internal static object Execute( 10 | ScriptExecutor scriptExecutor, 11 | Node expression, 12 | CancellationToken token) 13 | { 14 | var expr = (AssignmentPattern)expression; 15 | var left = scriptExecutor.ExecuteStatement(expr.Left, token); 16 | var right = scriptExecutor.ExecuteExpressionAndGetValue(expr.Right, token); 17 | scriptExecutor 18 | .SetReferenceValue(left, right); 19 | return right; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/MethodDefinition.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class MethodDefinition : ClassProperty 6 | { 7 | public readonly bool Static; 8 | 9 | public MethodDefinition( 10 | Expression key, 11 | bool computed, 12 | FunctionExpression value, 13 | PropertyKind kind, 14 | bool isStatic) 15 | : base(Nodes.MethodDefinition) 16 | { 17 | Static = isStatic; 18 | Key = key; 19 | Computed = computed; 20 | Value = value; 21 | Kind = kind; 22 | } 23 | 24 | protected internal override void Accept(AstVisitor visitor) 25 | { 26 | visitor.VisitMethodDefinition(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ForInStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ForInStatement : Statement 6 | { 7 | public readonly Node Left; 8 | public readonly Expression Right; 9 | public readonly Statement Body; 10 | 11 | public ForInStatement( 12 | Node left, 13 | Expression right, 14 | Statement body) : base(Nodes.ForInStatement) 15 | { 16 | Left = left; 17 | Right = right; 18 | Body = body; 19 | } 20 | 21 | public override NodeCollection ChildNodes => new(Left, Right, Body); 22 | 23 | protected internal override void Accept(AstVisitor visitor) 24 | { 25 | visitor.VisitForInStatement(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/ArrowFunctionExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz.Expressions; 5 | 6 | internal static partial class ArrowFunctionExpressionHandler 7 | { 8 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression) 9 | { 10 | var expr = (ArrowFunctionExpression)expression; 11 | var identifier = expr.Id; 12 | var name = identifier?.Name ?? string.Empty; 13 | var function = new TopazFunction( 14 | scriptExecutor.NewFunctionScope(), 15 | name, 16 | expr); 17 | scriptExecutor.DefineVariable(identifier, function, VariableKind.Var); 18 | return function; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/MemberExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class MemberExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var expr = (MemberExpression)expression; 13 | var obj = await scriptExecutor.ExecuteStatementAsync(expr.Object, token); 14 | var prop = await scriptExecutor.ExecuteStatementAsync(expr.Property, token); 15 | return new TopazMemberAccessor(obj, prop, expr.Computed, expr.Optional); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/RestElement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class RestElement : Expression 6 | { 7 | // Identifier in esprima but not forced and 8 | // for instance ...i[0] is a SpreadElement 9 | // which is reinterpreted to RestElement with a ComputerMemberExpression 10 | 11 | public readonly Expression Argument; // BindingIdentifier | BindingPattern 12 | 13 | public RestElement(Expression argument) : base(Nodes.RestElement) 14 | { 15 | Argument = argument; 16 | } 17 | 18 | public override NodeCollection ChildNodes => new(Argument); 19 | 20 | protected internal override void Accept(AstVisitor visitor) 21 | { 22 | visitor.VisitRestElement(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/SwitchCase.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class SwitchCase : Node 6 | { 7 | private readonly NodeList _consequent; 8 | public readonly Expression? Test; 9 | 10 | public SwitchCase(Expression? test, in NodeList consequent) : base(Nodes.SwitchCase) 11 | { 12 | Test = test; 13 | _consequent = consequent; 14 | } 15 | 16 | public ref readonly NodeList Consequent => ref _consequent; 17 | 18 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Test, _consequent); 19 | 20 | protected internal override void Accept(AstVisitor visitor) 21 | { 22 | visitor.VisitSwitchCase(this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/BinaryExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class BinaryExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var expr = (BinaryExpression)expression; 13 | var left = await scriptExecutor.ExecuteExpressionAndGetValueAsync(expr.Left, token); 14 | var right = await scriptExecutor .ExecuteExpressionAndGetValueAsync(expr.Right, token); 15 | return ExecuteBinaryOperator(scriptExecutor, expr.Operator, left, right); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ExportAllDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ExportAllDeclaration : ExportDeclaration 6 | { 7 | public readonly Literal Source; 8 | public readonly Identifier? Exported; 9 | 10 | public ExportAllDeclaration(Literal source) : this(source, null) 11 | { 12 | } 13 | 14 | public ExportAllDeclaration(Literal source, Identifier? exported) : base(Nodes.ExportAllDeclaration) 15 | { 16 | Source = source; 17 | Exported = exported; 18 | } 19 | 20 | public override NodeCollection ChildNodes => new(Source, Exported); 21 | 22 | protected internal override void Accept(AstVisitor visitor) 23 | { 24 | visitor.VisitExportAllDeclaration(this); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/TemplateElement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class TemplateElement : Node 6 | { 7 | public readonly TemplateElementValue Value; 8 | public readonly bool Tail; 9 | 10 | public TemplateElement(TemplateElementValue value, bool tail) : base(Nodes.TemplateElement) 11 | { 12 | Value = value; 13 | Tail = tail; 14 | } 15 | 16 | public sealed class TemplateElementValue 17 | { 18 | public string? Cooked; 19 | public string Raw = ""; 20 | } 21 | 22 | public override NodeCollection ChildNodes => NodeCollection.Empty; 23 | 24 | protected internal override void Accept(AstVisitor visitor) 25 | { 26 | visitor.VisitTemplateElement(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Property.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class Property : ClassProperty 6 | { 7 | public readonly bool Method; 8 | public readonly bool Shorthand; 9 | 10 | public Property( 11 | PropertyKind kind, 12 | Expression key, 13 | bool computed, 14 | Expression value, 15 | bool method, 16 | bool shorthand) 17 | : base(Nodes.Property) 18 | { 19 | Key = key; 20 | Computed = computed; 21 | Value = value; 22 | Kind = kind; 23 | Method = method; 24 | Shorthand = shorthand; 25 | } 26 | 27 | protected internal override void Accept(AstVisitor visitor) 28 | { 29 | visitor.VisitProperty(this); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Statements/IfStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Statements; 6 | 7 | internal static partial class IfStatementHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 10 | { 11 | var expr = (IfStatement)statement; 12 | var test = expr.Test; 13 | var onTrue = expr.Consequent; 14 | var onFalse = expr.Alternate; 15 | if (JavascriptTypeUtility 16 | .IsObjectTrue(scriptExecutor.ExecuteExpressionAndGetValue(test, token))) 17 | return scriptExecutor.ExecuteStatement(onTrue, token); 18 | return scriptExecutor.ExecuteStatement(onFalse, token); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/TryStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class TryStatement : Statement 6 | { 7 | public readonly Statement Block; 8 | public readonly CatchClause? Handler; 9 | public readonly Statement? Finalizer; 10 | 11 | public TryStatement( 12 | Statement block, 13 | CatchClause? handler, 14 | Statement? finalizer) : 15 | base(Nodes.TryStatement) 16 | { 17 | Block = block; 18 | Handler = handler; 19 | Finalizer = finalizer; 20 | } 21 | 22 | public override NodeCollection ChildNodes => new(Block, Handler, Finalizer); 23 | 24 | protected internal override void Accept(AstVisitor visitor) 25 | { 26 | visitor.VisitTryStatement(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/IfStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class IfStatement : Statement 6 | { 7 | public readonly Expression Test; 8 | public readonly Statement Consequent; 9 | public readonly Statement? Alternate; 10 | 11 | public IfStatement( 12 | Expression test, 13 | Statement consequent, 14 | Statement? alternate) 15 | : base(Nodes.IfStatement) 16 | { 17 | Test = test; 18 | Consequent = consequent; 19 | Alternate = alternate; 20 | } 21 | 22 | public override NodeCollection ChildNodes => new(Test, Consequent, Alternate); 23 | 24 | protected internal override void Accept(AstVisitor visitor) 25 | { 26 | visitor.VisitIfStatement(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/SwitchStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class SwitchStatement : Statement 6 | { 7 | private readonly NodeList _cases; 8 | 9 | public readonly Expression Discriminant; 10 | 11 | public SwitchStatement(Expression discriminant, in NodeList cases) : base(Nodes.SwitchStatement) 12 | { 13 | Discriminant = discriminant; 14 | _cases = cases; 15 | } 16 | 17 | public ref readonly NodeList Cases => ref _cases; 18 | 19 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Discriminant, _cases); 20 | 21 | protected internal override void Accept(AstVisitor visitor) 22 | { 23 | visitor.VisitSwitchStatement(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Topaz.Test/GlobalThisTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Tenray.Topaz.API; 3 | 4 | namespace Tenray.Topaz.Test; 5 | 6 | public sealed class GlobalThisTests 7 | { 8 | [Test] 9 | public void TestGlobalThis1() 10 | { 11 | var engine = new TopazEngine(); 12 | dynamic model = new JsObject(); 13 | engine.SetValue("globalThis", new GlobalThis(engine.GlobalScope)); 14 | engine.SetValue("model", model); 15 | engine.SetValue("JSON", new JSONObject()); 16 | engine.ExecuteScript(@" 17 | model.a = globalThis.JSON 18 | model.b = globalThis.model 19 | var x = 3 20 | model.c = globalThis.x 21 | "); 22 | Assert.AreEqual("3", ((JSONObject)model.a).stringify(3)); 23 | Assert.AreEqual(model, model.b); 24 | Assert.AreEqual(3, model.c); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/NewExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class NewExpression : Expression 6 | { 7 | private readonly NodeList _arguments; 8 | public readonly Expression Callee; 9 | 10 | public NewExpression( 11 | Expression callee, 12 | in NodeList args) 13 | : base(Nodes.NewExpression) 14 | { 15 | Callee = callee; 16 | _arguments = args; 17 | } 18 | 19 | public ref readonly NodeList Arguments => ref _arguments; 20 | 21 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Callee, Arguments); 22 | 23 | protected internal override void Accept(AstVisitor visitor) 24 | { 25 | visitor.VisitNewExpression(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/AssignmentPatternHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class AssignmentPatternHandler 9 | { 10 | internal async static ValueTask ExecuteAsync( 11 | ScriptExecutor scriptExecutor, 12 | Node expression, CancellationToken token) 13 | { 14 | var expr = (AssignmentPattern)expression; 15 | var left = await scriptExecutor.ExecuteStatementAsync(expr.Left, token); 16 | var right = await scriptExecutor.ExecuteExpressionAndGetValueAsync(expr.Right, token); 17 | scriptExecutor 18 | .SetReferenceValue(left, right); 19 | return right; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/MethodAndParameterInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace Tenray.Topaz.Interop; 6 | 7 | public sealed class MethodAndParameterInfo 8 | { 9 | public readonly IReadOnlyList MethodInfos; 10 | 11 | public readonly ParameterInfo[][] ParameterInfos; 12 | 13 | public bool HasAny => MethodInfos.Count > 0; 14 | 15 | public readonly static MethodAndParameterInfo Empty = 16 | new MethodAndParameterInfo(Array.Empty(), Array.Empty()); 17 | 18 | public MethodAndParameterInfo( 19 | IReadOnlyList methodInfos, 20 | ParameterInfo[][] parameterInfos) 21 | { 22 | MethodInfos = methodInfos; 23 | ParameterInfos = parameterInfos; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/ConditionalExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class ConditionalExpressionHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 10 | { 11 | var expr = (ConditionalExpression)expression; 12 | var test = expr.Test; 13 | var onTrue = expr.Consequent; 14 | var onFalse = expr.Alternate; 15 | if (JavascriptTypeUtility 16 | .IsObjectTrue(scriptExecutor.ExecuteExpressionAndGetValue(test, token))) 17 | return scriptExecutor.ExecuteStatement(onTrue, token); 18 | return scriptExecutor.ExecuteStatement(onFalse, token); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ForOfStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ForOfStatement : Statement 6 | { 7 | public readonly bool Await; 8 | public readonly Node Left; 9 | public readonly Expression Right; 10 | public readonly Statement Body; 11 | 12 | public ForOfStatement( 13 | Node left, 14 | Expression right, 15 | Statement body, 16 | bool _await) : base(Nodes.ForOfStatement) 17 | { 18 | Left = left; 19 | Right = right; 20 | Body = body; 21 | Await = _await; 22 | } 23 | 24 | public override NodeCollection ChildNodes => new(Left, Right, Body); 25 | 26 | protected internal override void Accept(AstVisitor visitor) 27 | { 28 | visitor.VisitForOfStatement(this); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Topaz.Test/Topaz.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | 6 | false 7 | Tenray.Topaz 8 | False 9 | False 10 | False 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ConditionalExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ConditionalExpression : Expression 6 | { 7 | public readonly Expression Test; 8 | public readonly Expression Consequent; 9 | public readonly Expression Alternate; 10 | 11 | public ConditionalExpression( 12 | Expression test, 13 | Expression consequent, 14 | Expression alternate) : base(Nodes.ConditionalExpression) 15 | { 16 | Test = test; 17 | Consequent = consequent; 18 | Alternate = alternate; 19 | } 20 | 21 | public override NodeCollection ChildNodes => new(Test, Consequent, Alternate); 22 | 23 | protected internal override void Accept(AstVisitor visitor) 24 | { 25 | visitor.VisitConditionalExpression(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/SequenceExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class SequenceExpression : Expression 6 | { 7 | private NodeList _expressions; 8 | 9 | public SequenceExpression(in NodeList expressions) : base(Nodes.SequenceExpression) 10 | { 11 | _expressions = expressions; 12 | } 13 | 14 | public ref readonly NodeList Expressions => ref _expressions; 15 | 16 | internal void UpdateExpressions(in NodeList value) 17 | { 18 | _expressions = value; 19 | } 20 | 21 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Expressions); 22 | 23 | protected internal override void Accept(AstVisitor visitor) 24 | { 25 | visitor.VisitSequenceExpression(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/IndexedPropertyMetaGetter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace Tenray.Topaz.Interop; 6 | 7 | public static class IndexedPropertyMetaGetter 8 | { 9 | public static (PropertyInfo[], ParameterInfo[][]) 10 | GetIndexedPropertiesAndParameters(Type type, BindingFlags bindingFlags) 11 | { 12 | // Javascript notation does not let multiple index parameters. 13 | // Retrieve only single parametered indexes. 14 | var props = 15 | type.GetProperties(bindingFlags) 16 | .Where(x => x.GetIndexParameters().Length == 1) 17 | .ToArray(); 18 | var parameters = props 19 | .Select(x => x.GetIndexParameters()) 20 | .ToArray(); 21 | return (props, parameters); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/TopazArrayWrapper.cs: -------------------------------------------------------------------------------- 1 | using Tenray.Topaz.API; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz; 5 | 6 | internal sealed class TopazArrayWrapper 7 | { 8 | internal ScriptExecutor ScriptExecutor { get; } 9 | 10 | internal IJsArray WrappedArray { get; } 11 | 12 | bool isUnwrapped; 13 | 14 | internal TopazArrayWrapper(ScriptExecutor scriptExecutor, IJsArray array) 15 | { 16 | ScriptExecutor = scriptExecutor; 17 | WrappedArray = array; 18 | } 19 | 20 | internal object UnwrapArray() 21 | { 22 | var array = WrappedArray; 23 | if (array == null) 24 | return null; 25 | if (isUnwrapped) 26 | return array; 27 | WrappedArray.UnwrapArray(ScriptExecutor); 28 | isUnwrapped = true; 29 | return array; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/SequenceExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class SequenceExpressionHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 10 | { 11 | var expr = (SequenceExpression)expression; 12 | var list = expr.Expressions; 13 | var len = list.Count; 14 | object result = null; 15 | for (var i = 0; i < len; ++i) 16 | { 17 | token.ThrowIfCancellationRequested(); 18 | result = scriptExecutor.ExecuteStatement(list[i], token); 19 | if (result is ReturnWrapper) 20 | return result; 21 | } 22 | return result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/TopazObjectWrapper.cs: -------------------------------------------------------------------------------- 1 | using Tenray.Topaz.API; 2 | using Tenray.Topaz.Core; 3 | 4 | namespace Tenray.Topaz; 5 | 6 | internal sealed class TopazObjectWrapper 7 | { 8 | internal ScriptExecutor ScriptExecutor { get; } 9 | 10 | internal IJsObject WrappedObject { get; } 11 | 12 | bool isUnwrapped; 13 | 14 | internal TopazObjectWrapper( 15 | ScriptExecutor scriptExecutor, 16 | IJsObject value) 17 | { 18 | ScriptExecutor = scriptExecutor; 19 | WrappedObject = value; 20 | } 21 | 22 | internal IJsObject UnwrapObject() 23 | { 24 | var value = WrappedObject; 25 | if (value == null) 26 | return null; 27 | if (isUnwrapped) 28 | return value; 29 | value.UnwrapObject(ScriptExecutor); 30 | isUnwrapped = true; 31 | return value; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ImportDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ImportDeclaration : Declaration 6 | { 7 | private readonly NodeList _specifiers; 8 | 9 | public readonly Literal Source; 10 | 11 | public ImportDeclaration( 12 | in NodeList specifiers, 13 | Literal source) 14 | : base(Nodes.ImportDeclaration) 15 | { 16 | _specifiers = specifiers; 17 | Source = source; 18 | } 19 | 20 | public ref readonly NodeList Specifiers => ref _specifiers; 21 | 22 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_specifiers, Source); 23 | 24 | protected internal override void Accept(AstVisitor visitor) 25 | { 26 | visitor.VisitImportDeclaration(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/VariableDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class VariableDeclaration : Declaration 6 | { 7 | private readonly NodeList _declarations; 8 | public readonly VariableDeclarationKind Kind; 9 | 10 | public VariableDeclaration( 11 | in NodeList declarations, 12 | VariableDeclarationKind kind) 13 | : base(Nodes.VariableDeclaration) 14 | { 15 | _declarations = declarations; 16 | Kind = kind; 17 | } 18 | 19 | public ref readonly NodeList Declarations => ref _declarations; 20 | 21 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_declarations); 22 | 23 | protected internal override void Accept(AstVisitor visitor) 24 | { 25 | visitor.VisitVariableDeclaration(this); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/CallExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class CallExpression : Expression 6 | { 7 | private readonly NodeList _arguments; 8 | 9 | public readonly Expression Callee; 10 | public readonly bool Optional; 11 | 12 | public CallExpression( 13 | Expression callee, 14 | in NodeList args, 15 | bool optional) : base(Nodes.CallExpression) 16 | { 17 | Callee = callee; 18 | _arguments = args; 19 | Optional = optional; 20 | } 21 | 22 | public ref readonly NodeList Arguments => ref _arguments; 23 | 24 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Callee, _arguments); 25 | 26 | protected internal override void Accept(AstVisitor visitor) 27 | { 28 | visitor.VisitCallExpression(this); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Topaz/Wrappers/TopazMemberAccessor.cs: -------------------------------------------------------------------------------- 1 | using Tenray.Topaz.Core; 2 | 3 | namespace Tenray.Topaz; 4 | 5 | internal sealed class TopazMemberAccessor 6 | { 7 | internal object Instance { get; } 8 | 9 | internal object Property { get; } 10 | 11 | internal bool Computed { get; } 12 | 13 | internal bool Optional { get; } 14 | 15 | internal TopazMemberAccessor(object instance, object property, bool computed, bool optional) 16 | { 17 | Instance = instance; 18 | Property = property; 19 | Computed = computed; 20 | Optional = optional; 21 | } 22 | 23 | internal object Execute(ScriptExecutor executionScope) 24 | { 25 | return executionScope.GetMemberValue(Instance, Property, Computed, Optional); 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return $"{Instance}{(Optional ? "?" : "")}.{Property}"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Topaz.Test/MultithreadingTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.API; 5 | 6 | namespace Tenray.Topaz.Test; 7 | 8 | public sealed class MultithreadingTests 9 | { 10 | [Test] 11 | public void TestParallelLoop() 12 | { 13 | var engine = new TopazEngine(); 14 | dynamic model = new ConcurrentJsObject(); 15 | engine.AddType(typeof(Parallel), "Parallel"); 16 | engine.AddType(typeof(Action), "Action"); 17 | engine.SetValue("model", model); 18 | engine.ExecuteScript(@" 19 | var g = 0; 20 | function f1(i) { 21 | ++g; 22 | } 23 | new Action(f1) 24 | Parallel.For(0, 10000, f1) 25 | var q = 0 26 | Parallel.For(0, 10000, (x) => q = x) 27 | Parallel.For(0, 10000, (x) => q = x) 28 | model.g = g 29 | model.q = q 30 | "); 31 | Assert.IsTrue(100 < model.g); 32 | Assert.IsTrue(100 < model.q); 33 | } 34 | } -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Statements/IfStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Statements; 7 | 8 | internal static partial class IfStatementHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 11 | { 12 | var expr = (IfStatement)statement; 13 | var test = expr.Test; 14 | var onTrue = expr.Consequent; 15 | var onFalse = expr.Alternate; 16 | if (JavascriptTypeUtility 17 | .IsObjectTrue(await 18 | scriptExecutor.ExecuteExpressionAndGetValueAsync(test, token))) 19 | return await scriptExecutor.ExecuteStatementAsync(onTrue, token); 20 | return await scriptExecutor.ExecuteStatementAsync(onFalse, token); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ClassExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ClassExpression : Expression, IClass 6 | { 7 | public readonly Identifier? Id; 8 | Identifier? IClass.Id => Id; 9 | 10 | public readonly Expression? SuperClass; 11 | Expression? IClass.SuperClass => SuperClass; 12 | 13 | public readonly ClassBody Body; 14 | ClassBody IClass.Body => Body; 15 | 16 | public ClassExpression( 17 | Identifier? id, 18 | Expression? superClass, 19 | ClassBody body) : base(Nodes.ClassExpression) 20 | { 21 | Id = id; 22 | SuperClass = superClass; 23 | Body = body; 24 | } 25 | 26 | public override NodeCollection ChildNodes => new(Id, SuperClass, Body); 27 | 28 | protected internal override void Accept(AstVisitor visitor) 29 | { 30 | visitor.VisitClassExpression(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/MemberExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public abstract class MemberExpression : Expression 6 | { 7 | public readonly Expression Object; 8 | public readonly Expression Property; 9 | 10 | // true if an indexer is used and the property to be evaluated 11 | public readonly bool Computed; 12 | public readonly bool Optional; 13 | 14 | protected MemberExpression(Expression obj, Expression property, bool computed, bool optional) 15 | : base(Nodes.MemberExpression) 16 | { 17 | Object = obj; 18 | Property = property; 19 | Computed = computed; 20 | Optional = optional; 21 | } 22 | 23 | public override NodeCollection ChildNodes => new(Object, Property); 24 | 25 | protected internal override void Accept(AstVisitor visitor) 26 | { 27 | visitor.VisitMemberExpression(this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/ConditionalExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class ConditionalExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var expr = (ConditionalExpression)expression; 13 | var test = expr.Test; 14 | var onTrue = expr.Consequent; 15 | var onFalse = expr.Alternate; 16 | if (JavascriptTypeUtility 17 | .IsObjectTrue(await scriptExecutor.ExecuteExpressionAndGetValueAsync(test, token))) 18 | return await scriptExecutor.ExecuteStatementAsync(onTrue, token); 19 | return await scriptExecutor.ExecuteStatementAsync(onFalse, token); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/SequenceExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class SequenceExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var expr = (SequenceExpression)expression; 13 | var list = expr.Expressions; 14 | var len = list.Count; 15 | object result = null; 16 | for (var i = 0; i < len; ++i) 17 | { 18 | token.ThrowIfCancellationRequested(); 19 | result = await scriptExecutor.ExecuteStatementAsync(list[i], token); 20 | if (result is ReturnWrapper) 21 | return result; 22 | } 23 | return result; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ClassDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ClassDeclaration : Declaration, IClass 6 | { 7 | public readonly Identifier? Id; 8 | Identifier? IClass.Id => Id; 9 | 10 | public readonly Expression? SuperClass; // Identifier || CallExpression 11 | Expression? IClass.SuperClass => SuperClass; 12 | 13 | public readonly ClassBody Body; 14 | ClassBody IClass.Body => Body; 15 | 16 | public ClassDeclaration(Identifier? id, Expression? superClass, ClassBody body) : 17 | base(Nodes.ClassDeclaration) 18 | { 19 | Id = id; 20 | SuperClass = superClass; 21 | Body = body; 22 | } 23 | 24 | public override NodeCollection ChildNodes => new(Id, SuperClass, Body); 25 | 26 | protected internal override void Accept(AstVisitor visitor) 27 | { 28 | visitor.VisitClassDeclaration(this); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ForStatement.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ForStatement : Statement 6 | { 7 | // can be a Statement (var i) or an Expression (i=0) 8 | public readonly StatementListItem? Init; 9 | public readonly Expression? Test; 10 | public readonly Expression? Update; 11 | public readonly Statement Body; 12 | 13 | public ForStatement( 14 | StatementListItem? init, 15 | Expression? test, 16 | Expression? update, 17 | Statement body) 18 | : base(Nodes.ForStatement) 19 | { 20 | Init = init; 21 | Test = test; 22 | Update = update; 23 | Body = body; 24 | } 25 | 26 | public override NodeCollection ChildNodes => new(Init, Test, Update, Body); 27 | 28 | protected internal override void Accept(AstVisitor visitor) 29 | { 30 | visitor.VisitForStatement(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Topaz.Test/GenericListTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.Collections.Generic; 3 | using Tenray.Topaz.API; 4 | 5 | namespace Tenray.Topaz.Test; 6 | 7 | public sealed class GenericListTests 8 | { 9 | [Test] 10 | public void TestList1() 11 | { 12 | var engine = new TopazEngine(); 13 | dynamic model = new JsObject(); 14 | engine.SetValue("model", model); 15 | var list = new List() 16 | { 17 | 1,2,3,4,5 18 | }; 19 | engine.SetValue("list", list); 20 | engine.ExecuteScript(@" 21 | model.a1 = list[0] 22 | model.a2 = list['0'] 23 | list[1] = 8 24 | model.a3 = list[1] 25 | list.Add(9) 26 | model.a4 = list[5] 27 | model.a5 = list.Count 28 | "); 29 | Assert.AreEqual(1, model.a1); 30 | Assert.AreEqual(1, model.a2); 31 | Assert.AreEqual(8, model.a3); 32 | Assert.AreEqual(9, model.a4); 33 | Assert.AreEqual(6, model.a5); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/TemplateLiteral.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class TemplateLiteral : Expression 6 | { 7 | private readonly NodeList _quasis; 8 | private readonly NodeList _expressions; 9 | 10 | public TemplateLiteral( 11 | in NodeList quasis, 12 | in NodeList expressions) 13 | : base(Nodes.TemplateLiteral) 14 | { 15 | _quasis = quasis; 16 | _expressions = expressions; 17 | } 18 | 19 | public ref readonly NodeList Quasis => ref _quasis; 20 | public ref readonly NodeList Expressions => ref _expressions; 21 | 22 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_quasis, _expressions); 23 | 24 | protected internal override void Accept(AstVisitor visitor) 25 | { 26 | visitor.VisitTemplateLiteral(this); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ArrowParameterPlaceHolder.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ArrowParameterPlaceHolder : Expression 6 | { 7 | public static readonly ArrowParameterPlaceHolder Empty = new(new NodeList(), false); 8 | 9 | private readonly NodeList _params; 10 | 11 | public ArrowParameterPlaceHolder( 12 | in NodeList parameters, 13 | bool async) : 14 | base(Nodes.ArrowParameterPlaceHolder) 15 | { 16 | Async = async; 17 | _params = parameters; 18 | } 19 | 20 | public ref readonly NodeList Params => ref _params; 21 | 22 | public bool Async { get; } 23 | 24 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(_params); 25 | 26 | protected internal override void Accept(AstVisitor visitor) 27 | { 28 | visitor.VisitArrowParameterPlaceHolder(this); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Topaz/TopazEngineSetup.cs: -------------------------------------------------------------------------------- 1 | using Tenray.Topaz.Interop; 2 | using Tenray.Topaz.Options; 3 | 4 | namespace Tenray.Topaz; 5 | 6 | /// 7 | /// Initialization properties for TopazEngine constructor. 8 | /// If you don't set some property in the setup, TopazEngine will use default implementation. 9 | /// 10 | public sealed class TopazEngineSetup 11 | { 12 | public bool IsThreadSafe { get; set; } = true; 13 | 14 | public TopazEngineOptions Options { get; set; } 15 | 16 | public IObjectProxyRegistry ObjectProxyRegistry { get; set; } 17 | 18 | public IObjectProxy DefaultObjectProxy { get; set; } 19 | 20 | public IDelegateInvoker DelegateInvoker { get; set; } 21 | 22 | public IMemberAccessPolicy MemberAccessPolicy { get; set; } 23 | 24 | public IValueConverter ValueConverter { get; set; } 25 | 26 | public IMemberInfoProvider MemberInfoProvider { get; set; } 27 | 28 | public IAwaitExpressionHandler AwaitExpressionHandler { get; set; } 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] The title of the bug" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior with sample source code. 15 | If you can not reproduce the issue with a sample source code or unit test, 16 | describe all the configuration options and your use case step by step. 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Desktop (please complete the following information):** 25 | - Operating System: [Linux, MacOS, Windows] 26 | - Topaz Version: [Make sure that your bug is not reproducible with latest version.] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | paths-ignore: 7 | - '*.md' 8 | pull_request: 9 | branches: ['main'] 10 | paths-ignore: 11 | - '*.md' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v3 20 | with: 21 | dotnet-version: | 22 | 9.0.x 23 | 8.0.x 24 | 7.0.x 25 | 6.0.x 26 | - name: Display dotnet version 27 | run: dotnet --version 28 | - name: Restore dependencies 29 | working-directory: ./src 30 | run: dotnet restore 31 | - name: Build 32 | run: dotnet build --configuration Release --no-restore 33 | working-directory: ./src 34 | - name: Test 35 | run: dotnet test --no-build --configuration Release --verbosity normal 36 | working-directory: ./src 37 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/TemplateLiteralHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Text; 3 | using System.Threading; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class TemplateLiteralHandler 9 | { 10 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var literal = (TemplateLiteral)expression; 13 | var quasis = literal.Quasis; 14 | var list = literal.Expressions; 15 | var quasisLen = quasis.Count; 16 | var listLen = list.Count; 17 | var sb = new StringBuilder(); 18 | for (var i = 0; i < quasisLen; i++) 19 | { 20 | var quasi = quasis[i]; 21 | sb.Append(quasi.Value.Cooked); 22 | if (i < listLen) 23 | { 24 | sb.Append(scriptExecutor.ExecuteExpressionAndGetValue(list[i], token)); 25 | } 26 | } 27 | return sb.ToString(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Topaz/API/object/normal/JsObject.Dynamic.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Dynamic; 3 | using System.Linq; 4 | using System.Text.Json; 5 | 6 | namespace Tenray.Topaz.API; 7 | 8 | public partial class JsObject : DynamicObject 9 | { 10 | public override bool TrySetMember(SetMemberBinder binder, object value) 11 | { 12 | SetValue(binder.Name, value); 13 | return true; 14 | } 15 | 16 | public override bool TryGetMember(GetMemberBinder binder, out object result) 17 | { 18 | return TryGetValue(binder.Name, out result); 19 | } 20 | 21 | public override IEnumerable GetDynamicMemberNames() 22 | { 23 | return GetObjectKeys().Cast(); 24 | } 25 | 26 | protected static object ConvertJsonElementToJsObject(object value) 27 | { 28 | if (value == null) 29 | return null; 30 | if (value is JsonElement jsonElement) 31 | return jsonElement.ConvertToJsObject(); 32 | return value; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ExportNamedDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ExportNamedDeclaration : ExportDeclaration 6 | { 7 | private readonly NodeList _specifiers; 8 | 9 | public readonly StatementListItem? Declaration; 10 | public readonly Literal? Source; 11 | 12 | public ExportNamedDeclaration( 13 | StatementListItem? declaration, 14 | in NodeList specifiers, 15 | Literal? source) 16 | : base(Nodes.ExportNamedDeclaration) 17 | { 18 | Declaration = declaration; 19 | _specifiers = specifiers; 20 | Source = source; 21 | } 22 | 23 | public ref readonly NodeList Specifiers => ref _specifiers; 24 | 25 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Declaration, _specifiers, Source); 26 | 27 | protected internal override void Accept(AstVisitor visitor) 28 | { 29 | visitor.VisitExportNamedDeclaration(this); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/DictionaryObjectProxyRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Tenray.Topaz.API; 4 | 5 | namespace Tenray.Topaz.Interop; 6 | 7 | public sealed class DictionaryObjectProxyRegistry : IObjectProxyRegistry 8 | { 9 | readonly Dictionary proxyRegistryMap = new(); 10 | 11 | public void AddObjectProxy(Type type, IObjectProxy proxy) 12 | { 13 | proxyRegistryMap.Add(type, proxy); 14 | } 15 | 16 | public void RemoveObjectProxy(Type type) 17 | { 18 | proxyRegistryMap.Remove(type); 19 | } 20 | 21 | public bool TryGetObjectProxy(object instance, out IObjectProxy proxy) 22 | { 23 | if (instance == null) 24 | { 25 | proxy = null; 26 | return false; 27 | } 28 | if (instance is JsArray) 29 | { 30 | proxy = JsArrayObjectProxy.Instance; 31 | return true; 32 | } 33 | return proxyRegistryMap.TryGetValue(instance.GetType(), out proxy); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Topaz/API/object/concurrent/ConcurrentJsObject.Dynamic.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Dynamic; 3 | using System.Linq; 4 | using System.Text.Json; 5 | 6 | namespace Tenray.Topaz.API; 7 | 8 | public partial class ConcurrentJsObject : DynamicObject 9 | { 10 | public override bool TrySetMember(SetMemberBinder binder, object value) 11 | { 12 | SetValue(binder.Name, value); 13 | return true; 14 | } 15 | 16 | public override bool TryGetMember(GetMemberBinder binder, out object result) 17 | { 18 | return TryGetValue(binder.Name, out result); 19 | } 20 | 21 | public override IEnumerable GetDynamicMemberNames() 22 | { 23 | return dictionary.Keys.ToArray(); 24 | } 25 | 26 | protected static object ConvertJsonElementToConcurrentJsObject(object value) 27 | { 28 | if (value == null) 29 | return null; 30 | if (value is JsonElement jsonElement) 31 | return jsonElement.ConvertToConcurrentJsObject(); 32 | return value; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Topaz.Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | Tenray.Topaz 7 | False 8 | False 9 | False 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/TemplateLiteralHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Text; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Tenray.Topaz.Core; 6 | 7 | namespace Tenray.Topaz.Expressions; 8 | 9 | internal static partial class TemplateLiteralHandler 10 | { 11 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 12 | { 13 | var literal = (TemplateLiteral)expression; 14 | var quasis = literal.Quasis; 15 | var list = literal.Expressions; 16 | var quasisLen = quasis.Count; 17 | var listLen = list.Count; 18 | var sb = new StringBuilder(); 19 | for (var i = 0; i < quasisLen; i++) 20 | { 21 | var quasi = quasis[i]; 22 | sb.Append(quasi.Value.Cooked); 23 | if (i < listLen) 24 | { 25 | sb.Append(await scriptExecutor.ExecuteExpressionAndGetValueAsync(list[i], token)); 26 | } 27 | } 28 | return sb.ToString(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Topaz.Test/CancellationTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Threading; 4 | 5 | namespace Tenray.Topaz.Test; 6 | 7 | public sealed class CancellationTests 8 | { 9 | [Test] 10 | public void CancelInfiniteWhileLoop() 11 | { 12 | var engine = new TopazEngine(); 13 | var thrown = 0; 14 | Func getSource = 15 | () => new CancellationTokenSource(TimeSpan.FromMilliseconds(250)); 16 | 17 | try 18 | { 19 | using var source = getSource(); 20 | engine.ExecuteScript(@" 21 | while(true) { } 22 | ", 23 | source.Token); 24 | } 25 | catch (OperationCanceledException) 26 | { 27 | ++thrown; 28 | } 29 | 30 | try 31 | { 32 | using var source = getSource(); 33 | engine.ExecuteScript(@" 34 | while(true); 35 | ", 36 | source.Token); 37 | } 38 | catch (OperationCanceledException) 39 | { 40 | ++thrown; 41 | } 42 | 43 | Assert.AreEqual(thrown, 2); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/ParserException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esprima; 4 | 5 | public sealed class ParserException : Exception 6 | { 7 | public ParseError? Error { get; } 8 | 9 | public string? Description => Error?.Description; 10 | public string? SourceText => Error?.Source; 11 | public int Index => Error?.Index ?? -1; 12 | public int LineNumber => Error?.LineNumber ?? 0; 13 | public int Column => Error?.Column ?? 0; 14 | 15 | public ParserException() : this(null, null, null) 16 | { 17 | } 18 | 19 | public ParserException(string? message, Exception innerException) : this(message, null, innerException) 20 | { 21 | } 22 | 23 | public ParserException(ParseError error) : this(null, error) 24 | { 25 | } 26 | 27 | public ParserException(string? message, ParseError error) : this(message, error, null) 28 | { 29 | } 30 | 31 | public ParserException(string? message, ParseError? error = null, Exception? innerException = null) 32 | : base(message ?? error?.ToString(), innerException) 33 | { 34 | Error = error; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Topaz.Test/DictionaryTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.Collections.Generic; 3 | using Tenray.Topaz.API; 4 | 5 | namespace Tenray.Topaz.Test; 6 | 7 | public sealed class DictionaryTests 8 | { 9 | [Test] 10 | public void TestDictionary1() 11 | { 12 | var engine = new TopazEngine(); 13 | dynamic model = new JsObject(); 14 | engine.SetValue("model", model); 15 | var dic = new Dictionary 16 | { 17 | { "a", 1 }, 18 | { "b", 2 }, 19 | { "c", 3 } 20 | }; 21 | engine.SetValue("dic", dic); 22 | engine.ExecuteScript(@" 23 | model.a1 = dic.a 24 | model.a2 = dic['a'] 25 | dic.a = 3 26 | model.a3 = dic.a 27 | dic['a'] = 4 28 | model.a4 = dic.a 29 | model.a5 = dic.ContainsKey('b') 30 | dic.Remove('b') 31 | model.a6 = dic.ContainsKey('b') 32 | "); 33 | Assert.AreEqual(1, model.a1); 34 | Assert.AreEqual(1, model.a2); 35 | Assert.AreEqual(3, model.a3); 36 | Assert.AreEqual(4, model.a4); 37 | Assert.IsTrue(model.a5); 38 | Assert.IsFalse(model.a6); 39 | } 40 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/ParseError.cs: -------------------------------------------------------------------------------- 1 | using static Esprima.EsprimaExceptionHelper; 2 | 3 | namespace Esprima; 4 | 5 | public sealed class ParseError 6 | { 7 | public string Description { get; } 8 | public string? Source { get; } 9 | 10 | public bool IsIndexDefined => Index >= 0; 11 | public int Index { get; } 12 | 13 | public bool IsPositionDefined => Position.Line > 0; 14 | public Position Position { get; } 15 | public int LineNumber => Position.Line; 16 | public int Column => Position.Column; 17 | 18 | public ParseError(string description) : 19 | this(description, null, -1, default) 20 | { 21 | } 22 | 23 | public ParseError(string description, 24 | string? source, int index, Position position) 25 | { 26 | Description = description ?? ThrowArgumentNullException(nameof(description)); 27 | Source = source; 28 | Index = index; 29 | Position = position; 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return LineNumber > 0 ? $"Line {LineNumber}: {Description}" : Description; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Statements/BlockStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Statements; 6 | 7 | internal static partial class BlockStatementHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 10 | { 11 | var expr = (BlockStatement)statement; 12 | var list = expr.Body; 13 | var len = list.Count; 14 | scriptExecutor = scriptExecutor.NewBlockScope(); 15 | for (var i = 0; i < len; ++i) 16 | { 17 | var el = list[i]; 18 | var result = scriptExecutor.ExecuteStatement(el, token); 19 | if (result is ReturnWrapper || 20 | result is BreakWrapper || 21 | result is ContinueWrapper) 22 | { 23 | scriptExecutor.ReturnToPool(); 24 | return result; 25 | } 26 | } 27 | var returnValue = scriptExecutor.GetNullOrUndefined(); 28 | scriptExecutor.ReturnToPool(); 29 | return returnValue; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ahmed Yasin Koculu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Range.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Esprima.Ast; 5 | 6 | public readonly struct Range : IEquatable 7 | { 8 | public readonly int Start; 9 | public readonly int End; 10 | 11 | public Range(int start, int end) 12 | { 13 | Start = start; 14 | End = end; 15 | } 16 | 17 | public bool Equals(Range other) 18 | { 19 | return Start == other.Start && End == other.End; 20 | } 21 | 22 | public override bool Equals(object obj) 23 | { 24 | return obj is Range other && Equals(other); 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return unchecked((Start * 397) ^ End); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return string.Format(CultureInfo.InvariantCulture, "[{0}..{1})", Start, End); 35 | } 36 | 37 | public static bool operator ==(Range left, Range right) 38 | { 39 | return left.Equals(right); 40 | } 41 | 42 | public static bool operator !=(Range left, Range right) 43 | { 44 | return !left.Equals(right); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/ErrorHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima; 2 | 3 | /// 4 | /// Default error handling logic for Esprima. 5 | /// 6 | public class ErrorHandler : IErrorHandler 7 | { 8 | public string? Source { get; set; } 9 | public bool Tolerant { get; set; } 10 | 11 | public virtual void RecordError(ParserException error) 12 | { 13 | } 14 | 15 | public void Tolerate(ParserException error) 16 | { 17 | if (Tolerant) 18 | { 19 | RecordError(error); 20 | } 21 | else 22 | { 23 | throw error; 24 | } 25 | } 26 | 27 | public ParserException CreateError(int index, int line, int column, string message) 28 | { 29 | return new ParserException(new ParseError(message, Source, index, new Position(line, column))); 30 | } 31 | 32 | public void TolerateError(int index, int line, int column, string message) 33 | { 34 | var error = CreateError(index, line, column, message); 35 | if (Tolerant) 36 | { 37 | RecordError(error); 38 | } 39 | else 40 | { 41 | throw error; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Statements/BlockStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Statements; 7 | 8 | internal static partial class BlockStatementHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 11 | { 12 | var expr = (BlockStatement)statement; 13 | var list = expr.Body; 14 | var len = list.Count; 15 | scriptExecutor = scriptExecutor.NewBlockScope(); 16 | for (var i = 0; i < len; ++i) 17 | { 18 | var el = list[i]; 19 | var result = await scriptExecutor.ExecuteStatementAsync(el, token); 20 | if (result is ReturnWrapper || 21 | result is BreakWrapper || 22 | result is ContinueWrapper) 23 | { 24 | scriptExecutor.ReturnToPool(); 25 | return result; 26 | } 27 | } 28 | var returnValue = scriptExecutor.GetNullOrUndefined(); 29 | scriptExecutor.ReturnToPool(); 30 | return returnValue; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/DynamicObjectKeysGetter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Tenray.Topaz.API; 6 | 7 | namespace Tenray.Topaz.Interop; 8 | 9 | public static class DynamicObjectKeysGetter 10 | { 11 | public static IEnumerable GetObjectKeys(object obj) 12 | { 13 | if (obj == null) 14 | return Array.Empty(); 15 | 16 | if (obj is IJsObject jsObject) 17 | { 18 | return jsObject.GetObjectKeys(); 19 | } 20 | 21 | if (obj is IList list) 22 | return Enumerable.Range(0, list.Count); 23 | 24 | if (obj is IDictionary dic) 25 | return dic.Keys; 26 | 27 | return obj.GetType() 28 | .GetMembers( 29 | BindingFlags.Public | 30 | BindingFlags.Instance | 31 | BindingFlags.GetField | 32 | BindingFlags.GetProperty) 33 | .Where(x => !( 34 | x is PropertyInfo pi && 35 | (pi.GetMethod == null || pi.GetMethod.IsPrivate) 36 | )) 37 | .Select(x => x.Name) 38 | .Distinct() 39 | .ToArray(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Benchmark2.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Jint; 3 | using Microsoft.ClearScript.V8; 4 | 5 | namespace Tenray.Topaz.Benchmark; 6 | 7 | public class Benchmark2 8 | { 9 | public string Code = @" 10 | for (var i = 0.0 ; i < 1000000; ++i) { 11 | model.Value++; 12 | } 13 | "; 14 | 15 | public class Model 16 | { 17 | public int Value; 18 | } 19 | 20 | [Benchmark] 21 | public void RunTopaz() 22 | { 23 | var topazEngine = new TopazEngine(new TopazEngineSetup 24 | { 25 | IsThreadSafe = true, 26 | }); 27 | var model = new Model(); 28 | topazEngine.SetValue("model", model); 29 | topazEngine.ExecuteScript(Code); 30 | } 31 | 32 | // [Benchmark] 33 | public void RunV8Engine() 34 | { 35 | var v8Engine = new V8ScriptEngine(); 36 | var model = new Model(); 37 | v8Engine.AddHostObject("model", model); 38 | v8Engine.Execute(Code); 39 | } 40 | 41 | [Benchmark] 42 | public void RunJint() 43 | { 44 | var jintEngine = new Engine(); 45 | var model = new Model(); 46 | jintEngine.SetValue("model", model); 47 | jintEngine.Execute(Code); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/FunctionExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class FunctionExpression : Expression, IFunction 6 | { 7 | private readonly NodeList _parameters; 8 | 9 | public FunctionExpression( 10 | Identifier? id, 11 | in NodeList parameters, 12 | BlockStatement body, 13 | bool generator, 14 | bool strict, 15 | bool async) : 16 | base(Nodes.FunctionExpression) 17 | { 18 | Id = id; 19 | _parameters = parameters; 20 | Body = body; 21 | Generator = generator; 22 | Expression = false; 23 | Strict = strict; 24 | Async = async; 25 | } 26 | 27 | public Identifier? Id { get; } 28 | public ref readonly NodeList Params => ref _parameters; 29 | public Node Body { get; } 30 | public bool Generator { get; } 31 | public bool Expression { get; } 32 | public bool Async { get; } 33 | public bool Strict { get; } 34 | 35 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Id, _parameters, Body); 36 | 37 | protected internal override void Accept(AstVisitor visitor) 38 | { 39 | visitor.VisitFunctionExpression(this); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/ArrowFunctionExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class ArrowFunctionExpression : Expression, IFunction 6 | { 7 | private readonly NodeList _params; 8 | 9 | public ArrowFunctionExpression( 10 | in NodeList parameters, 11 | Node body, 12 | bool expression, 13 | bool strict, 14 | bool async) 15 | : base(Nodes.ArrowFunctionExpression) 16 | { 17 | Id = null; 18 | _params = parameters; 19 | Body = body; 20 | Generator = false; 21 | Expression = expression; 22 | Strict = strict; 23 | Async = async; 24 | } 25 | 26 | public Identifier? Id { get; } 27 | public Node Body { get; } // : BlockStatement | Expression; 28 | public bool Generator { get; } 29 | public bool Expression { get; } 30 | public bool Strict { get; } 31 | public bool Async { get; } 32 | 33 | public ref readonly NodeList Params => ref _params; 34 | 35 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Params, Body); 36 | 37 | protected internal override void Accept(AstVisitor visitor) 38 | { 39 | visitor.VisitArrowFunctionExpression(this); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/publish-nuget.yml: -------------------------------------------------------------------------------- 1 | name: Publish Nuget Package 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | name: 7 | description: 'When you press run workflow, the nuget package will be published.' 8 | default: 'I understand.' 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Setup .NET 15 | uses: actions/setup-dotnet@v3 16 | with: 17 | dotnet-version: | 18 | 9.0.x 19 | 8.0.x 20 | 7.0.x 21 | 6.0.x 22 | - name: Display dotnet version 23 | run: dotnet --version 24 | - name: Restore dependencies 25 | working-directory: ./src 26 | run: dotnet restore 27 | - name: Build 28 | run: dotnet build --configuration Release --no-restore 29 | working-directory: ./src 30 | - name: Test 31 | run: dotnet test --no-build --configuration Release --verbosity normal 32 | working-directory: ./src 33 | - name: Publish the package to nuget.org 34 | run: dotnet nuget push Topaz/bin/Release/*.nupkg -k $NUGET_AUTH_TOKEN -s https://api.nuget.org/v3/index.json 35 | env: 36 | NUGET_AUTH_TOKEN: ${{ secrets.NUGET_TOKEN }} 37 | working-directory: ./src 38 | -------------------------------------------------------------------------------- /src/Topaz.Test/AsyncFunctionTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Net.Http; 4 | using Tenray.Topaz.API; 5 | 6 | namespace Tenray.Topaz.Test; 7 | 8 | public sealed class AsyncFunctionTests 9 | { 10 | [Test] 11 | public void HttpGetAsync() 12 | { 13 | var engine = new TopazEngine(); 14 | dynamic model = new JsObject(); 15 | engine.AddType("HttpClient"); 16 | engine.AddType(typeof(Console), "Console"); 17 | engine.SetValue("model", model); 18 | engine.AddType("Uri"); 19 | var task = engine.ExecuteScriptAsync(@" 20 | async function httpGet(url) { 21 | try { 22 | var httpClient = new HttpClient() 23 | var response = await httpClient.GetAsync(url) 24 | return await response.Content.ReadAsStringAsync() 25 | } 26 | catch (err) { 27 | Console.WriteLine('Caught Error:\n' + err) 28 | } 29 | finally { 30 | httpClient.Dispose() 31 | } 32 | } 33 | const html = model.html = await httpGet('http://example.com') 34 | Console.WriteLine(html); 35 | "); 36 | task.Wait(); 37 | Assert.IsNotNull(model.html); 38 | Assert.IsTrue(model.html.GetType() == typeof(string)); 39 | Assert.IsTrue(model.html.StartsWith("")); 40 | } 41 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/FunctionDeclaration.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | 3 | namespace Esprima.Ast; 4 | 5 | public sealed class FunctionDeclaration : Declaration, IFunction 6 | { 7 | private readonly NodeList _parameters; 8 | 9 | public FunctionDeclaration( 10 | Identifier? id, 11 | in NodeList parameters, 12 | BlockStatement body, 13 | bool generator, 14 | bool strict, 15 | bool async) 16 | : base(Nodes.FunctionDeclaration) 17 | { 18 | Id = id; 19 | _parameters = parameters; 20 | Body = body; 21 | Generator = generator; 22 | Expression = false; 23 | Strict = strict; 24 | Async = async; 25 | } 26 | 27 | public Identifier? Id { get; } 28 | 29 | public Node Body { get; } 30 | public bool Generator { get; } 31 | public bool Expression { get; } 32 | public bool Async { get; } 33 | public bool Strict { get; } 34 | 35 | public ref readonly NodeList Params => ref _parameters; 36 | 37 | public override NodeCollection ChildNodes => GenericChildNodeYield.Yield(Id, _parameters, Body); 38 | 39 | protected internal override void Accept(AstVisitor visitor) 40 | { 41 | visitor.VisitFunctionDeclaration(this); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/UnaryExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class UnaryExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var unaryExpr = (UnaryExpression)expression; 13 | var expr = await scriptExecutor.ExecuteStatementAsync(unaryExpr.Argument, token); 14 | var value = scriptExecutor.GetValue(expr); 15 | var unaryOperator = unaryExpr.Operator; 16 | if (unaryOperator == UnaryOperator.Delete) 17 | { 18 | scriptExecutor.SetReferenceValue(expr, scriptExecutor.GetNullOrUndefined()); 19 | return scriptExecutor.GetNullOrUndefined(); 20 | } 21 | var newValue = ExecuteUnaryOperator(scriptExecutor, unaryOperator, value); 22 | if (unaryOperator == UnaryOperator.Increment || 23 | unaryOperator == UnaryOperator.Decrement || 24 | unaryOperator == UnaryOperator.Delete) 25 | scriptExecutor.SetReferenceValue(expr, newValue); 26 | return unaryExpr.Prefix ? newValue : value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Topaz/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Ahmed Yasin Koculu 5 | Ahmed Yasin Koculu 6 | Topaz 7 | Topaz 8 | 1.4.1.0 9 | 1.4.1.0 10 | Ahmed Yasin Koculu 11 | Topaz 12 | Multithreaded Javascript Engine for .NET 13 | High performance, lightweight, customizable Javascript Engine. 14 | high-performance, javascript, engine, interpreter, es5, es2015, es6, ecmascript, interop 15 | 16 | koculu 17 | 18 | https://github.com/koculu/topaz 19 | MIT 20 | 21 | true 22 | true 23 | true 24 | portable 25 | snupkg 26 | false 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Benchmark4.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Jint; 3 | using Microsoft.ClearScript.V8; 4 | 5 | namespace Tenray.Topaz.Benchmark; 6 | 7 | public class Benchmark4 8 | { 9 | public string Code = @" 10 | for (var i = 0.0 ; i < 1000000; ++i) { 11 | model.Increment(); 12 | } 13 | "; 14 | public class Model 15 | { 16 | public int Value; 17 | public void Increment() 18 | { 19 | ++Value; 20 | } 21 | } 22 | 23 | [Benchmark] 24 | public void RunTopaz() 25 | { 26 | var topazEngine = new TopazEngine(new TopazEngineSetup 27 | { 28 | IsThreadSafe = false 29 | }); 30 | var model = new Model(); 31 | topazEngine.SetValue("model", model); 32 | topazEngine.ExecuteScript(Code); 33 | } 34 | 35 | //[Benchmark] 36 | public void RunV8Engine() 37 | { 38 | var v8Engine = new V8ScriptEngine(); 39 | var model = new Model(); 40 | v8Engine.AddHostObject("model", model); 41 | v8Engine.Execute(Code); 42 | } 43 | 44 | [Benchmark] 45 | public void RunJint() 46 | { 47 | var jintEngine = new Engine(); 48 | var model = new Model(); 49 | jintEngine.SetValue("model", model); 50 | jintEngine.Execute(Code); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Topaz/Options/AssignmentWithoutDefinitionBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Options; 2 | 3 | public enum AssignmentWithoutDefinitionBehavior 4 | { 5 | /// 6 | /// Suggested and default parameter 7 | /// 8 | DefineAsVarInExecutionScope, 9 | 10 | /// 11 | /// ECMA behavior, not good for multithreaded execution environment 12 | /// 13 | DefineAsVarInGlobalScope, 14 | 15 | /// 16 | /// Slightly adjusted ECMA behavior to protect global scope 17 | /// from frequent changes. Variable definition occurs in 18 | /// the top level scope right before global scope. 19 | /// Since Function scope (Closure) is frozen, 20 | /// if the first child of Global Scope is Function Scope, 21 | /// then variable definition occurs in FunctionInnerBlock Scope. 22 | /// 23 | DefineAsVarInFirstChildOfGlobalScope, 24 | 25 | /// 26 | /// Automatically assigns a let variable in the execution scope. 27 | /// Please note that let variables are expensive, 28 | /// because they are captured (cloned) in function closures. 29 | /// 30 | DefineAsLetInExecutionScope, 31 | 32 | /// 33 | /// Strict mode behavior which throws exception. 34 | /// 35 | ThrowException 36 | } 37 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/LiteralHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima; 2 | using Esprima.Ast; 3 | using Tenray.Topaz.Core; 4 | 5 | namespace Tenray.Topaz.Expressions; 6 | 7 | internal static partial class LiteralHandler 8 | { 9 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression) 10 | { 11 | var literal = (Literal)expression; 12 | if (literal.TokenType == TokenType.BooleanLiteral) 13 | { 14 | return literal.NumericValue > 0.0; 15 | } 16 | 17 | if (literal.TokenType == TokenType.NullLiteral) 18 | { 19 | return null; 20 | } 21 | 22 | if (literal.TokenType == TokenType.NumericLiteral) 23 | { 24 | if (scriptExecutor.Options.LiteralNumbersAreConvertedToDouble) 25 | return literal.NumericValue; 26 | var value = literal.Value; 27 | if (value is long d) 28 | { 29 | if (d < int.MinValue || d > int.MaxValue) 30 | return d; 31 | return (int)d; 32 | } 33 | return value; 34 | } 35 | 36 | if (literal.TokenType == TokenType.StringLiteral) 37 | { 38 | return literal.StringValue; 39 | } 40 | return scriptExecutor.GetNullOrUndefined(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Topaz/Core/Variable.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Core; 2 | 3 | internal sealed class Variable 4 | { 5 | internal object Value; 6 | 7 | internal VariableKind Kind; 8 | 9 | internal VariableState State; 10 | 11 | internal bool ShouldCapture; 12 | 13 | internal Variable(object value, 14 | VariableKind kind, 15 | VariableState state = VariableState.None) 16 | { 17 | Value = value; 18 | Kind = kind; 19 | State = state; 20 | ShouldCapture = kind == VariableKind.Let; 21 | } 22 | 23 | internal void SetValueAndKind( 24 | object value, VariableKind kind, VariableState state) 25 | { 26 | Value = value; 27 | if (kind == Kind) 28 | return; 29 | State = state; 30 | Kind = kind; 31 | ShouldCapture = kind == VariableKind.Let; 32 | } 33 | 34 | internal void SetKind(VariableKind kind) 35 | { 36 | if (kind == Kind) 37 | return; 38 | Kind = kind; 39 | ShouldCapture = kind == VariableKind.Let; 40 | } 41 | 42 | internal bool IsConst => Kind == VariableKind.Const; 43 | 44 | internal bool IsLet => Kind == VariableKind.Let; 45 | 46 | internal bool IsVar => Kind == VariableKind.Var; 47 | 48 | internal bool IsCaptured => State == VariableState.Captured; 49 | } 50 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/TaggedTemplateExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class TaggedTemplateExpressionHandler 9 | { 10 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var tagged = (TaggedTemplateExpression)expression; 13 | var tag = tagged.Tag; 14 | var literal = tagged.Quasi; 15 | var quasis = literal.Quasis; 16 | var list = literal.Expressions; 17 | var quasisLen = quasis.Count; 18 | var listLen = list.Count; 19 | var strings = new List(); 20 | for (var i = 0; i < quasisLen; i++) 21 | { 22 | var quasi = quasis[i]; 23 | strings.Add(quasi.Value.Cooked); 24 | } 25 | var args = new List(); 26 | args.Add(strings); 27 | for (var i = 0; i < listLen; ++i) 28 | { 29 | args.Add(scriptExecutor.ExecuteExpressionAndGetValue(list[i], token)); 30 | } 31 | var callee = scriptExecutor.ExecuteExpressionAndGetValue(tag, token); 32 | return scriptExecutor.CallFunction(callee, args.ToArray(), false, token); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Topaz.Test/WhileLoopTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Tenray.Topaz.API; 3 | 4 | namespace Tenray.Topaz.Test; 5 | 6 | public sealed class WhileLoopTests 7 | { 8 | [Test] 9 | public void WhileLoop() 10 | { 11 | var engine = new TopazEngine(); 12 | dynamic model = new JsObject(); 13 | engine.SetValue("model", model); 14 | engine.ExecuteScript(@" 15 | let i = 0 16 | while (i < 10) { 17 | ++i; 18 | const i = 4; 19 | } 20 | var j = 0 21 | while (j < 10) { 22 | ++j; 23 | if (j < 6) continue; 24 | else if (j == 6) break; 25 | j += 10; 26 | const i = 4; 27 | } 28 | model.i = i 29 | model.j = j 30 | "); 31 | Assert.AreEqual(10, model.i); 32 | Assert.AreEqual(6, model.j); 33 | } 34 | 35 | [Test] 36 | public void DoWhileLoop() 37 | { 38 | var engine = new TopazEngine(); 39 | dynamic model = new JsObject(); 40 | engine.SetValue("model", model); 41 | engine.ExecuteScript(@" 42 | let i = 0 43 | do { 44 | ++i; 45 | const i = 4; 46 | } 47 | while (i < 10) 48 | var j = 0 49 | do { 50 | ++j; 51 | if (j < 6) continue; 52 | else if (j == 6) break; 53 | j += 10; 54 | const i = 4; 55 | } while (j < 10) 56 | model.i = i 57 | model.j = j 58 | "); 59 | Assert.AreEqual(10, model.i); 60 | Assert.AreEqual(6, model.j); 61 | } 62 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/EsprimaExceptionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Esprima; 4 | 5 | internal static class EsprimaExceptionHelper 6 | { 7 | public static void ThrowIndexOutOfRangeException() 8 | { 9 | throw new IndexOutOfRangeException(); 10 | } 11 | 12 | public static T ThrowArgumentOutOfRangeException(string paramName, object actualValue, string? message = null) 13 | { 14 | throw new ArgumentOutOfRangeException(paramName, actualValue, message); 15 | } 16 | 17 | public static void ThrowArgumentOutOfRangeException(string paramName, object actualValue, string? message = null) 18 | { 19 | throw new ArgumentOutOfRangeException(paramName, actualValue, message); 20 | } 21 | 22 | public static T ThrowInvalidOperationException(string? message = null) 23 | { 24 | throw new InvalidOperationException(message); 25 | } 26 | 27 | public static void ThrowInvalidOperationException(string? message = null) 28 | { 29 | throw new InvalidOperationException(message); 30 | } 31 | 32 | public static void ThrowArgumentNullException(string message) 33 | { 34 | throw new ArgumentNullException(message); 35 | } 36 | 37 | public static T ThrowArgumentNullException(string message) 38 | { 39 | throw new ArgumentNullException(message); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/DefaultMemberAccessPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Tenray.Topaz.Interop; 5 | 6 | public sealed class DefaultMemberAccessPolicy : IMemberAccessPolicy 7 | { 8 | readonly TopazEngine TopazEngine; 9 | 10 | static readonly HashSet TypeMemberWhiteList = new () 11 | { 12 | "IsClass", 13 | "FullName", 14 | "Namespace", 15 | "ToString", 16 | "IsEnum", 17 | "IsValueType", 18 | "IsPrimitive", 19 | "GetTypeCode", 20 | "GetEnumName", 21 | "GetEnumNames", 22 | "GetEnumValues", 23 | "IsAssignableFrom", 24 | "IsAssignableTo", 25 | "IsSubclassOf" 26 | }; 27 | 28 | public DefaultMemberAccessPolicy(TopazEngine topazEngine) 29 | { 30 | TopazEngine = topazEngine; 31 | } 32 | 33 | public bool IsObjectMemberAccessAllowed(object obj, string memberName) 34 | { 35 | if (obj == null || memberName == null) 36 | return true; 37 | var enableReflection = TopazEngine.Options.SecurityPolicy 38 | .HasFlag(Options.SecurityPolicy.EnableReflection); 39 | if (enableReflection) 40 | return true; 41 | if (obj is Type) 42 | { 43 | return TypeMemberWhiteList.Contains(memberName); 44 | } 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Topaz/API/array/normal/JsArrayInvoker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Tenray.Topaz.Interop; 5 | 6 | namespace Tenray.Topaz.API; 7 | 8 | /// 9 | /// Fast invoker for critical methods. The remaining methods would be invoked via default invoker. 10 | /// 11 | internal sealed class JsArrayInvoker : IInvokable 12 | { 13 | readonly JsArray JsArray; 14 | 15 | readonly JsArrayOptimizedMethods Method; 16 | 17 | public JsArrayInvoker(JsArray instance, JsArrayOptimizedMethods method) 18 | { 19 | JsArray = instance; 20 | Method = method; 21 | } 22 | 23 | public object Invoke(IReadOnlyList args) 24 | { 25 | var len = args.Count; 26 | switch (Method) 27 | { 28 | case JsArrayOptimizedMethods.push: 29 | if (len == 0) 30 | return JsArray.Count; 31 | if (len == 1) 32 | return JsArray.push(args[0]); 33 | return JsArray.push(args[0], args.ToArray()[1..]); 34 | case JsArrayOptimizedMethods.pop: 35 | return JsArray.pop(); 36 | case JsArrayOptimizedMethods.shift: 37 | return JsArray.shift(); 38 | default: 39 | throw new NotImplementedException($"JsArray.{Method} invoker is not implemented"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Topaz/ITopazFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Tenray.Topaz; 5 | 6 | public interface ITopazFunction 7 | { 8 | /// 9 | /// Gets name of the function. 10 | /// 11 | string Name { get; } 12 | 13 | /// 14 | /// Gets the number of arguments of the function. 15 | /// 16 | int Length { get; } 17 | 18 | /// 19 | /// Gets the nth argument name of the function. 20 | /// 21 | /// The index of the argument. 22 | /// Name of the argument. 23 | string this[int index] { get; } 24 | 25 | /// 26 | /// Invokes the function using arguments. 27 | /// 28 | /// CancellationToken. 29 | /// Arguments. 30 | /// The object returned from function implementation. 31 | object Invoke(CancellationToken token, params object[] args); 32 | 33 | /// 34 | /// Invokes the function asynchronously using arguments. 35 | /// 36 | /// Arguments. 37 | /// CancellationToken. 38 | /// The object returned from function implementation. 39 | ValueTask InvokeAsync(CancellationToken token, params object[] args); 40 | } -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Benchmark3.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Jint; 3 | using Microsoft.ClearScript.V8; 4 | using System.Collections.Generic; 5 | 6 | namespace Tenray.Topaz.Benchmark; 7 | 8 | public class Benchmark3 9 | { 10 | public string Code = @" 11 | for (var i = 0.0 ; i < 1000000; ++i) { 12 | model.Value++; 13 | } 14 | "; 15 | public class Model 16 | { 17 | public int Value; 18 | } 19 | 20 | [Benchmark] 21 | public void RunTopaz() 22 | { 23 | var topazEngine = new TopazEngine(new TopazEngineSetup 24 | { 25 | IsThreadSafe = false 26 | }); 27 | var model = new Dictionary(); 28 | model.Add("Value", 0); 29 | topazEngine.SetValue("model", model); 30 | topazEngine.ExecuteScript(Code); 31 | } 32 | 33 | // [Benchmark] 34 | public void RunV8Engine() 35 | { 36 | var v8Engine = new V8ScriptEngine(); 37 | var model = new Dictionary(); 38 | model.Add("Value", 0); 39 | v8Engine.AddHostObject("model", model); 40 | v8Engine.Execute(Code); 41 | } 42 | 43 | [Benchmark] 44 | public void RunJint() 45 | { 46 | var jintEngine = new Engine(); 47 | var model = new Dictionary(); 48 | model.Add("Value", 0); 49 | jintEngine.SetValue("model", model); 50 | jintEngine.Execute(Code); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/TaggedTemplateExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Tenray.Topaz.Core; 6 | 7 | namespace Tenray.Topaz.Expressions; 8 | 9 | internal static partial class TaggedTemplateExpressionHandler 10 | { 11 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 12 | { 13 | var tagged = (TaggedTemplateExpression)expression; 14 | var tag = tagged.Tag; 15 | var literal = tagged.Quasi; 16 | var quasis = literal.Quasis; 17 | var list = literal.Expressions; 18 | var quasisLen = quasis.Count; 19 | var listLen = list.Count; 20 | var strings = new List(); 21 | for (var i = 0; i < quasisLen; i++) 22 | { 23 | var quasi = quasis[i]; 24 | strings.Add(quasi.Value.Cooked); 25 | } 26 | var args = new List(); 27 | args.Add(strings); 28 | for (var i = 0; i < listLen; ++i) 29 | { 30 | args.Add(await scriptExecutor.ExecuteExpressionAndGetValueAsync(list[i], token)); 31 | } 32 | var callee = await scriptExecutor.ExecuteExpressionAndGetValueAsync(tag, token); 33 | return await scriptExecutor.CallFunctionAsync(callee, args.ToArray(), false, token); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Token.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | 3 | namespace Esprima; 4 | 5 | public enum TokenType 6 | { 7 | BooleanLiteral, 8 | EOF, 9 | Identifier, 10 | Keyword, 11 | NullLiteral, 12 | NumericLiteral, 13 | Punctuator, 14 | StringLiteral, 15 | RegularExpression, 16 | Template 17 | }; 18 | 19 | public sealed class Token 20 | { 21 | public TokenType Type; 22 | public string? Literal; 23 | 24 | public int Start; // Range[0] 25 | public int End; // Range[1] 26 | public int LineNumber; 27 | public int LineStart; 28 | 29 | public Location Location; 30 | 31 | // For NumericLiteral 32 | public bool Octal; 33 | public char? NotEscapeSequenceHead; 34 | 35 | // For templates 36 | public bool Head; 37 | public bool Tail; 38 | public string? RawTemplate; 39 | 40 | public bool BooleanValue; 41 | public double NumericValue; 42 | public object? Value; 43 | public RegexValue? RegexValue; 44 | 45 | public void Clear() 46 | { 47 | Type = TokenType.BooleanLiteral; 48 | Literal = null; 49 | Start = 0; 50 | End = 0; 51 | LineNumber = 0; 52 | LineStart = 0; 53 | Location = default; 54 | Octal = false; 55 | Head = false; 56 | Tail = false; 57 | RawTemplate = null; 58 | BooleanValue = false; 59 | NumericValue = 0; 60 | Value = null; 61 | RegexValue = null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Topaz.Test/SwitchCaseTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.Collections; 3 | using Tenray.Topaz.API; 4 | 5 | namespace Tenray.Topaz.Test; 6 | 7 | public sealed class SwitchCaseTests 8 | { 9 | [Test] 10 | public void TestSwitchCase1() 11 | { 12 | var engine = new TopazEngine(); 13 | engine.Options.LiteralNumbersAreConvertedToDouble = false; 14 | engine.Options.NumbersAreConvertedToDoubleInArithmeticOperations = false; 15 | dynamic model = new JsObject(); 16 | engine.SetValue("model", model); 17 | engine.ExecuteScript(@" 18 | function f1(key) { 19 | var result = 0 20 | switch (key) { 21 | case 1: result = 1; break 22 | case 2: result = 2; break 23 | default: result = 3 24 | } 25 | return result 26 | } 27 | 28 | function f2(key) { 29 | var result = 0 30 | var loop = true 31 | while(loop) { 32 | switch (key) { 33 | case 1: result = 1; loop = false; continue 34 | case 2: result = 2; loop = false; continue 35 | default: result = 3; loop = false; continue 36 | } 37 | } 38 | return result 39 | } 40 | 41 | var a = [] 42 | i = 0 43 | a[i++] = f1(1) == 1 44 | a[i++] = f1(2) == 2 45 | a[i++] = f1('1') == 3 46 | 47 | a[i++] = f2(1) == 1 48 | a[i++] = f2(2) == 2 49 | a[i++] = f2('1') == 3 50 | 51 | model.a = a 52 | "); 53 | 54 | foreach (var item in (IEnumerable)(model.a)) 55 | Assert.IsTrue((bool)item); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Topaz/Collections/ThrowHelper.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace Microsoft.Collections.Extensions; 9 | 10 | internal static class ThrowHelper 11 | { 12 | [MethodImpl(MethodImplOptions.NoInlining)] 13 | internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported() 14 | { 15 | throw new InvalidOperationException(Strings.InvalidOperation_ConcurrentOperationsNotSupported); 16 | } 17 | 18 | [MethodImpl(MethodImplOptions.NoInlining)] 19 | internal static void ThrowKeyArgumentNullException() 20 | { 21 | throw new ArgumentNullException("key"); 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.NoInlining)] 25 | internal static void ThrowCapacityArgumentOutOfRangeException() 26 | { 27 | throw new ArgumentOutOfRangeException("capacity"); 28 | } 29 | 30 | [MethodImpl(MethodImplOptions.NoInlining)] 31 | internal static bool ThrowNotSupportedException_ReadOnly_Modification() 32 | { 33 | throw new NotSupportedException(Strings.ReadOnly_Modification); 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.NoInlining)] 37 | internal static bool ThrowNotSupportedException() 38 | { 39 | throw new NotSupportedException(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | using System; 3 | using System.Diagnostics; 4 | 5 | #pragma warning disable CS0162 // Unreachable code detected 6 | namespace Tenray.Topaz.Benchmark; 7 | 8 | class Program 9 | { 10 | static void Main() 11 | { 12 | if (false) 13 | { 14 | RunUsingBenchmarkDotNet(); 15 | } 16 | else 17 | { 18 | BenchmarkRunner.Run(); 19 | //RunSingleJob(); 20 | } 21 | } 22 | 23 | private static void RunSingleJob() 24 | { 25 | var sw = new Stopwatch(); 26 | var b = new Benchmark5(); 27 | var engine = Engines.Topaz; 28 | sw.Start(); 29 | switch (engine) 30 | { 31 | case Engines.Topaz: 32 | b.RunTopaz(); 33 | break; 34 | case Engines.V8Engine: 35 | b.RunV8Engine(); 36 | break; 37 | case Engines.Jint: 38 | b.RunJint(); 39 | break; 40 | } 41 | sw.Stop(); 42 | Console.WriteLine($"{engine}: {sw.ElapsedMilliseconds} ms"); 43 | } 44 | 45 | private static void RunUsingBenchmarkDotNet() 46 | { 47 | BenchmarkRunner.Run(); 48 | BenchmarkRunner.Run(); 49 | BenchmarkRunner.Run(); 50 | BenchmarkRunner.Run(); 51 | BenchmarkRunner.Run(); 52 | } 53 | } 54 | #pragma warning restore CS0162 // Unreachable code detected 55 | 56 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/CallExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using Tenray.Topaz.Core; 6 | 7 | namespace Tenray.Topaz.Expressions; 8 | 9 | internal static partial class CallExpressionHandler 10 | { 11 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 12 | { 13 | var expr = (CallExpression)expression; 14 | var calleeArgs = expr.Arguments; 15 | var len = calleeArgs.Count; 16 | var args = new List(len); 17 | for (var i = 0; i < len; ++i) 18 | { 19 | var arg = calleeArgs[i]; 20 | if (arg is SpreadElement spreadElement) 21 | { 22 | var inner = scriptExecutor.ExecuteExpressionAndGetValue(spreadElement.Argument, token); 23 | if (inner is IEnumerable enumerable) 24 | { 25 | foreach (var item in enumerable) 26 | { 27 | token.ThrowIfCancellationRequested(); 28 | args.Add(item); 29 | } 30 | } 31 | continue; 32 | } 33 | args.Add(scriptExecutor.ExecuteExpressionAndGetValue(arg, token)); 34 | } 35 | token.ThrowIfCancellationRequested(); 36 | var callee = scriptExecutor.ExecuteStatement(expr.Callee, token); 37 | return scriptExecutor.CallFunction(callee, args, expr.Optional, token); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Topaz/API/object/normal/JsonElementJsObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Tenray.Topaz.API; 5 | 6 | internal static class JsonElementJsObjectConverter 7 | { 8 | internal static object ConvertToJsObject(this JsonElement jsonElement) 9 | { 10 | var kind = jsonElement.ValueKind; 11 | 12 | return kind switch 13 | { 14 | JsonValueKind.Null => null, 15 | JsonValueKind.Undefined => null, 16 | JsonValueKind.String => jsonElement.GetString(), 17 | JsonValueKind.Number => jsonElement.GetDouble(), 18 | JsonValueKind.Array => ConvertToArray(jsonElement), 19 | JsonValueKind.False => false, 20 | JsonValueKind.True => true, 21 | JsonValueKind.Object => ConvertToDynamicObject(jsonElement), 22 | _ => throw new Exception("Cannot convert JsonElement to known object.") 23 | }; 24 | } 25 | 26 | static object ConvertToArray(JsonElement jsonElement) 27 | { 28 | var result = new JsArray(); 29 | var i = 0; 30 | foreach (var el in jsonElement.EnumerateArray()) 31 | { 32 | result[i++] = el.ConvertToJsObject(); 33 | } 34 | return result; 35 | } 36 | 37 | static JsObject ConvertToDynamicObject(JsonElement jsonElement) 38 | { 39 | var obj = new JsObject(); 40 | foreach (var el in jsonElement.EnumerateObject()) 41 | { 42 | obj[el.Name] = ConvertToJsObject(el.Value); 43 | } 44 | return obj; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Topaz/Interop/IObjectProxy.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Interop; 2 | 3 | public interface IObjectProxy 4 | { 5 | /// 6 | /// Returns true if a member is found, false otherwise. 7 | /// Output value is member of the given instance. 8 | /// If instance member is a function then output value is IInvokable. 9 | /// 10 | /// Object instance. 11 | /// Member name or indexed member value. 12 | /// Returned value. 13 | /// Indicates if the member 14 | /// retrieval should be through an indexed property. 15 | /// 16 | bool TryGetObjectMember( 17 | object instance, 18 | object member, 19 | out object value, 20 | bool isIndexedProperty = false); 21 | 22 | /// 23 | /// Returns true if a member is found and updated with new value, 24 | /// false otherwise. 25 | /// If instance member is a function, returns false. 26 | /// 27 | /// Object instance. 28 | /// Member name or indexed member value. 29 | /// New value. 30 | /// Indicates if the member 31 | /// retrieval should be through an indexed property. 32 | /// 33 | bool TrySetObjectMember( 34 | object instance, 35 | object member, 36 | object value, 37 | bool isIndexedProperty = false); 38 | } 39 | -------------------------------------------------------------------------------- /src/Topaz/API/json/JSONObject.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Encodings.Web; 2 | using System.Text.Json; 3 | 4 | namespace Tenray.Topaz.API; 5 | 6 | public sealed class JSONObject : JsObject 7 | { 8 | public JsonSerializerOptions Options { get; set; } = new() 9 | { 10 | Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, 11 | #if NET6_0_OR_GREATER 12 | ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles, 13 | #endif 14 | AllowTrailingCommas = true, 15 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 16 | ReadCommentHandling = JsonCommentHandling.Skip, 17 | WriteIndented = false, 18 | PropertyNameCaseInsensitive = true 19 | }; 20 | 21 | public JsonSerializerOptions OptionsIndented { get; set; } = new() 22 | { 23 | Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, 24 | #if NET6_0_OR_GREATER 25 | ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles, 26 | #endif 27 | AllowTrailingCommas = true, 28 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase, 29 | ReadCommentHandling = JsonCommentHandling.Skip, 30 | WriteIndented = true, 31 | PropertyNameCaseInsensitive = true 32 | }; 33 | 34 | public string stringify(object value, object replacer = null, int space = 0) 35 | { 36 | return JsonSerializer.Serialize(value, space == 0 ? Options : OptionsIndented); 37 | } 38 | 39 | public object parse(string json) 40 | { 41 | return JsonSerializer.Deserialize(json, Options).ConvertToJsObject(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/GenericChildNodeYield.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Esprima.Ast; 4 | 5 | /// 6 | /// Helps to succinctly implement for subclasses of . 7 | /// 8 | internal static class GenericChildNodeYield 9 | { 10 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 11 | public static NodeCollection Yield(Node? first, in NodeList second, Node? third) where T : Node 12 | { 13 | return new NodeCollection(first, second._items, second._count, third); 14 | } 15 | 16 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 17 | public static NodeCollection Yield(in NodeList first) where T : Node 18 | { 19 | return new NodeCollection(first._items, first._count); 20 | } 21 | 22 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 | public static NodeCollection Yield(in NodeList first, in NodeList second) where T1 : Node where T2 : Node 24 | { 25 | return new NodeCollection(first._items, first._count, second._items, second._count); 26 | } 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public static NodeCollection Yield(Node? first, in NodeList second) where T : Node 30 | { 31 | return new NodeCollection(first, second._items, second._count); 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public static NodeCollection Yield(in NodeList first, Node? second) where T : Node 36 | { 37 | return new NodeCollection(first._items, first._count, second); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Sebastien Ros. All rights reserved. 2 | BSD 3-Clause License - https://opensource.org/licenses/BSD-3-Clause 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Orchard nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/Topaz.Test/SpreadOperatorTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.Collections.Generic; 3 | using Tenray.Topaz.API; 4 | 5 | namespace Tenray.Topaz.Test; 6 | 7 | public sealed class SpreadOperatorTests 8 | { 9 | [Test] 10 | public void SpreadOperator() 11 | { 12 | var engine = new TopazEngine(); 13 | dynamic model = new List(); 14 | engine.SetValue("model", model); 15 | engine.ExecuteScript(@" 16 | max = (...args) => { 17 | for (const item of args) { 18 | model.Add(item) 19 | } 20 | } 21 | max(-1, 5, 11, 3) 22 | max(...[-1, 5, 11, 3]) 23 | "); 24 | Assert.AreEqual(new List 25 | { 26 | -1, 5, 11, 3,-1, 5, 11, 3 27 | }, model); 28 | } 29 | 30 | [Test] 31 | public void SpreadArray() 32 | { 33 | var engine = new TopazEngine(); 34 | dynamic model = new List(); 35 | engine.SetValue("model", model); 36 | engine.ExecuteScript(@" 37 | var arr = [1, ...[2,3], 4] 38 | function max(...args) { 39 | for (const item of args) { 40 | model.Add(item) 41 | } 42 | } 43 | max(...arr) 44 | "); 45 | Assert.AreEqual(new List 46 | { 47 | 1,2,3,4 48 | }, model); 49 | } 50 | 51 | [Test] 52 | public void EnumerableObjectSpread() 53 | { 54 | var engine = new TopazEngine(); 55 | dynamic model = new JsObject(); 56 | model.a = "aa"; 57 | model.b = "bb"; 58 | engine.SetValue("model", model); 59 | engine.ExecuteScript(@" 60 | var c = [...model] 61 | model.c = c 62 | "); 63 | Assert.AreEqual("bb", model.c[1].Value); 64 | } 65 | } -------------------------------------------------------------------------------- /src/Topaz/API/object/concurrent/JsonElementConcurrentJsObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | 4 | namespace Tenray.Topaz.API; 5 | 6 | internal static class JsonElementConcurrentJsObjectConverter 7 | { 8 | internal static object ConvertToConcurrentJsObject(this JsonElement jsonElement) 9 | { 10 | var kind = jsonElement.ValueKind; 11 | 12 | return kind switch 13 | { 14 | JsonValueKind.Null => null, 15 | JsonValueKind.Undefined => null, 16 | JsonValueKind.String => jsonElement.GetString(), 17 | JsonValueKind.Number => jsonElement.GetDouble(), 18 | JsonValueKind.Array => ConvertToArray(jsonElement), 19 | JsonValueKind.False => false, 20 | JsonValueKind.True => true, 21 | JsonValueKind.Object => ConvertToDynamicObject(jsonElement), 22 | _ => throw new Exception("Cannot convert JsonElement to known object.") 23 | }; 24 | } 25 | 26 | private static object ConvertToArray( 27 | JsonElement jsonElement) 28 | { 29 | var result = new JsArray(); 30 | var i = 0; 31 | foreach (var el in jsonElement.EnumerateArray()) 32 | { 33 | result[i++] = el.ConvertToConcurrentJsObject(); 34 | } 35 | return result; 36 | } 37 | 38 | private static ConcurrentJsObject ConvertToDynamicObject( 39 | JsonElement jsonElement) 40 | { 41 | var obj = new ConcurrentJsObject(); 42 | foreach (var el in jsonElement.EnumerateObject()) 43 | { 44 | obj[el.Name] = ConvertToConcurrentJsObject(el.Value); 45 | } 46 | return obj; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Topaz.Benchmark/Benchmark6.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Jint; 3 | using Microsoft.ClearScript.V8; 4 | using System.Threading.Tasks; 5 | 6 | namespace Tenray.Topaz.Benchmark; 7 | 8 | public class Benchmark6 9 | { 10 | public string Code = @" 11 | function square(x) { 12 | return x*x; 13 | } 14 | "; 15 | public int LoopLength = 1000000; 16 | 17 | //[Benchmark] 18 | public void RunV8Engine() 19 | { 20 | var v8Engine = new V8ScriptEngine(); 21 | v8Engine.Execute(Code); 22 | var squareFunction = v8Engine.Script.square; 23 | Parallel.For(0, LoopLength, (x) => 24 | { 25 | var result = v8Engine.Invoke("square", x); ; 26 | }); 27 | } 28 | 29 | [Benchmark] 30 | public void RunTopaz() 31 | { 32 | var topazEngine = new TopazEngine(new TopazEngineSetup 33 | { 34 | IsThreadSafe = false 35 | }); 36 | topazEngine.ExecuteScript(Code); 37 | var squareFunction = topazEngine.GetValue("square"); 38 | Parallel.For(0, LoopLength, (x) => 39 | { 40 | var result = topazEngine.InvokeFunction(squareFunction, default, x); 41 | }); 42 | } 43 | 44 | [Benchmark] 45 | public void RunJint() 46 | { 47 | var jintEngine = new Engine(); 48 | jintEngine.Execute(Code); 49 | var squareFunction = jintEngine.GetValue("square"); 50 | var syncObject = new object(); 51 | Parallel.For(0, LoopLength, (x) => 52 | { 53 | lock (syncObject) 54 | { 55 | var result = jintEngine.Invoke(squareFunction, null, x); 56 | } 57 | }); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/Topaz.Test/TemplateLiteralTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Tenray.Topaz.API; 3 | 4 | namespace Tenray.Topaz.Test; 5 | 6 | public sealed class TemplateLiteralTests 7 | { 8 | [Test] 9 | public void TaggedTemplate() 10 | { 11 | var engine = new TopazEngine(); 12 | dynamic model = new JsObject(); 13 | engine.SetValue("model", model); 14 | engine.ExecuteScript(@" 15 | let person = 'Mike'; 16 | let age = 28; 17 | 18 | function myTag(strings, personExp, ageExp) { 19 | let str0 = strings[0]; // That 20 | let str1 = strings[1]; // is a 21 | let str2 = strings[2]; // . 22 | 23 | let ageStr; 24 | if (ageExp > 99) 25 | { 26 | ageStr = 'centenarian'; 27 | } 28 | else 29 | { 30 | ageStr = 'youngster'; 31 | } 32 | 33 | // We can even return a string built using a template literal 34 | return `${ str0}${ personExp}${ str1}${ ageStr}${ str2}`; 35 | } 36 | 37 | let output = myTag`That ${ person 38 | } is a ${ age 39 | }.`; 40 | 41 | model.a = myTag`That ${ person } is a ${ age }.` 42 | model.b = myTag`That ${ person } is a ${ 157 }.` 43 | "); 44 | Assert.AreEqual("That Mike is a youngster.", model.a); 45 | Assert.AreEqual("That Mike is a centenarian.", model.b); 46 | } 47 | 48 | [Test] 49 | public void TemplateArithmetic() 50 | { 51 | var engine = new TopazEngine(); 52 | dynamic model = new JsObject(); 53 | engine.SetValue("model", model); 54 | engine.ExecuteScript(@" 55 | model.a = `2 + 3 = ${2+3}` 56 | "); 57 | Assert.AreEqual("2 + 3 = 5", model.a);; 58 | } 59 | } -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/CallExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Tenray.Topaz.Core; 7 | 8 | namespace Tenray.Topaz.Expressions; 9 | 10 | internal static partial class CallExpressionHandler 11 | { 12 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 13 | { 14 | var expr = (CallExpression)expression; 15 | var calleeArgs = expr.Arguments; 16 | var len = calleeArgs.Count; 17 | var args = new List(len); 18 | for (var i = 0; i < len; ++i) 19 | { 20 | var arg = calleeArgs[i]; 21 | if (arg is SpreadElement spreadElement) 22 | { 23 | var inner = await scriptExecutor.ExecuteExpressionAndGetValueAsync(spreadElement.Argument, token); 24 | if (inner is IEnumerable enumerable) 25 | { 26 | foreach (var item in enumerable) 27 | { 28 | token.ThrowIfCancellationRequested(); 29 | args.Add(item); 30 | } 31 | } 32 | continue; 33 | } 34 | args.Add(await scriptExecutor.ExecuteExpressionAndGetValueAsync(arg, token)); 35 | } 36 | token.ThrowIfCancellationRequested(); 37 | var callee = await scriptExecutor.ExecuteStatementAsync(expr.Callee, token); 38 | var result = await scriptExecutor.CallFunctionAsync(callee, args, expr.Optional, token); 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/AwaitExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Expressions; 7 | 8 | internal static partial class AwaitExpressionHandler 9 | { 10 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 11 | { 12 | var expr = (AwaitExpression)expression; 13 | var result = await scriptExecutor.ExecuteExpressionAndGetValueAsync(expr.Argument, token); 14 | if (result == null) 15 | return null; 16 | var awaitHandler = scriptExecutor.TopazEngine.AwaitExpressionHandler; 17 | if (awaitHandler != null) 18 | { 19 | return await awaitHandler.HandleAwaitExpression(result, token); 20 | } 21 | if (result is Task task) 22 | { 23 | var type = task.GetType(); 24 | var returnType = type.GetMethod("GetAwaiter").ReturnType.GetMethod("GetResult").ReturnType; 25 | if (returnType != typeof(void) && returnType.FullName != "System.Threading.Tasks.VoidTaskResult") 26 | return await (dynamic)task; 27 | await task; 28 | return null; 29 | } 30 | 31 | if (result is ValueTask valueTask) 32 | { 33 | await valueTask; 34 | return null; 35 | } 36 | 37 | var type2 = result.GetType(); 38 | if (type2.IsGenericType && typeof(ValueTask<>) == type2.GetGenericTypeDefinition()) 39 | { 40 | dynamic awaitable = result; 41 | return await awaitable; 42 | } 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/NewExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using Tenray.Topaz.Core; 6 | using Tenray.Topaz.ErrorHandling; 7 | using Tenray.Topaz.Interop; 8 | 9 | namespace Tenray.Topaz.Expressions; 10 | 11 | internal static partial class NewExpressionHandler 12 | { 13 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 14 | { 15 | var expr = (NewExpression)expression; 16 | var callee = scriptExecutor.ExecuteExpressionAndGetValue(expr.Callee, token); 17 | if (callee is not ITypeProxy typeProxy) 18 | { 19 | Exceptions.ThrowCanNotCallConstructor(callee); 20 | return null; 21 | } 22 | var calleeArgs = expr.Arguments; 23 | var len = calleeArgs.Count; 24 | var args = new List(len); 25 | for (var i = 0; i < len; ++i) 26 | { 27 | var arg = calleeArgs[i]; 28 | if (arg is SpreadElement spreadElement) 29 | { 30 | var inner = scriptExecutor.ExecuteExpressionAndGetValue(spreadElement.Argument, token); 31 | if (inner is IEnumerable enumerable) 32 | { 33 | foreach (var item in enumerable) 34 | { 35 | token.ThrowIfCancellationRequested(); 36 | args.Add(item); 37 | } 38 | } 39 | continue; 40 | } 41 | args.Add(scriptExecutor.ExecuteExpressionAndGetValue(arg, token)); 42 | } 43 | return typeProxy.CallConstructor(args); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Topaz/Core/ScriptExecutor.Capture.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Tenray.Topaz.Core; 5 | 6 | internal sealed partial class ScriptExecutor 7 | { 8 | private void CaptureVariables() 9 | { 10 | // Closure and its ancesstors should not return to pool. 11 | MarkCanNotReturnToPool(); 12 | var scope = ParentScope; 13 | var capturedKeys = new HashSet(); 14 | while (scope != null) 15 | { 16 | if (scope.isEmptyScope) 17 | { 18 | scope = scope.ParentScope; 19 | continue; 20 | } 21 | KeyValuePair[] list; 22 | if (scope.IsThreadSafeScope) 23 | list = scope.SafeVariables.ToArray(); 24 | else 25 | list = scope.UnsafeVariables.ToArray(); 26 | var len = list.Length; 27 | for (var i = 0; i < len; ++i) 28 | { 29 | var variable = list[i].Value; 30 | var key = list[i].Key; 31 | if (!variable.ShouldCapture) 32 | { 33 | capturedKeys.Add(key); 34 | continue; 35 | } 36 | if (!capturedKeys.Contains(key)) 37 | { 38 | AddOrUpdateVariableValueAndKindInTheScope( 39 | key, 40 | variable.Value, 41 | variable.Kind, 42 | VariableState.Captured); 43 | capturedKeys.Add(key); 44 | } 45 | } 46 | scope = scope.ParentScope; 47 | } 48 | IsFrozen = true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Topaz/Options/PresetOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Tenray.Topaz.Options; 2 | 3 | public static class PresetOptions 4 | { 5 | /// 6 | /// Default style 7 | /// 8 | public static TopazEngineOptions FriendlyStyle => 9 | new() 10 | { 11 | AllowNullReferenceMemberAccess = true, 12 | AllowUndefinedReferenceAccess = true, 13 | AllowUndefinedReferenceMemberAccess = true, 14 | AssignmentWithoutDefinitionBehavior = 15 | AssignmentWithoutDefinitionBehavior.DefineAsVarInExecutionScope, 16 | NoUndefined = true, 17 | VarScopeBehavior = VarScopeBehavior.FunctionScope 18 | }; 19 | 20 | public static TopazEngineOptions EcmaJavascript => 21 | new() 22 | { 23 | AllowNullReferenceMemberAccess = false, 24 | AllowUndefinedReferenceAccess = false, 25 | AllowUndefinedReferenceMemberAccess = false, 26 | AssignmentWithoutDefinitionBehavior = 27 | AssignmentWithoutDefinitionBehavior.DefineAsVarInGlobalScope, 28 | NoUndefined = false, 29 | VarScopeBehavior = VarScopeBehavior.FunctionScope 30 | }; 31 | 32 | public static TopazEngineOptions EarlyErrorCatchStyle => 33 | new() 34 | { 35 | AllowNullReferenceMemberAccess = false, 36 | AllowUndefinedReferenceAccess = true, 37 | AllowUndefinedReferenceMemberAccess = false, 38 | AssignmentWithoutDefinitionBehavior = 39 | AssignmentWithoutDefinitionBehavior.DefineAsVarInExecutionScope, 40 | NoUndefined = true, 41 | VarScopeBehavior = VarScopeBehavior.FunctionScope 42 | }; 43 | } 44 | -------------------------------------------------------------------------------- /src/Topaz.Test/ConstructorTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using Tenray.Topaz.API; 5 | 6 | namespace Tenray.Topaz.Test; 7 | 8 | public sealed class ConstructorTests 9 | { 10 | [Test] 11 | public void DateTimeConstruction() 12 | { 13 | var engine = new TopazEngine(); 14 | engine.AddType("DateTime"); 15 | dynamic model = new JsObject(); 16 | engine.SetValue("model", model); 17 | engine.ExecuteScript(@" 18 | model.a = new DateTime(2021, 7, 21, 5, 5, 5, 'uTC') 19 | model.a = model.a.AddTicks(5555); 20 | model.b = DateTime.Parse('2021-11-11').ToString('dd/MM/yyyy HH:mm:ss') 21 | model.c = (DateTime.Now.GetType()).ToString() 22 | "); 23 | Assert.AreEqual( 24 | new DateTime(2021, 7, 21, 5, 5, 5, DateTimeKind.Utc) 25 | .AddTicks(5555), model.a); 26 | Assert.AreEqual( 27 | "11/11/2021 00:00:00", model.b); 28 | Assert.AreEqual( 29 | "System.DateTime", model.c); 30 | } 31 | 32 | [Test] 33 | public void GenericTypeConstruction() 34 | { 35 | var engine = new TopazEngine(); 36 | dynamic model = new JsObject(); 37 | model["int"] = typeof(int); 38 | model["string"] = typeof(string); 39 | engine.SetValue("model", model); 40 | engine.AddType(typeof(Dictionary<,>), "GenericDictionary"); 41 | 42 | engine.ExecuteScript(@" 43 | var dic = model.dic = new GenericDictionary(model.string, model.int) 44 | dic.Add('hello', 1) 45 | dic.Add('dummy', 0) 46 | dic.Add('world', 2) 47 | dic.Remove('dummy') 48 | "); 49 | Assert.AreEqual(1, model.dic["hello"]); 50 | Assert.AreEqual(2, model.dic["world"]); 51 | Assert.IsFalse(model.dic.ContainsKey("dummy")); 52 | } 53 | } -------------------------------------------------------------------------------- /src/Topaz.Test/ForLoopsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Tenray.Topaz.API; 3 | 4 | namespace Tenray.Topaz.Test; 5 | 6 | public sealed class ForLoopsTests 7 | { 8 | [Test] 9 | public void ForInLoop() 10 | { 11 | var engine = new TopazEngine(); 12 | dynamic model = new JsObject(); 13 | engine.SetValue("model", model); 14 | engine.ExecuteScript(@" 15 | let items = [1, 2, 3] 16 | let index = 3 17 | for (const index in items) { 18 | model[index] = items[index] 19 | } 20 | "); 21 | Assert.AreEqual(1, model["0"]); 22 | Assert.AreEqual(2, model["1"]); 23 | Assert.AreEqual(3, model["2"]); 24 | } 25 | 26 | [Test] 27 | public void ForOfLoop() 28 | { 29 | var engine = new TopazEngine(); 30 | dynamic model = new JsObject(); 31 | engine.SetValue("model", model); 32 | engine.ExecuteScript(@" 33 | let items = [51, 22, 33] 34 | for (const item of items) { 35 | model[item] = item 36 | } 37 | "); 38 | Assert.AreEqual(51, model["51"]); 39 | Assert.AreEqual(22, model["22"]); 40 | Assert.AreEqual(33, model["33"]); 41 | } 42 | 43 | [Test] 44 | public void ForLoopConstVarDef() 45 | { 46 | var engine = new TopazEngine(); 47 | dynamic model = new JsObject(); 48 | engine.SetValue("model", model); 49 | engine.ExecuteScript(@" 50 | let items = [1, 2, 3] 51 | let index = 3 52 | for (const index in items) { 53 | const a = 3 54 | model[index] = items[index] 55 | } 56 | for (const index of items) { 57 | const a = 3 58 | } 59 | for (var i = 0 ; i < 3; ++i) { 60 | const a = 3 61 | } 62 | "); 63 | Assert.AreEqual(1, model["0"]); 64 | Assert.AreEqual(2, model["1"]); 65 | Assert.AreEqual(3, model["2"]); 66 | } 67 | } -------------------------------------------------------------------------------- /src/Topaz/API/JsObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Tenray.Topaz.API; 4 | 5 | public static class JsObjectConverter 6 | { 7 | /// 8 | /// Creates a new IJsObject by using all public instance properties of the given source object. 9 | /// If the source object is IJsObject returns the source object. 10 | /// By default the property names are converted to lowercase to match Javascript common code style. 11 | /// 12 | /// The source object 13 | /// JsObject creation option 14 | /// 15 | public static IJsObject ToJsObject(this object sourceObject, 16 | JsObjectConverterOption option = JsObjectConverterOption.None) 17 | { 18 | if (sourceObject == null) 19 | return null; 20 | if (sourceObject is IJsObject jsObject) 21 | return jsObject; 22 | jsObject = option.HasFlag(JsObjectConverterOption.CreateConcurrentJsObject) ? 23 | new ConcurrentJsObject() 24 | : new JsObject(); 25 | var type = sourceObject.GetType(); 26 | var props = type.GetProperties( 27 | BindingFlags.Public | 28 | BindingFlags.Instance | 29 | BindingFlags.GetProperty); 30 | var useLowerCasePropertyNames = option.HasFlag(JsObjectConverterOption.UseLowerCasePropertyNames); 31 | foreach (var prop in props) 32 | { 33 | var value = prop.GetValue(sourceObject); 34 | var propertyName = useLowerCasePropertyNames ? 35 | char.ToLowerInvariant(prop.Name[0]) + prop.Name[1..] : 36 | prop.Name; 37 | jsObject.SetValue(propertyName, value); 38 | } 39 | return jsObject; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/NewExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Tenray.Topaz.Core; 7 | using Tenray.Topaz.ErrorHandling; 8 | using Tenray.Topaz.Interop; 9 | 10 | namespace Tenray.Topaz.Expressions; 11 | 12 | internal static partial class NewExpressionHandler 13 | { 14 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 15 | { 16 | var expr = (NewExpression)expression; 17 | var callee = await scriptExecutor.ExecuteExpressionAndGetValueAsync(expr.Callee, token); 18 | if (callee is not ITypeProxy typeProxy) 19 | { 20 | Exceptions.ThrowCanNotCallConstructor(callee); 21 | return null; 22 | } 23 | var calleeArgs = expr.Arguments; 24 | var len = calleeArgs.Count; 25 | var args = new List(len); 26 | for (var i = 0; i < len; ++i) 27 | { 28 | var arg = calleeArgs[i]; 29 | if (arg is SpreadElement spreadElement) 30 | { 31 | var inner = await scriptExecutor.ExecuteExpressionAndGetValueAsync(spreadElement.Argument, token); 32 | if (inner is IEnumerable enumerable) 33 | { 34 | foreach (var item in enumerable) 35 | { 36 | token.ThrowIfCancellationRequested(); 37 | args.Add(item); 38 | } 39 | } 40 | continue; 41 | } 42 | args.Add(await scriptExecutor.ExecuteExpressionAndGetValueAsync(arg, token)); 43 | } 44 | return typeProxy.CallConstructor(args); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Nodes.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima.Ast; 2 | 3 | public enum Nodes 4 | { 5 | AssignmentExpression, 6 | ArrayExpression, 7 | BlockStatement, 8 | BinaryExpression, 9 | BreakStatement, 10 | CallExpression, 11 | CatchClause, 12 | ChainExpression, 13 | ConditionalExpression, 14 | ContinueStatement, 15 | DoWhileStatement, 16 | DebuggerStatement, 17 | EmptyStatement, 18 | ExpressionStatement, 19 | ForStatement, 20 | ForInStatement, 21 | FunctionDeclaration, 22 | FunctionExpression, 23 | Identifier, 24 | IfStatement, 25 | Import, 26 | Literal, 27 | LabeledStatement, 28 | LogicalExpression, 29 | MemberExpression, 30 | NewExpression, 31 | ObjectExpression, 32 | Program, 33 | Property, 34 | RestElement, 35 | ReturnStatement, 36 | SequenceExpression, 37 | SwitchStatement, 38 | SwitchCase, 39 | TemplateElement, 40 | TemplateLiteral, 41 | ThisExpression, 42 | ThrowStatement, 43 | TryStatement, 44 | UnaryExpression, 45 | UpdateExpression, 46 | VariableDeclaration, 47 | VariableDeclarator, 48 | WhileStatement, 49 | WithStatement, 50 | ArrayPattern, 51 | AssignmentPattern, 52 | SpreadElement, 53 | ObjectPattern, 54 | ArrowParameterPlaceHolder, 55 | MetaProperty, 56 | Super, 57 | TaggedTemplateExpression, 58 | YieldExpression, 59 | ArrowFunctionExpression, 60 | AwaitExpression, 61 | ClassBody, 62 | ClassDeclaration, 63 | ForOfStatement, 64 | MethodDefinition, 65 | ImportSpecifier, 66 | ImportDefaultSpecifier, 67 | ImportNamespaceSpecifier, 68 | ImportDeclaration, 69 | ExportSpecifier, 70 | ExportNamedDeclaration, 71 | ExportAllDeclaration, 72 | ExportDefaultDeclaration, 73 | ClassExpression, 74 | ValueWrapper 75 | }; 76 | -------------------------------------------------------------------------------- /src/Topaz/Interop/ITypeProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Tenray.Topaz.Interop; 5 | 6 | public interface ITypeProxy 7 | { 8 | /// 9 | /// Proxied type. 10 | /// 11 | Type ProxiedType { get; } 12 | 13 | /// 14 | /// Calls generic or non-generic constructor. 15 | /// 16 | /// Generic and constructor arguments. 17 | /// 18 | object CallConstructor(IReadOnlyList args); 19 | 20 | /// 21 | /// Returns true if a member is found, false otherwise. 22 | /// Output value is member of the static type. 23 | /// If static type member is a function then output value is IInvokable. 24 | /// 25 | /// Member name or indexed member value. 26 | /// Returned value. 27 | /// Indicates if the member 28 | /// retrieval should be through an indexed property. 29 | /// 30 | bool TryGetStaticMember( 31 | object member, 32 | out object value, 33 | bool isIndexedProperty = false); 34 | 35 | /// 36 | /// Returns true if a member is found and updated with new value, 37 | /// false otherwise. 38 | /// If statyic type member is a function, returns false. 39 | /// 40 | /// Member name or indexed member value. 41 | /// New value. 42 | /// Indicates if the member 43 | /// retrieval should be through an indexed property. 44 | /// 45 | bool TrySetStaticMember( 46 | object member, 47 | object value, 48 | bool isIndexedProperty = false); 49 | } 50 | -------------------------------------------------------------------------------- /src/Topaz/Interop/Impl/InvokerUsingReflectionContext.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Reflection; 3 | 4 | namespace Tenray.Topaz.Interop; 5 | 6 | public sealed class InvokerUsingReflectionContext 7 | { 8 | public readonly string Name; 9 | 10 | public readonly MethodInfo[] MethodInfos; 11 | 12 | public readonly ProxyOptions Options; 13 | 14 | public readonly IValueConverter ValueConverter; 15 | 16 | public readonly MethodAndParameterInfo ExtMethodParameterInfo; 17 | 18 | public readonly ParameterInfo[][] AllParameters; 19 | 20 | public InvokerUsingReflectionContext( 21 | string name, 22 | MethodInfo[] methodInfos, 23 | ProxyOptions options, 24 | IValueConverter valueConverter, 25 | MethodAndParameterInfo extMethodParameterInfo = null) 26 | { 27 | Name = name; 28 | ValueConverter = valueConverter; 29 | MethodInfos = methodInfos; 30 | var len = methodInfos.Length; 31 | for (var i = 0; i < len; ++i) 32 | { 33 | var methodInfo = methodInfos[i]; 34 | if (methodInfo.ContainsGenericParameters) 35 | { 36 | var genericParamCount = 37 | methodInfo.GetGenericArguments().Length; 38 | var genericArgs = Enumerable.Range(0, genericParamCount).Select(x => typeof(object)).ToArray(); 39 | methodInfos[i] = methodInfo.MakeGenericMethod(genericArgs); 40 | } 41 | } 42 | 43 | Options = options; 44 | ExtMethodParameterInfo = extMethodParameterInfo; 45 | AllParameters = methodInfos 46 | .Select(x => x.GetParameters()) 47 | .ToArray(); 48 | } 49 | 50 | public InvokerUsingReflection ToInvoker(object instance) 51 | { 52 | return new InvokerUsingReflection(this, instance); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Topaz/Core/ScriptExecutor.CallFunction.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Tenray.Topaz.ErrorHandling; 5 | using Tenray.Topaz.Interop; 6 | 7 | namespace Tenray.Topaz.Core; 8 | 9 | internal sealed partial class ScriptExecutor 10 | { 11 | internal object CallFunction(object callee, IReadOnlyList args, bool optional, CancellationToken token) 12 | { 13 | var value = GetValue(callee); 14 | if (value == null) 15 | { 16 | if (optional) 17 | return GetNullOrUndefined(); 18 | Exceptions.ThrowFunctionIsNotDefined(callee, this); 19 | } 20 | 21 | if (value is TopazFunction topazFunction) 22 | { 23 | return topazFunction.Execute(args, token); 24 | } 25 | 26 | if (value is IInvokable invokable) 27 | { 28 | return invokable.Invoke(args); 29 | } 30 | 31 | return TopazEngine.DelegateInvoker.Invoke(value, args); 32 | } 33 | 34 | internal async ValueTask CallFunctionAsync(object callee, IReadOnlyList args, bool optional, CancellationToken token) 35 | { 36 | var value = GetValue(callee); 37 | if (value == null) 38 | { 39 | if (optional) 40 | return GetNullOrUndefined(); 41 | Exceptions.ThrowFunctionIsNotDefined(callee, this); 42 | } 43 | 44 | if (value is TopazFunction topazFunction) 45 | { 46 | return await topazFunction.ExecuteAsync(args, token); 47 | } 48 | 49 | if (value is IInvokable invokable) 50 | { 51 | return invokable.Invoke(args); 52 | } 53 | 54 | return TopazEngine.DelegateInvoker.Invoke(value, args); 55 | } 56 | 57 | internal object GetNullOrUndefined() 58 | { 59 | return Options.NoUndefined ? null : Undefined.Value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/ArrayExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections; 3 | using System.Threading; 4 | using Tenray.Topaz.API; 5 | using Tenray.Topaz.Core; 6 | using Tenray.Topaz.ErrorHandling; 7 | 8 | namespace Tenray.Topaz.Expressions; 9 | 10 | internal static partial class ArrayExpressionHandler 11 | { 12 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 13 | { 14 | var expr = (ArrayExpression)expression; 15 | var elements = expr.Elements; 16 | var len = elements.Count; 17 | var result = new JsArray(); 18 | if (len == 0) 19 | return result; 20 | for (var i = 0; i < len; ++i) 21 | { 22 | token.ThrowIfCancellationRequested(); 23 | var el = elements[i]; 24 | if (el is SpreadElement spreadElement) 25 | { 26 | var value = scriptExecutor 27 | .ExecuteExpressionAndGetValue(spreadElement.Argument, token); 28 | if (value is not IEnumerable enumerable) 29 | { 30 | return Exceptions.ThrowValueIsNotEnumerable(value); 31 | } 32 | foreach (var item in enumerable) 33 | { 34 | token.ThrowIfCancellationRequested(); 35 | result.AddArrayValue(item); 36 | } 37 | continue; 38 | } 39 | result.AddArrayValue(scriptExecutor.ExecuteStatement(el, token)); 40 | } 41 | // We cannot return salt array or 42 | // unwrap here as assignment pattern can define variables 43 | // in an array. 44 | // Unwrap will happen in the first GetValue statement once. 45 | // And this is the stage where the array is actually used in an expression. 46 | return new TopazArrayWrapper(scriptExecutor, result); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Topaz.Test/NamespaceTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.Collections.Generic; 3 | using Tenray.Topaz.API; 4 | 5 | namespace Tenray.Topaz.Test; 6 | 7 | public sealed class NamespaceTests 8 | { 9 | [Test] 10 | public void TestSingleLevelNamespace() 11 | { 12 | var engine = new TopazEngine(); 13 | engine.AddNamespace("System", new HashSet 14 | { 15 | "System.Int32" 16 | }, false); 17 | 18 | dynamic model = new JsObject(); 19 | engine.SetValue("model", model); 20 | engine.ExecuteScript(@" 21 | var a = System.Int32.Parse('3') 22 | model.a = a 23 | model.b = 9 24 | model.b = System.Collections.ArrayList 25 | "); 26 | Assert.AreEqual(3, model.a); 27 | Assert.IsNull(model.b); 28 | } 29 | 30 | [Test] 31 | public void TestEntireNamespace() 32 | { 33 | var engine = new TopazEngine(); 34 | engine.AddNamespace("System", null, true); 35 | dynamic model = new JsObject(); 36 | engine.SetValue("model", model); 37 | engine.ExecuteScript(@" 38 | var a = new System.Collections.Generic.Dictionary(System.String, System.Int32) 39 | a.Add('key1', 13) 40 | model.a = a 41 | "); 42 | Assert.AreEqual(13, model.a["key1"]); 43 | } 44 | 45 | [Test] 46 | public void TestRestrictedNamespace() 47 | { 48 | var engine = new TopazEngine(); 49 | var whitelist = new HashSet { 50 | "System.Int32", 51 | "System.String", 52 | "System.Collections.Generic.Dictionary" 53 | }; 54 | 55 | engine.AddNamespace("System", whitelist, true); 56 | dynamic model = new JsObject(); 57 | engine.SetValue("model", model); 58 | engine.ExecuteScript(@" 59 | var a = new System.Collections.Generic.Dictionary(System.String, System.Int32) 60 | a.Add('key1', 13) 61 | model.a = a 62 | model.b = System.Double 63 | "); 64 | Assert.AreEqual(13, model.a["key1"]); 65 | Assert.IsNull(model.b); 66 | } 67 | } -------------------------------------------------------------------------------- /src/Topaz/Esprima/Utils/EnumMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | #region Copyright (c) .NET Foundation and Contributors. All rights reserved. 2 | 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | #endregion 26 | 27 | using System; 28 | 29 | namespace Esprima.Utils; 30 | 31 | // System.Runtime.Serialization; 32 | 33 | // https://github.com/dotnet/corefx/blob/795957f4e18238ae7688ccab729f1ef8e825ab3a/src/System.Runtime.Serialization.Primitives/src/System/Runtime/Serialization/EnumMemberAttribute.cs 34 | 35 | [AttributeUsage(AttributeTargets.Field)] 36 | internal sealed class EnumMemberAttribute : Attribute 37 | { 38 | private string _value = ""; 39 | 40 | public string Value 41 | { 42 | get => _value; 43 | set 44 | { 45 | _value = value; 46 | IsValueSetExplicitly = true; 47 | } 48 | } 49 | 50 | public bool IsValueSetExplicitly { get; set; } 51 | } 52 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/UnaryExpression.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Utils; 2 | using static Esprima.EsprimaExceptionHelper; 3 | 4 | namespace Esprima.Ast; 5 | 6 | public enum UnaryOperator 7 | { 8 | [EnumMember(Value = "+")] Plus, 9 | [EnumMember(Value = "-")] Minus, 10 | [EnumMember(Value = "~")] BitwiseNot, 11 | [EnumMember(Value = "!")] LogicalNot, 12 | [EnumMember(Value = "delete")] Delete, 13 | [EnumMember(Value = "void")] Void, 14 | [EnumMember(Value = "typeof")] TypeOf, 15 | [EnumMember(Value = "++")] Increment, 16 | [EnumMember(Value = "--")] Decrement 17 | } 18 | 19 | public class UnaryExpression : Expression 20 | { 21 | public readonly UnaryOperator Operator; 22 | public readonly Expression Argument; 23 | public bool Prefix { get; protected set; } 24 | 25 | public UnaryExpression(string? op, Expression arg) : this(Nodes.UnaryExpression, op, arg) 26 | { 27 | } 28 | 29 | protected UnaryExpression(Nodes type, string? op, Expression arg) : base(type) 30 | { 31 | Operator = ParseUnaryOperator(op); 32 | Argument = arg; 33 | Prefix = true; 34 | } 35 | 36 | private static UnaryOperator ParseUnaryOperator(string? op) 37 | { 38 | return op switch 39 | { 40 | "+" => UnaryOperator.Plus, 41 | "-" => UnaryOperator.Minus, 42 | "++" => UnaryOperator.Increment, 43 | "--" => UnaryOperator.Decrement, 44 | "~" => UnaryOperator.BitwiseNot, 45 | "!" => UnaryOperator.LogicalNot, 46 | "delete" => UnaryOperator.Delete, 47 | "void" => UnaryOperator.Void, 48 | "typeof" => UnaryOperator.TypeOf, 49 | _ => ThrowArgumentOutOfRangeException(nameof(op), "Invalid unary operator: " + op) 50 | }; 51 | } 52 | 53 | public override NodeCollection ChildNodes => new(Argument); 54 | 55 | protected internal override void Accept(AstVisitor visitor) 56 | { 57 | visitor.VisitUnaryExpression(this); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/AsyncHandlers/Expressions/ArrayExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Collections; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Tenray.Topaz.API; 6 | using Tenray.Topaz.Core; 7 | using Tenray.Topaz.ErrorHandling; 8 | 9 | namespace Tenray.Topaz.Expressions; 10 | 11 | internal static partial class ArrayExpressionHandler 12 | { 13 | internal async static ValueTask ExecuteAsync(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 14 | { 15 | var expr = (ArrayExpression)expression; 16 | var elements = expr.Elements; 17 | var len = elements.Count; 18 | var result = new JsArray(); 19 | if (len == 0) 20 | return result; 21 | for (var i = 0; i < len; ++i) 22 | { 23 | token.ThrowIfCancellationRequested(); 24 | var el = elements[i]; 25 | if (el is SpreadElement spreadElement) 26 | { 27 | var value = await scriptExecutor 28 | .ExecuteExpressionAndGetValueAsync(spreadElement.Argument, token); 29 | if (value is not IEnumerable enumerable) 30 | { 31 | return Exceptions.ThrowValueIsNotEnumerable(value); 32 | } 33 | foreach (var item in enumerable) 34 | { 35 | token.ThrowIfCancellationRequested(); 36 | result.AddArrayValue(item); 37 | } 38 | continue; 39 | } 40 | result.AddArrayValue(await scriptExecutor.ExecuteStatementAsync(el, token)); 41 | } 42 | // We cannot return salt array or 43 | // unwrap here as assignment pattern can define variables 44 | // in an array. 45 | // Unwrap will happen in the first GetValue statement once. 46 | // And this is the stage where the array is actually used in an expression. 47 | return new TopazArrayWrapper(scriptExecutor, result); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/ParserOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Esprima; 2 | 3 | /// 4 | /// Parser options. 5 | /// 6 | public sealed class ParserOptions 7 | { 8 | /// 9 | /// Create a new instance. 10 | /// 11 | public ParserOptions() : this(new ErrorHandler()) 12 | { 13 | } 14 | 15 | /// 16 | /// Create a new instance. 17 | /// 18 | /// A string representing where the code is coming from, if an error occurs. 19 | public ParserOptions(string source) : this(new ErrorHandler { Source = source }) 20 | { 21 | } 22 | 23 | /// 24 | /// Create a new instance. 25 | /// 26 | /// The to use to handle errors. 27 | public ParserOptions(IErrorHandler errorHandler) 28 | { 29 | ErrorHandler = errorHandler; 30 | } 31 | 32 | /// 33 | /// Gets or sets whether the tokens are included in the parsed tree. 34 | /// 35 | public bool Tokens { get; set; } 36 | 37 | /// 38 | /// Gets or sets whether the comments are included in the parsed tree. 39 | /// 40 | public bool Comment { get; set; } 41 | 42 | /// 43 | /// Gets or sets whether the parser is tolerant to errors. 44 | /// 45 | public bool Tolerant { get; set; } = true; 46 | 47 | /// 48 | /// Gets or sets the to use. 49 | /// 50 | public IErrorHandler ErrorHandler { get; set; } 51 | 52 | /// 53 | /// Gets or sets whether the Regular Expression syntax should be converted to a .NET compatible one. 54 | /// 55 | public bool AdaptRegexp { get; set; } 56 | 57 | /// 58 | /// Gets or sets whether top level await statements allowed. 59 | /// 60 | public bool TopLevelAsync { get; set; } 61 | } 62 | -------------------------------------------------------------------------------- /src/Topaz/Esprima/Ast/Literal.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Esprima.Utils; 3 | 4 | namespace Esprima.Ast; 5 | 6 | public sealed class Literal : Expression 7 | { 8 | public string? StringValue => TokenType == TokenType.StringLiteral ? Value as string : null; 9 | public readonly double NumericValue; 10 | public bool BooleanValue => TokenType == TokenType.BooleanLiteral && NumericValue != 0; 11 | public Regex? RegexValue => TokenType == TokenType.RegularExpression ? (Regex?) Value : null; 12 | 13 | public readonly RegexValue? Regex; 14 | public readonly object? Value; 15 | public readonly string Raw; 16 | public readonly TokenType TokenType; 17 | 18 | internal Literal(TokenType tokenType, object? value, string raw) : base(Nodes.Literal) 19 | { 20 | TokenType = tokenType; 21 | Value = value; 22 | Raw = raw; 23 | } 24 | 25 | public Literal(string? value, string raw) : this(TokenType.StringLiteral, value, raw) 26 | { 27 | } 28 | 29 | public Literal(bool value, string raw) : this(TokenType.BooleanLiteral, value, raw) 30 | { 31 | NumericValue = value ? 1 : 0; 32 | } 33 | 34 | public Literal(double value, string raw) : this(TokenType.NumericLiteral, value, raw) 35 | { 36 | NumericValue = value; 37 | } 38 | 39 | public Literal(long value, string raw) : this(TokenType.NumericLiteral, value, raw) 40 | { 41 | NumericValue = value; 42 | } 43 | 44 | public Literal(string raw) : this(TokenType.NullLiteral, null, raw) 45 | { 46 | } 47 | 48 | public Literal(string pattern, string flags, object? value, string raw) : this(TokenType.RegularExpression, value, raw) 49 | { 50 | // value is null if a Regex object couldn't be created out of the pattern or options 51 | Regex = new RegexValue(pattern, flags); 52 | } 53 | 54 | public override NodeCollection ChildNodes => NodeCollection.Empty; 55 | 56 | protected internal override void Accept(AstVisitor visitor) 57 | { 58 | visitor.VisitLiteral(this); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Expressions/AwaitExpressionHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System.Reflection; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Tenray.Topaz.Core; 6 | 7 | namespace Tenray.Topaz.Expressions; 8 | 9 | internal static partial class AwaitExpressionHandler 10 | { 11 | internal static object Execute(ScriptExecutor scriptExecutor, Node expression, CancellationToken token) 12 | { 13 | var expr = (AwaitExpression)expression; 14 | var result = scriptExecutor.ExecuteExpressionAndGetValue(expr.Argument, token); 15 | var awaitHandler = scriptExecutor.TopazEngine.AwaitExpressionHandler; 16 | if (result == null) 17 | return null; 18 | if (awaitHandler != null) 19 | { 20 | var task1 = awaitHandler.HandleAwaitExpression(result, token); 21 | task1.Wait(token); 22 | return task1.Result; 23 | } 24 | if (result is Task task) 25 | { 26 | task.Wait(token); 27 | var type = task.GetType(); 28 | var returnType = type.GetMethod("GetAwaiter").ReturnType.GetMethod("GetResult").ReturnType; 29 | if (returnType != typeof(void) && returnType.FullName != "System.Threading.Tasks.VoidTaskResult") 30 | return GetTaskResult(task); 31 | return null; 32 | } 33 | 34 | if (result is ValueTask valueTask) 35 | { 36 | valueTask.AsTask().Wait(token); 37 | return null; 38 | } 39 | 40 | var type2 = result.GetType(); 41 | if (type2.IsGenericType && typeof(ValueTask<>) == type2.GetGenericTypeDefinition()) 42 | { 43 | dynamic r = result; 44 | r.AsTask().Wait(token); 45 | return r.Result; 46 | } 47 | return result; 48 | } 49 | 50 | internal static object GetTaskResult(Task task) 51 | { 52 | var property = task.GetType().GetProperty("Result", 53 | BindingFlags.Public | BindingFlags.Instance); 54 | if (property == null) 55 | return null; 56 | return property.GetValue(task); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Topaz/AstHandlers/Statements/TryStatementHandler.cs: -------------------------------------------------------------------------------- 1 | using Esprima.Ast; 2 | using System; 3 | using System.Threading; 4 | using Tenray.Topaz.Core; 5 | 6 | namespace Tenray.Topaz.Statements; 7 | 8 | internal static partial class TryStatementHandler 9 | { 10 | internal static object Execute(ScriptExecutor scriptExecutor, Node statement, CancellationToken token) 11 | { 12 | var expr = (TryStatement)statement; 13 | var block = expr.Block; 14 | var handler = expr.Handler; 15 | var finalizer = expr.Finalizer; 16 | var bodyScope = scriptExecutor.NewBlockScope(); 17 | try 18 | { 19 | return bodyScope.ExecuteStatement(block, token); 20 | } 21 | catch (OperationCanceledException cancelledException) 22 | { 23 | if (cancelledException.CancellationToken == token) 24 | { 25 | throw; 26 | } 27 | else 28 | { 29 | if (handler == null) 30 | throw; 31 | return HandleCatch(scriptExecutor, handler, cancelledException, token); 32 | } 33 | } 34 | catch (Exception e) 35 | { 36 | if (handler == null) 37 | throw; 38 | return HandleCatch(scriptExecutor, handler, e, token); 39 | } 40 | finally 41 | { 42 | bodyScope.ReturnToPool(); 43 | if (finalizer != null) 44 | { 45 | bodyScope = scriptExecutor.NewBlockScope(); 46 | bodyScope.ExecuteStatement(finalizer, token); 47 | bodyScope.ReturnToPool(); 48 | } 49 | } 50 | } 51 | 52 | private static object HandleCatch(ScriptExecutor scriptExecutor, CatchClause handler, Exception e, CancellationToken token) 53 | { 54 | var bodyScope = scriptExecutor.NewBlockScope(); 55 | var identifier = handler.Param as Identifier; 56 | bodyScope.DefineVariable(identifier, e, VariableKind.Var); 57 | var result = bodyScope.ExecuteStatement(handler.Body, token); 58 | bodyScope.ReturnToPool(); 59 | return result; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Topaz/Topaz.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | true 6 | en-US 7 | net9.0;net8.0;net7.0;net6.0 8 | https://github.com/koculu/topaz 9 | true 10 | README.md 11 | 11.0 12 | true 13 | true 14 | snupkg 15 | git 16 | true 17 | Tenray.Topaz 18 | True 19 | False 20 | All 21 | False 22 | False 23 | 24 | 25 | 26 | D:\code\modern\topaz\src\Tenray.Topaz\Tenray.Topaz.xml 27 | 28 | 29 | 30 | 31 | True 32 | \ 33 | 34 | 35 | True 36 | \ 37 | 38 | 39 | True 40 | \3rdParty\ 41 | 42 | 43 | 44 | 45 | 46 | 47 | all 48 | runtime; build; native; contentfiles; analyzers; buildtransitive 49 | 50 | 51 | 52 | --------------------------------------------------------------------------------