Bug 202019 - Built-in functions should not be constructors. r=jorendorff.

--HG--
extra : rebase_source : 2f845218384004c09a60c59ae03704729f1d91dc
This commit is contained in:
Tom Schuster 2011-04-20 13:22:57 -05:00
parent 4a9bde3182
commit 479dc912bc
17 changed files with 144 additions and 117 deletions

View File

@ -767,7 +767,6 @@ nsDOMWorkerFunctions::CTypesLazyGetter(JSContext* aCx,
JS_GetPropertyById(aCx, aObj, aId, aVp);
}
#endif
JSFunctionSpec gDOMWorkerFunctions[] = {
{ "dump", nsDOMWorkerFunctions::Dump, 1, 0 },
{ "setTimeout", nsDOMWorkerFunctions::SetTimeout, 1, 0 },
@ -775,19 +774,16 @@ JSFunctionSpec gDOMWorkerFunctions[] = {
{ "setInterval", nsDOMWorkerFunctions::SetInterval, 1, 0 },
{ "clearInterval", nsDOMWorkerFunctions::KillTimeout, 1, 0 },
{ "importScripts", nsDOMWorkerFunctions::LoadScripts, 1, 0 },
{ "XMLHttpRequest", nsDOMWorkerFunctions::NewXMLHttpRequest, 0, 0 },
{ "Worker", nsDOMWorkerFunctions::NewWorker, 1, 0 },
{ "XMLHttpRequest", nsDOMWorkerFunctions::NewXMLHttpRequest, 0, JSFUN_CONSTRUCTOR },
{ "Worker", nsDOMWorkerFunctions::NewWorker, 1, JSFUN_CONSTRUCTOR },
{ "atob", nsDOMWorkerFunctions::AtoB, 1, 0 },
{ "btoa", nsDOMWorkerFunctions::BtoA, 1, 0 },
{ nsnull, nsnull, 0, 0 }
};
JSFunctionSpec gDOMWorkerChromeFunctions[] = {
{ "ChromeWorker", nsDOMWorkerFunctions::NewChromeWorker, 1, 0 },
{ "ChromeWorker", nsDOMWorkerFunctions::NewChromeWorker, 1, JSFUN_CONSTRUCTOR },
{ nsnull, nsnull, 0, 0 }
};
enum DOMWorkerStructuredDataType
{
// We have a special tag for XPCWrappedNatives that are being passed between

View File

@ -1,7 +1,7 @@
for (a = 0; a < 4; a++) {
new Math.round(0).t
try {
for (a = 0; a < 4; a++) {
new Math.round(0).t
}
}
catch (e) {}
/* Don't assert. */

View File

@ -1,6 +0,0 @@
Object.defineProperty(Function.prototype, "prototype", {set:function(){}});
var x;
for (var i = 0; i < HOTLOOP + 2; i++)
x = new Function.prototype;
assertEq(toString.call(x), "[object Object]");
assertEq(Object.getPrototypeOf(x), Object.prototype);

View File

@ -1,6 +0,0 @@
Function.prototype.prototype = function () {};
var x;
for (var i = 0; i < HOTLOOP + 2; i++)
x = new Function.prototype;
assertEq(toString.call(x), "[object Object]");
assertEq(Object.getPrototypeOf(x), Function.prototype.prototype);

View File

@ -1,12 +0,0 @@
var a = [];
function next() {
var x = {};
a.push(x);
return x;
}
Object.defineProperty(Function.prototype, 'prototype', {get: next});
var b = [];
for (var i = 0; i < HOTLOOP + 2; i++)
b[i] = new Function.prototype;
for (var i = 0; i < HOTLOOP + 2; i++)
assertEq(Object.getPrototypeOf(b[i]), a[i]);

View File

@ -1,10 +0,0 @@
Object.defineProperty(Function.prototype, 'prototype',
{get: function () { if (i == HOTLOOP + 1) throw "X"; }});
var x;
try {
for (var i = 0; i < HOTLOOP + 2; i++)
x = new Function.prototype;
} catch (exc) {
assertEq(i, HOTLOOP + 1);
assertEq(exc, "X");
}

View File

@ -1,19 +1,18 @@
var funProto = Function.prototype;
assertEq(Object.getOwnPropertyDescriptor(funProto, "prototype"), undefined);
assertEq(parseInt.prototype, undefined);
var oldObj;
for (var i = 0, sz = RUNLOOP; i < sz; oldObj = obj, i++)
{
var obj = new funProto;
assertEq(obj === oldObj, false);
assertEq(Object.prototype.toString.call(obj), "[object Object]");
try {
var obj = new funProto;
}
catch (e) {}
assertEq(Object.getOwnPropertyDescriptor(funProto, "prototype"), undefined);
assertEq(Object.getOwnPropertyDescriptor(parseInt, "prototype"), undefined);
assertEq(parseInt.prototype, undefined);
}
checkStats({
recorderStarted: 1,
recorderAborted: 0,

View File

@ -1,10 +1,8 @@
var gen = (function () {yield})();
var t = gen.throw;
try {
new t;
} catch (e) {
actual = "" + e;
}
assertEq(actual, "TypeError: Generator.prototype.throw called on incompatible Object");
assertEq(actual, "TypeError: t is not a constructor");

View File

@ -2,15 +2,16 @@
// proxies can return primitives
assertEq(new (Proxy.createFunction({}, function(){}, function(){})), undefined);
x = Proxy.createFunction((function () {}), Uint16Array, wrap)
new(wrap(x))
// proxies can return the callee
var x = Proxy.createFunction({}, function (q) { return q; });
new x(x);
// not an error
new (Proxy.createFunction({}, "".indexOf));
assertEq(new x(x), x);
try {
var x = (Proxy.createFunction({}, "".indexOf));
new x;
}
catch (e) {
assertEq(e.message, 'new x is not a constructor');
}
throw "ExitCleanly"

View File

@ -152,12 +152,10 @@ struct JSFunction : public JSObject_Slots2
bool isConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
bool isFlatClosure() const { return FUN_KIND(this) == JSFUN_FLAT_CLOSURE; }
bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); }
/* Returns the strictness of this function, which must be interpreted. */
inline bool inStrictMode() const;
void setArgCount(uint16 nargs) {
JS_ASSERT(this->nargs == 0);
this->nargs = nargs;

View File

@ -1251,56 +1251,37 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
JS_ASSERT(!js_FunctionClass.construct);
CallArgs args = argsRef;
JSObject *callee;
if (args.calleev().isPrimitive() || !(callee = &args.callee())->getParent()) {
js_ReportIsNotFunction(cx, &args.calleev(), JSV2F_CONSTRUCT);
return false;
}
if (args.calleev().isObject()) {
JSObject *callee = &args.callee();
if (callee->isFunction()) {
JSFunction *fun = callee->getFunctionPrivate();
/* Handle the fast-constructors cases before falling into the general case . */
Class *clasp = callee->getClass();
JSFunction *fun = NULL;
if (clasp == &js_FunctionClass) {
fun = callee->getFunctionPrivate();
if (fun->isConstructor()) {
args.thisv().setMagicWithObjectOrNullPayload(NULL);
return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base());
}
} else if (clasp->construct) {
args.thisv().setMagicWithObjectOrNullPayload(NULL);
return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
}
/* Scripts create their own |this| in JSOP_BEGIN */
if (!fun || !fun->isInterpreted()) {
JSObject *obj = js_CreateThis(cx, callee);
if (!obj)
return false;
args.thisv().setObject(*obj);
}
if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
return false;
if (args.rval().isPrimitive()) {
if (clasp != &js_FunctionClass) {
/* native [[Construct]] returning primitive is error */
JSAutoByteString bytes;
if (js_ValueToPrintable(cx, args.rval(), &bytes)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_NEW_RESULT, bytes.ptr());
return false;
if (fun->isConstructor()) {
args.thisv().setMagicWithObjectOrNullPayload(NULL);
return CallJSNativeConstructor(cx, fun->u.n.native, args.argc(), args.base());
}
if (!fun->isInterpretedConstructor())
goto error;
if (!Invoke(cx, args, JSINVOKE_CONSTRUCT))
return false;
JS_ASSERT(args.rval().isObject());
JS_RUNTIME_METER(cx->runtime, constructs);
return true;
}
/* The interpreter fixes rval for us. */
JS_ASSERT(!fun->isInterpreted());
args.rval() = args.thisv();
Class *clasp = callee->getClass();
if (clasp->construct) {
args.thisv().setMagicWithObjectOrNullPayload(NULL);
return CallJSNativeConstructor(cx, clasp->construct, args.argc(), args.base());
}
}
JS_RUNTIME_METER(cx->runtime, constructs);
return true;
error:
js_ReportIsNotFunction(cx, &args.calleev(), JSV2F_CONSTRUCT);
return false;
}
bool
@ -4588,7 +4569,7 @@ BEGIN_CASE(JSOP_NEW)
*/
if (IsFunctionObject(vp[0], &callee)) {
newfun = callee->getFunctionPrivate();
if (newfun->isInterpreted()) {
if (newfun->isInterpretedConstructor()) {
if (newfun->u.i.script->isEmpty()) {
JSObject *obj2 = js_CreateThisForFunction(cx, callee);
if (!obj2)

View File

@ -409,12 +409,10 @@ void
stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
{
ucr->init();
JSContext *cx = f.cx;
Value *vp = f.regs.sp - (argc + 2);
/* Try to do a fast inline call before the general Invoke path. */
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpretedConstructor()) {
ucr->callee = &vp->toObject();
if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, &ucr->unjittable, argc))
THROW();

View File

@ -0,0 +1,52 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function checkMethod(method) {
try {
new method();
assertEq(0, 1, "not reached " + method);
} catch (e) {
assertEq(e.message, "method is not a constructor");
}
}
function checkMethods(proto) {
var names = Object.getOwnPropertyNames(proto);
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (name == "constructor")
continue;
var prop = proto[name];
if (typeof prop === "function")
checkMethod(prop);
}
}
checkMethod(Function.prototype);
checkMethods(JSON);
checkMethods(Math);
checkMethods(Proxy);
var builtin_ctors = [
Object, Function, Array, String, Boolean, Number, Date, RegExp, Error,
EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError,
];
for (var i = 0; i < builtin_ctors.length; i++) {
checkMethods(builtin_ctors[i]);
checkMethods(builtin_ctors[i].prototype);
}
var builtin_funcs = [
eval, isFinite, isNaN, parseFloat, parseInt,
decodeURI, decodeURIComponent, encodeURI, encodeURIComponent
];
for (var i = 0; i < builtin_funcs.length; i++) {
checkMethod(builtin_funcs[i]);
}
if (typeof reportCompare == 'function')
reportCompare(0, 0, "ok");

View File

@ -10,3 +10,4 @@ script function-call.js
script redefine-arguments-length.js
script builtin-no-prototype.js
script Function-arguments-gc.js
script builtin-no-construct.js

View File

@ -9,4 +9,5 @@ skip-if(!xulRuntime.shell) script function-definition-evaluate.js # needs evalua
script future-reserved-words.js
script builtin-methods-reject-null-undefined-this.js
script regress-bug632003.js
script new-with-non-constructor.js
script error-undefined-message.js

View File

@ -0,0 +1,39 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function checkConstruct(thing, buggy) {
try {
new thing();
assertEq(0, 1, "not reached " + thing);
} catch (e) {
if (buggy)
assertEq(e.message, "new thing is not a constructor");
else
assertEq(e.message, "thing is not a constructor");
}
}
var re = /aaa/
checkConstruct(re, false);
var boundFunctionPrototype = Function.prototype.bind();
checkConstruct(boundFunctionPrototype, true);
var boundBuiltin = Math.sin.bind();
checkConstruct(boundBuiltin, true);
/* We set the proxies construct trap to undefined,
* so the call trap is used as constructor.
*/
var proxiedFunctionPrototype = Proxy.create({}, Function.prototype, undefined);
checkConstruct(proxiedFunctionPrototype, false);
var proxiedBuiltin = Proxy.create({}, parseInt, undefined);
checkConstruct(proxiedBuiltin, false);
if (typeof reportCompare == 'function')
reportCompare(0, 0, "ok");

View File

@ -103,7 +103,6 @@ XrayWrapperConstructor(JSContext *cx, uintN argc, jsval *vp)
*vp = OBJECT_TO_JSVAL(obj);
return JS_WrapValue(cx, vp);
}
// static
PRBool
AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject)
@ -111,15 +110,13 @@ AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject)
JSObject *xpcnativewrapper =
JS_DefineFunction(ccx, aGlobalObject, "XPCNativeWrapper",
XrayWrapperConstructor, 1,
JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_STUB_GSOPS);
JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_STUB_GSOPS | JSFUN_CONSTRUCTOR);
if (!xpcnativewrapper) {
return PR_FALSE;
}
return JS_DefineFunction(ccx, xpcnativewrapper, "unwrap", UnwrapNW, 1,
JSPROP_READONLY | JSPROP_PERMANENT) != nsnull;
}
}
namespace XPCWrapper {