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

rokucommunity / brighterscript / #13678

03 Feb 2025 03:04PM UTC coverage: 86.757% (-1.4%) from 88.185%
#13678

push

web-flow
Merge 142d6f80c into 4afb6f658

12464 of 15187 branches covered (82.07%)

Branch coverage included in aggregate %.

7747 of 8404 new or added lines in 101 files covered. (92.18%)

85 existing lines in 17 files now uncovered.

13394 of 14618 relevant lines covered (91.63%)

34275.33 hits per line

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

76.61
/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

14

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

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

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

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

31
    static typedFunctionFactory: (type: BscType) => TypedFunctionType;
32
    static unionTypeFactory: (types: BscType[]) => UnionType;
33

34
    static getLookupTable: () => SymbolTable;
35

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

57
        //add any direct methods from this component to the member table
58
        if (this.isBrightScriptComponent(thisType)) {
332,500✔
59
            for (const method of builtInComponent.methods ?? []) {
136,045✔
60
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
1,871✔
61
                let flags = SymbolTypeFlag.runtime;
1,871✔
62
                //set the deprecated flag if applicable
63
                if ((method as any).isDeprecated) {
1,871!
64
                    flags |= SymbolTypeFlag.deprecated; // eslint-disable-line no-bitwise
1,871✔
65
                }
66
                builtInMemberTable.addSymbol(method.name, { ...builtInSymbolData, description: method.description }, methodFuncType, flags);
1,871✔
67
            }
68
        }
69

70
        for (const iface of interfacesToLoop) {
332,500✔
71
            const lowerIfaceName = iface.name.toLowerCase();
555,711✔
72
            const ifaceData = (interfaces[lowerIfaceName] ?? events[lowerIfaceName]) as BRSInterfaceData;
555,711✔
73

74
            if (builtInComponent.interfaces) {
555,711✔
75
                // this type has interfaces - add them directly as members
76
                const ifaceType = this.getLookupTable()?.getSymbolType(iface.name, { flags: SymbolTypeFlag.typetime });
359,256!
77
                if (ifaceType) {
359,256!
78
                    builtInMemberTable.addSymbol(iface.name, { ...builtInSymbolData }, ifaceType, SymbolTypeFlag.runtime);
359,256✔
79
                }
80
            }
81

82
            for (const method of ifaceData.methods ?? []) {
555,711!
83
                if (ifaceData.name.toLowerCase() === 'ifintops' && method.name.toLowerCase() === 'tostr') {
4,091,865✔
84
                    // handle special case - this messed up the .toStr() method on integers
85
                    continue;
4,004✔
86
                }
87
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
4,087,861✔
88
                builtInMemberTable.addSymbol(method.name, { ...builtInSymbolData, description: method.description }, methodFuncType, SymbolTypeFlag.runtime);
4,087,861✔
89
            }
90
            for (const property of ifaceData.properties ?? []) {
555,711!
NEW
91
                const override = overrides?.get(property.name.toLowerCase());
×
NEW
92
                builtInMemberTable.addSymbol(property.name, { ...builtInSymbolData, description: property.description }, override?.type ??
×
93
                    this.getPrimitiveType(property.type) ?? this.getPrimitiveType('dynamic'), SymbolTypeFlag.runtime);
×
94
            }
95
        }
96
    }
97

98
    private static buildMethodFromDocData(method: BRSInterfaceMethodData, overrides?: Map<string, BuiltInInterfaceOverride>, thisType?: BscType): TypedFunctionType {
99
        const override = overrides?.get(method.name.toLowerCase());
4,089,732✔
100
        let returnType = override?.returnType ?? this.getPrimitiveType(method.returnType);
4,089,732✔
101
        if (!returnType && method.returnType.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
4,089,732!
NEW
102
            returnType = thisType;
×
103
        }
104
        const methodFuncType = this.typedFunctionFactory(returnType);
4,089,732✔
105
        methodFuncType.name = method.name;
4,089,732✔
106
        methodFuncType.isVariadic = method.isVariadic ?? false;
4,089,732✔
107
        // eslint-disable-next-line @typescript-eslint/prefer-for-of
108
        for (let i = 0; i < method.params.length; i++) {
4,089,732✔
109
            const param = method.params[i];
3,218,255✔
110
            let paramType = override?.parameterTypes?.[i] ?? this.getPrimitiveType(param.type);
3,218,255✔
111
            if (!paramType && param.type.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
3,218,255!
NEW
112
                paramType = thisType;
×
113
            }
114
            paramType ??= this.primitiveTypeInstanceCache.get('dynamic');
3,218,255!
115
            methodFuncType.addParameter(param.name, paramType, !param.isRequired);
3,218,255✔
116
        }
117
        return methodFuncType;
4,089,732✔
118
    }
119

120
    private static getPrimitiveType(typeName: string): BscType {
121
        if (typeName.includes(' or ')) {
7,447,436✔
122
            if (!this.unionTypeFactory) {
69,896!
NEW
123
                throw new Error(`Unable to build union types - no union type factory`);
×
124
            }
125
            // union types!
126
            const unionOfTypeNames = typeName.split(' or ');
69,896✔
127
            return this.unionTypeFactory(unionOfTypeNames.map(name => this.getPrimitiveType(name)));
139,792✔
128
        }
129
        const returnType = this.primitiveTypeInstanceCache.get(typeName.toLowerCase());
7,377,540✔
130
        if (!returnType) {
7,377,540✔
131
            if (!this.getLookupTable) {
286,351!
NEW
132
                throw new Error(`Unable to find type instance '${typeName}'`);
×
133
            }
134
            return this.getLookupTable()?.getSymbolType(typeName, { flags: SymbolTypeFlag.typetime, fullName: typeName, tableProvider: this.getLookupTable });
286,351!
135
        }
136

137
        return returnType;
7,091,189✔
138
    }
139

140
    static getMatchingRokuComponentName(theType: BscType) {
141
        if (isStringType(theType)) {
669,379✔
142
            return 'roString';
1,086✔
143
        } else if (isIntegerType(theType)) {
668,293✔
144
            return 'roInt';
522✔
145
        } else if (isBooleanType(theType)) {
667,771✔
146
            return 'roBoolean';
56✔
147
        } else if (isFloatType(theType)) {
667,715✔
148
            return 'roFloat';
154✔
149
        } else if (isDoubleType(theType)) {
667,561✔
150
            return 'roDouble';
24✔
151
        } else if (isLongIntegerType(theType)) {
667,537✔
152
            return 'roLongInteger';
22✔
153
        } else if (isInvalidType(theType)) {
667,515!
NEW
154
            return 'roInvalid';
×
155
        } else if (isCallableType(theType)) {
667,515✔
156
            return 'roFunction';
60✔
157
        } else if (isAssociativeArrayType(theType)) {
667,455✔
158
            return 'roAssociativeArray';
618✔
159
        } else if (isArrayType(theType)) {
666,837✔
160
            return 'roArray';
100✔
161
        } else if (isEnumMemberType(theType)) {
666,737✔
162
            return this.getMatchingRokuComponentName(theType.underlyingType);
458✔
163
        } else if (isInterfaceType(theType)) {
666,279✔
164
            return theType.name;
638,208✔
165
        } else if (isComponentType(theType)) {
28,071✔
166
            return 'roSGNode';
26,210✔
167
        }
168
    }
169

170
    static isBrightScriptComponent(theType: BscType) {
171
        const componentName = this.getMatchingRokuComponentName(theType);
332,500✔
172
        if (!componentName) {
332,500!
173
            // No component matches the given type
NEW
174
            return;
×
175
        }
176
        const lowerComponentName = componentName.toLowerCase();
332,500✔
177
        return !!components[lowerComponentName];
332,500✔
178
    }
179

180
    //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
181
    static getMatchingRokuComponent(theType: BscType): typeof components['roappinfo'] & typeof interfaces['ifappinfo'] & typeof events['rourlevent'] {
182
        const componentName = this.getMatchingRokuComponentName(theType);
336,421✔
183
        if (!componentName) {
336,421✔
184
            // No component matches the given type
185
            return;
1,861✔
186
        }
187
        const lowerComponentName = componentName.toLowerCase();
334,560✔
188
        return components[lowerComponentName] ?? interfaces[lowerComponentName] ?? events[lowerComponentName];
334,560✔
189
    }
190

191
    static addBuiltInFieldsToNodeType(thisType: ComponentType) {
192
        const nodeName = thisType.name;
180,089✔
193
        const memberTable = thisType.memberTable;
180,089✔
194
        if (!memberTable) {
180,089!
195
            // no memberTable to add to
NEW
196
            return;
×
197
        }
198
        const builtInNode = nodes[nodeName.toLowerCase()] as SGNodeData;
180,089✔
199
        if (!builtInNode) {
180,089✔
200
            return;
472✔
201
        }
202
        if (!this.typedFunctionFactory) {
179,617!
NEW
203
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
204
        }
205
        const lookupTable = this.getLookupTable();
179,617✔
206
        for (const field of builtInNode.fields) {
179,617✔
207
            memberTable.addSymbol(field.name, { ...builtInSymbolData, description: field.description }, util.getNodeFieldType(field.type, lookupTable), SymbolTypeFlag.runtime);
1,650,236✔
208
        }
209
        for (const method of builtInNode.methods ?? []) {
179,617!
NEW
210
            const methodFuncType = this.buildMethodFromDocData(method, null, thisType);
×
NEW
211
            let flags = SymbolTypeFlag.runtime;
×
212
            //set the deprecated flag if applicable
NEW
213
            if (method.isDeprecated) {
×
NEW
214
                flags |= SymbolTypeFlag.deprecated; // eslint-disable-line no-bitwise
×
215
            }
NEW
216
            memberTable.addSymbol(method.name, { ...builtInSymbolData, description: method.description }, methodFuncType, flags);
×
217
        }
218
    }
219

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