Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions src/asl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
isArgument,
isArrayBinding,
isArrayLiteralExpr,
isArrowFunctionExpr,
isAwaitExpr,
isBinaryExpr,
isBindingElem,
Expand Down Expand Up @@ -51,7 +50,7 @@ import {
isForInStmt,
isForOfStmt,
isFunctionDecl,
isFunctionExpr,
isFunctionLike,
isIdentifier,
isIfStmt,
isLabelledStmt,
Expand Down Expand Up @@ -104,7 +103,6 @@ import {
isGetAccessorDecl,
isTaggedTemplateExpr,
isOmittedExpr,
isFunctionLike,
isQuasiString,
} from "./guards";
import {
Expand Down Expand Up @@ -1680,7 +1678,7 @@ export class ASL {
const throwTransition = this.throw(expr);

const callbackfn = expr.args[0].expr;
if (callbackfn !== undefined && isFunctionExpr(callbackfn)) {
if (callbackfn !== undefined && isFunctionLike(callbackfn)) {
const callbackStates = this.evalStmt(callbackfn.body);

return this.evalExpr(
Expand Down Expand Up @@ -2448,7 +2446,7 @@ export class ASL {
// detect the immediate for-loop closure surrounding this throw statement
// because of how step function's Catch feature works, we need to check if the try
// is inside or outside the closure
const mapOrParallelClosure = node.findParent(isFunctionExpr);
const mapOrParallelClosure = node.findParent(isFunctionLike);

// catchClause or finallyBlock that will run upon throwing this error
const catchOrFinally = node.throw();
Expand Down Expand Up @@ -2709,7 +2707,7 @@ export class ASL {
expr: CallExpr & { expr: PropAccessExpr }
): ASLGraph.NodeResults {
const predicate = expr.args[0]?.expr;
if (!(isFunctionExpr(predicate) || isArrowFunctionExpr(predicate))) {
if (!isFunctionLike(predicate)) {
throw new SynthError(
ErrorCodes.StepFunction_invalid_filter_syntax,
`the 'predicate' argument of slice must be a function or arrow expression, found: ${predicate?.kindName}`
Expand Down Expand Up @@ -4739,7 +4737,7 @@ function nodeToString(
return `[${nodeToString(expr.expr)}]`;
} else if (isElementAccessExpr(expr)) {
return `${nodeToString(expr.expr)}[${nodeToString(expr.element)}]`;
} else if (isFunctionExpr(expr) || isArrowFunctionExpr(expr)) {
} else if (isFunctionLike(expr)) {
return `function(${expr.parameters.map(nodeToString).join(", ")})`;
} else if (isIdentifier(expr)) {
return expr.name;
Expand Down
24 changes: 14 additions & 10 deletions src/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,22 @@ export function assertConstantValue(val: any, message?: string): ConstantValue {
);
}

export function assertNodeKind<Kind extends NodeKind>(
export function assertNodeKind<Kind extends NodeKind[]>(
node: FunctionlessNode | undefined,
kind: Kind
): NodeInstance<Kind> {
if (node?.kind !== kind) {
throw Error(
`Expected node of type ${getNodeKindName(kind)} and found ${
node ? getNodeKindName(node.kind) : "undefined"
}`
);
...kinds: Kind
): NodeInstance<Kind[number]> {
if (node) {
for (const kind of kinds) {
if (node?.kind === kind) {
return <NodeInstance<Kind[number]>>node;
}
}
}
return <NodeInstance<Kind>>node;
throw Error(
`Expected node of type ${kinds.map(getNodeKindName).join(", ")} and found ${
node ? getNodeKindName(node.kind) : "undefined"
}`
);
}

// to prevent the closure serializer from trying to import all of functionless.
Expand Down
56 changes: 51 additions & 5 deletions src/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,31 @@ export function compile(
]),
]);

const isAsync =
ts.isFunctionDeclaration(impl) ||
ts.isFunctionExpression(impl) ||
ts.isArrowFunction(impl) ||
ts.isMethodDeclaration(impl)
? [
impl.modifiers?.find(
(mod) => mod.kind === ts.SyntaxKind.AsyncKeyword
)
? ts.factory.createTrue()
: ts.factory.createFalse(),
]
: [];

const isAsterisk =
ts.isFunctionDeclaration(impl) ||
ts.isFunctionExpression(impl) ||
ts.isMethodDeclaration(impl)
? [
impl.asteriskToken
? ts.factory.createTrue()
: ts.factory.createFalse(),
]
: [];

return newExpr(type, [
...resolveFunctionName(),
ts.factory.createArrayLiteralExpression(
Expand All @@ -309,6 +334,10 @@ export function compile(
)
),
body,
// isAsync for Functions, Arrows and Methods
...isAsync,
// isAsterisk for Functions and Methods
...isAsterisk,
]);
});

Expand Down Expand Up @@ -340,7 +369,9 @@ export function compile(
): ts.Expression {
if (node === undefined) {
return ts.factory.createIdentifier("undefined");
} else if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
} else if (ts.isArrowFunction(node)) {
return toFunction(NodeKind.ArrowFunctionExpr, node, scope);
} else if (ts.isFunctionExpression(node)) {
return toFunction(NodeKind.FunctionExpr, node, scope);
} else if (ts.isExpressionStatement(node)) {
return newExpr(NodeKind.ExprStmt, [toExpr(node.expression, scope)]);
Expand Down Expand Up @@ -419,14 +450,29 @@ export function compile(
? ts.factory.createTrue()
: ts.factory.createFalse(),
]);
} else if (ts.isPropertyAccessChain(node)) {
return newExpr(NodeKind.PropAccessExpr, [
toExpr(node.expression, scope),
toExpr(node.name, scope),
node.questionDotToken
? ts.factory.createTrue()
: ts.factory.createFalse(),
]);
} else if (ts.isElementAccessChain(node)) {
return newExpr(NodeKind.ElementAccessExpr, [
toExpr(node.expression, scope),
toExpr(node.argumentExpression, scope),
node.questionDotToken
? ts.factory.createTrue()
: ts.factory.createFalse(),
]);
} else if (ts.isElementAccessExpression(node)) {
const type = checker.getTypeAtLocation(node.argumentExpression);
return newExpr(NodeKind.ElementAccessExpr, [
toExpr(node.expression, scope),
toExpr(node.argumentExpression, scope),
type
? ts.factory.createStringLiteral(checker.typeToString(type))
: ts.factory.createIdentifier("undefined"),
node.questionDotToken
? ts.factory.createTrue()
: ts.factory.createFalse(),
]);
} else if (ts.isVariableStatement(node)) {
return newExpr(NodeKind.VariableStmt, [
Expand Down
54 changes: 52 additions & 2 deletions src/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,40 @@ export class MethodDecl extends BaseDecl<NodeKind.MethodDecl> {
constructor(
readonly name: PropName,
readonly parameters: ParameterDecl[],
readonly body: BlockStmt
readonly body: BlockStmt,

/**
* true if this function has an `async` modifier
* ```ts
* class Foo {
* async foo() {}
*
* // asterisk can co-exist
* async *foo() {}
* }
* ```
*/
readonly isAsync: boolean,
/**
* true if this function has an `*` modifier
*
* ```ts
* class Foo {
* foo*() {}
*
* // async can co-exist
* async *foo() {}
* }
* ```
*/
readonly isAsterisk: boolean
) {
super(NodeKind.MethodDecl, arguments);
this.ensure(name, "name", NodeKind.PropName);
this.ensureArrayOf(parameters, "parameters", [NodeKind.ParameterDecl]);
this.ensure(body, "body", [NodeKind.BlockStmt]);
this.ensure(isAsync, "isAsync", ["boolean"]);
this.ensure(isAsterisk, "isAsterisk", ["boolean"]);
}
}

Expand Down Expand Up @@ -142,12 +170,34 @@ export class FunctionDecl<
// according to the spec, name is mandatory on a FunctionDecl and FunctionExpr
readonly name: string | undefined,
readonly parameters: ParameterDecl[],
readonly body: BlockStmt
readonly body: BlockStmt,
/**
* true if this function has an `async` modifier
* ```ts
* async function foo() {}
* // asterisk can co-exist
* async function *foo() {}
* ```
*/
readonly isAsync: boolean,
/**
* true if this function has an `*` modifier
*
* ```ts
* function foo*() {}
*
* // async can co-exist
* async function *foo() {}
* ```
*/
readonly isAsterisk: boolean
) {
super(NodeKind.FunctionDecl, arguments);
this.ensure(name, "name", ["undefined", "string"]);
this.ensureArrayOf(parameters, "parameters", [NodeKind.ParameterDecl]);
this.ensure(body, "body", [NodeKind.BlockStmt]);
this.ensure(isAsync, "isAsync", ["boolean"]);
this.ensure(isAsterisk, "isAsterisk", ["boolean"]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ensure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function ensure<Assert extends Assertion>(
nodeKind: NodeKind,
item: any,
fieldName: string,
assertions: Assert[]
assertions: Assert[] | readonly Assert[]
): asserts item is AssertionToInstance<Assert> {
if (!is(item, assertions)) {
throw new Error(
Expand Down
8 changes: 6 additions & 2 deletions src/event-bridge/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ export const getPropertyAccessKeyFlatten = (
): string | number => {
if (isElementAccessExpr(expr)) {
return getPropertyAccessKey(
new ElementAccessExpr(expr.expr, flattenExpression(expr.element, scope))
new ElementAccessExpr(
expr.expr,
flattenExpression(expr.element, scope),
expr.isOptional
)
);
}
return getPropertyAccessKey(expr);
Expand Down Expand Up @@ -186,7 +190,7 @@ export const flattenExpression = (expr: Expr, scope: EventScope): Expr => {
}
return typeof key === "string"
? new PropAccessExpr(parent, new Identifier(key), false)
: new ElementAccessExpr(parent, new NumberLiteralExpr(key));
: new ElementAccessExpr(parent, new NumberLiteralExpr(key), false);
} else if (isComputedPropertyNameExpr(expr)) {
return flattenExpression(expr.expr, scope);
} else if (isArrayLiteralExpr(expr)) {
Expand Down
55 changes: 52 additions & 3 deletions src/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,21 @@ export class ArrowFunctionExpr<
F extends AnyFunction = AnyFunction
> extends BaseExpr<NodeKind.ArrowFunctionExpr> {
readonly _functionBrand?: F;
constructor(readonly parameters: ParameterDecl[], readonly body: BlockStmt) {
constructor(
readonly parameters: ParameterDecl[],
readonly body: BlockStmt,
/**
* true if this function has an `async` modifier
* ```ts
* async () => {}
* ```
*/
readonly isAsync: boolean
) {
super(NodeKind.ArrowFunctionExpr, arguments);
this.ensure(body, "body", [NodeKind.BlockStmt]);
this.ensureArrayOf(parameters, "parameters", [NodeKind.ParameterDecl]);
this.ensure(isAsync, "isAsync", ["boolean"]);
}
}

Expand All @@ -94,12 +105,34 @@ export class FunctionExpr<
constructor(
readonly name: string | undefined,
readonly parameters: ParameterDecl[],
readonly body: BlockStmt
readonly body: BlockStmt,
/**
* true if this function has an `async` modifier
* ```ts
* async function foo() {}
* // asterisk can co-exist
* async function *foo() {}
* ```
*/
readonly isAsync: boolean,
/**
* true if this function has an `*` modifier
*
* ```ts
* function foo*() {}
*
* // async can co-exist
* async function *foo() {}
* ```
*/
readonly isAsterisk: boolean
) {
super(NodeKind.FunctionExpr, arguments);
this.ensure(name, "name", ["undefined", "string"]);
this.ensureArrayOf(parameters, "parameters", [NodeKind.ParameterDecl]);
this.ensure(body, "body", [NodeKind.BlockStmt]);
this.ensure(isAsync, "isAsync", ["boolean"]);
this.ensure(isAsterisk, "isAsterisk", ["boolean"]);
}
}

Expand Down Expand Up @@ -158,6 +191,12 @@ export class PropAccessExpr extends BaseExpr<NodeKind.PropAccessExpr> {
constructor(
readonly expr: Expr,
readonly name: Identifier | PrivateIdentifier,
/**
* Whether this is using optional chaining.
* ```ts
* a?.prop
* ```
*/
readonly isOptional: boolean
) {
super(NodeKind.PropAccessExpr, arguments);
Expand All @@ -167,7 +206,17 @@ export class PropAccessExpr extends BaseExpr<NodeKind.PropAccessExpr> {
}

export class ElementAccessExpr extends BaseExpr<NodeKind.ElementAccessExpr> {
constructor(readonly expr: Expr, readonly element: Expr) {
constructor(
readonly expr: Expr,
readonly element: Expr,
/**
* Whether this is using optional chaining.
* ```ts
* a?.[element]
* ```
*/
readonly isOptional: boolean
) {
super(NodeKind.ElementAccessExpr, arguments);
this.ensure(expr, "expr", ["Expr"]);
this.ensure(element, "element", ["Expr"]);
Expand Down
Loading