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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
From 4e4e34238e5bb5af83a645a5f4d2097e3b30e9dd Mon Sep 17 00:00:00 2001
From: Tom Schuster <evilpies@gmail.com>
Date: Sun, 25 Jan 2015 21:42:10 +0100
Subject: [PATCH] Bug 1111243 - Implement ES6 proxy behavior for IsArray.
r=efaust, a=abillings
---
browser/devtools/app-manager/app-projects.js | 2 ++
js/public/Class.h | 5 +++-
js/src/jsarray.cpp | 9 ++++--
js/src/jsobjinlines.h | 15 +++++++++-
js/src/json.cpp | 11 +++----
js/src/jsproxy.cpp | 45 ++++++++++++++++++++++++++++
6 files changed, 78 insertions(+), 9 deletions(-)
diff --git a/browser/devtools/app-manager/app-projects.js b/browser/devtools/app-manager/app-projects.js
index d09f72f..77ca67b 100644
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -61,6 +61,8 @@ const IDB = {
add: function(project) {
let deferred = promise.defer();
+ project = JSON.parse(JSON.stringify(project));
+
if (!project.location) {
// We need to make sure this object has a `.location` property.
deferred.reject("Missing location property on project object.");
diff --git a/js/public/Class.h b/js/public/Class.h
index ff864b1..46f7d39 100644
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -521,7 +521,10 @@ Valueify(const JSClass *c)
*/
enum ESClassValue {
ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean,
- ESClass_RegExp, ESClass_ArrayBuffer, ESClass_Date
+ ESClass_RegExp, ESClass_ArrayBuffer, ESClass_Date,
+ // Special snowflake for the ES6 IsArray method.
+ // Please don't use it without calling that function.
+ ESClass_IsArray
};
/*
diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp
index 24da176..46f1c20 100644
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2645,7 +2645,8 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp)
HandleValue v = HandleValue::fromMarkedLocation(&p[i]);
if (v.isObject()) {
RootedObject obj(cx, &v.toObject());
- if (ObjectClassIs(obj, ESClass_Array, cx)) {
+ // This should be IsConcatSpreadable
+ if (IsArray(obj, cx)) {
uint32_t alength;
if (!GetLengthProperty(cx, obj, &alength))
return false;
@@ -2870,7 +2871,11 @@ static bool
array_isArray(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
+ bool isArray = false;
+ if (args.get(0).isObject()) {
+ RootedObject obj(cx, &args[0].toObject());
+ isArray = IsArray(obj, cx);
+ }
args.rval().setBoolean(isArray);
return true;
}
diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h
index e848ba7..557dd26 100644
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1032,7 +1032,10 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
return Proxy::objectClassIs(obj, classValue, cx);
switch (classValue) {
- case ESClass_Array: return obj->is<ArrayObject>();
+ case ESClass_Array:
+ case ESClass_IsArray:
+ // There difference between those is only relevant for proxies.
+ return obj->is<ArrayObject>();
case ESClass_Number: return obj->is<NumberObject>();
case ESClass_String: return obj->is<StringObject>();
case ESClass_Boolean: return obj->is<BooleanObject>();
@@ -1053,6 +1056,16 @@ IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx)
return ObjectClassIs(obj, classValue, cx);
}
+// ES6 7.2.2
+inline bool
+IsArray(HandleObject obj, JSContext *cx)
+{
+ if (obj->is<ArrayObject>())
+ return true;
+
+ return ObjectClassIs(obj, ESClass_IsArray, cx);
+}
+
static MOZ_ALWAYS_INLINE bool
NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata)
{
diff --git a/js/src/json.cpp b/js/src/json.cpp
index 6e45bfd..81a99a6 100644
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -300,7 +300,7 @@ JO(JSContext *cx, HandleObject obj, StringifyContext *scx)
Maybe<AutoIdVector> ids;
const AutoIdVector *props;
if (scx->replacer && !scx->replacer->isCallable()) {
- JS_ASSERT(JS_IsArrayObject(cx, scx->replacer));
+ JS_ASSERT(IsArray(scx->replacer, cx));
props = &scx->propertyList;
} else {
JS_ASSERT_IF(scx->replacer, scx->propertyList.length() == 0);
@@ -488,7 +488,7 @@ Str(JSContext *cx, const Value &v, StringifyContext *scx)
scx->depth++;
bool ok;
- if (ObjectClassIs(obj, ESClass_Array, cx))
+ if (IsArray(obj, cx))
ok = JA(cx, obj, scx);
else
ok = JO(cx, obj, scx);
@@ -510,7 +510,7 @@ js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value sp
if (replacer) {
if (replacer->isCallable()) {
/* Step 4a(i): use replacer to transform values. */
- } else if (ObjectClassIs(replacer, ESClass_Array, cx)) {
+ } else if (IsArray(replacer, cx)) {
/*
* Step 4b: The spec algorithm is unhelpfully vague about the exact
* steps taken when the replacer is an array, regarding the exact
@@ -541,7 +541,8 @@ js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value sp
/* Step 4b(ii). */
uint32_t len;
- JS_ALWAYS_TRUE(GetLengthProperty(cx, replacer, &len));
+ if (!GetLengthProperty(cx, replacer, &len))
+ return false;
if (replacer->is<ArrayObject>() && !replacer->isIndexed())
len = Min(len, replacer->getDenseInitializedLength());
@@ -678,7 +679,7 @@ Walk(JSContext *cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
if (val.isObject()) {
RootedObject obj(cx, &val.toObject());
- if (ObjectClassIs(obj, ESClass_Array, cx)) {
+ if (IsArray(obj, cx)) {
/* Step 2a(ii). */
uint32_t length;
if (!GetLengthProperty(cx, obj, &length))
diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp
index 7644da1..7453103 100644
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -1108,6 +1108,14 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler {
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
/* Spidermonkey extensions. */
+ // A scripted proxy should not be treated as generic in most contexts.
+ virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
+ CallArgs args) MOZ_OVERRIDE;
+ virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
+ JSContext *cx) MOZ_OVERRIDE;
+ virtual bool regexp_toShared(JSContext *cx, HandleObject proxy,
+ RegExpGuard *g) MOZ_OVERRIDE;
+
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool isScripted() MOZ_OVERRIDE { return true; }
@@ -2350,6 +2358,43 @@ ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const C
return true;
}
+bool
+ScriptedDirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
+ CallArgs args)
+{
+ ReportIncompatible(cx, args);
+ return false;
+}
+
+bool
+ScriptedDirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
+ JSContext *cx)
+{
+ // Special case IsArray. In every other instance ES wants to have exactly
+ // one object type and not a proxy around it, so return false.
+ if (classValue != ESClass_IsArray)
+ return false;
+
+ // In ES6 IsArray is supposed to poke at the Proxy target, instead we do this here.
+ // The reason for this is that we have proxies for which looking at the target might
+ // be impossible. So instead we use our little objectClassIs function that just works
+ // already across different wrappers.
+ RootedObject target(cx, proxy->as<ProxyObject>().target());
+ if (!target)
+ return false;
+
+ return IsArray(target, cx);
+}
+
+bool
+ScriptedDirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy,
+ RegExpGuard *g)
+{
+ MOZ_CRASH("Should not end up in ScriptedDirectProxyHandler::regexp_toShared");
+ return false;
+}
+
+
ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;
#define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \
--
2.2.1
|