Browse Source

Set correct 'this' when calling member functions within a 'with' block. Fixes #151.

Dmitry Panov 5 years ago
parent
commit
e92122c4a4
3 changed files with 103 additions and 20 deletions
  1. 19 2
      compiler_expr.go
  2. 28 0
      compiler_test.go
  3. 56 18
      vm.go

+ 19 - 2
compiler_expr.go

@@ -308,6 +308,24 @@ func (e *compiledIdentifierExpr) emitGetterOrRef() {
 	} else {
 	} else {
 		if found {
 		if found {
 			e.c.emit(getVar{name: e.name, idx: idx, ref: true})
 			e.c.emit(getVar{name: e.name, idx: idx, ref: true})
+		} else {
+			e.c.emit(getVar1Ref(e.name))
+		}
+	}
+}
+
+func (e *compiledIdentifierExpr) emitGetterAndCallee() {
+	e.addSrcMap()
+	if idx, found, noDynamics := e.c.scope.lookupName(e.name); noDynamics {
+		if found {
+			e.c.emit(loadUndef)
+			e.c.emit(getLocal(idx))
+		} else {
+			panic("No dynamics and not found")
+		}
+	} else {
+		if found {
+			e.c.emit(getVar{name: e.name, idx: idx, ref: true, callee: true})
 		} else {
 		} else {
 			e.c.emit(getVar1Callee(e.name))
 			e.c.emit(getVar1Callee(e.name))
 		}
 		}
@@ -1423,9 +1441,8 @@ func (e *compiledCallExpr) emitGetter(putOnStack bool) {
 		callee.member.emitGetter(true)
 		callee.member.emitGetter(true)
 		e.c.emit(getElemCallee)
 		e.c.emit(getElemCallee)
 	case *compiledIdentifierExpr:
 	case *compiledIdentifierExpr:
-		e.c.emit(loadUndef)
 		calleeName = callee.name
 		calleeName = callee.name
-		callee.emitGetterOrRef()
+		callee.emitGetterAndCallee()
 	default:
 	default:
 		e.c.emit(loadUndef)
 		e.c.emit(loadUndef)
 		callee.emitGetter(true)
 		callee.emitGetter(true)

+ 28 - 0
compiler_test.go

@@ -1993,6 +1993,34 @@ func TestIfStackLeaks(t *testing.T) {
 	testScript1(SCRIPT, _positiveZero, t)
 	testScript1(SCRIPT, _positiveZero, t)
 }
 }
 
 
+func TestWithCallee(t *testing.T) {
+	const SCRIPT = `
+	function O() {
+		var that = this;
+		this.m = function() {
+			return this === that;
+		}
+	}
+	with(new O()) {
+		m();
+	}
+	`
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestEvalCallee(t *testing.T) {
+	const SCRIPT = `
+	(function () {
+		'use strict';
+		var v = function() {
+			return this === undefined;
+		};
+		return eval('v()');
+	})();
+	`
+	testScript1(SCRIPT, valueTrue, t)
+}
+
 // FIXME
 // FIXME
 /*
 /*
 func TestDummyCompile(t *testing.T) {
 func TestDummyCompile(t *testing.T) {

+ 56 - 18
vm.go

@@ -19,7 +19,7 @@ type stash struct {
 	values    valueStack
 	values    valueStack
 	extraArgs valueStack
 	extraArgs valueStack
 	names     map[string]uint32
 	names     map[string]uint32
-	obj       objectImpl
+	obj       *Object
 
 
 	outer *stash
 	outer *stash
 }
 }
@@ -200,8 +200,8 @@ func (s *valueStack) expand(idx int) {
 
 
 func (s *stash) put(name string, v Value) bool {
 func (s *stash) put(name string, v Value) bool {
 	if s.obj != nil {
 	if s.obj != nil {
-		if found := s.obj.getStr(name); found != nil {
-			s.obj.putStr(name, v, false)
+		if found := s.obj.self.getStr(name); found != nil {
+			s.obj.self.putStr(name, v, false)
 			return true
 			return true
 		}
 		}
 		return false
 		return false
@@ -232,7 +232,7 @@ func (s *stash) getByIdx(idx uint32) Value {
 
 
 func (s *stash) getByName(name string, _ *vm) (v Value, exists bool) {
 func (s *stash) getByName(name string, _ *vm) (v Value, exists bool) {
 	if s.obj != nil {
 	if s.obj != nil {
-		v = s.obj.getStr(name)
+		v = s.obj.self.getStr(name)
 		if v == nil {
 		if v == nil {
 			return nil, false
 			return nil, false
 			//return valueUnresolved{r: vm.r, ref: name}, false
 			//return valueUnresolved{r: vm.r, ref: name}, false
@@ -258,7 +258,7 @@ func (s *stash) createBinding(name string) {
 
 
 func (s *stash) deleteBinding(name string) bool {
 func (s *stash) deleteBinding(name string) bool {
 	if s.obj != nil {
 	if s.obj != nil {
-		return s.obj.deleteStr(name, false)
+		return s.obj.self.deleteStr(name, false)
 	}
 	}
 	if idx, found := s.names[name]; found {
 	if idx, found := s.names[name]; found {
 		s.values[idx] = nil
 		s.values[idx] = nil
@@ -1332,9 +1332,9 @@ func (s resolveVar1) exec(vm *vm) {
 	var ref ref
 	var ref ref
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 		if stash.obj != nil {
 		if stash.obj != nil {
-			if stash.obj.hasPropertyStr(name) {
+			if stash.obj.self.hasPropertyStr(name) {
 				ref = &objRef{
 				ref = &objRef{
-					base: stash.obj,
+					base: stash.obj.self,
 					name: name,
 					name: name,
 				}
 				}
 				goto end
 				goto end
@@ -1366,8 +1366,8 @@ func (d deleteVar) exec(vm *vm) {
 	ret := true
 	ret := true
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 		if stash.obj != nil {
 		if stash.obj != nil {
-			if stash.obj.hasPropertyStr(name) {
-				ret = stash.obj.deleteStr(name, false)
+			if stash.obj.self.hasPropertyStr(name) {
+				ret = stash.obj.self.deleteStr(name, false)
 				goto end
 				goto end
 			}
 			}
 		} else {
 		} else {
@@ -1416,9 +1416,9 @@ func (s resolveVar1Strict) exec(vm *vm) {
 	var ref ref
 	var ref ref
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 		if stash.obj != nil {
 		if stash.obj != nil {
-			if stash.obj.hasPropertyStr(name) {
+			if stash.obj.self.hasPropertyStr(name) {
 				ref = &objRef{
 				ref = &objRef{
-					base:   stash.obj,
+					base:   stash.obj.self,
 					name:   name,
 					name:   name,
 					strict: true,
 					strict: true,
 				}
 				}
@@ -1492,23 +1492,33 @@ func (g getLocal) exec(vm *vm) {
 }
 }
 
 
 type getVar struct {
 type getVar struct {
-	name string
-	idx  uint32
-	ref  bool
+	name        string
+	idx         uint32
+	ref, callee bool
 }
 }
 
 
 func (g getVar) exec(vm *vm) {
 func (g getVar) exec(vm *vm) {
 	level := int(g.idx >> 24)
 	level := int(g.idx >> 24)
-	idx := uint32(g.idx & 0x00FFFFFF)
+	idx := g.idx & 0x00FFFFFF
 	stash := vm.stash
 	stash := vm.stash
 	name := g.name
 	name := g.name
 	for i := 0; i < level; i++ {
 	for i := 0; i < level; i++ {
 		if v, found := stash.getByName(name, vm); found {
 		if v, found := stash.getByName(name, vm); found {
+			if g.callee {
+				if stash.obj != nil {
+					vm.push(stash.obj)
+				} else {
+					vm.push(_undefined)
+				}
+			}
 			vm.push(v)
 			vm.push(v)
 			goto end
 			goto end
 		}
 		}
 		stash = stash.outer
 		stash = stash.outer
 	}
 	}
+	if g.callee {
+		vm.push(_undefined)
+	}
 	if stash != nil {
 	if stash != nil {
 		vm.push(stash.getByIdx(idx))
 		vm.push(stash.getByIdx(idx))
 	} else {
 	} else {
@@ -1539,9 +1549,9 @@ func (r resolveVar) exec(vm *vm) {
 	var ref ref
 	var ref ref
 	for i := 0; i < level; i++ {
 	for i := 0; i < level; i++ {
 		if stash.obj != nil {
 		if stash.obj != nil {
-			if stash.obj.hasPropertyStr(r.name) {
+			if stash.obj.self.hasPropertyStr(r.name) {
 				ref = &objRef{
 				ref = &objRef{
-					base:   stash.obj,
+					base:   stash.obj.self,
 					name:   r.name,
 					name:   r.name,
 					strict: r.strict,
 					strict: r.strict,
 				}
 				}
@@ -1632,13 +1642,36 @@ func (n getVar1) exec(vm *vm) {
 	vm.pc++
 	vm.pc++
 }
 }
 
 
+type getVar1Ref string
+
+func (n getVar1Ref) exec(vm *vm) {
+	name := string(n)
+	var val Value
+	for stash := vm.stash; stash != nil; stash = stash.outer {
+		if v, exists := stash.getByName(name, vm); exists {
+			val = v
+			break
+		}
+	}
+	if val == nil {
+		val = vm.r.globalObject.self.getStr(name)
+		if val == nil {
+			val = valueUnresolved{r: vm.r, ref: name}
+		}
+	}
+	vm.push(val)
+	vm.pc++
+}
+
 type getVar1Callee string
 type getVar1Callee string
 
 
 func (n getVar1Callee) exec(vm *vm) {
 func (n getVar1Callee) exec(vm *vm) {
 	name := string(n)
 	name := string(n)
 	var val Value
 	var val Value
+	var callee *Object
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 	for stash := vm.stash; stash != nil; stash = stash.outer {
 		if v, exists := stash.getByName(name, vm); exists {
 		if v, exists := stash.getByName(name, vm); exists {
+			callee = stash.obj
 			val = v
 			val = v
 			break
 			break
 		}
 		}
@@ -1649,6 +1682,11 @@ func (n getVar1Callee) exec(vm *vm) {
 			val = valueUnresolved{r: vm.r, ref: name}
 			val = valueUnresolved{r: vm.r, ref: name}
 		}
 		}
 	}
 	}
+	if callee != nil {
+		vm.push(callee)
+	} else {
+		vm.push(_undefined)
+	}
 	vm.push(val)
 	vm.push(val)
 	vm.pc++
 	vm.pc++
 }
 }
@@ -2376,7 +2414,7 @@ var enterWith _enterWith
 
 
 func (_enterWith) exec(vm *vm) {
 func (_enterWith) exec(vm *vm) {
 	vm.newStash()
 	vm.newStash()
-	vm.stash.obj = vm.stack[vm.sp-1].ToObject(vm.r).self
+	vm.stash.obj = vm.stack[vm.sp-1].ToObject(vm.r)
 	vm.sp--
 	vm.sp--
 	vm.pc++
 	vm.pc++
 }
 }