Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error Recovery #1462

Closed
wants to merge 51 commits into from
Closed

Error Recovery #1462

wants to merge 51 commits into from

Commits on Oct 24, 2023

  1. Starting work on error recovery

    Co-authored-by: Godfrey Chan <[email protected]>
    wycats and chancancode committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    9d8be07 View commit details
    Browse the repository at this point in the history
  2. Revert "Starting work on error recovery"

    This reverts commit 3e83045.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    c3056db View commit details
    Browse the repository at this point in the history
  3. Added error recovery machine infrastructure

    The infrastructure doesn't *do* anything yet, but it exists end-to-end.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    164e52a View commit details
    Browse the repository at this point in the history
  4. Consolidate stack internals

    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    2ad715e View commit details
    Browse the repository at this point in the history
  5. Configuration menu
    Copy the full SHA
    df1f01b View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    2d87f07 View commit details
    Browse the repository at this point in the history
  7. Configuration menu
    Copy the full SHA
    f37e1e6 View commit details
    Browse the repository at this point in the history
  8. In progress updating stack checks

    These changes will make it easier to iterate on the low-level VM, which
    will be necessary in order to add error recovery in appropriate opcodes.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    e5fbcf4 View commit details
    Browse the repository at this point in the history
  9. Finish overhauling debug logging

    The logging should be significantly more useful, and the metadata is now
    extensible to support arbitrary assertions.
    
    I already added stack type assertions to the metadata, but these are
    currently still implemented in terms of the old stack change check.
    
    The improvements to metadata already improve the logs (because they
    facilitate improved deserialization of immediate values in operands).
    
    The next step is to assert the stack types and also log the (formatted)
    stack parameters and return values in the log.
    
    All of this facilitates quicker iteration on the in-progress error
    recovery PR.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    f162c71 View commit details
    Browse the repository at this point in the history
  10. Cleanup

    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    9a14393 View commit details
    Browse the repository at this point in the history
  11. Tweak stack checks

    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    44262f1 View commit details
    Browse the repository at this point in the history
  12. chore(package.json): update devDependencies versions to latest

    The devDependencies in package.json have been updated to their latest versions to ensure compatibility and take advantage of any bug fixes or new features. This includes updating various Babel plugins and presets, Rollup, ESLint, Prettier, Puppeteer, QUnit, rimraf, and TypeScript.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    e557a58 View commit details
    Browse the repository at this point in the history
  13. Update qunit

    Update setup harness and add local types.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    ed47021 View commit details
    Browse the repository at this point in the history
  14. chore(vscode): update editor.codeActionsOnSave settings to prioritize…

    … source.fixAll over source.formatDocument
    
    feat(vscode): add custom inline bookmark mapping for @fixme to highlight and style fixme comments in code
    feat(vscode): add custom style for @fixme inline bookmarks to make them bold and display in red color
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    7637009 View commit details
    Browse the repository at this point in the history
  15. Configuration menu
    Copy the full SHA
    a2d876f View commit details
    Browse the repository at this point in the history
  16. refactor(debug): Make nulls and arrays in stack verification more acc…

    …urate
    
    Handle `null`s in the metadata more reliably, and add nullable arrays to the metadata.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    6bd4205 View commit details
    Browse the repository at this point in the history
  17. chore: Clean up unused code

    Also tweak code for better conformance to modern TypeScript.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    099cbc9 View commit details
    Browse the repository at this point in the history
  18. infra(debugger): Improve and restore the debug tracing infra

    - Make the debug infrastructure work again
    - Have the "before" hook return the "after" hook so it can close
      over state it needs.
    - Snapshot and formalize internal debug state.
    - Properly handle errors in tracing so that log groups are properly
      closed.
    - Improve the tracing output:
      - properly visualize the state of the element builder
      - properly visualize the block stack
      - improve visualization of the scope
    - Streamline the interaction between the VM and debugger
    
    The next commit uses all of these changes to significantly improve
    stack verification and debugging metadata.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    4670f73 View commit details
    Browse the repository at this point in the history
  19. infra(debugger): Significant improvement to stack checks and trace lo…

    …gging
    
    This commit overhauls opcode metadata:
    
    Previously, many opcodes opted out of stack changes because their
    changes were too dynamic (e.g. Pop reduces the stack size by a
    parameter, which was previously too dynamic to be checked). This commit
    enables dynamic stack checks, which are functions that take the runtime
    op and debug VM state and dynamically determine the stack change.
    
    This makes it possible to check the stack for every opcode except
    `EnterList`, `Iterate`, `PrepareArgs` and `PopFrame` (the reasons for
    these exceptions is documented in `opcode-metadata.ts`).
    
    Dynamic checking is now used in:
    
    - Concat (pops N from the stack based on an operand)
    - Pop (same)
    - BindDynamicScope (binds a number of names from an operand)
    
    A previous commit enabled operand metadata. That infrastructure allows
    the trace logger and compilation logger to print friendly versions of
    opcodes.
    
    This commit makes the metadata pervasively more accurate, piggy-backing
    on the previous commit, which made nullable operands more accurate.
    
    This deserialization process serves as a sort-of verification pass. If
    an opcode is expecting an array, for example, and the operand
    deserializes to a string, it will fail.
    
    It currently fails confusingly (and not reliably) when the deserializer
    fails to deserialize, but this can and should be improved in the future.
    
    Enabling pervasive stack checks caused quite a few tests to fail.
    
    For the most part, getting the tests to pass required improving the
    accuracy of the opcode metadata.
    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    ae8f46d View commit details
    Browse the repository at this point in the history
  20. style: Style tweaks

    wycats committed Oct 24, 2023
    Configuration menu
    Copy the full SHA
    fac3d89 View commit details
    Browse the repository at this point in the history

Commits on Oct 25, 2023

  1. infra(opcodes): Generate opcode types and values

    Previously, the source of truth for opcodes was an array literal and a
    matching interface for that literal. All other types were generated
    using TypeScript reflection.
    
    However, the TypeScript reflection was fairly elaborate and slowed down
    type feedback when working with the types.
    
    This commit moves the source of truth to bin/opcodes.json and generates:
    
    - @glimmer/interfaces/lib/generated/vm-opcodes.d.ts
    - @glimmer/vm/lib/generated/opcodes.ts
    - @glimmer/debug/lib/generated/op-list.ts
    
    It adds a lint rule to avoid code casually importing directly from these
    generated files, preferring import paths that re-export the generated
    types and values.
    
    The schema in `bin/opcodes/opcodes.schema.json` validates
    `bin/opcodes.json`.
    
    An npm script (`generate:opcodes`) updates all three files, but can also
    be used to update a single file or review the generated files.
    
    The generator script is written in TypeScript and this commit adds
    `esyes` (https://github.com/starbeamjs/esyes) to generate the
    `bin/opcodes.mts` file. (esyes requires node 21, but that doesn't
    affect users of Glimmer, just contributors).
    wycats committed Oct 25, 2023
    Configuration menu
    Copy the full SHA
    acd251d View commit details
    Browse the repository at this point in the history
  2. style: Style changes

    wycats committed Oct 25, 2023
    Configuration menu
    Copy the full SHA
    2d5c4d1 View commit details
    Browse the repository at this point in the history
  3. refactor(typescript): Replace ContentType and CurriedType enums

    There are better, more ergnomic, and more broadly supported ways to
    handle enum-like patterns in TypeScript now.
    
    TL;DR This commit replaces enums with literal types and type unions.
    wycats committed Oct 25, 2023
    Configuration menu
    Copy the full SHA
    570ee41 View commit details
    Browse the repository at this point in the history

Commits on Oct 26, 2023

  1. feat(error-boundary): Basic stack error recovery works

    This commit also calls the specified handler asynchronously when an
    error occurs.
    wycats committed Oct 26, 2023
    Configuration menu
    Copy the full SHA
    a5f2fbf View commit details
    Browse the repository at this point in the history

Commits on Oct 29, 2023

  1. refactor(opcodes): Nail down opcode behavior

    Refactors the opcode metadata to be much more precise about how the
    opcodes behave.
    
    This is in service of properly accounting for possible throwing behavior
    in opcodes.
    wycats committed Oct 29, 2023
    Configuration menu
    Copy the full SHA
    2d044f5 View commit details
    Browse the repository at this point in the history

Commits on Oct 30, 2023

  1. refactor(tests): Prepare for error recovery tests

    The plan is to put a no-op {{#-try}} around everything.
    wycats committed Oct 30, 2023
    Configuration menu
    Copy the full SHA
    2d8d30a View commit details
    Browse the repository at this point in the history
  2. refactor(tests): Further trim down render delegate

    To maximize the applicability of the no-op error recovery tests.
    wycats committed Oct 30, 2023
    Configuration menu
    Copy the full SHA
    e393dc2 View commit details
    Browse the repository at this point in the history
  3. refactor(tests): Further trim down render delegate

    Render delegates now only need to supply two pieces of DOM information:
    
    - the document
    - the initial element
    
    Everything else is derived in the singular `RenderTest` class.
    wycats committed Oct 30, 2023
    Configuration menu
    Copy the full SHA
    8b862b2 View commit details
    Browse the repository at this point in the history

Commits on Oct 31, 2023

  1. chore(checkpoint): Tests pass

    wycats committed Oct 31, 2023
    Configuration menu
    Copy the full SHA
    463efc7 View commit details
    Browse the repository at this point in the history
  2. fix(tests): Support else blocks in glimmer tests

    Previously, the test suite skipped Glimmer-style templates for any tests
    that yielded to `else` or `inverse`.
    
    In general, the test suite uses a matrix test style to describe
    templates and invocations abstractly (a "blueprint"), and then compiles
    the blueprint into each of the styles.
    
    It's possible to compile `else` blocks in Glimmer:
    
    ```hbs
    <TestComponent>
      <:default as |block params|>
    
      </:default>
      <:else>
    
      </:else>
    </TestComponent>
    ```
    
    This is not purely pedantic: the current test suite fails to test that
    `yield to="inverse"` actually yields to `<:else>` blocks and this fix
    therefore improves coverage of the public API.
    wycats committed Oct 31, 2023
    Configuration menu
    Copy the full SHA
    14cc692 View commit details
    Browse the repository at this point in the history
  3. refactor(tests): Separate invoke & template styles

    The test harness previously didn't provide a way to test attributes
    passed to classic components.
    
    This commit makes it possible to use the Glimmer invocation style, which
    supports attributes, alongside templates implemented using classic
    features.
    
    Example:
    
    ```ts
    @test({ invokeAs: 'glimmer' })
    'with ariaRole specified as an outer binding'() {
      this.render.template(
        {
          layout: 'Here!',
          attributes: {
            id: '"aria-test"',
            class: '"foo"',
            role: 'this.ariaRole'
          },
        },
        { ariaRole: 'main' }
      );
    
      this.assertComponent('Here!', {
        id: 'aria-test',
        class: classes('ember-view foo'),
        role: 'main',
      });
      this.assertStableRerender();
    }
    ```
    
    This is a test that uses a classic-style component manager and
    implementation strategy but uses a Glimmer invocation style. This allows
    us to test that attributes passed to classic components are preserved.
    wycats committed Oct 31, 2023
    Configuration menu
    Copy the full SHA
    4fa1a67 View commit details
    Browse the repository at this point in the history

Commits on Nov 1, 2023

  1. test(error-recovery): Add error recovery suites

    This commit makes it possible to take any existing test suite and add
    tests for no-op error recovery.
    
    This works by wrapping each template with `{{#-try this.handleError}}`
    and adding minor infrastructure to merge the wrapping properties with
    the original properties.
    
    This commit adds error recovery testing to initial render tests, but
    more tests will be added in future commits.
    
    The implementation assumes that templates all funnel through
    `this.renderTemplate` in the test delegate, but this may not be
    accurate, especially in rehydration tests.
    wycats committed Nov 1, 2023
    Configuration menu
    Copy the full SHA
    1fd0f61 View commit details
    Browse the repository at this point in the history
  2. chore(eslint): Turn on stricter testing lints

    Turn on (largely) the full gamut of lints we use in the codebase across
    the workspace packages, **including in integration tests**.
    
    Also restrict test packages in `packages/@glimmer-workspace` from
    directly importing their parent package via relative imports.
    wycats committed Nov 1, 2023
    Configuration menu
    Copy the full SHA
    24a5fd9 View commit details
    Browse the repository at this point in the history

Commits on Nov 2, 2023

  1. feat(error-boundary): DOM clearing works

    This commit finally gets to the meat of the matter.
    
    This (passing) test is a good way to see where we're at.
    
    ```ts
    @render
    'error boundaries can happen in nested context'() {
      const actions = new Actions();
    
      class Woops {
        get woops() {
          actions.record('get woops');
          throw Error('woops');
        }
      }
    
      this.render.template(
        stripTight`
          <p>
            {{this.outer.before}}|
            {{#-try this.handler}}
              {{this.inner.before}}
              |message: [{{this.woops.woops}}]|
              {{this.inner.after}}
            {{/-try}}
            |{{this.outer.after}}
          </p>
        `,
        {
          woops: new Woops(),
          outer: {
            before: 'outer:before',
            after: 'outer:after',
          },
          inner: {
            before: 'inner:before',
            after: 'inner:after',
          },
          handler: (_err: unknown, _retry: () => void) => {
            actions.record('error handled');
          },
        }
      );
    
      actions.expect(['get woops', 'error handled']);
      this.assertHTML('<p>outer:before||outer:after</p>');
    }
    ```
    
    TL;DR when the error is handled, the surrounding try block is cleared,
    and execution resumes after the try block.
    
    If the error is not handled, the DOM is still cleared, but the error is
    rethrown at the top-level once evaluation is complete.
    
    Next steps:
    
    - Make sure that other VM state is cleared properly (especially the
      block tracking state).
    - Handle all errors that occur in user code. The current code handles
      errors thrown while appending content, but there are more cases. The
      good news is that the infrastructure for handling and propagating
      errors should Just Work for the remaining cases.
    - Do a consistent job of making sure that errors that occur in the
      updating VM are handled properly. For example, if an error occurs in
      the updating part of an append opcode, that should consistently clear
      back to the nearest `#-try` block. At the moment, those blocks are not
      represented in the updating VM.
    
    There's more work to do, but the crux of the conceptual and
    prototyping work needed to support error boundaries is now done.
    wycats committed Nov 2, 2023
    Configuration menu
    Copy the full SHA
    ecbabd2 View commit details
    Browse the repository at this point in the history

Commits on Nov 3, 2023

  1. refactor(references): Bake errors in more deeply

    This commit significantly overhauls the reference architecture to bake
    errors in at a deeper level.
    
    After this commit, references have an additional `error` field that can
    be set to indicate that a reference is in the error state.
    
    The internal reference API was overhauled to explicitly support error
    cases, most notably via a new `readReactive` function, which returns a
    `Result<T, UserException>`. If the reference is in the error state,
    `readReactive` returns an `Err`.
    
    There is also an `unwrapReactive` function, which throws an exception if
    the the reference is in the error state. This replaces the `valueForRef`
    function.
    
    This commit adds new primitives and updates the terminology used in the
    reactive API to reflect the error recovery changes (and also to align
    with Starbeam's terminology).
    
    - `MutableCell`: a new internal primitive that reflects a single piece
      of mutable data.
    - `ReadonlyCell`: a new internal primitive that reflects a single piece
      of immutable data.
    - `DeeplyConstant`: replaces the `unbound` collection of APIs. Where
      `ReadonlyCell` is shallowly immutable, `DeeplyConstant` values
      produce `DeeplyConstant` property reactives.
    - `FallibleFormula`: A fallible formula is a read-only function that
      can throw errors. If the compute function throws an error, the
      `FallibleFormula` will be in an error state.
    - `InfallibleFormula`: An infallible formula is a read-only function
      that cannot throw errors. If the compute function throws an error,
      the `InfallibleFormula` will be in an error state.
    - `Accessor`: A read-write formula is an `InfallibleFormula` on the read
      side and also has an `update` side.
    
    At the moment, virtually all uses of `valueForRef` have been
    updated to use `unwrapReactive`. This is an acceptable (but not ideal)
    transition path inside of combinators (such as `concatReference`)
    because they can use `FallibleFormula` to handle the error.
    
    The VM, on the other hand, must not use `unwrapReactive` directly (and
    instead must use the previously committed `vm.deref` and `vm.unwrap`
    methods to handle errors in a VM-aware way).
    
    Ultimately, combinators should also not use `unwrapReactive`, to avoid
    needing so many `try/catch`es, but that can come after the VM usages
    have been updated.
    wycats committed Nov 3, 2023
    Configuration menu
    Copy the full SHA
    37cdc0e View commit details
    Browse the repository at this point in the history
  2. refactor(test-infra): Restructure matrix tests

    This commit introduces a new way to write matrix integration tests that
    doesn't rely on JS inheritance. This makes it easier to mix-and-match
    different parts of the tests and also makes it easier to add error
    recovery tests.
    wycats committed Nov 3, 2023
    Configuration menu
    Copy the full SHA
    e6b7543 View commit details
    Browse the repository at this point in the history

Commits on Nov 4, 2023

  1. feat(error-recovery): Recover more VM getErrorMessage

    And write tests.
    wycats committed Nov 4, 2023
    Configuration menu
    Copy the full SHA
    1bffa26 View commit details
    Browse the repository at this point in the history

Commits on Nov 5, 2023

  1. Configuration menu
    Copy the full SHA
    b12092d View commit details
    Browse the repository at this point in the history
  2. feat(errors): Added checkpointing to Stack

    The VM uses the `Stack` class (and friends) to manage its internal
    state. This commit adds checkpointing to `Stack` so that the VM can
    easily roll back to a point before a try frame began.
    wycats committed Nov 5, 2023
    Configuration menu
    Copy the full SHA
    5f1653b View commit details
    Browse the repository at this point in the history

Commits on Nov 6, 2023

  1. feat(errors): Closing in!

    There are a few remaining open issues, but this commit gets us really
    close to the finish line.
    wycats committed Nov 6, 2023
    Configuration menu
    Copy the full SHA
    dd8d40d View commit details
    Browse the repository at this point in the history
  2. feat(errors): Test and clarify references

    This commit cleans up and tests the behavior of references in the error
    state. Documentation for the semantics is forthcoming.
    wycats committed Nov 6, 2023
    Configuration menu
    Copy the full SHA
    c11ab6d View commit details
    Browse the repository at this point in the history

Commits on Nov 7, 2023

  1. feat(references): More fully test ref API

    Also improve the labelling infrastructure to be more useful for
    debugging tools such as the trace logger.
    wycats committed Nov 7, 2023
    Configuration menu
    Copy the full SHA
    82f4135 View commit details
    Browse the repository at this point in the history

Commits on Nov 8, 2023

  1. feat(debug): Better descriptions/devmode values

    Improve the overall infrastructure of debug labels, and introduce a
    minifier-friendly approach to dev-mode values that captures the notion
    of values that should *always* be present in development but never in
    production.
    wycats committed Nov 8, 2023
    Configuration menu
    Copy the full SHA
    bb08649 View commit details
    Browse the repository at this point in the history
  2. feat(errors): Support error recovery

    This commit makes it possible for a code to reset the error state in a
    reference in order to attempt error recovery.
    
    When a reference's error is reset, its tag is invalidated, which will
    result in consumers of that reference recomputing its value.
    wycats committed Nov 8, 2023
    Configuration menu
    Copy the full SHA
    3aa570b View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    2e21994 View commit details
    Browse the repository at this point in the history

Commits on Nov 9, 2023

  1. Configuration menu
    Copy the full SHA
    9bfa6f4 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    dd562d5 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    8cfa580 View commit details
    Browse the repository at this point in the history

Commits on Nov 10, 2023

  1. Configuration menu
    Copy the full SHA
    5a7e790 View commit details
    Browse the repository at this point in the history
  2. refactor(vm): Refactor and document VM

    This commit removes gratuitous differences between different modes in an
    attempt to clarify how the system works.
    
    It also begins some documentation on how blocks and frames work.
    wycats committed Nov 10, 2023
    Configuration menu
    Copy the full SHA
    8536a36 View commit details
    Browse the repository at this point in the history

Commits on Nov 13, 2023

  1. feat(errors): Handle errors in more cases

    This commit migrates more cases from unwrapReactive to readReactive.
    
    Ideally we'd have explicit tests for each `readReactive` case, but there
    are already some blanket error recovery tests and I'm moving forward
    with these changes as long as all of the existing tests and blanket
    error tests pass.
    wycats committed Nov 13, 2023
    Configuration menu
    Copy the full SHA
    50e24f2 View commit details
    Browse the repository at this point in the history