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

rokucommunity / brighterscript / #13117

01 Oct 2024 08:24AM UTC coverage: 86.842% (-1.4%) from 88.193%
#13117

push

web-flow
Merge abd960cd5 into 3a2dc7282

11537 of 14048 branches covered (82.13%)

Branch coverage included in aggregate %.

6991 of 7582 new or added lines in 100 files covered. (92.21%)

83 existing lines in 18 files now uncovered.

12692 of 13852 relevant lines covered (91.63%)

29478.96 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();
464,488✔
32
        if (!builtInMemberTable) {
464,488✔
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;
155,016✔
37
        }
38
        //const realMemberTable = thisType.getMemberTable();
39
        //const checkForExistingMembers = realMemberTable && realMemberTable !== builtInMemberTable;
40
        const builtInComponent = this.getMatchingRokuComponent(thisType);
309,472✔
41
        if (!builtInComponent) {
309,472✔
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,223✔
45
        }
46
        if (!this.typedFunctionFactory) {
306,249!
NEW
47
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
48
        }
49
        const interfacesToLoop = builtInComponent.interfaces ?? [builtInComponent];
306,249✔
50

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

64
        for (const iface of interfacesToLoop) {
306,249✔
65
            const lowerIfaceName = iface.name.toLowerCase();
496,231✔
66
            const ifaceData = (interfaces[lowerIfaceName] ?? events[lowerIfaceName]) as BRSInterfaceData;
496,231✔
67

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

76
            for (const method of ifaceData.methods ?? []) {
496,231!
77
                if (ifaceData.name.toLowerCase() === 'ifintops' && method.name.toLowerCase() === 'tostr') {
3,593,171✔
78
                    // handle special case - this messed up the .toStr() method on integers
79
                    continue;
3,488✔
80
                }
81
                const methodFuncType = this.buildMethodFromDocData(method, overrides, thisType);
3,589,683✔
82
                builtInMemberTable.addSymbol(method.name, { description: method.description, completionPriority: 1 }, methodFuncType, SymbolTypeFlag.runtime);
3,589,683✔
83
            }
84
            for (const property of ifaceData.properties ?? []) {
496,231!
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,591,421✔
93
        let returnType = override?.returnType ?? this.getPrimitiveType(method.returnType);
3,591,421✔
94
        if (!returnType && method.returnType.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
3,591,421!
NEW
95
            returnType = thisType;
×
96
        }
97
        const methodFuncType = this.typedFunctionFactory(returnType);
3,591,421✔
98
        methodFuncType.name = method.name;
3,591,421✔
99
        methodFuncType.isVariadic = method.isVariadic ?? false;
3,591,421✔
100
        // eslint-disable-next-line @typescript-eslint/prefer-for-of
101
        for (let i = 0; i < method.params.length; i++) {
3,591,421✔
102
            const param = method.params[i];
2,733,764✔
103
            let paramType = override?.parameterTypes?.[i] ?? this.getPrimitiveType(param.type);
2,733,764✔
104
            if (!paramType && param.type.toLowerCase() === (thisType as any)?.name?.toLowerCase()) {
2,733,764!
NEW
105
                paramType = thisType;
×
106
            }
107
            paramType ??= this.primitiveTypeInstanceCache.get('dynamic');
2,733,764!
108
            methodFuncType.addParameter(param.name, paramType, !param.isRequired);
2,733,764✔
109
        }
110
        return methodFuncType;
3,591,421✔
111
    }
112

113
    private static getPrimitiveType(typeName: string): BscType {
114
        if (typeName.includes(' or ')) {
6,422,502✔
115
            if (!this.unionTypeFactory) {
48,718!
NEW
116
                throw new Error(`Unable to build union types - no union type factory`);
×
117
            }
118
            // union types!
119
            const unionOfTypeNames = typeName.split(' or ');
48,718✔
120
            return this.unionTypeFactory(unionOfTypeNames.map(name => this.getPrimitiveType(name)));
97,436✔
121
        }
122
        const returnType = this.primitiveTypeInstanceCache.get(typeName.toLowerCase());
6,373,784✔
123
        if (!returnType) {
6,373,784✔
124
            if (!this.getLookupTable) {
201,689!
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 });
201,689!
128
        }
129

130
        return returnType;
6,172,095✔
131
    }
132

133
    static getMatchingRokuComponentName(theType: BscType) {
134
        if (isStringType(theType)) {
615,731✔
135
            return 'roString';
54✔
136
        } else if (isIntegerType(theType)) {
615,677✔
137
            return 'roInt';
22✔
138
        } else if (isBooleanType(theType)) {
615,655✔
139
            return 'roBoolean';
10✔
140
        } else if (isFloatType(theType)) {
615,645✔
141
            return 'roFloat';
10✔
142
        } else if (isDoubleType(theType)) {
615,635✔
143
            return 'roDouble';
4✔
144
        } else if (isLongIntegerType(theType)) {
615,631!
NEW
145
            return 'roLongInteger';
×
146
        } else if (isInvalidType(theType)) {
615,631!
NEW
147
            return 'roInvalid';
×
148
        } else if (isCallableType(theType)) {
615,631✔
149
            return 'roFunction';
20✔
150
        } else if (isAssociativeArrayType(theType)) {
615,611✔
151
            return 'roAssociativeArray';
540✔
152
        } else if (isArrayType(theType)) {
615,071✔
153
            return 'roArray';
36✔
154
        } else if (isEnumMemberType(theType)) {
615,035✔
155
            return this.getMatchingRokuComponentName(theType.underlyingType);
10✔
156
        } else if (isInterfaceType(theType)) {
615,025✔
157
            return theType.name;
592,789✔
158
        } else if (isComponentType(theType)) {
22,236✔
159
            return 'roSGNode';
20,874✔
160
        }
161
    }
162

163
    static isBrightScriptComponent(theType: BscType) {
164
        const componentName = this.getMatchingRokuComponentName(theType);
306,249✔
165
        if (!componentName) {
306,249!
166
            // No component matches the given type
NEW
167
            return;
×
168
        }
169
        const lowerComponentName = componentName.toLowerCase();
306,249✔
170
        return !!components[lowerComponentName];
306,249✔
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);
309,472✔
176
        if (!componentName) {
309,472✔
177
            // No component matches the given type
178
            return;
1,362✔
179
        }
180
        const lowerComponentName = componentName.toLowerCase();
308,110✔
181
        return components[lowerComponentName] ?? interfaces[lowerComponentName] ?? events[lowerComponentName];
308,110✔
182
    }
183

184
    static addBuiltInFieldsToNodeType(thisType: ComponentType) {
185
        const nodeName = thisType.name;
165,411✔
186
        const memberTable = thisType.memberTable;
165,411✔
187
        if (!memberTable) {
165,411!
188
            // no memberTable to add to
NEW
189
            return;
×
190
        }
191
        const builtInNode = nodes[nodeName.toLowerCase()] as SGNodeData;
165,411✔
192
        if (!builtInNode) {
165,411✔
193
            return;
300✔
194
        }
195
        if (!this.typedFunctionFactory) {
165,111!
NEW
196
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
197
        }
198
        const lookupTable = this.getLookupTable();
165,111✔
199
        for (const field of builtInNode.fields) {
165,111✔
200
            memberTable.addSymbol(field.name, { description: field.description, completionPriority: 1 }, util.getNodeFieldType(field.type, lookupTable), SymbolTypeFlag.runtime);
1,496,432✔
201
        }
202
        for (const method of builtInNode.methods ?? []) {
165,111!
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