MichaReiser/speedy.js

View on GitHub
packages/compiler/src/code-generation/default-code-generation-context.ts

Summary

Maintainability
A
0 mins
Test Coverage
import * as assert from "assert";
import * as debug from "debug";
import * as llvm from "llvm-node";
import * as ts from "typescript";
import {toCodeGenerationDiagnostic} from "../code-generation-diagnostic";
import {CompilationContext} from "../compilation-context";
import {CodeGenerationContext} from "./code-generation-context";
import {applyCodeGenerationContextMixin} from "./code-generation-context-mixin";
import {FallbackCodeGenerator} from "./fallback-code-generator";
import {Scope} from "./scope";

import {SyntaxCodeGenerator} from "./syntax-code-generator";
import {DefaultTypeConverter} from "./util/default-type-converter";
import {RuntimeSystemTypeConverter} from "./util/runtime-system-type-converter";
import {TypeScriptToLLVMTypeConverter} from "./util/typescript-to-llvm-type-converter";
import {ClassReference} from "./value/class-reference";
import {Value} from "./value/value";

const log = debug("code-generation/default-code-generation-context");

/**
 * Default implementation of the code generation context
 */
export class DefaultCodeGenerationContext implements CodeGenerationContext {
    builder: llvm.IRBuilder;
    scope: Scope;
    requiresGc = false;
    typeConverter: TypeScriptToLLVMTypeConverter;
    runtimeTypeConverter: TypeScriptToLLVMTypeConverter;

    constructor(public compilationContext: CompilationContext, public module: llvm.Module,
                private rootScope = new Scope(),
                private codeGenerators = new Map<ts.SyntaxKind, SyntaxCodeGenerator<ts.Node, Value | void>>(),
                private entryFunctions = new Set<string>(),
                private fallbackCodeGenerator?: FallbackCodeGenerator) {
        this.builder = new llvm.IRBuilder(this.compilationContext.llvmContext);
        this.scope = rootScope;
        this.typeConverter = new DefaultTypeConverter(this);
        this.runtimeTypeConverter = new RuntimeSystemTypeConverter(this);
    }

    get llvmContext() {
        return this.compilationContext.llvmContext;
    }

    get typeChecker() {
        return this.compilationContext.typeChecker;
    }

    // These functions are implemented in the code generation context mixin
    assignValue: (target: Value, value: Value) => void;
    generateChildren: (node: ts.Node) => void;
    generateValue: (node: ts.Node) => Value;
    value: (value: llvm.Value, type: ts.Type) => Value;
    resolveClass: (type: ts.Type, symbol?: ts.Symbol) => ClassReference | undefined;
    toLLVMType: (type: ts.Type) => llvm.Type;
    toRuntimeLLVMType: (type: ts.Type) => llvm.Type;

    createChildContext(): CodeGenerationContext {
        return new DefaultCodeGenerationContext(
            this.compilationContext,
            this.module,
            this.rootScope,
            this.codeGenerators,
            this.entryFunctions,
            this.fallbackCodeGenerator
        );
    }

    generate(node: ts.Node): void | Value {
        log(`Generate node ${ts.SyntaxKind[node.kind]}`);
        try {
            const codeGenerator = this.getCodeGenerator(node);
            return codeGenerator.generate(node, this);
        } catch (error) {
            throw toCodeGenerationDiagnostic(error, node);
        }
    }

    registerCodeGenerator(codeGenerator: SyntaxCodeGenerator<ts.Node, Value | void>): void {
        assert(codeGenerator);
        const syntaxKind = codeGenerator.syntaxKind;
        assert(syntaxKind, "Code Generator returned undefined as syntax kind");
        assert(!this.codeGenerators.has(syntaxKind), `An other Code Generator is already registered for the syntax kind ${ts.SyntaxKind[syntaxKind]}`);

        log(`Register Code Generator for syntax kind ${ts.SyntaxKind[syntaxKind]}`);

        this.codeGenerators.set(syntaxKind, codeGenerator);
    }

    setFallbackCodeGenerator(fallbackCodeGenerator?: FallbackCodeGenerator) {
        this.fallbackCodeGenerator = fallbackCodeGenerator;
    }

    addEntryFunction(name: string) {
        assert(name, "Name is required");
        this.entryFunctions.add(name);
    }

    getEntryFunctionNames() {
        return Array.from(this.entryFunctions.values());
    }

    enterChildScope(fn?: llvm.Function): Scope {
        this.scope = this.scope.enterChild(fn);
        return this.scope;
    }

    leaveChildScope(): Scope {
        const child = this.scope;
        this.scope = this.scope.exitChild();
        return child;
    }

    private getCodeGenerator(node: ts.Node): SyntaxCodeGenerator<ts.Node, Value | void> | FallbackCodeGenerator {
        const codeGenerator = this.codeGenerators.get(node.kind) || this.fallbackCodeGenerator;

        assert(codeGenerator, `No Code Generator registered for syntax kind ${ts.SyntaxKind[node.kind]} nor is a fallback code generator defined`);
        return codeGenerator!;
    }
}

applyCodeGenerationContextMixin(DefaultCodeGenerationContext);