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

rokucommunity / brighterscript / #15035

15 Dec 2025 08:42PM UTC coverage: 86.889%. Remained the same
#15035

push

web-flow
Merge a60226157 into 2ea4d2108

14466 of 17575 branches covered (82.31%)

Branch coverage included in aggregate %.

113 of 217 new or added lines in 8 files covered. (52.07%)

116 existing lines in 6 files now uncovered.

15185 of 16550 relevant lines covered (91.75%)

24075.2 hits per line

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

77.48
/src/types/BuiltInInterfaceAdder.ts
1
import type { BRSInterfaceData, BRSInterfaceMethodData, SGNodeData } from '../roku-types';
2
import { components, events, interfaces, nodes } from '../roku-types';
1✔
3
import { Cache } from '../Cache';
1✔
4
import type { TypedFunctionType } from './TypedFunctionType';
5
import type { SymbolTable } from '../SymbolTable';
6
import { SymbolTypeFlag } from '../SymbolTypeFlag';
1✔
7
import type { BscType } from './BscType';
8
import { isArrayType, isAssociativeArrayType, isBooleanType, isCallableType, isComponentType, isDoubleType, isEnumMemberType, isFloatType, isIntegerType, isInterfaceType, isInvalidType, isLongIntegerType, isStringType } from '../astUtils/reflection';
1✔
9
import type { ComponentType } from './ComponentType';
10
import { util } from '../util';
1✔
11
import type { UnionType } from './UnionType';
12
import type { ExtraSymbolData } from '../interfaces';
13
import type { IntersectionType } from './IntersectionType';
14

15

16
export interface BuiltInInterfaceOverride {
17
    type?: BscType;
18
    parameterTypes?: BscType[];
19
    returnType?: BscType;
20
}
21

22
const builtInSymbolData: ExtraSymbolData = {
1✔
23
    completionPriority: 1,
24
    isBuiltIn: true
25
};
26

27
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
28
export class BuiltInInterfaceAdder {
1✔
29

30
    static readonly primitiveTypeInstanceCache = new Cache<string, BscType>();
1✔
31

32
    static typedFunctionFactory: (type: BscType) => TypedFunctionType;
33
    static intersectionTypeFactory: (types: BscType[]) => IntersectionType;
34
    static unionTypeFactory: (types: BscType[]) => UnionType;
35

36
    static getLookupTable: () => SymbolTable;
37

38
    static addBuiltInInterfacesToType(thisType: BscType, overrides?: Map<string, BuiltInInterfaceOverride>) {
39
        const builtInMemberTable = thisType.getBuiltInMemberTable();
572,977✔
40
        if (!builtInMemberTable) {
572,977✔
41
            // no memberTable to add to
42
            // this could be because it's a class that has a parent
43
            // the original ancestor should get the built ins
44
            return;
191,101✔
45
        }
46
        //const realMemberTable = thisType.getMemberTable();
47
        //const checkForExistingMembers = realMemberTable && realMemberTable !== builtInMemberTable;
48
        const builtInComponent = this.getMatchingRokuComponent(thisType);
381,876✔
49
        if (!builtInComponent) {
381,876✔
50
            // TODO: Perhaps have error here, but docs have some references to unknown types
51
            //throw new Error(`Unknown Roku component '${this.getMatchingRokuComponentName(thisType)}' for type '${thisType.toString()}'`);
52
            return;
4,094✔
53
        }
54
        if (!this.typedFunctionFactory) {
377,782!
UNCOV
55
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
56
        }
57
        const interfacesToLoop = builtInComponent.interfaces ?? [builtInComponent];
377,782✔
58

59
        //add any direct methods from this component to the member table
60
        if (this.isBrightScriptComponent(thisType)) {
377,782✔
61
            for (const method of builtInComponent.methods ?? []) {
155,392✔
62
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
14,826✔
63
                let flags = SymbolTypeFlag.runtime;
14,826✔
64
                //set the deprecated flag if applicable
65
                if ((method as any).isDeprecated) {
14,826✔
66
                    flags |= SymbolTypeFlag.deprecated; // eslint-disable-line no-bitwise
2,118✔
67
                }
68
                builtInMemberTable.addSymbol(method.name, { ...builtInSymbolData, description: method.description }, methodFuncType, flags);
14,826✔
69
            }
70
        }
71

72
        for (const iface of interfacesToLoop) {
377,782✔
73
            const lowerIfaceName = iface.name.toLowerCase();
612,158✔
74
            const ifaceData = (interfaces[lowerIfaceName] ?? events[lowerIfaceName]) as BRSInterfaceData;
612,158✔
75

76
            if (builtInComponent.interfaces) {
612,158✔
77
                // this type has interfaces - add them directly as members
78
                const ifaceType = this.getLookupTable()?.getSymbolType(iface.name, { flags: SymbolTypeFlag.typetime });
389,768!
79
                if (ifaceType) {
389,768!
80
                    builtInMemberTable.addSymbol(iface.name, { ...builtInSymbolData }, ifaceType, SymbolTypeFlag.runtime);
389,768✔
81
                }
82
            }
83

84
            for (const method of ifaceData.methods ?? []) {
612,158!
85
                if (ifaceData.name.toLowerCase() === 'ifintops' && method.name.toLowerCase() === 'tostr') {
4,542,983✔
86
                    // handle special case - this messed up the .toStr() method on integers
87
                    continue;
4,297✔
88
                }
89
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
4,538,686✔
90
                builtInMemberTable.addSymbol(method.name, { ...builtInSymbolData, description: method.description }, methodFuncType, SymbolTypeFlag.runtime);
4,538,686✔
91
            }
92
            for (const property of ifaceData.properties ?? []) {
612,158!
UNCOV
93
                const override = overrides?.get(property.name.toLowerCase());
×
UNCOV
94
                builtInMemberTable.addSymbol(property.name, { ...builtInSymbolData, description: property.description }, override?.type ??
×
95
                    this.getPrimitiveType(property.type) ?? this.getPrimitiveType('dynamic'), SymbolTypeFlag.runtime);
×
96
            }
97
        }
98
    }
99

100
    private static builtInMethodCache = new Cache<BRSInterfaceMethodData, TypedFunctionType>();
1✔
101

102
    private static buildMethodFromDocData(method: BRSInterfaceMethodData, overrides?: Map<string, BuiltInInterfaceOverride>, thisType?: BscType): TypedFunctionType {
103
        const override = overrides?.get(method.name.toLowerCase());
4,553,512✔
104
        const getMethodType = () => {
4,553,512✔
105

106
            let returnType = override?.returnType ?? this.getPrimitiveType(method.returnType);
1,191✔
107
            if (!returnType && method.returnType.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
1,191!
UNCOV
108
                returnType = thisType;
×
109
            }
110
            const methodFuncType = this.typedFunctionFactory(returnType);
1,191✔
111
            methodFuncType.name = method.name;
1,191✔
112
            methodFuncType.isVariadic = method.isVariadic ?? false;
1,191✔
113
            // eslint-disable-next-line @typescript-eslint/prefer-for-of
114
            for (let i = 0; i < method.params.length; i++) {
1,191✔
115
                const param = method.params[i];
700✔
116
                let paramType = override?.parameterTypes?.[i] ?? this.getPrimitiveType(param.type);
700✔
117
                if (!paramType && param.type.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
700!
UNCOV
118
                    paramType = thisType;
×
119
                }
120
                paramType ??= this.primitiveTypeInstanceCache.get('dynamic');
700!
121
                methodFuncType.addParameter(param.name, paramType, !param.isRequired);
700✔
122
            }
123
            return methodFuncType;
1,191✔
124
        };
125

126
        if (override) {
4,553,512✔
127
            return getMethodType();
406✔
128
        }
129

130
        return this.builtInMethodCache.getOrAdd(method, getMethodType);
4,553,106✔
131
    }
132

133
    private static getPrimitiveType(typeName: string): BscType {
134
        if (typeName.includes(' or ')) {
1,499✔
135
            if (!this.unionTypeFactory) {
7!
UNCOV
136
                throw new Error(`Unable to build union types - no union type factory`);
×
137
            }
138
            // union types!
139
            const unionOfTypeNames = typeName.split(' or ');
7✔
140
            return this.unionTypeFactory(unionOfTypeNames.map(name => this.getPrimitiveType(name)));
14✔
141
        }
142
        const returnType = this.primitiveTypeInstanceCache.get(typeName.toLowerCase());
1,492✔
143
        if (!returnType) {
1,492✔
144
            if (!this.getLookupTable) {
38!
UNCOV
145
                throw new Error(`Unable to find type instance '${typeName}'`);
×
146
            }
147
            return this.getLookupTable()?.getSymbolType(typeName, { flags: SymbolTypeFlag.typetime, fullName: typeName, tableProvider: this.getLookupTable });
38!
148
        }
149

150
        return returnType;
1,454✔
151
    }
152

153
    static getMatchingRokuComponentName(theType: BscType) {
154
        if (isStringType(theType)) {
760,276✔
155
            return 'roString';
234✔
156
        } else if (isIntegerType(theType)) {
760,042✔
157
            return 'roInt';
120✔
158
        } else if (isBooleanType(theType)) {
759,922✔
159
            return 'roBoolean';
10✔
160
        } else if (isFloatType(theType)) {
759,912✔
161
            return 'roFloat';
12✔
162
        } else if (isDoubleType(theType)) {
759,900✔
163
            return 'roDouble';
6✔
164
        } else if (isLongIntegerType(theType)) {
759,894✔
165
            return 'roLongInteger';
2✔
166
        } else if (isInvalidType(theType)) {
759,892!
UNCOV
167
            return 'roInvalid';
×
168
        } else if (isCallableType(theType)) {
759,892✔
169
            return 'roFunction';
4,534✔
170
        } else if (isAssociativeArrayType(theType)) {
755,358✔
171
            return 'roAssociativeArray';
692✔
172
        } else if (isArrayType(theType)) {
754,666✔
173
            return 'roArray';
118✔
174
        } else if (isEnumMemberType(theType)) {
754,548✔
175
            return this.getMatchingRokuComponentName(theType.underlyingType);
618✔
176
        } else if (isInterfaceType(theType)) {
753,930✔
177
            return theType.name;
722,511✔
178
        } else if (isComponentType(theType)) {
31,419✔
179
            return 'roSGNode';
29,708✔
180
        }
181
    }
182

183
    static isBrightScriptComponent(theType: BscType) {
184
        const componentName = this.getMatchingRokuComponentName(theType);
377,782✔
185
        if (!componentName) {
377,782!
186
            // No component matches the given type
UNCOV
187
            return;
×
188
        }
189
        const lowerComponentName = componentName.toLowerCase();
377,782✔
190
        return !!components[lowerComponentName];
377,782✔
191
    }
192

193
    //the return type is a union of the three data types. Just pick the first item from each collection, as every item in the collection should have the same shape
194
    static getMatchingRokuComponent(theType: BscType): typeof components['roappinfo'] & typeof interfaces['ifappinfo'] & typeof events['rourlevent'] {
195
        const componentName = this.getMatchingRokuComponentName(theType);
381,876✔
196
        if (!componentName) {
381,876✔
197
            // No component matches the given type
198
            return;
1,751✔
199
        }
200
        const lowerComponentName = componentName.toLowerCase();
380,125✔
201
        return components[lowerComponentName] ?? interfaces[lowerComponentName] ?? events[lowerComponentName];
380,125✔
202
    }
203

204
    static addBuiltInFieldsToNodeType(thisType: ComponentType) {
205
        const nodeName = thisType.name;
205,850✔
206
        const memberTable = thisType.memberTable;
205,850✔
207
        if (!memberTable) {
205,850!
208
            // no memberTable to add to
UNCOV
209
            return;
×
210
        }
211
        const builtInNode = nodes[nodeName.toLowerCase()] as SGNodeData;
205,850✔
212
        if (!builtInNode) {
205,850✔
213
            return;
403✔
214
        }
215
        if (!this.typedFunctionFactory) {
205,447!
UNCOV
216
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
217
        }
218
        const lookupTable = this.getLookupTable();
205,447✔
219
        for (const field of builtInNode.fields) {
205,447✔
220
            memberTable.addSymbol(field.name, { ...builtInSymbolData, description: field.description }, util.getNodeFieldType(field.type, lookupTable), SymbolTypeFlag.runtime);
1,912,569✔
221
        }
222
        for (const method of builtInNode.methods ?? []) {
205,447!
UNCOV
223
            const methodFuncType = this.buildMethodFromDocData(method, null, thisType);
×
UNCOV
224
            let flags = SymbolTypeFlag.runtime;
×
225
            //set the deprecated flag if applicable
226
            if (method.isDeprecated) {
×
UNCOV
227
                flags |= SymbolTypeFlag.deprecated; // eslint-disable-line no-bitwise
×
228
            }
229
            memberTable.addSymbol(method.name, { ...builtInSymbolData, description: method.description }, methodFuncType, flags);
×
230
        }
231
    }
232

233
}
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