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

rokucommunity / brighterscript / #12930

13 Aug 2024 05:02PM UTC coverage: 86.193% (-1.7%) from 87.933%
#12930

push

web-flow
Merge 58ad447a2 into 0e968f1c3

10630 of 13125 branches covered (80.99%)

Branch coverage included in aggregate %.

6675 of 7284 new or added lines in 99 files covered. (91.64%)

84 existing lines in 18 files now uncovered.

12312 of 13492 relevant lines covered (91.25%)

26865.48 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();
413,426✔
32
        if (!builtInMemberTable) {
413,426✔
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;
138,462✔
37
        }
38
        //const realMemberTable = thisType.getMemberTable();
39
        //const checkForExistingMembers = realMemberTable && realMemberTable !== builtInMemberTable;
40
        const builtInComponent = this.getMatchingRokuComponent(thisType);
274,964✔
41
        if (!builtInComponent) {
274,964✔
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,019✔
45
        }
46
        if (!this.typedFunctionFactory) {
271,945!
NEW
47
            throw new Error(`Unable to build typed functions - no typed function factory`);
×
48
        }
49
        const interfacesToLoop = builtInComponent.interfaces ?? [builtInComponent];
271,945✔
50

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

64
        for (const iface of interfacesToLoop) {
271,945✔
65
            const lowerIfaceName = iface.name.toLowerCase();
440,085✔
66
            const ifaceData = (interfaces[lowerIfaceName] ?? events[lowerIfaceName]) as BRSInterfaceData;
440,085✔
67

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

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

113
    private static getPrimitiveType(typeName: string): BscType {
114
        if (typeName.includes(' or ')) {
5,728,050✔
115
            if (!this.unionTypeFactory) {
43,510!
NEW
116
                throw new Error(`Unable to build union types - no union type factory`);
×
117
            }
118
            // union types!
119
            const unionOfTypeNames = typeName.split(' or ');
43,510✔
120
            return this.unionTypeFactory(unionOfTypeNames.map(name => this.getPrimitiveType(name)));
87,020✔
121
        }
122
        const returnType = this.primitiveTypeInstanceCache.get(typeName.toLowerCase());
5,684,540✔
123
        if (!returnType) {
5,684,540✔
124
            if (!this.getLookupTable) {
180,113!
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 });
180,113!
128
        }
129

130
        return returnType;
5,504,427✔
131
    }
132

133
    static getMatchingRokuComponentName(theType: BscType) {
134
        if (isStringType(theType)) {
546,919✔
135
            return 'roString';
54✔
136
        } else if (isIntegerType(theType)) {
546,865✔
137
            return 'roInt';
22✔
138
        } else if (isBooleanType(theType)) {
546,843✔
139
            return 'roBoolean';
10✔
140
        } else if (isFloatType(theType)) {
546,833✔
141
            return 'roFloat';
10✔
142
        } else if (isDoubleType(theType)) {
546,823✔
143
            return 'roDouble';
4✔
144
        } else if (isLongIntegerType(theType)) {
546,819!
NEW
145
            return 'roLongInteger';
×
146
        } else if (isInvalidType(theType)) {
546,819!
NEW
147
            return 'roInvalid';
×
148
        } else if (isCallableType(theType)) {
546,819✔
149
            return 'roFunction';
20✔
150
        } else if (isAssociativeArrayType(theType)) {
546,799✔
151
            return 'roAssociativeArray';
508✔
152
        } else if (isArrayType(theType)) {
546,291✔
153
            return 'roArray';
36✔
154
        } else if (isEnumMemberType(theType)) {
546,255✔
155
            return this.getMatchingRokuComponentName(theType.underlyingType);
10✔
156
        } else if (isInterfaceType(theType)) {
546,245✔
157
            return theType.name;
526,258✔
158
        } else if (isComponentType(theType)) {
19,987✔
159
            return 'roSGNode';
18,642✔
160
        }
161
    }
162

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

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