Explorar el Código

Array.from(), Array.of()

Dmitry Panov hace 5 años
padre
commit
7a45ca5b93
Se han modificado 5 ficheros con 179 adiciones y 25 borrados
  1. 2 2
      array.go
  2. 63 6
      builtin_array.go
  3. 98 0
      builtin_arrray_test.go
  4. 0 4
      runtime.go
  5. 16 13
      tc39_test.go

+ 2 - 2
array.go

@@ -358,7 +358,7 @@ func (a *arrayObject) enumerate(all, recursive bool) iterNextFunc {
 
 func (a *arrayObject) hasOwnProperty(n Value) bool {
 	if idx := toIdx(n); idx >= 0 {
-		return idx < int64(len(a.values)) && a.values[idx] != nil && a.values[idx] != _undefined
+		return idx < int64(len(a.values)) && a.values[idx] != nil
 	} else {
 		return a.baseObject.hasOwnProperty(n)
 	}
@@ -366,7 +366,7 @@ func (a *arrayObject) hasOwnProperty(n Value) bool {
 
 func (a *arrayObject) hasOwnPropertyStr(name string) bool {
 	if idx := strToIdx(name); idx >= 0 {
-		return idx < int64(len(a.values)) && a.values[idx] != nil && a.values[idx] != _undefined
+		return idx < int64(len(a.values)) && a.values[idx] != nil
 	} else {
 		return a.baseObject.hasOwnPropertyStr(name)
 	}

+ 63 - 6
builtin_array.go

@@ -870,9 +870,11 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 	}
 
 	var ctor func(args []Value) *Object
-	if o, ok := call.This.(*Object); ok {
-		if c := getConstructor(o); c != nil {
-			ctor = c
+	if call.This != r.global.Array {
+		if o, ok := call.This.(*Object); ok {
+			if c := getConstructor(o); c != nil {
+				ctor = c
+			}
 		}
 	}
 	var arr *Object
@@ -883,12 +885,22 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 			arr = r.newArrayValues(nil)
 		}
 		iter := r.getIterator(items, usingIterator)
+		if mapFn == nil {
+			if a := r.checkStdArrayIter(arr); a != nil {
+				var values []Value
+				r.iterate(iter, func(val Value) {
+					values = append(values, val)
+				})
+				setArrayValues(a, values)
+				return arr
+			}
+		}
 		k := int64(0)
 		r.iterate(iter, func(val Value) {
 			if mapFn != nil {
 				val = mapFn(FunctionCall{This: t, Arguments: []Value{val, intToValue(k)}})
 			}
-			arr.self.put(intToValue(k), val, true)
+			defineDataPropertyOrThrow(arr, intToValue(k), val)
 			k++
 		})
 		arr.self.putStr("length", intToValue(k), true)
@@ -898,15 +910,27 @@ func (r *Runtime) array_from(call FunctionCall) Value {
 		if ctor != nil {
 			arr = ctor([]Value{intToValue(l)})
 		} else {
-			arr = r.newArrayLength(l)
+			arr = r.newArrayValues(nil)
+		}
+		if mapFn == nil {
+			if a := r.checkStdArrayIter(arr); a != nil {
+				values := make([]Value, l)
+				for k := int64(0); k < l; k++ {
+					values[k] = nilSafe(arrayLike.self.get(intToValue(k)))
+				}
+				setArrayValues(a, values)
+				return arr
+			}
 		}
 		for k := int64(0); k < l; k++ {
 			idx := intToValue(k)
 			item := arrayLike.self.get(idx)
 			if mapFn != nil {
 				item = mapFn(FunctionCall{This: t, Arguments: []Value{item, idx}})
+			} else {
+				item = nilSafe(item)
 			}
-			arr.self.put(idx, item, true)
+			defineDataPropertyOrThrow(arr, idx, item)
 		}
 		arr.self.putStr("length", intToValue(l), true)
 	}
@@ -923,6 +947,38 @@ func (r *Runtime) array_isArray(call FunctionCall) Value {
 	return valueFalse
 }
 
+func defineDataPropertyOrThrow(o *Object, p Value, v Value) {
+	o.self.defineOwnProperty(p, propertyDescr{
+		Writable:     FLAG_TRUE,
+		Enumerable:   FLAG_TRUE,
+		Configurable: FLAG_TRUE,
+		Value:        v,
+	}, true)
+}
+
+func (r *Runtime) array_of(call FunctionCall) Value {
+	var ctor func(args []Value) *Object
+	if call.This != r.global.Array {
+		if o, ok := call.This.(*Object); ok {
+			if c := getConstructor(o); c != nil {
+				ctor = c
+			}
+		}
+	}
+	if ctor == nil {
+		values := make([]Value, len(call.Arguments))
+		copy(values, call.Arguments)
+		return r.newArrayValues(values)
+	}
+	l := intToValue(int64(len(call.Arguments)))
+	arr := ctor([]Value{l})
+	for i, val := range call.Arguments {
+		defineDataPropertyOrThrow(arr, intToValue(int64(i)), val)
+	}
+	arr.self.putStr("length", l, true)
+	return arr
+}
+
 func (r *Runtime) arrayIterProto_next(call FunctionCall) Value {
 	thisObj := r.toObject(call.This)
 	if iter, ok := thisObj.self.(*arrayIterObject); ok {
@@ -976,6 +1032,7 @@ func (r *Runtime) createArray(val *Object) objectImpl {
 	o := r.newNativeFuncConstructObj(val, r.builtin_newArray, "Array", r.global.ArrayPrototype, 1)
 	o._putProp("from", r.newNativeFunc(r.array_from, nil, "from", nil, 1), true, false, true)
 	o._putProp("isArray", r.newNativeFunc(r.array_isArray, nil, "isArray", nil, 1), true, false, true)
+	o._putProp("of", r.newNativeFunc(r.array_of, nil, "of", nil, 0), true, false, true)
 	o.putSym(symSpecies, &valueProperty{
 		getterFunc:   r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0),
 		accessor:     true,

+ 98 - 0
builtin_arrray_test.go

@@ -98,3 +98,101 @@ func TestArraySetLengthWithPropItems(t *testing.T) {
 
 	testScript1(SCRIPT, valueTrue, t)
 }
+
+func TestArrayFrom(t *testing.T) {
+	const SCRIPT = `
+	function checkDest(dest, prefix) {
+		assert(dest !== source, prefix + ": dest !== source");
+		assert.sameValue(dest.length, 3, prefix + ": dest.length");
+		assert.sameValue(dest[0], 1, prefix + ": [0]");
+		assert.sameValue(dest[1], undefined, prefix + ": [1]");
+		assert(dest.hasOwnProperty("1"), prefix + ': hasOwnProperty("1")');
+		assert.sameValue(dest[2], 3, prefix + ": [2]");
+	}
+
+	var source = [];
+	source[0] = 1;
+	source[2] = 3;
+	checkDest(Array.from(source), "std source/std dest");
+
+	function Iter() {
+		this.idx = 0;
+	}
+	Iter.prototype.next = function() {
+		if (this.idx < source.length) {
+			return {value: source[this.idx++]};
+		} else {
+			return {done: true};
+		}
+	}
+
+	var src = {};
+	src[Symbol.iterator] = function() {
+		return new Iter();
+	}
+	checkDest(Array.from(src), "iter src/std dest");
+
+	src = {0: 1, 2: 3, length: 3};
+	checkDest(Array.from(src), "arrayLike src/std dest");
+
+	function A() {}
+	A.from = Array.from;
+
+	checkDest(A.from(source), "std src/cust dest");
+	checkDest(A.from(src), "arrayLike src/cust dest");
+
+	function T2() {
+	  Object.defineProperty(this, 0, {
+		configurable: false,
+		writable: true,
+		enumerable: true
+	  });
+	}
+
+	assert.throws(TypeError, function() {
+		Array.from.call(T2, source);
+	});
+
+	`
+
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}
+
+func TestArrayOf(t *testing.T) {
+	const SCRIPT = `
+	function T1() {
+	  Object.preventExtensions(this);
+	}
+	
+	assert.throws(TypeError, function() {
+	  Array.of.call(T1, 'Bob');
+	});
+
+	function T2() {
+	  Object.defineProperty(this, 0, {
+		configurable: false,
+		writable: true,
+		enumerable: true
+	  });
+	}
+	
+	assert.throws(TypeError, function() {
+	  Array.of.call(T2, 'Bob');
+	})
+
+	result = Array.of.call(undefined);
+	assert(
+	  result instanceof Array,
+	  'this is not a constructor'
+	);
+
+	result = Array.of.call(Math.cos);
+	assert(
+	  result instanceof Array,
+	  'this is a builtin function with no [[Construct]] slot'
+	);
+
+	`
+
+	testScript1(TESTLIB+SCRIPT, _undefined, t)
+}

+ 0 - 4
runtime.go

@@ -621,14 +621,10 @@ repeat:
 	case *nativeFuncObject:
 		if f.construct != nil {
 			return f.construct
-		} else {
-			panic(construct.runtime.NewTypeError("Not a constructor"))
 		}
 	case *boundFuncObject:
 		if f.construct != nil {
 			return f.construct
-		} else {
-			panic(construct.runtime.NewTypeError("Not a constructor"))
 		}
 	case *funcObject:
 		return f.construct

+ 16 - 13
tc39_test.go

@@ -52,6 +52,7 @@ var (
 		"test/built-ins/Set/proto-from-ctor-realm.js":             true,
 		"test/built-ins/Object/proto-from-ctor.js":                true,
 		"test/built-ins/Array/from/proto-from-ctor-realm.js":      true,
+		"test/built-ins/Array/of/proto-from-ctor-realm.js":        true,
 
 		// class
 		"test/language/statements/class/subclass/builtin-objects/Symbol/symbol-valid-as-extends-value.js": true,
@@ -67,19 +68,6 @@ var (
 		"test/language/statements/class/subclass/builtin-objects/Object/replacing-prototype.js":           true,
 		"test/language/statements/class/subclass/builtin-objects/Object/regular-subclassing.js":           true,
 
-		// Proxy
-		"test/built-ins/Object/prototype/toString/proxy-revoked.js":    true,
-		"test/built-ins/Object/prototype/toString/proxy-function.js":   true,
-		"test/built-ins/Object/prototype/toString/proxy-array.js":      true,
-		"test/built-ins/JSON/stringify/value-proxy.js":                 true,
-		"test/built-ins/Object/setPrototypeOf/set-error.js":            true,
-		"test/built-ins/Object/assign/source-own-prop-keys-error.js":   true,
-		"test/built-ins/Object/assign/source-own-prop-error.js":        true,
-		"test/built-ins/Object/assign/source-own-prop-desc-missing.js": true,
-
-		// Arrow functions
-		"test/built-ins/Set/prototype/forEach/this-arg-explicit-cannot-override-lexical-this-arrow.js": true,
-
 		// full unicode regexp flag
 		"test/built-ins/RegExp/prototype/Symbol.match/u-advance-after-empty.js":               true,
 		"test/built-ins/RegExp/prototype/Symbol.match/get-unicode-error.js":                   true,
@@ -94,6 +82,11 @@ var (
 		"test/built-ins/Array/from/items-is-arraybuffer.js": true,
 	}
 
+	featuresBlackList = []string{
+		"Proxy",
+		"arrow-function",
+	}
+
 	es6WhiteList = map[string]bool{}
 
 	es6IdWhiteList = []string{
@@ -106,6 +99,7 @@ var (
 		"21.1.3.17",
 		"21.2.5.6",
 		"22.1.2.1",
+		"22.1.2.3",
 		"22.1.2.5",
 		//"22.1.3.1",
 		"22.1.3.29",
@@ -140,6 +134,7 @@ type tc39Meta struct {
 	Negative TC39MetaNegative
 	Includes []string
 	Flags    []string
+	Features []string
 	Es5id    string
 	Es6id    string
 	Esid     string
@@ -274,6 +269,14 @@ func (ctx *tc39TestCtx) runTC39File(name string, t testing.TB) {
 		if skip {
 			t.Skip("Not ES5")
 		}
+
+		for _, feature := range meta.Features {
+			for _, bl := range featuresBlackList {
+				if feature == bl {
+					t.Skip("Blacklisted feature")
+				}
+			}
+		}
 	}
 
 	hasRaw := meta.hasFlag("raw")