Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefix task function name #34

Merged
merged 5 commits into from
Feb 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ Result:
end sub
```

### Tasks (`m.top.functionName`)
BrightScript Task objects have a special `m.top.functionName` property that specifies which function should be run during the task. ropm will find all instances of `m.top.functionName = "<anything>"` and add the prefix to the beginning of the string.

The syntax must be exactly `m.top.functionName = ` followed by a string (i.e. `m.top.functionName = "taskCallback"`). ropm will skip the statement if anything other than a string is found to the right-hand-side of the equals sign. If you dynamically generate the value for `m.top.functionName`, or assign it in some other fashion, consider using the [ROPM_PREFIX](#ropm_prefix-source-literal) source literal instead.

### Never-prefixed functions
Due to their special handling within the Roku architecture, the following functions will never be prefixed:
Expand Down
27 changes: 26 additions & 1 deletion src/prefixer/File.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class File {
}

/**
* The full pkg path to the file (minus the `pkg:/` protocol since we never actually need that part
* The full pkg path to the file (minus the `pkg:/` protocol since we never actually need that part)
*/
public pkgPath: string;

Expand Down Expand Up @@ -99,6 +99,15 @@ export class File {
offset: number;
}>;

/**
* Every instance of `m.top.functionName = "<anything>".
* Used only if this file is referenced by a Task
*/
public taskFunctionNameAssignments = [] as Array<{
name: string;
offset: number;
}>;

/**
* A list of locations in this file that DECLARE a component (i.e. <component name="<component_name"
*/
Expand Down Expand Up @@ -209,6 +218,7 @@ export class File {
this.findCreateObjectComponentReferences();
this.findCreateChildComponentReferences();
this.findObserveFieldFunctionCalls();
this.findTaskFunctionNameAssignments();
this.walkAst();
} else if (this.isXmlFile) {
this.findFilePathStrings();
Expand Down Expand Up @@ -557,6 +567,21 @@ export class File {
}
}
}

/**
* Look for every statement that looks exactly like the default task functionName assignment.
*/
private findTaskFunctionNameAssignments() {
//look for any string containing `m.top.functionName = "<anything>"`
const regexp = /(m\s*\.\s*top\s*\.\s*functionName\s*=\s*")(.*?)"/gi;
let match: RegExpExecArray | null;
while (match = regexp.exec(this.bscFile.fileContents)) {
this.taskFunctionNameAssignments.push({
offset: match.index + match[1].length,
name: match[2]
});
}
}
}

export interface Edit {
Expand Down
101 changes: 101 additions & 0 deletions src/prefixer/ModuleManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,107 @@ describe('ModuleManager', () => {
});
});

it('prefixes m.top.functionName for files NOT imported by a Task', async () => {
await testProcess({
'logger:source/lib.brs': [
trim`
sub a()
m.top.functionName = "doSomething"
end sub
`,
trim`
sub logger_a()
m.top.functionName = "logger_doSomething"
end sub
`]
});
});

it('prefixes direct task reference from component in same module', async () => {
await testProcess({
'logger:components/SimpleTask.brs': [
trim`
sub init()
m.top.functionName = "doSomething"
end sub
`, trim`
sub init()
m.top.functionName = "logger_doSomething"
end sub
`
],
//directly extends task
'logger:components/SimpleTask.xml': [
trim`
<?xml version="1.0" encoding="utf-8" ?>
<component name="SimpleTask" extends="Task">
<script uri="SimpleTask.brs" />
</component>
`
]
});
});

it('prefixes indirect task reference from component in same module', async () => {
await testProcess({
'logger:components/HardTask.brs': [
trim`
sub init()
m.top.functionName = "doSomething"
end sub
`, trim`
sub init()
m.top.functionName = "logger_doSomething"
end sub
`
],
'logger:components/SimpleTask.xml': [
trim`
<?xml version="1.0" encoding="utf-8" ?>
<component name="SimpleTask" extends="Task">
</component>
`
], 'logger:components/HardTask.xml': [
trim`
<?xml version="1.0" encoding="utf-8" ?>
<component name="HardTask" extends="SimpleTask">
<script uri="HardTask.brs" />
</component>
`
]
});
});

it('prefixes indirect task reference from component in different module', async () => {
await testProcess({
'logger:components/HardTask.brs': [
trim`
sub init()
m.top.functionName = "doSomething"
end sub
`, trim`
sub init()
m.top.functionName = "logger_doSomething"
end sub
`
],
'tasker:components/SimpleTask.xml': [
trim`
<?xml version="1.0" encoding="utf-8" ?>
<component name="SimpleTask" extends="Task">
</component>
`
], 'logger:components/HardTask.xml': [
trim`
<?xml version="1.0" encoding="utf-8" ?>
<component name="HardTask" extends="tasker_SimpleTask">
<script uri="HardTask.brs" />
</component>
`
]
});
});

it('prefixes namespaces and not their child functions or classes', async () => {
await testProcess({
'logger:source/lib.d.bs': [
Expand Down
11 changes: 8 additions & 3 deletions src/prefixer/RopmModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export class RopmModule {
'!**/roku_modules/**/*'
];

public files = [] as File[];

/**
* The name of this module. Users can rename modules on install-time, so this is the folder we must use
*/
Expand Down Expand Up @@ -235,6 +237,7 @@ export class RopmModule {
file.discover(this.program);
}


//create the edits for every file
this.createEdits(noprefixRopmAliases);

Expand Down Expand Up @@ -318,6 +321,11 @@ export class RopmModule {
//only apply prefixes if configured to do so
if (applyOwnPrefix) {

// replace `m.top.functionName = "<anything>"` assignments to support Tasks
for (const ref of file.taskFunctionNameAssignments) {
file.addEdit(ref.offset, ref.offset, prefix);
}

//create an edit for each this-module-owned function
for (const func of file.functionDefinitions) {
const lowerName = func.name.toLowerCase();
Expand Down Expand Up @@ -541,7 +549,4 @@ export class RopmModule {
}
return Object.keys(result);
}


public files = [] as File[];
}