Browse Source

Make sure variables dynamically bound in parameter scope conflict with parameter bindings. See #305.

Dmitry Panov 4 years ago
parent
commit
53123f0c9f
4 changed files with 68 additions and 23 deletions
  1. 27 22
      compiler_expr.go
  2. 1 1
      compiler_stmt.go
  3. 39 0
      compiler_test.go
  4. 1 0
      tc39_test.go

+ 27 - 22
compiler_expr.go

@@ -891,7 +891,6 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 				firstDupIdx = offset
 				firstDupIdx = offset
 			}
 			}
 			b.isArg = true
 			b.isArg = true
-			b.isVar = true
 		case ast.Pattern:
 		case ast.Pattern:
 			b := e.c.scope.addBinding(int(item.Idx0()) - 1)
 			b := e.c.scope.addBinding(int(item.Idx0()) - 1)
 			b.isArg = true
 			b.isArg = true
@@ -943,6 +942,10 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	preambleLen := 4 // enter, boxThis, createArgs, set
 	preambleLen := 4 // enter, boxThis, createArgs, set
 	e.c.p.code = make([]instruction, preambleLen, 8)
 	e.c.p.code = make([]instruction, preambleLen, 8)
 
 
+	emitArgsRestMark := -1
+	firstForwardRef := -1
+	enterFunc2Mark := -1
+
 	if hasPatterns || hasInits {
 	if hasPatterns || hasInits {
 		if e.isExpr && e.expr.Name != nil {
 		if e.isExpr && e.expr.Name != nil {
 			if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
 			if b, created := s.bindNameLexical(e.expr.Name.Name, false, 0); created {
@@ -954,13 +957,6 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 			e.c.emit(loadCallee)
 			e.c.emit(loadCallee)
 			calleeBinding.emitInit()
 			calleeBinding.emitInit()
 		}
 		}
-	}
-
-	emitArgsRestMark := -1
-	firstForwardRef := -1
-	enterFunc2Mark := -1
-
-	if hasPatterns || hasInits {
 		for i, item := range e.expr.ParameterList.List {
 		for i, item := range e.expr.ParameterList.List {
 			if pattern, ok := item.Target.(ast.Pattern); ok {
 			if pattern, ok := item.Target.(ast.Pattern); ok {
 				i := i
 				i := i
@@ -1035,6 +1031,11 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 			}
 			}
 		}
 		}
 	} else {
 	} else {
+		// To avoid triggering variable conflict when binding from non-strict direct eval().
+		// Parameters are supposed to be in a parent scope, hence no conflict.
+		for _, b := range s.bindings[:paramsCount] {
+			b.isVar = true
+		}
 		e.c.compileDeclList(e.expr.DeclarationList, true)
 		e.c.compileDeclList(e.expr.DeclarationList, true)
 		e.c.createFunctionBindings(funcs)
 		e.c.createFunctionBindings(funcs)
 		e.c.compileLexicalDeclarations(body, true)
 		e.c.compileLexicalDeclarations(body, true)
@@ -1074,22 +1075,26 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	}
 	}
 
 
 	if s.argsNeeded {
 	if s.argsNeeded {
-		pos := preambleLen - 2
-		delta += 2
-		if s.strict || hasPatterns || hasInits {
-			code[pos] = createArgsUnmapped(paramsCount)
+		b, created := s.bindNameLexical("arguments", false, 0)
+		if !created && !b.isVar {
+			s.argsNeeded = false
 		} else {
 		} else {
-			code[pos] = createArgsMapped(paramsCount)
-		}
-		pos++
-		b, _ := s.bindNameLexical("arguments", false, 0)
-		if s.strict {
-			b.isConst = true
-		} else {
-			b.isVar = true
+			if s.strict {
+				b.isConst = true
+			} else {
+				b.isVar = true
+			}
+			pos := preambleLen - 2
+			delta += 2
+			if s.strict || hasPatterns || hasInits {
+				code[pos] = createArgsUnmapped(paramsCount)
+			} else {
+				code[pos] = createArgsMapped(paramsCount)
+			}
+			pos++
+			b.markAccessPointAtScope(s, pos)
+			code[pos] = storeStashP(0)
 		}
 		}
-		b.markAccessPointAtScope(s, pos)
-		code[pos] = storeStashP(0)
 	}
 	}
 
 
 	stashSize, stackSize := s.finaliseVarAlloc(0)
 	stashSize, stackSize := s.finaliseVarAlloc(0)

+ 1 - 1
compiler_stmt.go

@@ -726,7 +726,7 @@ func (c *compiler) compileReturnStatement(v *ast.ReturnStatement) {
 
 
 func (c *compiler) checkVarConflict(name unistring.String, offset int) {
 func (c *compiler) checkVarConflict(name unistring.String, offset int) {
 	for sc := c.scope; sc != nil; sc = sc.outer {
 	for sc := c.scope; sc != nil; sc = sc.outer {
-		if b, exists := sc.boundNames[name]; exists && !b.isVar {
+		if b, exists := sc.boundNames[name]; exists && !b.isVar && !(b.isArg && sc != c.scope) {
 			c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
 			c.throwSyntaxError(offset, "Identifier '%s' has already been declared", name)
 		}
 		}
 		if sc.function {
 		if sc.function {

+ 39 - 0
compiler_test.go

@@ -1679,6 +1679,45 @@ func TestArgumentsInEval(t *testing.T) {
 	testScript1(SCRIPT, intToValue(1), t)
 	testScript1(SCRIPT, intToValue(1), t)
 }
 }
 
 
+func TestArgumentsRedeclareInEval(t *testing.T) {
+	const SCRIPT = `
+	assert.sameValue("arguments" in this, false, "No global 'arguments' binding");
+
+	function f(p = eval("var arguments = 'param'"), arguments) {}
+	assert.throws(SyntaxError, f);
+
+	assert.sameValue("arguments" in this, false, "No global 'arguments' binding");
+	`
+
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}
+
+func TestEvalParamWithDef(t *testing.T) {
+	const SCRIPT = `
+	function f(param = 0) {
+		eval("var param = 1");
+		return param;
+	}
+	f();
+	`
+
+	testScript1(SCRIPT, valueInt(1), t)
+}
+
+func TestArgumentsRedefinedAsLetDyn(t *testing.T) {
+	const SCRIPT = `
+	function f() {
+		let arguments;
+		eval(""); // force dynamic scope
+		return arguments;
+	}
+	
+	f(1,2);
+	`
+
+	testScript1(SCRIPT, _undefined, t)
+}
+
 func TestWith(t *testing.T) {
 func TestWith(t *testing.T) {
 	const SCRIPT = `
 	const SCRIPT = `
 	var b = 1;
 	var b = 1;

+ 1 - 0
tc39_test.go

@@ -408,6 +408,7 @@ var (
 		"sec-function-definitions-static-semantics-early-errors",
 		"sec-function-definitions-static-semantics-early-errors",
 		"sec-functiondeclarationinstantiation",
 		"sec-functiondeclarationinstantiation",
 		"sec-functiondeclarations-in-ifstatement-statement-clauses",
 		"sec-functiondeclarations-in-ifstatement-statement-clauses",
+		"sec-evaldeclarationinstantiation",
 	}
 	}
 )
 )