diff --git a/packages/compiler/src/frontend/lowering/cycle-initialization.ts b/packages/compiler/src/frontend/lowering/cycle-initialization.ts index 4b1e74fcf0..3b865e6c1e 100644 --- a/packages/compiler/src/frontend/lowering/cycle-initialization.ts +++ b/packages/compiler/src/frontend/lowering/cycle-initialization.ts @@ -240,7 +240,12 @@ function resolvesTo( symbol: ts.Symbol, aliases: boolean, ): boolean { - let referenced = lowerer.checker.getSymbolAtLocation(node); + // A shorthand property's name (`{ K }`) carries the property's symbol; + // the binding it reads is the shorthand's value symbol. + let referenced = + ts.isShorthandPropertyAssignment(node.parent) && node.parent.name === node + ? lowerer.checker.getShorthandAssignmentValueSymbol(node.parent) + : lowerer.checker.getSymbolAtLocation(node); if (aliases && referenced !== undefined && referenced.flags & ts.SymbolFlags.Alias) referenced = lowerer.checker.getAliasedSymbol(referenced); return referenced === symbol; diff --git a/packages/compiler/src/frontend/lowering/expressions/identifiers.ts b/packages/compiler/src/frontend/lowering/expressions/identifiers.ts index ef0d8a2d1e..5dc7cb77d7 100644 --- a/packages/compiler/src/frontend/lowering/expressions/identifiers.ts +++ b/packages/compiler/src/frontend/lowering/expressions/identifiers.ts @@ -3,6 +3,7 @@ import { predeclareForwardClass } from "../lower-stmts.js"; import { cjsMemberAliasValueOf } from "../cjs-exports-object.js"; import { builtinClassValue, builtinMemberClassKind } from "../builtins/builtin-class-dyn.js"; import * as ts from "../../ts7/adapter.js"; +import type { ClassInfo } from "../lower-classes.js"; import { InternalCompilerError } from "../../../errors.js"; import * as posix from "node:path/posix"; import { type Lowerer, PoisonError } from "../lowerer.js"; @@ -860,57 +861,15 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc: // reach here — their hooks claim the property/new forms first). { const sym = lowerer.resolveValueSymbol(expr); - let classInfo = sym ? lowerer.classBySymbol.get(sym) : undefined; - // A class local to a function lowered once per instantiation - // (`$constructor` called with different argument types) registers per - // instance under the same symbol: a hoisted function of a later - // instance, lowered before its own class statement, must not reach the - // class an earlier instance collected. - const declaration = sym?.valueDeclaration; - if ( - classInfo && - lowerer.localClassInstantiations.length > 0 && - declaration && - ts.isClassDeclaration(declaration) && - lowerer.classNamer(declaration) !== classInfo.def.name - ) - classInfo = undefined; + const classInfo = sym ? currentClassInfo(lowerer, sym) : undefined; // A local class referenced from a function hoisted above its statement: // its TDZ box, holding the class as a checked-dynamic constructor. if (!classInfo && sym && predeclareForwardClass(lowerer, sym)) { const local = lowerer.resolveLocal(expr); if (local) return { kind: "varRef", localId: local.id, type: local.type, loc }; } - // A GENERIC class name whose CONTEXTUAL type pins one instantiation - // (`const B: new (v: string) => Counter = Counter`): the - // value is that instantiation's class object — the generic-fn - // pinning rule on the static side. Unpinned references fall - // through to classValueRef's family fence. - if (classInfo?.generic) { - const ctxT = lowerer.checker.getContextualType(expr); - const mapped = ctxT ? lowerer.mapTypeOf(ctxT) : null; - const instInfo = - mapped?.kind === "classval" ? lowerer.classes.get(mapped.className) : undefined; - if (instInfo && instInfo.genericInstance?.family === classInfo) { - return lowerer.classValueRef(instInfo, expr); - } - } - if (classInfo) { - // A decorated name a replacing decorator can REBIND: the value - // is the decoration result — the mutable classval global %init - // assigned at the class statement (TC39's name binding), never - // the declaration's own class object. - const decoratedGlobal = classInfo.classDecorators?.valueGlobalId; - if (decoratedGlobal !== undefined) { - return { - kind: "varRef", - localId: decoratedGlobal, - type: { kind: "classval", className: classInfo.def.name }, - loc, - }; - } - return lowerer.classValueRef(classInfo, expr); - } + const value = sym ? classNameValue(lowerer, sym, expr, loc) : null; + if (value) return value; // The enum OBJECT as a value (member reads never reach here — the // access hooks fold them): iteration, storage, reverse lookups // through variables all land on this pointed fence. @@ -967,6 +926,67 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc: ); } +/** A program CLASS NAME as a value (`sym` is the name's value symbol; an + * identifier or a shorthand property `{ Name }` spells it): the classRef + * over the class's immortal class object, or null when `sym` names no + * program class. */ +/** The program class `sym` names here, or undefined. A class local to a + * function lowered once per instantiation (`$constructor` called with + * different argument types) registers per instance under the same symbol: + * a hoisted function of a later instance, lowered before its own class + * statement, must not reach the class an earlier instance collected. */ +function currentClassInfo(lowerer: Lowerer, sym: ts.Symbol): ClassInfo | undefined { + const classInfo = lowerer.classBySymbol.get(sym); + const declaration = sym.valueDeclaration; + if ( + classInfo && + lowerer.localClassInstantiations.length > 0 && + declaration && + ts.isClassDeclaration(declaration) && + lowerer.classNamer(declaration) !== classInfo.def.name + ) + return undefined; + return classInfo; +} + +export function classNameValue( + lowerer: Lowerer, + sym: ts.Symbol, + expr: ts.Identifier, + loc: SrcLoc, +): IrExpr | null { + const classInfo = currentClassInfo(lowerer, sym); + if (!classInfo) return null; + // A GENERIC class name whose CONTEXTUAL type pins one instantiation + // (`const B: new (v: string) => Counter = Counter`): the + // value is that instantiation's class object — the generic-fn + // pinning rule on the static side. Unpinned references fall + // through to classValueRef's family fence. + if (classInfo.generic) { + const ctxT = lowerer.checker.getContextualType(expr); + const mapped = ctxT ? lowerer.mapTypeOf(ctxT) : null; + const instInfo = + mapped?.kind === "classval" ? lowerer.classes.get(mapped.className) : undefined; + if (instInfo && instInfo.genericInstance?.family === classInfo) { + return lowerer.classValueRef(instInfo, expr); + } + } + // A decorated name a replacing decorator can REBIND: the value + // is the decoration result — the mutable classval global %init + // assigned at the class statement (TC39's name binding), never + // the declaration's own class object. + const decoratedGlobal = classInfo.classDecorators?.valueGlobalId; + if (decoratedGlobal !== undefined) { + return { + kind: "varRef", + localId: decoratedGlobal, + type: { kind: "classval", className: classInfo.def.name }, + loc, + }; + } + return lowerer.classValueRef(classInfo, expr); +} + function moduleFileName(lowerer: Lowerer, sf: ts.SourceFile): string { return lowerer.targetPlatform === "wasi" ? (wasiGuestPath(sf.fileName) ?? sf.fileName.replace(/\\/g, "/")) diff --git a/packages/compiler/src/frontend/lowering/expressions/object-literals.ts b/packages/compiler/src/frontend/lowering/expressions/object-literals.ts index 4cc0bee1a4..33b4d350b3 100644 --- a/packages/compiler/src/frontend/lowering/expressions/object-literals.ts +++ b/packages/compiler/src/frontend/lowering/expressions/object-literals.ts @@ -36,6 +36,7 @@ import { tryLowerExpression } from "./try-lower-expression.js"; import { fenceSymbolFieldCopy } from "../symbol-fields.js"; import { planUnionRetag } from "../union-retag.js"; import { jsHoldsDynObject, runtimeOptionalStorageOperand } from "../lower-exprs.js"; +import { classNameValue } from "./identifiers.js"; import { builtinGlobalValue } from "./property-access.js"; import { holdsEvolvingArray } from "../lower-stmts.js"; @@ -3309,6 +3310,10 @@ export function lowerShorthandValue( if (g) { return lowerer.maybeNarrow({ kind: "varRef", localId: g.id, type: g.type, loc }, propName); } + // A class declaration's name (`{ Name }`): the class value, exactly + // like the long form `{ Name: Name }`. + const classValue = classNameValue(lowerer, resolved, propName, loc); + if (classValue) return classValue; const sig = lowerer.fnSigsBySymbol.get(resolved); const decl = resolved.declarations[0]; if ( @@ -3337,22 +3342,6 @@ export function lowerShorthandValue( `generic functions as values (call '${propName.text}' directly)`, ); } - // A program class name (`{ parse, TomlError }`, an imported class - // included): the class object, exactly the long form `{ C: C }`. - // Generic classes keep the identifier path's contextual pinning. - const classInfo = lowerer.classBySymbol.get(resolved); - if (classInfo && !classInfo.generic) { - const decoratedGlobal = classInfo.classDecorators?.valueGlobalId; - if (decoratedGlobal !== undefined) { - return { - kind: "varRef", - localId: decoratedGlobal, - type: { kind: "classval", className: classInfo.def.name }, - loc, - }; - } - return lowerer.classValueRef(classInfo, propName); - } // A standard-library global (`{ Date }`): the value the long form // `{ Date: Date }` reads. if (lowerer.isStdlibSymbol(resolved)) return lowerer.lowerExpr(propName); diff --git a/packages/compiler/src/frontend/lowering/expressions/relational.ts b/packages/compiler/src/frontend/lowering/expressions/relational.ts index b731e49126..20e4237145 100644 --- a/packages/compiler/src/frontend/lowering/expressions/relational.ts +++ b/packages/compiler/src/frontend/lowering/expressions/relational.ts @@ -476,22 +476,14 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc const value = left.type.className === targetInfo.def.name || lowerer.isSubclassOf(left.type.className, targetInfo.def.name); + const answer: IrExpr = { kind: "boolLit", value, type: BOOL, loc }; if ( left.kind === "varRef" && (ts.isIdentifier(expr.right) || expr.right.kind === ts.SyntaxKind.ThisKeyword) ) { - return readsClassAfter(lowerer, targetInfo, expr.right, [], { - kind: "boolLit", - value, - type: BOOL, - loc, - }); + return readsClassAfter(lowerer, targetInfo, expr.right, [], answer); } - lowerer.unsupported( - "SC1090", - expr, - "statically-decided 'instanceof' on computed operands (bind the values to variables first)", - ); + return evaluatedOperandsThen(lowerer, left, expr.right, answer); } const constructor = tryLowerExpression(lowerer, expr.right); if (constructor?.type.kind === "dyn" && isJsSourceFile(ts.getSourceFileOfNode(expr))) { @@ -832,19 +824,26 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc leftClass === className || lowerer.isSubclassOf(leftClass, className); const value = isInstance(target.def.name) || (duplex !== undefined && isInstance(duplex.def.name)); + const answer: IrExpr = { kind: "boolLit", value, type: BOOL, loc }; if (left.kind === "varRef") { - return readsClassAfter(lowerer, target, expr.right, [], { - kind: "boolLit", - value, - type: BOOL, - loc, - }); + return readsClassAfter(lowerer, target, expr.right, [], answer); } - lowerer.unsupported( - "SC1090", - expr, - "statically-decided 'instanceof' on computed operands (bind the value to a variable first)", - ); + return evaluatedOperandsThen(lowerer, left, expr.right, answer); +} + +/** A statically decided `instanceof` over computed operands: both operands + * still evaluate, left then right, for their effects (a construction, a + * call, a property read, a binding read that throws before the binding is + * initialized), and the folded answer follows. */ +function evaluatedOperandsThen( + lowerer: Lowerer, + left: IrExpr, + right: ts.Expression, + answer: IrExpr, +): IrExpr { + const stmts: IrStmt[] = [{ kind: "exprStmt", expr: left, loc: left.loc }]; + stmts.push({ kind: "exprStmt", expr: lowerer.lowerExpr(right), loc: locOf(right) }); + return { kind: "seqExpr", stmts, result: answer, type: answer.type, loc: answer.loc }; } /** `x instanceof K` for a union-typed operand whose arms are class diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6585-class-value-shorthand.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6585-class-value-shorthand.cjs.json new file mode 100644 index 0000000000..220dfa3964 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6585-class-value-shorthand.cjs.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6585-class-value-shorthand.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6586-class-value-shorthand.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6586-class-value-shorthand.ts.json new file mode 100644 index 0000000000..3d6dede39c --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6586-class-value-shorthand.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6586-class-value-shorthand.ts" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6587-class-value-shorthand-exports/main.cjs.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6587-class-value-shorthand-exports/main.cjs.json new file mode 100644 index 0000000000..b15756051f --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6587-class-value-shorthand-exports/main.cjs.json @@ -0,0 +1,7 @@ +{ + "order": [ + "/tests/corpus/6587-class-value-shorthand-exports/channel.cjs", + "/tests/corpus/6587-class-value-shorthand-exports/main.cjs" + ], + "diags": [] +} diff --git a/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6588-class-value-shorthand-early-reads.ts.json b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6588-class-value-shorthand-early-reads.ts.json new file mode 100644 index 0000000000..d94b0b6a93 --- /dev/null +++ b/packages/compiler/test/ts7/baselines/order-parity/tests/corpus/6588-class-value-shorthand-early-reads.ts.json @@ -0,0 +1,6 @@ +{ + "order": [ + "/tests/corpus/6588-class-value-shorthand-early-reads.ts" + ], + "diags": [] +} diff --git a/tests/corpus/6585-class-value-shorthand.cjs b/tests/corpus/6585-class-value-shorthand.cjs new file mode 100644 index 0000000000..74d9be45eb --- /dev/null +++ b/tests/corpus/6585-class-value-shorthand.cjs @@ -0,0 +1,30 @@ +// Object-literal shorthand of class values: class declarations (plain and +// extending EventEmitter) and class expressions stored as properties. The +// property holds the class itself: construction through it, its name, +// identity with the binding, instanceof, and the emitter surface of its +// instances. +"use strict"; +const { EventEmitter } = require("node:events"); + +class Base { + hi() { + return "base"; + } +} +class Bus extends EventEmitter { + constructor(name) { + super(); + this.name = name; + } +} +const Anon = class extends EventEmitter {}; +const api = { Base, Bus, Anon, version: 1 }; +const bus = new api.Bus("main"); +console.log(api.Base.name, api.Bus.name, api.Anon.name, api.version); +console.log(bus.name, bus instanceof Bus, bus instanceof EventEmitter, bus instanceof api.Bus); +console.log(new api.Base().hi(), new api.Base() instanceof Base, new api.Bus("x") instanceof Base); +bus.on("x", (v) => console.log("x", v)); +console.log(bus.emit("x", 1), bus.listenerCount("x")); +const anon = new api.Anon(); +console.log(anon instanceof EventEmitter, anon instanceof Anon, typeof api.Anon); +console.log(api.Bus === Bus, typeof api.Bus, Object.keys(api)); diff --git a/tests/corpus/6586-class-value-shorthand.ts b/tests/corpus/6586-class-value-shorthand.ts new file mode 100644 index 0000000000..91bb2bc182 --- /dev/null +++ b/tests/corpus/6586-class-value-shorthand.ts @@ -0,0 +1,30 @@ +// Object-literal shorthand of class values in TypeScript, including a +// class extending EventEmitter, and statically decided instanceof whose +// operands are computed: both operands still evaluate, left first. +import { EventEmitter } from "node:events"; + +const A = class { + x = 1; +}; +class B extends EventEmitter { + n = 2; +} +class C { + label(): string { + return "c"; + } +} +const registry = { A, B, C }; +console.log(registry.A.name, registry.B.name, registry.C.name); +console.log(new registry.A().x, new registry.B().n, new registry.C().label()); +const order: string[] = []; +function make(): C { + order.push("make"); + return new C(); +} +function target(): typeof C { + order.push("target"); + return registry.C; +} +console.log(make() instanceof target(), new registry.B() instanceof EventEmitter); +console.log(new registry.B() instanceof B, make() instanceof registry.C, order.join(",")); diff --git a/tests/corpus/6587-class-value-shorthand-exports/channel.cjs b/tests/corpus/6587-class-value-shorthand-exports/channel.cjs new file mode 100644 index 0000000000..77197f1e88 --- /dev/null +++ b/tests/corpus/6587-class-value-shorthand-exports/channel.cjs @@ -0,0 +1,17 @@ +const { EventEmitter } = require("node:events"); + +class Channel extends EventEmitter { + constructor(name) { + super(); + this.name = name; + } + send(message) { + this.emit("message", message, this.name); + } +} +const Plain = class { + constructor() { + this.kind = "plain"; + } +}; +module.exports = { Channel, Plain }; diff --git a/tests/corpus/6587-class-value-shorthand-exports/main.cjs b/tests/corpus/6587-class-value-shorthand-exports/main.cjs new file mode 100644 index 0000000000..18ff83b9c9 --- /dev/null +++ b/tests/corpus/6587-class-value-shorthand-exports/main.cjs @@ -0,0 +1,15 @@ +// A module exporting classes through object-literal shorthand +// (`module.exports = { Channel, Plain }`), one of them extending +// EventEmitter; the importer constructs, compares and tests instances +// through the exported values. +const { EventEmitter } = require("node:events"); +const lib = require("./channel.cjs"); +const { Channel, Plain } = lib; + +const ch = new lib.Channel("alpha"); +ch.on("message", (message, from) => console.log("message", message, from)); +ch.send("hello"); +console.log(lib.Channel.name, Channel === lib.Channel, ch instanceof Channel, ch instanceof EventEmitter); +const p = new Plain(); +console.log(p.kind, p instanceof lib.Plain, lib.Plain.name, new lib.Plain() instanceof Plain); +console.log(ch.listenerCount("message"), ch.eventNames()); diff --git a/tests/corpus/6588-class-value-shorthand-early-reads.ts b/tests/corpus/6588-class-value-shorthand-early-reads.ts new file mode 100644 index 0000000000..d021a82656 --- /dev/null +++ b/tests/corpus/6588-class-value-shorthand-early-reads.ts @@ -0,0 +1,31 @@ +// Reads that run before their binding is initialized throw Node's +// ReferenceError: a class read through object-literal shorthand from a +// function called before the class statement, and the left operand of a +// statically decided instanceof whose constructor operand is computed. +class Plain {} +const lib = { Plain }; + +function early(): { Late: typeof Late } { + return { Late }; +} +function earlyInstance(): boolean { + return pending instanceof lib.Plain; +} + +function attempt(label: string, run: () => unknown): void { + try { + run(); + console.log(label, "returned"); + } catch (error) { + console.log(label, (error as Error).name, (error as Error).message); + } +} +attempt("shorthand", early); +attempt("instanceof", earlyInstance); + +class Late { + static tag = "late"; +} +let pending = new Plain(); +const late = early().Late; +console.log(late.tag, late === Late, earlyInstance()); diff --git a/tests/diagnostics/inheritance.ts b/tests/diagnostics/inheritance.ts index a2a09e7323..6eb91358f9 100644 --- a/tests/diagnostics/inheritance.ts +++ b/tests/diagnostics/inheritance.ts @@ -72,13 +72,6 @@ class Conditional extends Animal { const rec = { name: "r" }; console.log(rec instanceof Animal); -// ...and folds statically for standalone classes, so a computed operand -// whose effects would be dropped is rejected -class Point { - x: number = 0; -} -console.log(new Point() instanceof Point); - // Reached: unreached bodies never lower, so their rejections only exist // when something on the entry path uses them. new Conditional(true); diff --git a/tests/harness/__snapshots__/inheritance.ts.txt b/tests/harness/__snapshots__/inheritance.ts.txt index 1fc3312d7c..b3c64c6ea7 100644 --- a/tests/harness/__snapshots__/inheritance.ts.txt +++ b/tests/harness/__snapshots__/inheritance.ts.txt @@ -26,39 +26,32 @@ inheritance.ts:55:3 - error SC1090: overriding a method with a different signatu | ^~~~ 56 | return other.name; -inheritance.ts:80:13 - error SC1090: statically-decided 'instanceof' on computed operands (bind the value to a variable first) are not supported yet +inheritance.ts:82:15 - error SC2004: uses of 'GenericAbstract' inherit the blocker on its declaration - 79 | } - 80 | console.log(new Point() instanceof Point); - | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 81 | - -inheritance.ts:89:15 - error SC2004: uses of 'GenericAbstract' inherit the blocker on its declaration - - 88 | new Impl().usesLimit(); - 89 | const gaRef = GenericAbstract; + 81 | new Impl().usesLimit(); + 82 | const gaRef = GenericAbstract; | ^~~~~~~~~~~~~~~ - 90 | new Aliased(); + 83 | new Aliased(); hint: the declaration of 'GenericAbstract' did not compile — fix the diagnostic reported there and these sites clear with it -inheritance.ts:91:1 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet +inheritance.ts:84:1 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet - 90 | new Aliased(); - 91 | new BareRepeat(); + 83 | new Aliased(); + 84 | new BareRepeat(); | ^~~~~~~~~~~~~~~~ - 92 | new RetypedRepeat(); + 85 | new RetypedRepeat(); -inheritance.ts:92:1 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet +inheritance.ts:85:1 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet - 91 | new BareRepeat(); - 92 | new RetypedRepeat(); + 84 | new BareRepeat(); + 85 | new RetypedRepeat(); | ^~~~~~~~~~~~~~~~~~~ - 93 | new Bivariant(); + 86 | new Bivariant(); -inheritance.ts:93:1 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet +inheritance.ts:86:1 - error SC1090: constructing through a class value whose class has no lowering (the class declaration itself was rejected — see its own diagnostic) is not supported yet - 92 | new RetypedRepeat(); - 93 | new Bivariant(); + 85 | new RetypedRepeat(); + 86 | new Bivariant(); | ^~~~~~~~~~~~~~~ - 94 | \ No newline at end of file + 87 | \ No newline at end of file diff --git a/tests/harness/corpus-costs/6585-class-value-shorthand.cjs.json b/tests/harness/corpus-costs/6585-class-value-shorthand.cjs.json new file mode 100644 index 0000000000..44fa538545 --- /dev/null +++ b/tests/harness/corpus-costs/6585-class-value-shorthand.cjs.json @@ -0,0 +1 @@ +{ "plain": 3180, "san": 4640 } diff --git a/tests/harness/corpus-costs/6586-class-value-shorthand.ts.json b/tests/harness/corpus-costs/6586-class-value-shorthand.ts.json new file mode 100644 index 0000000000..080a08370c --- /dev/null +++ b/tests/harness/corpus-costs/6586-class-value-shorthand.ts.json @@ -0,0 +1 @@ +{ "plain": 2230, "san": 3250 } diff --git a/tests/harness/corpus-costs/6587-class-value-shorthand-exports/main.cjs.json b/tests/harness/corpus-costs/6587-class-value-shorthand-exports/main.cjs.json new file mode 100644 index 0000000000..53a2dcb300 --- /dev/null +++ b/tests/harness/corpus-costs/6587-class-value-shorthand-exports/main.cjs.json @@ -0,0 +1 @@ +{ "plain": 2270, "san": 3300 } diff --git a/tests/harness/corpus-costs/6588-class-value-shorthand-early-reads.ts.json b/tests/harness/corpus-costs/6588-class-value-shorthand-early-reads.ts.json new file mode 100644 index 0000000000..b38645e4a2 --- /dev/null +++ b/tests/harness/corpus-costs/6588-class-value-shorthand-early-reads.ts.json @@ -0,0 +1 @@ +{ "plain": 2110, "san": 3160 }