Browse Source

Merge branch 'master' into es6

# Conflicts:
#	compiler_test.go
Dmitry Panov 5 years ago
parent
commit
00c4500f2b
4 changed files with 52 additions and 11 deletions
  1. 2 1
      compiler.go
  2. 17 10
      compiler_stmt.go
  3. 25 0
      compiler_test.go
  4. 8 0
      tc39_test.go

+ 2 - 1
compiler.go

@@ -12,6 +12,7 @@ import (
 
 const (
 	blockLoop = iota
+	blockLoopEnum
 	blockTry
 	blockBranch
 	blockSwitch
@@ -87,7 +88,7 @@ func (c *compiler) leaveBlock() {
 	for _, item := range c.block.breaks {
 		c.p.code[item] = jump(lbl - item)
 	}
-	if c.block.typ == blockLoop {
+	if t := c.block.typ; t == blockLoop || t == blockLoopEnum {
 		for _, item := range c.block.conts {
 			c.p.code[item] = jump(c.block.cont - item)
 		}

+ 17 - 10
compiler_stmt.go

@@ -310,7 +310,7 @@ func (c *compiler) compileForInStatement(v *ast.ForInStatement, needResult bool)
 
 func (c *compiler) compileLabeledForInStatement(v *ast.ForInStatement, needResult bool, label unistring.String) {
 	c.block = &block{
-		typ:        blockLoop,
+		typ:        blockLoopEnum,
 		outer:      c.block,
 		label:      label,
 		needResult: needResult,
@@ -463,7 +463,7 @@ func (c *compiler) findBranchBlock(st *ast.BranchStatement) *block {
 func (c *compiler) findContinueBlock(label *ast.Identifier) (block *block) {
 	if label != nil {
 		for b := c.block; b != nil; b = b.outer {
-			if b.typ == blockLoop && b.label == label.Name {
+			if (b.typ == blockLoop || b.typ == blockLoopEnum) && b.label == label.Name {
 				block = b
 				break
 			}
@@ -471,7 +471,7 @@ func (c *compiler) findContinueBlock(label *ast.Identifier) (block *block) {
 	} else {
 		// find the nearest loop
 		for b := c.block; b != nil; b = b.outer {
-			if b.typ == blockLoop {
+			if b.typ == blockLoop || b.typ == blockLoopEnum {
 				block = b
 				break
 			}
@@ -494,7 +494,7 @@ func (c *compiler) findBreakBlock(label *ast.Identifier) (block *block) {
 	L:
 		for b := c.block; b != nil; b = b.outer {
 			switch b.typ {
-			case blockLoop, blockSwitch:
+			case blockLoop, blockLoopEnum, blockSwitch:
 				block = b
 				break L
 			}
@@ -532,7 +532,7 @@ func (c *compiler) compileBreak(label *ast.Identifier, idx file.Idx) {
 				c.emit(halt)
 			case blockWith:
 				c.emit(leaveWith)
-			case blockLoop, blockSwitch:
+			case blockLoop, blockLoopEnum, blockSwitch:
 				block = b
 				break L
 			}
@@ -556,7 +556,7 @@ func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
 		for b := c.block; b != nil; b = b.outer {
 			if b.typ == blockTry {
 				c.emit(halt)
-			} else if b.typ == blockLoop && b.label == label.Name {
+			} else if (b.typ == blockLoop || b.typ == blockLoopEnum) && b.label == label.Name {
 				block = b
 				break
 			}
@@ -570,7 +570,7 @@ func (c *compiler) compileContinue(label *ast.Identifier, idx file.Idx) {
 		for b := c.block; b != nil; b = b.outer {
 			if b.typ == blockTry {
 				c.emit(halt)
-			} else if b.typ == blockLoop {
+			} else if b.typ == blockLoop || b.typ == blockLoopEnum {
 				block = b
 				break
 			}
@@ -637,10 +637,14 @@ func (c *compiler) compileIfStatement(v *ast.IfStatement, needResult bool) {
 		c.p.code[jmp1] = jump(len(c.p.code) - jmp1)
 		c.markBlockStart()
 	} else {
-		c.p.code[jmp] = jne(len(c.p.code) - jmp)
-		c.markBlockStart()
 		if needResult {
+			c.emit(jump(2))
+			c.p.code[jmp] = jne(len(c.p.code) - jmp)
 			c.emit(loadUndef)
+			c.markBlockStart()
+		} else {
+			c.p.code[jmp] = jne(len(c.p.code) - jmp)
+			c.markBlockStart()
 		}
 	}
 }
@@ -653,8 +657,11 @@ func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
 		c.emit(loadUndef)
 	}
 	for b := c.block; b != nil; b = b.outer {
-		if b.typ == blockTry {
+		switch b.typ {
+		case blockTry:
 			c.emit(halt)
+		case blockLoopEnum:
+			c.emit(enumPop)
 		}
 	}
 	c.emit(ret)

+ 25 - 0
compiler_test.go

@@ -67,6 +67,10 @@ func testScript1(script string, expectedResult Value, t *testing.T) {
 	if vm.sp != 0 {
 		t.Fatalf("sp: %d", vm.sp)
 	}
+
+	if l := len(vm.iterStack); l > 0 {
+		t.Fatalf("iter stack is not empty: %d", l)
+	}
 }
 
 func TestEmptyProgram(t *testing.T) {
@@ -2017,6 +2021,27 @@ func TestForOfReturn(t *testing.T) {
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }
 
+func TestReturnFromForInLoop(t *testing.T) {
+	const SCRIPT = `
+	(function f() {
+		for (var i in {a: 1}) {
+			return true;
+		}
+	})();
+	`
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestIfStackLeaks(t *testing.T) {
+	const SCRIPT = `
+	var t = 0;
+	if (t === 0) {
+		t;
+	}
+	`
+	testScript1(SCRIPT, _positiveZero, t)
+}
+
 // FIXME
 /*
 func TestDummyCompile(t *testing.T) {

+ 8 - 0
tc39_test.go

@@ -297,6 +297,14 @@ func (ctx *tc39TestCtx) runTC39Test(name, src string, meta *tc39Meta, t testing.
 			t.Fatalf("%s: Expected error: %v", name, err)
 		}
 	}
+
+	if vm.vm.sp != 0 {
+		t.Fatalf("sp: %d", vm.vm.sp)
+	}
+
+	if l := len(vm.vm.iterStack); l > 0 {
+		t.Fatalf("iter stack is not empty: %d", l)
+	}
 }
 
 func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {