packages/compiler/src/code-generation/value/speedyjs-function-factory.ts
import * as assert from "assert";
import * as llvm from "llvm-node";
import * as ts from "typescript";
import {CodeGenerationDiagnostics} from "../../code-generation-diagnostic";
import {CodeGenerationContext} from "../code-generation-context";
import {DefaultNameMangler} from "../default-name-mangler";
import {Parameter} from "../name-mangler";
import {FunctionDefinitionBuilder} from "../util/function-definition-builder";
import {FunctionFactory, FunctionProperties} from "./function-factory";
import {ObjectReference} from "./object-reference";
import {ResolvedFunction} from "./resolved-function";
export function verifyIsSupportedSpeedyJSFunction(declaration: ts.Declaration, context: CodeGenerationContext) {
// tslint:disable-next-line: max-line-length
if (!(declaration.kind === ts.SyntaxKind.FunctionDeclaration || declaration.kind === ts.SyntaxKind.MethodDeclaration || declaration.kind === ts.SyntaxKind.Constructor)) {
throw CodeGenerationDiagnostics.unsupportedFunctionDeclaration(declaration);
}
const functionDeclaration = declaration as ts.FunctionDeclaration | ts.MethodDeclaration | ts.ConstructorDeclaration;
if (functionDeclaration.typeParameters && functionDeclaration.typeParameters.length > 0) {
throw CodeGenerationDiagnostics.unsupportedGenericFunction(functionDeclaration);
}
// tslint:disable-next-line: max-line-length
if (functionDeclaration.kind === ts.SyntaxKind.FunctionDeclaration && functionDeclaration.parent && functionDeclaration.parent.kind !== ts.SyntaxKind.SourceFile) {
throw CodeGenerationDiagnostics.unsupportedNestedFunctionDeclaration(functionDeclaration);
}
if (context.typeChecker.isImplementationOfOverload(functionDeclaration)) {
throw CodeGenerationDiagnostics.unsupportedOverloadedFunctionDeclaration(functionDeclaration);
}
}
/**
* Function factory for functions marked with "speedyjs"
*/
export class SpeedyJSFunctionFactory extends FunctionFactory {
constructor(context: CodeGenerationContext) {
super(new DefaultNameMangler(context.compilationContext), context.typeConverter);
}
protected mangleFunctionName(resolvedFunction: ResolvedFunction, usedParameters: Parameter[]) {
if (resolvedFunction.async) {
assert(resolvedFunction.functionName, "Missing name for entry function");
return resolvedFunction.functionName!; // entry functions always have a name
}
return super.mangleFunctionName(resolvedFunction, usedParameters);
}
protected getDefaultFunctionProperties(): FunctionProperties {
return Object.assign({}, super.getDefaultFunctionProperties(), {
visibility: llvm.VisibilityTypes.Hidden
});
}
protected createFunction(mangledName: string,
resolvedFunction: ResolvedFunction,
numberOfArguments: number,
context: CodeGenerationContext,
properties: FunctionProperties,
objectReference?: ObjectReference) {
const definition = resolvedFunction.definition as ts.FunctionDeclaration;
assert(definition, "Functions only with a declaration cannot be defined");
verifyIsSupportedSpeedyJSFunction(definition, context);
assert(definition.body, "Cannot define a function without a body");
const fn = super.createFunction(mangledName, resolvedFunction, numberOfArguments, context, properties, objectReference);
const childContext = context.createChildContext();
FunctionDefinitionBuilder.create(fn, resolvedFunction, childContext).define();
return fn;
}
}