diff --git a/src/api.ts b/src/api.ts index 3045988f..a3c48a35 100644 --- a/src/api.ts +++ b/src/api.ts @@ -39,7 +39,7 @@ import { isSpreadAssignExpr, isFunctionLike, } from "./guards"; -import { Integration, IntegrationImpl, isIntegration } from "./integration"; +import { IntegrationImpl, tryFindIntegration } from "./integration"; import { validateFunctionLike } from "./reflect"; import { Stmt } from "./statement"; import { AnyFunction, singletonConstruct } from "./util"; @@ -732,12 +732,13 @@ export class APIGatewayVTL extends VTL { return this.exprToJson(expr.expr); } } else if (isCallExpr(expr)) { - if (isReferenceExpr(expr.expr) || isThisExpr(expr.expr)) { + const integration = tryFindIntegration(expr.expr); + if (integration) { + const serviceCall = new IntegrationImpl(integration); + return this.integrate(serviceCall, expr); + } else if (isReferenceExpr(expr.expr) || isThisExpr(expr.expr)) { const ref = expr.expr.ref(); - if (isIntegration(ref)) { - const serviceCall = new IntegrationImpl(ref); - return this.integrate(serviceCall, expr); - } else if (ref === Number) { + if (ref === Number) { return this.exprToJson(expr.args[0]); } else { throw new SynthError( diff --git a/src/appsync.ts b/src/appsync.ts index ef2d623c..c84e07e4 100644 --- a/src/appsync.ts +++ b/src/appsync.ts @@ -466,14 +466,18 @@ function synthesizeFunctions(api: appsync.GraphqlApi, decl: FunctionLike) { // we find the range of nodes to hoist so that we avoid visiting the middle nodes. // The start node is the first node in the integration pattern (integ, await, or promise) // The end is always the integration. + const end = getIntegrationExprFromIntegrationCallPattern(node); - const updatedChild = visitSpecificChildren(node, [end], (expr) => - normalizeAST(expr, hoist) - ); - // when we find an integration call, - // if it is nested, hoist it up (create variable, add above, replace expr with variable) - return hoist && doHoist(node) ? hoist(updatedChild) : updatedChild; + if (end) { + const updatedChild = visitSpecificChildren(node, [end], (expr) => + normalizeAST(expr, hoist) + ); + + // when we find an integration call, + // if it is nested, hoist it up (create variable, add above, replace expr with variable) + return hoist && doHoist(node) ? hoist(updatedChild) : updatedChild; + } } else if (isVariableStmt(node) && node.declList.decls.length > 1) { /** * Flatten variable declarations into multiple variable statements. diff --git a/src/asl.ts b/src/asl.ts index ab3a90e7..5faf982f 100644 --- a/src/asl.ts +++ b/src/asl.ts @@ -110,10 +110,10 @@ import { isQuasiString, } from "./guards"; import { - Integration, IntegrationImpl, isIntegration, isIntegrationCallPattern, + tryFindIntegration, } from "./integration"; import { FunctionlessNode } from "./node"; import { @@ -1647,75 +1647,66 @@ export class ASL { }; }); } else if (isCallExpr(expr)) { - if (isReferenceExpr(expr.expr)) { - const ref = expr.expr.ref(); - if (isIntegration(ref)) { - const serviceCall = new IntegrationImpl(ref); - const integStates = serviceCall.asl(expr, this); + const integration = tryFindIntegration(expr.expr); + if (integration) { + const serviceCall = new IntegrationImpl(integration); + const integStates = serviceCall.asl(expr, this); + if ( + ASLGraph.isLiteralValue(integStates) || + ASLGraph.isJsonPath(integStates) + ) { + return integStates; + } + + const updateState = (state: ASLGraph.NodeState): ASLGraph.NodeState => { + const throwOrPass = this.throw(expr); if ( - ASLGraph.isLiteralValue(integStates) || - ASLGraph.isJsonPath(integStates) + throwOrPass?.Next && + (isTaskState(state) || + isMapTaskState(state) || + isParallelTaskState(state)) ) { - return integStates; + return { + ...state, + Catch: [ + { + ErrorEquals: ["States.ALL"], + Next: throwOrPass.Next, + ResultPath: throwOrPass.ResultPath, + }, + ], + }; + } else { + return state; } + }; - const updateState = ( - state: ASLGraph.NodeState - ): ASLGraph.NodeState => { - const throwOrPass = this.throw(expr); - if ( - throwOrPass?.Next && - (isTaskState(state) || - isMapTaskState(state) || - isParallelTaskState(state)) - ) { - return { - ...state, - Catch: [ - { - ErrorEquals: ["States.ALL"], - Next: throwOrPass.Next, - ResultPath: throwOrPass.ResultPath, - }, - ], - }; - } else { - return state; - } - }; - - const updateStates = ( - states: ASLGraph.NodeState | ASLGraph.SubState - ): ASLGraph.NodeState | ASLGraph.SubState => { - return ASLGraph.isSubState(states) - ? { - ...states, - states: Object.fromEntries( - Object.entries(states.states ?? {}).map( - ([stateName, state]) => { - if (ASLGraph.isSubState(state)) { - return [stateName, updateStates(state)]; - } else { - return [stateName, updateState(state)]; - } + const updateStates = ( + states: ASLGraph.NodeState | ASLGraph.SubState + ): ASLGraph.NodeState | ASLGraph.SubState => { + return ASLGraph.isSubState(states) + ? { + ...states, + states: Object.fromEntries( + Object.entries(states.states ?? {}).map( + ([stateName, state]) => { + if (ASLGraph.isSubState(state)) { + return [stateName, updateStates(state)]; + } else { + return [stateName, updateState(state)]; } - ) - ), - } - : updateState(states); - }; + } + ) + ), + } + : updateState(states); + }; - return { - ...integStates, - ...updateStates(integStates), - }; - } else { - throw new SynthError( - ErrorCodes.Unexpected_Error, - "Called references are expected to be an integration." - ); - } + return { + ...integStates, + ...updateStates(integStates), + }; } else if (isMapOrForEach(expr)) { const throwTransition = this.throw(expr); diff --git a/src/checker.ts b/src/checker.ts index fa653cd2..7279921b 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -402,7 +402,8 @@ export function makeFunctionlessChecker( return updatedSymbol ? isSymbolOutOfScope(updatedSymbol, scope) : false; } else if ( ts.isVariableDeclaration(symbol.valueDeclaration) || - ts.isClassDeclaration(symbol.valueDeclaration) + ts.isClassDeclaration(symbol.valueDeclaration) || + ts.isParameter(symbol.valueDeclaration) ) { return !hasParent(symbol.valueDeclaration, scope); } else if (ts.isBindingElement(symbol.valueDeclaration)) { diff --git a/src/compile.ts b/src/compile.ts index 2ae7506c..c732d087 100644 --- a/src/compile.ts +++ b/src/compile.ts @@ -400,18 +400,6 @@ export function compile( } else if (node.text === "null") { return newExpr(NodeKind.NullLiteralExpr, []); } - if (checker.isIntegrationNode(node)) { - // if this is a reference to a Table or Lambda, retain it - const _ref = checker.getOutOfScopeValueNode(node, scope); - if (_ref) { - return ref(_ref); - } else { - throw new SynthError( - ErrorCodes.Unable_to_find_reference_out_of_application_function, - `Unable to find reference out of application function: ${node.getText()}` - ); - } - } /** * If the identifier is not within the closure, we attempt to enclose the reference in its own closure. @@ -423,28 +411,13 @@ export function compile( * return { value: () => val }; */ if (checker.isIdentifierOutOfScope(node, scope)) { - const _ref = checker.getOutOfScopeValueNode(node, scope); - if (_ref) { - return ref(_ref); - } + return ref(node); } return newExpr(NodeKind.Identifier, [ ts.factory.createStringLiteral(node.text), ]); } else if (ts.isPropertyAccessExpression(node)) { - if (checker.isIntegrationNode(node)) { - // if this is a reference to a Table or Lambda, retain it - const _ref = checker.getOutOfScopeValueNode(node, scope); - if (_ref) { - return ref(_ref); - } else { - throw new SynthError( - ErrorCodes.Unable_to_find_reference_out_of_application_function, - `Unable to find reference out of application function: ${node.getText()}` - ); - } - } return newExpr(NodeKind.PropAccessExpr, [ toExpr(node.expression, scope), toExpr(node.name, scope), diff --git a/src/expression.ts b/src/expression.ts index 3dd8769b..c49dad11 100644 --- a/src/expression.ts +++ b/src/expression.ts @@ -187,11 +187,13 @@ export class Argument extends BaseExpr { } } -export class CallExpr extends BaseExpr { - constructor( - readonly expr: Expr | SuperKeyword | ImportKeyword, - readonly args: Argument[] - ) { +export class CallExpr< + E extends Expr | SuperKeyword | ImportKeyword = + | Expr + | SuperKeyword + | ImportKeyword +> extends BaseExpr { + constructor(readonly expr: E, readonly args: Argument[]) { super(NodeKind.CallExpr, arguments); } } diff --git a/src/function.ts b/src/function.ts index 5e6f2714..2a9ec198 100644 --- a/src/function.ts +++ b/src/function.ts @@ -31,7 +31,7 @@ import { ApiGatewayVtlIntegration } from "./api"; import type { AppSyncVtlIntegration } from "./appsync"; import { ASL, ASLGraph } from "./asl"; import { BindFunctionName, RegisterFunctionName } from "./compile"; -import { FunctionLike, IntegrationInvocation } from "./declaration"; +import { IntegrationInvocation } from "./declaration"; import { ErrorCodes, formatErrorMessage, SynthError } from "./error-code"; import { IEventBus, @@ -49,19 +49,16 @@ import { PrewarmProps, } from "./function-prewarm"; import { + findDeepIntegrations, Integration, - IntegrationCallExpr, IntegrationImpl, INTEGRATION_TYPE_KEYS, isIntegration, - isIntegrationCallExpr, } from "./integration"; -import { FunctionlessNode } from "./node"; import { ReflectionSymbols, validateFunctionLike } from "./reflect"; import { isStepFunction } from "./step-function"; import { isTable } from "./table"; import { AnyAsyncFunction, AnyFunction } from "./util"; -import { visitEachChild } from "./visit"; export function isFunction( a: any @@ -659,7 +656,7 @@ export class Function< super(_resource); - const integrations = getInvokedIntegrations(ast).map( + const integrations = findDeepIntegrations(ast).map( (i) => { args: i.args, @@ -723,19 +720,6 @@ export class Function< } } -function getInvokedIntegrations(ast: FunctionLike): IntegrationCallExpr[] { - const nodes: IntegrationCallExpr[] = []; - visitEachChild(ast, function visit(node: FunctionlessNode): FunctionlessNode { - if (isIntegrationCallExpr(node)) { - nodes.push(node); - } - - return visitEachChild(node, visit); - }); - - return nodes; -} - /** * A {@link Function} which wraps a CDK function. * diff --git a/src/integration.ts b/src/integration.ts index cb2bafb6..debfb187 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -6,39 +6,43 @@ import { AwaitExpr, CallExpr, PromiseExpr, ReferenceExpr } from "./expression"; import { Function, NativeIntegration } from "./function"; import { isAwaitExpr, + isBindingElem, + isBindingPattern, isCallExpr, + isConditionExpr, + isElementAccessExpr, + isIdentifier, isPromiseExpr, + isPropAccessExpr, isReferenceExpr, isThisExpr, + isVariableDecl, } from "./guards"; import { FunctionlessNode } from "./node"; -import { AnyFunction } from "./util"; +import { AnyFunction, evalToConstant } from "./util"; import { visitEachChild } from "./visit"; import { VTL } from "./vtl"; -export const isIntegration = >( +export const isIntegration = >( i: any ): i is I => typeof i === "object" && "kind" in i; -export type IntegrationCallExpr = CallExpr & { - expr: ReferenceExpr; -}; +export interface IntegrationCallExpr extends CallExpr {} export function isIntegrationCallExpr( node: FunctionlessNode ): node is IntegrationCallExpr { - return ( - isCallExpr(node) && - (isReferenceExpr(node.expr) || isThisExpr(node.expr)) && - isIntegration(node.expr.ref()) - ); + if (isCallExpr(node)) { + return tryFindIntegration(node.expr) !== undefined; + } + return false; } export type IntegrationCallPattern = - | IntegrationCallExpr - | (AwaitExpr & { expr: IntegrationCallExpr }) - | (PromiseExpr & { expr: IntegrationCallExpr }) - | (AwaitExpr & { expr: PromiseExpr & { expr: IntegrationCallExpr } }); + | CallExpr + | (AwaitExpr & { expr: CallExpr }) + | (PromiseExpr & { expr: CallExpr }) + | (AwaitExpr & { expr: PromiseExpr & { expr: CallExpr } }); export function isIntegrationCallPattern( node: FunctionlessNode @@ -58,20 +62,16 @@ export function isIntegrationCallPattern( */ export function getIntegrationExprFromIntegrationCallPattern( pattern: IntegrationCallPattern -): IntegrationCallExpr { - if (isAwaitExpr(pattern)) { - if (isIntegrationCallExpr(pattern.expr)) { - return pattern.expr; - } else if ( - isPromiseExpr(pattern.expr) && - isIntegrationCallExpr(pattern.expr.expr) - ) { - return pattern.expr.expr; +): IntegrationCallExpr | undefined { + if (isAwaitExpr(pattern) || isPromiseExpr(pattern)) { + return getIntegrationExprFromIntegrationCallPattern(pattern.expr); + } else if (isCallExpr(pattern)) { + const integration = tryFindIntegration(pattern.expr); + if (integration) { + return pattern; } - } else if (isPromiseExpr(pattern) && isIntegrationCallExpr(pattern.expr)) { - return pattern.expr; } - return pattern as IntegrationCallExpr; + return undefined; } /** @@ -301,20 +301,144 @@ export function makeIntegration( export type CallContext = ASL | VTL | Function | EventBus; -/** - * Dive until we find a integration object. - */ export function findDeepIntegrations( - expr: FunctionlessNode -): IntegrationCallExpr[] { - const integrations: IntegrationCallExpr[] = []; - visitEachChild(expr, function find(node: FunctionlessNode): FunctionlessNode { - if (isIntegrationCallExpr(node)) { - integrations.push(node); + ast: FunctionlessNode +): CallExpr[] { + const nodes: CallExpr[] = []; + visitEachChild(ast, function visit(node: FunctionlessNode): FunctionlessNode { + if (isCallExpr(node)) { + const integrations = tryFindIntegrations(node.expr); + if (integrations) { + nodes.push( + ...integrations.map((integration) => + node.fork( + new CallExpr( + new ReferenceExpr("", () => integration), + node.args.map((arg) => arg.clone()) + ) + ) + ) + ); + } } - return visitEachChild(node, find); + + return visitEachChild(node, visit); }); - return integrations; + + return nodes; +} + +/** + * A bottom-up algorithm that determines the ONLY {@link Integration} value that the {@link node} + * will resolve to at runtime. + * + * If the {@link node} resolves to 0 or more than 1 {@link Integration} then `undefined` is returned. + * + * **Note**: This function is an intermediate helper until we migrate the interpreters to be more general + * (likely by migrating to top-down algorithms, see https://github.com/functionless/functionless/issues/374#issuecomment-1203313604) + * + * @param node the node to resolve the {@link Integration} of. + * @returns the ONLY {@link Integration} that {@link node} can resolve to, otherwise `undefined`. + */ +export function tryFindIntegration( + node: FunctionlessNode +): Integration | undefined { + const integrations = tryFindIntegrations(node); + if (integrations?.length === 1) { + return integrations[0]; + } + return undefined; +} + +/** + * A bottom-up algorithm that determines all of the possible {@link Integration}s that a {@link node} + * may resolve to at runtime. + * + * ```ts + * declare const table1; + * declare const table2; + * + * const tables = [table1, table2]; + * + * const a = table1; + * // ^ [table1] + * + * for (const a of tables) { + * const b = a; + * // ^ [table1, table2] + * + * const { appsync: { getItem } } = a; + * // ^ [ table1.appsync.getItem, table2.appsync.getItem ] + * } + * + * const a = tables[0]; + * // ^ [table1] + * + * const { appsync: { getItem } } = table[0]; + * // ^ [ table1.appsync.getItem ] + * + * const { appsync: { getItem = table1.appsync.getItem } } = table[2]; + * // ^ [ table1.appsync.getItem ] (because of initializer) + * ``` + * + * @param node the node to resolve the possible {@link Integration}s of. + * @returns a list of all the {@link Integration}s that the {@link node} could evaluate to. + */ +export function tryFindIntegrations(node: FunctionlessNode): Integration[] { + return resolve(node, undefined).filter(isIntegration); + + /** + * Resolve all of the possible values that {@link node} may resolve to at runtime. + * + * @param node + * @param defaultValue default value to use if the value cannot be resolved (set by default initializers in BindingElement) + * @returns an array of all the values the {@link node} resolves to. + */ + function resolve( + node: FunctionlessNode | undefined, + defaultValue: FunctionlessNode | undefined + ): any[] { + if (node === undefined) { + if (defaultValue === undefined) { + return []; + } else { + return resolve(defaultValue, undefined); + } + } else if (isReferenceExpr(node) || isThisExpr(node)) { + return [node.ref()]; + } else if (isIdentifier(node)) { + return resolve(node.lookup(), defaultValue); + } else if (isBindingElem(node)) { + return resolve(node.parent, node.initializer).flatMap((value) => { + if (isIdentifier(node.name)) { + return [value[node.name.name]]; + } else { + throw new Error("should be impossible"); + } + }); + } else if (isBindingPattern(node)) { + // we only ever evaluate `{ a }` or `[ a ]` when walking backwards from `a` + // the BindingElem resolver case will pluck `a` from the object returned by this + return resolve(node.parent, defaultValue); + } else if (isVariableDecl(node)) { + return resolve(node.initializer, defaultValue); + } else if (isPropAccessExpr(node) || isElementAccessExpr(node)) { + return resolve(node.expr, undefined).flatMap((expr) => { + const key: any = isPropAccessExpr(node) + ? node.name.name + : evalToConstant(node.element)?.constant; + if (key !== undefined) { + return [(expr)?.[key]]; + } + return []; + }); + } else if (isConditionExpr(node)) { + return resolve(node.then, defaultValue).concat( + resolve(node._else, defaultValue) + ); + } + return []; + } } // to prevent the closure serializer from trying to import all of functionless. diff --git a/src/node-ctor.ts b/src/node-ctor.ts index e4306302..62897850 100644 --- a/src/node-ctor.ts +++ b/src/node-ctor.ts @@ -93,9 +93,9 @@ export function getCtor( return n[kind] as any; } -export type NodeInstance = InstanceType< - typeof nodes[Kind] ->; +export type NodeCtor = typeof nodes[Kind]; + +export type NodeInstance = InstanceType>; export const declarations = { [NodeKind.ArrayBinding]: ArrayBinding, diff --git a/src/node.ts b/src/node.ts index 1c040fcc..0a013ac3 100644 --- a/src/node.ts +++ b/src/node.ts @@ -38,6 +38,7 @@ import { isVariableStmt, isWhileStmt, } from "./guards"; +import type { NodeCtor } from "./node-ctor"; import { NodeKind, NodeKindName, getNodeKindName } from "./node-kind"; import type { BlockStmt, CatchClause, Stmt } from "./statement"; @@ -57,6 +58,9 @@ export interface HasParent { set parent(parent: Parent); } +type Binding = [string, BindingDecl]; +type BindingDecl = VariableDecl | ParameterDecl | BindingElem; + export abstract class BaseNode< Kind extends NodeKind, Parent extends FunctionlessNode | undefined = FunctionlessNode | undefined @@ -71,7 +75,10 @@ export abstract class BaseNode< */ readonly children: FunctionlessNode[] = []; - constructor(readonly kind: Kind, readonly _arguments: IArguments) { + readonly _arguments: ConstructorParameters>; + + constructor(readonly kind: Kind, _arguments: IArguments) { + this._arguments = Array.from(_arguments) as any; const setParent = (node: any) => { if (isNode(node)) { // @ts-ignore @@ -93,7 +100,7 @@ export abstract class BaseNode< public toSExpr(): [kind: this["kind"], ...args: any[]] { return [ this.kind, - ...Array.from(this._arguments).map(function toSExpr(arg): any { + ...this._arguments.map(function toSExpr(arg: any): any { if (isNode(arg)) { return arg.toSExpr(); } else if (Array.isArray(arg)) { @@ -105,6 +112,17 @@ export abstract class BaseNode< ]; } + /** + * Forks the tree starting from `this` node with {@link node} as its child + * + * This function simply sets the {@link node}'s parent and returns it. + */ + public fork(node: N): N { + // @ts-ignore + node.parent = this; + return node; + } + protected ensureArrayOf( arr: any[], fieldName: string, @@ -362,11 +380,9 @@ export abstract class BaseNode< /** * @returns a mapping of name to the node visible in this node's scope. */ - public getLexicalScope(): Map { + public getLexicalScope(): Map { return new Map(getLexicalScope(this as unknown as FunctionlessNode)); - type Binding = [string, VariableDecl | ParameterDecl | BindingElem]; - function getLexicalScope(node: FunctionlessNode | undefined): Binding[] { if (node === undefined) { return []; diff --git a/src/visit.ts b/src/visit.ts index 3a3a8288..9dc6c118 100644 --- a/src/visit.ts +++ b/src/visit.ts @@ -23,48 +23,37 @@ export function visitEachChild( ) => FunctionlessNode | FunctionlessNode[] | undefined ): T { const ctor = getCtor(node.kind); - const args = Array.from(node._arguments).map( - ( - argument: - | FunctionlessNode - | FunctionlessNode[] - | string - | number - | boolean - | undefined - ): typeof argument => { - if (!(typeof argument === "object" || Array.isArray(argument))) { - // all primitives are simply returned as-is - // all objects are assumed - return argument; - } else if (isNode(argument)) { - const transformed = visit(argument); - if (transformed === undefined || isNode(transformed)) { - return transformed; - } else { - throw new Error( - `cannot spread nodes into an argument taking a single ${argument.kindName} node` - ); - } - } else if (Array.isArray(argument)) { - // is an Array of nodes - return argument.flatMap((item) => { - const transformed = visit(item); - if (transformed === undefined) { - // the item was deleted, so remove it from the array - return []; - } else if (isNode(transformed)) { - return [transformed]; - } else { - // spread the nodes into the array - return transformed; - } - }); + const args = node._arguments.map((argument) => { + if (argument === null || typeof argument !== "object") { + // all primitives are simply returned as-is + return argument; + } else if (isNode(argument)) { + const transformed = visit(argument); + if (transformed === undefined || isNode(transformed)) { + return transformed; } else { - return argument; + throw new Error( + `cannot spread nodes into an argument taking a single ${argument.kindName} node` + ); } + } else if (Array.isArray(argument)) { + // is an Array of nodes + return argument.flatMap((item) => { + const transformed = visit(item); + if (transformed === undefined) { + // the item was deleted, so remove it from the array + return []; + } else if (isNode(transformed)) { + return [transformed]; + } else { + // spread the nodes into the array + return transformed; + } + }); + } else { + return argument; } - ); + }); return new ctor(...args) as T; } @@ -83,11 +72,10 @@ export function visitBlock( const nestedTasks: FunctionlessNode[] = []; function hoist(expr: Expr): Identifier { const id = new Identifier(nameGenerator.generateOrGet(expr)); - nestedTasks.push( - new VariableStmt( - new VariableDeclList([new VariableDecl(id.clone(), expr)]) - ) + const stmt = new VariableStmt( + new VariableDeclList([new VariableDecl(id.clone(), expr.clone())]) ); + nestedTasks.push(stmt); return id; } diff --git a/src/vtl.ts b/src/vtl.ts index d4c57500..45581ba9 100644 --- a/src/vtl.ts +++ b/src/vtl.ts @@ -95,7 +95,12 @@ import { isWithStmt, isYieldExpr, } from "./guards"; -import { Integration, IntegrationImpl, isIntegration } from "./integration"; +import { + Integration, + IntegrationImpl, + isIntegration, + tryFindIntegration, +} from "./integration"; import { NodeKind } from "./node-kind"; import { Stmt } from "./statement"; import { AnyFunction, isInTopLevelScope } from "./util"; @@ -377,10 +382,8 @@ export abstract class VTL { ? node.expr.unwrap() : node.expr; - if (isSuperKeyword(expr) || isImportKeyword(expr)) { - throw new Error(`super and import are not supported by VTL`); - } else if (isReferenceExpr(expr)) { - const ref = expr.ref(); + const ref = expr ? tryFindIntegration(expr) : undefined; + if (ref) { if (isIntegration(ref)) { const serviceCall = new IntegrationImpl(ref); return this.integrate(serviceCall, node); @@ -390,6 +393,13 @@ export abstract class VTL { "Called references are expected to be an integration." ); } + } else if (isReferenceExpr(expr)) { + throw new SynthError( + ErrorCodes.Unexpected_Error, + "Called references are expected to be an integration." + ); + } else if (isSuperKeyword(expr) || isImportKeyword(expr)) { + throw new Error(`super and import are not supported by VTL`); } else if ( // If the parent is a propAccessExpr isPropAccessExpr(expr) && diff --git a/test/reflect.test.ts b/test/reflect.test.ts index 8c47ff81..35b3adca 100644 --- a/test/reflect.test.ts +++ b/test/reflect.test.ts @@ -209,7 +209,7 @@ test("ObjectBinding with out-of-bound reference", () => { assertNodeKind(binding2.propertyName, NodeKind.Identifier); assertNodeKind(binding3.name, NodeKind.Identifier); assertNodeKind(binding3.propertyName, NodeKind.Identifier); - assertNodeKind(binding3.initializer, NodeKind.ReferenceExpr); + assertNodeKind(binding3.initializer, NodeKind.Identifier); }); test("ArrayBinding with out-of-bound reference", () => { @@ -243,7 +243,7 @@ test("ArrayBinding with out-of-bound reference", () => { assertNodeKind(binding1.name, NodeKind.Identifier); assertNodeKind(binding2.name, NodeKind.Identifier); - assertNodeKind(binding2.initializer, NodeKind.ReferenceExpr); + assertNodeKind(binding2.initializer, NodeKind.Identifier); }); test("reflect on a bound function declaration", () => { diff --git a/test/s-expression.test.ts b/test/s-expression.test.ts index 6a5aca8d..1e4ae2fa 100644 --- a/test/s-expression.test.ts +++ b/test/s-expression.test.ts @@ -15,7 +15,7 @@ test("s-expression isomorphism", () => { function equals(self: any, other: any): boolean { if (isNode(self) && isNode(other)) { if (self.kind === other.kind) { - return Array.from(self._arguments).every((thisArg, i) => + return (self._arguments as any[]).every((thisArg, i) => equals(thisArg, other._arguments[i]) ); } else {