MichaReiser/speedy.js

View on GitHub
packages/compiler/src/code-generation/util/types.ts

Summary

Maintainability
C
1 day
Test Coverage
import * as assert from "assert";
import * as llvm from "llvm-node";
import * as ts from "typescript";
import {CodeGenerationContext} from "../code-generation-context";

/**
 * Returns the llvm type for the given typescript type
 * @param type the type script type to convert into an llvm type
 * @param context the code generation context
 * @return the llvm type
 * @deprecated use context.toLLVMType instead.
 */
export function toLLVMType(type: ts.Type, context: CodeGenerationContext): llvm.Type {
   return context.toLLVMType(type);
}

/**
 * Tests if the given type describes a reference to a function
 * @param {ts.Type} type the type to test
 * @return {boolean} true if this type is a function type, false other wise
 */
export function isFunctionType(type: ts.Type) {
    if (type.flags & ts.TypeFlags.Union) {
        const unionType = type as ts.UnionType;

        if (unionType.types.length === 2 && unionType.types.some(t => !!(t.flags & ts.TypeFlags.Undefined))) {
            type = unionType.types.find(t => !(t.flags & ts.TypeFlags.Undefined))!;
        }
    }

    if (type.flags & ts.TypeFlags.Object) {
        return type.getCallSignatures().length === 1 && type.getProperties().length === 0;
    }

    return false;
}

export function getCallSignature(type: ts.Type) {
    assert(isFunctionType(type), "Function type expected");

    if (type.flags & ts.TypeFlags.Union) {
        const unionType = type as ts.UnionType;

        if (unionType.types.length === 2 && unionType.types.some(t => !!(t.flags & ts.TypeFlags.Undefined))) {
            type = unionType.types.find(t => !(t.flags & ts.TypeFlags.Undefined))!;
        }
    }

    assert(type.getCallSignatures().length === 1, "Overloaded functions not yet supported");

    return type.getCallSignatures()[0];
}

/**
 * Tests if the given type is a maybe object type (objectType | undefined).
 * @param type the type to test
 * @return {boolean} true if the type contains either an object or undefined
 */
export function isMaybeObjectType(type: ts.Type): type is ts.UnionType {
    if (type.flags & ts.TypeFlags.Union) {
        const unionType = type as ts.UnionType;

        return unionType.types.length === 2 &&
            unionType.types.some(t => !!(t.flags & ts.TypeFlags.Undefined)) &&
            unionType.types.some(t => !!(t.flags & ts.TypeFlags.Object));
    }

    return false;
}

export function getArrayElementType(arrayType: ts.Type): ts.Type {
    const genericType = arrayType as ts.GenericType;
    assert(genericType.typeArguments.length === 1, "An array type needs to have one type argument, the type of the array elements");

    return genericType.typeArguments[0]!;
}

/**
 * Computes the size of the given type
 * http://stackoverflow.com/questions/14608250/how-can-i-find-the-size-of-a-type
 * @param type the type of which the size is to be computed
 * @param context the code generation context
 * @return {Value} the value containing the size of the type
 */
export function sizeof(type: llvm.Type, context: CodeGenerationContext) {
    const size = context.module.dataLayout.getTypeStoreSize(type);
    return llvm.ConstantInt.get(context.llvmContext, size);
}

/**
 * Computes the offset of a field
 * @param type the type of the class
 * @param field the field number for which the offset is to be computed
 * @param context the context
 * @return {Value} the offset as llvm.value
 */
export function offset(type: llvm.PointerType, field: number, context: CodeGenerationContext) {
    const fieldIndex = [ llvm.ConstantInt.get(context.llvmContext, 0), llvm.ConstantInt.get(context.llvmContext, field)];
    const fieldOffset = context.builder.createInBoundsGEP(llvm.ConstantPointerNull.get(type), fieldIndex);
    return context.builder.createPtrToInt(fieldOffset, llvm.Type.getInt32Ty(context.llvmContext), "offset");
}