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

More performance fixes #936

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2036db4
Keep a per-scope type cache
markwpearce Oct 24, 2023
5a78b39
Much Smarter Validation scheme - only file-level unresolved functions…
markwpearce Oct 24, 2023
8804e14
remove logging
markwpearce Oct 24, 2023
11433d3
Make sure extra data is passed back from a cache
markwpearce Oct 24, 2023
370c84f
fixed lint error
markwpearce Oct 24, 2023
97c1d9f
A few small fixes
markwpearce Oct 24, 2023
2e0ca0c
Fixed ifDraw2d.DrawScaledObject overload
markwpearce Oct 24, 2023
5d8bcc9
re-scraped docs
markwpearce Oct 24, 2023
034184a
More granularity for re-validations
markwpearce Oct 24, 2023
d424138
Removed testing and commented out code
markwpearce Oct 24, 2023
1d49708
Minimize namespace link operations
markwpearce Oct 25, 2023
90c8a4e
Fighting with typecache... I will revert it
markwpearce Oct 26, 2023
c6c249d
Fixed bugs.. reverted misguided scope-type-caching
markwpearce Oct 26, 2023
b529967
Reworked validation to only validate sections that have unresolved de…
markwpearce Oct 27, 2023
cf53fc7
Moved all Validation segmenting code to its own class
markwpearce Oct 29, 2023
9ed3a01
Moved all scope validation to happen on walks
markwpearce Oct 30, 2023
99b089d
re-added all the statement lookup maps to the namespace container
markwpearce Nov 1, 2023
e2d9379
Reworked Scope.diagnosticDetectShadowedLocalVars() to only do type lo…
markwpearce Nov 2, 2023
5b5c844
Adds a lot of logging around timing and validation
markwpearce Nov 2, 2023
0341f8d
Removed mistakenly cached brsFile.validationSegments
markwpearce Nov 2, 2023
7d9fe64
Removed unused code paths and console logs
markwpearce Nov 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
146 changes: 75 additions & 71 deletions benchmarks/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion scripts/.cache.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions scripts/scrape-roku-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,9 @@ class Runner {
// fix ifSGNodeField overloads
fixOverloadedMethod(this.result.interfaces.ifsgnodefield, 'observeField');
fixOverloadedMethod(this.result.interfaces.ifsgnodefield, 'observeFieldScoped');

// fix ifdraw2d overloads
fixOverloadedMethod(this.result.interfaces.ifdraw2d, 'drawScaledObject');
}
}

Expand Down
153 changes: 153 additions & 0 deletions src/AstValidationSegmenter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import type { DottedGetExpression, TypeExpression, VariableExpression } from '.';
import { SymbolTypeFlag } from './SymbolTable';
import { UnresolvedNodeSet } from './UnresolvedNodeSet';
import { isBody, isClassStatement, isCommentStatement, isInterfaceStatement, isNamespaceStatement } from './astUtils/reflection';
import { ChildrenSkipper, WalkMode, createVisitor } from './astUtils/visitors';
import type { ExtraSymbolData, GetTypeOptions } from './interfaces';
import type { AstNode } from './parser/AstNode';
import { util } from './util';

// eslint-disable-next-line no-bitwise
export const InsideSegmentWalkMode = WalkMode.visitStatements | WalkMode.visitExpressions | WalkMode.recurseChildFunctions;

export class AstValidationSegmenter {

public unresolvedSegments = new Map<AstNode, UnresolvedNodeSet>();
public validatedSegments = new Map<AstNode, boolean>();
public segmentsForValidation = new Array<AstNode>();
public singleValidationSegments = new Set<AstNode>();
public ast: AstNode;


reset() {
this.unresolvedSegments.clear();
this.validatedSegments.clear();
this.singleValidationSegments.clear();
this.segmentsForValidation = [];
}

processTree(ast: AstNode) {
this.reset();

ast?.walk((segment) => {
this.checkSegmentWalk(segment);
}, {
walkMode: WalkMode.visitStatements
});
}


checkExpressionForUnresolved(segment: AstNode, expression: VariableExpression | DottedGetExpression | TypeExpression) {
if (!expression || isCommentStatement(expression)) {
return false;
}
const flag = util.isInTypeExpression(expression) ? SymbolTypeFlag.typetime : SymbolTypeFlag.runtime;
const options: GetTypeOptions = { flags: flag, onlyCacheResolvedTypes: true };
const nodeType = expression.getType(options);
if (!nodeType.isResolvable()) {
let nodeSet: UnresolvedNodeSet;
if (!this.unresolvedSegments.has(segment)) {
nodeSet = new UnresolvedNodeSet(segment);
this.unresolvedSegments.set(segment, nodeSet);
} else {
nodeSet = this.unresolvedSegments.get(segment);
}
nodeSet.addExpression(expression, options);
return true;
}
return false;
}

checkSegmentWalk = (segment: AstNode) => {
if (isNamespaceStatement(segment) || isBody(segment)) {
return;
}
if (isClassStatement(segment)) {
if (segment.parentClassName) {
this.segmentsForValidation.push(segment.parentClassName);
this.validatedSegments.set(segment.parentClassName, false);
let foundUnresolvedInSegment = this.checkExpressionForUnresolved(segment.parentClassName, segment.parentClassName);
if (!foundUnresolvedInSegment) {
this.singleValidationSegments.add(segment.parentClassName);
}
}
return;
}
if (isInterfaceStatement(segment)) {
if (segment.parentInterfaceName) {
this.segmentsForValidation.push(segment.parentInterfaceName);
this.validatedSegments.set(segment.parentInterfaceName, false);
let foundUnresolvedInSegment = this.checkExpressionForUnresolved(segment.parentInterfaceName, segment.parentInterfaceName);
if (!foundUnresolvedInSegment) {
this.singleValidationSegments.add(segment.parentInterfaceName);
}
}
return;
}

this.segmentsForValidation.push(segment);
this.validatedSegments.set(segment, false);
let foundUnresolvedInSegment = false;
const skipper = new ChildrenSkipper();
segment.walk(createVisitor({
VariableExpression: (expr) => {
const expressionIsUnresolved = this.checkExpressionForUnresolved(segment, expr);
foundUnresolvedInSegment = expressionIsUnresolved || foundUnresolvedInSegment;
skipper.skip();
},
DottedGetExpression: (expr) => {
const expressionIsUnresolved = this.checkExpressionForUnresolved(segment, expr);
foundUnresolvedInSegment = expressionIsUnresolved || foundUnresolvedInSegment;
skipper.skip();
},
TypeExpression: (expr) => {
const expressionIsUnresolved = this.checkExpressionForUnresolved(segment, expr);
foundUnresolvedInSegment = expressionIsUnresolved || foundUnresolvedInSegment;
skipper.skip();
}
}), {
walkMode: InsideSegmentWalkMode,
skipChildren: skipper
});
if (!foundUnresolvedInSegment) {
this.singleValidationSegments.add(segment);
}
};


getSegments(): AstNode[] {
const segmentsToWalkForValidation: AstNode[] = [];
for (const segment of this.segmentsForValidation) {
const unresolvedNodeSet = this.unresolvedSegments.get(segment);
const isSingleValidationSegment = this.singleValidationSegments.has(segment);
const singleValidationSegmentAlreadyValidated = isSingleValidationSegment ? this.validatedSegments.get(segment) : false;
let segmentNeedsRevalidation = !singleValidationSegmentAlreadyValidated;
if (unresolvedNodeSet) {
for (let node of unresolvedNodeSet.nodes) {
const data: ExtraSymbolData = {};
const options: GetTypeOptions = { flags: util.isInTypeExpression(node) ? SymbolTypeFlag.typetime : SymbolTypeFlag.runtime, data: data };
const type = node.getType(options);
if (!type || !type.isResolvable()) {
// the type that we're checking here is not found - force validation
segmentNeedsRevalidation = true;
break;
} else {
const newTypeToCheck = unresolvedNodeSet.addTypeForExpression(node, options, type);
segmentNeedsRevalidation = segmentNeedsRevalidation || newTypeToCheck;
}
}
if (segmentNeedsRevalidation) {
segmentsToWalkForValidation.push(segment);
continue;
}
} else if (segmentNeedsRevalidation) {
segmentsToWalkForValidation.push(segment);
}
}
return segmentsToWalkForValidation;
}

markSegmentAsValidated(segment: AstNode) {
this.validatedSegments.set(segment, true);
}
}
2 changes: 0 additions & 2 deletions src/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -817,11 +817,9 @@ export class Program {
file: file
};
this.plugins.emit('beforeFileValidate', validateFileEvent);

//emit an event to allow plugins to contribute to the file validation process
this.plugins.emit('onFileValidate', validateFileEvent);
file.isValidated = true;

this.plugins.emit('afterFileValidate', validateFileEvent);
}
}
Expand Down