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

rokucommunity / brighterscript / #13220

20 Oct 2024 10:07AM UTC coverage: 86.844% (-1.4%) from 88.214%
#13220

push

web-flow
Merge ab48ea8a7 into 7cfaaa047

11599 of 14119 branches covered (82.15%)

Branch coverage included in aggregate %.

7024 of 7616 new or added lines in 100 files covered. (92.23%)

87 existing lines in 18 files now uncovered.

12726 of 13891 relevant lines covered (91.61%)

30006.11 hits per line

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

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

13

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

20
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
21
export class BuiltInInterfaceAdder {
1✔
22

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

25
    static typedFunctionFactory: (type: BscType) => TypedFunctionType;
26
    static unionTypeFactory: (types: BscType[]) => UnionType;
27

28
    static getLookupTable: () => SymbolTable;
29

30
    static addBuiltInInterfacesToType(thisType: BscType, overrides?: Map<string, BuiltInInterfaceOverride>) {
31
        const builtInMemberTable = thisType.getBuiltInMemberTable();
473,336✔
32
        if (!builtInMemberTable) {
473,336✔
33
            // no memberTable to add to
34
            // this could be because it's a class that has a parent
35
            // the original ancestor should get the built ins
36
            return;
157,953✔
37
        }
38
        //const realMemberTable = thisType.getMemberTable();
39
        //const checkForExistingMembers = realMemberTable && realMemberTable !== builtInMemberTable;
40
        const builtInComponent = this.getMatchingRokuComponent(thisType);
315,383✔
41
        if (!builtInComponent) {
315,383✔
42
            // TODO: Perhaps have error here, but docs have some references to unknown types
43
            //throw new Error(`Unknown Roku component '${this.getMatchingRokuComponentName(thisType)}' for type '${thisType.toString()}'`);
44
            return;
3,322✔
45
        }
46
        if (!this.typedFunctionFactory) {
312,061!
NEW
47
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
48
        }
49
        const interfacesToLoop = builtInComponent.interfaces ?? [builtInComponent];
312,061✔
50

51
        //add any direct methods from this component to the member table
52
        if (this.isBrightScriptComponent(thisType)) {
312,061✔
53
            for (const method of builtInComponent.methods ?? []) {
126,106✔
54
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
1,771✔
55
                let flags = SymbolTypeFlag.runtime;
1,771✔
56
                //set the deprecated flag if applicable
57
                if ((method as any).isDeprecated) {
1,771!
58
                    flags |= SymbolTypeFlag.deprecated; // eslint-disable-line no-bitwise
1,771✔
59
                }
60
                builtInMemberTable.addSymbol(method.name, { description: method.description, completionPriority: 1 }, methodFuncType, flags);
1,771✔
61
            }
62
        }
63

64
        for (const iface of interfacesToLoop) {
312,061✔
65
            const lowerIfaceName = iface.name.toLowerCase();
505,644✔
66
            const ifaceData = (interfaces[lowerIfaceName] ?? events[lowerIfaceName]) as BRSInterfaceData;
505,644✔
67

68
            if (builtInComponent.interfaces) {
505,644✔
69
                // this type has interfaces - add them directly as members
70
                const ifaceType = this.getLookupTable()?.getSymbolType(iface.name, { flags: SymbolTypeFlag.typetime });
319,689!
71
                if (ifaceType) {
319,689!
72
                    builtInMemberTable.addSymbol(iface.name, { completionPriority: 1 }, ifaceType, SymbolTypeFlag.runtime);
319,689✔
73
                }
74
            }
75

76
            for (const method of ifaceData.methods ?? []) {
505,644!
77
                if (ifaceData.name.toLowerCase() === 'ifintops' && method.name.toLowerCase() === 'tostr') {
3,661,343✔
78
                    // handle special case - this messed up the .toStr() method on integers
79
                    continue;
3,554✔
80
                }
81
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
3,657,789✔
82
                builtInMemberTable.addSymbol(method.name, { description: method.description, completionPriority: 1 }, methodFuncType, SymbolTypeFlag.runtime);
3,657,789✔
83
            }
84
            for (const property of ifaceData.properties ?? []) {
505,644!
NEW
85
                const override = overrides?.get(property.name.toLowerCase());
×
NEW
86
                builtInMemberTable.addSymbol(property.name, { description: property.description, completionPriority: 1 }, override?.type ?? this.getPrimitiveType(property.type) ?? this.getPrimitiveType('dynamic'), SymbolTypeFlag.runtime);
×
87
            }
88
        }
89
    }
90

91
    private static buildMethodFromDocData(method: BRSInterfaceMethodData, overrides?: Map<string, BuiltInInterfaceOverride>, thisType?: BscType): TypedFunctionType {
92
        const override = overrides?.get(method.name.toLowerCase());
3,659,560✔
93
        let returnType = override?.returnType ?? this.getPrimitiveType(method.returnType);
3,659,560✔
94
        if (!returnType && method.returnType.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
3,659,560!
NEW
95
            returnType = thisType;
×
96
        }
97
        const methodFuncType = this.typedFunctionFactory(returnType);
3,659,560✔
98
        methodFuncType.name = method.name;
3,659,560✔
99
        methodFuncType.isVariadic = method.isVariadic ?? false;
3,659,560✔
100
        // eslint-disable-next-line @typescript-eslint/prefer-for-of
101
        for (let i = 0; i < method.params.length; i++) {
3,659,560✔
102
            const param = method.params[i];
2,785,635✔
103
            let paramType = override?.parameterTypes?.[i] ?? this.getPrimitiveType(param.type);
2,785,635✔
104
            if (!paramType && param.type.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
2,785,635!
NEW
105
                paramType = thisType;
×
106
            }
107
            paramType ??= this.primitiveTypeInstanceCache.get('dynamic');
2,785,635!
108
            methodFuncType.addParameter(param.name, paramType, !param.isRequired);
2,785,635✔
109
        }
110
        return methodFuncType;
3,659,560✔
111
    }
112

113
    private static getPrimitiveType(typeName: string): BscType {
114
        if (typeName.includes(' or ')) {
6,544,360✔
115
            if (!this.unionTypeFactory) {
49,642!
NEW
116
                throw new Error(`Unable to build union types - no union type factory`);
×
117
            }
118
            // union types!
119
            const unionOfTypeNames = typeName.split(' or ');
49,642✔
120
            return this.unionTypeFactory(unionOfTypeNames.map(name => this.getPrimitiveType(name)));
99,284✔
121
        }
122
        const returnType = this.primitiveTypeInstanceCache.get(typeName.toLowerCase());
6,494,718✔
123
        if (!returnType) {
6,494,718✔
124
            if (!this.getLookupTable) {
205,517!
NEW
125
                throw new Error(`Unable to find type instance '${typeName}'`);
×
126
            }
127
            return this.getLookupTable()?.getSymbolType(typeName, { flags: SymbolTypeFlag.typetime, fullName: typeName, tableProvider: this.getLookupTable });
205,517!
128
        }
129

130
        return returnType;
6,289,201✔
131
    }
132

133
    static getMatchingRokuComponentName(theType: BscType) {
134
        if (isStringType(theType)) {
627,454✔
135
            return 'roString';
54✔
136
        } else if (isIntegerType(theType)) {
627,400✔
137
            return 'roInt';
22✔
138
        } else if (isBooleanType(theType)) {
627,378✔
139
            return 'roBoolean';
10✔
140
        } else if (isFloatType(theType)) {
627,368✔
141
            return 'roFloat';
10✔
142
        } else if (isDoubleType(theType)) {
627,358✔
143
            return 'roDouble';
4✔
144
        } else if (isLongIntegerType(theType)) {
627,354!
NEW
145
            return 'roLongInteger';
×
146
        } else if (isInvalidType(theType)) {
627,354!
NEW
147
            return 'roInvalid';
×
148
        } else if (isCallableType(theType)) {
627,354✔
149
            return 'roFunction';
20✔
150
        } else if (isAssociativeArrayType(theType)) {
627,334✔
151
            return 'roAssociativeArray';
548✔
152
        } else if (isArrayType(theType)) {
626,786✔
153
            return 'roArray';
36✔
154
        } else if (isEnumMemberType(theType)) {
626,750✔
155
            return this.getMatchingRokuComponentName(theType.underlyingType);
10✔
156
        } else if (isInterfaceType(theType)) {
626,740✔
157
            return theType.name;
604,044✔
158
        } else if (isComponentType(theType)) {
22,696✔
159
            return 'roSGNode';
21,270✔
160
        }
161
    }
162

163
    static isBrightScriptComponent(theType: BscType) {
164
        const componentName = this.getMatchingRokuComponentName(theType);
312,061✔
165
        if (!componentName) {
312,061!
166
            // No component matches the given type
NEW
167
            return;
×
168
        }
169
        const lowerComponentName = componentName.toLowerCase();
312,061✔
170
        return !!components[lowerComponentName];
312,061✔
171
    }
172

173
    //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
174
    static getMatchingRokuComponent(theType: BscType): typeof components['roappinfo'] & typeof interfaces['ifappinfo'] & typeof events['rourlevent'] {
175
        const componentName = this.getMatchingRokuComponentName(theType);
315,383✔
176
        if (!componentName) {
315,383✔
177
            // No component matches the given type
178
            return;
1,426✔
179
        }
180
        const lowerComponentName = componentName.toLowerCase();
313,957✔
181
        return components[lowerComponentName] ?? interfaces[lowerComponentName] ?? events[lowerComponentName];
313,957✔
182
    }
183

184
    static addBuiltInFieldsToNodeType(thisType: ComponentType) {
185
        const nodeName = thisType.name;
168,546✔
186
        const memberTable = thisType.memberTable;
168,546✔
187
        if (!memberTable) {
168,546!
188
            // no memberTable to add to
NEW
189
            return;
×
190
        }
191
        const builtInNode = nodes[nodeName.toLowerCase()] as SGNodeData;
168,546✔
192
        if (!builtInNode) {
168,546✔
193
            return;
300✔
194
        }
195
        if (!this.typedFunctionFactory) {
168,246!
NEW
196
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
197
        }
198
        const lookupTable = this.getLookupTable();
168,246✔
199
        for (const field of builtInNode.fields) {
168,246✔
200
            memberTable.addSymbol(field.name, { description: field.description, completionPriority: 1 }, util.getNodeFieldType(field.type, lookupTable), SymbolTypeFlag.runtime);
1,524,845✔
201
        }
202
        for (const method of builtInNode.methods ?? []) {
168,246!
NEW
203
            const methodFuncType = this.buildMethodFromDocData(method, null, thisType);
×
NEW
204
            let flags = SymbolTypeFlag.runtime;
×
205
            //set the deprecated flag if applicable
NEW
206
            if (method.isDeprecated) {
×
NEW
207
                flags |= SymbolTypeFlag.deprecated; // eslint-disable-line no-bitwise
×
208
            }
NEW
209
            memberTable.addSymbol(method.name, { description: method.description, completionPriority: 1 }, methodFuncType, flags);
×
210
        }
211
    }
212

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