Bug 784293 - Support creating and lazily cloning arbitrary objects in self-hosted code. r=jwalden
authorTill Schneidereit <tschneidereit@gmail.com>
Wed, 10 Oct 2012 22:53:51 +0200
changeset 116169 7e44aec095e3cf35eae3543115ed608038a22b2b
parent 116168 18bc32f799d15288898e6f94abd7d0e5585a7373
child 116170 209a63d0a38fb449620d31e38553f6275db00380
push id19776
push usertschneidereit@gmail.com
push dateSun, 16 Dec 2012 00:23:56 +0000
treeherdermozilla-inbound@7e44aec095e3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs784293
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 784293 - Support creating and lazily cloning arbitrary objects in self-hosted code. r=jwalden
js/src/frontend/BytecodeEmitter.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/jsanalyze.cpp
js/src/jscntxt.h
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsopcode.tbl
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/src/vm/SelfHosting.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1156,27 +1156,31 @@ EmitEnterBlock(JSContext *cx, BytecodeEm
  *
  *   "use strict";
  *   function foo()
  *   {
  *     undeclared = 17; // throws ReferenceError
  *   }
  *   foo();
  *
- * In self-hosting mode, JSOP_NAME is unconditionally converted to
- * JSOP_INTRINSICNAME. This causes the lookup to be redirected to the special
- * intrinsics holder in the global object, into which any missing objects are
+ * In self-hosting mode, JSOP_*NAME is unconditionally converted to
+ * JSOP_*INTRINSIC. This causes lookups to be redirected to the special
+ * intrinsics holder in the global object, into which any missing values are
  * cloned lazily upon first access.
  */
 static bool
 TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
 {
     if (bce->selfHostingMode) {
-        JS_ASSERT(*op == JSOP_NAME);
-        *op = JSOP_INTRINSICNAME;
+        switch (*op) {
+          case JSOP_NAME:     *op = JSOP_GETINTRINSIC; break;
+          case JSOP_SETNAME:  *op = JSOP_SETINTRINSIC; break;
+          /* Other *NAME ops aren't (yet) supported in self-hosted code. */
+          default: JS_NOT_REACHED("intrinsic");
+        }
         return true;
     }
     if (bce->script->compileAndGo &&
         bce->hasGlobalScope &&
         !(bce->sc->isFunction && bce->sc->asFunbox()->mightAliasLocals()) &&
         !pn->isDeoptimized() &&
         !bce->sc->strict)
     {
@@ -1739,17 +1743,17 @@ EmitNameOp(JSContext *cx, BytecodeEmitte
         return false;
     op = pn->getOp();
 
     if (callContext) {
         switch (op) {
           case JSOP_NAME:
             op = JSOP_CALLNAME;
             break;
-          case JSOP_INTRINSICNAME:
+          case JSOP_GETINTRINSIC:
             op = JSOP_CALLINTRINSIC;
             break;
           case JSOP_GETGNAME:
             op = JSOP_CALLGNAME;
             break;
           case JSOP_GETARG:
             op = JSOP_CALLARG;
             break;
@@ -3378,19 +3382,25 @@ EmitVariables(JSContext *cx, BytecodeEmi
         JS_ASSERT(!pn2->pn_cookie.isFree() || !pn->isOp(JSOP_NOP));
 
         jsatomid atomIndex;
         if (!MaybeEmitVarDecl(cx, bce, pn->getOp(), pn2, &atomIndex))
             return false;
 
         if (pn3) {
             JS_ASSERT(emitOption != DefineVars);
-            if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
+            if (op == JSOP_SETNAME || op == JSOP_SETGNAME || op == JSOP_SETINTRINSIC) {
                 JS_ASSERT(emitOption != PushInitialValues);
-                JSOp bindOp = (op == JSOP_SETNAME) ? JSOP_BINDNAME : JSOP_BINDGNAME;
+                JSOp bindOp;
+                if (op == JSOP_SETNAME)
+                    bindOp = JSOP_BINDNAME;
+                else if (op == JSOP_SETGNAME)
+                    bindOp = JSOP_BINDGNAME;
+                else
+                    bindOp = JSOP_BINDINTRINSIC;
                 if (!EmitIndex32(cx, bindOp, atomIndex, bce))
                     return false;
             }
 
             bool oldEmittingForInit = bce->emittingForInit;
             bce->emittingForInit = false;
             if (!EmitTree(cx, bce, pn3))
                 return false;
@@ -3465,18 +3475,24 @@ EmitAssignment(JSContext *cx, BytecodeEm
     switch (lhs->getKind()) {
       case PNK_NAME:
         if (!BindNameToSlot(cx, bce, lhs))
             return false;
         if (lhs->pn_cookie.isFree()) {
             if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
                 return false;
             if (!lhs->isConst()) {
-                JSOp op = lhs->isOp(JSOP_SETGNAME) ? JSOP_BINDGNAME : JSOP_BINDNAME;
-                if (!EmitIndex32(cx, op, atomIndex, bce))
+                JSOp bindOp;
+                if (lhs->isOp(JSOP_SETNAME))
+                    bindOp = JSOP_BINDNAME;
+                else if (lhs->isOp(JSOP_SETGNAME))
+                    bindOp = JSOP_BINDGNAME;
+                else
+                    bindOp = JSOP_BINDINTRINSIC;
+                if (!EmitIndex32(cx, bindOp, atomIndex, bce))
                     return false;
                 offset++;
             }
         }
         break;
       case PNK_DOT:
         if (!EmitTree(cx, bce, lhs->expr()))
             return false;
@@ -3538,16 +3554,20 @@ EmitAssignment(JSContext *cx, BytecodeEm
                 if (Emit1(cx, bce, JSOP_DUP) < 0)
                     return false;
                 if (!EmitIndex32(cx, JSOP_GETXPROP, atomIndex, bce))
                     return false;
             } else if (lhs->isOp(JSOP_SETGNAME)) {
                 JS_ASSERT(lhs->pn_cookie.isFree());
                 if (!EmitAtomOp(cx, lhs, JSOP_GETGNAME, bce))
                     return false;
+            } else if (lhs->isOp(JSOP_SETINTRINSIC)) {
+                JS_ASSERT(lhs->pn_cookie.isFree());
+                if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce))
+                    return false;
             } else {
                 JSOp op = lhs->isOp(JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL;
                 if (!EmitVarOp(cx, lhs, op, bce))
                     return false;
             }
             break;
           case PNK_DOT: {
             if (Emit1(cx, bce, JSOP_DUP) < 0)
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -969,21 +969,21 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_NAME:
       case JSOP_CALLNAME:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
         return jsop_getname(name);
       }
 
-      case JSOP_INTRINSICNAME:
+      case JSOP_GETINTRINSIC:
       case JSOP_CALLINTRINSIC:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
-        return jsop_intrinsicname(name);
+        return jsop_intrinsic(name);
       }
 
       case JSOP_BINDNAME:
         return jsop_bindname(info().getName(pc));
 
       case JSOP_DUP:
         current->pushSlot(current->stackDepth() - 1);
         return true;
@@ -5123,17 +5123,17 @@ IonBuilder::jsop_getname(HandlePropertyN
     types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc);
     types::StackTypeSet *types = oracle->propertyRead(script(), pc);
 
     monitorResult(ins, barrier, types);
     return pushTypeBarrier(ins, types, barrier);
 }
 
 bool
-IonBuilder::jsop_intrinsicname(HandlePropertyName name)
+IonBuilder::jsop_intrinsic(HandlePropertyName name)
 {
     types::StackTypeSet *types = oracle->propertyRead(script(), pc);
     JSValueType type = types->getKnownTypeTag();
 
     // If we haven't executed this opcode yet, we need to get the intrinsic
     // value and monitor the result.
     if (type == JSVAL_TYPE_UNKNOWN) {
         MCallGetIntrinsicValue *ins = MCallGetIntrinsicValue::New(name);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -344,17 +344,17 @@ class IonBuilder : public MIRGenerator
     bool jsop_condswitch();
     bool jsop_andor(JSOp op);
     bool jsop_dup2();
     bool jsop_loophead(jsbytecode *pc);
     bool jsop_compare(JSOp op);
     bool jsop_getgname(HandlePropertyName name);
     bool jsop_setgname(HandlePropertyName name);
     bool jsop_getname(HandlePropertyName name);
-    bool jsop_intrinsicname(HandlePropertyName name);
+    bool jsop_intrinsic(HandlePropertyName name);
     bool jsop_bindname(PropertyName *name);
     bool jsop_getelem();
     bool jsop_getelem_dense();
     bool jsop_getelem_typed(int arrayType);
     bool jsop_getelem_string();
     bool jsop_setelem();
     bool jsop_setelem_dense();
     bool jsop_setelem_typed(int arrayType);
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -562,17 +562,19 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_INSTANCEOF:
           case JSOP_LINENO:
           case JSOP_ENUMELEM:
           case JSOP_CONDSWITCH:
           case JSOP_LABEL:
           case JSOP_RETRVAL:
           case JSOP_GETGNAME:
           case JSOP_CALLGNAME:
-          case JSOP_INTRINSICNAME:
+          case JSOP_GETINTRINSIC:
+          case JSOP_SETINTRINSIC:
+          case JSOP_BINDINTRINSIC:
           case JSOP_CALLINTRINSIC:
           case JSOP_SETGNAME:
           case JSOP_REGEXP:
           case JSOP_OBJECT:
           case JSOP_UINT24:
           case JSOP_GETXPROP:
           case JSOP_INT8:
           case JSOP_INT32:
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -558,18 +558,16 @@ struct JSRuntime : js::RuntimeFriendFiel
         return ionRuntime_ ? ionRuntime_ : createIonRuntime(cx);
     }
 
     bool initSelfHosting(JSContext *cx);
     void markSelfHostingGlobal(JSTracer *trc);
     bool isSelfHostingGlobal(js::HandleObject global) {
         return global == selfHostingGlobal_;
     }
-    bool getUnclonedSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
-                                    js::MutableHandleValue vp);
     bool cloneSelfHostedFunctionScript(JSContext *cx, js::Handle<js::PropertyName*> name,
                                        js::Handle<JSFunction*> targetFun);
     bool cloneSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
                               js::MutableHandleValue vp);
 
     /* Base address of the native stack for the current thread. */
     uintptr_t           nativeStackBase;
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1473,16 +1473,21 @@ js_CloneFunctionObject(JSContext *cx, Ha
         NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
     if (!cloneobj)
         return NULL;
     RootedFunction clone(cx, cloneobj->toFunction());
 
     clone->nargs = fun->nargs;
     clone->flags = fun->flags & ~JSFunction::EXTENDED;
     if (fun->isInterpreted()) {
+        if (fun->isInterpretedLazy()) {
+            AutoCompartment ac(cx, fun);
+            if (!fun->getOrCreateScript(cx))
+                return NULL;
+        }
         clone->initScript(fun->nonLazyScript());
         clone->initEnvironment(parent);
     } else {
         clone->initNative(fun->native(), fun->jitInfo());
     }
     clone->initAtom(fun->displayAtom());
 
     if (kind == JSFunction::ExtendedFinalizeKind) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3870,38 +3870,40 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         if (state.hasPropertyReadTypes)
             PropertyAccess<PROPERTY_READ_EXISTING>(cx, script, pc, global, seen, id);
         else
             PropertyAccess<PROPERTY_READ>(cx, script, pc, global, seen, id);
         break;
       }
 
       case JSOP_NAME:
-      case JSOP_INTRINSICNAME:
+      case JSOP_GETINTRINSIC:
       case JSOP_CALLNAME:
       case JSOP_CALLINTRINSIC: {
         StackTypeSet *seen = bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
+      case JSOP_BINDINTRINSIC:
         break;
 
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
         TypeObject *global = script_->global().getType(cx);
         PropertyAccess<PROPERTY_WRITE>(cx, script, pc, global, poppedTypes(pc, 0), id);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_SETNAME:
+      case JSOP_SETINTRINSIC:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GETXPROP: {
         StackTypeSet *seen = bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
@@ -5403,16 +5405,17 @@ types::TypeMonitorResult(JSContext *cx, 
  */
 static inline bool
 IgnorePushed(const jsbytecode *pc, unsigned index)
 {
     switch (JSOp(*pc)) {
       /* We keep track of the scopes pushed by BINDNAME separately. */
       case JSOP_BINDNAME:
       case JSOP_BINDGNAME:
+      case JSOP_BINDINTRINSIC:
       case JSOP_BINDXMLNAME:
         return true;
 
       /* Stack not consistent in TRY_BRANCH_AFTER_COND. */
       case JSOP_IN:
       case JSOP_EQ:
       case JSOP_NE:
       case JSOP_LT:
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1340,18 +1340,16 @@ js::Interpret(JSContext *cx, StackFrame 
         goto do_switch;
     }
 
 /* No-ops for ease of decompilation. */
 ADD_EMPTY_CASE(JSOP_NOP)
 ADD_EMPTY_CASE(JSOP_UNUSED1)
 ADD_EMPTY_CASE(JSOP_UNUSED2)
 ADD_EMPTY_CASE(JSOP_UNUSED3)
-ADD_EMPTY_CASE(JSOP_UNUSED10)
-ADD_EMPTY_CASE(JSOP_UNUSED11)
 ADD_EMPTY_CASE(JSOP_UNUSED12)
 ADD_EMPTY_CASE(JSOP_UNUSED13)
 ADD_EMPTY_CASE(JSOP_UNUSED17)
 ADD_EMPTY_CASE(JSOP_UNUSED18)
 ADD_EMPTY_CASE(JSOP_UNUSED19)
 ADD_EMPTY_CASE(JSOP_UNUSED20)
 ADD_EMPTY_CASE(JSOP_UNUSED21)
 ADD_EMPTY_CASE(JSOP_UNUSED22)
@@ -1790,16 +1788,20 @@ BEGIN_CASE(JSOP_ENUMCONSTELEM)
 }
 END_CASE(JSOP_ENUMCONSTELEM)
 #endif
 
 BEGIN_CASE(JSOP_BINDGNAME)
     PUSH_OBJECT(regs.fp()->global());
 END_CASE(JSOP_BINDGNAME)
 
+BEGIN_CASE(JSOP_BINDINTRINSIC)
+    PUSH_OBJECT(*cx->global()->intrinsicsHolder());
+END_CASE(JSOP_BINDGNAME)
+
 BEGIN_CASE(JSOP_BINDNAME)
 {
     RootedObject &scopeChain = rootObject0;
     scopeChain = regs.fp()->scopeChain();
 
     RootedPropertyName &name = rootName0;
     name = script->getName(regs.pc);
 
@@ -2230,16 +2232,28 @@ BEGIN_CASE(JSOP_CALLPROP)
 
     TypeScript::Monitor(cx, script, regs.pc, rval);
 
     regs.sp[-1] = rval;
     assertSameCompartmentDebugOnly(cx, regs.sp[-1]);
 }
 END_CASE(JSOP_GETPROP)
 
+BEGIN_CASE(JSOP_SETINTRINSIC)
+{
+    HandleValue value = HandleValue::fromMarkedLocation(&regs.sp[-1]);
+
+    if (!SetIntrinsicOperation(cx, script, regs.pc, value))
+        goto error;
+
+    regs.sp[-2] = regs.sp[-1];
+    regs.sp--;
+}
+END_CASE(JSOP_SETINTRINSIC)
+
 BEGIN_CASE(JSOP_SETGNAME)
 BEGIN_CASE(JSOP_SETNAME)
 {
     RootedObject &scope = rootObject0;
     scope = &regs.sp[-2].toObject();
 
     HandleValue value = HandleValue::fromMarkedLocation(&regs.sp[-1]);
 
@@ -2467,28 +2481,28 @@ BEGIN_CASE(JSOP_CALLNAME)
     if (!NameOperation(cx, regs.pc, &rval))
         goto error;
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, regs.pc, rval);
 }
 END_CASE(JSOP_NAME)
 
-BEGIN_CASE(JSOP_INTRINSICNAME)
+BEGIN_CASE(JSOP_GETINTRINSIC)
 BEGIN_CASE(JSOP_CALLINTRINSIC)
 {
     RootedValue &rval = rootValue0;
 
-    if (!IntrinsicNameOperation(cx, script, regs.pc, &rval))
+    if (!GetIntrinsicOperation(cx, script, regs.pc, &rval))
         goto error;
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, regs.pc, rval);
 }
-END_CASE(JSOP_INTRINSICNAME)
+END_CASE(JSOP_GETINTRINSIC)
 
 BEGIN_CASE(JSOP_UINT16)
     PUSH_INT32((int32_t) GET_UINT16(regs.pc));
 END_CASE(JSOP_UINT16)
 
 BEGIN_CASE(JSOP_UINT24)
     PUSH_INT32((int32_t) GET_UINT24(regs.pc));
 END_CASE(JSOP_UINT24)
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -398,24 +398,31 @@ FetchName(JSContext *cx, HandleObject ob
             normalized = &normalized->asWith().object();
         if (!NativeGet(cx, normalized, obj2, shape, 0, vp))
             return false;
     }
     return true;
 }
 
 inline bool
-IntrinsicNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, MutableHandleValue vp)
+GetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, MutableHandleValue vp)
 {
     JSOp op = JSOp(*pc);
     RootedPropertyName name(cx, GetNameFromBytecode(cx, script, pc, op));
     return cx->global()->getIntrinsicValue(cx, name, vp);
 }
 
 inline bool
+SetIntrinsicOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleValue val)
+{
+    RootedPropertyName name(cx, script->getName(pc));
+    return cx->global()->setIntrinsicValue(cx, name, val);
+}
+
+inline bool
 NameOperation(JSContext *cx, jsbytecode *pc, MutableHandleValue vp)
 {
     RootedObject obj(cx, cx->stack.currentScriptedScopeChain());
     RootedPropertyName name(cx, cx->stack.currentScript()->getName(pc));
 
     /*
      * Skip along the scope chain to the enclosing global object. This is
      * used for GNAME opcodes where the bytecode emitter has determined a
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -5,17 +5,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * JavaScript operation bytecodes.  If you need to allocate a bytecode, look
  * for a name of the form JSOP_UNUSED* and claim it.  Otherwise, always add at
  * the end of the table.
  *
- * When changing the bytecode, don't forget to update JSXDR_BYTECODE_VERSION!
+ * When changing the bytecode, don't forget to update XDR_BYTECODE_VERSION in
+ * vm/Xdr.h!
  *
  * Includers must define an OPDEF macro of the following form:
  *
  * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ...
  *
  * Selected arguments can be expanded in initializers.  The op argument is
  * expanded followed by comma in the JSOp enum (jsopcode.h), e.g.  The value
  * field must be dense for now, because jsopcode.c uses an OPDEF() expansion
@@ -353,28 +354,28 @@ OPDEF(JSOP_GETALIASEDVAR, 136,"getaliase
 OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL,  9,  0,  1, 19,  JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET)
 OPDEF(JSOP_SETALIASEDVAR, 138,"setaliasedvar",NULL,   9,  1,  1,  3,  JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INCALIASEDVAR, 139,"incaliasedvar",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
 OPDEF(JSOP_DECALIASEDVAR, 140,"decaliasedvar",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
 OPDEF(JSOP_ALIASEDVARINC, 141,"aliasedvarinc",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
 OPDEF(JSOP_ALIASEDVARDEC, 142,"aliasedvardec",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
 
 /*
- * Intrinsic names are emitted instead of JSOP_*NAME ops when the 
+ * Intrinsic names are emitted instead of JSOP_*NAME ops when the
  * CompileOptions flag "selfHostingMode" is set.
  *
- * They are used in self-hosted code to access other self-hosted values and 
+ * They are used in self-hosted code to access other self-hosted values and
  * intrinsic functions the runtime doesn't give client JS code access to.
  */
-OPDEF(JSOP_INTRINSICNAME, 143, "intrinsicname", NULL, 5,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET)
+OPDEF(JSOP_GETINTRINSIC,  143, "getintrinsic",  NULL, 5,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET)
 OPDEF(JSOP_CALLINTRINSIC, 144, "callintrinsic", NULL, 5,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET)
+OPDEF(JSOP_SETINTRINSIC,  145, "setintrinsic",  NULL, 5,  2,  1,  3,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING)
+OPDEF(JSOP_BINDINTRINSIC, 146, "bindintrinsic", NULL, 5,  0,  1,  0,  JOF_ATOM|JOF_NAME|JOF_SET)
 
 /* Unused. */
-OPDEF(JSOP_UNUSED10,      145,"unused10", NULL,       1,  0,  0,  0,  JOF_BYTE)
-OPDEF(JSOP_UNUSED11,      146,"unused11", NULL,       1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED12,      147,"unused12", NULL,       1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED13,      148,"unused13", NULL,       1,  0,  0,  0,  JOF_BYTE)
 
 /* Placeholders for a real jump opcode set during backpatch chain fixup. */
 OPDEF(JSOP_BACKPATCH,     149,"backpatch",NULL,       5,  0,  0,  0,  JOF_JUMP|JOF_BACKPATCH)
 OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL,   5,  1,  0,  0,  JOF_JUMP|JOF_BACKPATCH)
 
 /* Set pending exception from the stack, to trigger rethrow. */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -2776,25 +2776,25 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_CALLNAME)
           {
             PropertyName *name = script_->getName(GET_UINT32_INDEX(PC));
             jsop_name(name, knownPushedType(0));
             frame.extra(frame.peek(-1)).name = name;
           }
           END_CASE(JSOP_NAME)
 
-          BEGIN_CASE(JSOP_INTRINSICNAME)
+          BEGIN_CASE(JSOP_GETINTRINSIC)
           BEGIN_CASE(JSOP_CALLINTRINSIC)
           {
             PropertyName *name = script_->getName(GET_UINT32_INDEX(PC));
-            if (!jsop_intrinsicname(name, knownPushedType(0)))
+            if (!jsop_intrinsic(name, knownPushedType(0)))
                 return Compile_Error;
             frame.extra(frame.peek(-1)).name = name;
           }
-          END_CASE(JSOP_INTRINSICNAME)
+          END_CASE(JSOP_GETINTRINSIC)
 
           BEGIN_CASE(JSOP_IMPLICITTHIS)
           {
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(script_->getName(GET_UINT32_INDEX(PC))), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::ImplicitThis, REJOIN_FALLTHROUGH);
             frame.pushSynced(JSVAL_TYPE_UNKNOWN);
           }
@@ -5802,17 +5802,17 @@ mjit::Compiler::jsop_setprop(PropertyNam
     labels.setInlineValueStore(masm, pic.fastPathRejoin, inlineValueStore);
     labels.setInlineShapeJump(masm, pic.shapeGuard, afterInlineShapeJump);
 
     pics.append(pic);
     return true;
 }
 
 bool
-mjit::Compiler::jsop_intrinsicname(PropertyName *name, JSValueType type)
+mjit::Compiler::jsop_intrinsic(PropertyName *name, JSValueType type)
 {
     if (type == JSVAL_TYPE_UNKNOWN) {
         prepareStubCall(Uses(0));
         masm.move(ImmPtr(name), Registers::ArgReg1);
         INLINE_STUBCALL(stubs::IntrinsicName, REJOIN_FALLTHROUGH);
         testPushedType(REJOIN_FALLTHROUGH, 0, /* ool = */ false);
         frame.pushSynced(JSVAL_TYPE_UNKNOWN);
         return true;
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -658,17 +658,17 @@ private:
     void jsop_setelem_slow();
     void jsop_getelem_slow();
     bool jsop_getprop(PropertyName *name, JSValueType type,
                       bool typeCheck = true, bool forPrototype = false);
     bool jsop_getprop_dispatch(PropertyName *name);
     bool jsop_setprop(PropertyName *name, bool popGuaranteed);
     void jsop_setprop_slow(PropertyName *name);
     bool jsop_instanceof();
-    bool jsop_intrinsicname(PropertyName *name, JSValueType type);
+    bool jsop_intrinsic(PropertyName *name, JSValueType type);
     void jsop_name(PropertyName *name, JSValueType type);
     bool jsop_xname(PropertyName *name);
     void enterBlock(StaticBlockObject *block);
     void leaveBlock();
     void emitEval(uint32_t argc);
     bool jsop_tableswitch(jsbytecode *pc);
     Jump getNewObject(JSContext *cx, RegisterID result, JSObject *templateObject);
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -371,22 +371,27 @@ GlobalObject::initFunctionAndObjectClass
     RootedFunction throwTypeError(cx, js_NewFunction(cx, NullPtr(), ThrowTypeError, 0,
                                                      JSFunction::NATIVE_FUN, self, NullPtr()));
     if (!throwTypeError)
         return NULL;
     if (!throwTypeError->preventExtensions(cx))
         return NULL;
     self->setThrowTypeError(throwTypeError);
 
-    RootedObject intrinsicsHolder(cx, JS_NewObject(cx, NULL, NULL, self));
-    if (!intrinsicsHolder)
-        return NULL;
+    RootedObject intrinsicsHolder(cx);
+    if (cx->runtime->isSelfHostingGlobal(self)) {
+        intrinsicsHolder = this;
+    } else {
+        intrinsicsHolder = NewObjectWithClassProto(cx, &ObjectClass, NULL, self);
+        if (!intrinsicsHolder)
+            return NULL;
+    }
     self->setIntrinsicsHolder(intrinsicsHolder);
     /* Define a property 'global' with the current global as its value. */
-    RootedValue global(cx, OBJECT_TO_JSVAL(self));
+    RootedValue global(cx, ObjectValue(*self));
     if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global,
                                   global, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return NULL;
     }
 
     /*
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -329,21 +329,16 @@ class GlobalObject : public JSObject
     JSObject *getOrCreateDateTimeFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
     }
 
     JSObject *getIteratorPrototype() {
         return &getPrototype(JSProto_Iterator).toObject();
     }
 
-    JSObject *intrinsicsHolder() {
-        JS_ASSERT(!getSlotRef(INTRINSICS).isUndefined());
-        return &getSlotRef(INTRINSICS).toObject();
-    }
-
   private:
     typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
 
     JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
         Value v = getSlotRef(slot);
         if (v.isObject())
             return &v.toObject();
         Rooted<GlobalObject*> self(cx, this);
@@ -377,29 +372,44 @@ class GlobalObject : public JSObject
         if (dataViewClassInitialized())
             return &getPrototype(JSProto_DataView).toObject();
         Rooted<GlobalObject*> self(cx, this);
         if (!js_InitTypedArrayClasses(cx, self))
             return NULL;
         return &self->getPrototype(JSProto_DataView).toObject();
     }
 
+    JSObject *intrinsicsHolder() {
+        JS_ASSERT(!getSlotRef(INTRINSICS).isUndefined());
+        return &getSlotRef(INTRINSICS).toObject();
+    }
+
     bool getIntrinsicValue(JSContext *cx, PropertyName *name, MutableHandleValue value) {
-        RootedObject holder(cx, &getSlotRef(INTRINSICS).toObject());
+        RootedObject holder(cx, intrinsicsHolder());
         RootedId id(cx, NameToId(name));
         if (HasDataProperty(cx, holder, id, value.address()))
             return true;
         Rooted<PropertyName*> rootedName(cx, name);
         if (!cx->runtime->cloneSelfHostedValue(cx, rootedName, value))
             return false;
         mozilla::DebugOnly<bool> ok = JS_DefinePropertyById(cx, holder, id, value, NULL, NULL, 0);
         JS_ASSERT(ok);
         return true;
     }
 
+    bool setIntrinsicValue(JSContext *cx, PropertyName *name, HandleValue value) {
+#ifdef DEBUG
+        RootedObject self(cx, this);
+        JS_ASSERT(cx->runtime->isSelfHostingGlobal(self));
+#endif
+        RootedObject holder(cx, intrinsicsHolder());
+        RootedValue valCopy(cx, value);
+        return JSObject::setProperty(cx, holder, holder, name, &valCopy, false);
+     }
+
     inline RegExpStatics *getRegExpStatics() const;
 
     JSObject *getThrowTypeError() const {
         JS_ASSERT(functionObjectClassesInitialized());
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
     Value booleanValueOf() const {
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -9,16 +9,22 @@
 #include "jscompartment.h"
 #include "jsinterp.h"
 #include "jsnum.h"
 #include "jsobj.h"
 
 #include "gc/Marking.h"
 
 #include "jsfuninlines.h"
+#include "jstypedarrayinlines.h"
+
+#include "vm/BooleanObject-inl.h"
+#include "vm/NumberObject-inl.h"
+#include "vm/RegExpObject-inl.h"
+#include "vm/StringObject-inl.h"
 
 #include "selfhosted.out.h"
 
 using namespace js;
 
 static void
 selfHosting_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
 {
@@ -123,19 +129,18 @@ intrinsic_DecompileArg(JSContext *cx, un
 }
 
 static JSBool
 intrinsic_MakeConstructible(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() >= 1);
     JS_ASSERT(args[0].isObject());
-    RootedObject obj(cx, &args[0].toObject());
-    JS_ASSERT(obj->isFunction());
-    obj->toFunction()->setIsSelfHostedConstructor();
+    JS_ASSERT(args[0].toObject().isFunction());
+    args[0].toObject().toFunction()->setIsSelfHostedConstructor();
     return true;
 }
 
 JSFunctionSpec intrinsic_functions[] = {
     JS_FN("ToObject",           intrinsic_ToObject,             1,0),
     JS_FN("ToInteger",          intrinsic_ToInteger,            1,0),
     JS_FN("IsCallable",         intrinsic_IsCallable,           1,0),
     JS_FN("ThrowError",         intrinsic_ThrowError,           4,0),
@@ -147,17 +152,24 @@ bool
 JSRuntime::initSelfHosting(JSContext *cx)
 {
     JS_ASSERT(!selfHostingGlobal_);
     RootedObject savedGlobal(cx, JS_GetGlobalObject(cx));
     if (!(selfHostingGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class, NULL)))
         return false;
     JS_SetGlobalObject(cx, selfHostingGlobal_);
     JSAutoCompartment ac(cx, cx->global());
-    RootedObject shg(cx, selfHostingGlobal_);
+    Rooted<GlobalObject*> shg(cx, &selfHostingGlobal_->asGlobal());
+    /*
+     * During initialization of standard classes for the self-hosting global,
+     * all self-hosted functions are ignored. Thus, we don't create cyclic
+     * dependencies in the order of initialization.
+     */
+    if (!GlobalObject::initStandardClasses(cx, shg))
+        return false;
 
     if (!JS_DefineFunctions(cx, shg, intrinsic_functions))
         return false;
 
     CompileOptions options(cx);
     options.setFileAndLine("self-hosted", 1);
     options.setSelfHostingMode(true);
 
@@ -186,31 +198,151 @@ JSRuntime::initSelfHosting(JSContext *cx
 }
 
 void
 JSRuntime::markSelfHostingGlobal(JSTracer *trc)
 {
     MarkObjectRoot(trc, &selfHostingGlobal_, "self-hosting global");
 }
 
-bool
-JSRuntime::getUnclonedSelfHostedValue(JSContext *cx, Handle<PropertyName*> name,
-                                      MutableHandleValue vp)
+typedef AutoObjectObjectHashMap CloneMemory;
+static bool CloneValue(JSContext *cx, MutableHandleValue vp, CloneMemory &clonedObjects);
+
+static bool
+GetUnclonedValue(JSContext *cx, Handle<JSObject*> src, HandleId id, MutableHandleValue vp)
+{
+    AutoCompartment ac(cx, src);
+    return JSObject::getGeneric(cx, src, src, id, vp);
+}
+
+static bool
+CloneProperties(JSContext *cx, HandleObject obj, HandleObject clone, CloneMemory &clonedObjects)
+{
+    RootedId id(cx);
+    RootedValue val(cx);
+    AutoIdVector ids(cx);
+    {
+        AutoCompartment ac(cx, obj);
+        if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &ids))
+            return false;
+    }
+    for (uint32_t i = 0; i < ids.length(); i++) {
+        id = ids[i];
+        if (!GetUnclonedValue(cx, obj, id, &val) ||
+            !CloneValue(cx, &val, clonedObjects) ||
+            !JSObject::setGeneric(cx, clone, clone, id, &val, false))
+        {
+            return false;
+        }
+    }
+    return true;
+}
+static RawObject
+CloneDenseArray(JSContext *cx, HandleObject obj, CloneMemory &clonedObjects)
+{
+    uint32_t len = obj->getArrayLength();
+    RootedObject clone(cx, NewDenseAllocatedArray(cx, len));
+    clone->setDenseArrayInitializedLength(len);
+    for (uint32_t i = 0; i < len; i++)
+        JSObject::initDenseArrayElementWithType(cx, clone, i, UndefinedValue());
+    RootedValue elt(cx);
+    for (uint32_t i = 0; i < len; i++) {
+        bool present;
+        if (!obj->getElementIfPresent(cx, obj, obj, i, &elt, &present))
+            return NULL;
+        if (present) {
+            if (!CloneValue(cx, &elt, clonedObjects))
+                return NULL;
+            JSObject::setDenseArrayElementWithType(cx, clone, i, elt);
+        }
+    }
+    return clone;
+}
+static RawObject
+CloneObject(JSContext *cx, HandleObject srcObj, CloneMemory &clonedObjects)
 {
-    RootedObject shg(cx, selfHostingGlobal_);
-    AutoCompartment ac(cx, shg);
-    return JS_GetPropertyById(cx, shg, NameToId(name), vp.address());
+    CloneMemory::AddPtr p = clonedObjects.lookupForAdd(srcObj.get());
+    if (p)
+        return p->value;
+    RootedObject clone(cx);
+    if (srcObj->isFunction()) {
+        RootedFunction fun(cx, srcObj->toFunction());
+        clone = CloneFunctionObject(cx, fun, cx->global(), fun->getAllocKind());
+    } else if (srcObj->isRegExp()) {
+        RegExpObject &reobj = srcObj->asRegExp();
+        RootedAtom source(cx, reobj.getSource());
+        clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), NULL);
+    } else if (srcObj->isDate()) {
+        clone = JS_NewDateObjectMsec(cx, srcObj->getDateUTCTime().toNumber());
+    } else if (srcObj->isBoolean()) {
+        clone = BooleanObject::create(cx, srcObj->asBoolean().unbox());
+    } else if (srcObj->isNumber()) {
+        clone = NumberObject::create(cx, srcObj->asNumber().unbox());
+    } else if (srcObj->isString()) {
+        Rooted<JSStableString*> str(cx, srcObj->asString().unbox()->ensureStable(cx));
+        if (!str)
+            return NULL;
+        str = js_NewStringCopyN(cx, str->chars().get(), str->length())->ensureStable(cx);
+        if (!str)
+            return NULL;
+        clone = StringObject::create(cx, str);
+    } else if (srcObj->isDenseArray()) {
+        return CloneDenseArray(cx, srcObj, clonedObjects);
+    } else {
+        if (srcObj->isArray()) {
+            clone = NewDenseEmptyArray(cx);
+        } else {
+            JS_ASSERT(srcObj->isNative());
+            clone = NewObjectWithClassProto(cx, srcObj->getClass(), NULL, cx->global(),
+                                            srcObj->getAllocKind());
+        }
+    }
+    if (!clone || !clonedObjects.relookupOrAdd(p, srcObj.get(), clone.get()) ||
+        !CloneProperties(cx, srcObj, clone, clonedObjects))
+    {
+        return NULL;
+    }
+    return clone;
+}
+
+static bool
+CloneValue(JSContext *cx, MutableHandleValue vp, CloneMemory &clonedObjects)
+{
+    if (vp.isObject()) {
+        RootedObject obj(cx, &vp.toObject());
+        RootedObject clone(cx, CloneObject(cx, obj, clonedObjects));
+        if (!clone)
+            return false;
+        vp.setObject(*clone);
+    } else if (vp.isBoolean() || vp.isNumber() || vp.isNullOrUndefined()) {
+        // Nothing to do here: these are represented inline in the value
+    } else if (vp.isString()) {
+        Rooted<JSStableString*> str(cx, vp.toString()->ensureStable(cx));
+        if (!str)
+            return false;
+        RootedString clone(cx, js_NewStringCopyN(cx, str->chars().get(), str->length()));
+        if (!clone)
+            return false;
+        vp.setString(clone);
+    } else {
+        if (JSString *valSrc = JS_ValueToSource(cx, vp))
+            printf("Error: Can't yet clone value: %s\n", JS_EncodeString(cx, valSrc));
+        return false;
+    }
+    return true;
 }
 
 bool
 JSRuntime::cloneSelfHostedFunctionScript(JSContext *cx, Handle<PropertyName*> name,
                                          Handle<JSFunction*> targetFun)
 {
+    RootedObject shg(cx, selfHostingGlobal_);
     RootedValue funVal(cx);
-    if (!getUnclonedSelfHostedValue(cx, name, &funVal))
+    RootedId id(cx, NameToId(name));
+    if (!GetUnclonedValue(cx, shg, id, &funVal))
         return false;
 
     RootedFunction sourceFun(cx, funVal.toObject().toFunction());
     Rooted<JSScript*> sourceScript(cx, sourceFun->nonLazyScript());
     JS_ASSERT(!sourceScript->enclosingStaticScope());
     RawScript cscript = CloneScript(cx, NullPtr(), targetFun, sourceScript);
     if (!cscript)
         return false;
@@ -219,30 +351,27 @@ JSRuntime::cloneSelfHostedFunctionScript
     JS_ASSERT(sourceFun->nargs == targetFun->nargs);
     targetFun->flags = sourceFun->flags | JSFunction::EXTENDED;
     return true;
 }
 
 bool
 JSRuntime::cloneSelfHostedValue(JSContext *cx, Handle<PropertyName*> name, MutableHandleValue vp)
 {
-    RootedValue funVal(cx);
-    if (!getUnclonedSelfHostedValue(cx, name, &funVal))
-        return false;
+    RootedObject shg(cx, selfHostingGlobal_);
+    RootedValue val(cx);
+    RootedId id(cx, NameToId(name));
+    if (!GetUnclonedValue(cx, shg, id, &val))
+         return false;
 
     /*
      * We don't clone if we're operating in the self-hosting global, as that
      * means we're currently executing the self-hosting script while
      * initializing the runtime (see JSRuntime::initSelfHosting).
      */
-    if (cx->global() == selfHostingGlobal_) {
-        vp.set(funVal);
-    } else if (funVal.isObject() && funVal.toObject().isFunction()) {
-        RootedFunction fun(cx, funVal.toObject().toFunction());
-        RootedObject clone(cx, CloneFunctionObject(cx, fun, cx->global(), fun->getAllocKind()));
-        if (!clone)
+    if (cx->global() != selfHostingGlobal_) {
+        CloneMemory clonedObjects(cx);
+        if (!clonedObjects.init() || !CloneValue(cx, &val, clonedObjects))
             return false;
-        vp.setObject(*clone);
-    } else {
-        vp.setUndefined();
     }
+    vp.set(val);
     return true;
 }