Преглед на файлове

Shorthand property fixes and support for NamedEvaluation so that function name is set when assigned to a variable

Dmitry Panov преди 4 години
родител
ревизия
13aee8e3cc
променени са 6 файла, в които са добавени 115 реда и са изтрити 40 реда
  1. 36 14
      compiler_expr.go
  2. 64 0
      compiler_test.go
  3. 0 2
      object_test.go
  4. 6 8
      parser/expression.go
  5. 5 5
      parser/lexer.go
  6. 4 11
      tc39_test.go

+ 36 - 14
compiler_expr.go

@@ -98,8 +98,10 @@ type compiledIdentifierExpr struct {
 
 type compiledFunctionLiteral struct {
 	baseCompiledExpr
-	expr   *ast.FunctionLiteral
-	isExpr bool
+	expr    *ast.FunctionLiteral
+	lhsName unistring.String
+	isExpr  bool
+	strict  bool
 }
 
 type compiledBracketExpr struct {
@@ -659,6 +661,13 @@ func (e *compiledAssignExpr) emitGetter(putOnStack bool) {
 	e.addSrcMap()
 	switch e.operator {
 	case token.ASSIGN:
+		if fn, ok := e.right.(*compiledFunctionLiteral); ok {
+			if fn.expr.Name == nil {
+				if id, ok := e.left.(*compiledIdentifierExpr); ok {
+					fn.lhsName = id.name
+				}
+			}
+		}
 		e.left.emitSetter(e.right)
 	case token.PLUS:
 		e.left.emitUnary(nil, func() {
@@ -754,8 +763,15 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	}
 	e.c.blockStart = 0
 
+	var name unistring.String
 	if e.expr.Name != nil {
-		e.c.p.funcName = e.expr.Name.Name
+		name = e.expr.Name.Name
+	} else {
+		name = e.lhsName
+	}
+
+	if name != "" {
+		e.c.p.funcName = name
 	}
 	block := e.c.block
 	e.c.block = nil
@@ -764,13 +780,10 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	}()
 
 	if !e.c.scope.strict {
-		e.c.scope.strict = e.c.isStrictStatement(e.expr.Body)
+		e.c.scope.strict = e.strict
 	}
 
 	if e.c.scope.strict {
-		if e.expr.Name != nil {
-			e.c.checkIdentifierLName(e.expr.Name.Name, int(e.expr.Name.Idx)-1)
-		}
 		for _, item := range e.expr.ParameterList.List {
 			e.c.checkIdentifierName(item.Name, int(item.Idx)-1)
 			e.c.checkIdentifierLName(item.Name, int(item.Idx)-1)
@@ -874,10 +887,6 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 	e.c.popScope()
 	e.c.p = savedPrg
 	e.c.blockStart = savedBlockStart
-	var name unistring.String
-	if e.expr.Name != nil {
-		name = e.expr.Name.Name
-	}
 	e.c.emit(&newFunc{prg: p, length: uint32(length), name: name, srcStart: uint32(e.expr.Idx0() - 1), srcEnd: uint32(e.expr.Idx1() - 1), strict: strict})
 	if !putOnStack {
 		e.c.emit(pop)
@@ -885,12 +894,14 @@ func (e *compiledFunctionLiteral) emitGetter(putOnStack bool) {
 }
 
 func (c *compiler) compileFunctionLiteral(v *ast.FunctionLiteral, isExpr bool) compiledExpr {
-	if v.Name != nil && c.scope.strict {
+	strict := c.scope.strict || c.isStrictStatement(v.Body)
+	if v.Name != nil && strict {
 		c.checkIdentifierLName(v.Name.Name, int(v.Name.Idx)-1)
 	}
 	r := &compiledFunctionLiteral{
 		expr:   v,
 		isExpr: isExpr,
+		strict: strict,
 	}
 	r.init(c, v.Idx0())
 	return r
@@ -1369,6 +1380,9 @@ func (c *compiler) compileVariableExpression(v *ast.VariableExpression) compiled
 		name:        v.Name,
 		initializer: c.compileExpression(v.Initializer),
 	}
+	if fn, ok := r.initializer.(*compiledFunctionLiteral); ok {
+		fn.lhsName = v.Name
+	}
 	r.init(c, v.Idx0())
 	return r
 }
@@ -1383,7 +1397,13 @@ func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 			e.c.throwSyntaxError(e.offset, "non-literal properties in object literal are not supported yet")
 		}
 		key := cl.val.string()
-		e.c.compileExpression(prop.Value).emitGetter(true)
+		valueExpr := e.c.compileExpression(prop.Value)
+		if fn, ok := valueExpr.(*compiledFunctionLiteral); ok {
+			if fn.expr.Name == nil {
+				fn.lhsName = key
+			}
+		}
+		valueExpr.emitGetter(true)
 		switch prop.Kind {
 		case "value":
 			if key == __proto__ {
@@ -1391,12 +1411,14 @@ func (e *compiledObjectLiteral) emitGetter(putOnStack bool) {
 			} else {
 				e.c.emit(setProp1(key))
 			}
+		case "method":
+			e.c.emit(setProp1(key))
 		case "get":
 			e.c.emit(setPropGetter(key))
 		case "set":
 			e.c.emit(setPropSetter(key))
 		default:
-			panic(fmt.Errorf("Unknown property kind: %s", prop.Kind))
+			panic(fmt.Errorf("unknown property kind: %s", prop.Kind))
 		}
 	}
 	if !putOnStack {

+ 64 - 0
compiler_test.go

@@ -2163,6 +2163,70 @@ func TestObjectLiteralWithNumericKeys(t *testing.T) {
 	testScript1(SCRIPT, valueTrue, t)
 }
 
+func TestEscapedObjectPropertyKeys(t *testing.T) {
+	const SCRIPT = `
+	var obj = {
+		w\u0069th: 42
+	};
+	var obj = {
+		with() {42}
+	};
+	`
+
+	_, err := Compile("", SCRIPT, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestObjectLiteralFuncProps(t *testing.T) {
+	const SCRIPT = `
+	(function() {
+		'use strict';
+		var o = {
+			eval: function() {return 1;},
+			arguments() {return 2;},
+			test: function test1() {}
+		}
+		assert.sameValue(o.eval.name, "eval");
+		assert.sameValue(o.arguments.name, "arguments");
+		assert.sameValue(o.eval(), 1);
+		assert.sameValue(o.arguments(), 2);
+		assert.sameValue(o.test.name, "test1");
+	})();
+	`
+
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}
+
+func TestFuncName(t *testing.T) {
+	const SCRIPT = `
+	var method = 1;
+	var o = {
+		method: function() {
+			return method;
+		},
+		method1: function method() {
+			return method;
+		}
+	}
+	o.method() === 1 && o.method1() === o.method1;
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}
+
+func TestFuncNameAssign(t *testing.T) {
+	const SCRIPT = `
+	var f = function() {};
+	var f1;
+	f1 = function() {};
+	f.name === "f" && f1.name === "f1";
+	`
+
+	testScript1(SCRIPT, valueTrue, t)
+}
+
 func BenchmarkCompile(b *testing.B) {
 	f, err := os.Open("testdata/S15.10.2.12_A1_T1.js")
 

+ 0 - 2
object_test.go

@@ -121,13 +121,11 @@ func TestObjectShorthandProperties(t *testing.T) {
 	assert.sameValue(a.b, b, "#1");
 	assert.sameValue(a.get(), "c", "#2");
 
-	/* doesn't work
 	var obj = {
 		w\u0069th() { return 42; }
     };
 
 	assert.sameValue(obj['with'](), 42, 'property exists');
-	*/
 	`
 	testScript1(TESTLIB+SCRIPT, _undefined, t)
 }

+ 6 - 8
parser/expression.go

@@ -189,7 +189,7 @@ func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expressio
 	return list
 }
 
-func (self *_parser) parseObjectPropertyKey() (string, ast.Expression, token.Token) {
+func (self *_parser) parseObjectPropertyKey() (unistring.String, ast.Expression, token.Token) {
 	idx, tkn, literal, parsedLiteral := self.idx, self.token, self.literal, self.parsedLiteral
 	var value ast.Expression
 	self.next()
@@ -225,23 +225,21 @@ func (self *_parser) parseObjectPropertyKey() (string, ast.Expression, token.Tok
 				Literal: literal,
 				Value:   unistring.String(literal),
 			}
+			tkn = token.STRING
 		}
 	}
-	return literal, value, tkn
+	return parsedLiteral, value, tkn
 }
 
 func (self *_parser) parseObjectProperty() ast.Property {
 	literal, value, tkn := self.parseObjectPropertyKey()
-	if tkn == token.IDENTIFIER {
+	if tkn == token.IDENTIFIER || tkn == token.STRING {
 		switch {
 		case self.token == token.LEFT_PARENTHESIS:
 			idx := self.idx
 			parameterList := self.parseFunctionParameterList()
 
 			node := &ast.FunctionLiteral{
-				Name: &ast.Identifier{
-					Name: unistring.String(literal), Idx: idx,
-				},
 				Function:      idx,
 				ParameterList: parameterList,
 			}
@@ -249,7 +247,7 @@ func (self *_parser) parseObjectProperty() ast.Property {
 
 			return ast.Property{
 				Key:   value,
-				Kind:  "value",
+				Kind:  "method",
 				Value: node,
 			}
 		case self.token == token.COMMA || self.token == token.RIGHT_BRACE: // shorthand property
@@ -257,7 +255,7 @@ func (self *_parser) parseObjectProperty() ast.Property {
 				Key:  value,
 				Kind: "value",
 				Value: &ast.Identifier{
-					Name: unistring.String(literal),
+					Name: literal,
 					Idx:  self.idx,
 				},
 			}

+ 5 - 5
parser/lexer.go

@@ -211,7 +211,7 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 				case 0: // Not a keyword
 					if parsedLiteral == "true" || parsedLiteral == "false" {
 						if hasEscape {
-							tkn = token.ILLEGAL
+							tkn = token.STRING
 							return
 						}
 						self.insertSemicolon = true
@@ -219,7 +219,7 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 						return
 					} else if parsedLiteral == "null" {
 						if hasEscape {
-							tkn = token.ILLEGAL
+							tkn = token.STRING
 							return
 						}
 						self.insertSemicolon = true
@@ -229,7 +229,7 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 
 				case token.KEYWORD:
 					if hasEscape {
-						tkn = token.ILLEGAL
+						tkn = token.STRING
 						return
 					}
 					tkn = token.KEYWORD
@@ -247,7 +247,7 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 					token.CONTINUE,
 					token.DEBUGGER:
 					if hasEscape {
-						tkn = token.ILLEGAL
+						tkn = token.STRING
 						return
 					}
 					self.insertSemicolon = true
@@ -255,7 +255,7 @@ func (self *_parser) scan() (tkn token.Token, literal string, parsedLiteral unis
 
 				default:
 					if hasEscape {
-						tkn = token.ILLEGAL
+						tkn = token.STRING
 					}
 					return
 

+ 4 - 11
tc39_test.go

@@ -110,6 +110,10 @@ var (
 		"test/language/statements/class/subclass/builtin-objects/RegExp/regular-subclassing.js":                      true,
 		"test/language/statements/class/subclass/builtin-objects/RegExp/lastIndex.js":                                true,
 		"TestTC39/tc39/test/language/statements/class/definition/fn-name-method.js":                                  true,
+		"test/language/expressions/object/method-definition/name-invoke-ctor.js":                                     true,
+		"test/language/expressions/object/method.js":                                                                 true,
+		"test/language/expressions/object/setter-super-prop.js":                                                      true,
+		"test/language/expressions/object/getter-super-prop.js":                                                      true,
 
 		// object literals
 		"test/built-ins/Array/from/source-object-iterator-1.js":                                                true,
@@ -266,19 +270,8 @@ var (
 		"test/language/expressions/object/accessor-name-computed-yield-id.js":             true,
 		"test/language/expressions/object/accessor-name-computed-in.js":                   true,
 
-		// new should not be usable with object's methods
-		"test/language/expressions/object/method-definition/name-invoke-ctor.js": true,
-
 		// get [Symbol.*]
 		"test/language/expressions/object/prop-def-id-eval-error.js": true,
-
-		// super
-		"test/language/expressions/object/method.js":            true,
-		"test/language/expressions/object/setter-super-prop.js": true,
-		"test/language/expressions/object/getter-super-prop.js": true,
-
-		// eval/arguments can be property names in non strict mode
-		"test/language/expressions/object/properties-names-eval-arguments.js": true,
 	}
 
 	featuresBlackList = []string{