diff --git a/benchmark/benchmarks/krausest/vite.config.mts b/benchmark/benchmarks/krausest/vite.config.mts index ae34fe4b7b..8a1ffe006c 100644 --- a/benchmark/benchmarks/krausest/vite.config.mts +++ b/benchmark/benchmarks/krausest/vite.config.mts @@ -17,6 +17,7 @@ const packagePath = (name: string) => { export default defineConfig({ plugins: [benchmark()], resolve: { + extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json', '.d.ts'], alias: { '@glimmer-workspace/benchmark-env': '@glimmer-workspace/benchmark-env/index.ts', '@glimmer/debug': packagePath('@glimmer/debug'), @@ -42,7 +43,7 @@ function benchmark(): Plugin { if (id === '\0@glimmer/env') { return `export const DEBUG = false;`; } else if (id === '\0@glimmer/local-debug-flags') { - return `export const LOCAL_SHOULD_LOG = false;`; + return `export const LOCAL_SHOULD_LOG = false;export const LOCAL_DEBUG=false`; } /** @type {string | undefined} */ let result: string | undefined; diff --git a/packages/@glimmer/interfaces/lib/references.d.ts b/packages/@glimmer/interfaces/lib/references.d.ts index c25708f21c..bd1efb4f40 100644 --- a/packages/@glimmer/interfaces/lib/references.d.ts +++ b/packages/@glimmer/interfaces/lib/references.d.ts @@ -24,6 +24,7 @@ export type ReferenceSymbol = typeof REFERENCE; export interface Reference { [REFERENCE]: ReferenceType; debugLabel?: string | undefined; + debugMeta?: object | undefined; compute: Nullable<() => T>; children: null | Map; } diff --git a/packages/@glimmer/reference/index.ts b/packages/@glimmer/reference/index.ts index 176cf42353..111eb998be 100644 --- a/packages/@glimmer/reference/index.ts +++ b/packages/@glimmer/reference/index.ts @@ -29,4 +29,6 @@ export { UNDEFINED_REFERENCE, updateRef, valueForRef, + setEnableRefTrace, + getRefsTrace } from './lib/reference'; diff --git a/packages/@glimmer/reference/lib/reference.ts b/packages/@glimmer/reference/lib/reference.ts index 42c56b269b..0edfcf82ea 100644 --- a/packages/@glimmer/reference/lib/reference.ts +++ b/packages/@glimmer/reference/lib/reference.ts @@ -49,6 +49,7 @@ class ReferenceImpl implements Reference { public update: Nullable<(val: T) => void> = null; public debugLabel?: string; + public debugMeta?: object; constructor(type: ReferenceType) { this[REFERENCE] = type; @@ -75,20 +76,21 @@ export const NULL_REFERENCE = createPrimitiveRef(null); export const TRUE_REFERENCE = createPrimitiveRef(true as const); export const FALSE_REFERENCE = createPrimitiveRef(false as const); -export function createConstRef(value: T, debugLabel: false | string): Reference { +export function createConstRef(value: T, debugLabel: false | string, meta?: object | undefined): Reference { const ref = new ReferenceImpl(CONSTANT); ref.lastValue = value; ref.tag = CONSTANT_TAG; if (import.meta.env.DEV) { - ref.debugLabel = debugLabel as string; + ref.debugLabel = String(debugLabel || value); + ref.debugMeta = meta; } return ref; } -export function createUnboundRef(value: T, debugLabel: false | string): Reference { +export function createUnboundRef(value: T, debugLabel: false | string, meta?: object | undefined): Reference { const ref = new ReferenceImpl(UNBOUND); ref.lastValue = value; @@ -96,6 +98,7 @@ export function createUnboundRef(value: T, debugLabel: false | string): Refer if (import.meta.env.DEV) { ref.debugLabel = debugLabel as string; + ref.debugMeta = meta; } return ref; @@ -104,7 +107,8 @@ export function createUnboundRef(value: T, debugLabel: false | string): Refer export function createComputeRef( compute: () => T, update: Nullable<(value: T) => void> = null, - debugLabel: false | string = 'unknown' + debugLabel: false | string = 'unknown', + meta?: object | undefined ): Reference { const ref = new ReferenceImpl(COMPUTE); @@ -113,6 +117,7 @@ export function createComputeRef( if (import.meta.env.DEV) { ref.debugLabel = `(result of a \`${debugLabel}\` helper)`; + ref.debugMeta = meta; } return ref; @@ -151,9 +156,31 @@ export function isUpdatableRef(_ref: Reference) { return ref.update !== null; } +const trackedRefs = new WeakMap(); +const refStack = []; +let isTraceEnabled = false; + +export function setEnableRefTrace(enable: boolean) { + isTraceEnabled = enable; +} + +function getCurrentRef() { + return refStack.at(-1); +} + +export function getRefsTrace() { + return trackedRefs; +} + export function valueForRef(_ref: Reference): T { const ref = _ref as ReferenceImpl; + if (import.meta.env.DEV) { + if (refStack.length) { + trackedRefs.get(getCurrentRef()).push(ref); + } + } + let { tag } = ref; if (tag === CONSTANT_TAG) { @@ -164,6 +191,10 @@ export function valueForRef(_ref: Reference): T { let lastValue; if (tag === null || !validateTag(tag, lastRevision)) { + if (import.meta.env.DEV) { + refStack.push(ref); + trackedRefs.set(ref, []); + } const { compute } = ref; const newTag = track( @@ -176,6 +207,10 @@ export function valueForRef(_ref: Reference): T { tag = ref.tag = newTag; ref.lastRevision = valueForTag(newTag); + + if (import.meta.env.DEV) { + refStack.pop(); + } } else { lastValue = ref.lastValue; } diff --git a/packages/@glimmer/reference/package.json b/packages/@glimmer/reference/package.json index dbefda09db..67e9bd2d0d 100644 --- a/packages/@glimmer/reference/package.json +++ b/packages/@glimmer/reference/package.json @@ -9,25 +9,21 @@ "directory": "packages/@glimmer/reference" }, "type": "module", - "main": "index.ts", - "types": "index.ts", - "publishConfig": { - "access": "public", - "types": "dist/dev/index.d.ts", - "exports": { - ".": { - "types": "./dist/dev/index.d.ts", - "development": { - "require": "./dist/dev/index.cjs", - "import": "./dist/dev/index.js" - }, + "access": "public", + "types": "dist/dev/index.d.ts", + "exports": { + ".": { + "types": "./dist/dev/index.d.ts", + "development": { "require": "./dist/dev/index.cjs", - "import": "./dist/prod/index.js" - } - }, - "main": null, - "module": "dist/dev/index.js" + "import": "./dist/dev/index.js" + }, + "require": "./dist/dev/index.cjs", + "import": "./dist/prod/index.js" + } }, + "main": null, + "module": "dist/dev/index.js", "files": [ "dist" ], @@ -39,14 +35,14 @@ }, "dependencies": { "@glimmer/env": "^0.1.7", - "@glimmer/global-context": "workspace:*", - "@glimmer/interfaces": "workspace:*", - "@glimmer/util": "workspace:*", - "@glimmer/validator": "workspace:*" + "@glimmer/global-context": "*", + "@glimmer/interfaces": "*", + "@glimmer/util": "*", + "@glimmer/validator": "*" }, "devDependencies": { - "@glimmer-workspace/build-support": "workspace:*", - "@glimmer/local-debug-flags": "workspace:*", + "@glimmer-workspace/build-support": "*", + "@glimmer/local-debug-flags": "*", "eslint": "^8.52.0", "publint": "^0.2.5", "rollup": "^4.5.1", diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts index 2fa7af7475..bc5866dd08 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts @@ -7,7 +7,7 @@ import type { ScopeBlock, VM as PublicVM, } from '@glimmer/interfaces'; -import type { Reference } from '@glimmer/reference'; +import { createDebugAliasRef, type Reference } from "@glimmer/reference"; import { check, CheckBlockSymbolTable, @@ -28,7 +28,7 @@ import { valueForRef, } from '@glimmer/reference'; import { assert, assign, debugToString, decodeHandle, isObject } from '@glimmer/util'; -import { $v0, CurriedTypes, Op } from '@glimmer/vm'; +import { $s0, $v0, CurriedTypes, Op } from "@glimmer/vm"; import { isCurriedType, resolveCurriedValue } from '../../curried-value'; import { APPEND_OPCODES } from '../../opcodes'; @@ -67,7 +67,10 @@ APPEND_OPCODES.add(Op.Curry, (vm, { op1: type, op2: _isStrict }) => { vm.loadValue( $v0, - createCurryRef(type as CurriedType, definition, owner, capturedArgs, resolver, isStrict) + addHelper(createCurryRef(type as CurriedType, definition, owner, capturedArgs, resolver, isStrict), { + type, + definition + }) ); }); @@ -170,6 +173,13 @@ APPEND_OPCODES.add(Op.GetVariable, (vm, { op1: symbol }) => { APPEND_OPCODES.add(Op.SetVariable, (vm, { op1: symbol }) => { let expr = check(vm.stack.pop(), CheckReference); + if (import.meta.env.DEV && symbol > 0) { + let state = vm.fetchValue($s0); + let symbols = state.table?.symbols; + if (symbols && symbols.length >= symbol) { + expr = createDebugAliasRef(`${symbols[symbol - 1]} from let`, expr); + } + } vm.scope().bindSymbol(symbol, expr); }); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts index 6ac1d0585d..a0f28f0d9f 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts @@ -56,7 +56,7 @@ APPEND_OPCODES.add(Op.Constant, (vm, { op1: other }) => { }); APPEND_OPCODES.add(Op.ConstantReference, (vm, { op1: other }) => { - vm.stack.push(createConstRef(vm[CONSTANTS].getValue(decodeHandle(other)), false)); + vm.stack.push(createConstRef(vm[CONSTANTS].getValue(decodeHandle(other)))); }); APPEND_OPCODES.add(Op.Primitive, (vm, { op1: primitive }) => { diff --git a/packages/@glimmer/runtime/lib/debug-render-tree.ts b/packages/@glimmer/runtime/lib/debug-render-tree.ts index 8d110e8284..3506ca5ce3 100644 --- a/packages/@glimmer/runtime/lib/debug-render-tree.ts +++ b/packages/@glimmer/runtime/lib/debug-render-tree.ts @@ -7,7 +7,7 @@ import type { } from '@glimmer/interfaces'; import { assign, expect, Stack } from '@glimmer/util'; -import { reifyArgsDebug } from './vm/arguments'; +import { getArgTags, reifyArgsDebug } from "./vm/arguments"; interface InternalRenderNode extends RenderNode { bounds: Nullable; diff --git a/packages/@glimmer/runtime/lib/vm/arguments.ts b/packages/@glimmer/runtime/lib/vm/arguments.ts index ce94d4a494..026bd0286a 100644 --- a/packages/@glimmer/runtime/lib/vm/arguments.ts +++ b/packages/@glimmer/runtime/lib/vm/arguments.ts @@ -19,7 +19,7 @@ import type { import type { Reference } from '@glimmer/reference'; import type { Tag } from '@glimmer/validator'; import { check, CheckBlockSymbolTable, CheckHandle, CheckOption, CheckOr } from '@glimmer/debug'; -import { createDebugAliasRef, UNDEFINED_REFERENCE, valueForRef } from '@glimmer/reference'; +import { createDebugAliasRef, UNDEFINED_REFERENCE, valueForRef, setEnableRefTrace, getRefsTrace } from '@glimmer/reference'; import { dict, EMPTY_STRING_ARRAY, emptyArray, enumerate, unwrap } from '@glimmer/util'; import { CONSTANT_TAG } from '@glimmer/validator'; import { $sp } from '@glimmer/vm'; @@ -503,6 +503,7 @@ export function reifyArgs(args: CapturedArguments) { } const ARGUMENT_ERROR = Symbol('ARGUMENT_ERROR'); +const ARGUMENT_TAG = Symbol('ARGUMENT_TAG'); export function isArgumentError(arg: unknown): arg is ArgumentError { return ( @@ -519,6 +520,42 @@ function ArgumentErrorImpl(error: any) { }; } +export function getArgTags(args: CapturedArguments) { + let named = dict(); + for (const [key, value] of Object.entries(args.named)) { + named[key] = value.tag; + } + let positional = args.positional.map(p => p.tag); + return { + named, + positional, + }; +} + +function buildRefTree(tracedRefs: Map, ref: Reference) { + return { + ref: { + label: ref.debugLabel, + value: ref.lastValue, + meta: ref.debugMeta + }, + children: tracedRefs.get(ref)?.map(r => buildRefTree(tracedRefs, r)) || [] + } +} + +export function getArgRefsTree(args: CapturedArguments) { + const tracedRefs = getRefsTrace(); + let named = dict(); + for (const [key, value] of Object.entries(args.named)) { + named[key] = buildRefTree(tracedRefs, value); + } + let positional = args.positional.map(p => buildRefTree(tracedRefs, p)); + return { + named, + positional, + }; +} + export function reifyNamedDebug(named: CapturedNamedArguments) { let reified = dict(); for (const [key, value] of Object.entries(named)) { @@ -543,11 +580,15 @@ export function reifyPositionalDebug(positional: CapturedPositionalArguments) { } export function reifyArgsDebug(args: CapturedArguments) { + setEnableRefTrace(true); let named = reifyNamedDebug(args.named); let positional = reifyPositionalDebug(args.positional); + setEnableRefTrace(false); return { named, positional, + tags: getArgTags(args), + refs: getArgRefsTree(args) }; } diff --git a/packages/@glimmer/runtime/package.json b/packages/@glimmer/runtime/package.json index 29e1356e00..6ef81d872f 100644 --- a/packages/@glimmer/runtime/package.json +++ b/packages/@glimmer/runtime/package.json @@ -1,27 +1,23 @@ { "name": "@glimmer/runtime", - "version": "0.92.4", + "version": "0.92.5-beta", "license": "MIT", "description": "Minimal runtime needed to render Glimmer templates", "repository": "https://github.com/glimmerjs/glimmer-vm/tree/main/packages/@glimmer/runtime", "type": "module", - "main": "index.ts", - "types": "index.ts", - "publishConfig": { - "access": "public", - "types": "dist/dev/index.d.ts", - "exports": { - ".": { - "types": "./dist/dev/index.d.ts", - "development": { - "import": "./dist/dev/index.js" - }, - "import": "./dist/prod/index.js" - } - }, - "main": null, - "module": "dist/dev/index.js" + "access": "public", + "types": "dist/dev/index.d.ts", + "exports": { + ".": { + "types": "./dist/dev/index.d.ts", + "development": { + "import": "./dist/dev/index.js" + }, + "import": "./dist/prod/index.js" + } }, + "main": null, + "module": "dist/dev/index.js", "files": [ "dist" ], @@ -32,24 +28,24 @@ "test:types": "tsc --noEmit -p ../tsconfig.json" }, "dependencies": { - "@glimmer/destroyable": "workspace:*", + "@glimmer/destroyable": "*", "@glimmer/env": "0.1.7", - "@glimmer/global-context": "workspace:*", - "@glimmer/interfaces": "workspace:*", - "@glimmer/manager": "workspace:*", - "@glimmer/owner": "workspace:*", - "@glimmer/program": "workspace:*", - "@glimmer/reference": "workspace:*", - "@glimmer/util": "workspace:*", - "@glimmer/validator": "workspace:*", - "@glimmer/vm": "workspace:*", - "@glimmer/wire-format": "workspace:*" + "@glimmer/global-context": "*", + "@glimmer/interfaces": "*", + "@glimmer/manager": "*", + "@glimmer/owner": "*", + "@glimmer/program": "*", + "@glimmer/reference": "*", + "@glimmer/util": "*", + "@glimmer/validator": "*", + "@glimmer/vm": "*", + "@glimmer/wire-format": "*" }, "devDependencies": { - "@glimmer-workspace/build-support": "workspace:*", - "@glimmer/debug": "workspace:*", - "@glimmer/local-debug-flags": "workspace:*", - "@glimmer/opcode-compiler": "workspace:*", + "@glimmer-workspace/build-support": "*", + "@glimmer/debug": "*", + "@glimmer/local-debug-flags": "*", + "@glimmer/opcode-compiler": "*", "@types/qunit": "^2.19.9", "eslint": "^8.52.0", "publint": "^0.2.5",