import { expect, it } from "vitest"; import * as t from "@babel/types"; import { createSemanticModuleInterop } from "./javascriptModuleInterop.js"; import { parseJavaScriptSource } from "./javascriptSourceParser.js"; import { indexJavaScriptBindingsSteps } from "./javascriptSemanticAnalysis.js"; import { completeSemanticSteps, traverseJavaScriptAst, } from "./javascriptSemanticTraversal.js"; const invocationKinds = (source: string) => { const parsed = parseJavaScriptSource(source); if (parsed === null) throw new Error("invocation fixture did not parse"); const state = completeSemanticSteps( indexJavaScriptBindingsSteps(parsed.program), ); const interop = createSemanticModuleInterop(state); const invocations: (ReturnType | null)[] = []; traverseJavaScriptAst(parsed.program, { enter: (node) => { if (t.isCallExpression(node) || t.isOptionalCallExpression(node)) invocations.push(interop.intrinsicInvocation(node) ?? null); }, }); return invocations; }; it.each([ 'const change = Object.defineProperty.call; change(null,Object,"create",{value:customCreate});', 'const {apply:change} = Object.defineProperty; change(null,[Object,"create",{value:customCreate}]);', 'const change = Reflect.apply.call; change(null,Object.defineProperty,null,[Object,"create",{value:customCreate}]);', ])( "keeps a bare invocation member alias unknown, not effect-free: %s", (source) => { expect(invocationKinds(source)).toEqual([ expect.objectContaining({ kind: "opaque" }), ]); }, ); it("keeps a comma-expression intrinsic call unknown when its receiver is erased", () => { expect( invocationKinds( '(0, Object.defineProperty.call)(null, Object, "create", {value: customCreate});', ), ).toEqual([ expect.objectContaining({ kind: "opaque", path: ["intrinsic", "Object", "defineProperty", "call"], }), ]); });