yum-mirror/slang

Making it easier to work with shaders

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

Yong HeLanguageServer: Enhance auto completion for override. (#7465)4d517794e

master
18.8 KiB611 linesraw
1#include "slang-json-native.h"
2
3#include "../core/slang-rtti-util.h"
4#include "slang-com-helper.h"
5#include "slang-json-diagnostics.h"
6
7namespace Slang
8{
9
10/* static */ RttiTypeFuncsMap JSONNativeUtil::getTypeFuncsMap()
11{
12    RttiTypeFuncsMap typeMap;
13    typeMap.add(GetRttiInfo<JSONValue>::get(), GetRttiTypeFuncsForZeroPod<JSONValue>::getFuncs());
14    return typeMap;
15}
16
17/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! JSONToNativeConverter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
18
19/* static */ Index JSONToNativeConverter::_getFieldCount(const StructRttiInfo* structRttiInfo)
20{
21    if (structRttiInfo->m_super)
22    {
23        return _getFieldCount(structRttiInfo->m_super) + structRttiInfo->m_fieldCount;
24    }
25    else
26    {
27        return structRttiInfo->m_fieldCount;
28    }
29}
30
31/* static */ Index JSONToNativeConverter::_findFieldIndex(
32    const StructRttiInfo* structRttiInfo,
33    const UnownedStringSlice& fieldName)
34{
35    if (structRttiInfo->m_super)
36    {
37        const Index index = _findFieldIndex(structRttiInfo->m_super, fieldName);
38        if (index >= 0)
39        {
40            return index + _getFieldCount(structRttiInfo->m_super);
41        }
42    }
43
44    ConstArrayView<StructRttiInfo::Field> fields(
45        structRttiInfo->m_fields,
46        structRttiInfo->m_fieldCount);
47
48    Index index = fields.findFirstIndex(
49        [fieldName](const StructRttiInfo::Field& field) -> bool
50        { return fieldName == field.m_name; });
51    if (index >= 0 && structRttiInfo->m_super)
52    {
53        index += _getFieldCount(structRttiInfo->m_super);
54    }
55
56    return index;
57}
58
59SlangResult JSONToNativeConverter::_structToNative(
60    const ConstArrayView<JSONKeyValue>& pairs,
61    const StructRttiInfo* structRttiInfo,
62    void* out,
63    Index& outFieldCount)
64{
65    Index fieldCount = 0;
66
67    if (structRttiInfo->m_super)
68    {
69        SLANG_RETURN_ON_FAIL(_structToNative(pairs, structRttiInfo->m_super, out, fieldCount));
70    }
71
72    Byte* dst = (Byte*)out;
73
74    const Index count = structRttiInfo->m_fieldCount;
75
76    for (Index i = 0; i < count; ++i)
77    {
78        const auto& field = structRttiInfo->m_fields[i];
79
80        auto key = m_container->findKey(UnownedStringSlice(field.m_name));
81
82        if (key == 0)
83        {
84            if (field.m_flags & StructRttiInfo::Flag::Optional)
85            {
86                continue;
87            }
88
89            m_sink->diagnose(
90                SourceLoc(),
91                JSONDiagnostics::fieldRequiredOnType,
92                field.m_name,
93                structRttiInfo->m_name);
94
95            // Unable to find this key
96            return SLANG_FAIL;
97        }
98
99        // If there are any of the pairs, that are not in the type.. it's an error
100        const Index index = pairs.findFirstIndex(
101            [key](const JSONKeyValue& pair) -> bool { return pair.key == key; });
102        if (index < 0)
103        {
104            if (field.m_flags & StructRttiInfo::Flag::Optional)
105            {
106                continue;
107            }
108
109            m_sink->diagnose(
110                SourceLoc(),
111                JSONDiagnostics::fieldRequiredOnType,
112                field.m_name,
113                structRttiInfo->m_name);
114
115            // Unable to find this key
116            return SLANG_FAIL;
117        }
118
119        auto& pair = pairs[index];
120
121        // Copy the field over
122        SLANG_RETURN_ON_FAIL(convert(pair.value, field.m_type, dst + field.m_offset));
123
124        // Field was handled
125        ++fieldCount;
126    }
127
128    // Write off the amount of fields converted/handled.
129    outFieldCount = fieldCount;
130    return SLANG_OK;
131}
132
133SlangResult JSONToNativeConverter::convert(const JSONValue& in, const RttiInfo* rttiInfo, void* out)
134{
135    if (rttiInfo->isIntegral())
136    {
137        return RttiUtil::setInt(m_container->asInteger(in), rttiInfo, out);
138    }
139    else if (rttiInfo->isFloat())
140    {
141        return RttiUtil::setFromDouble(m_container->asFloat(in), rttiInfo, out);
142    }
143
144    switch (rttiInfo->m_kind)
145    {
146    case RttiInfo::Kind::Bool:
147        {
148            *(bool*)out = m_container->asBool(in);
149            return SLANG_OK;
150        }
151    case RttiInfo::Kind::Struct:
152        {
153            if (in.getKind() != JSONValue::Kind::Object)
154            {
155                return SLANG_FAIL;
156            }
157
158            auto pairs = m_container->getObject(in);
159            const StructRttiInfo* structRttiInfo = static_cast<const StructRttiInfo*>(rttiInfo);
160
161            Index fieldCount = 0;
162            SLANG_RETURN_ON_FAIL(_structToNative(pairs, structRttiInfo, out, fieldCount));
163
164            if (fieldCount != pairs.getCount() && !structRttiInfo->m_ignoreUnknownFieldsInJson)
165            {
166                // We want to find the fields not found in the type
167
168                for (auto& pair : pairs)
169                {
170                    UnownedStringSlice fieldName = m_container->getStringFromKey(pair.key);
171                    const Index index =
172                        _findFieldIndex(structRttiInfo, UnownedStringSlice(fieldName));
173
174                    if (index < 0)
175                    {
176                        m_sink->diagnose(
177                            pair.keyLoc,
178                            JSONDiagnostics::fieldNotDefinedOnType,
179                            fieldName,
180                            structRttiInfo->m_name);
181                    }
182                }
183
184                // If these are different then there are fields defined in the object that are *not*
185                // defined in class definition
186                return SLANG_FAIL;
187            }
188
189            return SLANG_OK;
190        }
191    case RttiInfo::Kind::Enum:
192        {
193            return SLANG_E_NOT_IMPLEMENTED;
194        }
195    case RttiInfo::Kind::String:
196        {
197            *(String*)out = m_container->getTransientString(in);
198            return SLANG_OK;
199        }
200    case RttiInfo::Kind::UnownedStringSlice:
201        {
202            // Problem -> if the slice is a lexeme, then when we decode with getString, it will lose
203            // scope. So we do something a bit odd and place the decoding string
204
205            *(UnownedStringSlice*)out = m_container->getString(in);
206            return SLANG_OK;
207        }
208    case RttiInfo::Kind::Optional:
209        {
210            if (in.getKind() == JSONValue::Kind::Null)
211            {
212                return SLANG_OK;
213            }
214            typedef List<Byte> Type;
215            const OptionalRttiInfo* optionalRttiInfo =
216                static_cast<const OptionalRttiInfo*>(rttiInfo);
217            auto hasValue = (uint8_t*)out;
218            *hasValue = 1;
219            return convert(
220                in,
221                optionalRttiInfo->m_elementType,
222                (uint8_t*)out + optionalRttiInfo->m_valueOffset);
223        }
224    case RttiInfo::Kind::List:
225        {
226            if (in.getKind() == JSONValue::Kind::Null)
227                return SLANG_OK;
228            if (in.getKind() != JSONValue::Kind::Array)
229            {
230                return SLANG_FAIL;
231            }
232
233            typedef List<Byte> Type;
234            Type& list = *(Type*)out;
235
236            auto arr = m_container->getArray(in);
237
238            const Index count = arr.getCount();
239
240            const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
241            auto elementType = listRttiInfo->m_elementType;
242
243            SLANG_RETURN_ON_FAIL(
244                RttiUtil::setListCount(m_typeMap, elementType, out, arr.getCount()));
245
246            // Okay, we need to copy over one by one
247            Byte* dstEles = list.getBuffer();
248            for (Index i = 0; i < count; ++i, dstEles += elementType->m_size)
249            {
250                SLANG_RETURN_ON_FAIL(convert(arr[i], elementType, dstEles));
251            }
252
253            return SLANG_OK;
254        }
255    case RttiInfo::Kind::FixedArray:
256        {
257            if (in.getKind() != JSONValue::Kind::Array)
258            {
259                return SLANG_FAIL;
260            }
261            const FixedArrayRttiInfo* fixedArrayRttiInfo =
262                static_cast<const FixedArrayRttiInfo*>(rttiInfo);
263            const auto elementType = fixedArrayRttiInfo->m_elementType;
264            const Index elementCount = Index(fixedArrayRttiInfo->m_elementCount);
265            const auto elementSize = elementType->m_size;
266
267            auto srcArray = m_container->getArray(in);
268
269            if (srcArray.getCount() > elementCount)
270            {
271                m_sink->diagnose(
272                    in.loc,
273                    JSONDiagnostics::tooManyElementsForArray,
274                    srcArray.getCount(),
275                    elementCount);
276                return SLANG_FAIL;
277            }
278
279            Byte* dstEles = (Byte*)out;
280            for (Index i = 0; i < elementCount; ++i, dstEles += elementSize)
281            {
282                SLANG_RETURN_ON_FAIL(convert(srcArray[i], elementType, dstEles));
283            }
284
285            return SLANG_OK;
286        }
287    case RttiInfo::Kind::Dictionary:
288        {
289            // We can *only* serialize this into a straight JSON object iff the key is a string-like
290            // type We could turn into (say) an array of keys and values
291            break;
292        }
293    case RttiInfo::Kind::Other:
294        {
295            if (rttiInfo == GetRttiInfo<JSONValue>::get())
296            {
297                // Do we need to copy into the container?
298                // As it stands we have to assume src is stored in container.
299                *(JSONValue*)out = in;
300                return SLANG_OK;
301            }
302            return SLANG_FAIL;
303        }
304    default:
305        break;
306    }
307    return SLANG_FAIL;
308}
309
310SlangResult JSONToNativeConverter::convertArrayToStruct(
311    const JSONValue& value,
312    const RttiInfo* rttiInfo,
313    void* out)
314{
315    // Check converting JSON array into a struct, as that's what this method supports
316    if (!(rttiInfo->m_kind == RttiInfo::Kind::Struct && value.getKind() == JSONValue::Kind::Array))
317    {
318        // If they are the wrong types then just fail
319        return SLANG_FAIL;
320    }
321
322    // Find the total amount of fields, and all the classes involved
323    Index totalFieldCount = 0;
324
325    ShortList<const StructRttiInfo*, 8> infos;
326    for (const StructRttiInfo* cur = static_cast<const StructRttiInfo*>(rttiInfo); cur;
327         cur = cur->m_super)
328    {
329        totalFieldCount += cur->m_fieldCount;
330        infos.add(cur);
331    }
332
333    // Must have the same amount of fields
334    auto array = m_container->getArray(value);
335    if (array.getCount() != totalFieldCount)
336    {
337        return SLANG_FAIL;
338    }
339
340    Byte* dstBase = (Byte*)out;
341
342    // We work in the order from the base class to the final type
343    Index argIndex = 0;
344    for (Index i = infos.getCount() - 1; i >= 0; --i)
345    {
346        auto info = infos[i];
347
348        const Index fieldCount = info->m_fieldCount;
349        for (Index j = 0; j < fieldCount; ++j)
350        {
351            // Convert the field
352            const auto& field = info->m_fields[j];
353            SLANG_RETURN_ON_FAIL(
354                convert(array[argIndex++], field.m_type, dstBase + field.m_offset));
355        }
356    }
357
358    return SLANG_OK;
359}
360
361/* !!!!!!!!!!!!!!!!!!!!!!!!!!!! NativeToJSONConverter !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
362
363SlangResult NativeToJSONConverter::_structToJSON(
364    const StructRttiInfo* structRttiInfo,
365    const void* src,
366    List<JSONKeyValue>& outPairs)
367{
368    // Do the super class first
369    if (structRttiInfo->m_super)
370    {
371        SLANG_RETURN_ON_FAIL(_structToJSON(structRttiInfo->m_super, src, outPairs));
372    }
373
374    const Byte* base = (const Byte*)src;
375    const Index count = structRttiInfo->m_fieldCount;
376
377    for (Index i = 0; i < count; ++i)
378    {
379        const auto& field = structRttiInfo->m_fields[i];
380
381        if (field.m_flags & StructRttiInfo::Flag::Optional)
382        {
383            const RttiDefaultValue defaultValue =
384                RttiDefaultValue(field.m_flags & uint8_t(RttiDefaultValue::Mask));
385            if (RttiUtil::isDefault(defaultValue, field.m_type, base + field.m_offset))
386            {
387                // If it's a default, we don't bother writing it
388                continue;
389            }
390        }
391
392        JSONKeyValue pair;
393        pair.key = m_container->getKey(UnownedStringSlice(field.m_name));
394        auto res = convert(field.m_type, base + field.m_offset, pair.value);
395
396        if (SLANG_FAILED(res))
397        {
398            m_sink->diagnose(
399                SourceLoc(),
400                JSONDiagnostics::unableToConvertField,
401                field.m_name,
402                structRttiInfo->m_name);
403            return res;
404        }
405
406        outPairs.add(pair);
407    }
408
409    return SLANG_OK;
410}
411
412
413SlangResult NativeToJSONConverter::convert(const RttiInfo* rttiInfo, const void* in, JSONValue& out)
414{
415    if (rttiInfo->isIntegral())
416    {
417        out = JSONValue::makeInt(RttiUtil::getInt64(rttiInfo, in));
418        return SLANG_OK;
419    }
420    else if (rttiInfo->isFloat())
421    {
422        out = JSONValue::makeFloat(RttiUtil::asDouble(rttiInfo, in));
423        return SLANG_OK;
424    }
425
426    switch (rttiInfo->m_kind)
427    {
428    case RttiInfo::Kind::Invalid:
429        return SLANG_FAIL;
430    case RttiInfo::Kind::Bool:
431        {
432            out = JSONValue::makeBool(RttiUtil::asBool(rttiInfo, in));
433            return SLANG_OK;
434        }
435    case RttiInfo::Kind::String:
436        {
437            const String& str = *(const String*)in;
438            out = m_container->createString(str.getUnownedSlice());
439            return SLANG_OK;
440        }
441    case RttiInfo::Kind::UnownedStringSlice:
442        {
443            const UnownedStringSlice& slice = *(const UnownedStringSlice*)in;
444            out = m_container->createString(slice);
445            return SLANG_OK;
446        }
447    case RttiInfo::Kind::Struct:
448        {
449            const StructRttiInfo* structRttiInfo = static_cast<const StructRttiInfo*>(rttiInfo);
450
451            List<JSONKeyValue> pairs;
452            SLANG_RETURN_ON_FAIL(_structToJSON(structRttiInfo, in, pairs));
453            out = m_container->createObject(pairs.getBuffer(), pairs.getCount());
454            return SLANG_OK;
455        }
456    case RttiInfo::Kind::Enum:
457        {
458            return SLANG_E_NOT_IMPLEMENTED;
459        }
460    case RttiInfo::Kind::Optional:
461        {
462            const OptionalRttiInfo* optionalRttiInfo =
463                static_cast<const OptionalRttiInfo*>(rttiInfo);
464            auto hasValue = (const uint8_t*)in;
465            if (*hasValue)
466            {
467                return convert(
468                    optionalRttiInfo->m_elementType,
469                    (const uint8_t*)in + optionalRttiInfo->m_valueOffset,
470                    out);
471            }
472            else
473            {
474                out = JSONValue::makeNull();
475                return SLANG_OK;
476            }
477        }
478    case RttiInfo::Kind::List:
479        {
480            const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
481            const auto elementRttiInfo = listRttiInfo->m_elementType;
482
483            // The src probably *doesn't* contain bytes, but can cast like this because
484            // we only need the count (which doesn't depend on <T>), and the backing buffer
485            const List<Byte>& srcValuesList = *(const List<Byte>*)in;
486
487            const Index count = srcValuesList.getCount();
488            const Byte* srcValues = srcValuesList.getBuffer();
489
490            List<JSONValue> dstValues;
491            dstValues.setCount(count);
492
493            const size_t elementStride = elementRttiInfo->m_size;
494
495            for (Index i = 0; i < count; ++i, srcValues += elementStride)
496            {
497                SLANG_RETURN_ON_FAIL(convert(elementRttiInfo, srcValues, dstValues[i]));
498            }
499
500            out = m_container->createArray(dstValues.getBuffer(), count);
501            return SLANG_OK;
502        }
503    case RttiInfo::Kind::FixedArray:
504        {
505            const FixedArrayRttiInfo* fixedArrayRttiInfo =
506                static_cast<const FixedArrayRttiInfo*>(rttiInfo);
507            const auto elementType = fixedArrayRttiInfo->m_elementType;
508            const auto elementCount = Index(fixedArrayRttiInfo->m_elementCount);
509            const auto elementSize = elementType->m_size;
510
511            List<JSONValue> dstValues;
512            dstValues.setCount(elementCount);
513
514            const Byte* src = (const Byte*)in;
515            for (Index i = 0; i < elementCount; ++i, src += elementSize)
516            {
517                SLANG_RETURN_ON_FAIL(convert(elementType, src, dstValues[i]));
518            }
519
520            out = m_container->createArray(dstValues.getBuffer(), elementCount);
521            return SLANG_OK;
522        }
523    case RttiInfo::Kind::Dictionary:
524        {
525            const DictionaryRttiInfo* listRttiInfo =
526                static_cast<const DictionaryRttiInfo*>(rttiInfo);
527            const auto keyRttiInfo = listRttiInfo->m_keyType;
528            const auto valueRttiInfo = listRttiInfo->m_valueType;
529
530            SLANG_UNUSED(keyRttiInfo);
531            SLANG_UNUSED(valueRttiInfo);
532
533            // We can *only* serialize this into a straight JSON object iff the key is a string-like
534            // type We could turn into (say) an array of keys and values
535
536            break;
537        }
538    case RttiInfo::Kind::Other:
539        {
540            if (rttiInfo == GetRttiInfo<JSONValue>::get())
541            {
542                // Do we need to copy into the container?
543                // As it stands we have to assume src is stored in container.
544                const JSONValue& src = *(const JSONValue*)in;
545
546                out = src;
547                return SLANG_OK;
548            }
549            break;
550        }
551    default:
552        break;
553    }
554
555    return SLANG_E_NOT_IMPLEMENTED;
556}
557
558SlangResult NativeToJSONConverter::convertStructToArray(
559    const RttiInfo* rttiInfo,
560    const void* in,
561    JSONValue& out)
562{
563    if (rttiInfo->m_kind != RttiInfo::Kind::Struct)
564    {
565        // Must be a struct
566        return SLANG_FAIL;
567    }
568
569    // Work out the total amount of fields, and all invloved struct types
570    Index totalFieldsCount = 0;
571    ShortList<const StructRttiInfo*, 8> infos;
572    for (const StructRttiInfo* cur = static_cast<const StructRttiInfo*>(rttiInfo); cur;
573         cur = cur->m_super)
574    {
575        totalFieldsCount += Index(cur->m_fieldCount);
576        infos.add(cur);
577    }
578
579    // Convert the args/params
580    List<JSONValue> argsArray;
581    argsArray.setCount(totalFieldsCount);
582
583    // NOTE! We do no special handling here around optional parameters.
584    // All fields of the input args are output
585    {
586        Index argsArrayIndex = 0;
587        const Byte* argsBase = (const Byte*)in;
588
589        // Work in the order from the base class to the actual type
590        for (Index i = infos.getCount() - 1; i >= 0; --i)
591        {
592            auto structRttiInfo = infos[i];
593            const Index fieldCount = Index(structRttiInfo->m_fieldCount);
594
595            for (Index j = 0; j < fieldCount; ++j)
596            {
597                const auto& field = structRttiInfo->m_fields[j];
598                // Convert the field
599                SLANG_RETURN_ON_FAIL(
600                    convert(field.m_type, argsBase + field.m_offset, argsArray[argsArrayIndex++]));
601            }
602        }
603    }
604
605    // Okay now we convert the List to the output, which will just be a JSON array.
606    SLANG_RETURN_ON_FAIL(convert(&argsArray, out));
607
608    return SLANG_OK;
609}
610
611} // namespace Slang