From b69d70664cf52bac084ea092a1cf2d181239aa5e Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 27 Apr 2023 10:34:31 -0700 Subject: [PATCH] Console methods are readonly Defines common `console` methods to tell the compiler that they take readonly args. This ensures that things like `console.log()` aren't accidentally viewed as a mutation. Previously the pattern of "build object, then log it after mutation is done" would have grouped the console.log as part of the mutation and the log only would fire if the value got reconstructed. Now we know the log isn't mutating, and the log will happen regardless of whether the value is rebuilt or cached. --- compiler/forget/src/HIR/Globals.ts | 65 ++++++++++++++++++- .../compiler/console-readonly.expect.md | 46 +++++++++++++ .../fixtures/compiler/console-readonly.js | 11 ++++ ...ssa-renaming-ternary-destruction.expect.md | 16 +++-- .../compiler/ssa-renaming-ternary.expect.md | 14 ++-- ...a-renaming-unconditional-ternary.expect.md | 14 ++-- 6 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/console-readonly.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/compiler/console-readonly.js diff --git a/compiler/forget/src/HIR/Globals.ts b/compiler/forget/src/HIR/Globals.ts index 7ea650abd4..d1511fa5bb 100644 --- a/compiler/forget/src/HIR/Globals.ts +++ b/compiler/forget/src/HIR/Globals.ts @@ -8,11 +8,11 @@ import { Effect, ValueKind } from "./HIR"; import { Hook } from "./Hooks"; import { + BUILTIN_SHAPES, + BuiltInArrayId, + ShapeRegistry, addFunction, addObject, - BuiltInArrayId, - BUILTIN_SHAPES, - ShapeRegistry, } from "./ObjectShape"; import { BuiltInType, HookType, PolyType } from "./Types"; @@ -128,6 +128,65 @@ const TYPED_GLOBALS: Array<[string, BuiltInType]> = [ ], ["Infinity", { kind: "Primitive" }], ["NaN", { kind: "Primitive" }], + [ + "console", + addObject(DEFAULT_SHAPES, "console", [ + [ + "error", + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: { kind: "Primitive" }, + calleeEffect: Effect.Read, + }), + ], + [ + "info", + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: { kind: "Primitive" }, + calleeEffect: Effect.Read, + }), + ], + [ + "log", + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: { kind: "Primitive" }, + calleeEffect: Effect.Read, + }), + ], + [ + "table", + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: { kind: "Primitive" }, + calleeEffect: Effect.Read, + }), + ], + [ + "trace", + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: { kind: "Primitive" }, + calleeEffect: Effect.Read, + }), + ], + [ + "warn", + addFunction(DEFAULT_SHAPES, [], { + positionalParams: [], + restParam: Effect.Read, + returnType: { kind: "Primitive" }, + calleeEffect: Effect.Read, + }), + ], + ]), + ], // TODO: rest of Global objects ]; diff --git a/compiler/forget/src/__tests__/fixtures/compiler/console-readonly.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/console-readonly.expect.md new file mode 100644 index 0000000000..320a974303 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/console-readonly.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function Component(props) { + const x = makeObject(props); + // These calls should view x as readonly and be grouped outside of the reactive scope for x: + console.log(x); + console.info(x); + console.warn(x); + console.error(x); + console.trace(x); + console.table(x); + return x; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function Component(props) { + const $ = useMemoCache(2); + const c_0 = $[0] !== props; + let t0; + if (c_0) { + t0 = makeObject(props); + $[0] = props; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + + console.log(x); + console.info(x); + console.warn(x); + console.error(x); + console.trace(x); + console.table(x); + return x; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/console-readonly.js b/compiler/forget/src/__tests__/fixtures/compiler/console-readonly.js new file mode 100644 index 0000000000..e2486ba7fe --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/console-readonly.js @@ -0,0 +1,11 @@ +function Component(props) { + const x = makeObject(props); + // These calls should view x as readonly and be grouped outside of the reactive scope for x: + console.log(x); + console.info(x); + console.warn(x); + console.error(x); + console.trace(x); + console.table(x); + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md index 5b7fe68b16..38a7dd0086 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary-destruction.expect.md @@ -19,7 +19,7 @@ function foo(props) { ```javascript import { unstable_useMemoCache as useMemoCache } from "react"; function foo(props) { - const $ = useMemoCache(4); + const $ = useMemoCache(5); const c_0 = $[0] !== props.bar; let x; if (c_0) { @@ -31,15 +31,19 @@ function foo(props) { x = $[1]; } const c_2 = $[2] !== props; + let t0; if (c_2) { - const _ = props.cond ? (([x] = [[]]), x.push(props.foo)) : null; - - console.log(_); + t0 = props.cond ? (([x] = [[]]), x.push(props.foo)) : null; $[2] = props; - $[3] = x; + $[3] = t0; + $[4] = x; } else { - x = $[3]; + t0 = $[3]; + x = $[4]; } + const _ = t0; + + console.log(_); return x; } diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md index 4d9a788eb7..86250c8e68 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-ternary.expect.md @@ -17,7 +17,7 @@ function foo(props) { ```javascript import { unstable_useMemoCache as useMemoCache } from "react"; function foo(props) { - const $ = useMemoCache(4); + const $ = useMemoCache(5); const c_0 = $[0] !== props.bar; let x; if (c_0) { @@ -29,14 +29,18 @@ function foo(props) { x = $[1]; } const c_2 = $[2] !== props; + let t0; if (c_2) { - const _ = props.cond ? ((x = []), x.push(props.foo)) : null; - console.log(_); + t0 = props.cond ? ((x = []), x.push(props.foo)) : null; $[2] = props; - $[3] = x; + $[3] = t0; + $[4] = x; } else { - x = $[3]; + t0 = $[3]; + x = $[4]; } + const _ = t0; + console.log(_); return x; } diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md index 98cd976145..3c01da132c 100644 --- a/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md +++ b/compiler/forget/src/__tests__/fixtures/compiler/ssa-renaming-unconditional-ternary.expect.md @@ -19,7 +19,7 @@ function foo(props) { ```javascript import { unstable_useMemoCache as useMemoCache } from "react"; function foo(props) { - const $ = useMemoCache(4); + const $ = useMemoCache(5); const c_0 = $[0] !== props.bar; let x; if (c_0) { @@ -31,16 +31,20 @@ function foo(props) { x = $[1]; } const c_2 = $[2] !== props; + let t0; if (c_2) { - const _ = props.cond + t0 = props.cond ? ((x = []), x.push(props.foo)) : ((x = []), x.push(props.bar)); - console.log(_); $[2] = props; - $[3] = x; + $[3] = t0; + $[4] = x; } else { - x = $[3]; + t0 = $[3]; + x = $[4]; } + const _ = t0; + console.log(_); return x; }