bool
DefineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
{
    JSObject *globalObj = globalScope.globalObj;

    /* Define and update global properties. */
    for (size_t i = 0; i < globalScope.defs.length(); i++) {
        GlobalScope::GlobalDef &def = globalScope.defs[i];

        /* Names that could be resolved ahead of time can be skipped. */
        if (!def.atom)
            continue;

        jsid id = ATOM_TO_JSID(def.atom);
        Value rval;

        if (def.funbox) {
            JSFunction *fun = def.funbox->function();

            /*
             * No need to check for redeclarations or anything, global
             * optimizations only take place if the property is not defined.
             */
            rval.setObject(*fun);
            types::AddTypePropertyId(cx, globalObj, id, rval);
        } else {
            rval.setUndefined();
        }

        /*
         * Don't update the type information when defining the property for the
         * global object, per the consistency rules for type properties. If the
         * property is only undefined before it is ever written, we can check
         * the global directly during compilation and avoid having to emit type
         * checks every time it is accessed in the script.
         */
        const Shape *shape =
            DefineNativeProperty(cx, globalObj, id, rval, JS_PropertyStub, JS_StrictPropertyStub,
                                 JSPROP_ENUMERATE | JSPROP_PERMANENT, 0, 0, DNP_SKIP_TYPE);
        if (!shape)
            return false;
        def.knownSlot = shape->slot();
    }

    Vector<JSScript *, 16> worklist(cx);
    if (!worklist.append(script))
        return false;

    /*
     * Recursively walk through all scripts we just compiled. For each script,
     * go through all global uses. Each global use indexes into globalScope->defs.
     * Use this information to repoint each use to the correct slot in the global
     * object.
     */
    while (worklist.length()) {
        JSScript *outer = worklist.back();
        worklist.popBack();

        if (JSScript::isValidOffset(outer->objectsOffset)) {
            JSObjectArray *arr = outer->objects();

            /*
             * If this is an eval script, don't treat the saved caller function
             * stored in the first object slot as an inner function.
             */
            size_t start = outer->savedCallerFun ? 1 : 0;

            for (size_t i = start; i < arr->length; i++) {
                JSObject *obj = arr->vector[i];
                if (!obj->isFunction())
                    continue;
                JSFunction *fun = obj->toFunction();
                JS_ASSERT(fun->isInterpreted());
                JSScript *inner = fun->script();
                if (outer->function() && outer->function()->isHeavyweight()) {
                    outer->isOuterFunction = true;
                    inner->isInnerFunction = true;
                }
                if (!JSScript::isValidOffset(inner->globalsOffset) &&
                    !JSScript::isValidOffset(inner->objectsOffset)) {
                    continue;
                }
                if (!worklist.append(inner))
                    return false;
            }
        }

        if (!JSScript::isValidOffset(outer->globalsOffset))
            continue;

        GlobalSlotArray *globalUses = outer->globals();
        uint32_t nGlobalUses = globalUses->length;
        for (uint32_t i = 0; i < nGlobalUses; i++) {
            uint32_t index = globalUses->vector[i].slot;
            JS_ASSERT(index < globalScope.defs.length());
            globalUses->vector[i].slot = globalScope.defs[index].knownSlot;
        }
    }

    return true;
}