• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brs / #305

18 Jan 2024 09:05PM UTC coverage: 91.463% (+5.2%) from 86.214%
#305

push

TwitchBronBron
0.45.4

1796 of 2095 branches covered (85.73%)

Branch coverage included in aggregate %.

5275 of 5636 relevant lines covered (93.59%)

8947.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.95
/src/stdlib/Json.ts
1
import { RoAssociativeArray } from "../brsTypes/components/RoAssociativeArray";
131✔
2
import { RoArray } from "../brsTypes/components/RoArray";
131✔
3
import { Interpreter } from "../interpreter";
4

5
import {
131✔
6
    BrsBoolean,
7
    BrsInvalid,
8
    BrsString,
9
    BrsType,
10
    Callable,
11
    Float,
12
    Int32,
13
    Int64,
14
    ValueKind,
15
    StdlibArgument,
16
} from "../brsTypes";
17
import { isUnboxable } from "../brsTypes/Boxing";
131✔
18

19
/**
20
 * Converts a value to its representation as a BrsType. If no such
21
 * representation is possible, throws an Error.
22
 * @param {any} x Some value.
23
 * @return {BrsType} The BrsType representaion of `x`.
24
 * @throws {Error} If `x` cannot be represented as a BrsType.
25
 */
26
function brsValueOf(x: any): BrsType {
27
    if (x === null) {
38✔
28
        return BrsInvalid.Instance;
5✔
29
    }
30
    let t: string = typeof x;
33✔
31
    switch (t) {
33!
32
        case "boolean":
33
            return BrsBoolean.from(x);
5✔
34
        case "string":
35
            return new BrsString(x);
6✔
36
        case "number":
37
            if (Number.isInteger(x)) {
16✔
38
                return x >= -2_147_483_648 && x <= 2_147_483_647 ? new Int32(x) : new Int64(x);
11✔
39
            }
40
            return new Float(x);
5✔
41
        case "object":
42
            if (Array.isArray(x)) {
6✔
43
                return new RoArray(x.map(brsValueOf));
2✔
44
            }
45
            return new RoAssociativeArray(
4✔
46
                Object.getOwnPropertyNames(x).map((k: string) => ({
20✔
47
                    name: new BrsString(k),
48
                    value: brsValueOf(x[k]),
49
                }))
50
            );
51
        default:
52
            throw new Error(`brsValueOf not implemented for: ${x} <${t}>`);
×
53
    }
54
}
55

56
type BrsAggregate = RoAssociativeArray | RoArray;
57

58
function visit(x: BrsAggregate, visited: Set<BrsAggregate>): void {
59
    if (visited.has(x)) {
10✔
60
        throw new Error("Nested object reference");
3✔
61
    }
62
    visited.add(x);
7✔
63
}
64

65
/**
66
 * Converts a BrsType value to its representation as a JSON string. If no such
67
 * representation is possible, throws an Error. Objects with cyclical references
68
 * are rejected.
69
 * @param {Interpreter} interpreter An Interpreter.
70
 * @param {BrsType} x Some BrsType value.
71
 * @param {Set<BrsAggregate>} visited An optional Set of visited of RoArray or
72
 *   RoAssociativeArray. If not provided, a new Set will be created.
73
 * @return {string} The JSON string representation of `x`.
74
 * @throws {Error} If `x` cannot be represented as a JSON string.
75
 */
76
function jsonOf(
77
    interpreter: Interpreter,
78
    x: BrsType,
79
    visited: Set<BrsAggregate> = new Set()
15✔
80
): string {
81
    switch (x.kind) {
56!
82
        case ValueKind.Invalid:
83
            return "null";
5✔
84
        case ValueKind.String:
85
            return `"${x.toString()}"`;
8✔
86
        case ValueKind.Boolean:
87
        case ValueKind.Double:
88
        case ValueKind.Float:
89
        case ValueKind.Int32:
90
        case ValueKind.Int64:
91
            return x.toString();
24✔
92
        case ValueKind.Object:
93
            if (x instanceof RoAssociativeArray) {
18✔
94
                visit(x, visited);
7✔
95
                return `{${x
5✔
96
                    .getElements()
97
                    .map((k: BrsString) => {
98
                        return `"${k.toString()}":${jsonOf(interpreter, x.get(k), visited)}`;
24✔
99
                    })
100
                    .join(",")}}`;
101
            }
102
            if (x instanceof RoArray) {
11✔
103
                visit(x, visited);
3✔
104
                return `[${x
2✔
105
                    .getElements()
106
                    .map((el: BrsType) => {
107
                        return jsonOf(interpreter, el, visited);
9✔
108
                    })
109
                    .join(",")}]`;
110
            }
111
            if (isUnboxable(x)) {
8✔
112
                return jsonOf(interpreter, x.unbox(), visited);
8✔
113
            }
114
            break;
×
115
        case ValueKind.Callable:
116
        case ValueKind.Uninitialized:
117
        case ValueKind.Interface:
118
            break;
1✔
119
        default:
120
            // Exhaustive check as per:
121
            // https://basarat.gitbooks.io/typescript/content/docs/types/discriminated-unions.html
122
            const _: never = x;
×
123
            break;
×
124
    }
125
    throw new Error(`jsonOf not implemented for: ${x}`);
1✔
126
}
127

128
function logBrsErr(functionName: string, err: Error): void {
129
    if (process.env.NODE_ENV === "test") {
5✔
130
        return;
1✔
131
    }
132
    console.error(`BRIGHTSCRIPT: ERROR: ${functionName}: ${err.message}`);
4✔
133
}
134

135
export const FormatJson = new Callable("FormatJson", {
131✔
136
    signature: {
137
        returns: ValueKind.String,
138
        args: [
139
            new StdlibArgument("x", ValueKind.Object),
140
            new StdlibArgument("flags", ValueKind.Int32, new Int32(0)),
141
        ],
142
    },
143
    impl: (interpreter: Interpreter, x: BrsType, _flags: Int32) => {
144
        try {
15✔
145
            return new BrsString(jsonOf(interpreter, x));
15✔
146
        } catch (err: any) {
147
            // example RBI error:
148
            // "BRIGHTSCRIPT: ERROR: FormatJSON: Value type not supported: roFunction: pkg:/source/main.brs(14)"
149
            logBrsErr("FormatJSON", err);
4✔
150
            return new BrsString("");
4✔
151
        }
152
    },
153
});
154

155
export const ParseJson = new Callable("ParseJson", {
131✔
156
    signature: {
157
        returns: ValueKind.Dynamic,
158
        args: [new StdlibArgument("jsonString", ValueKind.String)],
159
    },
160
    impl: (_: Interpreter, jsonString: BrsString) => {
161
        try {
12✔
162
            let s: string = jsonString.toString().trim();
12✔
163

164
            if (s === "") {
12✔
165
                throw new Error("Data is empty");
1✔
166
            }
167

168
            return brsValueOf(JSON.parse(s));
11✔
169
        } catch (err: any) {
170
            // example RBI error:
171
            // "BRIGHTSCRIPT: ERROR: ParseJSON: Unknown identifier 'x': pkg:/source/main.brs(25)"
172
            logBrsErr("ParseJSON", err);
1✔
173
            return BrsInvalid.Instance;
1✔
174
        }
175
    },
176
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc