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

feat(transformer): Support overloaded functions by attaching signatures on use #390

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

martinjlowm
Copy link
Contributor

@martinjlowm martinjlowm commented Jun 20, 2020

Add overloads feature flag that enables this feature.

Enabling it makes the transformer process function calls if their declaration
was previously marked for mocking (via getMethod).

From a type-perspective, typed methods shouldn't bother to consider their inputs
in order to determine the output in runtime. At transformation time, the type
checker resolves the matching overload and that information can be used to
attach to the function, by utilizing the "instance" (this) of it. The
transformer changes transform functions in the following way.

mockedFunction() -> mockedFunction.apply(<signature>, [])

As for constructor instantiation signatures in interfaces, those can be wrapped
by an intermediate function that will copy the mocked properties to preserve the
instantiation behavior.

new mockedNewFunction()
    |
    `-> new (mockedNewFunction[<signature>] || (mockedNewFunction[<signature>] = function() {
               Object.assign(this, mockedNewFunction.apply(<signature>, []));
            }))()

These attached interfaces will determine the branching at runtime and to reduce
as much overhead as possible, all signatures of an overloaded function are
mapped to the resolved return type and stored in a jump table, i.e.:

getMethod("functionName", function () {
  const jt = {
    ['<signature-1>']: () => <signature-1-return-descriptor>,
    ['<signature-2>']: () => <signature-2-return-descriptor>,
    ...
  };

  return jt[this]();
})

It should be noted, that if spies are introduced using the method provider, then
this will be occupied by the signature key.


┌────────────────────────┬────────┬──────────┬──────────┬─────────────┬──────────┬─────────┬─────────────┬──────────┬───────────┬────────────┬───────────┬────────────┬───────────┬────────────┐
│        (index)         │ Files  │  Lines   │  Nodes   │ Identifiers │ Symbols  │  Types  │ Memory used │ I/O read │ I/O write │ Parse time │ Bind time │ Check time │ Emit time │ Total time │
├────────────────────────┼────────┼──────────┼──────────┼─────────────┼──────────┼─────────┼─────────────┼──────────┼───────────┼────────────┼───────────┼────────────┼───────────┼────────────┤
│     no transformer     │ '5094' │ '112423' │ '409342' │  '127883'   │ '103050' │ '37318' │  '456630K'  │  '0.33'  │  '0.80'   │   '1.45'   │  '0.52'   │   '2.94'   │  '2.70'   │   '7.60'   │
│     no createMock      │ '5094' │ '112423' │ '409342' │  '127883'   │ '103050' │ '37318' │  '484535K'  │  '0.31'  │  '0.79'   │   '1.35'   │  '0.49'   │   '2.91'   │  '2.70'   │   '7.45'   │
│ One interface per file │ '5099' │ '112439' │ '439498' │  '137931'   │ '103085' │ '32346' │  '506628K'  │  '0.40'  │  '0.82'   │   '1.77'   │  '0.50'   │   '3.13'   │  '3.57'   │   '8.98'   │
│    Interface reuse     │ '5099' │ '97439'  │ '444498' │  '132931'   │ '93086'  │ '27346' │  '499979K'  │  '0.31'  │  '0.96'   │   '1.53'   │  '0.49'   │   '3.04'   │  '3.96'   │   '9.02'   │
│ One Interface / Reuse  │ '5099' │ '104939' │ '441998' │  '135431'   │ '98086'  │ '29846' │  '503146K'  │  '0.31'  │  '0.89'   │   '1.48'   │  '0.52'   │   '3.11'   │  '3.79'   │   '8.90'   │
│  Features (disabled)   │ '5099' │ '112439' │ '584498' │  '172931'   │ '93219'  │ '27361' │  '394716K'  │  '0.30'  │  '0.83'   │   '1.60'   │  '0.63'   │   '3.98'   │  '3.93'   │  '10.14'   │
│  Features - Overloads  │ '5099' │ '112439' │ '584498' │  '172931'   │ '93219'  │ '27361' │  '390708K'  │  '0.31'  │  '0.90'   │   '1.57'   │  '0.57'   │   '3.78'   │  '4.05'   │   '9.97'   │
└────────────────────────┴────────┴──────────┴──────────┴─────────────┴──────────┴─────────┴─────────────┴──────────┴───────────┴────────────┴───────────┴────────────┴───────────┴────────────┘

This is an alternative implementation of the feature(s) covered in #303 and #313

…es on use

Add overloads feature flag that enables this feature.

Enabling it makes the transformer process function calls if their declaration
was previously marked for mocking (via getMethod).

From a type-perspective, typed methods shouldn't bother to consider their inputs
in order to determine the output in runtime. At transformation time, the type
checker resolves the matching overload and that information can be used to
attach to the function, by utilizing the "instance" (`this`) of it. The
transformer changes transform functions in the following way.

```
mockedFunction() -> mockedFunction.apply(<signature>, [])
```

As for constructor instantiation signatures in interfaces, those can be wrapped
by an intermediate function that will copy the mocked properties to preserve the
instantiation behavior.

```
new mockedNewFunction()
    |
    `-> new (mockedNewFunction[<signature>] || (mockedNewFunction[<signature>] = function() {
               Object.assign(this, mockedNewFunction.apply(<signature>, []));
            }))()
```

These attached interfaces will determine the branching at runtime and to reduce
as much overhead as possible, all signatures of an overloaded function are
mapped to the resolved return type and stored in a jump table, i.e.:

```
getMethod("functionName", function () {
  const jt = {
    ['<signature-1>']: () => <signature-1-return-descriptor>,
    ['<signature-2>']: () => <signature-2-return-descriptor>,
    ...
  };

  return jt[this]();
})
```

It should be noted, that if spies are introduced using the method provider, then
`this` will be occupied by the signature key.
@martinjlowm martinjlowm force-pushed the feature/support-overloads-through-signature-attachment branch from 19df57e to c1f7cb2 Compare June 20, 2020 11:52
@martinjlowm martinjlowm force-pushed the feature/support-overloads-through-signature-attachment branch from da5ce65 to bd4d366 Compare June 20, 2020 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant