1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
// Tests for the argumentList argument to Reflect.apply and Reflect.construct.
// Reflect.apply and Reflect.construct require an argumentList argument that must be an object.
assertThrowsInstanceOf(() => Reflect.apply(Math.min, undefined), // missing
TypeError);
assertThrowsInstanceOf(() => Reflect.construct(Object), // missing
TypeError);
for (var primitive of SOME_PRIMITIVE_VALUES) {
assertThrowsInstanceOf(() => Reflect.apply(Math.min, undefined, primitive),
TypeError);
assertThrowsInstanceOf(() => Reflect.construct(Object, primitive),
TypeError);
}
// Array used by several tests below.
var BOTH = [
Reflect.apply,
// Adapt Reflect.construct to accept the same arguments as Reflect.apply.
(target, thisArgument, argumentList) => Reflect.construct(target, argumentList)
];
// The argumentList is copied and becomes the list of arguments passed to the function.
function getRest(...x) { return x; }
var args = [1, 2, 3];
for (var method of BOTH) {
var result = method(getRest, undefined, args);
assertEq(result.join(), args.join());
assertEq(result !== args, true);
}
// argumentList.length can be less than func.length.
function testLess(a, b, c, d, e) {
assertEq(a, 1);
assertEq(b, true);
assertEq(c, "three");
assertEq(d, Symbol.for);
assertEq(e, undefined);
assertEq(arguments.length, 4);
assertEq(arguments !== args, true);
return "ok";
}
args = [1, true, "three", Symbol.for];
assertEq(Reflect.apply(testLess, undefined, args), "ok");
assertEq(Reflect.construct(testLess, args) instanceof testLess, true);
// argumentList.length can be more than func.length.
function testMoar(a) {
assertEq(a, args[0]);
return "good";
}
assertEq(Reflect.apply(testMoar, undefined, args), "good");
assertEq(Reflect.construct(testMoar, args) instanceof testMoar, true);
// argumentList can be any object with a .length property.
function getArgs(...args) {
return args;
}
for (var method of BOTH) {
assertDeepEq(method(getArgs, undefined, {length: 0}),
[]);
assertDeepEq(method(getArgs, undefined, {length: 1, "0": "zero"}),
["zero"]);
assertDeepEq(method(getArgs, undefined, {length: 2}),
[undefined, undefined]);
assertDeepEq(method(getArgs, undefined, function (a, b, c) {}),
[undefined, undefined, undefined]);
}
// The Iterable/Iterator interfaces are not used.
var funnyArgs = {
0: "zero",
1: "one",
length: 2,
[Symbol.iterator]() { throw "FAIL 1"; },
next() { throw "FAIL 2"; }
};
for (var method of BOTH) {
assertDeepEq(method(getArgs, undefined, funnyArgs),
["zero", "one"]);
}
// If argumentList has no .length property, no arguments are passed.
function count() { return {numArgsReceived: arguments.length}; }
for (var method of BOTH) {
assertEq(method(count, undefined, {"0": 0, "1": 1}).numArgsReceived,
0);
function* g() { yield 1; yield 2; }
assertEq(method(count, undefined, g()).numArgsReceived,
0);
}
// If argumentsList.length has a getter, it is called.
var log;
args = {
get length() { log += "L"; return 1; },
get "0"() { log += "0"; return "zero"; },
get "1"() { log += "1"; return "one"; }
};
for (var method of BOTH) {
log = "";
assertDeepEq(method(getArgs, undefined, args),
["zero"]);
assertEq(log, "L0");
}
// The argumentsList.length getter can throw; the exception is propagated.
var exc = {status: "bad"};
args = {
get length() { throw exc; }
};
for (var method of BOTH) {
assertThrowsValue(() => method(count, undefined, args), exc);
}
// If argumentsList.length is unreasonably huge, we get an error.
// (This is an implementation limit.)
for (var method of BOTH) {
for (var value of [1e12, 1e240, Infinity]) {
assertThrowsInstanceOf(() => method(count, undefined, {length: value}),
Error);
}
}
// argumentsList.length is converted to an integer.
for (var value of [1.7, "1", {valueOf() { return "1"; }}]) {
args = {
length: value,
"0": "ponies"
};
for (var method of BOTH) {
var result = method(getArgs, undefined, args);
assertEq(result.length, 1);
assertEq(result[0], "ponies");
}
}
// If argumentsList.length is negative or NaN, no arguments are passed.
for (var method of BOTH) {
for (var num of [-1, -0.1, -0, -1e99, -Infinity, NaN]) {
assertEq(method(count, undefined, {length: num}).numArgsReceived,
0);
}
}
// Many arguments can be passed.
var many = 65537;
var args = {length: many, 0: "zero", [many - 1]: "last"};
function testMany(...args) {
for (var i = 0; i < many; i++) {
assertEq(i in args, true);
assertEq(args[i], i === 0 ? "zero" : i === many - 1 ? "last" : undefined);
}
return this;
}
assertEq(Reflect.apply(testMany, "pass", args).toString(), "pass");
assertEq(Reflect.construct(testMany, args) instanceof testMany, true);
assertEq(Reflect.apply(new Proxy(testMany, {}), "pass", args).toString(), "pass");
assertEq(Reflect.construct(new Proxy(testMany, {}), args) instanceof testMany, true);
reportCompare(0, 0);
|