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

refactor(engine): Locker Service related refactor #280

Merged
merged 10 commits into from
May 15, 2018
Merged

Conversation

caridy
Copy link
Contributor

@caridy caridy commented May 6, 2018

This is a Locker Service related refactor.

Details

  • Decorators should act directly into the class so the class can be wrapped or preprocess before the engine touches it.
  • Work in preparation for the decorating process to be carry on during class declaration rather than during first instantiation.
  • Work in preparation for the babel implementation of decorators
  • render method should be cached at the def level.
  • all public methods should be cached at the def level.
  • removing unnecessary access to root from component (we can get it from vm)
  • avoid using the component instance directly since it might be controlled by locker.

Follow up work

At the moment, the engine is still in charge of executing the decorators based on the metadata provided by the compilation step. e.g.:

Original Code:

import { track } from "lwc";
class Foo extends LightningElement {
    @track counter;
    @track label;
}

Transpilation output today:

import { track } from "lwc";
class Foo extends LightningElement {}
Foo.track = { counter: 1, label: 1 };

This should eventually be changed to:

import { track, decorate } from "lwc";
class Foo extends LightningElement {}
decorate(Foo, { counter: track(), label: track() });

Does this PR introduce a breaking change?

  • No

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 2a5a5ed | Target commit: 66662f5

lwc-engine-benchmark

table-append-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table/append/1k duration 146.50 (± 4.10 ms) 147.10 (± 4.70 ms) -0.41% 👌
table-clear-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table/clear/1k duration 11.50 (± 0.50 ms) 11.90 (± 0.40 ms) -3.48% 👎
table-create-10k metric base(2a5a5ed) target(66662f5) trend
benchmark-table/create/10k duration 842.30 (± 3.50 ms) 847.60 (± 13.30 ms) -0.63% 👌
table-create-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table/create/1k duration 100.25 (± 1.70 ms) 102.80 (± 1.90 ms) -2.54% 👎
table-update-10th-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table/update-10th/1k duration 89.20 (± 5.00 ms) 94.30 (± 4.60 ms) -5.72% 👌
tablecmp-append-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table-component/append/1k duration 244.70 (± 4.50 ms) 239.50 (± 3.70 ms) 2.13% 👌
tablecmp-clear-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table/clear/1k duration 31.20 (± 0.70 ms) 31.40 (± 1.40 ms) -0.64% 👌
tablecmp-create-10k metric base(2a5a5ed) target(66662f5) trend
benchmark-table-component/create/10k duration 1658.70 (± 4.10 ms) 1725.80 (± 7.20 ms) -4.05% 👎
tablecmp-create-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table-component/create/1k duration 193.70 (± 4.60 ms) 198.40 (± 3.20 ms) -2.43% 👎
tablecmp-update-10th-1k metric base(2a5a5ed) target(66662f5) trend
benchmark-table-component/update-10th/1k duration 74.30 (± 5.60 ms) 77.60 (± 4.00 ms) -4.44% 👌

}
}

export default function api(): DecoratorFunction | any {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api decorator factory

export type DecoratorFunction = (Ctor: any, key: PropertyKey, descriptor: PropertyDescriptor | undefined) => PropertyDescriptor;
export type DecoratorMap = Record<string, DecoratorFunction>;

export default function decorate(Ctor: any, decorators: DecoratorMap) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file is inspired by Mobx (https://medium.com/@mweststrate/mobx-4-better-simpler-faster-smaller-c1fbc08008da), simplifying the application of decorators into classes with a simple transformation. It is limited, but it is all we need to make our decorators work for now.


// stub function to prevent misuse of the @track decorator
export default function track(obj: any): any {
export default function track(obj?: any): DecoratorFunction | any {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

track decorator factory

createTrackedPropertyDescriptor(proto, key, descriptor);

// @wire is a factory that when invoked, returns the wire decorator
export default function wire(adapter: any, config: any): DecoratorFunction {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wire decorator factory

createPublicPropertyDescriptor(proto, propName, descriptor);
const decoratorMap: DecoratorMap = create(null);

// TODO: eventually, the compiler should do this work
Copy link
Contributor Author

@caridy caridy May 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this entire block is to keep backward compatibility. once we modify the compiler to decorate the class, we can remove this.

Copy link
Contributor

@yungcheng yungcheng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda get what you're doing here, could you share some info about what babel or browser implementation of decorator would do to the decorate api you've created? would it be replaced?

@caridy
Copy link
Contributor Author

caridy commented May 7, 2018

@yungcheng yeah, today this is mostly based on Mobx and ember, because decorators are ready yet, but eventually it will just be replaced with the babel implementation.

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 4ecce8a | Target commit: 6c81b26

lwc-engine-benchmark

table-append-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table/append/1k duration 146.90 (± 4.20 ms) 150.30 (± 7.90 ms) -2.31% 👌
table-clear-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table/clear/1k duration 11.60 (± 0.40 ms) 11.60 (± 0.50 ms) 0.00% 👌
table-create-10k metric base(4ecce8a) target(6c81b26) trend
benchmark-table/create/10k duration 889.10 (± 12.70 ms) 911.00 (± 6.10 ms) -2.46% 👎
table-create-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table/create/1k duration 100.80 (± 1.60 ms) 101.80 (± 1.70 ms) -0.99% 👌
table-update-10th-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table/update-10th/1k duration 86.40 (± 5.70 ms) 88.80 (± 5.60 ms) -2.78% 👌
tablecmp-append-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table-component/append/1k duration 248.50 (± 2.30 ms) 248.70 (± 5.50 ms) -0.08% 👌
tablecmp-clear-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table/clear/1k duration 30.90 (± 1.00 ms) 32.60 (± 1.40 ms) -5.50% 👌
tablecmp-create-10k metric base(4ecce8a) target(6c81b26) trend
benchmark-table-component/create/10k duration 1755.80 (± 7.10 ms) 1772.85 (± 11.35 ms) -0.97% 👎
tablecmp-create-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table-component/create/1k duration 197.00 (± 3.40 ms) 196.60 (± 4.00 ms) 0.20% 👌
tablecmp-update-10th-1k metric base(4ecce8a) target(6c81b26) trend
benchmark-table-component/update-10th/1k duration 76.30 (± 3.40 ms) 75.10 (± 5.00 ms) 1.57% 👌

Copy link
Member

@pmdartus pmdartus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR lack tests for the newly introduced decorate method.

export type DecoratorFunction = (Ctor: any, key: PropertyKey, descriptor: PropertyDescriptor | undefined) => PropertyDescriptor;
export type DecoratorMap = Record<string, DecoratorFunction>;

export default function decorate(Ctor: any, decorators: DecoratorMap) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@caridy What do you think of the decorate returning the Ctor the same way than MobX is doing?

This modification would make the greatly simplify the future job of the transform. For example with the current signature, in some case, the transform will need to create a temporary identifier in the scope.

// Original
export default class {
	@api foo;
}

// Transformed
const _tmp = class {}
decorate(_tmp, {
	foo: api()
});
export default _tmp;

If the decorate method returns the passed object the transform wouldn't need to do this.

export default decorate(
	class {},
	{
		foo: api()
	}
);

export type DecoratorMap = Record<string, DecoratorFunction>;

export default function decorate(Ctor: any, decorators: DecoratorMap) {
if (process.env.NODE_ENV !== 'production') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since decorate should become a public API, it would be better to do those checks regardless of the mode. The same way we do for createElement for example.

if (process.env.NODE_ENV !== 'production') {
assert.isTrue(isFunction(decorator), `decorate() expects a decorator function for "${prop}".`);
}
const target = isFunction(Ctor) ? Ctor.prototype : Ctor;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target is a loop invariant. It can get extracted from the loop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not even needed, since line 9, the code asserts that Ctor is a function.

const target = isFunction(Ctor) ? Ctor.prototype : Ctor;
const originalDescriptor = getOwnPropertyDescriptor(target, prop);
const descriptor = decorator(Ctor, prop, originalDescriptor);
if (descriptor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (descriptor) { --> if (!isUndefined(descriptor)) {

@@ -24,8 +49,8 @@ export function prepareForPropUpdate(vm: VM) {
}

// TODO: how to allow symbols as property keys?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can now remove the TODO. 💃

@@ -24,8 +49,8 @@ export function prepareForPropUpdate(vm: VM) {
}

// TODO: how to allow symbols as property keys?
export function createPublicPropertyDescriptor(proto: object, key: string, descriptor: PropertyDescriptor | undefined) {
defineProperty(proto, key, {
export function createPublicPropertyDescriptor(proto: object, key: PropertyKey, descriptor: PropertyDescriptor | undefined) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add return PropertyDescriptor type.

}

export function createPublicAccessorDescriptor(proto: object, key: string, descriptor: PropertyDescriptor) {
export function createPublicAccessorDescriptor(Ctor: any, key: PropertyKey, descriptor: PropertyDescriptor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add return PropertyDescriptor type.

// TODO: how to allow symbols as property keys?
export function createTrackedPropertyDescriptor(proto: object, key: string, descriptor: PropertyDescriptor | undefined) {
defineProperty(proto, key, {
export function createTrackedPropertyDescriptor(Ctor: any, key: PropertyKey, enumerable: boolean) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add return PropertyDescriptor type.

assert.isTrue(writable !== false, `Compiler Error: A @track decorator can only be applied to a writable property.`);
}
}
return createTrackedPropertyDescriptor(target, prop, isObject(descriptor) ? descriptor.enumerable === true : true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not passing down the PropertyDescriptor | undefined like in createPublicPropertyDescriptor.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, those don't need the definition (at least not for now), they just need the enumerable. But if you feel strong about it, I can change it.

const { track } = getComponentDef(Ctor);
if ('state' in component && (!track || !track.state)) {
assert.logWarning(`Non-trackable component state detected in ${component}. Updates to state property will not be reactive. To make state reactive, add @track decorator.`);
if ('state' in (vm.component as Component) && (!track || !track.state)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this warning anymore. This was added when we removed implicit tracking to this.state. Its been long enough now, that I think we can remove this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove this in another PR: #295

}
// if it is configured as an accessor it must have a descriptor
return createPublicAccessorDescriptor(target, propName, descriptor as PropertyDescriptor);
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need else

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain more? I don't get this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

He is saying you are doing the same in the if and in the else, so you can put it out

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it is not the same, we are calling two different methods, look closer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm saying that else is not needed because we return inside of the if block (line 38)

// linking elm and its component with VM
component[ViewModelReflection] = elm[ViewModelReflection] = vm;
defineProperties(elm, def.descriptors);
function LWCElement(this: Component) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we switch from class to function ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because class constructor can't be invoked without new, which is needed by locker.

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: d436493 | Target commit: e0fb827

lwc-engine-benchmark

table-append-1k metric base(d436493) target(e0fb827) trend
benchmark-table/append/1k duration 153.00 (± 6.40 ms) 148.95 (± 6.05 ms) 2.65% 👍
table-clear-1k metric base(d436493) target(e0fb827) trend
benchmark-table/clear/1k duration 11.90 (± 0.60 ms) 12.10 (± 0.30 ms) -1.68% 👌
table-create-10k metric base(d436493) target(e0fb827) trend
benchmark-table/create/10k duration 893.20 (± 9.90 ms) 844.60 (± 4.80 ms) 5.44% 👍
table-create-1k metric base(d436493) target(e0fb827) trend
benchmark-table/create/1k duration 102.60 (± 2.50 ms) 102.60 (± 1.90 ms) 0.00% 👌
table-update-10th-1k metric base(d436493) target(e0fb827) trend
benchmark-table/update-10th/1k duration 87.20 (± 3.30 ms) 87.40 (± 5.40 ms) -0.23% 👌
tablecmp-append-1k metric base(d436493) target(e0fb827) trend
benchmark-table-component/append/1k duration 250.60 (± 5.50 ms) 244.50 (± 5.00 ms) 2.43% 👍
tablecmp-clear-1k metric base(d436493) target(e0fb827) trend
benchmark-table/clear/1k duration 32.10 (± 1.40 ms) 31.05 (± 1.30 ms) 3.27% 👍
tablecmp-create-10k metric base(d436493) target(e0fb827) trend
benchmark-table-component/create/10k duration 1751.50 (± 7.20 ms) 1699.90 (± 9.50 ms) 2.95% 👍
tablecmp-create-1k metric base(d436493) target(e0fb827) trend
benchmark-table-component/create/1k duration 201.90 (± 3.70 ms) 189.30 (± 3.30 ms) 6.24% 👍
tablecmp-update-10th-1k metric base(d436493) target(e0fb827) trend
benchmark-table-component/update-10th/1k duration 76.80 (± 3.90 ms) 75.70 (± 3.70 ms) 1.43% 👌

@caridy
Copy link
Contributor Author

caridy commented May 11, 2018

@pmdartus @davidturissini I have updated the PR based on the reviews, added tests.

@diervo I still have to fold those decorators from factories to just decorators, will do it tomorrow. Numbers are looking really good.

@caridy caridy changed the title refactor(engine): separating decorators from def.ts refactor(engine): Locker Service related refactor May 11, 2018
@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: d436493 | Target commit: 19cbdcd

lwc-engine-benchmark

table-append-1k metric base(d436493) target(19cbdcd) trend
benchmark-table/append/1k duration 153.00 (± 6.40 ms) 149.60 (± 5.30 ms) 2.22% 👍
table-clear-1k metric base(d436493) target(19cbdcd) trend
benchmark-table/clear/1k duration 11.90 (± 0.60 ms) 11.90 (± 0.40 ms) 0.00% 👌
table-create-10k metric base(d436493) target(19cbdcd) trend
benchmark-table/create/10k duration 893.20 (± 9.90 ms) 871.40 (± 6.40 ms) 2.44% 👍
table-create-1k metric base(d436493) target(19cbdcd) trend
benchmark-table/create/1k duration 102.60 (± 2.50 ms) 103.30 (± 2.30 ms) -0.68% 👌
table-update-10th-1k metric base(d436493) target(19cbdcd) trend
benchmark-table/update-10th/1k duration 87.20 (± 3.30 ms) 94.05 (± 5.45 ms) -7.86% 👎
tablecmp-append-1k metric base(d436493) target(19cbdcd) trend
benchmark-table-component/append/1k duration 250.60 (± 5.50 ms) 245.90 (± 4.50 ms) 1.88% 👍
tablecmp-clear-1k metric base(d436493) target(19cbdcd) trend
benchmark-table/clear/1k duration 32.10 (± 1.40 ms) 31.90 (± 1.30 ms) 0.62% 👌
tablecmp-create-10k metric base(d436493) target(19cbdcd) trend
benchmark-table-component/create/10k duration 1751.50 (± 7.20 ms) 1688.20 (± 7.20 ms) 3.61% 👍
tablecmp-create-1k metric base(d436493) target(19cbdcd) trend
benchmark-table-component/create/1k duration 201.90 (± 3.70 ms) 190.60 (± 4.10 ms) 5.60% 👍
tablecmp-update-10th-1k metric base(d436493) target(19cbdcd) trend
benchmark-table-component/update-10th/1k duration 76.80 (± 3.90 ms) 75.90 (± 3.90 ms) 1.17% 👍

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: d436493 | Target commit: 2988fc0

lwc-engine-benchmark

table-append-1k metric base(d436493) target(2988fc0) trend
benchmark-table/append/1k duration 153.00 (± 6.40 ms) 143.40 (± 3.10 ms) 6.27% 👍
table-clear-1k metric base(d436493) target(2988fc0) trend
benchmark-table/clear/1k duration 11.90 (± 0.60 ms) 11.50 (± 0.50 ms) 3.36% 👍
table-create-10k metric base(d436493) target(2988fc0) trend
benchmark-table/create/10k duration 893.20 (± 9.90 ms) 851.40 (± 7.60 ms) 4.68% 👍
table-create-1k metric base(d436493) target(2988fc0) trend
benchmark-table/create/1k duration 102.60 (± 2.50 ms) 100.50 (± 2.20 ms) 2.05% 👍
table-update-10th-1k metric base(d436493) target(2988fc0) trend
benchmark-table/update-10th/1k duration 87.20 (± 3.30 ms) 86.50 (± 4.40 ms) 0.80% 👌
tablecmp-append-1k metric base(d436493) target(2988fc0) trend
benchmark-table-component/append/1k duration 250.60 (± 5.50 ms) 247.00 (± 3.30 ms) 1.44% 👍
tablecmp-clear-1k metric base(d436493) target(2988fc0) trend
benchmark-table/clear/1k duration 32.10 (± 1.40 ms) 31.00 (± 1.20 ms) 3.43% 👍
tablecmp-create-10k metric base(d436493) target(2988fc0) trend
benchmark-table-component/create/10k duration 1751.50 (± 7.20 ms) 1678.60 (± 5.70 ms) 4.16% 👍
tablecmp-create-1k metric base(d436493) target(2988fc0) trend
benchmark-table-component/create/1k duration 201.90 (± 3.70 ms) 187.60 (± 4.30 ms) 7.08% 👍
tablecmp-update-10th-1k metric base(d436493) target(2988fc0) trend
benchmark-table-component/update-10th/1k duration 76.80 (± 3.90 ms) 74.00 (± 4.20 ms) 3.65% 👍

it('should throw when invoking it with no arguments', () => {
expect(() => {
decorate();
}).toThrow();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should validate a specific error message to eliminate the possibility of other failures inside 'decorate()' method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are testing the decorate function, it doesn't invoke anything since no arguments are provided.

class f {}
expect(decorate(f, {})).toBe(f);
});
it('should throw when invoking it with no arguments', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test title isn't clear since the decorator is invoked with paremeters

const COMPUTED_GETTER_MASK = 1;
const COMPUTED_SETTER_MASK = 2;

export default function api(target: any, propName: PropertyKey, descriptor: PropertyDescriptor | undefined): PropertyDescriptor {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to create Target type so that expected attributes can be easily validated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hard to define a class..., we can decorate any class.

}
}
const meta = target.publicProps;
const config = (hasOwnProperty.call(target, 'publicProps') && hasOwnProperty.call(meta, propName)) ? meta[propName].config : 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use meta !== undefined as a first condition since it has already been retrieved on line #20?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, this is testing that what we are retrieving is in fact an own prop, otherwise it is coming from inheritance, which is not the intent here, I can add a comment.

export type DecoratorMap = Record<string, DecoratorFunction>;

export default function decorate(Ctor: any, decorators: DecoratorMap): any {
if (!isFunction(Ctor) || decorators == null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't second condition be triple equals? Otherwise we need to change the parameter type to include ' | undefined'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentionally ==, will add a comment

import { isRendering, vmBeingRendered } from "../invoker";
import { observeMutation, notifyMutation } from "../watcher";
import { VMElement } from "../vm";
import { getCustomElementVM } from "../html-element";
import { reactiveMembrane } from '../membrane';

// stub function to prevent misuse of the @track decorator
export default function track(obj: any): any {
export default function track(target: any, prop: PropertyKey, descriptor: PropertyDescriptor | undefined): PropertyDescriptor | any {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to be more explicit on return type, instead of 'any'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is literally any because this method is overloaded, so you can call it with anything that it will wrap with a proxy, or u can use it as a decorator. I can probably improve that using TS overloading def.

}
if (!isUndefined(descriptor)) {
const { get, set, configurable, writable } = descriptor;
assert.isTrue(!get && !set, `Compiler Error: A @track decorator can only be applied to a public field.`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should check for 'get' existence if 'set' is specified

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, we are testing that none of them exists, track can't have getter or setter.

export default function wire(adapter: any, config: any): DecoratorFunction {
const len = arguments.length;
if (len > 0 && len < 3) {
return wireDecorator;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we care to check for undefined values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, at the moment, we don't do anything with those just yet.

export interface MethodDef {
[key: string]: 1;
[key: string]: PublicMethod;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thumbs up on adding a type

if (wire) {
for (const propName in wire) {
const wireDef: WireDef = wire[propName];
if (wireDef.method) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to check 'method' type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, this is not userland code, just compiled code. method there is a number 1 always.

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 31fd3f9 | Target commit: 3c8db26

lwc-engine-benchmark

table-append-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table/append/1k duration 149.70 (± 4.40 ms) 145.70 (± 4.50 ms) 2.67% 👍
table-clear-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table/clear/1k duration 11.85 (± 0.55 ms) 11.40 (± 0.40 ms) 3.80% 👌
table-create-10k metric base(31fd3f9) target(3c8db26) trend
benchmark-table/create/10k duration 839.50 (± 5.60 ms) 839.90 (± 4.50 ms) -0.05% 👌
table-create-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table/create/1k duration 101.25 (± 1.75 ms) 100.50 (± 1.60 ms) 0.74% 👌
table-update-10th-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table/update-10th/1k duration 87.50 (± 6.00 ms) 84.90 (± 2.20 ms) 2.97% 👌
tablecmp-append-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table-component/append/1k duration 246.40 (± 2.90 ms) 243.50 (± 4.80 ms) 1.18% 👍
tablecmp-clear-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table/clear/1k duration 30.90 (± 1.60 ms) 31.80 (± 1.30 ms) -2.91% 👌
tablecmp-create-10k metric base(31fd3f9) target(3c8db26) trend
benchmark-table-component/create/10k duration 1683.10 (± 7.90 ms) 1688.40 (± 10.30 ms) -0.31% 👌
tablecmp-create-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table-component/create/1k duration 194.60 (± 4.70 ms) 189.60 (± 3.40 ms) 2.57% 👍
tablecmp-update-10th-1k metric base(31fd3f9) target(3c8db26) trend
benchmark-table-component/update-10th/1k duration 75.40 (± 4.50 ms) 74.10 (± 3.70 ms) 1.72% 👌

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 31fd3f9 | Target commit: d7c951e

lwc-engine-benchmark

table-append-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table/append/1k duration 149.70 (± 4.40 ms) 144.40 (± 3.40 ms) 3.54% 👍
table-clear-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table/clear/1k duration 11.85 (± 0.55 ms) 11.70 (± 0.50 ms) 1.27% 👌
table-create-10k metric base(31fd3f9) target(d7c951e) trend
benchmark-table/create/10k duration 839.50 (± 5.60 ms) 850.00 (± 10.90 ms) -1.25% 👎
table-create-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table/create/1k duration 101.25 (± 1.75 ms) 100.90 (± 1.90 ms) 0.35% 👌
table-update-10th-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table/update-10th/1k duration 87.50 (± 6.00 ms) 86.80 (± 5.40 ms) 0.80% 👌
tablecmp-append-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table-component/append/1k duration 246.40 (± 2.90 ms) 242.80 (± 5.90 ms) 1.46% 👌
tablecmp-clear-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table/clear/1k duration 30.90 (± 1.60 ms) 30.70 (± 1.50 ms) 0.65% 👌
tablecmp-create-10k metric base(31fd3f9) target(d7c951e) trend
benchmark-table-component/create/10k duration 1683.10 (± 7.90 ms) 1706.80 (± 6.70 ms) -1.41% 👎
tablecmp-create-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table-component/create/1k duration 194.60 (± 4.70 ms) 188.60 (± 4.30 ms) 3.08% 👍
tablecmp-update-10th-1k metric base(31fd3f9) target(d7c951e) trend
benchmark-table-component/update-10th/1k duration 75.40 (± 4.50 ms) 73.70 (± 2.90 ms) 2.25% 👌

const component = getCustomElementComponent(this);
return component[key].apply(component, ArraySlice.call(arguments));
const vm = getCustomElementVM(this);
return method.apply(vm.component, ArraySlice.call(arguments));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apply with arguments... review this.

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 31fd3f9 | Target commit: 765ccb9

lwc-engine-benchmark

table-append-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table/append/1k duration 149.70 (± 4.40 ms) 144.10 (± 3.20 ms) 3.74% 👍
table-clear-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table/clear/1k duration 11.85 (± 0.55 ms) 11.90 (± 0.50 ms) -0.42% 👌
table-create-10k metric base(31fd3f9) target(765ccb9) trend
benchmark-table/create/10k duration 839.50 (± 5.60 ms) 837.50 (± 8.70 ms) 0.24% 👌
table-create-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table/create/1k duration 101.25 (± 1.75 ms) 100.80 (± 1.90 ms) 0.44% 👌
table-update-10th-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table/update-10th/1k duration 87.50 (± 6.00 ms) 86.30 (± 6.70 ms) 1.37% 👌
tablecmp-append-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table-component/append/1k duration 246.40 (± 2.90 ms) 201.90 (± 5.80 ms) 18.06% 👍
tablecmp-clear-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table/clear/1k duration 30.90 (± 1.60 ms) 31.00 (± 1.10 ms) -0.32% 👌
tablecmp-create-10k metric base(31fd3f9) target(765ccb9) trend
benchmark-table-component/create/10k duration 1683.10 (± 7.90 ms) 1678.60 (± 7.10 ms) 0.27% 👍
tablecmp-create-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table-component/create/1k duration 194.60 (± 4.70 ms) 188.00 (± 3.20 ms) 3.39% 👍
tablecmp-update-10th-1k metric base(31fd3f9) target(765ccb9) trend
benchmark-table-component/update-10th/1k duration 75.40 (± 4.50 ms) 74.80 (± 3.20 ms) 0.80% 👌

@caridy
Copy link
Contributor Author

caridy commented May 12, 2018

@davidturissini numbers are looking good! this is waiting for your review. I already did a first pass with @diervo, and @apapko did another pass.

vm.component = component;
// interaction hooks
if (arguments.length === 1) {
const { callHook, setHook, getHook } = arguments[0] as {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lift the type definition out of the function body.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And change the function signature to:

function LWCElement(this: Component, hooks?: ComponentHooks) {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, intentionally not exposing this behavior so users don't need to know about it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO there 2 aspects here, the type completeness and the APIs we want to advertise to customers:

  • We should aim to have 100% type coverage in the codebase and make sure that even internal APIs are typed properly.
  • While for SFDC customers we provide a set of blessed APIs to be used on the platform. This type definition file should be hand-crafted so this way we have control of what we expose or not.

const props = getOwnPropertyNames(decorators);
// intentionally allowing decoration of classes only for now
const target = Ctor.prototype;
for (let i = 0, len = props.length; i < len; i += 1) {
Copy link
Contributor

@davidturissini davidturissini May 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache length

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is caching the length.

const target = Ctor.prototype;
for (let i = 0, len = props.length; i < len; i += 1) {
const prop = props[i];
const decorator = decorators[prop];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think propName is better variable name here

// stub function to prevent misuse of the @track decorator
export default function track(obj: any): any {
export default function track(target: ComponentConstructor, prop: PropertyKey, descriptor: PropertyDescriptor | undefined): PropertyDescriptor;
export default function track(target: any, prop?, descriptor?): any {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure optional types is what we want here. Perhaps prop: PropertyKey | undefined, descriptor: PropertyDescriptor | undefined is better but I'm not sure.

I'm also not sure how prop is optional

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we change it to this one to have more clarity! cc @apapko

vm.component = component;
// interaction hooks
if (arguments.length === 1) {
const { callHook, setHook, getHook } = arguments[0] as {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not name this argument? This is confusing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we name the arg, we can put the type in the function arguments, not inline

@@ -59,6 +61,18 @@ export interface VM {
let idx: number = 0;
let uid: number = 0;

function callHook(cmp: Component | undefined, fn: (...args: any[]) => any, args?: any[]): any {
return fn.apply(cmp, args);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming we will add the actual hooks in another PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

locker pass those in... we have default behaviors here.

Copy link
Contributor

@davidturissini davidturissini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets go

@salesforce-best-lwc-internal
Copy link

Benchmark results

Base commit: 75722cb | Target commit: 239837c

lwc-engine-benchmark

table-append-1k metric base(75722cb) target(239837c) trend
benchmark-table/append/1k duration 142.90 (± 2.40 ms) 146.60 (± 4.30 ms) -2.59% 👎
table-clear-1k metric base(75722cb) target(239837c) trend
benchmark-table/clear/1k duration 11.40 (± 0.40 ms) 12.15 (± 0.45 ms) -6.58% 👎
table-create-10k metric base(75722cb) target(239837c) trend
benchmark-table/create/10k duration 849.10 (± 7.70 ms) 852.70 (± 7.70 ms) -0.42% 👌
table-create-1k metric base(75722cb) target(239837c) trend
benchmark-table/create/1k duration 101.00 (± 2.30 ms) 101.40 (± 2.20 ms) -0.40% 👌
table-update-10th-1k metric base(75722cb) target(239837c) trend
benchmark-table/update-10th/1k duration 95.00 (± 4.60 ms) 86.40 (± 3.90 ms) 9.05% 👍
tablecmp-append-1k metric base(75722cb) target(239837c) trend
benchmark-table-component/append/1k duration 243.40 (± 5.40 ms) 244.20 (± 5.70 ms) -0.33% 👌
tablecmp-clear-1k metric base(75722cb) target(239837c) trend
benchmark-table/clear/1k duration 31.60 (± 1.10 ms) 31.10 (± 1.10 ms) 1.58% 👌
tablecmp-create-10k metric base(75722cb) target(239837c) trend
benchmark-table-component/create/10k duration 1717.10 (± 9.00 ms) 1684.20 (± 6.70 ms) 1.92% 👍
tablecmp-create-1k metric base(75722cb) target(239837c) trend
benchmark-table-component/create/1k duration 193.60 (± 3.80 ms) 189.40 (± 4.80 ms) 2.17% 👍
tablecmp-update-10th-1k metric base(75722cb) target(239837c) trend
benchmark-table-component/update-10th/1k duration 74.10 (± 3.80 ms) 74.20 (± 3.40 ms) -0.13% 👌

@caridy caridy merged commit 07213f0 into master May 15, 2018
@caridy caridy deleted the caridy/decorate branch May 15, 2018 17:28
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.

None yet

6 participants