ArgumentsObject * ArgumentsObject::create(JSContext *cx, StackFrame *fp) { JSFunction &callee = fp->callee(); RootedObject proto(cx, callee.global().getOrCreateObjectPrototype(cx)); if (!proto) return NULL; RootedTypeObject type(cx); type = proto->getNewType(cx); if (!type) return NULL; bool strict = callee.inStrictMode(); Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass; RootedShape emptyArgumentsShape(cx); emptyArgumentsShape = EmptyShape::getInitialShape(cx, clasp, proto, proto->getParent(), FINALIZE_KIND, BaseShape::INDEXED); if (!emptyArgumentsShape) return NULL; unsigned numActuals = fp->numActualArgs(); unsigned numFormals = fp->numFormalArgs(); unsigned numDeletedWords = NumWordsForBitArrayOfLength(numActuals); unsigned numArgs = Max(numActuals, numFormals); unsigned numBytes = offsetof(ArgumentsData, args) + numDeletedWords * sizeof(size_t) + numArgs * sizeof(Value); ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes); if (!data) return NULL; data->numArgs = numArgs; data->callee.init(ObjectValue(callee)); data->script = fp->script(); /* Copy [0, numArgs) into data->slots. */ HeapValue *dst = data->args, *dstEnd = data->args + numArgs; for (Value *src = fp->formals(), *end = src + numFormals; src != end; ++src, ++dst) dst->init(*src); if (numActuals > numFormals) { for (Value *src = fp->actuals() + numFormals; dst != dstEnd; ++src, ++dst) dst->init(*src); } else if (numActuals < numFormals) { for (; dst != dstEnd; ++dst) dst->init(UndefinedValue()); } data->deletedBits = reinterpret_cast<size_t *>(dstEnd); ClearAllBitArrayElements(data->deletedBits, numDeletedWords); JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL); if (!obj) return NULL; obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT)); obj->initFixedSlot(DATA_SLOT, PrivateValue(data)); /* * If it exists and the arguments object aliases formals, the call object * is the canonical location for formals. */ JSScript *script = fp->script(); if (fp->fun()->isHeavyweight() && script->argsObjAliasesFormals()) { obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(fp->callObj())); /* Flag each slot that canonically lives in the callObj. */ if (script->bindingsAccessedDynamically) { for (unsigned i = 0; i < numFormals; ++i) data->args[i] = MagicValue(JS_FORWARD_TO_CALL_OBJECT); } else { for (unsigned i = 0; i < script->numClosedArgs(); ++i) data->args[script->getClosedArg(i)] = MagicValue(JS_FORWARD_TO_CALL_OBJECT); } } ArgumentsObject &argsobj = obj->asArguments(); JS_ASSERT(argsobj.initialLength() == numActuals); JS_ASSERT(!argsobj.hasOverriddenLength()); return &argsobj; }
void js_PutCallObject(StackFrame *fp) { CallObject &callobj = fp->callObj().asCall(); JS_ASSERT(callobj.maybeStackFrame() == fp); JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame()); JS_ASSERT(fp->isEvalFrame() == callobj.isForEval()); /* Get the arguments object to snapshot fp's actual argument values. */ if (fp->hasArgsObj()) { if (callobj.arguments().isMagic(JS_UNASSIGNED_ARGUMENTS)) callobj.setArguments(ObjectValue(fp->argsObj())); js_PutArgsObject(fp); } JSScript *script = fp->script(); Bindings &bindings = script->bindings; if (callobj.isForEval()) { JS_ASSERT(script->strictModeCode); JS_ASSERT(bindings.countArgs() == 0); /* This could be optimized as below, but keep it simple for now. */ callobj.copyValues(0, NULL, bindings.countVars(), fp->slots()); } else { JSFunction *fun = fp->fun(); JS_ASSERT(script == callobj.getCalleeFunction()->script()); JS_ASSERT(script == fun->script()); unsigned n = bindings.countArgsAndVars(); if (n > 0) { uint32_t nvars = bindings.countVars(); uint32_t nargs = bindings.countArgs(); JS_ASSERT(fun->nargs == nargs); JS_ASSERT(nvars + nargs == n); JSScript *script = fun->script(); if (script->usesEval #ifdef JS_METHODJIT || script->debugMode #endif ) { callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots()); } else { /* * For each arg & var that is closed over, copy it from the stack * into the call object. We use initArg/VarUnchecked because, * when you call a getter on a call object, js_NativeGetInline * caches the return value in the slot, so we can't assert that * it's undefined. */ uint32_t nclosed = script->nClosedArgs; for (uint32_t i = 0; i < nclosed; i++) { uint32_t e = script->getClosedArg(i); #ifdef JS_GC_ZEAL callobj.setArg(e, fp->formalArg(e)); #else callobj.initArgUnchecked(e, fp->formalArg(e)); #endif } nclosed = script->nClosedVars; for (uint32_t i = 0; i < nclosed; i++) { uint32_t e = script->getClosedVar(i); #ifdef JS_GC_ZEAL callobj.setVar(e, fp->slots()[e]); #else callobj.initVarUnchecked(e, fp->slots()[e]); #endif } } /* * Update the args and vars for the active call if this is an outer * function in a script nesting. */ types::TypeScriptNesting *nesting = script->nesting(); if (nesting && script->isOuterFunction) { nesting->argArray = callobj.argArray(); nesting->varArray = callobj.varArray(); } } /* Clear private pointers to fp, which is about to go away. */ if (js_IsNamedLambda(fun)) { JSObject &env = callobj.enclosingScope(); JS_ASSERT(env.asDeclEnv().maybeStackFrame() == fp); env.setPrivate(NULL); } } callobj.setStackFrame(NULL); }