closes #610: M1317422 + tests
This commit is contained in:
parent
256479e9fc
commit
f187b2dfeb
|
@ -719,7 +719,7 @@ struct JSClass {
|
||||||
// application.
|
// application.
|
||||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 36)
|
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 37)
|
||||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||||
#define JSCLASS_GLOBAL_FLAGS \
|
#define JSCLASS_GLOBAL_FLAGS \
|
||||||
|
|
|
@ -1079,6 +1079,11 @@ JS_ResolveStandardClass(JSContext* cx, HandleObject obj, HandleId id, bool* reso
|
||||||
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING);
|
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve a "globalThis" self-referential property if necessary.
|
||||||
|
if (idAtom == cx->names().globalThis) {
|
||||||
|
return GlobalObject::maybeResolveGlobalThis(cx, global, resolved);
|
||||||
|
}
|
||||||
|
|
||||||
/* Try for class constructors/prototypes named by well-known atoms. */
|
/* Try for class constructors/prototypes named by well-known atoms. */
|
||||||
stdnm = LookupStdName(cx->names(), idAtom, standard_class_names);
|
stdnm = LookupStdName(cx->names(), idAtom, standard_class_names);
|
||||||
|
|
||||||
|
@ -1128,6 +1133,7 @@ JS_MayResolveStandardClass(const JSAtomState& names, jsid id, JSObject* maybeObj
|
||||||
JSAtom* atom = JSID_TO_ATOM(id);
|
JSAtom* atom = JSID_TO_ATOM(id);
|
||||||
|
|
||||||
return atom == names.undefined ||
|
return atom == names.undefined ||
|
||||||
|
atom == names.globalThis ||
|
||||||
LookupStdName(names, atom, standard_class_names) ||
|
LookupStdName(names, atom, standard_class_names) ||
|
||||||
LookupStdName(names, atom, builtin_property_names);
|
LookupStdName(names, atom, builtin_property_names);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright (C) 2016 Jordan Harband. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-other-properties-of-the-global-object-globalThis
|
||||||
|
description: "'globalThis' should be the global object"
|
||||||
|
author: Jordan Harband
|
||||||
|
features: [globalThis]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
assert.sameValue(this, globalThis);
|
||||||
|
assert.sameValue(globalThis.globalThis, globalThis);
|
||||||
|
|
||||||
|
assert.sameValue(Array, globalThis.Array);
|
||||||
|
assert.sameValue(Boolean, globalThis.Boolean);
|
||||||
|
assert.sameValue(Date, globalThis.Date);
|
||||||
|
assert.sameValue(Error, globalThis.Error);
|
||||||
|
assert.sameValue(Function, globalThis.Function);
|
||||||
|
assert.sameValue(JSON, globalThis.JSON);
|
||||||
|
assert.sameValue(Math, globalThis.Math);
|
||||||
|
assert.sameValue(Number, globalThis.Number);
|
||||||
|
assert.sameValue(RegExp, globalThis.RegExp);
|
||||||
|
assert.sameValue(String, globalThis.String);
|
||||||
|
|
||||||
|
var globalVariable = {};
|
||||||
|
assert.sameValue(globalVariable, globalThis.globalVariable);
|
||||||
|
|
||||||
|
reportCompare(0, 0);
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (C) 2016 Jordan Harband. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-other-properties-of-the-global-object-global
|
||||||
|
description: "'globalThis' should be writable, non-enumerable, and configurable"
|
||||||
|
author: Jordan Harband
|
||||||
|
includes: [propertyHelper.js]
|
||||||
|
features: [globalThis]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
verifyNotEnumerable(this, 'globalThis');
|
||||||
|
verifyWritable(this, 'globalThis');
|
||||||
|
verifyConfigurable(this, 'globalThis');
|
||||||
|
|
||||||
|
reportCompare(0, 0);
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -938,3 +938,148 @@ var fnGlobalObject = (function()
|
||||||
var global = Function("return this")();
|
var global = Function("return this")();
|
||||||
return function fnGlobalObject() { return global; };
|
return function fnGlobalObject() { return global; };
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/* hack */
|
||||||
|
function _assert(c, s)
|
||||||
|
{
|
||||||
|
if (!c) $ERROR(s);
|
||||||
|
}
|
||||||
|
var assert = {
|
||||||
|
sameValue : function(x, y) {
|
||||||
|
assertEq(x,y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function isConfigurable(obj, name) {
|
||||||
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
try {
|
||||||
|
delete obj[name];
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof TypeError)) {
|
||||||
|
$ERROR("Expected TypeError, got " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !hasOwnProperty.call(obj, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEnumerable(obj, name) {
|
||||||
|
var stringCheck = false;
|
||||||
|
|
||||||
|
if (typeof name === "string") {
|
||||||
|
for (var x in obj) {
|
||||||
|
if (x === name) {
|
||||||
|
stringCheck = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// skip it if name is not string, works for Symbol names.
|
||||||
|
stringCheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringCheck &&
|
||||||
|
Object.prototype.hasOwnProperty.call(obj, name) &&
|
||||||
|
Object.prototype.propertyIsEnumerable.call(obj, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSameValue(a, b) {
|
||||||
|
if (a === 0 && b === 0) return 1 / a === 1 / b;
|
||||||
|
if (a !== a && b !== b) return true;
|
||||||
|
|
||||||
|
return a === b;
|
||||||
|
}
|
||||||
|
|
||||||
|
var __isArray = Array.isArray;
|
||||||
|
function isWritable(obj, name, verifyProp, value) {
|
||||||
|
var unlikelyValue = __isArray(obj) && name === "length" ?
|
||||||
|
Math.pow(2, 32) - 1 :
|
||||||
|
"unlikelyValue";
|
||||||
|
var newValue = value || unlikelyValue;
|
||||||
|
var hadValue = Object.prototype.hasOwnProperty.call(obj, name);
|
||||||
|
var oldValue = obj[name];
|
||||||
|
var writeSucceeded;
|
||||||
|
|
||||||
|
try {
|
||||||
|
obj[name] = newValue;
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof TypeError)) {
|
||||||
|
$ERROR("Expected TypeError, got " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSucceeded = isSameValue(obj[verifyProp || name], newValue);
|
||||||
|
|
||||||
|
// Revert the change only if it was successful (in other cases, reverting
|
||||||
|
// is unnecessary and may trigger exceptions for certain property
|
||||||
|
// configurations)
|
||||||
|
if (writeSucceeded) {
|
||||||
|
if (hadValue) {
|
||||||
|
obj[name] = oldValue;
|
||||||
|
} else {
|
||||||
|
delete obj[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeSucceeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyEqualTo(obj, name, value) {
|
||||||
|
if (!isSameValue(obj[name], value)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] to equal " + value +
|
||||||
|
", actually " + obj[name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyWritable(obj, name, verifyProp, value) {
|
||||||
|
if (!verifyProp) {
|
||||||
|
_assert(Object.getOwnPropertyDescriptor(obj, name).writable,
|
||||||
|
"Expected obj[" + String(name) + "] to have writable:true.");
|
||||||
|
}
|
||||||
|
if (!isWritable(obj, name, verifyProp, value)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] to be writable, but was not.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyNotWritable(obj, name, verifyProp, value) {
|
||||||
|
if (!verifyProp) {
|
||||||
|
_assert(!Object.getOwnPropertyDescriptor(obj, name).writable,
|
||||||
|
"Expected obj[" + String(name) + "] to have writable:false.");
|
||||||
|
}
|
||||||
|
if (isWritable(obj, name, verifyProp)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] NOT to be writable, but was.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyEnumerable(obj, name) {
|
||||||
|
_assert(Object.getOwnPropertyDescriptor(obj, name).enumerable,
|
||||||
|
"Expected obj[" + String(name) + "] to have enumerable:true.");
|
||||||
|
if (!isEnumerable(obj, name)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] to be enumerable, but was not.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyNotEnumerable(obj, name) {
|
||||||
|
_assert(!Object.getOwnPropertyDescriptor(obj, name).enumerable,
|
||||||
|
"Expected obj[" + String(name) + "] to have enumerable:false.");
|
||||||
|
if (isEnumerable(obj, name)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] NOT to be enumerable, but was.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyConfigurable(obj, name) {
|
||||||
|
_assert(Object.getOwnPropertyDescriptor(obj, name).configurable,
|
||||||
|
"Expected obj[" + String(name) + "] to have configurable:true.");
|
||||||
|
if (!isConfigurable(obj, name)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] to be configurable, but was not."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyNotConfigurable(obj, name) {
|
||||||
|
_assert(!Object.getOwnPropertyDescriptor(obj, name).configurable,
|
||||||
|
"Expected obj[" + String(name) + "] to have configurable:false.");
|
||||||
|
if (isConfigurable(obj, name)) {
|
||||||
|
$ERROR("Expected obj[" + String(name) + "] NOT to be configurable, but was."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -109,6 +109,7 @@
|
||||||
macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
|
macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
|
||||||
macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
|
macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
|
||||||
macro(global, global, "global") \
|
macro(global, global, "global") \
|
||||||
|
macro(globalThis, globalThis, "globalThis") \
|
||||||
macro(Handle, Handle, "Handle") \
|
macro(Handle, Handle, "Handle") \
|
||||||
macro(has, has, "has") \
|
macro(has, has, "has") \
|
||||||
macro(hasOwn, hasOwn, "hasOwn") \
|
macro(hasOwn, hasOwn, "hasOwn") \
|
||||||
|
|
|
@ -238,6 +238,34 @@ GlobalObject::initBuiltinConstructor(JSContext* cx, Handle<GlobalObject*> global
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Resolve a "globalThis" self-referential property if necessary,
|
||||||
|
// per a stage-3 proposal. https://github.com/tc39/ecma262/pull/702
|
||||||
|
//
|
||||||
|
// We could also do this in |FinishObjectClassInit| to trim the global
|
||||||
|
// resolve hook. Unfortunately, |ToWindowProxyIfWindow| doesn't work then:
|
||||||
|
// the browser's |nsGlobalWindow::SetNewDocument| invokes Object init
|
||||||
|
// *before* it sets the global's WindowProxy using |js::SetWindowProxy|.
|
||||||
|
//
|
||||||
|
// Refactoring global object creation code to support this approach is a
|
||||||
|
// challenge for another day.
|
||||||
|
/* static */ bool
|
||||||
|
GlobalObject::maybeResolveGlobalThis(JSContext* cx, Handle<GlobalObject*> global, bool* resolved)
|
||||||
|
{
|
||||||
|
if (global->getSlot(GLOBAL_THIS_RESOLVED).isUndefined()) {
|
||||||
|
RootedValue v(cx, ObjectValue(*ToWindowProxyIfWindow(global)));
|
||||||
|
if (!DefineProperty(cx, global, cx->names().globalThis, v, nullptr, nullptr,
|
||||||
|
JSPROP_RESOLVING)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*resolved = true;
|
||||||
|
global->setSlot(GLOBAL_THIS_RESOLVED, BooleanValue(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
GlobalObject*
|
GlobalObject*
|
||||||
GlobalObject::createInternal(JSContext* cx, const Class* clasp)
|
GlobalObject::createInternal(JSContext* cx, const Class* clasp)
|
||||||
{
|
{
|
||||||
|
@ -346,6 +374,12 @@ GlobalObject::initStandardClasses(JSContext* cx, Handle<GlobalObject*> global)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve a "globalThis" self-referential property if necessary.
|
||||||
|
bool resolved;
|
||||||
|
if (!GlobalObject::maybeResolveGlobalThis(cx, global, &resolved)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t k = 0; k < JSProto_LIMIT; ++k) {
|
for (size_t k = 0; k < JSProto_LIMIT; ++k) {
|
||||||
if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k)))
|
if (!ensureConstructor(cx, global, static_cast<JSProtoKey>(k)))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -119,6 +119,7 @@ class GlobalObject : public NativeObject
|
||||||
FOR_OF_PIC_CHAIN,
|
FOR_OF_PIC_CHAIN,
|
||||||
MODULE_RESOLVE_HOOK,
|
MODULE_RESOLVE_HOOK,
|
||||||
WINDOW_PROXY,
|
WINDOW_PROXY,
|
||||||
|
GLOBAL_THIS_RESOLVED,
|
||||||
|
|
||||||
/* Total reserved-slot count for global objects. */
|
/* Total reserved-slot count for global objects. */
|
||||||
RESERVED_SLOTS
|
RESERVED_SLOTS
|
||||||
|
@ -167,6 +168,7 @@ class GlobalObject : public NativeObject
|
||||||
static bool resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key);
|
static bool resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key);
|
||||||
static bool initBuiltinConstructor(JSContext* cx, Handle<GlobalObject*> global,
|
static bool initBuiltinConstructor(JSContext* cx, Handle<GlobalObject*> global,
|
||||||
JSProtoKey key, HandleObject ctor, HandleObject proto);
|
JSProtoKey key, HandleObject ctor, HandleObject proto);
|
||||||
|
static bool maybeResolveGlobalThis(JSContext* cx, Handle<GlobalObject*> global, bool* resolved);
|
||||||
|
|
||||||
void setConstructor(JSProtoKey key, const Value& v) {
|
void setConstructor(JSProtoKey key, const Value& v) {
|
||||||
MOZ_ASSERT(key <= JSProto_LIMIT);
|
MOZ_ASSERT(key <= JSProto_LIMIT);
|
||||||
|
|
Loading…
Reference in New Issue