Skip to content

Commit

Permalink
Emit for decorators, updated to ES6 classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Mar 17, 2015
1 parent 39001f5 commit bd4d7fc
Show file tree
Hide file tree
Showing 4 changed files with 438 additions and 27 deletions.
24 changes: 24 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11267,6 +11267,29 @@ module ts {
!hasProperty(getGeneratedNamesForSourceFile(getSourceFile(location)), name);
}

function getClassDeclarationVariableId(n: Identifier): number {

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Mar 20, 2015

Contributor

Why do we need this?

This comment has been minimized.

Copy link
@rbuckton

rbuckton Mar 21, 2015

Author Member

ES6 classes have a doubly-bound identifier:

class C {
  method() {
    C; // binding inside of class
  }
}
C; // binding in outer scope

This differs from function declarations (whose identifier is only bound in the containing scope), and function expressions (whose identifier is only bound inside the function). As a result, I need to alias the class to ensure we use the decorated C:

// TS
@dec
class C {
  method() {
    C; // needs to be the decorated C, not the undecorated C.
  }
}
// ES6
var C = (function() {
  let _C = class C {
    method() {
      _C; // uses _C, since C is always the undecorated C inside of the class body.
    }
  }
  _C = __decorate([dec], _C);
  return _C;
})();

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Mar 21, 2015

Contributor

Makes sense. So the binding in the outer scope is fine as is, but the binding in the class needs to be altered. Please explain in a comment in the code.

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Mar 22, 2015

Contributor

Is this also the case when you refer to 'this' inside a static method of a class? Will you get the undecorated class constructor?

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Mar 22, 2015

Contributor

Also, I think the IIFE needs to be assigned to a let, not a var.

This comment has been minimized.

Copy link
@rbuckton

rbuckton Mar 23, 2015

Author Member

this should be fine as it should be the this bound by the call site, which should decorated version of the class at all call sites.

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Mar 23, 2015

Contributor

Ah yes, true

Debug.assert(!nodeIsSynthesized(n));

let isClassDeclaration = n.parent.kind === SyntaxKind.ClassDeclaration;

let symbol =
(isClassDeclaration ? getSymbolOfNode(n.parent) : undefined) ||
getNodeLinks(n).resolvedSymbol ||
resolveName(n, n.text, SymbolFlags.Value | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);

symbol = getExportSymbolOfValueSymbolIfExported(symbol);

let isClass = symbol && (symbol.flags & SymbolFlags.Class) !== 0;

if (isClass) {
// side-effect of calling this method:
// assign id to symbol if it was not yet set
getSymbolLinks(symbol);
return symbol.id;
}
return undefined;
}

function getBlockScopedVariableId(n: Identifier): number {
Debug.assert(!nodeIsSynthesized(n));

Expand Down Expand Up @@ -11309,6 +11332,7 @@ module ts {
getConstantValue,
isUnknownIdentifier,
getBlockScopedVariableId,
getClassDeclarationVariableId,
};
}

Expand Down
Loading

1 comment on commit bd4d7fc

@JsonFreeman
Copy link
Contributor

Choose a reason for hiding this comment

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

In general, I think the emitter needs a bunch of commenting with code samples. The comments should explain whatever caveats you need to account for, and the code examples are useful because the emitter's source code does not resemble the emitted code.

Please sign in to comment.