Skip to content
This repository was archived by the owner on Sep 10, 2023. It is now read-only.

Commit 269d97c

Browse files
committed
fix: refactor how class declaration
1 parent c0d42f7 commit 269d97c

File tree

4 files changed

+139
-93
lines changed

4 files changed

+139
-93
lines changed

src/evaluate.ts

Lines changed: 91 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -652,22 +652,15 @@ const evaluate_map: EvaluateMap = {
652652
MemberExpression(path) {
653653
const {node, scope, ctx} = path;
654654
const {object, property, computed} = node;
655-
if (types.isSuper(node.object)) {
656-
const $var = scope.$find("this");
657-
if ($var) {
658-
const __this = $var.$get();
659-
if (ctx.SuperClass) {
660-
return ctx.SuperClass.prototype[(<any>property).name].bind(__this);
661-
} else {
662-
throw node;
663-
}
664-
}
665-
}
666-
if (computed) {
667-
return evaluate(path.$child(object))[evaluate(path.$child(property))];
668-
} else {
669-
return evaluate(path.$child(object))[(<types.Identifier>property).name];
670-
}
655+
656+
const propertyName: string = computed
657+
? evaluate(path.$child(property))
658+
: (<types.Identifier>property).name;
659+
660+
const obj = evaluate(path.$child(object));
661+
const target = obj[propertyName];
662+
663+
return typeof target === "function" ? target.bind(obj) : target;
671664
},
672665
AssignmentExpression(path) {
673666
const {node, scope} = path;
@@ -781,10 +774,10 @@ const evaluate_map: EvaluateMap = {
781774
}
782775
};
783776

784-
Object.defineProperties(func,{
785-
length:{value:node.params.length},
786-
name: {value:node.id ? node.id.name : ""}
787-
})
777+
Object.defineProperties(func, {
778+
length: {value: node.params.length},
779+
name: {value: node.id ? node.id.name : ""}
780+
});
788781

789782
return func;
790783
},
@@ -800,96 +793,75 @@ const evaluate_map: EvaluateMap = {
800793
return path.node.value.raw;
801794
},
802795
ClassDeclaration(path) {
796+
const ClassConstructor = evaluate(
797+
path.$child(path.node.body, path.scope.$child("block"))
798+
);
799+
path.scope.$const(path.node.id.name, ClassConstructor);
800+
},
801+
ClassBody(path) {
803802
const {node, scope} = path;
804-
const constructor: types.ClassMethod | void = <types.ClassMethod | void>node.body.body.find(
803+
const constructor: types.ClassMethod | void = <types.ClassMethod | void>node.body.find(
805804
n => types.isClassMethod(n) && n.kind === "constructor"
806805
);
807-
const methods: types.ClassMethod[] = <types.ClassMethod[]>node.body.body.filter(
806+
const methods: types.ClassMethod[] = <types.ClassMethod[]>node.body.filter(
808807
n => types.isClassMethod(n) && n.kind !== "constructor"
809808
);
810-
const properties: types.ClassProperty[] = <types.ClassProperty[]>node.body.body.filter(
809+
const properties: types.ClassProperty[] = <types.ClassProperty[]>node.body.filter(
811810
n => types.isClassProperty(n)
812811
);
813812

814-
let SuperClass;
815-
816-
if (node.superClass) {
817-
const superClassName: string = (<any>node.superClass).name;
818-
const $var = scope.$find(superClassName);
819-
820-
if ($var) {
821-
SuperClass = $var.$get();
822-
} else {
823-
throw new ErrNotDefined(superClassName);
824-
}
825-
}
813+
const parentNode = (<Path<types.ClassDeclaration>>path.parent).node;
826814

827815
const Class = (function(SuperClass) {
828816
if (SuperClass) {
829817
_inherits(Class, SuperClass);
830818
}
819+
831820
function Class(...args) {
832821
_classCallCheck(this, Class);
833-
834-
const newScope = scope.$child("function");
835-
836-
// babel way to call super();
837-
const __this = _possibleConstructorReturn(
838-
this,
839-
((<any>Class).__proto__ || Object.getPrototypeOf(Class)).apply(
840-
this,
841-
args
842-
)
843-
);
844-
845-
newScope.$const("super", this.__proto__);
846-
847-
// typescript way to call super()
848-
// const __this = superClass ? superClass.apply(this, args) || this : this;
849-
850-
newScope.$const("this", __this);
822+
const classScope = scope.$child("function");
823+
classScope.$var("this", this);
851824

852825
// define class property
853826
properties.forEach(p => {
854-
__this[p.key.name] = evaluate(path.$child(p.value, newScope));
827+
this[p.key.name] = evaluate(path.$child(p.value, classScope));
855828
});
856829

857830
if (constructor) {
858831
// defined the params
859832
constructor.params.forEach((p: types.LVal, i) => {
860833
if (types.isIdentifier(p)) {
861-
newScope.$const(p.name, args[i]);
834+
classScope.$const(p.name, args[i]);
862835
} else {
863836
throw new Error("Invalid params");
864837
}
865838
});
839+
constructor.body.body.forEach(n =>
840+
evaluate(
841+
path.$child(n, classScope, {
842+
SuperClass,
843+
ClassConstructor: Class,
844+
ClassConstructorArguments: args,
845+
ClassEntity: this
846+
})
847+
)
848+
);
866849

867-
if (node.superClass) {
868-
// make sure super exist in first line
869-
const superCallExpression: types.ExpressionStatement = <types.ExpressionStatement>(<any>constructor).body.body.shift();
870-
871-
if (
872-
!types.isExpressionStatement(superCallExpression) ||
873-
!types.isCallExpression(superCallExpression.expression) ||
874-
!types.isSuper(superCallExpression.expression.callee)
875-
) {
850+
if (parentNode.superClass) {
851+
// if not apply super in construtor
852+
// FIXME: should define the var in private scope
853+
if (!scope.$find("@super")) {
876854
throw ErrNoSuper;
877-
} else {
878-
// TODO: run super
879855
}
880856
}
881-
882-
constructor.body.body.forEach(n =>
883-
evaluate(path.$child(n, newScope, {SuperClass}))
884-
);
885857
}
886858

887-
return __this;
859+
return this;
888860
}
889861

890862
// define class name
891863
Object.defineProperties(Class, {
892-
name: {value: node.id.name},
864+
name: {value: parentNode.id.name},
893865
length: {value: constructor ? constructor.params.length : 0}
894866
});
895867

@@ -907,7 +879,12 @@ const evaluate_map: EvaluateMap = {
907879
});
908880

909881
const result = evaluate(
910-
path.$child(method.body, newScope, {SuperClass})
882+
path.$child(method.body, newScope, {
883+
SuperClass,
884+
ClassConstructor: Class,
885+
ClassMethodArguments: args,
886+
ClassEntity: this
887+
})
911888
);
912889
if (result === RETURN_SINGAL) {
913890
return result.result ? result.result : result;
@@ -934,17 +911,54 @@ const evaluate_map: EvaluateMap = {
934911
_createClass(Class, _methods);
935912

936913
return Class;
937-
})(SuperClass);
914+
})(
915+
parentNode.superClass
916+
? (() => {
917+
const $var = scope.$find((<any>parentNode.superClass).name);
918+
return $var ? $var.$get() : null;
919+
})()
920+
: null
921+
);
938922

939-
scope.$const(node.id.name, Class);
923+
return Class;
940924
},
941925
ClassMethod(path) {
942926
return evaluate(path.$child(path.node.body));
943927
},
944928
ClassExpression(path) {},
945929
Super(path) {
946-
// FIXME: check it include in Class expression
947-
return function() {};
930+
const {scope, ctx} = path;
931+
const {
932+
SuperClass,
933+
ClassConstructor,
934+
ClassConstructorArguments,
935+
ClassEntity
936+
} = ctx;
937+
const ClassBodyPath = path.$findParent("ClassBody");
938+
// make sure it include in ClassDeclaration
939+
if (!ClassBodyPath) {
940+
throw new Error("super() only can use in ClassDeclaration");
941+
}
942+
const parentPath = path.parent;
943+
if (parentPath) {
944+
// super()
945+
if (types.isCallExpression(parentPath.node)) {
946+
return function inherits(...args) {
947+
_possibleConstructorReturn(
948+
ClassEntity,
949+
(
950+
(<any>ClassConstructor).__proto__ ||
951+
Object.getPrototypeOf(ClassConstructor)
952+
).apply(ClassEntity, args)
953+
);
954+
ClassBodyPath.scope.$const("@super", true);
955+
}.bind(ClassEntity);
956+
} else if (types.isMemberExpression(parentPath.node)) {
957+
// super.eat()
958+
// then return the superclass prototype
959+
return SuperClass.prototype;
960+
}
961+
}
948962
},
949963
SpreadElement(path) {
950964
return evaluate(path.$child(path.node.argument));

src/path.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
import {
2-
Node,
3-
ClassDeclaration,
4-
ClassExpression,
5-
CallExpression
6-
} from "babel-types";
1+
import {Node} from "babel-types";
72
import {Scope, ScopeType} from "./scope";
83

94
export class Path<T extends Node> {
105
constructor(
116
public node: T,
12-
public parent: Node | null,
7+
public parent: Path<Node> | null,
138
public scope: Scope,
149
public ctx: any = {}
1510
) {}
@@ -20,11 +15,20 @@ export class Path<T extends Node> {
2015
): Path<Child> {
2116
return new Path(
2217
node,
23-
this.parent,
18+
this,
2419
scope
2520
? typeof scope === "string" ? this.scope.$child(scope) : scope
2621
: this.scope,
2722
{...this.ctx, ...ctx}
2823
);
2924
}
25+
$findParent(type: string): Path<Node> | null {
26+
if (this.parent) {
27+
return this.parent.node.type === type
28+
? this.parent
29+
: this.parent.$findParent(type);
30+
} else {
31+
return null;
32+
}
33+
}
3034
}

src/type.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export interface NodeTypeMap {
6565
// ArrayPattern: types.ArrayPattern;
6666
RestElement: types.RestElement;
6767
AssignmentPattern: types.AssignmentPattern;
68-
// ClassBody: types.ClassBody;
68+
ClassBody: types.ClassBody;
6969
ImportDeclaration: types.ImportDeclaration;
7070
ExportNamedDeclaration: types.ExportNamedDeclaration;
7171
ExportDefaultDeclaration: types.ExportDefaultDeclaration;

test/ClassDeclaration.test.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ module.exports = {
144144
t.deepEqual(typeof people.learn, "function");
145145
});
146146

147-
test("ClassDeclaration-extends and super", t => {
147+
test("ClassDeclaration-extends and super-1", t => {
148148
const sandbox: any = vm.createContext({});
149149

150150
const {Life, People} = vm.runInContext(
@@ -191,7 +191,7 @@ module.exports = {
191191
t.true(people.learn()); // inherit from Life getSkill method
192192
});
193193

194-
test("ClassDeclaration-extends and super", t => {
194+
test("ClassDeclaration-extends and super-2", t => {
195195
const sandbox: any = vm.createContext({});
196196

197197
const {People} = vm.runInContext(
@@ -243,14 +243,42 @@ test("ClassDeclaration-extends without super", t => {
243243
t.throws(function() {
244244
vm.runInContext(
245245
`
246-
class Life{
246+
class Life{
247+
}
248+
class People extends Life{
249+
constructor(name){
247250
}
248-
class People extends Life{
249-
constructor(name){
250-
}
251+
}
252+
253+
module.exports = new People();
254+
`,
255+
sandbox
256+
);
257+
}, ErrNoSuper.message);
258+
});
259+
260+
test("ClassDeclaration-extends without super", t => {
261+
const sandbox: any = vm.createContext({});
262+
263+
t.throws(function() {
264+
vm.runInContext(
265+
`
266+
class Life{
267+
}
268+
class People extends Life{
269+
constructor(name){
270+
super()
251271
}
272+
}
273+
274+
class A extends Life{
275+
constructor(name){
276+
// here will throw an error
277+
}
278+
}
252279
253-
module.exports = new People();
280+
new People();
281+
new A(); // throw error
254282
`,
255283
sandbox
256284
);

0 commit comments

Comments
 (0)