Eliminate Closure per ECMA ed. 3, fix brutal sharing of lambdas (20076, r=rogerl,shaver).

git-svn-id: svn://10.0.0.236/trunk@54474 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
brendan%mozilla.org 1999-11-25 03:25:30 +00:00
parent 30da73de36
commit ca2032e66d
7 changed files with 169 additions and 192 deletions

View File

@ -974,7 +974,7 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
/* Initialize the rest of the standard objects and functions. */
return (array_proto = js_InitArrayClass(cx, obj)) != NULL &&
js_InitArgsCallClosureClasses(cx, obj, obj_proto) &&
js_InitArgsAndCallClasses(cx, obj, obj_proto) &&
js_InitBooleanClass(cx, obj) &&
js_InitMathClass(cx, obj) &&
js_InitNumberClass(cx, obj) &&
@ -2194,7 +2194,6 @@ JS_PUBLIC_API(JSObject *)
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
{
JSFunction *fun;
JSObject *newfunobj;
CHECK_REQUEST(cx);
if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) {
@ -2202,16 +2201,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
return funobj;
}
fun = JS_GetPrivate(cx, funobj);
newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent);
if (!newfunobj)
return NULL;
if (!js_LinkFunctionObject(cx, fun, newfunobj)) {
cx->newborn[GCX_OBJECT] = NULL;
return NULL;
}
return newfunobj;
return js_CloneFunctionObject(cx, funobj, parent);
}
JS_PUBLIC_API(JSObject *)

View File

@ -577,100 +577,6 @@ JSClass js_CallClass = {
#endif /* JS_HAS_CALL_OBJECT */
#if JS_HAS_LEXICAL_CLOSURE
static JSBool
Closure(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSStackFrame *caller;
JSObject *varParent, *closureParent;
JSFunction *fun;
if (!cx->fp->constructing) {
obj = js_NewObject(cx, &js_ClosureClass, NULL, NULL);
if (!obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
}
if (!(caller = cx->fp->down) || !caller->scopeChain)
return JS_TRUE;
varParent = js_FindVariableScope(cx, &fun);
if (!varParent)
return JS_FALSE;
closureParent = caller->scopeChain;
if (argc != 0) {
fun = js_ValueToFunction(cx, &argv[0], JS_FALSE);
if (!fun)
return JS_FALSE;
OBJ_SET_PROTO(cx, obj, fun->object);
if (argc > 1) {
if (!js_ValueToObject(cx, argv[1], &closureParent))
return JS_FALSE;
}
}
OBJ_SET_PARENT(cx, obj, closureParent);
/* Make sure constructor is not inherited from fun->object. */
return js_DefineProperty(cx, obj,
(jsid)cx->runtime->atomState.constructorAtom,
argv[-2], NULL, NULL,
JSPROP_READONLY | JSPROP_PERMANENT,
NULL);
}
static JSBool
closure_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JSObject *proto;
if (type == JSTYPE_FUNCTION) {
proto = OBJ_GET_PROTO(cx, obj);
if (proto)
*vp = OBJECT_TO_JSVAL(proto);
return JS_TRUE;
}
return js_TryValueOf(cx, obj, type, vp);
}
static JSBool
closure_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSStackFrame *fp;
JSObject *closure, *callobj;
JSFunction *fun;
jsval junk;
/* Get a call object to link the closure's parent into the scope chain. */
fp = cx->fp;
closure = JSVAL_TO_OBJECT(argv[-2]);
JS_ASSERT(OBJ_GET_CLASS(cx, closure) == &js_ClosureClass);
callobj = js_GetCallObject(cx, fp, OBJ_GET_PARENT(cx, closure), NULL);
if (!callobj)
return JS_FALSE;
fp->scopeChain = callobj;
/* Make the function object, not its closure, available as argv[-2]. */
fun = fp->fun;
argv[-2] = OBJECT_TO_JSVAL(fun->object);
if (fun->call)
return fun->call(cx, obj, argc, argv, rval);
if (fun->script)
return js_Interpret(cx, &junk);
return JS_TRUE;
}
JSClass js_ClosureClass = {
"Closure",
0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, closure_convert, JS_FinalizeStub,
NULL, NULL, closure_call, closure_call,
NULL,NULL,{0,0}
};
#endif /* JS_HAS_LEXICAL_CLOSURE */
static JSPropertySpec function_props[] = {
/*
* We make fun.arguments readonly in fun_setProperty, unless it is being
@ -1696,8 +1602,7 @@ bad:
}
JSBool
js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj,
JSObject *objProto)
js_InitArgsAndCallClasses(JSContext *cx, JSObject *obj, JSObject *objProto)
{
#if JS_HAS_ARGS_OBJECT
if (!JS_InitClass(cx, obj, objProto, &js_ArgumentsClass, Arguments, 0,
@ -1707,19 +1612,12 @@ js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj,
#endif
#if JS_HAS_CALL_OBJECT
if (!JS_InitClass(cx, obj, NULL, &js_CallClass, Call, 0,
if (!JS_InitClass(cx, obj, objProto, &js_CallClass, Call, 0,
call_props, NULL, NULL, NULL)) {
return JS_FALSE;
}
#endif
#if JS_HAS_LEXICAL_CLOSURE
if (!JS_InitClass(cx, obj, NULL, &js_ClosureClass, Closure, 0,
NULL, NULL, NULL, NULL)) {
return JS_FALSE;
}
#endif
return JS_TRUE;
}
@ -1767,12 +1665,30 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative call, uintN nargs,
return fun;
}
JSObject *
js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
{
JSObject *newfunobj;
JSFunction *fun;
JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);
newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent);
if (!newfunobj)
return NULL;
fun = JS_GetPrivate(cx, funobj);
if (!js_LinkFunctionObject(cx, fun, newfunobj)) {
cx->newborn[GCX_OBJECT] = NULL;
return NULL;
}
return newfunobj;
}
JSBool
js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object)
js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj)
{
if (!fun->object)
fun->object = object;
if (!JS_SetPrivate(cx, object, fun))
fun->object = funobj;
if (!JS_SetPrivate(cx, funobj, fun))
return JS_FALSE;
JS_ATOMIC_ADDREF(&fun->nrefs, 1);
return JS_TRUE;

View File

@ -59,7 +59,7 @@ struct JSFunction {
extern JSClass js_ArgumentsClass;
extern JSClass js_CallClass;
extern JSClass js_ClosureClass;
/* JS_FRIEND_DATA so that JSVAL_IS_FUNCTION is callable from outside */
extern JS_FRIEND_DATA(JSClass) js_FunctionClass;
@ -77,13 +77,16 @@ extern JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj);
extern JSBool
js_InitArgsCallClosureClasses(JSContext *cx, JSObject *obj,
js_InitArgsAndCallClasses(JSContext *cx, JSObject *obj,
JSObject *objProto);
extern JSFunction *
js_NewFunction(JSContext *cx, JSObject *funobj, JSNative call, uintN nargs,
uintN flags, JSObject *parent, JSAtom *atom);
extern JSObject *
js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);
extern JSBool
js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object);

View File

@ -471,33 +471,25 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
* We attempt the conversion under all circumstances for 1.2, but
* only if there is a call op defined otherwise.
*/
if (cx->version == JSVERSION_1_2
|| ((ops == &js_ObjectOps) ? clasp->call : ops->call)) {
if (cx->version == JSVERSION_1_2 ||
((ops == &js_ObjectOps) ? clasp->call : ops->call)) {
ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
if (!ok)
goto out2;
}
if (!JSVAL_IS_FUNCTION(cx, v)) {
fun = NULL;
script = NULL;
minargs = nvars = 0;
} else {
if (JSVAL_IS_FUNCTION(cx, v)) {
funobj = JSVAL_TO_OBJECT(v);
parent = OBJ_GET_PARENT(cx, funobj);
fun = JS_GetPrivate(cx, funobj);
if (clasp != &js_ClosureClass) {
/* Make vp refer to funobj to keep it available as argv[-2]. */
*vp = v;
goto have_fun;
}
/* Closure invocation may need extra arg and local var slots. */
script = fun->script;
minargs = fun->nargs + fun->extra;
nvars = fun->nvars;
/* Make vp refer to funobj to keep it available as argv[-2]. */
*vp = v;
goto have_fun;
}
fun = NULL;
script = NULL;
minargs = nvars = 0;
/* Try a call or construct native object op, using fun as fallback. */
native = frame.constructing ? ops->construct : ops->call;
@ -930,17 +922,11 @@ ImportProperty(JSContext *cx, JSObject *obj, jsid id)
goto out;
if (JSVAL_IS_FUNCTION(cx, value)) {
funobj = JSVAL_TO_OBJECT(value);
closure = js_ConstructObject(cx, &js_ClosureClass, funobj, obj);
closure = js_CloneFunctionObject(cx, funobj, obj);
if (!closure) {
ok = JS_FALSE;
goto out;
}
/*
* The Closure() constructor resets the closure object's parent
* to be the current scope chain. Set it to the object that the
* imported function is being defined in.
*/
OBJ_SET_PARENT(cx, closure, obj);
value = OBJECT_TO_JSVAL(closure);
}
@ -1027,10 +1013,6 @@ js_Interpret(JSContext *cx, jsval *result)
uintN off, npairs;
JSBool match;
#endif
#if JS_HAS_LEXICAL_CLOSURE
JSFunction *fun2;
JSObject *closure;
#endif
#if JS_HAS_GETTER_SETTER
JSPropertyOp getter, setter;
#endif
@ -1974,9 +1956,8 @@ js_Interpret(JSContext *cx, jsval *result)
if (!JSVAL_IS_OBJECT(lval) ||
(obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
/* XXX clean up to avoid special cases above ObjectOps layer */
(clasp = OBJ_GET_CLASS(cx, obj2),
clasp == &js_FunctionClass || clasp == &js_ClosureClass) ||
!obj2->map->ops->construct)
OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
!obj2->map->ops->construct)
{
fun = js_ValueToFunction(cx, vp, JS_TRUE);
if (!fun) {
@ -2440,30 +2421,30 @@ js_Interpret(JSContext *cx, jsval *result)
* If the nearest variable scope is a function, not a call object,
* replace it in the scope chain with its call object.
*/
obj = js_FindVariableScope(cx, &fun);
if (!obj) {
parent = js_FindVariableScope(cx, &fun);
if (!parent) {
ok = JS_FALSE;
goto out;
}
/*
* Name the closure in the object at the head of the scope chain,
* referenced by obj.
* referenced by parent.
*/
if (fp->scopeChain != obj) {
obj = fp->scopeChain;
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_WithClass);
if (fp->scopeChain != parent) {
parent = fp->scopeChain;
JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_WithClass);
#if JS_BUG_WITH_CLOSURE
/*
* If in a with statement, set obj to the With object's
* If in a with statement, set parent to the With object's
* prototype, i.e., the object specified in the head of
* the with statement.
*/
while (OBJ_GET_CLASS(cx, obj) == &js_WithClass) {
proto = OBJ_GET_PROTO(cx, obj);
while (OBJ_GET_CLASS(cx, parent) == &js_WithClass) {
proto = OBJ_GET_PROTO(cx, parent);
if (!proto)
break;
obj = proto;
parent = proto;
}
#endif
}
@ -2473,46 +2454,45 @@ js_Interpret(JSContext *cx, jsval *result)
* From it, get the function to close.
*/
atom = GET_ATOM(cx, script, pc);
JS_ASSERT(ATOM_IS_OBJECT(atom));
obj2 = ATOM_TO_OBJECT(atom);
fun2 = JS_GetPrivate(cx, obj2);
JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom)));
obj = ATOM_TO_OBJECT(atom);
fun = JS_GetPrivate(cx, obj);
/*
* Let closure = new Closure(obj2).
* NB: js_ConstructObject does not use the "constructor" property
* of the new object it creates, because in this case and others
* such as js_WithClass, that property refers to the prototype's
* constructor function.
* Clone the function object with the current scope chain as the
* clone's parent. The original function object is the prototype
* of the clone.
*/
SAVE_SP(fp);
closure = js_ConstructObject(cx, &js_ClosureClass, obj2, obj);
if (!closure) {
obj = js_CloneFunctionObject(cx, obj, parent);
if (!obj) {
ok = JS_FALSE;
goto out;
}
/*
* Define a property in obj with id fun2->atom and value closure,
* but only if fun2 is not anonymous.
* Define a property in parent with id fun->atom and value obj,
* but only if fun is not anonymous.
*/
if (fun2->atom) {
if (fun->atom) {
SAVE_SP(fp);
ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)fun2->atom,
OBJECT_TO_JSVAL(closure),
(fun2->flags & JSFUN_GETTER)
? (JSPropertyOp) closure
attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
rval = attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj);
ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom, rval,
(attrs & JSFUN_GETTER)
? (JSPropertyOp) obj
: NULL,
(fun2->flags & JSFUN_SETTER)
? (JSPropertyOp) closure
(attrs & JSFUN_SETTER)
? (JSPropertyOp) obj
: NULL,
fun2->flags | JSPROP_ENUMERATE,
attrs | JSPROP_ENUMERATE,
NULL);
if (!ok) {
cx->newborn[GCX_OBJECT] = NULL;
goto out;
}
}
PUSH_OPND(OBJECT_TO_JSVAL(closure));
PUSH_OPND(OBJECT_TO_JSVAL(obj));
break;
#endif /* JS_HAS_LEXICAL_CLOSURE */
@ -2675,16 +2655,11 @@ js_Interpret(JSContext *cx, jsval *result)
* is not and will not be standardized.
*/
if (OBJ_GET_PARENT(cx, obj) != parent) {
obj = js_NewObject(cx, &js_FunctionClass, obj, parent);
obj = js_CloneFunctionObject(cx, obj, parent);
if (!obj) {
ok = JS_FALSE;
goto out;
}
ok = js_LinkFunctionObject(cx, fun, obj);
if (!ok) {
cx->newborn[GCX_OBJECT] = NULL;
goto out;
}
}
attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
@ -2715,6 +2690,91 @@ js_Interpret(JSContext *cx, jsval *result)
goto out;
break;
#if JS_HAS_LEXICAL_CLOSURE
case JSOP_ANONFUNOBJ:
/* Push the specified function object literal. */
parent = fp->scopeChain;
JS_ASSERT(parent == js_FindVariableScope(cx, &fun));
atom = GET_ATOM(cx, script, pc);
obj = ATOM_TO_OBJECT(atom);
/* If re-parenting, push a clone of the function object. */
if (OBJ_GET_PARENT(cx, obj) != parent) {
obj = js_CloneFunctionObject(cx, obj, parent);
if (!obj) {
ok = JS_FALSE;
goto out;
}
}
PUSH_OPND(OBJECT_TO_JSVAL(obj));
break;
case JSOP_NAMEDFUNOBJ:
/* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */
atom = GET_ATOM(cx, script, pc);
rval = ATOM_KEY(atom);
JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval));
obj = JSVAL_TO_OBJECT(rval);
fun = JS_GetPrivate(cx, obj);
/*
* 1. Create a new object as if by the expression new Object().
* 2. Add Result(1) to the front of the scope chain.
*
* Step 2 is achieved by making the new object's parent be the
* current scope chain, and then making the new object the parent
* of the Function object clone.
*/
parent = js_ConstructObject(cx, &js_ObjectClass, NULL,
fp->scopeChain);
if (!parent) {
ok = JS_FALSE;
goto out;
}
/*
* 3. Create a new Function object as specified in section 13.2
* with [parameters and body specified by the function expression
* that was parsed by the compiler into a Function object, and
* saved in the script's atom map].
*/
obj = js_CloneFunctionObject(cx, obj, parent);
if (!obj) {
ok = JS_FALSE;
goto out;
}
/*
* 4. Create a property in the object Result(1). The property's
* name is [fun->atom, the identifier parsed by the compiler],
* value is Result(3), and attributes are { DontDelete, ReadOnly }.
*/
attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom,
attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),
(attrs & JSFUN_GETTER)
? (JSPropertyOp) obj
: NULL,
(attrs & JSFUN_SETTER)
? (JSPropertyOp) obj
: NULL,
attrs |
JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_READONLY,
NULL);
if (!ok) {
cx->newborn[GCX_OBJECT] = NULL;
goto out;
}
/*
* 5. Remove Result(1) from the front of the scope chain [no-op].
* 6. Return Result(3).
*/
PUSH_OPND(OBJECT_TO_JSVAL(obj));
break;
#endif /* JS_HAS_LEXICAL_CLOSURE */
#if JS_HAS_GETTER_SETTER
case JSOP_GETTER:
case JSOP_SETTER:

View File

@ -1672,6 +1672,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case JSOP_NUMBER:
case JSOP_STRING:
case JSOP_OBJECT:
case JSOP_ANONFUNOBJ:
case JSOP_NAMEDFUNOBJ:
atom = GET_ATOM(cx, jp->script, pc);
key = ATOM_KEY(atom);
if (JSVAL_IS_INT(key)) {

View File

@ -248,6 +248,12 @@ OPDEF(JSOP_SETTER, 124,js_setter_str,js_setter_str,1, 0, 0, 0, JOF_BYTE)
/*
* Prolog bytecodes for defining function, var, and const names.
*/
OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME|JOF_SET)
OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_CONST|JOF_SET)
OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME|JOF_SET)
OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME)
/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */
OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 12, JOF_CONST)
/* ECMA ed. 3 named function expression. */
OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 12, JOF_CONST)

View File

@ -554,8 +554,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
*/
if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc))
pn->pn_op = JSOP_CLOSURE;
else if (lambda)
pn->pn_op = JSOP_OBJECT;
else if (lambda || !fun->atom)
pn->pn_op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
else
#endif
pn->pn_op = JSOP_NOP;