diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index 17cdc5a777..341cdc94c7 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -10,12 +10,17 @@ type Scope = { declaredNames: string[]; }; +interface Externals { + [key: string]: Externals | string; +} + type Context = { /** Holds a set of all identifiers that were used in code, but were not declared in code. */ - externalNames: Set; + externalNames: Externals; /** Used to signal to identifiers that they should not treat their resolution as possible external uses. */ ignoreExternalDepth: number; stack: Scope[]; + ancestorChain: JsNode[]; }; type JsNode = babel.Node | acorn.AnyNode; @@ -28,6 +33,64 @@ const tsFallthrough = (ctx: Context, node: { expression: babel.Expression }): ti return transpile(ctx, node.expression); }; +function addExternal( + ctx: Context, + node: babel.ThisExpression | acorn.ThisExpression | babel.Identifier | acorn.Identifier, +) { + // TODO: clean up this mess + const chain: string[] = []; + for (let i = ctx.ancestorChain.length - 1; i >= 0; i--) { + const current = ctx.ancestorChain[i]; + + if (!current) { + break; + } + + if (current.type === 'Identifier') { + chain.push(current.name); + } else if (current.type === 'ThisExpression') { + chain.push('this'); + } else if (current.type === 'MemberExpression' && !current.computed) { + chain.push(`${(current.property as { name: string }).name}`); // TODO: better handling of other nodes + } else { + break; + } + } + + let currentExternals = ctx.externalNames; + if (typeof currentExternals !== 'object') { + throw new Error('??'); + } + for (const elem of chain) { + let nextExternals = currentExternals[elem]; + if (nextExternals) { + if (typeof nextExternals !== 'string') { + currentExternals = nextExternals; + } else { + // we already need this in externals, so we break + break; + } + } else { + const newExt = Object.create(null); + currentExternals[elem] = newExt; + currentExternals = newExt; + } + } + + const lastKey = chain[chain.length - 1]; + if (lastKey !== undefined) { + let parent = ctx.externalNames; + for (const key of chain.slice(0, -1)) { + const next = parent[key]; + if (!next || typeof next === 'string') break; + parent = next; + } + if (typeof parent[lastKey] === 'object') { + parent[lastKey] = chain.join('.'); + } + } +} + const Transpilers: Partial<{ [Type in JsNode['type']]: ( ctx: Context, @@ -70,14 +133,13 @@ const Transpilers: Partial<{ Identifier(ctx, node) { if (ctx.ignoreExternalDepth === 0 && !isDeclared(ctx, node.name)) { - ctx.externalNames.add(node.name); + addExternal(ctx, node); } - return node.name; }, - ThisExpression(ctx) { - ctx.externalNames.add('this'); + ThisExpression(ctx, node) { + addExternal(ctx, node); return 'this'; }, @@ -308,8 +370,11 @@ function transpile(ctx: Context, node: JsNode): tinyest.AnyNode { throw new Error(`Unsupported JS functionality: ${node.type}`); } + ctx.ancestorChain.push(node); // @ts-expect-error - return transpiler(ctx, node); + const result = transpiler(ctx, node); + ctx.ancestorChain.pop(); + return result; } export type TranspilationResult = { @@ -319,7 +384,7 @@ export type TranspilationResult = { * All identifiers found in the function code that are not declared in the * function itself, or in the block that is accessing that identifier. */ - externalNames: string[]; + externalNames: Externals; }; export function extractFunctionParts(rootNode: JsNode): { @@ -424,7 +489,7 @@ export function transpileFn(rootNode: JsNode): TranspilationResult { const { params, body } = extractFunctionParts(rootNode); const ctx: Context = { - externalNames: new Set(), + externalNames: Object.create(null), ignoreExternalDepth: 0, stack: [ { @@ -435,35 +500,36 @@ export function transpileFn(rootNode: JsNode): TranspilationResult { ), }, ], + ancestorChain: [], }; const tinyestBody = transpile(ctx, body); - const externalNames = [...ctx.externalNames]; if (body.type === 'BlockStatement') { return { params, body: tinyestBody as tinyest.Block, - externalNames, + externalNames: ctx.externalNames, }; } return { params, body: [NODE.block, [[NODE.return, tinyestBody as tinyest.Expression]]], - externalNames, + externalNames: ctx.externalNames, }; } export function transpileNode(node: JsNode): tinyest.AnyNode { const ctx: Context = { - externalNames: new Set(), + externalNames: Object.create(null), ignoreExternalDepth: 0, stack: [ { declaredNames: [], }, ], + ancestorChain: [], }; return transpile(ctx, node); diff --git a/packages/tinyest-for-wgsl/tests/parsers.test.ts b/packages/tinyest-for-wgsl/tests/parsers.test.ts index 7b5edb96d2..9290b3774c 100644 --- a/packages/tinyest-for-wgsl/tests/parsers.test.ts +++ b/packages/tinyest-for-wgsl/tests/parsers.test.ts @@ -30,7 +30,7 @@ describe('transpileFn', () => { expect(params).toStrictEqual([]); expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[]]"`); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -41,7 +41,7 @@ describe('transpileFn', () => { expect(params).toStrictEqual([]); expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[]]"`); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -57,7 +57,11 @@ describe('transpileFn', () => { expect(JSON.stringify(body)).toMatchInlineSnapshot( `"[0,[[10,[1,[1,"a","+","b"],"-","c"]]]]"`, ); - expect(externalNames).toStrictEqual(['c']); + expect(externalNames).toMatchInlineSnapshot(` + { + "c": "c", + } + `); }), ); @@ -76,7 +80,11 @@ describe('transpileFn', () => { `"[0,[[13,"a",[5,"0"]],[2,"c","=",[1,"a","+",[5,"2"]]]]]"`, ); // Only 'c' is external, as 'a' is declared in the same scope. - expect(externalNames).toStrictEqual(['c']); + expect(externalNames).toMatchInlineSnapshot(` + { + "c": "c", + } + `); }), ); @@ -97,7 +105,11 @@ describe('transpileFn', () => { `"[0,[[13,"a",[5,"0"]],[0,[[2,"c","=",[1,"a","+",[5,"2"]]]]]]]"`, ); // Only 'c' is external, as 'a' is declared in the outer scope. - expect(externalNames).toStrictEqual(['c']); + expect(externalNames).toMatchInlineSnapshot(` + { + "c": "c", + } + `); }), ); @@ -111,7 +123,15 @@ describe('transpileFn', () => { `"[0,[[10,[7,[7,"external","outside"],"prop"]]]]"`, ); // Only 'external' is external. - expect(externalNames).toStrictEqual(['external']); + expect(externalNames).toMatchInlineSnapshot(` + { + "external": { + "outside": { + "prop": "external.outside.prop", + }, + }, + } + `); }), ); @@ -140,7 +160,7 @@ describe('transpileFn', () => { }, ]); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -186,7 +206,7 @@ describe('transpileFn', () => { }, ]); - expect(externalNames).toStrictEqual([]); + expect(externalNames).toMatchInlineSnapshot(`{}`); }), ); @@ -195,4 +215,44 @@ describe('transpileFn', () => { expect(JSON.stringify(body)).toMatchInlineSnapshot(`"[0,[[10,[7,"x","y"]]]]"`); }); + + it( + 'handles complex external trees', + dualTest((p) => { + const { externalNames } = transpileFn( + p(`() => { + const a = ext.p; + const b = ext.q.a; + const c = ext.q.b; + + const d = ext.r.a; + const e = ext.r; + + const f = ext.s; + const g = ext.s.a; + + const h = ext.t.fn().x; + const i = ext.t.comp['computed'].x; + }`), + ); + + expect(externalNames).toMatchInlineSnapshot(` + { + "ext": { + "p": "ext.p", + "q": { + "a": "ext.q.a", + "b": "ext.q.b", + }, + "r": "ext.r", + "s": "ext.s", + "t": { + "comp": "ext.t.comp", + "fn": "ext.t.fn", + }, + }, + } + `); + }), + ); }); diff --git a/packages/typegpu/src/core/function/fnCore.ts b/packages/typegpu/src/core/function/fnCore.ts index 66429067e5..8f6262eedc 100644 --- a/packages/typegpu/src/core/function/fnCore.ts +++ b/packages/typegpu/src/core/function/fnCore.ts @@ -3,7 +3,7 @@ import { undecorate } from '../../data/dataTypes.ts'; import { type ResolvedSnippet, snip } from '../../data/snippet.ts'; import { type BaseData, isWgslData, isWgslStruct, Void } from '../../data/wgslTypes.ts'; import { MissingLinksError } from '../../errors.ts'; -import { getMetaData, getName } from '../../shared/meta.ts'; +import { getMetaData, getName, type Externals2 } from '../../shared/meta.ts'; import { $getNameForward } from '../../shared/symbols.ts'; import type { ResolutionCtx } from '../../types.ts'; import { applyExternals, type ExternalMap, replaceExternalsInWgsl } from '../resolve/externals.ts'; @@ -148,11 +148,14 @@ export function createFnCore(implementation: Implementation, fnAttribute = ''): const pluginData = getMetaData(implementation); // Passing a record happens prior to version 0.9.0 + // Passing a function happens prior to version 0.12.0 // TODO: Support for this can be removed down the line - const pluginExternals = - typeof pluginData?.externals === 'function' - ? pluginData.externals() - : pluginData?.externals; + let pluginExternals: ExternalMap | Record | undefined = + pluginData?.externals2 + ? externals2ToExternalMap(pluginData.externals2) + : typeof pluginData?.externals === 'function' + ? pluginData.externals() + : pluginData?.externals; if (pluginExternals) { const missing = Object.fromEntries( @@ -170,7 +173,9 @@ export function createFnCore(implementation: Implementation, fnAttribute = ''): } // verify all required externals are present - const missingExternals = ast.externalNames.filter((name) => !(name in externalMap)); + const missingExternals = Object.keys(ast.externalNames).filter( + (name) => !(name in externalMap), + ); if (missingExternals.length > 0) { throw new MissingLinksError(getName(this), missingExternals); } @@ -218,6 +223,22 @@ export function createFnCore(implementation: Implementation, fnAttribute = ''): return core; } +// TODO: deslopify, document, make sure it works as intended +function externals2ToExternalMap(ext2: Externals2): ExternalMap { + const result: ExternalMap = {}; + for (const [key, value] of Object.entries(ext2)) { + if (typeof value === 'function') { + Object.defineProperty(result, key, { + get: value, + enumerable: true, + }); + } else { + result[key] = externals2ToExternalMap(value); + } + } + return result; +} + function isArgUsedInBody(argName: string, body: string): boolean { return new RegExp(`\\b${argName}\\b`).test(body); } diff --git a/packages/typegpu/src/core/function/fnTypes.ts b/packages/typegpu/src/core/function/fnTypes.ts index 18c1f3213a..6d8d762ccf 100644 --- a/packages/typegpu/src/core/function/fnTypes.ts +++ b/packages/typegpu/src/core/function/fnTypes.ts @@ -1,4 +1,3 @@ -import type * as tinyest from 'tinyest'; import type { BuiltinClipDistances } from '../../builtin.ts'; import type { AnyAttribute } from '../../data/attributes.ts'; import type { @@ -27,19 +26,6 @@ import type { InferGPU } from '../../shared/repr.ts'; export type AnyFn = (...args: never[]) => unknown; -/** - * Information extracted from transpiling a JS function. - */ -export type TranspilationResult = { - params: tinyest.FuncParameter[]; - body: tinyest.Block; - /** - * All identifiers found in the function code that are not declared in the - * function itself, or in the block that is accessing that identifier. - */ - externalNames: string[]; -}; - export type InferArgs = { [Idx in keyof T]: InferGPU; }; diff --git a/packages/typegpu/src/shared/meta.ts b/packages/typegpu/src/shared/meta.ts index d401872629..b2d2eae87e 100644 --- a/packages/typegpu/src/shared/meta.ts +++ b/packages/typegpu/src/shared/meta.ts @@ -4,6 +4,14 @@ import type { Block, FuncParameter } from 'tinyest'; import { DEV, TEST } from './env.ts'; import { $getNameForward, isMarkedInternal } from './symbols.ts'; +export interface Externals { + [key: string]: Externals | string; +} + +export interface Externals2 { + [key: string]: Externals2 | (() => unknown); +} + export interface MetaData { v?: number; name?: string | undefined; @@ -11,13 +19,14 @@ export interface MetaData { | { params: FuncParameter[]; body: Block; - externalNames: string[]; + externalNames: Externals; } | undefined; externals?: // Passing a record happens prior to version 0.9.0 // TODO: Support for this can be removed down the line Record | (() => Record) | undefined; + externals2?: Externals2; } /** diff --git a/packages/typegpu/tests/unplugin/autoname.test.ts b/packages/typegpu/tests/unplugin/autoname.test.ts index 45faa7039b..17f525fb0e 100644 --- a/packages/typegpu/tests/unplugin/autoname.test.ts +++ b/packages/typegpu/tests/unplugin/autoname.test.ts @@ -196,8 +196,9 @@ describe('autonaming', () => { }), { v: 1, name: "myFun", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); @@ -206,8 +207,9 @@ describe('autonaming', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[6,"myFun",[]]]],"externalNames":["myFun"]}, + ast: {"params":[],"body":[0,[[6,"myFun",[]]]],"externalNames":{"myFun":"myFun"}}, externals: () => ({myFun}), + externals2: { myFun: () => myFun } }) && $.f)({}))), "main")); return main; }" diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts index 9e8b904bae..5c2470d053 100644 --- a/packages/unplugin-typegpu/src/babel.ts +++ b/packages/unplugin-typegpu/src/babel.ts @@ -4,6 +4,7 @@ import defu from 'defu'; import { transpileFn } from 'tinyest-for-wgsl'; import { FORMAT_VERSION } from 'tinyest'; import { + type Externals, type PluginState, defaultOptions, functionVisitor, @@ -16,6 +17,25 @@ function i(identifier: string): t.Identifier { return t.identifier(identifier); } +function externalsToNode(externals: Externals | string): t.Expression { + if (typeof externals === 'string') { + const chain = externals.split('.'); + if (!chain[0]) { + throw new Error('??'); + } + const base = chain[0] === 'this' ? t.thisExpression() : i(chain[0]); + const propAccess = chain + .slice(1) + .reduce((obj, prop) => t.memberExpression(obj, t.identifier(prop)), base); + return t.arrowFunctionExpression([], propAccess); + } + return t.objectExpression( + Object.entries(externals).map(([name, value]) => + t.objectProperty(i(name), externalsToNode(value), false), + ), + ); +} + function assignMetadata( this: PluginState, path: NodePath, @@ -33,7 +53,7 @@ function assignMetadata( t.blockStatement([ t.returnStatement( t.objectExpression( - ast.externalNames.map((name) => + Object.keys(ast.externalNames).map((name) => name === 'this' ? t.objectProperty(i('this'), t.thisExpression()) : t.objectProperty(i(name), i(name), false, /* shorthand */ name !== 'this'), @@ -43,6 +63,7 @@ function assignMetadata( ]), ), ), + t.objectProperty(i('externals2'), externalsToNode(ast.externalNames)), ]); let expression: t.Expression; diff --git a/packages/unplugin-typegpu/src/core/common.ts b/packages/unplugin-typegpu/src/core/common.ts index eed49b6184..dde1434d5f 100644 --- a/packages/unplugin-typegpu/src/core/common.ts +++ b/packages/unplugin-typegpu/src/core/common.ts @@ -30,6 +30,10 @@ export interface Options { earlyPruning?: boolean | undefined; } +export interface Externals { + [key: string]: Externals | string; +} + export type MetadatableFunction = | t.FunctionDeclaration | t.FunctionExpression diff --git a/packages/unplugin-typegpu/src/core/factory.ts b/packages/unplugin-typegpu/src/core/factory.ts index 57e6ca66ba..6b9d429b15 100644 --- a/packages/unplugin-typegpu/src/core/factory.ts +++ b/packages/unplugin-typegpu/src/core/factory.ts @@ -15,7 +15,7 @@ import { getBlockScope, } from './common.ts'; -import type { Options, UnpluginPluginState, MetadatableFunction } from './common.ts'; +import type { Options, UnpluginPluginState, MetadatableFunction, Externals } from './common.ts'; // I love CommonJS 💔 let traverse = _traverse; @@ -32,19 +32,31 @@ function embedJSON(jsValue: unknown) { .replace(/\u2029/g, '\\u2029'); } +function externalsToString(externals: Externals | string): string { + if (typeof externals === 'string') { + return `() => ${externals}`; + } + const entries = Object.entries(externals).map( + ([key, value]) => `${key}: ${externalsToString(value)}`, + ); + return `{ ${entries.join(', ')} }`; +} + function assignMetadata( this: UnpluginPluginState, path: NodePath, name: string | undefined, ast: ReturnType, ): void { + // TODO: check if we can omit externalNames here const metadata = `{ v: ${FORMAT_VERSION}, name: ${name ? `"${name}"` : 'undefined'}, ast: ${embedJSON(ast)}, - externals: () => ({${ast.externalNames + externals: () => ({${Object.keys(ast.externalNames) .map((e) => (e === 'this' ? '"this": this' : e)) .join(', ')}}), + externals2: ${externalsToString(ast.externalNames)} }`; const visibility = t.isFunctionDeclaration(path.node) diff --git a/packages/unplugin-typegpu/test/aliasing.test.ts b/packages/unplugin-typegpu/test/aliasing.test.ts index 5d07c0c5e7..fc08b76f9a 100644 --- a/packages/unplugin-typegpu/test/aliasing.test.ts +++ b/packages/unplugin-typegpu/test/aliasing.test.ts @@ -21,11 +21,12 @@ describe('[BABEL] tgpu alias gathering', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}));" `); }); @@ -49,11 +50,12 @@ describe('[BABEL] tgpu alias gathering', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}));" `); }); @@ -77,11 +79,12 @@ describe('[BABEL] tgpu alias gathering', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}));" `); }); @@ -103,8 +106,9 @@ describe('[ROLLUP] tgpu alias gathering', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); " `); @@ -126,8 +130,9 @@ describe('[ROLLUP] tgpu alias gathering', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); " `); @@ -149,8 +154,9 @@ describe('[ROLLUP] tgpu alias gathering', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); " `); diff --git a/packages/unplugin-typegpu/test/auto-naming.test.ts b/packages/unplugin-typegpu/test/auto-naming.test.ts index b9a1f01bf8..d62fe38b69 100644 --- a/packages/unplugin-typegpu/test/auto-naming.test.ts +++ b/packages/unplugin-typegpu/test/auto-naming.test.ts @@ -27,11 +27,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})), "fn"); let shell = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([]), "shell"); const cst = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "cst"); @@ -133,11 +134,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})), "myFunction"); const myComputeFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.computeFn({ workgroupSize: [1] @@ -147,11 +149,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})), "myComputeFn"); const myVertexFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexFn({ out: { @@ -167,11 +170,12 @@ describe('[BABEL] auto naming', () => { body: [0, [[10, [104, { ret: [5, "0"] }]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})), "myVertexFn"); const myFragmentFn = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fragmentFn({ in: { @@ -184,12 +188,21 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [6, [7, "d", "vec4f"], []]]]], - externalNames: ["d"] + externalNames: { + d: { + vec4f: "d.vec4f" + } + } }, externals: () => { return { d }; + }, + externals2: { + d: { + vec4f: () => d.vec4f + } } }) && $.f)({})), "myFragmentFn");" `); @@ -336,11 +349,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); const myFun1 = /*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { 'use gpu'; @@ -352,11 +366,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); const myFun2 = /*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function () { 'use gpu'; @@ -368,11 +383,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, [[10, [5, "0"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({});" `); }); @@ -518,11 +534,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})), "myGuardedPipeline"); const anotherGuardedPipeline = /*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root.createGuardedComputePipeline(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { 'use gpu'; @@ -532,11 +549,12 @@ describe('[BABEL] auto naming', () => { ast: { params: [], body: [0, []], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})).dispatchThreads(), "anotherGuardedPipeline"); console.log(myGuardedPipeline, anotherGuardedPipeline);" `); @@ -569,8 +587,9 @@ describe('[ROLLUP] auto naming', () => { (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {}), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))), "fn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.accessor(d.u32), "accessor")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.const(d.u32, 1), "cst")); @@ -653,23 +672,26 @@ describe('[ROLLUP] auto naming', () => { (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => 0), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))), "myFunction")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.computeFn({ workgroupSize: [1] })( (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => {}), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})), ), "myComputeFn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexFn({ out: { ret: d.i32 } })( (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => ({ ret: 0 })), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[104,{"ret":[5,"0"]}]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})), ), "myVertexFn")); (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fragmentFn({ @@ -679,8 +701,9 @@ describe('[ROLLUP] auto naming', () => { (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => d.vec4f()), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":["d"]}, + ast: {"params":[],"body":[0,[[10,[6,[7,"d","vec4f"],[]]]]],"externalNames":{"d":{"vec4f":"d.vec4f"}}}, externals: () => ({d}), + externals2: { d: { vec4f: () => d.vec4f } } }) && $.f)({})), ), "myFragmentFn")); " @@ -834,8 +857,9 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: "myFun3", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); const myFun1 = (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { @@ -844,8 +868,9 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: "myFun1", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); const myFun2 = (/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function () { @@ -854,8 +879,9 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: "myFun2", - ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[10,[5,"0"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); @@ -1031,8 +1057,9 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))), "myGuardedPipeline")); const anotherGuardedPipeline = (/*#__PURE__*/(globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(root @@ -1041,8 +1068,9 @@ describe('[ROLLUP] auto naming', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))) .dispatchThreads(), "anotherGuardedPipeline")); diff --git a/packages/unplugin-typegpu/test/nested-externals.test.ts b/packages/unplugin-typegpu/test/nested-externals.test.ts new file mode 100644 index 0000000000..0c32a25af9 --- /dev/null +++ b/packages/unplugin-typegpu/test/nested-externals.test.ts @@ -0,0 +1,197 @@ +import { describe, expect, it } from 'vitest'; +import { babelTransform, rollupTransform } from './transform.ts'; + +function extractExternals(code: string | undefined | null) { + if (!code) { + throw new Error('Expected code to be truthy.'); + } + const startIndex = code.indexOf('externals:') + 'externals:'.length; + const endIndex = code.indexOf('}) && $.f'); + return code.slice(startIndex, endIndex).trim(); +} + +const codes = { + 'allows multiple usages of one external': `\ + const ext = { + value: 7, + config: { + multiplier: 1, + zero: 0, + } + }; + const fn = () => { + 'use gpu'; + const a = ext.value; + const b = ext.config.multiplier; + const c = ext.config.zero; + const d = ext.config.multiplier; + }; + console.log(fn);`, + // --- + 'treats dereference like a regular external': `\ + import tgpu, { d } from 'typegpu'; + + const root = await tgpu.init(); + const buffer = root.createMutable(d.vec2u); + const fn = () => { + 'use gpu'; + const a = buffer.$.x; + }; + console.log(fn);`, + // --- + 'skips computed prop access': `\ + const ext = { + n: 1, + }; + + const fn = () => { + 'use gpu'; + const a = ext['n']; + }; + console.log(fn);`, + // --- + 'skips calls': `\ + import tgpu, { d } from 'typegpu'; + + const fn = () => { + 'use gpu'; + const a = ext.comptime().x; + const b = ext.runtime().y; + }; + + const ext = { + comptime: tgpu.comptime(() => { + return d.vec4f(); + }), + runtime: () => { + 'use gpu'; + return d.vec4f(); + }, + }; + + console.log(fn);`, + // TODO: private access test +}; + +describe('externals gathering', () => { + describe('BABEL', () => { + it('allows multiple usages of one external', () => { + const code = codes['allows multiple usages of one external']; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + ext + }; + }, + externals2: { + ext: { + value: () => ext.value, + config: { + multiplier: () => ext.config.multiplier, + zero: () => ext.config.zero + } + } + }" + `); + }); + + it('treats dereference like a regular external', () => { + const code = codes['treats dereference like a regular external']; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + buffer + }; + }, + externals2: { + buffer: { + $: { + x: () => buffer.$.x + } + } + }" + `); + }); + + it('skips computed prop access', () => { + const code = codes['skips computed prop access']; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + ext + }; + }, + externals2: { + ext: () => ext + }" + `); + }); + + it('skips calls', () => { + const code = codes['skips calls']; + + expect(extractExternals(babelTransform(code))).toMatchInlineSnapshot(` + "() => { + return { + ext + }; + }, + externals2: { + ext: { + comptime: () => ext.comptime, + runtime: () => ext.runtime + } + }" + `); + }); + }); + + describe('ROLLUP', () => { + it('allows multiple usages of one external', async () => { + const code = codes['allows multiple usages of one external']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + ` + "() => ({ext}), + externals2: { ext: { value: () => ext.value, config: { multiplier: () => ext.config.multiplier, zero: () => ext.config.zero } } }" + `, + ); + }); + + it('treats dereference like a regular external', async () => { + const code = codes['treats dereference like a regular external']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + ` + "() => ({buffer}), + externals2: { buffer: { $: { x: () => buffer.$.x } } }" + `, + ); + }); + + it('skips computed prop access', async () => { + const code = codes['skips computed prop access']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + ` + "() => ({ext}), + externals2: { ext: () => ext }" + `, + ); + }); + + it('skips calls', async () => { + const code = codes['skips calls']; + + expect(extractExternals(await rollupTransform(code))).toMatchInlineSnapshot( + ` + "() => ({ext}), + externals2: { ext: { comptime: () => ext.comptime, runtime: () => ext.runtime } }" + `, + ); + }); + }); +}); diff --git a/packages/unplugin-typegpu/test/parser-options.test.ts b/packages/unplugin-typegpu/test/parser-options.test.ts index 76225b0695..6cd850ff8f 100644 --- a/packages/unplugin-typegpu/test/parser-options.test.ts +++ b/packages/unplugin-typegpu/test/parser-options.test.ts @@ -21,11 +21,12 @@ describe('[BABEL] parser options', () => { ast: { params: [], body: [0, [[13, "x", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}));" `); @@ -61,8 +62,9 @@ describe('[ROLLUP] tgpu alias gathering', async () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, + ast: {"params":[],"body":[0,[]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); console.log(increment); diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts index 3ba05a8b9b..deab2cb936 100644 --- a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts +++ b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts @@ -45,13 +45,36 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "tmp", [7, [7, "counter", "$"], "x"]], [2, [7, [7, "counter", "$"], "x"], "=", [7, [7, "counter", "$"], "y"]], [2, [7, [7, "counter", "$"], "y"], "+=", "tmp"], [2, [7, [7, "counter", "$"], "z"], "+=", [6, [7, "d", "f32"], [[7, [7, "input", "num"], "x"]]]]]], - externalNames: ["counter", "d"] + externalNames: { + counter: { + $: { + x: "counter.$.x", + y: "counter.$.y", + z: "counter.$.z" + } + }, + d: { + f32: "d.f32" + } + } }, externals: () => { return { counter, d }; + }, + externals2: { + counter: { + $: { + x: () => counter.$.x, + y: () => counter.$.y, + z: () => counter.$.z + } + }, + d: { + f32: () => d.f32 + } } }) && $.f)({}));" `); @@ -90,11 +113,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); const b = tgpu.fn([])(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { const y = 2 + 2; @@ -104,11 +128,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: { params: [], body: [0, [[13, "y", [1, [5, "2"], "+", [5, "2"]]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); const cx = 2; const c = tgpu.fn([])(/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => cx, { @@ -117,12 +142,17 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: { params: [], body: [0, [[10, "cx"]]], - externalNames: ["cx"] + externalNames: { + cx: "cx" + } }, externals: () => { return { cx }; + }, + externals2: { + cx: () => cx } }) && $.f)({})); const d = tgpu.fn([])('() {}');" @@ -176,11 +206,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); const funcWithAs = tgpu.computeFn({ workgroupSize: [1] @@ -195,11 +226,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); const funcWithSatisfies = tgpu.computeFn({ workgroupSize: [1] @@ -214,11 +246,12 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { name: "input" }], body: [0, [[13, "x", true]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}));" `); }); @@ -255,12 +288,25 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: { params: [], body: [0, [[10, [7, [7, "this", "myBuffer"], "$"]]]], - externalNames: ["this"] + externalNames: { + "this": { + myBuffer: { + $: "this.myBuffer.$" + } + } + } }, externals: () => { return { this: this }; + }, + externals2: { + this: { + myBuffer: { + $: () => this.myBuffer.$ + } + } } }) && $.f)({})); } @@ -308,8 +354,9 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","$"],"x"]],[2,[7,[7,"counter","$"],"x"],"=",[7,[7,"counter","$"],"y"]],[2,[7,[7,"counter","$"],"y"],"+=","tmp"],[2,[7,[7,"counter","$"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":["counter","d"]}, + ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","$"],"x"]],[2,[7,[7,"counter","$"],"x"],"=",[7,[7,"counter","$"],"y"]],[2,[7,[7,"counter","$"],"y"],"+=","tmp"],[2,[7,[7,"counter","$"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":{"counter":{"$":{"x":"counter.$.x","y":"counter.$.y","z":"counter.$.z"}},"d":{"f32":"d.f32"}}}, externals: () => ({counter, d}), + externals2: { counter: { $: { x: () => counter.$.x, y: () => counter.$.y, z: () => counter.$.z } }, d: { f32: () => d.f32 } } }) && $.f)({}))); " `); @@ -340,24 +387,27 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]}, + ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); const cx = 2; tgpu.fn([])((/*#__PURE__*/($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => cx), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]}, + ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":{"cx":"cx"}}, externals: () => ({cx}), + externals2: { cx: () => cx } }) && $.f)({}))); tgpu.fn([])('() {}'); @@ -413,8 +463,9 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }), { v: 1, name: undefined, - ast: {"params":[],"body":[0,[[10,[7,[7,"this","myBuffer"],"$"]]]],"externalNames":["this"]}, + ast: {"params":[],"body":[0,[[10,[7,[7,"this","myBuffer"],"$"]]]],"externalNames":{"this":{"myBuffer":{"$":"this.myBuffer.$"}}}}, externals: () => ({"this": this}), + externals2: { this: { myBuffer: { $: () => this.myBuffer.$ } } } }) && $.f)({}))); } diff --git a/packages/unplugin-typegpu/test/typescript-syntax.test.ts b/packages/unplugin-typegpu/test/typescript-syntax.test.ts index b6af6fbff9..f0a5d475dc 100644 --- a/packages/unplugin-typegpu/test/typescript-syntax.test.ts +++ b/packages/unplugin-typegpu/test/typescript-syntax.test.ts @@ -29,11 +29,12 @@ describe('as type', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({});" `); }); diff --git a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts index 9a32b06b5a..3db2c75e36 100644 --- a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts +++ b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts @@ -37,11 +37,12 @@ describe('"use gpu" marked arrow function, assigned to a const', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); const addCPU = (a, b) => { return a + b; @@ -60,8 +61,9 @@ describe('"use gpu" marked arrow function, assigned to a const', () => { }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); const addCPU = (a, b) => { @@ -110,11 +112,12 @@ describe('marked arrow functions passed to shells', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); shell((a, b) => { return a + b; @@ -134,8 +137,9 @@ describe('marked arrow functions passed to shells', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); shell((a, b) => { @@ -182,11 +186,12 @@ describe('marked anonymous function expressions passed to shells', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); shell(function (a, b) { return a + b; @@ -206,8 +211,9 @@ describe('marked anonymous function expressions passed to shells', () => { }), { v: 1, name: undefined, - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); shell(function(a, b) { @@ -254,11 +260,12 @@ describe('marked named function expressions passed to shells', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({})); shell(function addCPU(a, b) { return a + b; @@ -278,8 +285,9 @@ describe('marked named function expressions passed to shells', () => { }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({}))); shell(function addCPU(a, b) { @@ -327,11 +335,12 @@ describe('marked function statements', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); function addCPU(a, b) { return a + b; @@ -350,8 +359,9 @@ describe('marked function statements', () => { }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); @@ -415,11 +425,12 @@ describe('marked object methods', () => { name: "b" }], body: [0, [[10, [1, "a", "%", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}) }; @@ -445,12 +456,21 @@ describe('marked object methods', () => { name: "n" }], body: [0, [[11, [1, "n", "<=", [5, "1"]], [0, [[10, false]]]], [14, [12, "i", [5, "2"]], [1, "i", "<", "n"], [102, "++", "i"], [0, [[11, [1, [6, [7, "obj", "mod"], ["n", "i"]], "===", [5, "0"]], [0, [[10, false]]]]]]], [10, true]]], - externalNames: ["obj"] + externalNames: { + obj: { + mod: "obj.mod" + } + } }, externals: () => { return { obj }; + }, + externals2: { + obj: { + mod: () => obj.mod + } } }) && $.f)({}); console.log(obj, isPrime);" @@ -467,8 +487,9 @@ describe('marked object methods', () => { }), { v: 1, name: "mod", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","%","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","%","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})) }; @@ -488,8 +509,9 @@ describe('marked object methods', () => { }), { v: 1, name: "isPrime", - ast: {"params":[{"type":"i","name":"n"}],"body":[0,[[11,[1,"n","<=",[5,"1"]],[0,[[10,false]]]],[14,[12,"i",[5,"2"]],[1,"i","<","n"],[102,"++","i"],[0,[[11,[1,[6,[7,"obj","mod"],["n","i"]],"===",[5,"0"]],[0,[[10,false]]]]]]],[10,true]]],"externalNames":["obj"]}, + ast: {"params":[{"type":"i","name":"n"}],"body":[0,[[11,[1,"n","<=",[5,"1"]],[0,[[10,false]]]],[14,[12,"i",[5,"2"]],[1,"i","<","n"],[102,"++","i"],[0,[[11,[1,[6,[7,"obj","mod"],["n","i"]],"===",[5,"0"]],[0,[[10,false]]]]]]],[10,true]]],"externalNames":{"obj":{"mod":"obj.mod"}}}, externals: () => ({obj}), + externals2: { obj: { mod: () => obj.mod } } }) && $.f)({})); console.log(obj, isPrime); @@ -543,12 +565,21 @@ describe('transforms numeric operations', () => { name: "b" }], body: [0, [[12, "c", [1, [1, "a", "+", "b"], "+", [5, "2"]]], [2, "c", "+=", [1, [5, "2"], "*", "b"]], [2, [7, "countMutable", "$"], "+=", [5, "3"]]]], - externalNames: ["countMutable"] + externalNames: { + countMutable: { + $: "countMutable.$" + } + } }, externals: () => { return { countMutable }; + }, + externals2: { + countMutable: { + $: () => countMutable.$ + } } }) && $.f)({}); console.log(main);" @@ -572,8 +603,9 @@ describe('transforms numeric operations', () => { }), { v: 1, name: "main", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]],[2,[7,"countMutable","$"],"+=",[5,"3"]]]],"externalNames":["countMutable"]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]],[2,[7,"countMutable","$"],"+=",[5,"3"]]]],"externalNames":{"countMutable":{"$":"countMutable.$"}}}, externals: () => ({countMutable}), + externals2: { countMutable: { $: () => countMutable.$ } } }) && $.f)({})); console.log(main); @@ -622,11 +654,12 @@ describe('hoists global function statements marked with "use gpu"', () => { name: "b" }], body: [0, [[10, [1, "a", "*", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); /** ADD */ // another comment @@ -646,11 +679,12 @@ describe('hoists global function statements marked with "use gpu"', () => { name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); console.log(add, mul);" `); @@ -666,8 +700,9 @@ describe('hoists global function statements marked with "use gpu"', () => { }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -678,8 +713,9 @@ describe('hoists global function statements marked with "use gpu"', () => { }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add, mul); @@ -730,11 +766,12 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe name: "b" }], body: [0, [[10, [1, "a", "*", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); /** ADD */ // another comment @@ -754,11 +791,12 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); console.log(add, mul); }" @@ -776,8 +814,9 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -788,8 +827,9 @@ describe('hoists function statements marked with "use gpu", scoped inside anothe }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add, mul); @@ -851,11 +891,12 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr name: "b" }], body: [0, [[10, [1, "a", "*", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); /** ADD */ // another comment @@ -875,11 +916,12 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr name: "b" }], body: [0, [[10, [1, "a", "+", "b"]]]], - externalNames: [] + externalNames: {} }, externals: () => { return {}; - } + }, + externals2: {} }) && $.f)({}); console.log(add, mul); };" @@ -897,8 +939,9 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -909,8 +952,9 @@ describe('hoists function statements marked with "use gpu", scoped inside an arr }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add, mul); @@ -974,12 +1018,17 @@ describe('hoists function statements marked with "use gpu", scoped inside an if name: "b" }], body: [0, [[10, [1, [1, "a", "*", "b"], "*", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, externals: () => { return { c }; + }, + externals2: { + c: () => c } }) && $.f)({}); /** ADD */ @@ -1000,12 +1049,17 @@ describe('hoists function statements marked with "use gpu", scoped inside an if name: "b" }], body: [0, [[10, [1, [1, "a", "+", "b"], "+", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, externals: () => { return { c }; + }, + externals2: { + c: () => c } }) && $.f)({}); console.log(add, mul); @@ -1025,8 +1079,9 @@ describe('hoists function statements marked with "use gpu", scoped inside an if }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":["c"]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":{"c":"c"}}, externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); /** ADD */ @@ -1037,8 +1092,9 @@ describe('hoists function statements marked with "use gpu", scoped inside an if }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":["c"]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":{"c":"c"}}, externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); console.log(add, mul); @@ -1106,12 +1162,17 @@ describe('replaces function statements marked with "use gpu" in place when condi name: "b" }], body: [0, [[10, [1, [1, "a", "+", "b"], "+", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, externals: () => { return { c }; + }, + externals2: { + c: () => c } }) && $.f)({}); break; @@ -1134,12 +1195,17 @@ describe('replaces function statements marked with "use gpu" in place when condi name: "b" }], body: [0, [[10, [1, [1, "a", "*", "b"], "*", "c"]]]], - externalNames: ["c"] + externalNames: { + c: "c" + } }, externals: () => { return { c }; + }, + externals2: { + c: () => c } }) && $.f)({}); break; @@ -1162,8 +1228,9 @@ describe('replaces function statements marked with "use gpu" in place when condi }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":["c"]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","+","b"],"+","c"]]]],"externalNames":{"c":"c"}}, externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); @@ -1177,8 +1244,9 @@ describe('replaces function statements marked with "use gpu" in place when condi }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":["c"]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,[1,"a","*","b"],"*","c"]]]],"externalNames":{"c":"c"}}, externals: () => ({c}), + externals2: { c: () => c } }) && $.f)({})); @@ -1216,8 +1284,9 @@ test('hoists exported marked function statements', async () => { }), { v: 1, name: "mul", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","*","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); /** ADD */ @@ -1227,8 +1296,9 @@ test('hoists exported marked function statements', async () => { }), { v: 1, name: "add", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":{}}, externals: () => ({}), + externals2: { } }) && $.f)({})); console.log(add);