static JSString * ValueToShortSource(JSContext *cx, const Value &v) { JSString *str; /* Avoid toSource bloat and fallibility for object types. */ if (!v.isObject()) return js_ValueToSource(cx, v); JSObject *obj = &v.toObject(); AutoCompartment ac(cx, obj); if (!ac.enter()) return NULL; if (obj->isFunction()) { /* * XXX Avoid function decompilation bloat for now. */ str = JS_GetFunctionId(obj->getFunctionPrivate()); if (!str && !(str = js_ValueToSource(cx, v))) { /* * Continue to soldier on if the function couldn't be * converted into a string. */ JS_ClearPendingException(cx); str = JS_NewStringCopyZ(cx, "[unknown function]"); } } else { /* * XXX Avoid toString on objects, it takes too long and uses too much * memory, for too many classes (see Mozilla bug 166743). */ char buf[100]; JS_snprintf(buf, sizeof buf, "[object %s]", obj->getClass()->name); str = JS_NewStringCopyZ(cx, buf); } ac.leave(); if (!str || !cx->compartment->wrap(cx, &str)) return NULL; return str; }
void JS_FASTCALL stubs::Eval(VMFrame &f, uint32 argc) { Value *vp = f.regs.sp - (argc + 2); JSObject *callee; JSFunction *fun; if (!IsFunctionObject(*vp, &callee) || !IsBuiltinEvalFunction((fun = callee->getFunctionPrivate()))) { if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0)) THROW(); return; } JS_ASSERT(f.regs.fp == f.cx->fp()); if (!DirectEval(f.cx, fun, argc, vp)) THROW(); }
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->getFunctionPrivate(); JS_ASSERT(fun->isInterpreted()); JSScript *inner = fun->script(); if (outer->isHeavyweightFunction) { 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 nGlobalUses = globalUses->length; for (uint32 i = 0; i < nGlobalUses; i++) { uint32 index = globalUses->vector[i].slot; JS_ASSERT(index < globalScope.defs.length()); globalUses->vector[i].slot = globalScope.defs[index].knownSlot; } } return true; }
CompileStatus mjit::Compiler::inlineNativeFunction(uint32 argc, bool callingNew) { if (!cx->typeInferenceEnabled()) return Compile_InlineAbort; if (applyTricks == LazyArgsObj) return Compile_InlineAbort; FrameEntry *origCallee = frame.peek(-((int)argc + 2)); FrameEntry *thisValue = frame.peek(-((int)argc + 1)); types::TypeSet *thisTypes = analysis->poppedTypes(PC, argc); if (!origCallee->isConstant() || !origCallee->isType(JSVAL_TYPE_OBJECT)) return Compile_InlineAbort; JSObject *callee = &origCallee->getValue().toObject(); if (!callee->isFunction()) return Compile_InlineAbort; /* * The callee must have the same parent as the script's global, otherwise * inference may not have accounted for any side effects correctly. */ if (!globalObj || globalObj != callee->getGlobal()) return Compile_InlineAbort; JSFunction *fun = callee->getFunctionPrivate(); Native native = fun->maybeNative(); if (!native) return Compile_InlineAbort; JSValueType type = knownPushedType(0); JSValueType thisType = thisValue->isTypeKnown() ? thisValue->getKnownType() : JSVAL_TYPE_UNKNOWN; /* * Note: when adding new natives which operate on properties, add relevant * constraint generation to the behavior of TypeConstraintCall. */ /* Handle natives that can be called either with or without 'new'. */ if (native == js_Array && type == JSVAL_TYPE_OBJECT && globalObj) { if (argc == 0 || argc == 1) return compileArrayWithLength(argc); return compileArrayWithArgs(argc); } /* Remaining natives must not be called with 'new'. */ if (callingNew) return Compile_InlineAbort; if (argc == 0) { if (native == js::array_pop && thisType == JSVAL_TYPE_OBJECT) { /* * Only inline pop() on dense arrays which have never been used in * an iterator --- when popping elements we don't account for * suppressing deleted properties in active iterators. * * Constraints propagating properties directly into the result * type set are generated by TypeConstraintCall during inference. */ if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY | types::OBJECT_FLAG_ITERATED) && !arrayPrototypeHasIndexedProperty()) { bool packed = !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); return compileArrayPop(thisValue, packed); } } } else if (argc == 1) { FrameEntry *arg = frame.peek(-1); JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN; if (native == js_math_abs) { if (argType == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_INT32) return compileMathAbsInt(arg); if (argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_DOUBLE) return compileMathAbsDouble(arg); } if (native == js_math_floor && argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_INT32) { return compileRound(arg, Floor); } if (native == js_math_round && argType == JSVAL_TYPE_DOUBLE && type == JSVAL_TYPE_INT32) { return compileRound(arg, Round); } if (native == js_math_sqrt && type == JSVAL_TYPE_DOUBLE && (argType == JSVAL_TYPE_INT32 || argType == JSVAL_TYPE_DOUBLE)) { return compileMathSqrt(arg); } if (native == js_str_charCodeAt && argType == JSVAL_TYPE_INT32 && thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_INT32) { return compileGetChar(thisValue, arg, GetCharCode); } if (native == js_str_charAt && argType == JSVAL_TYPE_INT32 && thisType == JSVAL_TYPE_STRING && type == JSVAL_TYPE_STRING) { return compileGetChar(thisValue, arg, GetChar); } if (native == js::array_push && thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_INT32) { /* * Constraints propagating properties into the 'this' object are * generated by TypeConstraintCall during inference. */ if (!thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && !arrayPrototypeHasIndexedProperty()) { return compileArrayPush(thisValue, arg); } } } else if (argc == 2) { FrameEntry *arg1 = frame.peek(-2); FrameEntry *arg2 = frame.peek(-1); JSValueType arg1Type = arg1->isTypeKnown() ? arg1->getKnownType() : JSVAL_TYPE_UNKNOWN; JSValueType arg2Type = arg2->isTypeKnown() ? arg2->getKnownType() : JSVAL_TYPE_UNKNOWN; if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE && (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) && arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant()) { Value arg2Value = arg2->getValue(); if (arg2Value.toDouble() == -0.5 || arg2Value.toDouble() == 0.5) return compileMathPowSimple(arg1, arg2); } } return Compile_InlineAbort; }