yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Jay KwakUse disassemble API from SPIRV-Tools (#6001)5621ace93

master
10.3 KiB384 linesraw
1// slang-json-rpc-connection.cpp
2#include "slang-json-rpc-connection.h"
3
4#include "../core/slang-process-util.h"
5#include "../core/slang-short-list.h"
6#include "../core/slang-string-util.h"
7#include "slang-json-native.h"
8#include "slang-json-rpc.h"
9
10namespace Slang
11{
12
13/// Ctor
14JSONRPCConnection::JSONRPCConnection()
15    : m_container(nullptr), m_typeMap(JSONNativeUtil::getTypeFuncsMap())
16{
17}
18
19SlangResult JSONRPCConnection::init(
20    HTTPPacketConnection* connection,
21    CallStyle defaultCallStyle,
22    Process* process)
23{
24    m_connection = connection;
25    m_process = process;
26
27    {
28        // If a call style isn't set, use the prefered style
29        const CallStyle preferedCallStyle = CallStyle::Array;
30        defaultCallStyle =
31            (defaultCallStyle == CallStyle::Default) ? preferedCallStyle : defaultCallStyle;
32        m_defaultCallStyle = defaultCallStyle;
33    }
34
35
36    m_sourceManager.initialize(nullptr, nullptr);
37    m_diagnosticSink.init(&m_sourceManager, &JSONLexer::calcLexemeLocation);
38    m_container.setSourceManager(&m_sourceManager);
39
40    return SLANG_OK;
41}
42
43SlangResult JSONRPCConnection::initWithStdStreams(CallStyle defaultCallStyle, Process* process)
44{
45    RefPtr<Stream> stdinStream, stdoutStream;
46
47    Process::getStdStream(StdStreamType::In, stdinStream);
48    Process::getStdStream(StdStreamType::Out, stdoutStream);
49
50    RefPtr<BufferedReadStream> readStream(new BufferedReadStream(stdinStream));
51
52    RefPtr<HTTPPacketConnection> connection = new HTTPPacketConnection(readStream, stdoutStream);
53    return init(connection, defaultCallStyle, process);
54}
55
56void JSONRPCConnection::clearBuffers()
57{
58    m_sourceManager.reset();
59    m_diagnosticSink.reset();
60    m_container.reset();
61    m_jsonRoot.reset();
62}
63
64bool JSONRPCConnection::isActive()
65{
66    return m_connection->isActive() && (m_process == nullptr || !m_process->isTerminated());
67}
68
69JSONValue JSONRPCConnection::getCurrentMessageId()
70{
71    SLANG_ASSERT(hasMessage());
72    return JSONRPCUtil::getId(&m_container, m_jsonRoot);
73}
74
75void JSONRPCConnection::disconnect()
76{
77    if (m_process)
78    {
79        if (!m_process->isTerminated())
80        {
81            if (m_connection)
82            {
83                // Send. If succeeded, wait
84                if (SLANG_SUCCEEDED(sendCall(UnownedStringSlice::fromLiteral("quit"))))
85                {
86                    // Wait for termination
87                    m_process->waitForTermination(m_terminationTimeOutInMs);
88                }
89            }
90
91            if (!m_process->isTerminated())
92            {
93                // Okay, just try terminating
94                m_process->waitForTermination(m_terminationTimeOutInMs);
95            }
96
97            // Okay just kill it then
98            if (!m_process->isTerminated())
99            {
100                m_process->kill(-1);
101            }
102        }
103        m_process.setNull();
104    }
105
106    m_connection.setNull();
107}
108
109SlangResult JSONRPCConnection::sendRPC(const RttiInfo* rttiInfo, const void* data)
110{
111    auto typeMap = JSONNativeUtil::getTypeFuncsMap();
112
113    // Convert to JSON
114    NativeToJSONConverter converter(&m_container, &typeMap, &m_diagnosticSink);
115    JSONValue value;
116
117    SLANG_RETURN_ON_FAIL(converter.convert(rttiInfo, data, value));
118
119    // Convert to text
120    JSONWriter writer(JSONWriter::IndentationStyle::Allman);
121
122    m_container.traverseRecursively(value, &writer);
123    const StringBuilder& builder = writer.getBuilder();
124    return m_connection->write(builder.getBuffer(), builder.getLength());
125}
126
127SlangResult JSONRPCConnection::sendError(JSONRPC::ErrorCode code, const JSONValue& id)
128{
129    return sendError(code, m_diagnosticSink.outputBuffer.getUnownedSlice(), id);
130}
131
132SlangResult JSONRPCConnection::sendError(
133    JSONRPC::ErrorCode errorCode,
134    const UnownedStringSlice& msg,
135    const JSONValue& id)
136{
137    JSONRPCErrorResponse errorResponse;
138    errorResponse.error.code = Int(errorCode);
139    errorResponse.error.message = msg;
140    errorResponse.id = id;
141
142    return sendRPC(&errorResponse);
143}
144
145SlangResult JSONRPCConnection::checkArrayObjectWrap(
146    const JSONValue& srcArgs,
147    const RttiInfo* dstArgsRttiInfo,
148    void* dstArgs,
149    const JSONValue& id)
150{
151    if (dstArgsRttiInfo->m_kind == RttiInfo::Kind::Struct &&
152        srcArgs.getKind() == JSONValue::Kind::Array)
153    {
154        auto array = m_container.getArray(srcArgs);
155        if (array.getCount() == 1)
156        {
157            return toNativeOrSendError(array[0], dstArgsRttiInfo, dstArgs, id);
158        }
159        return SLANG_OK;
160    }
161    else
162    {
163        return toNativeOrSendError(srcArgs, dstArgsRttiInfo, dstArgs, id);
164    }
165}
166
167SlangResult JSONRPCConnection::toNativeArgsOrSendError(
168    const JSONValue& srcArgs,
169    const RttiInfo* dstArgsRttiInfo,
170    void* dstArgs,
171    const JSONValue& id)
172{
173    if (dstArgsRttiInfo->m_kind == RttiInfo::Kind::Struct &&
174        srcArgs.getKind() == JSONValue::Kind::Array)
175    {
176        JSONToNativeConverter converter(&m_container, &m_typeMap, &m_diagnosticSink);
177        if (SLANG_FAILED(converter.convertArrayToStruct(srcArgs, dstArgsRttiInfo, dstArgs)))
178        {
179            return sendError(JSONRPC::ErrorCode::InvalidRequest, id);
180        }
181        return SLANG_OK;
182    }
183    else
184    {
185        return toNativeOrSendError(srcArgs, dstArgsRttiInfo, dstArgs, id);
186    }
187}
188
189SlangResult JSONRPCConnection::toNativeOrSendError(
190    const JSONValue& value,
191    const RttiInfo* info,
192    void* dst,
193    const JSONValue& id)
194{
195    m_diagnosticSink.outputBuffer.clear();
196
197    JSONToNativeConverter converter(&m_container, &m_typeMap, &m_diagnosticSink);
198
199    if (SLANG_FAILED(converter.convert(value, info, dst)))
200    {
201        return sendError(JSONRPC::ErrorCode::InvalidRequest, id);
202    }
203
204    return SLANG_OK;
205}
206
207SlangResult JSONRPCConnection::sendCall(const UnownedStringSlice& method, const JSONValue& id)
208{
209    JSONRPCCall call;
210    call.id = id;
211    call.method = method;
212
213    SLANG_RETURN_ON_FAIL(sendRPC(&call));
214    return SLANG_OK;
215}
216
217SlangResult JSONRPCConnection::sendResult(
218    const RttiInfo* rttiInfo,
219    const void* result,
220    const JSONValue& id)
221{
222    JSONResultResponse response;
223    response.id = id;
224
225    NativeToJSONConverter converter(&m_container, &m_typeMap, &m_diagnosticSink);
226    SLANG_RETURN_ON_FAIL(converter.convert(rttiInfo, result, response.result));
227
228    // Send the RPC
229    SLANG_RETURN_ON_FAIL(sendRPC(&response));
230    return SLANG_OK;
231}
232
233SlangResult JSONRPCConnection::sendCall(
234    const UnownedStringSlice& method,
235    const RttiInfo* argsRttiInfo,
236    const void* args,
237    const JSONValue& id)
238{
239    return sendCall(m_defaultCallStyle, method, argsRttiInfo, args, id);
240}
241
242SlangResult JSONRPCConnection::sendCall(
243    CallStyle callStyle,
244    const UnownedStringSlice& method,
245    const RttiInfo* argsRttiInfo,
246    const void* args,
247    const JSONValue& id)
248{
249    JSONRPCCall call;
250    call.id = id;
251    call.method = method;
252
253    // Set up the converter to now convert the args.
254    NativeToJSONConverter converter(&m_container, &m_typeMap, &m_diagnosticSink);
255
256    // If we have a struct *and* call style is 'array', do special handling
257    if (argsRttiInfo->m_kind == RttiInfo::Kind::Struct &&
258        _getCallStyle(callStyle) == CallStyle::Array)
259    {
260        // Convert the args/params in the 'array' style
261        SLANG_RETURN_ON_FAIL(converter.convertStructToArray(argsRttiInfo, args, call.params));
262    }
263    else
264    {
265        // Convert the args/params in the 'object' sytle
266        SLANG_RETURN_ON_FAIL(converter.convert(argsRttiInfo, args, call.params));
267    }
268
269    // Send the RPC
270    SLANG_RETURN_ON_FAIL(sendRPC(&call));
271    return SLANG_OK;
272}
273
274SlangResult JSONRPCConnection::waitForResult(Int timeOutInMs)
275{
276    // Invalidate m_jsonRoot before waitForResult, because when waitForResult fail,
277    // we don't want to use the result from the previous read.
278    m_jsonRoot.reset();
279
280    SLANG_RETURN_ON_FAIL(m_connection->waitForResult(timeOutInMs));
281    return tryReadMessage();
282}
283
284SlangResult JSONRPCConnection::tryReadMessage()
285{
286    m_jsonRoot.reset();
287
288    SLANG_RETURN_ON_FAIL(m_connection->update());
289    if (!m_connection->hasContent())
290    {
291        return SLANG_OK;
292    }
293
294    auto content = m_connection->getContent();
295    UnownedStringSlice slice((const char*)content.begin(), content.getCount());
296
297    clearBuffers();
298
299    {
300        const SlangResult res =
301            JSONRPCUtil::parseJSON(slice, &m_container, &m_diagnosticSink, m_jsonRoot);
302
303        // Consume that content/packet
304        m_connection->consumeContent();
305        if (SLANG_FAILED(res))
306        {
307            // if we can't parse JSON, we return with id of 'null' as per the standard
308            return sendError(JSONRPC::ErrorCode::ParseError, JSONValue::makeNull());
309        }
310    }
311
312    return SLANG_OK;
313}
314
315JSONRPCMessageType JSONRPCConnection::getMessageType()
316{
317    return JSONRPCUtil::getMessageType(&m_container, m_jsonRoot);
318}
319
320SlangResult JSONRPCConnection::getMessage(const RttiInfo* rttiInfo, void* out)
321{
322    if (!hasMessage())
323    {
324        return SLANG_FAIL;
325    }
326
327    m_diagnosticSink.outputBuffer.clear();
328    JSONToNativeConverter converter(&m_container, &m_typeMap, &m_diagnosticSink);
329
330    // Get the RPC response
331    JSONResultResponse resultResponse;
332    SLANG_RETURN_ON_FAIL(converter.convert(m_jsonRoot, &resultResponse));
333
334    // Convert the result in the response
335    SLANG_RETURN_ON_FAIL(converter.convert(resultResponse.result, rttiInfo, out));
336    return SLANG_OK;
337}
338
339SlangResult JSONRPCConnection::getMessageOrSendError(const RttiInfo* rttiInfo, void* out)
340{
341    if (!hasMessage())
342    {
343        return SLANG_FAIL;
344    }
345
346    const auto res = getMessage(rttiInfo, out);
347    if (SLANG_FAILED(res))
348    {
349        return sendError(JSONRPC::ErrorCode::ParseError, getCurrentMessageId());
350    }
351    return res;
352}
353
354SlangResult JSONRPCConnection::getRPC(const RttiInfo* rttiInfo, void* out)
355{
356    if (!hasMessage())
357    {
358        return SLANG_FAIL;
359    }
360
361    m_diagnosticSink.outputBuffer.clear();
362    JSONToNativeConverter converter(&m_container, &m_typeMap, &m_diagnosticSink);
363
364    // Convert the result in the response
365    SLANG_RETURN_ON_FAIL(converter.convert(m_jsonRoot, rttiInfo, out));
366    return SLANG_OK;
367}
368
369SlangResult JSONRPCConnection::getRPCOrSendError(const RttiInfo* rttiInfo, void* out)
370{
371    if (!hasMessage())
372    {
373        return SLANG_FAIL;
374    }
375
376    const auto res = getRPC(rttiInfo, out);
377    if (SLANG_FAILED(res))
378    {
379        return sendError(JSONRPC::ErrorCode::ParseError, getCurrentMessageId());
380    }
381    return res;
382}
383
384} // namespace Slang