Skip to content

Commit

Permalink
feat: allow unsupported syntax (#171)
Browse files Browse the repository at this point in the history
* Add syntax-forgiving debug parameter to allow/disallow unsupported syntax
* Add silence-test-output debug parameter to enable/disable test output to console
* Move constant pool code from launcher to root context
* Add common abstract factory for all analysis factories
  • Loading branch information
dstallenberg authored Sep 12, 2023
1 parent 3552746 commit b837ebc
Show file tree
Hide file tree
Showing 55 changed files with 609 additions and 162 deletions.
1 change: 1 addition & 0 deletions libraries/analysis-javascript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from "./lib/cfg/ControlFlowGraphFactory";
export * from "./lib/cfg/ControlFlowGraphVisitor";

export * from "./lib/constant/ConstantPool";
export * from "./lib/constant/ConstantPoolFactory";
export * from "./lib/constant/ConstantPoolManager";
export * from "./lib/constant/ConstantVisitor";

Expand Down
29 changes: 29 additions & 0 deletions libraries/analysis-javascript/lib/Factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
*
* This file is part of SynTest Framework - SynTest JavaScript.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export class Factory {
private _syntaxForgiving: boolean;

constructor(syntaxForgiving: boolean) {
this._syntaxForgiving = syntaxForgiving;
}

get syntaxForgiving() {
return this._syntaxForgiving;
}
}
48 changes: 47 additions & 1 deletion libraries/analysis-javascript/lib/RootContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,20 @@ import { Relation } from "./type/discovery/relation/Relation";
import { TypePool } from "./type/resolving/TypePool";
import TypedEmitter from "typed-emitter";
import { Events } from "./Events";
import { ConstantPoolManager } from "./constant/ConstantPoolManager";
import { Logger, getLogger } from "@syntest/logging";
import { ConstantPoolFactory } from "./constant/ConstantPoolFactory";
import { ConstantPool } from "./constant/ConstantPool";

export class RootContext extends CoreRootContext<t.Node> {
protected static LOGGER: Logger;

protected _exportFactory: ExportFactory;
protected _typeExtractor: TypeExtractor;
protected _typeResolver: TypeModelFactory;

protected _constantPoolFactory: ConstantPoolFactory;

protected _files: string[];
// filepath -> id -> element
protected _elementMap: Map<string, Map<string, Element>>;
Expand All @@ -65,7 +73,8 @@ export class RootContext extends CoreRootContext<t.Node> {
dependencyFactory: DependencyFactory,
exportFactory: ExportFactory,
typeExtractor: TypeExtractor,
typeResolver: TypeModelFactory
typeResolver: TypeModelFactory,
constantPoolFactory: ConstantPoolFactory
) {
super(
rootPath,
Expand All @@ -75,9 +84,11 @@ export class RootContext extends CoreRootContext<t.Node> {
targetFactory,
dependencyFactory
);
RootContext.LOGGER = getLogger("RootContext");
this._exportFactory = exportFactory;
this._typeExtractor = typeExtractor;
this._typeResolver = typeResolver;
this._constantPoolFactory = constantPoolFactory;
}

get rootPath(): string {
Expand Down Expand Up @@ -317,4 +328,39 @@ export class RootContext extends CoreRootContext<t.Node> {

return this._typePool;
}

// TODO cache
private _getContextConstantPool(): ConstantPool {
const constantPool = new ConstantPool();
for (const filepath of this.getFiles()) {
const ast = this.getAbstractSyntaxTree(filepath);
this._constantPoolFactory.extract(filepath, ast, constantPool);
}

return constantPool;
}

// TODO cache
getConstantPoolManager(filepath: string): ConstantPoolManager {
const absolutePath = this.resolvePath(filepath);

RootContext.LOGGER.info("Extracting constants");
const ast = this.getAbstractSyntaxTree(absolutePath);

const targetConstantPool = this._constantPoolFactory.extract(
absolutePath,
ast
);
const contextConstantPool = this._getContextConstantPool();
const dynamicConstantPool = new ConstantPool();

const constantPoolManager = new ConstantPoolManager(
targetConstantPool,
contextConstantPool,
dynamicConstantPool
);

RootContext.LOGGER.info("Extracting constants done");
return constantPoolManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import { ControlFlowGraphFactory as CoreControlFlowGraphFactory } from "@syntest
import { ControlFlowProgram, contractControlFlowProgram } from "@syntest/cfg";

import { ControlFlowGraphVisitor } from "./ControlFlowGraphVisitor";
import { Factory } from "../Factory";

export class ControlFlowGraphFactory
extends Factory
implements CoreControlFlowGraphFactory<t.Node>
{
convert(filePath: string, AST: t.Node): ControlFlowProgram {
const visitor = new ControlFlowGraphVisitor(filePath);
const visitor = new ControlFlowGraphVisitor(filePath, this.syntaxForgiving);
traverse(AST, visitor);

return contractControlFlowProgram(visitor.cfg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
};
}

constructor(filePath: string) {
super(filePath);
constructor(filePath: string, syntaxForgiving: boolean) {
super(filePath, syntaxForgiving);
ControlFlowGraphVisitor.LOGGER = getLogger("ControlFlowGraphVisitor");

this._nodesList = [];
Expand Down Expand Up @@ -365,7 +365,10 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
this._currentParents = [node.id];
}

const subVisitor = new ControlFlowGraphVisitor(this.filePath);
const subVisitor = new ControlFlowGraphVisitor(
this.filePath,
this.syntaxForgiving
);
path.traverse(subVisitor);

if (!subVisitor._nodes.has("ENTRY")) {
Expand Down
48 changes: 48 additions & 0 deletions libraries/analysis-javascript/lib/constant/ConstantPoolFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
*
* This file is part of SynTest Framework - SynTest JavaScript.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { traverse } from "@babel/core";
import * as t from "@babel/types";

import { Factory } from "../Factory";
import { ConstantVisitor } from "./ConstantVisitor";
import { ConstantPool } from "./ConstantPool";

export class ConstantPoolFactory extends Factory {
/**
* Generate function map for specified target.
*
* @param AST The AST of the target
*/
extract(
filePath: string,
AST: t.Node,
constantPool?: ConstantPool | undefined
): ConstantPool {
if (!constantPool) {
constantPool = new ConstantPool();
}
const constantVisitor = new ConstantVisitor(
filePath,
this.syntaxForgiving,
constantPool
);
traverse(AST, constantVisitor);

return constantPool;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ export class ConstantPoolManager {
protected _contextConstantPool: ConstantPool;
protected _dynamicConstantPool: ConstantPool;

constructor() {
this._targetConstantPool = new ConstantPool();
this._contextConstantPool = new ConstantPool();
this._dynamicConstantPool = new ConstantPool();
constructor(
targetConstantPool: ConstantPool,
contextConstantPool: ConstantPool,
dynamicConstantPool: ConstantPool
) {
this._targetConstantPool = targetConstantPool;
this._contextConstantPool = contextConstantPool;
this._dynamicConstantPool = dynamicConstantPool;
}

public get targetConstantPool(): ConstantPool {
Expand Down
12 changes: 10 additions & 2 deletions libraries/analysis-javascript/lib/constant/ConstantVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ import { ConstantPool } from "./ConstantPool";
export class ConstantVisitor extends AbstractSyntaxTreeVisitor {
protected _constantPool: ConstantPool;

constructor(filePath: string, constantPool: ConstantPool) {
super(filePath);
get constantPool() {
return this._constantPool;
}

constructor(
filePath: string,
syntaxForgiving: boolean,
constantPool: ConstantPool
) {
super(filePath, syntaxForgiving);
this._constantPool = constantPool;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@ import * as t from "@babel/types";
import { DependencyFactory as CoreDependencyFactory } from "@syntest/analysis";

import { DependencyVisitor } from "./DependencyVisitor";
import { Factory } from "../Factory";

/**
* Dependency generator for targets.
*
* @author Dimitri Stallenberg
*/
export class DependencyFactory implements CoreDependencyFactory<t.Node> {
export class DependencyFactory
extends Factory
implements CoreDependencyFactory<t.Node>
{
/**
* Generate function map for specified target.
*
* @param AST The AST of the target
*/
extract(filePath: string, AST: t.Node): string[] {
const visitor = new DependencyVisitor(filePath);
const visitor = new DependencyVisitor(filePath, this.syntaxForgiving);

traverse(AST, visitor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export class DependencyVisitor extends AbstractSyntaxTreeVisitor {

private _imports: Set<string>;

constructor(filePath: string) {
super(filePath);
constructor(filePath: string, syntaxForgiving: boolean) {
super(filePath, syntaxForgiving);
DependencyVisitor.LOGGER = getLogger("DependencyVisitor");
this._imports = new Set<string>();
}
Expand Down
10 changes: 7 additions & 3 deletions libraries/analysis-javascript/lib/target/TargetFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ import { TargetFactory as CoreTargetFactory } from "@syntest/analysis";
import { ExportVisitor } from "./export/ExportVisitor";
import { Target } from "./Target";
import { TargetVisitor } from "./TargetVisitor";
import { Factory } from "../Factory";

/**
* TargetFactory for Javascript.
*
* @author Dimitri Stallenberg
*/
export class TargetFactory implements CoreTargetFactory<t.Node> {
export class TargetFactory
extends Factory
implements CoreTargetFactory<t.Node>
{
/**
* Generate function map for specified target.
*
Expand All @@ -40,12 +44,12 @@ export class TargetFactory implements CoreTargetFactory<t.Node> {
*/
extract(filePath: string, AST: t.Node): Target {
// bit sad that we have to do this twice, but we need to know the exports
const exportVisitor = new ExportVisitor(filePath);
const exportVisitor = new ExportVisitor(filePath, this.syntaxForgiving);

traverse(AST, exportVisitor);

const exports = exportVisitor.exports;
const visitor = new TargetVisitor(filePath, exports);
const visitor = new TargetVisitor(filePath, this.syntaxForgiving, exports);

traverse(AST, visitor);

Expand Down
36 changes: 17 additions & 19 deletions libraries/analysis-javascript/lib/target/TargetVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export class TargetVisitor extends AbstractSyntaxTreeVisitor {

private _subTargets: SubTarget[];

constructor(filePath: string, exports: Export[]) {
super(filePath);
constructor(filePath: string, syntaxForgiving: boolean, exports: Export[]) {
super(filePath, syntaxForgiving);
TargetVisitor.LOGGER = getLogger("TargetVisitor");
this._exports = exports;
this._subTargets = [];
Expand Down Expand Up @@ -234,14 +234,18 @@ export class TargetVisitor extends AbstractSyntaxTreeVisitor {
);
}
}
case "ReturnStatement": {
// e.g. return class {}
// e.g. return function () {}
// e.g. return () => {}
return "id" in path.node && path.node.id && "name" in path.node.id
? path.node.id.name
: "anonymous";
}
case "ReturnStatement":
// e.g. return class {}
// e.g. return function () {}
// e.g. return () => {}
case "ArrowFunctionExpression":
// e.g. () => class {}
// e.g. () => function () {}
// e.g. () => () => {}
case "NewExpression":
// e.g. new Class(class {}) // dont think this one is possible but unsure
// e.g. new Class(function () {})
// e.g. new Class(() => {})
case "CallExpression": {
// e.g. function(class {}) // dont think this one is possible but unsure
// e.g. function(function () {})
Expand All @@ -255,28 +259,22 @@ export class TargetVisitor extends AbstractSyntaxTreeVisitor {
// e.g. c ? function () {} : b
// e.g. c ? () => {} : b
return this._getTargetNameOfExpression(path.parentPath);
// return "id" in path.node && path.node.id
// ? path.node.id.name
// : "anonymous";
}
case "LogicalExpression": {
// e.g. c || class {}
// e.g. c || function () {}
// e.g. c || () => {}
return this._getTargetNameOfExpression(path.parentPath);
// return "id" in path.node && path.node.id
// ? path.node.id.name
// : "anonymous";
}
default: {
// e.g. class {}
// e.g. function () {}
// e.g. () => {}
// Should not be possible
throw new Error(
`unknown class expression ${parentNode.type} in ${this._getNodeId(
path
)}`
`Unknown parent expression ${parentNode.type} for ${
path.node.type
} in ${this._getNodeId(path)}`
);
}
}
Expand Down
Loading

0 comments on commit b837ebc

Please sign in to comment.