summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-11-18 15:58:12 -0500
committerGitHub <noreply@github.com>2021-11-18 15:58:12 -0500
commit1d5f815b3964edee8a2d701e1a6cc078c89d677f (patch)
treeaa5b4b1473344e635d7ce1d2159fc57eeb40b841
parentb482844b689eb109ee1d70c527e098400ac6d409 (diff)
RTTI/JSON (#2021)
* #include an absolute path didn't work - because paths were taken to always be relative. * Use 'Process' to communicate with an command line tool. * Remove slang-win-stream * Tidy up windows ProcessUtil. * First version of BufferedReadStream. * Windows working IPC for steams. * Test proxy count option. * Split Process/ProcessUtil. Process is platform dependant. ProcessUtil are functions that are platform independent. * First implementation of Unix Process interface. * Unix process compiles on cygwin. * Fix typo in unix process. * Separate unix pipe stream error of invalid access, from pipe availability. * Fix in standard line extraction. * Make fd non blocking. * Fix issues with Windows Process streams. * Added UnixPipe. * Some fixes around UnixPipeStream. * Make a unix stream closed explicit. * Hack to debug linux process/stream. * Revert to old linux pipe handling. * Pass executable path for unit tests. Split out CommandLine into own source. * Small improvements in process/command line. * Check process behavior with crash. * Make stderr and stdout unbuffered for crash testing. * Only turn disable buffering in crash test. * Disable crash test on CI. * Fix crash on clang/linux. * Enable crash test. Remove _appendBuffer as can use StreamUtil functionality. * Added inital processing for http headers. * Small improvements to HttpHeader. * First pass HTTPPacketConnection working on windows. * Enable other Process communication tests. * Update comments. * WIP JSON RPC. * Add terminate to Process. Made JSONRPC a Util. * Small tidy up around HTTPPacketConnection. * Improve process termination options. * WIP for test-server. * Add diagnostics error handling to test-server. * Improved JSON support. Parsing/creating JSON-RPC messages. * WIP JSONRPC parsing. * First pass RttiInfo support. * WIP converting between JSON/native types. * Project files. * Split out RttiUtil. Made RttiInfo constuction thread safe. * WIP RTTI<->JSON. * Add diagnostics to JSON<->native conversions. * Make RttiInfo for structs globals. Avoids problem around derived types (like pointers), being able to cause an abort. * Add pointer support to RTTI. Fixed some compilation issues on linux. * Add fixed array support. * Added Rtti unit test. * Add rtti unit test. * Split out quoted/unquoted key handling. Fix bugs in JSON value/container. Added JSON native test. * Make default array allocator use malloc/free. Remove the new[] handler (doesn't work on visuals studio). * Fix for linux warning. * Remove some test code. * Fix issues on x86 win. * Fix warning on aarch64.
-rw-r--r--build/visual-studio/compiler-core/compiler-core.vcxproj2
-rw-r--r--build/visual-studio/compiler-core/compiler-core.vcxproj.filters6
-rw-r--r--build/visual-studio/core/core.vcxproj4
-rw-r--r--build/visual-studio/core/core.vcxproj.filters12
-rw-r--r--build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj2
-rw-r--r--build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters6
-rw-r--r--build/visual-studio/test-server/test-server.vcxproj286
-rw-r--r--build/visual-studio/test-server/test-server.vcxproj.filters27
-rw-r--r--premake5.lua6
-rw-r--r--slang.sln15
-rw-r--r--source/compiler-core/slang-json-diagnostic-defs.h5
-rw-r--r--source/compiler-core/slang-json-native.cpp377
-rw-r--r--source/compiler-core/slang-json-native.h50
-rw-r--r--source/compiler-core/slang-json-parser.cpp27
-rw-r--r--source/compiler-core/slang-json-parser.h6
-rw-r--r--source/compiler-core/slang-json-rpc.cpp402
-rw-r--r--source/compiler-core/slang-json-rpc.h101
-rw-r--r--source/compiler-core/slang-json-value.cpp83
-rw-r--r--source/compiler-core/slang-json-value.h34
-rw-r--r--source/core/slang-allocator.h21
-rw-r--r--source/core/slang-common.h3
-rw-r--r--source/core/slang-list.h13
-rw-r--r--source/core/slang-rtti-info.cpp184
-rw-r--r--source/core/slang-rtti-info.h341
-rw-r--r--source/core/slang-rtti-util.cpp392
-rw-r--r--source/core/slang-rtti-util.h30
-rw-r--r--tools/slang-unit-test/unit-test-json-native.cpp117
-rw-r--r--tools/slang-unit-test/unit-test-rtti.cpp74
-rw-r--r--tools/test-server/test-server-diagnostic-defs.h26
-rw-r--r--tools/test-server/test-server-diagnostics.cpp13
-rw-r--r--tools/test-server/test-server-diagnostics.h21
-rw-r--r--tools/test-server/test-server-main.cpp510
32 files changed, 3169 insertions, 27 deletions
diff --git a/build/visual-studio/compiler-core/compiler-core.vcxproj b/build/visual-studio/compiler-core/compiler-core.vcxproj
index 46603abc8..5e70d59f7 100644
--- a/build/visual-studio/compiler-core/compiler-core.vcxproj
+++ b/build/visual-studio/compiler-core/compiler-core.vcxproj
@@ -286,6 +286,7 @@
<ClInclude Include="..\..\..\source\compiler-core\slang-json-diagnostic-defs.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-json-diagnostics.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-json-lexer.h" />
+ <ClInclude Include="..\..\..\source\compiler-core\slang-json-native.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-json-parser.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-json-rpc.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-json-value.h" />
@@ -314,6 +315,7 @@
<ClCompile Include="..\..\..\source\compiler-core\slang-include-system.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-json-diagnostics.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-json-lexer.cpp" />
+ <ClCompile Include="..\..\..\source\compiler-core\slang-json-native.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-json-parser.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-json-rpc.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-json-value.cpp" />
diff --git a/build/visual-studio/compiler-core/compiler-core.vcxproj.filters b/build/visual-studio/compiler-core/compiler-core.vcxproj.filters
index 91ee4c5d9..c392612f6 100644
--- a/build/visual-studio/compiler-core/compiler-core.vcxproj.filters
+++ b/build/visual-studio/compiler-core/compiler-core.vcxproj.filters
@@ -45,6 +45,9 @@
<ClInclude Include="..\..\..\source\compiler-core\slang-json-lexer.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\compiler-core\slang-json-native.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\compiler-core\slang-json-parser.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -125,6 +128,9 @@
<ClCompile Include="..\..\..\source\compiler-core\slang-json-lexer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\compiler-core\slang-json-native.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\compiler-core\slang-json-parser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/build/visual-studio/core/core.vcxproj b/build/visual-studio/core/core.vcxproj
index 2a23f0116..817dca55d 100644
--- a/build/visual-studio/core/core.vcxproj
+++ b/build/visual-studio/core/core.vcxproj
@@ -311,6 +311,8 @@
<ClInclude Include="..\..\..\source\core\slang-render-api-util.h" />
<ClInclude Include="..\..\..\source\core\slang-riff-file-system.h" />
<ClInclude Include="..\..\..\source\core\slang-riff.h" />
+ <ClInclude Include="..\..\..\source\core\slang-rtti-info.h" />
+ <ClInclude Include="..\..\..\source\core\slang-rtti-util.h" />
<ClInclude Include="..\..\..\source\core\slang-secure-crt.h" />
<ClInclude Include="..\..\..\source\core\slang-semantic-version.h" />
<ClInclude Include="..\..\..\source\core\slang-shared-library.h" />
@@ -356,6 +358,8 @@
<ClCompile Include="..\..\..\source\core\slang-render-api-util.cpp" />
<ClCompile Include="..\..\..\source\core\slang-riff-file-system.cpp" />
<ClCompile Include="..\..\..\source\core\slang-riff.cpp" />
+ <ClCompile Include="..\..\..\source\core\slang-rtti-info.cpp" />
+ <ClCompile Include="..\..\..\source\core\slang-rtti-util.cpp" />
<ClCompile Include="..\..\..\source\core\slang-semantic-version.cpp" />
<ClCompile Include="..\..\..\source\core\slang-shared-library.cpp" />
<ClCompile Include="..\..\..\source\core\slang-signal.cpp" />
diff --git a/build/visual-studio/core/core.vcxproj.filters b/build/visual-studio/core/core.vcxproj.filters
index e526a558b..51b0313f6 100644
--- a/build/visual-studio/core/core.vcxproj.filters
+++ b/build/visual-studio/core/core.vcxproj.filters
@@ -120,6 +120,12 @@
<ClInclude Include="..\..\..\source\core\slang-riff.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-rtti-info.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-rtti-util.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-secure-crt.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -251,6 +257,12 @@
<ClCompile Include="..\..\..\source\core\slang-riff.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-rtti-info.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\source\core\slang-rtti-util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\core\slang-semantic-version.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj
index c8eadc484..4869c220f 100644
--- a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj
+++ b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj
@@ -277,11 +277,13 @@
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-compression.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-find-type-by-name.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-free-list.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-json-native.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-json.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-memory-arena.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-path.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-process.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-riff.cpp" />
+ <ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-rtti.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-short-list.cpp" />
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-string.cpp" />
<ClCompile Include="..\..\..\tools\unit-test\slang-unit-test.cpp" />
diff --git a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters
index 2578a8736..9db9bee4c 100644
--- a/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters
+++ b/build/visual-studio/slang-unit-test-tool/slang-unit-test-tool.vcxproj.filters
@@ -32,6 +32,9 @@
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-free-list.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-json-native.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-json.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -47,6 +50,9 @@
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-riff.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-rtti.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\slang-unit-test\unit-test-short-list.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/build/visual-studio/test-server/test-server.vcxproj b/build/visual-studio/test-server/test-server.vcxproj
new file mode 100644
index 000000000..756d1954b
--- /dev/null
+++ b/build/visual-studio/test-server/test-server.vcxproj
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|ARM">
+ <Configuration>Debug</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug aarch64|Win32">
+ <Configuration>Debug aarch64</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug aarch64|x64">
+ <Configuration>Debug aarch64</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug aarch64|ARM">
+ <Configuration>Debug aarch64</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|ARM">
+ <Configuration>Release</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release aarch64|Win32">
+ <Configuration>Release aarch64</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release aarch64|x64">
+ <Configuration>Release aarch64</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release aarch64|ARM">
+ <Configuration>Release aarch64</Configuration>
+ <Platform>ARM</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{23149706-C12F-4329-B6AA-8266407C32D3}</ProjectGuid>
+ <IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>test-server</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug aarch64|ARM'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ <PlatformToolset>v142</PlatformToolset>
+ <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug aarch64|ARM'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\bin\windows-x86\debug\</OutDir>
+ <IntDir>..\..\..\intermediate\windows-x86\debug\test-server\</IntDir>
+ <TargetName>test-server</TargetName>
+ <TargetExt>.exe</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\bin\windows-x64\debug\</OutDir>
+ <IntDir>..\..\..\intermediate\windows-x64\debug\test-server\</IntDir>
+ <TargetName>test-server</TargetName>
+ <TargetExt>.exe</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug aarch64|ARM'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\bin\windows-aarch64\debug\</OutDir>
+ <IntDir>..\..\..\intermediate\windows-aarch64\debug\test-server\</IntDir>
+ <TargetName>test-server</TargetName>
+ <TargetExt>.exe</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\bin\windows-x86\release\</OutDir>
+ <IntDir>..\..\..\intermediate\windows-x86\release\test-server\</IntDir>
+ <TargetName>test-server</TargetName>
+ <TargetExt>.exe</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\bin\windows-x64\release\</OutDir>
+ <IntDir>..\..\..\intermediate\windows-x64\release\test-server\</IntDir>
+ <TargetName>test-server</TargetName>
+ <TargetExt>.exe</TargetExt>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\bin\windows-aarch64\release\</OutDir>
+ <IntDir>..\..\..\intermediate\windows-aarch64\release\test-server\</IntDir>
+ <TargetName>test-server</TargetName>
+ <TargetExt>.exe</TargetExt>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <Optimization>Disabled</Optimization>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <Optimization>Disabled</Optimization>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug aarch64|ARM'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <Optimization>Disabled</Optimization>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <Optimization>Full</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <Optimization>Full</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM'">
+ <ClCompile>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <Optimization>Full</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\tools\test-server\test-server-diagnostic-defs.h" />
+ <ClInclude Include="..\..\..\tools\test-server\test-server-diagnostics.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\tools\test-server\test-server-diagnostics.cpp" />
+ <ClCompile Include="..\..\..\tools\test-server\test-server-main.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\compiler-core\compiler-core.vcxproj">
+ <Project>{12C1E89D-F5D0-41D3-8E8D-FB3F358F8126}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\core\core.vcxproj">
+ <Project>{F9BE7957-8399-899E-0C49-E714FDDD4B65}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\slang\slang.vcxproj">
+ <Project>{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/build/visual-studio/test-server/test-server.vcxproj.filters b/build/visual-studio/test-server/test-server.vcxproj.filters
new file mode 100644
index 000000000..2a5d2b9d3
--- /dev/null
+++ b/build/visual-studio/test-server/test-server.vcxproj.filters
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{21EB8090-0D4E-1035-B6D3-48EBA215DCB7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\tools\test-server\test-server-diagnostic-defs.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\tools\test-server\test-server-diagnostics.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\tools\test-server\test-server-diagnostics.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\tools\test-server\test-server-main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/premake5.lua b/premake5.lua
index c8c02ff09..13fee9e22 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -763,6 +763,12 @@ end
links { "compiler-core", "core", "slang" }
+ tool "test-server"
+ uuid "23149706-C12F-4329-B6AA-8266407C32D3"
+ includedirs { "." }
+
+ links { "compiler-core", "core", "slang" }
+
--
-- `slang-generate` is a tool we use for source code generation on
-- the compiler. It depends on the `core` library, so we need to
diff --git a/slang.sln b/slang.sln
index 08f03ae19..bdd9545dd 100644
--- a/slang.sln
+++ b/slang.sln
@@ -19,6 +19,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-generate", "build\vis
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-proxy", "build\visual-studio\test-proxy\test-proxy.vcxproj", "{BE412850-4BB9-429A-877C-BFBC4B34186C}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-server", "build\visual-studio\test-server\test-server.vcxproj", "{23149706-C12F-4329-B6AA-8266407C32D3}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "compiler-core", "build\visual-studio\compiler-core\compiler-core.vcxproj", "{12C1E89D-F5D0-41D3-8E8D-FB3F358F8126}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "build\visual-studio\core\core.vcxproj", "{F9BE7957-8399-899E-0C49-E714FDDD4B65}"
@@ -181,6 +183,18 @@ Global
{BE412850-4BB9-429A-877C-BFBC4B34186C}.Release|Win32.Build.0 = Release|Win32
{BE412850-4BB9-429A-877C-BFBC4B34186C}.Release|x64.ActiveCfg = Release|x64
{BE412850-4BB9-429A-877C-BFBC4B34186C}.Release|x64.Build.0 = Release|x64
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Debug|aarch64.ActiveCfg = Debug aarch64|ARM
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Debug|aarch64.Build.0 = Debug aarch64|ARM
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Debug|Win32.Build.0 = Debug|Win32
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Debug|x64.ActiveCfg = Debug|x64
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Debug|x64.Build.0 = Debug|x64
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Release|aarch64.ActiveCfg = Release aarch64|ARM
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Release|aarch64.Build.0 = Release aarch64|ARM
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Release|Win32.ActiveCfg = Release|Win32
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Release|Win32.Build.0 = Release|Win32
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Release|x64.ActiveCfg = Release|x64
+ {23149706-C12F-4329-B6AA-8266407C32D3}.Release|x64.Build.0 = Release|x64
{12C1E89D-F5D0-41D3-8E8D-FB3F358F8126}.Debug|aarch64.ActiveCfg = Debug aarch64|ARM
{12C1E89D-F5D0-41D3-8E8D-FB3F358F8126}.Debug|aarch64.Build.0 = Debug aarch64|ARM
{12C1E89D-F5D0-41D3-8E8D-FB3F358F8126}.Debug|Win32.ActiveCfg = Debug|Win32
@@ -446,6 +460,7 @@ Global
{7F773DD9-EB8F-2403-B43C-B49C2014B99C} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{66174227-8541-41FC-A6DF-4764FC66F78E} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{BE412850-4BB9-429A-877C-BFBC4B34186C} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
+ {23149706-C12F-4329-B6AA-8266407C32D3} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{4B47A364-37C4-96A7-6041-97BB4C1D333B} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{37BED5B5-23FA-D81F-8C0C-F1167867813A} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
{57C81DD3-4304-213D-AC16-39349871C957} = {EB5FC2C6-D72D-B6CC-C0C1-26F3AC2E9231}
diff --git a/source/compiler-core/slang-json-diagnostic-defs.h b/source/compiler-core/slang-json-diagnostic-defs.h
index da3b2a28c..23bea9b79 100644
--- a/source/compiler-core/slang-json-diagnostic-defs.h
+++ b/source/compiler-core/slang-json-diagnostic-defs.h
@@ -36,4 +36,9 @@ DIAGNOSTIC(20006, Error, expectingValueName, "expecting value name [null, true,
DIAGNOSTIC(20007, Error, unexpectedTokenExpectedTokenType, "unexpected '$0', expected '$1'")
DIAGNOSTIC(20008, Error, unexpectedToken, "unexpected '$0'")
+DIAGNOSTIC(20009, Error, unableToConvertField, "unable to convert field '$0' in type '$1'")
+DIAGNOSTIC(20010, Error, fieldNotFound, "field '$0' not found in type '$1'")
+DIAGNOSTIC(20011, Error, fieldNotDefinedOnType, "field '$0' not defined on type '$1'")
+DIAGNOSTIC(20011, Error, fieldRequiredOnType, "field '$0' required on '$1'")
+
#undef DIAGNOSTIC
diff --git a/source/compiler-core/slang-json-native.cpp b/source/compiler-core/slang-json-native.cpp
new file mode 100644
index 000000000..5b2fb5db4
--- /dev/null
+++ b/source/compiler-core/slang-json-native.cpp
@@ -0,0 +1,377 @@
+#include "slang-json-native.h"
+
+#include "../../slang-com-helper.h"
+
+#include "../core/slang-rtti-util.h"
+
+#include "slang-json-diagnostics.h"
+
+namespace Slang {
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! JSONToNativeConverter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+/* static */Index JSONToNativeConverter::_getFieldCount(const StructRttiInfo* structRttiInfo)
+{
+ if (structRttiInfo->m_super)
+ {
+ return _getFieldCount(structRttiInfo->m_super) + structRttiInfo->m_fieldCount;
+ }
+ else
+ {
+ return structRttiInfo->m_fieldCount;
+ }
+}
+
+/* static */Index JSONToNativeConverter::_findFieldIndex(const StructRttiInfo* structRttiInfo, const UnownedStringSlice& fieldName)
+{
+ if (structRttiInfo->m_super)
+ {
+ const Index index = _findFieldIndex(structRttiInfo->m_super, fieldName);
+ if (index >= 0)
+ {
+ return index + _getFieldCount(structRttiInfo->m_super);
+ }
+ }
+
+ ConstArrayView<StructRttiInfo::Field> fields(structRttiInfo->m_fields, structRttiInfo->m_fieldCount);
+
+ Index index = fields.findFirstIndex([fieldName](const StructRttiInfo::Field& field) ->bool { return fieldName == field.m_name; });
+ if (index >= 0 && structRttiInfo->m_super)
+ {
+ index += _getFieldCount(structRttiInfo->m_super);
+ }
+
+ return index;
+}
+
+SlangResult JSONToNativeConverter::_structToNative(const ConstArrayView<JSONKeyValue>& pairs, const StructRttiInfo* structRttiInfo, void* out, Index& outFieldCount)
+{
+ Index fieldCount = 0;
+
+ if (structRttiInfo->m_super)
+ {
+ SLANG_RETURN_ON_FAIL(_structToNative(pairs, structRttiInfo->m_super, out, fieldCount));
+ }
+
+ Byte* dst = (Byte*)out;
+
+ const Index count = structRttiInfo->m_fieldCount;
+
+ for (Index i = 0; i < count; ++i)
+ {
+ const auto& field = structRttiInfo->m_fields[i];
+
+ auto key = m_container->findKey(UnownedStringSlice(field.m_name));
+
+ if (key == 0)
+ {
+ if (field.m_flags & StructRttiInfo::Flag::Optional)
+ {
+ continue;
+ }
+
+ m_sink->diagnose(SourceLoc(), JSONDiagnostics::fieldRequiredOnType, field.m_name, structRttiInfo->m_name);
+
+ // Unable to find this key
+ return SLANG_FAIL;
+ }
+
+ // If there are any of the pairs, that are not in the type.. it's an error
+ const Index index = pairs.findFirstIndex([key](const JSONKeyValue& pair) -> bool { return pair.key == key; });
+ if (index < 0)
+ {
+ if (field.m_flags & StructRttiInfo::Flag::Optional)
+ {
+ continue;
+ }
+
+ m_sink->diagnose(SourceLoc(), JSONDiagnostics::fieldRequiredOnType, field.m_name, structRttiInfo->m_name);
+
+ // Unable to find this key
+ return SLANG_FAIL;
+ }
+
+ auto& pair = pairs[index];
+
+ // Copy the field over
+ SLANG_RETURN_ON_FAIL(convert(pair.value, field.m_type, dst + field.m_offset));
+
+ // Field was handled
+ ++fieldCount;
+ }
+
+ // Write off the amount of fields converted/handled.
+ outFieldCount = fieldCount;
+ return SLANG_OK;
+}
+
+SlangResult JSONToNativeConverter::convert(const JSONValue& in, const RttiInfo* rttiInfo, void* out)
+{
+ if (rttiInfo->isIntegral())
+ {
+ return RttiUtil::setInt(m_container->asInteger(in), rttiInfo, out);
+ }
+ else if (rttiInfo->isFloat())
+ {
+ return RttiUtil::setFromDouble(m_container->asFloat(in), rttiInfo, out);
+ }
+
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::Bool:
+ {
+ *(bool*)out = m_container->asBool(in);
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::Struct:
+ {
+ if (in.getKind() != JSONValue::Kind::Object)
+ {
+ return SLANG_FAIL;
+ }
+
+ auto pairs = m_container->getObject(in);
+ const StructRttiInfo* structRttiInfo = static_cast<const StructRttiInfo*>(rttiInfo);
+
+ Index fieldCount = 0;
+ SLANG_RETURN_ON_FAIL(_structToNative(pairs, structRttiInfo, out, fieldCount));
+
+ if (fieldCount != pairs.getCount())
+ {
+ // We want to find the fields not found in the type
+
+ for (auto& pair : pairs)
+ {
+ UnownedStringSlice fieldName = m_container->getStringFromKey(pair.key);
+ const Index index = _findFieldIndex(structRttiInfo, UnownedStringSlice(fieldName));
+
+ if (index < 0)
+ {
+ m_sink->diagnose(pair.keyLoc, JSONDiagnostics::fieldNotDefinedOnType, fieldName, structRttiInfo->m_name);
+ }
+ }
+
+ // If these are different then there are fields defined in the object that are *not* defined in class definition
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::Enum:
+ {
+ return SLANG_E_NOT_IMPLEMENTED;
+ }
+ case RttiInfo::Kind::String:
+ {
+ *(String*)out = m_container->getTransientString(in);
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::UnownedStringSlice:
+ {
+ // Problem -> if the slice is a lexeme, then when we decode with getString, it will lose scope.
+ // So we do something a bit odd and place the decoding string
+
+ *(UnownedStringSlice*)out = m_container->getString(in);
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::List:
+ {
+ if (in.getKind() != JSONValue::Kind::Array)
+ {
+ return SLANG_FAIL;
+ }
+
+ typedef List<Byte> Type;
+ Type& list = *(Type*)out;
+
+ auto arr = m_container->getArray(in);
+
+ const Index count = arr.getCount();
+
+ const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
+ auto elementType = listRttiInfo->m_elementType;
+
+ SLANG_RETURN_ON_FAIL(RttiUtil::setListCount(elementType, out, arr.getCount()));
+
+ // Okay, we need to copy over one by one
+
+ Byte* dstEles = list.getBuffer();
+ for (Index i = 0; i < count; ++i, dstEles += elementType->m_size)
+ {
+ SLANG_RETURN_ON_FAIL(convert(arr[i], elementType, dstEles));
+ }
+
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::Dictionary:
+ {
+ // We can *only* serialize this into a straight JSON object iff the key is a string-like type
+ // We could turn into (say) an array of keys and values
+ break;
+ }
+ case RttiInfo::Kind::Other:
+ {
+ if (rttiInfo == GetRttiInfo<JSONValue>::get())
+ {
+ // Do we need to copy into the container?
+ // As it stands we have to assume src is stored in container.
+ *(JSONValue*)out = in;
+ return SLANG_OK;
+ }
+ return SLANG_FAIL;
+ }
+ default: break;
+ }
+ return SLANG_FAIL;
+}
+
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! NativeToJSONConverter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+SlangResult NativeToJSONConverter::_structToJSON(const StructRttiInfo* structRttiInfo, const void* src, List<JSONKeyValue>& outPairs)
+{
+ // Do the super class first
+ if (structRttiInfo->m_super)
+ {
+ SLANG_RETURN_ON_FAIL(_structToJSON(structRttiInfo, src, outPairs));
+ }
+
+ const Byte* base = (const Byte*)src;
+ const Index count = structRttiInfo->m_fieldCount;
+
+ for (Index i = 0; i < count; ++i)
+ {
+ const auto& field = structRttiInfo->m_fields[i];
+
+ if (field.m_flags & StructRttiInfo::Flag::Optional)
+ {
+ const RttiDefaultValue defaultValue = RttiDefaultValue(field.m_flags & uint8_t(RttiDefaultValue::Mask));
+ if (RttiUtil::isDefault(defaultValue, field.m_type, base + field.m_offset))
+ {
+ // If it's a default, we don't bother writing it
+ continue;
+ }
+ }
+
+ JSONKeyValue pair;
+ pair.key = m_container->getKey(UnownedStringSlice(field.m_name));
+ auto res = convert(field.m_type, base + field.m_offset, pair.value);
+
+ if (SLANG_FAILED(res))
+ {
+ m_sink->diagnose(SourceLoc(), JSONDiagnostics::unableToConvertField, field.m_name, structRttiInfo->m_name);
+ return res;
+ }
+
+ outPairs.add(pair);
+ }
+
+ return SLANG_OK;
+}
+
+
+SlangResult NativeToJSONConverter::convert(const RttiInfo* rttiInfo, const void* in, JSONValue& out)
+{
+ if (rttiInfo->isIntegral())
+ {
+ out = JSONValue::makeInt(RttiUtil::getInt64(rttiInfo, in));
+ return SLANG_OK;
+ }
+ else if (rttiInfo->isFloat())
+ {
+ out = JSONValue::makeFloat(RttiUtil::asDouble(rttiInfo, in));
+ return SLANG_OK;
+ }
+
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::Invalid: return SLANG_FAIL;
+ case RttiInfo::Kind::Bool:
+ {
+ out = JSONValue::makeBool(RttiUtil::asBool(rttiInfo, in));
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::String:
+ {
+ const String& str = *(const String*)in;
+ out = m_container->createString(str.getUnownedSlice());
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::UnownedStringSlice:
+ {
+ const UnownedStringSlice& slice = *(const UnownedStringSlice*)in;
+ out = m_container->createString(slice);
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::Struct:
+ {
+ const StructRttiInfo* structRttiInfo = static_cast<const StructRttiInfo*>(rttiInfo);
+
+ List<JSONKeyValue> pairs;
+ SLANG_RETURN_ON_FAIL(_structToJSON(structRttiInfo, in, pairs));
+ out = m_container->createObject(pairs.getBuffer(), pairs.getCount());
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::Enum:
+ {
+ return SLANG_E_NOT_IMPLEMENTED;
+ }
+ case RttiInfo::Kind::List:
+ {
+ const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
+ const auto elementRttiInfo = listRttiInfo->m_elementType;
+
+ // The src probably *doesn't* contain bytes, but can cast like this because
+ // we only need the count (which doesn't depend on <T>), and the backing buffer
+ const List<Byte>& srcValuesList = *(const List<Byte>*)in;
+
+ const Index count = srcValuesList.getCount();
+ const Byte* srcValues = srcValuesList.getBuffer();
+
+ List<JSONValue> dstValues;
+ dstValues.setCount(count);
+
+ const size_t elementStride = elementRttiInfo->m_size;
+
+ for (Index i = 0; i < count; ++i, srcValues += elementStride)
+ {
+ SLANG_RETURN_ON_FAIL(convert(elementRttiInfo, srcValues, dstValues[i]));
+ }
+
+ out = m_container->createArray(dstValues.getBuffer(), count);
+ return SLANG_OK;
+ }
+ case RttiInfo::Kind::Dictionary:
+ {
+ const DictionaryRttiInfo* listRttiInfo = static_cast<const DictionaryRttiInfo*>(rttiInfo);
+ const auto keyRttiInfo = listRttiInfo->m_keyType;
+ const auto valueRttiInfo = listRttiInfo->m_valueType;
+
+ SLANG_UNUSED(keyRttiInfo);
+ SLANG_UNUSED(valueRttiInfo);
+
+ // We can *only* serialize this into a straight JSON object iff the key is a string-like type
+ // We could turn into (say) an array of keys and values
+
+ break;
+ }
+ case RttiInfo::Kind::Other:
+ {
+ if (rttiInfo == GetRttiInfo<JSONValue>::get())
+ {
+ // Do we need to copy into the container?
+ // As it stands we have to assume src is stored in container.
+ const JSONValue& src = *(const JSONValue*)in;
+
+ out = src;
+ return SLANG_OK;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ return SLANG_E_NOT_IMPLEMENTED;
+}
+
+} // namespace Slang
diff --git a/source/compiler-core/slang-json-native.h b/source/compiler-core/slang-json-native.h
new file mode 100644
index 000000000..66f20aafb
--- /dev/null
+++ b/source/compiler-core/slang-json-native.h
@@ -0,0 +1,50 @@
+#ifndef SLANG_COMPILER_CORE_JSON_NATIVE_H
+#define SLANG_COMPILER_CORE_JSON_NATIVE_H
+
+#include "../../slang.h"
+#include "../../slang-com-helper.h"
+#include "../../slang-com-ptr.h"
+
+#include "slang-json-value.h"
+
+namespace Slang {
+
+struct JSONToNativeConverter
+{
+ SlangResult convert(const JSONValue& value, const RttiInfo* rttiInfo, void* out);
+
+ JSONToNativeConverter(JSONContainer* container, DiagnosticSink* sink):
+ m_container(container),
+ m_sink(sink)
+ {}
+
+protected:
+ static Index _getFieldCount(const StructRttiInfo* structRttiInfo);
+ static Index _findFieldIndex(const StructRttiInfo* structRttiInfo, const UnownedStringSlice& fieldName);
+
+ SlangResult _structToNative(const ConstArrayView<JSONKeyValue>& pairs, const StructRttiInfo* structRttiInfo, void* out, Index& outFieldCount);
+
+ DiagnosticSink* m_sink;
+ JSONContainer* m_container;
+};
+
+struct NativeToJSONConverter
+{
+ SlangResult convert(const RttiInfo* rttiInfo, const void* in, JSONValue& out);
+
+ NativeToJSONConverter(JSONContainer* container, DiagnosticSink* sink) :
+ m_container(container),
+ m_sink(sink)
+ {}
+
+protected:
+ SlangResult _structToJSON(const StructRttiInfo* structRttiInfo, const void* src, List<JSONKeyValue>& outPairs);
+
+ DiagnosticSink* m_sink;
+ JSONContainer* m_container;
+};
+
+
+} // namespace Slang
+
+#endif // SLANG_COMPILER_CORE_JSON_NATIVE_H
diff --git a/source/compiler-core/slang-json-parser.cpp b/source/compiler-core/slang-json-parser.cpp
index a38afc418..fe9a0f580 100644
--- a/source/compiler-core/slang-json-parser.cpp
+++ b/source/compiler-core/slang-json-parser.cpp
@@ -32,7 +32,7 @@ SlangResult JSONParser::_parseObject()
{
JSONToken keyToken;
SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::StringLiteral, keyToken));
- m_listener->addKey(m_lexer->getLexeme(keyToken), keyToken.loc);
+ m_listener->addQuotedKey(m_lexer->getLexeme(keyToken), keyToken.loc);
SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::Colon));
@@ -345,18 +345,39 @@ void JSONWriter::endArray(SourceLoc loc)
m_stack.removeLast();
}
-void JSONWriter::addKey(const UnownedStringSlice& key, SourceLoc loc)
+void JSONWriter::addUnquotedKey(const UnownedStringSlice& key, SourceLoc loc)
{
SLANG_UNUSED(loc);
SLANG_ASSERT(m_state.m_kind == State::Kind::Object && (m_state.m_flags & State::Flag::HasKey) == 0);
_maybeEmitFieldComma();
+ _maybeEmitIndent();
+
+ // Output the key quoted
+ StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON);
+ StringEscapeUtil::appendQuoted(handler, key, m_builder);
+
+ m_builder << " : ";
+
+ m_state.m_flags |= State::Flag::HasKey;
+ // We don't want it to emit a , after the :
+ m_state.m_flags &= ~State::Flag::HasPrevious;
+}
+
+void JSONWriter::addQuotedKey(const UnownedStringSlice& key, SourceLoc loc)
+{
+ SLANG_UNUSED(loc);
+ SLANG_ASSERT(m_state.m_kind == State::Kind::Object && (m_state.m_flags & State::Flag::HasKey) == 0);
// It should be quoted
SLANG_ASSERT(key.getLength() >= 2 && key[0] == '"' && key[key.getLength() - 1] == '"');
+ _maybeEmitFieldComma();
_maybeEmitIndent();
- m_builder << key << " : ";
+
+ m_builder << key;
+
+ m_builder << " : ";
m_state.m_flags |= State::Flag::HasKey;
// We don't want it to emit a , after the :
diff --git a/source/compiler-core/slang-json-parser.h b/source/compiler-core/slang-json-parser.h
index 96531aee9..2391ea0d2 100644
--- a/source/compiler-core/slang-json-parser.h
+++ b/source/compiler-core/slang-json-parser.h
@@ -21,7 +21,8 @@ public:
/// Add the key. Must be followed by addXXXValue.
- virtual void addKey(const UnownedStringSlice& key, SourceLoc loc) = 0;
+ virtual void addQuotedKey(const UnownedStringSlice& key, SourceLoc loc) = 0;
+ virtual void addUnquotedKey(const UnownedStringSlice& key, SourceLoc loc) = 0;
/// Can be performed in an array or after an addLexemeKey in an object
virtual void addLexemeValue(JSONTokenType type, const UnownedStringSlice& value, SourceLoc loc) = 0;
@@ -93,7 +94,8 @@ public:
virtual void endObject(SourceLoc loc) SLANG_OVERRIDE;
virtual void startArray(SourceLoc loc) SLANG_OVERRIDE;
virtual void endArray(SourceLoc loc) SLANG_OVERRIDE;
- virtual void addKey(const UnownedStringSlice& key, SourceLoc loc) SLANG_OVERRIDE;
+ virtual void addQuotedKey(const UnownedStringSlice& key, SourceLoc loc) SLANG_OVERRIDE;
+ virtual void addUnquotedKey(const UnownedStringSlice& key, SourceLoc loc) SLANG_OVERRIDE;
virtual void addLexemeValue(JSONTokenType type, const UnownedStringSlice& value, SourceLoc loc) SLANG_OVERRIDE;
virtual void addIntegerValue(int64_t value, SourceLoc loc) SLANG_OVERRIDE;
virtual void addFloatValue(double value, SourceLoc loc) SLANG_OVERRIDE;
diff --git a/source/compiler-core/slang-json-rpc.cpp b/source/compiler-core/slang-json-rpc.cpp
index 15da92ff0..1ca55e9eb 100644
--- a/source/compiler-core/slang-json-rpc.cpp
+++ b/source/compiler-core/slang-json-rpc.cpp
@@ -1,5 +1,7 @@
#include "slang-json-rpc.h"
+#include "../../slang-com-helper.h"
+
namespace Slang {
// https://www.jsonrpc.org/specification
@@ -7,6 +9,406 @@ namespace Slang {
// m_sourceManager.initialize(nullptr, nullptr);
// m_diagnosticSink.init(&m_sourceManager, &JSONLexer::calcLexemeLocation);
+static const auto g_jsonRpc = UnownedStringSlice::fromLiteral("jsonrpc");
+static const auto g_jsonRpcVersion = UnownedStringSlice::fromLiteral("2.0");
+static const auto g_method = UnownedStringSlice::fromLiteral("method");
+static const auto g_id = UnownedStringSlice::fromLiteral("id");
+static const auto g_params = UnownedStringSlice::fromLiteral("params");
+static const auto g_code = UnownedStringSlice::fromLiteral("code");
+static const auto g_error = UnownedStringSlice::fromLiteral("error");
+static const auto g_message = UnownedStringSlice::fromLiteral("message");
+static const auto g_result = UnownedStringSlice::fromLiteral("result");
+static const auto g_data = UnownedStringSlice::fromLiteral("data");
+
+// Add the fields.
+// TODO(JS): This is a little verbose, and could be improved on with something like
+// * Tool that automatically generated from C++ (say via the C++ extractor)
+// * Macro magic to simplify the construction
+static const StructRttiInfo _makeJSONRPCErrorResponse_ErrorRtti()
+{
+ JSONRPCErrorResponse::Error obj;
+ StructRttiBuilder builder(&obj, "JSONRPCErrorResponse::Error", nullptr);
+ builder.addField("code", &obj.code);
+ builder.addField("message", &obj.message);
+ return builder.make();
+}
+/* static */const StructRttiInfo JSONRPCErrorResponse::Error::g_rttiInfo = _makeJSONRPCErrorResponse_ErrorRtti();
+
+static const StructRttiInfo _makeJSONRPCErrorResponseRtti()
+{
+ JSONRPCErrorResponse obj;
+ StructRttiBuilder builder(&obj, "JSONRPCErrorResponse", nullptr);
+
+ builder.addField("error", &obj.error);
+ builder.addField("data", &obj.data, StructRttiInfo::Flag::Optional);
+ builder.addField("id", &obj.id, combine(StructRttiInfo::Flag::Optional, RttiDefaultValue::MinusOne));
+
+ return builder.make();
+}
+/* static */const StructRttiInfo JSONRPCErrorResponse::g_rttiInfo = _makeJSONRPCErrorResponseRtti();
+
+static const StructRttiInfo _makeJSONRPCCallResponseRtti()
+{
+ JSONRPCCall obj;
+ StructRttiBuilder builder(&obj, "JSONRPCCall", nullptr);
+
+ builder.addField("method", &obj.method);
+ builder.addField("params", &obj.params, StructRttiInfo::Flag::Optional);
+ builder.addField("id", &obj.id, combine(StructRttiInfo::Flag::Optional, RttiDefaultValue::MinusOne));
+
+ return builder.make();
+}
+/* static */const StructRttiInfo JSONRPCCall::g_rttiInfo = _makeJSONRPCCallResponseRtti();
+
+static const StructRttiInfo _makeJSONResultResponseResponseRtti()
+{
+ JSONResultResponse obj;
+ StructRttiBuilder builder(&obj, "JSONResultResponse", nullptr);
+
+ builder.addField("result", &obj.result);
+ builder.addField("id", &obj.id, combine(StructRttiInfo::Flag::Optional, RttiDefaultValue::MinusOne));
+
+ return builder.make();
+}
+/* static */const StructRttiInfo JSONResultResponse::g_rttiInfo = _makeJSONResultResponseResponseRtti();
+
+
+/* static */JSONValue JSONRPCUtil::createCall(JSONContainer* container, const UnownedStringSlice& method, JSONValue params, Int id)
+{
+ const Index maxPairs = 4;
+ JSONKeyValue pairs[maxPairs];
+
+ Index i = 0;
+
+ // Version number is a string
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_jsonRpc), container->createString(g_jsonRpcVersion));
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_method), container->createString(method));
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_params), params);
+
+ if (id >= 0)
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_id), JSONValue::makeInt(id));
+ }
+
+ return container->createObject(pairs, i);
+}
+
+/* static */JSONValue JSONRPCUtil::createCall(JSONContainer* container, const UnownedStringSlice& method, Int id)
+{
+ const Index maxPairs = 3;
+ JSONKeyValue pairs[maxPairs];
+ Index i = 0;
+ // Version number is a string
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_jsonRpc), container->createString(g_jsonRpcVersion));
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_method), container->createString(method));
+
+ if (id >= 0)
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_id), JSONValue::makeInt(id));
+ }
+
+ return container->createObject(pairs, i);
+}
+
+/* static */JSONValue JSONRPCUtil::createErrorResponse(JSONContainer* container, Index code, const UnownedStringSlice& message, const JSONValue& data, Int id)
+{
+ // Set up the error value
+ JSONValue errorValue;
+ {
+ const Index maxPairs = 2;
+ JSONKeyValue pairs[maxPairs];
+ Index i = 0;
+
+ if (code != 0)
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_code), JSONValue::makeInt(code));
+ }
+ if (message.getLength() > 0)
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_message), container->createString(message));
+ }
+ errorValue = container->createObject(pairs, i);
+ }
+
+ const Index maxPairs = 4;
+ JSONKeyValue pairs[maxPairs];
+ Index i = 0;
+
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_jsonRpc), container->createString(g_jsonRpcVersion));
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_error), errorValue);
+
+ if (data.isValid())
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_data), data);
+ }
+
+ if (id >= 0)
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_id), JSONValue::makeInt(id));
+ }
+
+ return container->createObject(pairs, i);
+}
+
+
+/* static */JSONValue JSONRPCUtil::createErrorResponse(JSONContainer* container, ErrorCode code, const UnownedStringSlice& message, const JSONValue& data, Int id)
+{
+ return createErrorResponse(container, Index(code), message, data, id);
+}
+
+/* static */JSONValue JSONRPCUtil::createResultResponse(JSONContainer* container, const JSONValue& resultValue, Int id)
+{
+ const Index maxPairs = 3;
+ JSONKeyValue pairs[maxPairs];
+ Index i = 0;
+
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_jsonRpc), container->createString(g_jsonRpcVersion));
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_result), resultValue);
+
+ if (id >= 0)
+ {
+ pairs[i++] = JSONKeyValue::make(container->getKey(g_id), JSONValue::makeInt(id));
+ }
+
+ return container->createObject(pairs, i);
+}
+
+/* static */ JSONRPCUtil::ResponseType JSONRPCUtil::getResponseType(JSONContainer* container, const JSONValue& response)
+{
+ if (response.getKind() == JSONValue::Kind::Object)
+ {
+ const JSONKey resultKey = container->findKey(g_result);
+ const JSONKey errorKey = container->findKey(g_error);
+
+ auto pairs = container->getObject(response);
+
+ for (const auto& pair : pairs)
+ {
+ if (pair.key == resultKey)
+ {
+ return ResponseType::Result;
+ }
+ else if (pair.key == errorKey)
+ {
+ return ResponseType::Error;
+ }
+ }
+ }
+
+ return ResponseType::Error;
+}
+
+static SlangResult _parseError(JSONContainer* container, const JSONValue& error, JSONRPCUtil::ErrorResponse& out)
+{
+ if (error.getKind() != JSONValue::Kind::Object)
+ {
+ return SLANG_FAIL;
+ }
+ const auto pairs = container->getObject(error);
+
+ const JSONKey messageKey = container->findKey(g_message);
+ const JSONKey codeKey = container->findKey(g_code);
+ const JSONKey dataKey = container->findKey(g_data);
+
+ Int fieldBits = 0;
+
+ for (auto const& pair : pairs)
+ {
+ if (pair.key == messageKey)
+ {
+ if (pair.value.getKind() != JSONValue::Kind::String)
+ {
+ return SLANG_FAIL;
+ }
+ out.message = container->getString(pair.value);
+ fieldBits |= 0x1;
+ }
+ else if (pair.key == codeKey)
+ {
+ if (pair.value.getKind() != JSONValue::Kind::Integer)
+ {
+ return SLANG_FAIL;
+ }
+ out.code = Index(pair.value.asInteger());
+ fieldBits |= 0x2;
+ }
+ else if (pair.key == dataKey)
+ {
+ out.data = pair.value;
+ fieldBits |= 0x4;
+ }
+ else
+ {
+ return SLANG_FAIL;
+ }
+ }
+
+ // Check all required fields are set
+ return (fieldBits & 0x3) == 0x3 ? SLANG_OK : SLANG_FAIL;
+}
+
+/* static */SlangResult JSONRPCUtil::parseError(JSONContainer* container, const JSONValue& response, ErrorResponse& out)
+{
+ if (response.getKind() != JSONValue::Kind::Object)
+ {
+ return SLANG_FAIL;
+ }
+
+ const auto pairs = container->getObject(response);
+
+ const JSONKey jsonRpcKey = container->findKey(g_jsonRpc);
+ const JSONKey errorKey = container->findKey(g_error);
+ const JSONKey idKey = container->findKey(g_id);
+
+ Int fieldBits = 0;
+
+ for (auto const& pair : pairs)
+ {
+ if (pair.key == jsonRpcKey)
+ {
+ if (!container->areEqual(pair.value, g_jsonRpcVersion))
+ {
+ return SLANG_FAIL;
+ }
+ fieldBits |= 0x1;
+ }
+ else if (pair.key == errorKey)
+ {
+ // We need to decode the error
+ SLANG_RETURN_ON_FAIL(_parseError(container, pair.value, out));
+ fieldBits |= 0x2;
+ }
+ else if (pair.key == idKey)
+ {
+ if (pair.value.getKind() != JSONValue::Kind::Integer)
+ {
+ return SLANG_FAIL;
+ }
+ out.id = Int(pair.value.asInteger());
+ fieldBits |= 0x4;
+ }
+ else
+ {
+ // Unknown key
+ return SLANG_FAIL;
+ }
+ }
+
+ // Check all the required bits are set
+ return ((fieldBits & 0x3) == 0x3) ? SLANG_OK : SLANG_FAIL;
+}
+
+/* static */SlangResult JSONRPCUtil::parseResult(JSONContainer* container, const JSONValue& response, ResultResponse& out)
+{
+ if (response.getKind() != JSONValue::Kind::Object)
+ {
+ return SLANG_FAIL;
+ }
+
+ const auto pairs = container->getObject(response);
+
+ const JSONKey jsonRpcKey = container->findKey(g_jsonRpc);
+ const JSONKey resultKey = container->findKey(g_result);
+ const JSONKey idKey = container->findKey(g_id);
+
+ Int fieldBits = 0;
+
+ for (auto const& pair : pairs)
+ {
+ if (pair.key == jsonRpcKey)
+ {
+ if (!container->areEqual(pair.value, g_jsonRpcVersion))
+ {
+ return SLANG_FAIL;
+ }
+ fieldBits |= 0x1;
+ }
+ else if (pair.key == resultKey)
+ {
+ out.result = pair.value;
+ fieldBits |= 0x2;
+ }
+ else if (pair.key == idKey)
+ {
+ if (pair.value.getKind() != JSONValue::Kind::Integer)
+ {
+ return SLANG_FAIL;
+ }
+ out.id = Int(pair.value.asInteger());
+ fieldBits |= 0x4;
+ }
+ else
+ {
+ // Unknown key
+ return SLANG_FAIL;
+ }
+ }
+
+ // Check all the required bits are set
+ return ((fieldBits & 0x3) == 0x3) ? SLANG_OK : SLANG_FAIL;
+}
+
+/* static */SlangResult JSONRPCUtil::parseCall(JSONContainer* container, const JSONValue& value, Call& out)
+{
+ if (value.getKind() != JSONValue::Kind::Object)
+ {
+ return SLANG_FAIL;
+ }
+
+ const auto pairs = container->getObject(value);
+
+ const JSONKey jsonRpcKey = container->findKey(g_jsonRpc);
+ const JSONKey methodKey = container->findKey(g_method);
+ const JSONKey paramsKey = container->findKey(g_params);
+ const JSONKey idKey = container->findKey(g_id);
+
+ Int fieldBits = 0;
+
+ for (auto const& pair : pairs)
+ {
+ if (pair.key == jsonRpcKey)
+ {
+ if (!container->areEqual(pair.value, g_jsonRpcVersion))
+ {
+ return SLANG_FAIL;
+ }
+ fieldBits |= 0x1;
+ }
+ else if (pair.key == methodKey)
+ {
+ if (pair.value.getKind() != JSONValue::Kind::String)
+ {
+ return SLANG_FAIL;
+ }
+ out.method = container->getString(pair.value);
+ fieldBits |= 0x2;
+ }
+ else if (pair.key == idKey)
+ {
+ if (pair.value.getKind() != JSONValue::Kind::Integer)
+ {
+ return SLANG_FAIL;
+ }
+ out.id = Int(pair.value.asInteger());
+ fieldBits |= 0x4;
+ }
+ else if (pair.key == paramsKey)
+ {
+ out.params = pair.value;
+ fieldBits |= 0x8;
+ }
+ else
+ {
+ // Unknown key
+ return SLANG_FAIL;
+ }
+ }
+
+ // Check all the required bits are set
+ return ((fieldBits & 0x3) == 0x3) ? SLANG_OK : SLANG_FAIL;
+}
+
+
/* static */SlangResult JSONRPCUtil::parseJSON(const UnownedStringSlice& slice, JSONContainer* container, DiagnosticSink* sink, JSONValue& outValue)
{
SourceManager* sourceManager = sink->getSourceManager();
diff --git a/source/compiler-core/slang-json-rpc.h b/source/compiler-core/slang-json-rpc.h
index 3ed3e5fee..e85664ceb 100644
--- a/source/compiler-core/slang-json-rpc.h
+++ b/source/compiler-core/slang-json-rpc.h
@@ -12,11 +12,110 @@
namespace Slang {
+struct JSONRPCErrorResponse
+{
+ struct Error
+ {
+ Index code = 0; ///< Value from ErrorCode
+ UnownedStringSlice message; ///< Error message
+
+ static const StructRttiInfo g_rttiInfo;
+ };
+
+ Error error;
+ JSONValue data;
+ Int id = -1; ///< Id of initiating method or -1 if not set
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
+struct JSONRPCCall
+{
+ UnownedStringSlice method; ///< The name of the method
+ JSONValue params; ///< Can be invalid/array/object
+ Int id = -1; ///< Id associated with this request, or -1 if not set
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
+struct JSONResultResponse
+{
+ JSONValue result; ///< The result value
+ Int id = -1; ///< Id of initiating method or -1 if not set
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
/// Send and receive messages as JSON
+///
+/// Strictly speaking should support ids, as strings or ids. Currently just supports with integer ids.
+/// One way of dealing with this would be to just use JSONValue for ids, would allow invalid/string/integer and
+/// a mechanism to compare/display etc.
class JSONRPCUtil
{
public:
-
+
+ enum class ErrorCode
+ {
+ ParseError = -32700, ///< Invalid JSON was received by the server.
+ InvalidRequest = -32600, ///< The JSON sent is not a valid Request object.
+ MethodNotFound = -32601, ///< The method does not exist / is not available.
+ InvalidParams = -32602, ///< Invalid method parameter(s).
+ InternalError = -32603, ///< Internal JSON - RPC error.
+
+ ServerImplStart = -32000, ///< Server implementation defined error range
+ ServerImplEnd = -32099,
+ };
+
+ enum class ResponseType
+ {
+ Invalid,
+ Error,
+ Result
+ };
+
+ struct ErrorResponse
+ {
+ Index code = 0; ///< Value from ErrorCode
+ UnownedStringSlice message; ///< Error message
+ JSONValue data;
+ Int id = -1; ///< Id of initiating method or -1 if not set
+ };
+
+ struct ResultResponse
+ {
+ JSONValue result; ///< The result value
+ Int id = -1; ///< Id of initiating method or -1 if not set
+ };
+
+ struct Call
+ {
+ UnownedStringSlice method; ///< The name of the method
+ JSONValue params; ///< Can be invalid/array/object
+ Int id = -1; ///< Id associated with this request, or -1 if not set
+ };
+
+ /// Parameters can be either named or via index.
+ static JSONValue createCall(JSONContainer* container, const UnownedStringSlice& method, JSONValue params, Int id = -1);
+ /// Parameters can be either named or via index.
+ static JSONValue createCall(JSONContainer* container, const UnownedStringSlice& method, Int id = -1);
+
+ /// Create an error response
+ /// Code should typically be something in the ErrorCode range
+ static JSONValue createErrorResponse(JSONContainer* container, Index code, const UnownedStringSlice& message, const JSONValue& data = JSONValue(), Int id = -1);
+ static JSONValue createErrorResponse(JSONContainer* container, ErrorCode code, const UnownedStringSlice& message, const JSONValue& data = JSONValue(), Int id = -1);
+ /// Create a result response
+ static JSONValue createResultResponse(JSONContainer* container, const JSONValue& resultValue, Int id = -1);
+
+ /// Determine the response type
+ static ResponseType getResponseType(JSONContainer* container, const JSONValue& response);
+
+ static SlangResult parseError(JSONContainer* container, const JSONValue& response, ErrorResponse& out);
+
+ static SlangResult parseResult(JSONContainer* container, const JSONValue& response, ResultResponse& out);
+
+ static SlangResult parseCall(JSONContainer* container, const JSONValue& value, Call& out);
+
/// Parse slice into JSONContainer. outValue is the root of the hierarchy.
static SlangResult parseJSON(const UnownedStringSlice& slice, JSONContainer* container, DiagnosticSink* sink, JSONValue& outValue);
diff --git a/source/compiler-core/slang-json-value.cpp b/source/compiler-core/slang-json-value.cpp
index 9a2bb37f4..1e18bee39 100644
--- a/source/compiler-core/slang-json-value.cpp
+++ b/source/compiler-core/slang-json-value.cpp
@@ -26,6 +26,24 @@ namespace Slang {
JSONValue::Kind::Object, // Object,
};
+static bool _isDefault(const RttiInfo* type, const void* in)
+{
+ SLANG_UNUSED(type)
+ const JSONValue& value = *(const JSONValue*)in;
+ return value.getKind() == JSONValue::Kind::Invalid;
+}
+
+static OtherRttiInfo _getJSONValueRttiInfo()
+{
+ OtherRttiInfo info;
+ info.init<JSONValue>(RttiInfo::Kind::Other);
+ info.m_name = "JSONValue";
+ info.m_isDefaultFunc = _isDefault;
+ info.m_typeFuncs = GetRttiTypeFuncs<JSONValue>::getFuncs();
+ return info;
+}
+/* static */const OtherRttiInfo JSONValue::g_rttiInfo = _getJSONValueRttiInfo();
+
static JSONKeyValue _makeInvalidKeyValue()
{
JSONKeyValue keyValue;
@@ -204,7 +222,7 @@ JSONValue JSONContainer::createArray(const JSONValue* values, Index valuesCount,
JSONValue value;
value.type = JSONValue::Type::Array;
value.loc = loc;
- value.rangeIndex = _addRange(Range::Type::Array, m_objectValues.getCount(), valuesCount);
+ value.rangeIndex = _addRange(Range::Type::Array, m_arrayValues.getCount(), valuesCount);
m_arrayValues.addRange(values, valuesCount);
return value;
@@ -240,6 +258,12 @@ JSONKey JSONContainer::getKey(const UnownedStringSlice& slice)
return JSONKey(m_slicePool.add(slice));
}
+JSONKey JSONContainer::findKey(const UnownedStringSlice& slice) const
+{
+ const Index index = m_slicePool.findIndex(slice);
+ return (index < 0) ? JSONKey(0) : JSONKey(index);
+}
+
ConstArrayView<JSONValue> JSONContainer::getArray(const JSONValue& in) const
{
SLANG_ASSERT(in.type == JSONValue::Type::Array);
@@ -271,6 +295,8 @@ ArrayView<JSONValue> JSONContainer::getArray(const JSONValue& in)
return ArrayView<JSONValue>((JSONValue*)nullptr, 0);
}
const Range& range = m_ranges[in.rangeIndex];
+ SLANG_ASSERT(range.startIndex <= m_arrayValues.getCount() && range.startIndex + range.count <= m_arrayValues.getCount());
+
return ArrayView<JSONValue>(m_arrayValues.getBuffer() + range.startIndex, range.count);
}
@@ -311,9 +337,29 @@ UnownedStringSlice JSONContainer::getLexeme(const JSONValue& in)
UnownedStringSlice JSONContainer::getString(const JSONValue& in)
{
+ if (in.type == JSONValue::Type::StringValue)
+ {
+ return getStringFromKey(in.stringKey);
+ }
+ else if (in.type == JSONValue::Type::StringLexeme)
+ {
+ auto slice = getTransientString(in);
+ auto handle = m_slicePool.add(slice);
+ return m_slicePool.getSlice(handle);
+ }
+
+ SLANG_ASSERT(!"Not a string type");
+ return UnownedStringSlice();
+}
+
+UnownedStringSlice JSONContainer::getTransientString(const JSONValue& in)
+{
switch (in.type)
{
- case JSONValue::Type::StringValue: return getStringFromKey(in.stringKey);
+ case JSONValue::Type::StringValue:
+ {
+ return getStringFromKey(in.stringKey);
+ }
case JSONValue::Type::StringLexeme:
{
StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON);
@@ -340,7 +386,7 @@ UnownedStringSlice JSONContainer::getString(const JSONValue& in)
JSONKey JSONContainer::getStringKey(const JSONValue& in)
{
- return (in.type == JSONValue::Type::StringValue) ? in.stringKey : getKey(getString(in));
+ return (in.type == JSONValue::Type::StringValue) ? in.stringKey : getKey(getTransientString(in));
}
bool JSONContainer::asBool(const JSONValue& value)
@@ -393,6 +439,19 @@ double JSONContainer::asFloat(const JSONValue& value)
}
}
+Index JSONContainer::findObjectIndex(const JSONValue& obj, JSONKey key) const
+{
+ auto pairs = getObject(obj);
+ return pairs.findFirstIndex([key](const JSONKeyValue& pair) -> bool { return pair.key == key; });
+}
+
+JSONValue JSONContainer::findObjectValue(const JSONValue& obj, JSONKey key) const
+{
+ auto pairs = getObject(obj);
+ const Index index = pairs.findFirstIndex([key](const JSONKeyValue& pair) -> bool { return pair.key == key; });
+ return (index >= 0) ? pairs[index].value : JSONValue::makeInvalid();
+}
+
JSONValue& JSONContainer::getAt(const JSONValue& array, Index index)
{
SLANG_ASSERT(array.type == JSONValue::Type::Array);
@@ -742,6 +801,11 @@ bool JSONContainer::areEqual(const JSONKeyValue* a, const JSONKeyValue* b, Index
}
}
+bool JSONContainer::areEqual(const JSONValue& a, const UnownedStringSlice& slice)
+{
+ return a.getKind() == JSONValue::Kind::String && getTransientString(a) == slice;
+}
+
bool JSONContainer::areEqual(const JSONValue& a, const JSONValue& b)
{
if (&a == &b)
@@ -864,7 +928,7 @@ void JSONContainer::traverseRecursively(const JSONValue& value, JSONListener* li
{
// Emit the key
const auto keyString = getStringFromKey(objKeyValue.key);
- listener->addKey(keyString, objKeyValue.keyLoc);
+ listener->addUnquotedKey(keyString, objKeyValue.keyLoc);
// Emit the value associated with the key
traverseRecursively(objKeyValue.value, listener);
@@ -1035,7 +1099,16 @@ void JSONBuilder::endArray(SourceLoc loc)
_add(value);
}
-void JSONBuilder::addKey(const UnownedStringSlice& key, SourceLoc loc)
+void JSONBuilder::addQuotedKey(const UnownedStringSlice& key, SourceLoc loc)
+{
+ // We need to decode
+ m_work.Clear();
+ StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON);
+ StringEscapeUtil::appendUnquoted(handler, key, m_work);
+ addUnquotedKey(m_work.getUnownedSlice(), loc);
+}
+
+void JSONBuilder::addUnquotedKey(const UnownedStringSlice& key, SourceLoc loc)
{
SLANG_ASSERT(m_keyValue.key == JSONKey(0));
m_keyValue.key = m_container->getKey(key);
diff --git a/source/compiler-core/slang-json-value.h b/source/compiler-core/slang-json-value.h
index d008d5f18..2eae4ae08 100644
--- a/source/compiler-core/slang-json-value.h
+++ b/source/compiler-core/slang-json-value.h
@@ -9,6 +9,8 @@
#include "slang-json-parser.h"
+#include "../core/slang-rtti-info.h"
+
namespace Slang {
typedef uint32_t JSONKey;
@@ -111,8 +113,13 @@ struct JSONValue
};
static const Kind g_typeToKind[Index(Type::CountOf)];
+
+ static const OtherRttiInfo g_rttiInfo;
};
+template <>
+struct GetRttiInfo<JSONValue> { static const RttiInfo* get() { return &JSONValue::g_rttiInfo; } };
+
struct JSONKeyValue
{
/// True if it's valid
@@ -129,6 +136,11 @@ struct JSONKeyValue
SourceLoc keyLoc;
JSONValue value;
+ static JSONKeyValue make(JSONKey inKey, JSONValue inValue, SourceLoc inKeyLoc = SourceLoc())
+ {
+ return JSONKeyValue{ inKey, inKeyLoc, inValue };
+ }
+
static JSONKeyValue g_invalid;
};
@@ -155,6 +167,11 @@ public:
/// Get the value at the index in the array
JSONValue& getAt(const JSONValue& array, Index index);
+ /// Returns the index of key in obj, or -1 if not found
+ Index findObjectIndex(const JSONValue& obj, JSONKey key) const;
+ /// Get the value in the object at key. REturns invalid if not found.
+ JSONValue findObjectValue(const JSONValue& obj, JSONKey key) const;
+
/// Returns the index
Index findKeyGlobalIndex(const JSONValue& obj, JSONKey key);
Index findKeyGlobalIndex(const JSONValue& obj, const UnownedStringSlice& slice);
@@ -176,14 +193,20 @@ public:
/// Returns string as a key
JSONKey getStringKey(const JSONValue& in);
- /// Get as a string.
+ /// Get as a string. The slice may used backing lexeme (ie will only last
+ /// as long as the backing JSON text, or be decoded and be transitory).
+ UnownedStringSlice getTransientString(const JSONValue& in);
+
+ /// Get as a string. The contents will stay in scope as long as the container
UnownedStringSlice getString(const JSONValue& in);
- /// Gets the lexeme
+ /// Gets the lexeme
UnownedStringSlice getLexeme(const JSONValue& in);
/// Get a key for a name
JSONKey getKey(const UnownedStringSlice& slice);
+ /// Returns JSONKey(0) if not found
+ JSONKey findKey(const UnownedStringSlice& slice) const;
/// Get the string from the key
UnownedStringSlice getStringFromKey(JSONKey key) const { return m_slicePool.getSlice(StringSlicePool::Handle(key)); }
@@ -194,6 +217,8 @@ public:
bool areEqual(const JSONValue* a, const JSONValue* b, Index count);
bool areEqual(const JSONKeyValue* a, const JSONKeyValue* b, Index count);
+ bool areEqual(const JSONValue& a, const UnownedStringSlice& slice);
+
/// Destroy value
void destroy(JSONValue& value);
/// Destroy recursively from value
@@ -282,7 +307,8 @@ public:
virtual void endObject(SourceLoc loc) SLANG_OVERRIDE;
virtual void startArray(SourceLoc loc) SLANG_OVERRIDE;
virtual void endArray(SourceLoc loc) SLANG_OVERRIDE;
- virtual void addKey(const UnownedStringSlice& key, SourceLoc loc) SLANG_OVERRIDE;
+ virtual void addQuotedKey(const UnownedStringSlice& key, SourceLoc loc) SLANG_OVERRIDE;
+ virtual void addUnquotedKey(const UnownedStringSlice& key, SourceLoc loc) SLANG_OVERRIDE;
virtual void addLexemeValue(JSONTokenType type, const UnownedStringSlice& value, SourceLoc loc) SLANG_OVERRIDE;
virtual void addIntegerValue(int64_t value, SourceLoc loc) SLANG_OVERRIDE;
virtual void addFloatValue(double value, SourceLoc loc) SLANG_OVERRIDE;
@@ -330,6 +356,8 @@ protected:
JSONKeyValue m_keyValue;
JSONValue m_rootValue;
+
+ StringBuilder m_work;
};
} // namespace Slang
diff --git a/source/core/slang-allocator.h b/source/core/slang-allocator.h
index f25fd92c9..bc1b880f8 100644
--- a/source/core/slang-allocator.h
+++ b/source/core/slang-allocator.h
@@ -19,11 +19,9 @@ namespace Slang
#elif defined(__CYGWIN__)
return aligned_alloc(alignment, size);
#else
- void * rs = 0;
+ void* rs = nullptr;
int succ = posix_memalign(&rs, alignment, size);
- if (succ!=0)
- rs = 0;
- return rs;
+ return (succ == 0) ? rs : nullptr;
#endif
}
@@ -66,18 +64,15 @@ namespace Slang
// Helper utilties for calling allocators.
template<typename T, int isPOD>
- class Initializer
- {
-
- };
+ class Initializer;
template<typename T>
class Initializer<T, 0>
{
public:
- static void initialize(T* buffer, int size)
+ static void initialize(T* buffer, Index size)
{
- for (int i = 0; i < size; i++)
+ for (Index i = 0; i < size; i++)
new (buffer + i) T();
}
};
@@ -85,8 +80,10 @@ namespace Slang
class Initializer<T, 1>
{
public:
- static void initialize(T* buffer, int size)
+ static void initialize(T* buffer, Index size)
{
+ SLANG_UNUSED(buffer);
+ SLANG_UNUSED(size);
// It's pod so no initialization required
//for (int i = 0; i < size; i++)
// new (buffer + i) T;
@@ -116,6 +113,7 @@ namespace Slang
}
};
+#if 0
template<typename T>
class AllocateMethod<T, StandardAllocator>
{
@@ -129,6 +127,7 @@ namespace Slang
delete[] ptr;
}
};
+#endif
}
#endif
diff --git a/source/core/slang-common.h b/source/core/slang-common.h
index eb6502b41..6f91da873 100644
--- a/source/core/slang-common.h
+++ b/source/core/slang-common.h
@@ -33,8 +33,9 @@ namespace Slang
// TODO(JS): It looks like Index is actually 64 bit on 64 bit targets(!)
// Previous discussions landed on Index being int32_t.
- // Type used for indexing, in arrays/views etc
+ // Type used for indexing, in arrays/views etc. Signed.
typedef Int Index;
+ typedef UInt UIndex;
static const Index kMaxIndex = kMaxInt;
diff --git a/source/core/slang-list.h b/source/core/slang-list.h
index 4420cc084..25687d129 100644
--- a/source/core/slang-list.h
+++ b/source/core/slang-list.h
@@ -145,6 +145,15 @@ namespace Slang
m_capacity = 0;
return rs;
}
+ void attachBuffer(T* buffer, Index count, Index capacity)
+ {
+ // Can only attach a buffer if there isn't a buffer already associated
+ SLANG_ASSERT(m_buffer == nullptr);
+ SLANG_ASSERT(count <= capacity);
+ m_buffer = buffer;
+ m_count = count;
+ m_capacity = capacity;
+ }
inline ArrayView<T> getArrayView() const
{
@@ -324,7 +333,9 @@ namespace Slang
void reserve(Index size)
{
- if(size > m_capacity)
+ // The cast for this comparison is needed, otherwise some compilers erroneously detect
+ // the possiblity of a zero sized allocation (possible if m_capacity is assumed to be negative).
+ if(UIndex(size) > UIndex(m_capacity))
{
T* newBuffer = _allocate(size);
if (m_capacity)
diff --git a/source/core/slang-rtti-info.cpp b/source/core/slang-rtti-info.cpp
new file mode 100644
index 000000000..f53cf742f
--- /dev/null
+++ b/source/core/slang-rtti-info.cpp
@@ -0,0 +1,184 @@
+#include "slang-rtti-info.h"
+
+#include "../../slang-com-helper.h"
+
+#include <mutex>
+
+namespace Slang {
+
+#define SLANG_RTTI_INFO_INVALID(name) RttiInfo{RttiInfo::Kind::Invalid, 0, 0}
+#define SLANG_RTTI_INFO_BASIC(name, type) \
+ RttiInfo{RttiInfo::Kind::name, RttiInfo::AlignmentType(SLANG_ALIGN_OF(type)), RttiInfo::SizeType(sizeof(type))}
+
+/* static */const RttiInfo RttiInfo::g_basicTypes[Index(Kind::CountOf)] =
+{
+ SLANG_RTTI_INFO_INVALID(Invalid),
+ SLANG_RTTI_INFO_BASIC(I32, int32_t),
+ SLANG_RTTI_INFO_BASIC(U32, uint32_t),
+ SLANG_RTTI_INFO_BASIC(I64, int64_t),
+ SLANG_RTTI_INFO_BASIC(U64, uint64_t),
+ SLANG_RTTI_INFO_BASIC(F32, float),
+ SLANG_RTTI_INFO_BASIC(F64, double),
+ SLANG_RTTI_INFO_BASIC(Bool, bool),
+ SLANG_RTTI_INFO_BASIC(String, String),
+ SLANG_RTTI_INFO_BASIC(UnownedStringSlice, UnownedStringSlice),
+ SLANG_RTTI_INFO_BASIC(Ptr, void*),
+ SLANG_RTTI_INFO_BASIC(RefPtr, RefPtr<StringRepresentation>),
+ SLANG_RTTI_INFO_INVALID(FixedArray),
+ SLANG_RTTI_INFO_INVALID(Struct),
+ SLANG_RTTI_INFO_INVALID(Other),
+ SLANG_RTTI_INFO_INVALID(Enum),
+ SLANG_RTTI_INFO_INVALID(List),
+ SLANG_RTTI_INFO_INVALID(Dictionary),
+};
+
+struct RttiInfoManager
+{
+ void* allocate(size_t size)
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ return m_arena.allocate(size);
+ }
+
+ static RttiInfoManager& getSingleton()
+ {
+ static RttiInfoManager g_manager;
+ return g_manager;
+ }
+
+protected:
+ RttiInfoManager() :
+ m_arena(1024)
+ {
+ }
+
+ std::recursive_mutex m_mutex; ///< We need a mutex to guard access to m_arena
+ MemoryArena m_arena;
+};
+
+/* static */void* RttiInfo::allocate(size_t size)
+{
+ return RttiInfoManager::getSingleton().allocate(size);
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StructRttiBuilder !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+static void _appendFixedArray(const FixedArrayRttiInfo* inFixedArray, StringBuilder& out)
+{
+ List<const FixedArrayRttiInfo*> fixedArrays;
+ fixedArrays.add(inFixedArray);
+
+ const RttiInfo* cur = inFixedArray->m_elementType;
+ while (cur->m_kind == RttiInfo::Kind::FixedArray)
+ {
+ const FixedArrayRttiInfo* curArray = static_cast<const FixedArrayRttiInfo*>(cur);
+ fixedArrays.add(curArray);
+ cur = curArray->m_elementType;
+ }
+
+ // Append the 'target' which is in cur
+ RttiInfo::append(cur, out);
+ // Now all the fixed array values, in order
+ for (auto fixedArray : fixedArrays)
+ {
+ out << "[" << int32_t(fixedArray->m_elementCount) << "]";
+ }
+}
+
+/* static */void RttiInfo::append(const RttiInfo* info, StringBuilder& out)
+{
+ switch (info->m_kind)
+ {
+ case RttiInfo::Kind::I32: out << "int32_t"; break;
+ case RttiInfo::Kind::U32: out << "uint32_t"; break;
+ case RttiInfo::Kind::I64: out << "int64_t"; break;
+ case RttiInfo::Kind::U64: out << "uint64_t"; break;
+ case RttiInfo::Kind::F32: out << "float"; break;
+ case RttiInfo::Kind::F64: out << "double"; break;
+ case RttiInfo::Kind::Bool: out << "bool"; break;
+ case RttiInfo::Kind::String: out << "String"; break;
+ case RttiInfo::Kind::UnownedStringSlice: out << "UnownedStringSlice"; break;
+ case RttiInfo::Kind::Ptr:
+ {
+ const PtrRttiInfo* ptrRttiInfo = static_cast<const PtrRttiInfo*>(info);
+ append(ptrRttiInfo->m_targetType, out);
+ out << "*";
+ break;
+ }
+ case RttiInfo::Kind::RefPtr:
+ {
+ const RefPtrRttiInfo* ptrRttiInfo = static_cast<const RefPtrRttiInfo*>(info);
+ out << "RefPtr<";
+ append(ptrRttiInfo->m_targetType, out);
+ out << ">";
+ break;
+ }
+ case RttiInfo::Kind::FixedArray:
+ {
+ const FixedArrayRttiInfo* arrayRttiInfo = static_cast<const FixedArrayRttiInfo*>(info);
+ _appendFixedArray(arrayRttiInfo, out);
+ break;
+ }
+ case RttiInfo::Kind::List:
+ {
+ const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(info);
+ out << "List<";
+ append(listRttiInfo->m_elementType, out);
+ out << ">";
+ break;
+ }
+ case RttiInfo::Kind::Dictionary:
+ {
+ const DictionaryRttiInfo* dictionaryRttiInfo = static_cast<const DictionaryRttiInfo*>(info);
+
+ out << "Dictionary<";
+ append(dictionaryRttiInfo->m_keyType, out);
+ out << ",";
+ append(dictionaryRttiInfo->m_valueType, out);
+ out << ">";
+ break;
+ }
+ default:
+ {
+ if (info->isNamed())
+ {
+ const NamedRttiInfo* namedRttiInfo = static_cast<const NamedRttiInfo*>(info);
+ out << namedRttiInfo->m_name;
+ break;
+ }
+
+ out << "%Unknown%";
+ break;
+ }
+ }
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StructRttiBuilder !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void StructRttiBuilder::_init(const char* name, const StructRttiInfo* super, const Byte* base)
+{
+ m_rttiInfo.m_name = name;
+ m_rttiInfo.m_super = super;
+ m_base = base;
+
+ m_rttiInfo.m_fieldCount = 0;
+ m_rttiInfo.m_fields = nullptr;
+}
+
+StructRttiInfo StructRttiBuilder::make()
+{
+ const Index fieldCount = m_fields.getCount();
+
+ if (fieldCount)
+ {
+ StructRttiInfo::Field* dstFields = (StructRttiInfo::Field*)RttiInfo::allocate(sizeof(StructRttiInfo::Field) * fieldCount);
+ ::memcpy(dstFields, m_fields.getBuffer(), sizeof(StructRttiInfo::Field) * fieldCount);
+
+ m_rttiInfo.m_fields = dstFields;
+ m_rttiInfo.m_fieldCount = fieldCount;
+ }
+
+ return m_rttiInfo;
+}
+
+} // namespace Slang
diff --git a/source/core/slang-rtti-info.h b/source/core/slang-rtti-info.h
new file mode 100644
index 000000000..c1fe1810f
--- /dev/null
+++ b/source/core/slang-rtti-info.h
@@ -0,0 +1,341 @@
+#ifndef SLANG_CORE_RTTI_INFO_H
+#define SLANG_CORE_RTTI_INFO_H
+
+#include "slang-basic.h"
+#include "slang-memory-arena.h"
+
+#include "slang-list.h"
+#include "slang-dictionary.h"
+
+namespace Slang {
+
+struct RttiInfo;
+
+struct RttiTypeFuncs
+{
+ typedef void (*CtorArray)(const RttiInfo* rttiInfo, void* dst, Index count);
+ typedef void (*DtorArray)(const RttiInfo* rttiInfo, void* dst, Index count);
+ typedef void (*CopyArray)(const RttiInfo* rttiInfo, void* dst, const void* src, Index count);
+
+ bool isValid() const { return ctorArray && dtorArray && copyArray; }
+
+ static RttiTypeFuncs makeEmpty() { return RttiTypeFuncs{ nullptr, nullptr, nullptr }; }
+
+ CtorArray ctorArray;
+ DtorArray dtorArray;
+ CopyArray copyArray;
+};
+
+template <typename T>
+struct GetRttiTypeFuncs
+{
+ static void ctorArray(const RttiInfo* rttiInfo, void* in, Index count)
+ {
+ SLANG_UNUSED(rttiInfo);
+ T* dst = (T*)in;
+ for (Index i = 0; i < count; ++i)
+ {
+ new (dst + i) T;
+ }
+ }
+ static void dtorArray(const RttiInfo* rttiInfo, void* in, Index count)
+ {
+ SLANG_UNUSED(rttiInfo);
+ T* dst = (T*)in;
+ for (Index i = 0; i < count; ++i)
+ {
+ (dst + i)->~T();
+ }
+ }
+ static void copyArray(const RttiInfo* rttiInfo, void* inDst, const void* inSrc, Index count)
+ {
+ SLANG_UNUSED(rttiInfo);
+ T* dst = (T*)inDst;
+ const T* src = (T*)inSrc;
+ for (Index i = 0; i < count; ++i)
+ {
+ dst[i] = src[i];
+ }
+ }
+ static RttiTypeFuncs getFuncs()
+ {
+ RttiTypeFuncs funcs;
+ funcs.copyArray = &copyArray;
+ funcs.dtorArray = &dtorArray;
+ funcs.ctorArray = &ctorArray;
+ return funcs;
+ }
+};
+
+struct RttiInfo
+{
+ typedef uint8_t AlignmentType;
+ typedef uint16_t SizeType;
+
+ enum class Kind : uint8_t
+ {
+ Invalid,
+ I32,
+ U32,
+ I64,
+ U64,
+ F32,
+ F64,
+ Bool,
+ String,
+ UnownedStringSlice,
+ Ptr,
+ RefPtr,
+ FixedArray,
+ Struct,
+ Other,
+ Enum,
+ List,
+ Dictionary,
+
+ CountOf,
+ };
+
+ Kind m_kind;
+ AlignmentType m_alignment;
+ SizeType m_size;
+
+ void init(Kind kind, size_t alignment, size_t size) { m_kind = kind; m_alignment = AlignmentType(alignment); m_size = SizeType(size); }
+
+ template <typename T>
+ void init(Kind kind) { init(kind, SLANG_ALIGN_OF(T), sizeof(T)); }
+
+ /// Allocate memory for RttiInfo types.
+ /// Is thread safe, and doesn't require the memory to be freed explicitly
+ /// Will be freed at shutdown (via global dtor)
+ static void* allocate(size_t size);
+
+ static bool isIntegral(RttiInfo::Kind kind) { return Index(kind) >= Index(RttiInfo::Kind::I32) && Index(kind) <= Index(RttiInfo::Kind::U64); }
+ static bool isFloat(RttiInfo::Kind kind) { return kind == RttiInfo::Kind::F32 || kind == RttiInfo::Kind::F64; }
+ static bool isBuiltIn(RttiInfo::Kind kind) { return kind == RttiInfo::Kind::I32 || kind == RttiInfo::Kind::Bool; }
+ static bool isNamed(RttiInfo::Kind kind) { return Index(kind) >= Index(RttiInfo::Kind::Struct) && Index(kind) <= Index(RttiInfo::Kind::Enum); }
+
+ bool isIntegral() const { return isIntegral(m_kind); }
+ bool isFloat() const { return isFloat(m_kind); }
+ bool isBuiltIn() const { return isBuiltIn(m_kind); }
+ bool isNamed() const { return isNamed(m_kind); }
+
+ static void append(const RttiInfo* info, StringBuilder& out);
+
+ static const RttiInfo g_basicTypes[Index(Kind::CountOf)];
+};
+
+// Can combine into flags on a field. Could store default value with a field,
+// but this works fine for most purposes
+enum class RttiDefaultValue : uint8_t
+{
+ Normal, ///< Zero for integral/float types/false for bool
+ One,
+ MinusOne,
+
+ Mask = 0x7,
+};
+
+struct NamedRttiInfo : public RttiInfo
+{
+ const char* m_name; ///< Name
+};
+
+struct StructRttiInfo : public NamedRttiInfo
+{
+ typedef uint8_t Flags;
+ struct Flag
+ {
+ enum Enum : Flags
+ {
+ // We use low bits for 'RttiDefaultValue' value
+ Optional = 0x8,
+ };
+ };
+
+ struct Field
+ {
+ const char* m_name; ///< Name of this field
+ const RttiInfo* m_type; ///< The type of this field
+ uint32_t m_offset; ///< Offset from object type in bytes
+ Flags m_flags; ///< Field flags
+ };
+
+ const StructRttiInfo* m_super; ///< Super class or nullptr if not defined
+
+ Index m_fieldCount; ///< Amount of fields
+ const Field* m_fields; ///< Fields
+};
+
+struct EnumRttiInfo : public NamedRttiInfo
+{
+ // TODO(JS):
+};
+
+SLANG_FORCE_INLINE StructRttiInfo::Flags combine(StructRttiInfo::Flags flags, RttiDefaultValue defaultValue)
+{
+ return StructRttiInfo::Flags(defaultValue) | flags;
+}
+
+struct ListRttiInfo : public RttiInfo
+{
+ const RttiInfo* m_elementType;
+};
+
+struct DictionaryRttiInfo : public RttiInfo
+{
+ const RttiInfo* m_keyType;
+ const RttiInfo* m_valueType;
+};
+
+struct PtrRttiInfo : public RttiInfo
+{
+ const RttiInfo* m_targetType;
+};
+
+struct RefPtrRttiInfo : public RttiInfo
+{
+ const RttiInfo* m_targetType;
+};
+
+struct FixedArrayRttiInfo : public RttiInfo
+{
+ const RttiInfo* m_elementType;
+ size_t m_elementCount;
+};
+
+struct OtherRttiInfo : public NamedRttiInfo
+{
+ typedef bool (*IsDefaultFunc)(const RttiInfo* rttiInfo, const void* in);
+ IsDefaultFunc m_isDefaultFunc;
+ RttiTypeFuncs m_typeFuncs;
+};
+
+// The default is to just get the info from a global held inside the type.
+template <typename T>
+struct GetRttiInfo
+{
+ SLANG_FORCE_INLINE static const RttiInfo* get() { return &T::g_rttiInfo; }
+};
+
+template <> struct GetRttiInfo<bool> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::Bool)];} };
+template <> struct GetRttiInfo<int32_t> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::I32)]; } };
+template <> struct GetRttiInfo<int64_t> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::I64)]; } };
+template <> struct GetRttiInfo<uint32_t> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::U32)]; } };
+template <> struct GetRttiInfo<uint64_t> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::U64)]; } };
+template <> struct GetRttiInfo<float> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::F32)]; } };
+template <> struct GetRttiInfo<double> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::F64)]; } };
+template <> struct GetRttiInfo<String> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::String)]; } };
+template <> struct GetRttiInfo<UnownedStringSlice> { static const RttiInfo* get() { return &RttiInfo::g_basicTypes[Index(RttiInfo::Kind::UnownedStringSlice)]; } };
+
+template <typename T>
+struct GetRttiInfo<List<T>>
+{
+ static const ListRttiInfo _make()
+ {
+ ListRttiInfo info;
+ info.init<List<Byte>>(RttiInfo::Kind::List);
+ info.m_elementType = GetRttiInfo<T>::get();
+ return info;
+ }
+ static const RttiInfo* get() { static const ListRttiInfo g_info = _make(); return &g_info; }
+};
+
+// Strip const
+template <typename T>
+struct GetRttiInfo<const T>
+{
+ static const RttiInfo* get() { return GetRttiInfo<T>::get(); }
+};
+
+template <typename K, typename V>
+struct GetRttiInfo<Dictionary<K, V>>
+{
+ static const DictionaryRttiInfo _make()
+ {
+ DictionaryRttiInfo info;
+ info.init<Dictionary<Byte, Byte>>(RttiInfo::Kind::Dictionary);
+ info.m_keyType = GetRttiInfo<K>::get();
+ info.m_valueType = GetRttiInfo<V>::get();
+ return info;
+ }
+ static const RttiInfo* get() { static const DictionaryRttiInfo g_info = _make(); return &g_info; }
+};
+
+template <typename TARGET>
+struct GetRttiInfo<TARGET*>
+{
+ static const PtrRttiInfo _make()
+ {
+ PtrRttiInfo info;
+ info.init<void*>(RttiInfo::Kind::Ptr);
+ info.m_targetType = GetRttiInfo<TARGET>::get();
+ return info;
+ }
+ static const RttiInfo* get() { static const PtrRttiInfo g_info = _make(); return &g_info; }
+};
+
+template <typename TARGET>
+struct GetRttiInfo<RefPtr<TARGET>>
+{
+ static const RefPtrRttiInfo _make()
+ {
+ RefPtrRttiInfo info;
+ info.init<RefPtr<StringRepresentation>>(RttiInfo::Kind::RefPtr);
+ info.m_targetType = GetRttiInfo<TARGET>::get();
+ return info;
+ }
+ static const RttiInfo* get() { static const RefPtrRttiInfo g_info = _make(); return &g_info; }
+};
+
+template <typename T, size_t COUNT>
+struct GetRttiInfo<T[COUNT]>
+{
+ static const FixedArrayRttiInfo _make()
+ {
+ FixedArrayRttiInfo info;
+ info.m_kind = RttiInfo::Kind::FixedArray;
+ info.m_alignment = RttiInfo::AlignmentType(SLANG_ALIGN_OF(T));
+ info.m_size = RttiInfo::SizeType(sizeof(T) * COUNT);
+ info.m_elementType = GetRttiInfo<T>::get();
+ info.m_elementCount = COUNT;
+ return info;
+ }
+ static const RttiInfo* get() { static const FixedArrayRttiInfo g_info = _make(); return &g_info; }
+};
+
+struct StructRttiBuilder
+{
+ template <typename T>
+ StructRttiBuilder(T* obj, const char* name, const StructRttiInfo* super)
+ {
+ m_rttiInfo.init<T>(RttiInfo::Kind::Struct);
+ _init(name, super, (const Byte*)obj);
+ }
+
+ template <typename T>
+ void addField(const char* name, const T* fieldPtr, StructRttiInfo::Flags flags = 0)
+ {
+ StructRttiInfo::Field field;
+
+ field.m_name = name;
+ field.m_type = GetRttiInfo<T>::get();
+ field.m_offset = uint32_t(ptrdiff_t((const Byte*)fieldPtr - m_base));
+ field.m_flags = flags;
+ m_fields.add(field);
+ }
+
+ StructRttiInfo make();
+
+ void _init(const char* name, const StructRttiInfo* super, const Byte* base);
+
+ StructRttiInfo m_rttiInfo;
+
+ List<StructRttiInfo::Field> m_fields;
+ const Byte* m_base;
+};
+
+
+} // namespace Slang
+
+#endif // SLANG_CORE_RTTI_INFO_H
diff --git a/source/core/slang-rtti-util.cpp b/source/core/slang-rtti-util.cpp
new file mode 100644
index 000000000..ed4c32e58
--- /dev/null
+++ b/source/core/slang-rtti-util.cpp
@@ -0,0 +1,392 @@
+#include "slang-rtti-util.h"
+
+namespace Slang {
+
+/* static */SlangResult RttiUtil::setInt(int64_t value, const RttiInfo* rttiInfo, void* dst)
+{
+ SLANG_ASSERT(rttiInfo->isIntegral());
+
+ // We could check ranges are appropriate, but for now we just write.
+ // Passing in rttiInfo allows for other more complex types to be econverted
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::I32: *(int32_t*)dst = int32_t(value); break;
+ case RttiInfo::Kind::U32: *(uint32_t*)dst = uint32_t(value); break;
+ case RttiInfo::Kind::I64: *(int64_t*)dst = int64_t(value); break;
+ case RttiInfo::Kind::U64: *(uint64_t*)dst = uint64_t(value); break;
+ default: return SLANG_FAIL;
+ }
+ return SLANG_OK;
+}
+
+/* static */int64_t RttiUtil::getInt64(const RttiInfo* rttiInfo, const void* src)
+{
+ SLANG_ASSERT(rttiInfo->isIntegral());
+
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::I32: return *(const int32_t*)src;
+ case RttiInfo::Kind::U32: return *(const uint32_t*)src;
+ case RttiInfo::Kind::I64: return *(const int64_t*)src;
+ case RttiInfo::Kind::U64: return *(const uint64_t*)src;
+ default: break;
+ }
+
+ SLANG_ASSERT(!"Not integral!");
+ return -1;
+}
+
+/* static */double RttiUtil::asDouble(const RttiInfo* rttiInfo, const void* src)
+{
+ if (rttiInfo->isIntegral())
+ {
+ return (double)getInt64(rttiInfo, src);
+ }
+ else if (rttiInfo->isFloat())
+ {
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::F32: return *(const float*)src;
+ case RttiInfo::Kind::F64: return *(const double*)src;
+ default: break;
+ }
+ }
+
+ SLANG_ASSERT(!"Cannot convert to float");
+ return 0.0;
+}
+
+/* static */SlangResult RttiUtil::setFromDouble(double v, const RttiInfo* rttiInfo, void* dst)
+{
+ if (rttiInfo->isIntegral())
+ {
+ return setInt(int64_t(v), rttiInfo, dst);
+ }
+ else if (rttiInfo->isFloat())
+ {
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::F32: *(float*)dst = float(v); return SLANG_OK;
+ case RttiInfo::Kind::F64: *(double*)dst = v; return SLANG_OK;
+ default: break;
+ }
+ }
+
+ return SLANG_FAIL;
+}
+
+/* static */bool RttiUtil::asBool(const RttiInfo* rttiInfo, const void* src)
+{
+ if (rttiInfo->m_kind == RttiInfo::Kind::Bool)
+ {
+ return *(const bool*)src;
+ }
+
+ if (rttiInfo->isIntegral())
+ {
+ return getInt64(rttiInfo, src) != 0;
+ }
+ else if (rttiInfo->isFloat())
+ {
+ return asDouble(rttiInfo, src) != 0.0;
+ }
+
+ SLANG_ASSERT(!"Cannot convert to bool");
+ return false;
+}
+
+static int64_t _getIntDefaultValue(RttiDefaultValue value)
+{
+ switch (value)
+ {
+ default:
+ case RttiDefaultValue::Normal: return 0;
+ case RttiDefaultValue::One: return 1;
+ case RttiDefaultValue::MinusOne: return -1;
+ }
+}
+
+static bool _isStructDefault(const StructRttiInfo* type, const void* src)
+{
+ if (type->m_super)
+ {
+ if (!_isStructDefault(type->m_super, src))
+ {
+ return false;
+ }
+ }
+
+ const Byte* base = (const Byte*)src;
+
+ const Index count = type->m_fieldCount;
+ for (Index i = 0; i < count; ++i)
+ {
+ const auto& field = type->m_fields[i];
+
+ const RttiDefaultValue defaultValue = RttiDefaultValue(field.m_flags & uint8_t(RttiDefaultValue::Mask));
+
+ if (!RttiUtil::isDefault(defaultValue, field.m_type, base + field.m_offset))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */bool RttiUtil::isDefault(RttiDefaultValue defaultValue, const RttiInfo* rttiInfo, const void* src)
+{
+ if (rttiInfo->isIntegral())
+ {
+ const auto value = getInt64(rttiInfo, src);
+ return _getIntDefaultValue(defaultValue) == value;
+ }
+ else if (rttiInfo->isFloat())
+ {
+ const auto value = asDouble(rttiInfo, src);
+ return _getIntDefaultValue(defaultValue) == value;
+ }
+
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::Invalid: return true;
+ case RttiInfo::Kind::Bool: return *(const bool*)src == (_getIntDefaultValue(defaultValue) != 0);
+ case RttiInfo::Kind::String:
+ {
+ return ((const String*)src)->getLength() == 0;
+ }
+ case RttiInfo::Kind::UnownedStringSlice:
+ {
+ return ((const UnownedStringSlice*)src)->getLength() == 0;
+ }
+ case RttiInfo::Kind::Struct:
+ {
+ return _isStructDefault(static_cast<const StructRttiInfo*>(rttiInfo), src);
+ }
+ case RttiInfo::Kind::Enum:
+ {
+ SLANG_ASSERT(!"Not implemented yet");
+ return false;
+ }
+ case RttiInfo::Kind::List:
+ {
+ const auto& v = *(const List<Byte>*)src;
+ return v.getCount() == 0;
+ }
+ case RttiInfo::Kind::Dictionary:
+ {
+ const auto& v = *(const Dictionary<Byte, Byte>*)src;
+ return v.Count() == 0;
+ }
+ case RttiInfo::Kind::Other:
+ {
+ const OtherRttiInfo* otherRttiInfo = static_cast<const OtherRttiInfo*>(rttiInfo);
+ return otherRttiInfo->m_isDefaultFunc && otherRttiInfo->m_isDefaultFunc(rttiInfo, src);
+ }
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+template <typename T>
+struct GetRttiTypeFuncsForBuiltIn
+{
+ static void ctorArray(const RttiInfo* rttiInfo, void* dst, Index count) { SLANG_UNUSED(rttiInfo); ::memset(dst, 0, sizeof(T) * count); }
+ static void dtorArray(const RttiInfo* rttiInfo, void* dst, Index count) { SLANG_UNUSED(rttiInfo); SLANG_UNUSED(dst); SLANG_UNUSED(count); }
+ static void copyArray(const RttiInfo* rttiInfo, void* dst, const void* src, Index count) { SLANG_UNUSED(rttiInfo); ::memcpy(dst, src, sizeof(T) * count); }
+
+ static RttiTypeFuncs getFuncs()
+ {
+ RttiTypeFuncs funcs;
+ funcs.copyArray = &copyArray;
+ funcs.dtorArray = &dtorArray;
+ funcs.ctorArray = &ctorArray;
+ return funcs;
+ }
+};
+
+struct ListFuncs
+{
+ static void ctorArray(const RttiInfo* rttiInfo, void* inDst, Index count)
+ {
+ SLANG_UNUSED(rttiInfo);
+ SLANG_ASSERT(rttiInfo->m_kind == RttiInfo::Kind::List);
+
+ // We don't care about the element type, as we can just initialize them all as List<Byte>
+ //const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
+ typedef List<Byte> Type;
+
+ Type* dst = (Type*)inDst;
+
+ for (Index i = 0; i < count; ++i)
+ {
+ new (dst + i) Type;
+ }
+ }
+ static void copyArray(const RttiInfo* rttiInfo, void* inDst, const void* inSrc, Index count)
+ {
+ SLANG_ASSERT(rttiInfo->m_kind == RttiInfo::Kind::List);
+ const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
+ const auto elementType = listRttiInfo->m_elementType;
+
+ // We need to get the type funcs
+ auto typeFuncs = RttiUtil::getTypeFuncs(elementType);
+ SLANG_ASSERT(typeFuncs.isValid());
+
+ // We need a type that we can get information from the list from - List<Byte> gives us the functions we need.
+ typedef List<Byte> Type;
+
+ Type* dst = (Type*)inDst;
+ const Type* src = (const Type*)inSrc;
+
+ for (Index i = 0; i < count; ++i)
+ {
+ auto& dstList = dst[i];
+ auto& srcList = src[i];
+
+ const Index srcCount = srcList.getCount();
+
+ if (srcCount > dstList.getCount())
+ {
+ // Allocate new memory
+ const Index dstCapacity = dstList.getCapacity();
+ void* oldBuffer = dstList.detachBuffer();
+
+ void* newBuffer = ::malloc(count * elementType->m_size);
+ // Initialize it all first
+ typeFuncs.ctorArray(elementType, newBuffer, count);
+ typeFuncs.copyArray(elementType, newBuffer, oldBuffer, count);
+
+ // Attach the new buffer
+ dstList.attachBuffer((Byte*)newBuffer, count, count);
+
+ // Free the old buffer
+ if (oldBuffer)
+ {
+ typeFuncs.dtorArray(elementType, oldBuffer, dstCapacity);
+
+ ::free(oldBuffer);
+ }
+ }
+ else
+ {
+ typeFuncs.copyArray(elementType, dstList.getBuffer(), srcList.getBuffer(), srcCount);
+ dstList.unsafeShrinkToCount(srcCount);
+ }
+ }
+ }
+
+ static void dtorArray(const RttiInfo* rttiInfo, void* inDst, Index count)
+ {
+ SLANG_ASSERT(rttiInfo->m_kind == RttiInfo::Kind::List);
+ const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
+
+ const auto elementType = listRttiInfo->m_elementType;
+
+ // We need to get the type funcs
+ auto typeFuncs = RttiUtil::getTypeFuncs(elementType);
+ SLANG_ASSERT(typeFuncs.isValid());
+
+ typedef List<Byte> Type;
+ Type* dst = (Type*)inDst;
+
+ for (Index i = 0; i < count; ++i)
+ {
+ auto& dstList = dst[i];
+
+ const Index capacity = dstList.getCapacity();
+ Byte* buffer = dstList.detachBuffer();
+
+ if (buffer)
+ {
+ typeFuncs.dtorArray(elementType, buffer, capacity);
+ ::free(buffer);
+ }
+ }
+ }
+
+ static RttiTypeFuncs getFuncs()
+ {
+ RttiTypeFuncs funcs;
+ funcs.copyArray = &copyArray;
+ funcs.dtorArray = &dtorArray;
+ funcs.ctorArray = &ctorArray;
+ return funcs;
+ }
+};
+
+RttiTypeFuncs RttiUtil::getTypeFuncs(const RttiInfo* rttiInfo)
+{
+ if (rttiInfo->isBuiltIn())
+ {
+ switch (rttiInfo->m_size)
+ {
+ case 1: return GetRttiTypeFuncsForBuiltIn<uint8_t>::getFuncs();
+ case 2: return GetRttiTypeFuncsForBuiltIn<uint16_t>::getFuncs();
+ case 4: return GetRttiTypeFuncsForBuiltIn<uint32_t>::getFuncs();
+ case 8: return GetRttiTypeFuncsForBuiltIn<uint64_t>::getFuncs();
+ }
+ return RttiTypeFuncs::makeEmpty();
+ }
+
+ switch (rttiInfo->m_kind)
+ {
+ case RttiInfo::Kind::String: return GetRttiTypeFuncs<String>::getFuncs();
+ case RttiInfo::Kind::UnownedStringSlice: return GetRttiTypeFuncs<UnownedStringSlice>::getFuncs();
+ case RttiInfo::Kind::List: return ListFuncs::getFuncs();
+ default: break;
+ }
+
+ return RttiTypeFuncs::makeEmpty();
+}
+
+/* static */SlangResult RttiUtil::setListCount(const RttiInfo* elementType, void* dst, Index count)
+{
+ // NOTE! The following only works because List<T> has capacity initialized members, and
+ // setting the count if it is <= capacity just sets the count (ie things aren't released(!)).
+
+ List<Byte>& dstList = *(List<Byte>*)dst;
+ const Index oldCount = dstList.getCount();
+ if (oldCount == count)
+ {
+ return SLANG_OK;
+ }
+ if (count < oldCount)
+ {
+ dstList.unsafeShrinkToCount(count);
+ return SLANG_OK;
+ }
+
+ // Get funcs needed
+ const auto typeFuncs = RttiUtil::getTypeFuncs(elementType);
+
+ if (!typeFuncs.isValid())
+ {
+ return SLANG_FAIL;
+ }
+
+ const Index dstCapacity = dstList.getCapacity();
+ void* oldBuffer = dstList.detachBuffer();
+
+ void* newBuffer = ::malloc(count * elementType->m_size);
+ // Initialize it all first
+ typeFuncs.ctorArray(elementType, newBuffer, count);
+
+ typeFuncs.copyArray(elementType, newBuffer, oldBuffer, oldCount);
+
+ // Attach the new buffer
+ dstList.attachBuffer((Byte*)newBuffer, count, count);
+
+ // Free the old buffer
+ if (oldBuffer)
+ {
+ typeFuncs.dtorArray(elementType, oldBuffer, dstCapacity);
+ ::free(oldBuffer);
+ }
+
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/core/slang-rtti-util.h b/source/core/slang-rtti-util.h
new file mode 100644
index 000000000..d514d1980
--- /dev/null
+++ b/source/core/slang-rtti-util.h
@@ -0,0 +1,30 @@
+#ifndef SLANG_CORE_RTTI_UTIL_H
+#define SLANG_CORE_RTTI_UTIL_H
+
+#include "slang-rtti-info.h"
+
+namespace Slang {
+struct RttiUtil
+{
+
+ static SlangResult setInt(int64_t value, const RttiInfo* rttiInfo, void* dst);
+ static int64_t getInt64(const RttiInfo* rttiInfo, const void* src);
+
+ static double asDouble(const RttiInfo* rttiInfo, const void* src);
+
+ static SlangResult setFromDouble(double v, const RttiInfo* rttiInfo, void* dst);
+
+ static bool asBool(const RttiInfo* rttiInfo, const void* src);
+
+ static bool isDefault(RttiDefaultValue defaultValue, const RttiInfo* rttiInfo, const void* src);
+
+ static RttiTypeFuncs getTypeFuncs(const RttiInfo* rttiInfo);
+
+ /// Set a list count
+ static SlangResult setListCount(const RttiInfo* elementType, void* dst, Index count);
+
+};
+
+} // namespace Slang
+
+#endif // SLANG_CORE_RTTI_UTIL_H
diff --git a/tools/slang-unit-test/unit-test-json-native.cpp b/tools/slang-unit-test/unit-test-json-native.cpp
new file mode 100644
index 000000000..1d2085751
--- /dev/null
+++ b/tools/slang-unit-test/unit-test-json-native.cpp
@@ -0,0 +1,117 @@
+// unit-test-json-native.cpp
+
+#include "../../source/core/slang-rtti-info.h"
+
+#include "../../source/compiler-core/slang-json-native.h"
+#include "../../source/compiler-core/slang-json-parser.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+using namespace Slang;
+
+namespace { // anonymous
+
+struct SomeStruct
+{
+ typedef SomeStruct ThisType;
+
+ bool operator==(const ThisType& rhs) const
+ {
+ return a == rhs.a && b == rhs.b && s == rhs.s && list == rhs.list;
+ }
+ bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
+
+ int a = 0;
+ float b = 2.0f;
+ String s;
+ List<String> list;
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
+} // anonymous
+
+static const StructRttiInfo _makeSomeStructRtti()
+{
+ SomeStruct obj;
+ StructRttiBuilder builder(&obj, "SomeStruct", nullptr);
+
+ builder.addField("a", &obj.a);
+ builder.addField("b", &obj.b);
+ builder.addField("s", &obj.s);
+ builder.addField("list", &obj.list);
+
+ return builder.make();
+}
+/* static */const StructRttiInfo SomeStruct::g_rttiInfo = _makeSomeStructRtti();
+
+static SlangResult _check()
+{
+ // Convert into a JSON string
+
+ SomeStruct s;
+ s.list.add("Hello!");
+ s.s = "There";
+
+ // Try serializing it out
+
+ SourceManager sourceManager;
+ sourceManager.initialize(nullptr, nullptr);
+
+ DiagnosticSink sink(&sourceManager, &JSONLexer::calcLexemeLocation);
+
+ RefPtr<JSONContainer> container(new JSONContainer(&sourceManager));
+
+ String json;
+ {
+ NativeToJSONConverter converter(container, &sink);
+
+ JSONValue value;
+ SLANG_RETURN_ON_FAIL(converter.convert(GetRttiInfo<SomeStruct>::get(), &s, value));
+
+ // Convert into a string
+ JSONWriter writer(JSONWriter::IndentationStyle::Allman);
+ container->traverseRecursively(value, &writer);
+
+ json = writer.getBuilder();
+ }
+
+ JSONValue readValue;
+ {
+ // Now need to parse as JSON
+ String contents(json);
+ SourceFile* sourceFile = sourceManager.createSourceFileWithString(PathInfo::makeUnknown(), contents);
+ SourceView* sourceView = sourceManager.createSourceView(sourceFile, nullptr, SourceLoc());
+
+ JSONLexer lexer;
+ lexer.init(sourceView, &sink);
+
+ JSONBuilder builder(container);
+
+ JSONParser parser;
+ SLANG_RETURN_ON_FAIL(parser.parse(&lexer, sourceView, &builder, &sink));
+
+ readValue = builder.getRootValue();
+ }
+
+ // Convert back to native
+ {
+ JSONToNativeConverter converter(container, &sink);
+
+ {
+ SomeStruct readS;
+ SLANG_RETURN_ON_FAIL(converter.convert(readValue, GetRttiInfo<SomeStruct>::get(), &readS));
+
+ // Should be equal
+ SLANG_CHECK(readS == s);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SLANG_UNIT_TEST(JSONNative)
+{
+ SLANG_CHECK(SLANG_SUCCEEDED(_check()));
+
+}
diff --git a/tools/slang-unit-test/unit-test-rtti.cpp b/tools/slang-unit-test/unit-test-rtti.cpp
new file mode 100644
index 000000000..f72cfbde2
--- /dev/null
+++ b/tools/slang-unit-test/unit-test-rtti.cpp
@@ -0,0 +1,74 @@
+// unit-test-rtti.cpp
+
+#include "../../source/core/slang-rtti-info.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+using namespace Slang;
+
+namespace { // anonymous
+
+struct SomeStruct
+{
+ int a = 0;
+ float b = 2.0f;
+ String s;
+ List<String> list;
+
+ static const StructRttiInfo g_rttiInfo;
+};
+
+} // anonymous
+
+static const StructRttiInfo _makeSomeStructRtti()
+{
+ SomeStruct obj;
+ StructRttiBuilder builder(&obj, "SomeStruct", nullptr);
+
+ builder.addField("a", &obj.a);
+ builder.addField("b", &obj.b);
+ builder.addField("s", &obj.s);
+ builder.addField("list", &obj.list);
+
+ return builder.make();
+}
+/* static */const StructRttiInfo SomeStruct::g_rttiInfo = _makeSomeStructRtti();
+
+SLANG_UNIT_TEST(Rtti)
+{
+ using namespace Slang;
+
+ const RttiInfo* types[] =
+ {
+ GetRttiInfo<int32_t>::get(),
+ GetRttiInfo<int32_t[10]>::get(),
+ GetRttiInfo<String>::get(),
+ GetRttiInfo<List<String>>::get(),
+ GetRttiInfo<List<List<String>>>::get(),
+ GetRttiInfo<int32_t[2][3]>::get(),
+ GetRttiInfo<SomeStruct>::get(),
+ GetRttiInfo<SomeStruct*>::get(),
+ GetRttiInfo<const float*const>::get(),
+ };
+
+ StringBuilder buf;
+
+ for (auto type : types)
+ {
+ RttiInfo::append(type, buf);
+ buf << "\n";
+ }
+
+ const char expected[] =
+ "int32_t\n"
+ "int32_t[10]\n"
+ "String\n"
+ "List<String>\n"
+ "List<List<String>>\n"
+ "int32_t[2][3]\n"
+ "SomeStruct\n"
+ "SomeStruct*\n"
+ "float*\n";
+
+ SLANG_CHECK(buf == expected)
+}
diff --git a/tools/test-server/test-server-diagnostic-defs.h b/tools/test-server/test-server-diagnostic-defs.h
new file mode 100644
index 000000000..ef0b280e8
--- /dev/null
+++ b/tools/test-server/test-server-diagnostic-defs.h
@@ -0,0 +1,26 @@
+//
+
+// The file is meant to be included multiple times, to produce different
+// pieces of declaration/definition code related to diagnostic messages
+//
+// Each diagnostic is declared here with:
+//
+// DIAGNOSTIC(id, severity, name, messageFormat)
+//
+// Where `id` is the unique diagnostic ID, `severity` is the default
+// severity (from the `Severity` enum), `name` is a name used to refer
+// to this diagnostic from code, and `messageFormat` is the default
+// (non-localized) message for the diagnostic, with placeholders
+// for any arguments.
+
+#ifndef DIAGNOSTIC
+#error Need to #define DIAGNOSTIC(...) before including "test-server-diagnostics-defs.h"
+#define DIAGNOSTIC(id, severity, name, messageFormat) /* */
+#endif
+
+DIAGNOSTIC(100000, Error, unableToLoadSharedLibrary, "Unable to load shared library '$0'")
+DIAGNOSTIC(100001, Error, unableToFindFunctionInSharedLibrary, "Unable to find function '$0' in shared library")
+DIAGNOSTIC(100002, Error, unableToGetUnitTestModule, "Unable to get unit test module")
+DIAGNOSTIC(100003, Error, unableToFindTest, "Unable to find test '$0'")
+
+#undef DIAGNOSTIC
diff --git a/tools/test-server/test-server-diagnostics.cpp b/tools/test-server/test-server-diagnostics.cpp
new file mode 100644
index 000000000..ddefc53d3
--- /dev/null
+++ b/tools/test-server/test-server-diagnostics.cpp
@@ -0,0 +1,13 @@
+#include "test-server-diagnostics.h"
+
+namespace TestServer {
+
+namespace ServerDiagnostics
+{
+using namespace Slang;
+
+#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, #name, messageFormat };
+#include "test-server-diagnostic-defs.h"
+}
+
+} // namespace TestServer
diff --git a/tools/test-server/test-server-diagnostics.h b/tools/test-server/test-server-diagnostics.h
new file mode 100644
index 000000000..be816135a
--- /dev/null
+++ b/tools/test-server/test-server-diagnostics.h
@@ -0,0 +1,21 @@
+#ifndef TEST_SERVER_DIAGNOSTICS_H
+#define TEST_SERVER_DIAGNOSTICS_H
+
+#include "../../source/core/slang-basic.h"
+#include "../../source/core/slang-writer.h"
+
+#include "../../source/compiler-core/slang-source-loc.h"
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+
+namespace TestServer {
+using namespace Slang;
+
+namespace ServerDiagnostics {
+
+#define DIAGNOSTIC(id, severity, name, messageFormat) extern const DiagnosticInfo name;
+#include "test-server-diagnostic-defs.h"
+
+} // ServerDiagnostics
+} // TestServer
+
+#endif
diff --git a/tools/test-server/test-server-main.cpp b/tools/test-server/test-server-main.cpp
new file mode 100644
index 000000000..7e4180441
--- /dev/null
+++ b/tools/test-server/test-server-main.cpp
@@ -0,0 +1,510 @@
+// test-server.cpp
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../source/core/slang-secure-crt.h"
+
+#include "../../slang-com-helper.h"
+
+#include "../../source/core/slang-string.h"
+#include "../../source/core/slang-io.h"
+#include "../../source/core/slang-writer.h"
+#include "../../source/core/slang-string-util.h"
+#include "../../source/core/slang-process-util.h"
+
+#include "../../source/core/slang-shared-library.h"
+
+#include "../../source/core/slang-test-tool-util.h"
+#include "../../source/core/slang-http.h"
+
+#include "../../source/compiler-core/slang-source-loc.h"
+#include "../../source/compiler-core/slang-diagnostic-sink.h"
+
+#include "../../source/compiler-core/slang-json-parser.h"
+#include "../../source/compiler-core/slang-json-rpc.h"
+#include "../../source/compiler-core/slang-json-value.h"
+
+#include "test-server-diagnostics.h"
+
+#include "tools/unit-test/slang-unit-test.h"
+
+namespace TestServer
+{
+using namespace Slang;
+
+class TestReporter : public ITestReporter
+{
+public:
+ // ITestReporter
+ virtual SLANG_NO_THROW void SLANG_MCALL startTest(const char* testName) SLANG_OVERRIDE { }
+ virtual SLANG_NO_THROW void SLANG_MCALL addResult(TestResult result)SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addResultWithLocation(TestResult result, const char* testText, const char* file, int line) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addResultWithLocation(bool testSucceeded, const char* testText, const char* file, int line) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL addExecutionTime(double time) SLANG_OVERRIDE { }
+ virtual SLANG_NO_THROW void SLANG_MCALL message(TestMessageType type, const char* message) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW void SLANG_MCALL endTest() SLANG_OVERRIDE { }
+
+ StringBuilder m_buf;
+ Index m_failCount = 0;
+ Index m_testCount = 0;
+};
+
+class TestServer
+{
+public:
+ typedef Slang::TestToolUtil::InnerMainFunc InnerMainFunc;
+
+ SlangResult init(int argc, const char* const* argv);
+
+ /// Can return nullptr if cannot create the session
+ slang::IGlobalSession* getGlobalSession();
+
+ /// Can return nullptr if cannot load the tool
+ ISlangSharedLibrary* loadSharedLibrary(const String& name, DiagnosticSink* sink = nullptr);
+
+ /// Get a unit test module. Returns nullptr if not found.
+ IUnitTestModule* getUnitTestModule(const String& name, DiagnosticSink* sink = nullptr);
+
+ /// Given a tool name return it's function pointer. Or nullptr on failure.
+ InnerMainFunc getToolFunction(const String& name, DiagnosticSink* sink = nullptr);
+
+ /// Execute the server
+ SlangResult execute();
+
+ /// Dtor
+ ~TestServer();
+
+protected:
+ SlangResult _executeSingle();
+ SlangResult _executeUnitTest(JSONContainer* container, const JSONValue& root);
+ SlangResult _executeTool(JSONContainer* container, const JSONValue& root);
+ SlangResult _writeResponse(JSONContainer* containers, const JSONValue& root);
+
+ bool m_quit = false;
+
+ ComPtr<slang::IGlobalSession> m_session;
+
+ Dictionary<String, ComPtr<ISlangSharedLibrary>> m_sharedLibraryMap; ///< Maps tool names to the dll
+ Dictionary<String, IUnitTestModule*> m_unitTestModules;
+
+ String m_exePath; ///< Path to executable
+
+ DiagnosticSink m_diagnosticSink;
+ SourceManager m_sourceManager;
+
+ RefPtr<HTTPPacketConnection> m_connection;
+};
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! TestServer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+SlangResult TestServer::init(int argc, const char* const* argv)
+{
+ m_exePath = Path::getParentDirectory(argv[0]);
+
+ RefPtr<Stream> stdinStream, stdoutStream;
+
+ Process::getStdStream(Process::StreamType::StdIn, stdinStream);
+ Process::getStdStream(Process::StreamType::StdOut, stdoutStream);
+
+ RefPtr<BufferedReadStream> readStream(new BufferedReadStream(stdinStream));
+
+ m_connection = new HTTPPacketConnection(readStream, stdoutStream);
+
+ m_sourceManager.initialize(nullptr, nullptr);
+ m_diagnosticSink.init(&m_sourceManager, &JSONLexer::calcLexemeLocation);
+
+ return SLANG_OK;
+}
+
+TestServer::~TestServer()
+{
+ for (auto& pair : m_unitTestModules)
+ {
+ pair.Value->destroy();
+ }
+}
+
+slang::IGlobalSession* TestServer::getGlobalSession()
+{
+ if (!m_session)
+ {
+ // Just create the global session in the regular way if there isn't one set
+ if (SLANG_FAILED(slang_createGlobalSession(SLANG_API_VERSION, m_session.writeRef())))
+ {
+ return nullptr;
+ }
+ TestToolUtil::setSessionDefaultPreludeFromExePath(m_exePath.getBuffer(), m_session);
+ }
+
+ return m_session;
+}
+
+ISlangSharedLibrary* TestServer::loadSharedLibrary(const String& name, DiagnosticSink* sink)
+{
+ ComPtr<ISlangSharedLibrary> lib;
+ if (m_sharedLibraryMap.TryGetValue(name, lib))
+ {
+ return lib;
+ }
+
+ auto loader = DefaultSharedLibraryLoader::getSingleton();
+
+ auto toolPath = Path::combine(m_exePath, name);
+
+ ComPtr<ISlangSharedLibrary> sharedLibrary;
+ if (SLANG_FAILED(loader->loadSharedLibrary(toolPath.getBuffer(), sharedLibrary.writeRef())))
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToLoadSharedLibrary, name);
+ }
+
+ return nullptr;
+ }
+
+ m_sharedLibraryMap.Add(name, sharedLibrary);
+ return sharedLibrary;
+}
+
+IUnitTestModule* TestServer::getUnitTestModule(const String& name, DiagnosticSink* sink)
+{
+ auto unitTestModulePtr = m_unitTestModules.TryGetValue(name);
+ if (unitTestModulePtr)
+ {
+ return *unitTestModulePtr;
+ }
+
+ ISlangSharedLibrary* sharedLibrary = loadSharedLibrary(name, sink);
+ if (!sharedLibrary)
+ {
+ return nullptr;
+ }
+
+ UnownedStringSlice funcName = UnownedStringSlice::fromLiteral("slangUnitTestGetModule");
+
+ // get the unit test export name
+ UnitTestGetModuleFunc getModuleFunc = (UnitTestGetModuleFunc)sharedLibrary->findFuncByName(funcName.begin());
+ if (!getModuleFunc)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToFindFunctionInSharedLibrary, funcName);
+ }
+ return nullptr;
+ }
+
+ IUnitTestModule* testModule = getModuleFunc();
+ if (!testModule)
+ {
+ if (sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToGetUnitTestModule);
+ }
+ return nullptr;
+ }
+
+ m_unitTestModules.Add(name, testModule);
+ return testModule;
+}
+
+TestServer::InnerMainFunc TestServer::getToolFunction(const String& name, DiagnosticSink* sink)
+{
+ StringBuilder sharedLibToolBuilder;
+ sharedLibToolBuilder.append(name);
+ sharedLibToolBuilder.append("-tool");
+
+ ISlangSharedLibrary* sharedLibrary = loadSharedLibrary(sharedLibToolBuilder, sink);
+ if (!sharedLibrary)
+ {
+ return nullptr;
+ }
+
+ UnownedStringSlice funcName = UnownedStringSlice::fromLiteral("innerMain");
+
+ auto func = (InnerMainFunc)sharedLibrary->findFuncByName(funcName.begin());
+ if (!func && sink)
+ {
+ sink->diagnose(SourceLoc(), ServerDiagnostics::unableToFindFunctionInSharedLibrary, funcName);
+ }
+
+ return func;
+}
+
+SlangResult TestServer::_writeResponse(JSONContainer* container, const JSONValue& root)
+{
+ // TODO(JS): We may want a non indented style, to reduce size
+ JSONWriter writer(JSONWriter::IndentationStyle::Allman);
+ container->traverseRecursively(root, &writer);
+ const StringBuilder& builder = writer.getBuilder();
+ return m_connection->write(builder.getBuffer(), builder.getLength());
+}
+
+SlangResult TestServer::_executeSingle()
+{
+ // Block waiting for content (or error/closed)
+ SLANG_RETURN_ON_FAIL(m_connection->waitForResult());
+
+ // If we don't have content, we can quit for now
+ if (!m_connection->hasContent())
+ {
+ return SLANG_OK;
+ }
+
+ auto content = m_connection->getContent();
+
+ UnownedStringSlice slice((const char*)content.begin(), content.getCount());
+
+ // Reset for parse
+ m_sourceManager.reset();
+ m_diagnosticSink.reset();
+
+ JSONContainer container(&m_sourceManager);
+
+ // Parse as RPC JSON
+ JSONValue root;
+
+ {
+ SlangResult res = JSONRPCUtil::parseJSON(slice, &container, &m_diagnosticSink, root);
+ // Consume that content/packet
+ m_connection->consumeContent();
+
+ if (SLANG_FAILED(res))
+ {
+ return _writeResponse(&container, JSONRPCUtil::createErrorResponse(&container, JSONRPCUtil::ErrorCode::InvalidRequest, UnownedStringSlice::fromLiteral("Unable to parse JSON")));
+ }
+ }
+
+ JSONRPCUtil::Call call;
+ {
+ SlangResult res = JSONRPCUtil::parseCall(&container, root, call);
+ if (SLANG_FAILED(res))
+ {
+ return _writeResponse(&container, JSONRPCUtil::createErrorResponse(&container, Index(JSONRPCUtil::ErrorCode::InvalidRequest), UnownedStringSlice::fromLiteral("Cannot parse call")));
+ }
+ }
+
+ const auto& method = call.method;
+
+ // Do different things
+ if (method == "quit")
+ {
+ m_quit = true;
+ return SLANG_OK;
+ }
+ else if (method == "unitTest")
+ {
+ SLANG_RETURN_ON_FAIL(_executeUnitTest(&container, root));
+ return SLANG_OK;
+ }
+ else if (method == "tool")
+ {
+ SLANG_RETURN_ON_FAIL(_executeTool(&container, root));
+ return SLANG_OK;
+ }
+
+ return SLANG_FAIL;
+}
+
+static Index _findTestIndex(IUnitTestModule* testModule, const String& name)
+{
+ const auto testCount = testModule->getTestCount();
+ for (SlangInt i = 0; i < testCount; ++i)
+ {
+ auto testName = testModule->getTestName(i);
+
+ if (name == testName)
+ {
+ return Index(i);
+ }
+ }
+ return -1;
+}
+
+SlangResult TestServer::_executeUnitTest(JSONContainer* container, const JSONValue& root)
+{
+ String moduleName;
+ String testName;
+ Int enabledApis = 0;
+
+ IUnitTestModule* testModule = getUnitTestModule(moduleName, &m_diagnosticSink);
+ if (!testModule)
+ {
+ return SLANG_FAIL;
+ }
+
+ Index testIndex = _findTestIndex(testModule, moduleName);
+ if (testIndex < 0)
+ {
+ m_diagnosticSink.diagnose(SourceLoc(), ServerDiagnostics::unableToFindTest, testName);
+ return SLANG_FAIL;
+ }
+
+ TestReporter testReporter;
+
+ testModule->setTestReporter(&testReporter);
+
+ // Assume we will used the shared session
+ slang::IGlobalSession* session = getGlobalSession();
+ if (!session)
+ {
+ return SLANG_FAIL;
+ }
+
+ UnitTestContext unitTestContext;
+ unitTestContext.slangGlobalSession = session;
+ unitTestContext.workDirectory = "";
+ unitTestContext.enabledApis = RenderApiFlags(enabledApis);
+ unitTestContext.executableDirectory = m_exePath.getBuffer();
+
+ auto testCount = testModule->getTestCount();
+ SLANG_ASSERT(testIndex >= 0 && testIndex < testCount);
+
+ UnitTestFunc testFunc = testModule->getTestFunc(testIndex);
+
+ try
+ {
+ testFunc(&unitTestContext);
+ }
+ catch (...)
+ {
+ testReporter.m_failCount++;
+ }
+
+ if (testReporter.m_failCount > 0)
+ {
+ // Write out to stderr...
+ auto writers = StdWriters::createDefault();
+ writers->getError().put(testReporter.m_buf.getUnownedSlice());
+ return SLANG_FAIL;
+ }
+
+ if (testReporter.m_testCount == 0)
+ {
+ return SLANG_E_NOT_AVAILABLE;
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult TestServer::_executeTool(JSONContainer* container, const JSONValue& root)
+{
+ String toolName;
+
+ auto func = getToolFunction(toolName, &m_diagnosticSink);
+ if (!func)
+ {
+ // Write out to diagnostics
+ return SLANG_FAIL;
+ }
+
+ // Assume we will used the shared session
+ slang::IGlobalSession* session = getGlobalSession();
+ if (!session)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Get the args list
+
+ // Work out the args sent to the shared library
+ List<const char*> args;
+
+
+ RefPtr<StdWriters> stdWriters = StdWriters::createDefault();
+
+ const SlangResult res = func(stdWriters, session, int(args.getCount()), args.begin());
+ if (SLANG_FAILED(res))
+ {
+ return res;
+ }
+ return res;
+}
+
+
+SlangResult TestServer::execute()
+{
+ DiagnosticSink diagnosticSink;
+
+ while (m_connection->isActive() && !m_quit)
+ {
+ SlangResult res = _executeSingle();
+ if (m_quit)
+ {
+ break;
+ }
+
+ if (SLANG_FAILED(res))
+ {
+ // Return a result
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TestReporter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+void TestReporter::message(TestMessageType type, const char* message)
+{
+ if (type == TestMessageType::RunError ||
+ type == TestMessageType::TestFailure)
+ {
+ m_failCount++;
+ }
+
+ m_buf << message << "\n";
+}
+
+void TestReporter::addResultWithLocation(TestResult result, const char* testText, const char* file, int line)
+{
+ if (result == TestResult::Fail)
+ {
+ addResultWithLocation(false, testText, file, line);
+ }
+ else
+ {
+ m_testCount++;
+ }
+}
+
+void TestReporter::addResultWithLocation(bool testSucceeded, const char* testText, const char* file, int line)
+{
+ m_testCount++;
+
+ if (testSucceeded)
+ {
+ return;
+ }
+
+ m_buf << "[Failed]: " << testText << "\n";
+ m_buf << file << ":" << line << "\n";
+
+ m_failCount++;
+}
+
+void TestReporter::addResult(TestResult result)
+{
+ if (result == TestResult::Fail)
+ {
+ m_failCount++;
+ }
+}
+
+
+SlangResult _execute(int argc, const char* const* argv)
+{
+ TestServer server;
+ SLANG_RETURN_ON_FAIL(server.init(argc, argv));
+ SLANG_RETURN_ON_FAIL(server.execute());
+
+ return SLANG_OK;
+}
+
+} // namespace TestServer
+
+int main(int argc, const char* const* argv)
+{
+ return (int)Slang::TestToolUtil::getReturnCode(TestServer:: _execute(argc, argv));
+}