Skip to content

ts.TypeChecker.typeToTypeNode goes in infinite loop eating up CPU and Memory. #29564

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

Closed
abhijitparkhi1983 opened this issue Jan 24, 2019 · 18 comments · Fixed by #31586
Closed
Assignees
Labels
Bug A bug in TypeScript
Milestone

Comments

@abhijitparkhi1983
Copy link

TypeScript Version: 3.2.2

Search Terms:
ts.TypeChecker.typeToTypeNode,
ts.TypeLiteralNode,
ts.NodeBuilderFlags.NoTruncation

Description:

We are trying to use typescript compiler for getting the type of ObjectLiteralExpression and for the purpose we utilize following code:

getApparentTypeForObjectLiteralExpression(node : ts.Node) : {type : ts.Type, typeNode : ts.TypeNode} {
        let type : ts.Type = this.checker.getTypeAtLocation(node);
        let typeNode = this.checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.NoTruncation);

        if(ts.isTypeLiteralNode(typeNode))
        {
            var memberCount = typeNode.members.length;
            var name : string;
            typeNode.members.forEach(member => {
                name +=  member.name;
                name += " , ";
            });
        }
        return {type, typeNode}
    }

When we run the above code over the attached file SEA3DAmmo.txt (rename file to SEA3DAmmo.js), the line this.checker.typeToTypeNode goes into infinite loop and the application consumes high CPU and Memory and the call does not return back atall.

This is an absolute blocker, may be i am doing some mistake.
I also tried running with following options:
this.checker.typeToTypeNode(type, node, ts.NodeBuilderFlags.NoTruncation);
or
this.checker.typeToTypeNode(type, node.parent, ts.NodeBuilderFlags.NoTruncation);

Expected behavior:
It should return value and should not go in infinite loop.

SEA3DAmmo.txt

@abhijitparkhi1983
Copy link
Author

@weswigham , it will be very helpful if you can guide me on this blocker issue.
The compilation options which i am adding while creating the ts.createProgram

var options: ts.CompilerOptions = {
                noEmitOnError: true
                , strict: true
                , target: ts.ScriptTarget.Latest
                , module: ts.ModuleKind.CommonJS
                , skipLibCheck: true
                , experimentalDecorators: true
                , emitDecoratorMetadata : true
                , allowJs: true
                , noErrorTruncation : true
              };

@weswigham
Copy link
Member

weswigham commented Jan 24, 2019

As a workaround, I'd try setting noErrorTruncation: false and seeing if you get some (truncated) output (and not passing ts.NodeBuilderFlags.NoTruncation).

@abhijitparkhi1983
Copy link
Author

abhijitparkhi1983 commented Jan 24, 2019

If i do not pass ts.NodeBuilderFlags.NoTruncation, it does work with truncated data. But I want the Non Truncated data which is equally important.

@weswigham
Copy link
Member

Try gradually bumping up the (ts as any).defaultMaximumTruncationLength value so you get more type-literal output so you can see what type is making us take so long to print.

@abhijitparkhi1983
Copy link
Author

it stops at the property at line 87 from the file attached.

getGravity: function () {

		return this.gravity;

	},

@weswigham
Copy link
Member

That's with the default length, yeah? With a larger truncation length (eg, 1000), where's the truncation occur (that'll be more indicative of where the problem is when truncation is off)?

@abhijitparkhi1983
Copy link
Author

Ya thats with the default length i guess. But i am not aware how to set defaultMaximumTruncationLength.
Where do we set it ? In compilation options?

@weswigham
Copy link
Member

In your code that's running this, just set (ts as any).defaultMaximumTruncationLength = 1000 (where ts is whatever you've imported typescript as) before calling the typeToString function. It's an internal member of our namespace.

@abhijitparkhi1983
Copy link
Author

Without setting the above value and without passing ts.NodeBuilderFlags.NoTruncation , this.checker.typeToTypeNode(type) returns with 12 members where 11th member has an entry for 23 more.

When i set (ts as any).defaultMaximumTruncationLength = 1000 before calling this.checker.typeToTypeNode(type), the number of members now shown is 19 where 18th member has 17 more.

If i bump the value of (ts as any).defaultMaximumTruncationLength = 1000 any further, i am still getting 19 members alone

@weswigham
Copy link
Member

the number of members now shown is 19 where 18th member has 17 more.

What's the actual display of this type?

@abhijitparkhi1983
Copy link
Author

abhijitparkhi1983 commented Jan 25, 2019

I am not sure what do u mean by display of this type?
Its a ObjectLiteralExpression getting created, and the members are properties/functions.

You could see the definition of this type in the file which i had attached.

@weswigham
Copy link
Member

weswigham commented Jan 25, 2019 via email

@abhijitparkhi1983
Copy link
Author

Hi @weswigham sorry for coming late over this.
If i understand it right, what you mean to say that the ts.createPrinter for typeNode in the above code i have shared. Right ?.

if so, by setting (ts as any).defaultMaximumTruncationLength = 1000; and using following code

let typeNode = this.checker.typeToTypeNode(type);
        if(printRequired)
        {
            var test = this.printer.printNode(ts.EmitHint.Unspecified, typeNode, typeNode.getSourceFile());
           
        }

the value we get in test is as below:

{ world: null; rigidBodies: never[]; rigidBodiesTarget: never[]; rigidBodiesEnabled: never[]; constraints: never[]; vehicles: never[]; vehiclesWheels: never[]; ACTIVE: number; ISLAND_SLEEPING: number; WANTS_DEACTIVATION: number; DISABLE_DEACTIVATION: number; DISABLE_SIMULATION: number; VERSION: number; init: typeof init; setGravity: typeof setGravity; getGravity: () => any; setEnabledRigidBody: (rb: any, enabled: any) => { world: null; rigidBodies: never[]; rigidBodiesTarget: never[]; rigidBodiesEnabled: never[]; constraints: never[]; vehicles: never[]; vehiclesWheels: never[]; ACTIVE: number; ISLAND_SLEEPING: number; WANTS_DEACTIVATION: number; DISABLE_DEACTIVATION: number; DISABLE_SIMULATION: number; VERSION: number; init: typeof init; setGravity: typeof setGravity; getGravity: () => any; setEnabledRigidBody: ...; getEnabledRigidBody: (rb: any) => never; addRigidBody: (rb: any, target: any, enabled: any) => { world: null; rigidBodies: never[]; rigidBodiesTarget: never[]; rigidBodiesEnabled: never[]; constraints: never[]; vehicles: never[]; vehiclesWheels: never[]; ACTIVE: number; ISLAND_SLEEPING: number; WANTS_DEACTIVATION: number; DISABLE_DEACTIVATION: number; DISABLE_SIMULATION: number; VERSION: number; init: typeof init; setGravity: typeof setGravity; getGravity: () => any; setEnabledRigidBody: ...; getEnabledRigidBody: (rb: any) => never; addRigidBody: ...; removeRigidBody: (rb: any, destroy: any) => { world: null; rigidBodies: never[]; rigidBodiesTarget: never[]; rigidBodiesEnabled: never[]; constraints: never[]; vehicles: never[]; vehiclesWheels: never[]; ACTIVE: number; ISLAND_SLEEPING: number; WANTS_DEACTIVATION: number; DISABLE_DEACTIVATION: number; DISABLE_SIMULATION: number; VERSION: number; init: typeof init; setGravity: typeof setGravity; getGravity: () => any; setEnabledRigidBody: ...; getEnabledRigidBody: (rb: any) => never; addRigidBody: ...; removeRigidBody: ...; containsRigidBody: (rb: any) => boolean; addConstraint: (ctrt: any, disableCollisionsBetweenBodies: any) => { world: null; rigidBodies: never[]; rigidBodiesTarget: never[]; rigidBodiesEnabled: never[]; constraints: never[]; vehicles: never[]; vehiclesWheels: never[]; ACTIVE: number; ISLAND_SLEEPING: number; WANTS_DEACTIVATION: number; DISABLE_DEACTIVATION: number; DISABLE_SIMULATION: number; VERSION: number; init: typeof init; setGravity: typeof setGravity; getGravity: () => any; setEnabledRigidBody: ...; getEnabledRigidBody: (rb: any) => never; ... 16 more ...; update: (delta: any, iteration: any, fixedDelta: any) => { ...; }; }; ... 12 more ...; update: (delta: any, iteration: any, fixedDelta: any) => { ...; }; }; ... 14 more ...; update: (delta: any, iteration: any, fixedDelta: any) => { ...; }; }; ... 15 more ...; update: (delta: any, iteration: any, fixedDelta: any) => { ...; }; } | undefined; ... 17 more ...; update: (delta: any, iteration: any, fixedDelta: any) => { ...; }; }

I am not sure if this is what you were looking out for.

@weswigham
Copy link
Member

Ya. Looks like we're printing the this return types structurally, which is not great here.

@abhijitparkhi1983
Copy link
Author

Any ideas to go ahead with this problem ?

@weswigham
Copy link
Member

Hmmm, well - if you explicitly give the return type with a type annotation of some kind it'll at least short circuit the printout.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Feb 5, 2019
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Mar 14, 2019
@weswigham
Copy link
Member

@sandersn I suspect this is identical to the issue you were looking at with js object printback and this?

@weswigham
Copy link
Member

@abhijitparkhi1983 I've closed this, but if you could validate that your full example works in your local setup with our next nightly (ie, the nightly from anytime after tomorrow), that'd be swell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants