221 lines
8.7 KiB
Diff
221 lines
8.7 KiB
Diff
|
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
|
||
|
|