From 12d670890f7fafe889fc15fe696c20aee6af2554 Mon Sep 17 00:00:00 2001 From: baakqiu Date: Wed, 13 Nov 2019 13:35:30 +0800 Subject: [PATCH] [Update] Fix accessing this before calling super --- src/compile/expression.ts | 2 +- src/jsvm/index.ts | 34 +++++++++++++++++++---------- tests/class.test.ts | 46 +++++++++++++++++++-------------------- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/compile/expression.ts b/src/compile/expression.ts index 0d04686..128ddc5 100644 --- a/src/compile/expression.ts +++ b/src/compile/expression.ts @@ -5,7 +5,7 @@ import { OP } from '../share/const' import { compileFunc, compileClass, compilePattern } from './helper' export function ThisExpression(node: estree.ThisExpression, state: State) { - state.opCodes.push({ op: OP.LOADV, val: state.symbols.get('this').pointer }) + state.opCodes.push({ op: OP.LOADV, val: state.symbols.get('this').pointer, this: true }) } export function ArrayExpression(node: estree.ArrayExpression, state: State) { diff --git a/src/jsvm/index.ts b/src/jsvm/index.ts index 0a80f54..1f8cc46 100644 --- a/src/jsvm/index.ts +++ b/src/jsvm/index.ts @@ -8,7 +8,14 @@ function step(state: State) { let signal = SIGNAL.NONE switch (code.op) { case OP.LOADK: stack[state.esp++] = code.val; break - case OP.LOADV: stack[state.esp++] = state.context[code.val] && state.context[code.val].store; break + case OP.LOADV: { + const value = state.context[code.val] && state.context[code.val].store + if (code.this && value['#sval_super_called#'] === false) { + throw new ReferenceError("Must call super constructor in derived class before accessing 'this'") + } + stack[state.esp++] = value + break + } case OP.ALLOC: { const ebp = state.ebpList[state.ebpList.length - 1] const storeVal = state.esp > ebp ? stack[--state.esp] : undefined @@ -301,9 +308,15 @@ function step(state: State) { } let ctor = function () { if (classCtor) { + if (superClass) { + define(this, '#sval_super_called#', { value: false, configurable: true }) + } const result = classCtor.apply(this, arguments) - if (superClass && !this['#sval_es6_class_super_called#']) { - throw new ReferenceError('Must call super constructor in derived class before returning from derived constructor') + if (superClass) { + if (!this['#sval_super_called#']) { + throw new ReferenceError('Must call super constructor in derived class before returning from derived constructor') + } + delete this['#sval_super_called#'] } return result } else if (superClass) { @@ -362,6 +375,13 @@ function step(state: State) { const obj = stack[--state.esp] + if (code.super) { + if (obj['#sval_super_called#']) { + throw new ReferenceError('Super constructor may only be called once') + } + define(obj, '#sval_super_called#', { value: true, configurable: true }) + } + const spread = code.val.concat() const newEsp = state.esp - spread.pop() const argsItems = stack.slice(newEsp, state.esp) @@ -392,14 +412,6 @@ function step(state: State) { state.esp = state.ebpList.pop() stack[state.esp++] = result - if (code.super) { - if (obj['#sval_es6_class_super_called#']) { - throw new ReferenceError('Super constructor may only be called once') - } else { - define(obj, '#sval_es6_class_super_called#', { value: true }) - } - } - break } case OP.NEW: { diff --git a/tests/class.test.ts b/tests/class.test.ts index 27fbd1f..f4f0b3b 100644 --- a/tests/class.test.ts +++ b/tests/class.test.ts @@ -262,31 +262,31 @@ describe('testing src/index.ts', () => { expect(interpreter.exports.y).toBe(1) }) - // it('should throw ReferenceError when super() is not called before acessing this', () => { - // const interpreter = new Sval() - // let error = null - // try { - // interpreter.run(` - // class X { - // constructor() { - // this.x = 1 - // } - // } + it('should throw ReferenceError when super() is not called before acessing this', () => { + const interpreter = new Sval() + let error = null + try { + interpreter.run(` + class X { + constructor() { + this.x = 1 + } + } - // class Y extends X { - // constructor() { - // this.x = 2 - // super() - // } - // } + class Y extends X { + constructor() { + this.x = 2 + super() + } + } - // const y = new Y() - // `) - // } catch (err) { - // error = err - // } - // expect(error).toBeInstanceOf(ReferenceError) - // }) + const y = new Y() + `) + } catch (err) { + error = err + } + expect(error).toBeInstanceOf(ReferenceError) + }) it('should throw ReferenceError when super() is not called in constructor for derived class', () => { const interpreter = new Sval()