Skip to content

Commit

Permalink
[Update] Fix accessing this before calling super
Browse files Browse the repository at this point in the history
  • Loading branch information
Siubaak committed Nov 13, 2019
1 parent 722a65e commit 12d6708
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 35 deletions.
2 changes: 1 addition & 1 deletion src/compile/expression.ts
Expand Up @@ -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) {
Expand Down
34 changes: 23 additions & 11 deletions src/jsvm/index.ts
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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: {
Expand Down
46 changes: 23 additions & 23 deletions tests/class.test.ts
Expand Up @@ -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()
Expand Down

0 comments on commit 12d6708

Please sign in to comment.