Skip to content

Commit 8ea9617

Browse files
committed
Merge remote-tracking branch 'origin/master' into checkJSFiles
2 parents 0637f24 + 1ae5bef commit 8ea9617

16 files changed

+532
-152
lines changed

src/compiler/binder.ts

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ namespace ts {
259259
case SyntaxKind.ExportAssignment:
260260
return (<ExportAssignment>node).isExportEquals ? "export=" : "default";
261261
case SyntaxKind.BinaryExpression:
262-
switch (getSpecialPropertyAssignmentKind(node)) {
262+
switch (getSpecialPropertyAssignmentKind(node as BinaryExpression)) {
263263
case SpecialPropertyAssignmentKind.ModuleExports:
264264
// module.exports = ...
265265
return "export=";
@@ -2017,30 +2017,28 @@ namespace ts {
20172017
}
20182018
break;
20192019
case SyntaxKind.BinaryExpression:
2020-
if (isInJavaScriptFile(node)) {
2021-
const specialKind = getSpecialPropertyAssignmentKind(node);
2022-
switch (specialKind) {
2023-
case SpecialPropertyAssignmentKind.ExportsProperty:
2024-
bindExportsPropertyAssignment(<BinaryExpression>node);
2025-
break;
2026-
case SpecialPropertyAssignmentKind.ModuleExports:
2027-
bindModuleExportsAssignment(<BinaryExpression>node);
2028-
break;
2029-
case SpecialPropertyAssignmentKind.PrototypeProperty:
2030-
bindPrototypePropertyAssignment(<BinaryExpression>node);
2031-
break;
2032-
case SpecialPropertyAssignmentKind.ThisProperty:
2033-
bindThisPropertyAssignment(<BinaryExpression>node);
2034-
break;
2035-
case SpecialPropertyAssignmentKind.Property:
2036-
bindStaticPropertyAssignment(<BinaryExpression>node);
2037-
break;
2038-
case SpecialPropertyAssignmentKind.None:
2039-
// Nothing to do
2040-
break;
2041-
default:
2042-
Debug.fail("Unknown special property assignment kind");
2043-
}
2020+
const specialKind = getSpecialPropertyAssignmentKind(node as BinaryExpression);
2021+
switch (specialKind) {
2022+
case SpecialPropertyAssignmentKind.ExportsProperty:
2023+
bindExportsPropertyAssignment(<BinaryExpression>node);
2024+
break;
2025+
case SpecialPropertyAssignmentKind.ModuleExports:
2026+
bindModuleExportsAssignment(<BinaryExpression>node);
2027+
break;
2028+
case SpecialPropertyAssignmentKind.PrototypeProperty:
2029+
bindPrototypePropertyAssignment(<BinaryExpression>node);
2030+
break;
2031+
case SpecialPropertyAssignmentKind.ThisProperty:
2032+
bindThisPropertyAssignment(<BinaryExpression>node);
2033+
break;
2034+
case SpecialPropertyAssignmentKind.Property:
2035+
bindStaticPropertyAssignment(<BinaryExpression>node);
2036+
break;
2037+
case SpecialPropertyAssignmentKind.None:
2038+
// Nothing to do
2039+
break;
2040+
default:
2041+
Debug.fail("Unknown special property assignment kind");
20442042
}
20452043
return checkStrictModeBinaryExpression(<BinaryExpression>node);
20462044
case SyntaxKind.CatchClause:

src/compiler/checker.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8525,16 +8525,9 @@ namespace ts {
85258525
}
85268526
}
85278527
else if (target.flags & TypeFlags.IndexedAccess) {
8528-
// if we have indexed access types with identical index types, see if relationship holds for
8529-
// the two object types.
8530-
if (source.flags & TypeFlags.IndexedAccess && (<IndexedAccessType>source).indexType === (<IndexedAccessType>target).indexType) {
8531-
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, reportErrors)) {
8532-
return result;
8533-
}
8534-
}
85358528
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
85368529
// A is the apparent type of S.
8537-
const constraint = getBaseConstraintOfType(target);
8530+
const constraint = getConstraintOfType(<IndexedAccessType>target);
85388531
if (constraint) {
85398532
if (result = isRelatedTo(source, constraint, reportErrors)) {
85408533
errorInfo = saveErrorInfo;
@@ -8581,6 +8574,13 @@ namespace ts {
85818574
return result;
85828575
}
85838576
}
8577+
else if (target.flags & TypeFlags.IndexedAccess && (<IndexedAccessType>source).indexType === (<IndexedAccessType>target).indexType) {
8578+
// if we have indexed access types with identical index types, see if relationship holds for
8579+
// the two object types.
8580+
if (result = isRelatedTo((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType, reportErrors)) {
8581+
return result;
8582+
}
8583+
}
85848584
}
85858585
else {
85868586
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>target).target) {
@@ -11929,8 +11929,8 @@ namespace ts {
1192911929
// If this is a function in a JS file, it might be a class method. Check if it's the RHS
1193011930
// of a x.prototype.y = function [name]() { .... }
1193111931
if (container.kind === SyntaxKind.FunctionExpression &&
11932-
isInJavaScriptFile(container.parent) &&
11933-
getSpecialPropertyAssignmentKind(container.parent) === SpecialPropertyAssignmentKind.PrototypeProperty) {
11932+
container.parent.kind === SyntaxKind.BinaryExpression &&
11933+
getSpecialPropertyAssignmentKind(container.parent as BinaryExpression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
1193411934
// Get the 'x' of 'x.prototype.y = f' (here, 'f' is 'container')
1193511935
const className = (((container.parent as BinaryExpression) // x.prototype.y = f
1193611936
.left as PropertyAccessExpression) // x.prototype.y
@@ -21641,7 +21641,7 @@ namespace ts {
2164121641
}
2164221642

2164321643
function getSpecialPropertyAssignmentSymbolFromEntityName(entityName: EntityName | PropertyAccessExpression) {
21644-
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent);
21644+
const specialPropertyAssignmentKind = getSpecialPropertyAssignmentKind(entityName.parent.parent as BinaryExpression);
2164521645
switch (specialPropertyAssignmentKind) {
2164621646
case SpecialPropertyAssignmentKind.ExportsProperty:
2164721647
case SpecialPropertyAssignmentKind.PrototypeProperty:

src/compiler/utilities.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,13 +1450,10 @@ namespace ts {
14501450

14511451
/// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property
14521452
/// assignments we treat as special in the binder
1453-
export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind {
1453+
export function getSpecialPropertyAssignmentKind(expression: ts.BinaryExpression): SpecialPropertyAssignmentKind {
14541454
if (!isInJavaScriptFile(expression)) {
14551455
return SpecialPropertyAssignmentKind.None;
14561456
}
1457-
if (expression.kind !== SyntaxKind.BinaryExpression) {
1458-
return SpecialPropertyAssignmentKind.None;
1459-
}
14601457
const expr = <BinaryExpression>expression;
14611458
if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || expr.left.kind !== SyntaxKind.PropertyAccessExpression) {
14621459
return SpecialPropertyAssignmentKind.None;

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 114 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,11 @@ namespace ts.projectSystem {
176176
}
177177
};
178178

179-
export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken) {
179+
export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken, throttleWaitMilliseconds?: number) {
180180
if (typingsInstaller === undefined) {
181181
typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
182182
}
183-
return new TestSession(host, cancellationToken || server.nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ projectServiceEventHandler !== undefined, projectServiceEventHandler);
183+
return new TestSession(host, cancellationToken || server.nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ projectServiceEventHandler !== undefined, projectServiceEventHandler, throttleWaitMilliseconds);
184184
}
185185

186186
export interface CreateProjectServiceParameters {
@@ -547,6 +547,49 @@ namespace ts.projectSystem {
547547
readonly getEnvironmentVariable = notImplemented;
548548
}
549549

550+
/**
551+
* Test server cancellation token used to mock host token cancellation requests.
552+
* The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls
553+
* should be made before canceling the token. The id of the request to cancel should be set with
554+
* setRequestToCancel();
555+
*/
556+
export class TestServerCancellationToken implements server.ServerCancellationToken {
557+
private currentId = -1;
558+
private requestToCancel = -1;
559+
private isCancellationRequestedCount = 0;
560+
561+
constructor(private cancelAfterRequest = 0) {
562+
}
563+
564+
setRequest(requestId: number) {
565+
this.currentId = requestId;
566+
}
567+
568+
setRequestToCancel(requestId: number) {
569+
this.resetToken();
570+
this.requestToCancel = requestId;
571+
}
572+
573+
resetRequest(requestId: number) {
574+
assert.equal(requestId, this.currentId, "unexpected request id in cancellation");
575+
this.currentId = undefined;
576+
}
577+
578+
isCancellationRequested() {
579+
this.isCancellationRequestedCount++;
580+
// If the request id is the request to cancel and isCancellationRequestedCount
581+
// has been met then cancel the request. Ex: cancel the request if it is a
582+
// nav bar request & isCancellationRequested() has already been called three times.
583+
return this.requestToCancel === this.currentId && this.isCancellationRequestedCount >= this.cancelAfterRequest;
584+
}
585+
586+
resetToken() {
587+
this.currentId = -1;
588+
this.isCancellationRequestedCount = 0;
589+
this.requestToCancel = -1;
590+
}
591+
}
592+
550593
export function makeSessionRequest<T>(command: string, args: T) {
551594
const newRequest: protocol.Request = {
552595
seq: 0,
@@ -3384,6 +3427,7 @@ namespace ts.projectSystem {
33843427
},
33853428
resetRequest: noop
33863429
};
3430+
33873431
const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken);
33883432

33893433
expectedRequestId = session.getNextSeq();
@@ -3422,22 +3466,7 @@ namespace ts.projectSystem {
34223466
})
34233467
};
34243468

3425-
let requestToCancel = -1;
3426-
const cancellationToken: server.ServerCancellationToken = (function(){
3427-
let currentId: number;
3428-
return <server.ServerCancellationToken>{
3429-
setRequest(requestId) {
3430-
currentId = requestId;
3431-
},
3432-
resetRequest(requestId) {
3433-
assert.equal(requestId, currentId, "unexpected request id in cancellation");
3434-
currentId = undefined;
3435-
},
3436-
isCancellationRequested() {
3437-
return requestToCancel === currentId;
3438-
}
3439-
};
3440-
})();
3469+
const cancellationToken = new TestServerCancellationToken();
34413470
const host = createServerHost([f1, config]);
34423471
const session = createSession(host, /*typingsInstaller*/ undefined, () => {}, cancellationToken);
34433472
{
@@ -3472,13 +3501,13 @@ namespace ts.projectSystem {
34723501
host.clearOutput();
34733502

34743503
// cancel previously issued Geterr
3475-
requestToCancel = getErrId;
3504+
cancellationToken.setRequestToCancel(getErrId);
34763505
host.runQueuedTimeoutCallbacks();
34773506

34783507
assert.equal(host.getOutput().length, 1, "expect 1 message");
34793508
verifyRequestCompleted(getErrId, 0);
34803509

3481-
requestToCancel = -1;
3510+
cancellationToken.resetToken();
34823511
}
34833512
{
34843513
const getErrId = session.getNextSeq();
@@ -3495,12 +3524,12 @@ namespace ts.projectSystem {
34953524
assert.equal(e1.event, "syntaxDiag");
34963525
host.clearOutput();
34973526

3498-
requestToCancel = getErrId;
3527+
cancellationToken.setRequestToCancel(getErrId);
34993528
host.runQueuedImmediateCallbacks();
35003529
assert.equal(host.getOutput().length, 1, "expect 1 message");
35013530
verifyRequestCompleted(getErrId, 0);
35023531

3503-
requestToCancel = -1;
3532+
cancellationToken.resetToken();
35043533
}
35053534
{
35063535
const getErrId = session.getNextSeq();
@@ -3523,7 +3552,7 @@ namespace ts.projectSystem {
35233552
assert.equal(e2.event, "semanticDiag");
35243553
verifyRequestCompleted(getErrId, 1);
35253554

3526-
requestToCancel = -1;
3555+
cancellationToken.resetToken();
35273556
}
35283557
{
35293558
const getErr1 = session.getNextSeq();
@@ -3558,6 +3587,68 @@ namespace ts.projectSystem {
35583587
return JSON.parse(server.extractMessage(host.getOutput()[n]));
35593588
}
35603589
});
3590+
it("Lower priority tasks are cancellable", () => {
3591+
const f1 = {
3592+
path: "/a/app.ts",
3593+
content: `{ let x = 1; } var foo = "foo"; var bar = "bar"; var fooBar = "fooBar";`
3594+
};
3595+
const config = {
3596+
path: "/a/tsconfig.json",
3597+
content: JSON.stringify({
3598+
compilerOptions: {}
3599+
})
3600+
};
3601+
const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3);
3602+
const host = createServerHost([f1, config]);
3603+
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken, /*throttleWaitMilliseconds*/ 0);
3604+
{
3605+
session.executeCommandSeq(<protocol.OpenRequest>{
3606+
command: "open",
3607+
arguments: { file: f1.path }
3608+
});
3609+
3610+
// send navbar request (normal priority)
3611+
session.executeCommandSeq(<protocol.NavBarRequest>{
3612+
command: "navbar",
3613+
arguments: { file: f1.path }
3614+
});
3615+
3616+
// ensure the nav bar request can be canceled
3617+
verifyExecuteCommandSeqIsCancellable(<protocol.NavBarRequest>{
3618+
command: "navbar",
3619+
arguments: { file: f1.path }
3620+
});
3621+
3622+
// send outlining spans request (normal priority)
3623+
session.executeCommandSeq(<protocol.OutliningSpansRequest>{
3624+
command: "outliningSpans",
3625+
arguments: { file: f1.path }
3626+
});
3627+
3628+
// ensure the outlining spans request can be canceled
3629+
verifyExecuteCommandSeqIsCancellable(<protocol.OutliningSpansRequest>{
3630+
command: "outliningSpans",
3631+
arguments: { file: f1.path }
3632+
});
3633+
}
3634+
3635+
function verifyExecuteCommandSeqIsCancellable<T extends server.protocol.Request>(request: Partial<T>) {
3636+
// Set the next request to be cancellable
3637+
// The cancellation token will cancel the request the third time
3638+
// isCancellationRequested() is called.
3639+
cancellationToken.setRequestToCancel(session.getNextSeq());
3640+
let operationCanceledExceptionThrown = false;
3641+
3642+
try {
3643+
session.executeCommandSeq(request);
3644+
}
3645+
catch (e) {
3646+
assert(e instanceof OperationCanceledException);
3647+
operationCanceledExceptionThrown = true;
3648+
}
3649+
assert(operationCanceledExceptionThrown, "Operation Canceled Exception not thrown for request: " + JSON.stringify(request));
3650+
}
3651+
});
35613652
});
35623653

35633654
describe("occurence highlight on string", () => {

src/server/editorServices.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ namespace ts.server {
271271
public readonly cancellationToken: HostCancellationToken,
272272
public readonly useSingleInferredProject: boolean,
273273
readonly typingsInstaller: ITypingsInstaller = nullTypingsInstaller,
274-
private readonly eventHandler?: ProjectServiceEventHandler) {
274+
private readonly eventHandler?: ProjectServiceEventHandler,
275+
public readonly throttleWaitMilliseconds?: number) {
275276

276277
Debug.assert(!!host.createHash, "'ServerHost.createHash' is required for ProjectService");
277278

src/server/lsHost.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace ts.server {
1616
readonly realpath?: (path: string) => string;
1717

1818
constructor(private readonly host: ServerHost, private readonly project: Project, private readonly cancellationToken: HostCancellationToken) {
19+
this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds);
1920
this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames);
2021

2122
if (host.trace) {

src/server/session.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,8 @@ namespace ts.server {
338338
private hrtime: (start?: number[]) => number[],
339339
protected logger: Logger,
340340
protected readonly canUseEvents: boolean,
341-
eventHandler?: ProjectServiceEventHandler) {
341+
eventHandler?: ProjectServiceEventHandler,
342+
private readonly throttleWaitMilliseconds?: number) {
342343

343344
this.eventHander = canUseEvents
344345
? eventHandler || (event => this.defaultEventHandler(event))
@@ -353,7 +354,7 @@ namespace ts.server {
353354
isCancellationRequested: () => cancellationToken.isCancellationRequested()
354355
};
355356
this.errorCheck = new MultistepOperation(multistepOperationHost);
356-
this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander);
357+
this.projectService = new ProjectService(host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, this.eventHander, this.throttleWaitMilliseconds);
357358
this.gcTimer = new GcTimer(host, /*delay*/ 7000, logger);
358359
}
359360

0 commit comments

Comments
 (0)