CompileStatus mjit::Compiler::inlineNativeFunction(uint32_t 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->global()) return Compile_InlineAbort; Native native = callee->toFunction()->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 (native == js::num_parseInt && argc >= 1) { FrameEntry *arg = frame.peek(-(int32_t)argc); JSValueType argType = arg->isTypeKnown() ? arg->getKnownType() : JSVAL_TYPE_UNKNOWN; if ((argType == JSVAL_TYPE_DOUBLE || argType == JSVAL_TYPE_INT32) && type == JSVAL_TYPE_INT32) { return compileParseInt(argType, argc); } } if (argc == 0) { if ((native == js::array_pop || native == js::array_shift) && thisType == JSVAL_TYPE_OBJECT) { /* * Only handle pop/shift 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) && !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { bool packed = !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED_ARRAY); return compileArrayPopShift(thisValue, packed, native == js::array_pop); } } } else if (argc == 1) { FrameEntry *arg = frame.peek(-1); types::TypeSet *argTypes = frame.extra(arg).types; if (!argTypes) return Compile_InlineAbort; 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 && masm.supportsFloatingPointSqrt() && (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::str_fromCharCode && argType == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_STRING) { return compileStringFromCode(arg); } 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) && !types::ArrayPrototypeHasIndexedProperty(cx, outerScript)) { return compileArrayPush(thisValue, arg); } } if (native == js::array_concat && argType == JSVAL_TYPE_OBJECT && thisType == JSVAL_TYPE_OBJECT && type == JSVAL_TYPE_OBJECT && !thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY) && !argTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY)) { return compileArrayConcat(thisTypes, argTypes, 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 && masm.supportsFloatingPointSqrt() && (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); } if ((native == js_math_min || native == js_math_max)) { if (arg1Type == JSVAL_TYPE_INT32 && arg2Type == JSVAL_TYPE_INT32 && type == JSVAL_TYPE_INT32) { return compileMathMinMaxInt(arg1, arg2, native == js_math_min ? Assembler::LessThan : Assembler::GreaterThan); } if ((arg1Type == JSVAL_TYPE_INT32 || arg1Type == JSVAL_TYPE_DOUBLE) && (arg2Type == JSVAL_TYPE_INT32 || arg2Type == JSVAL_TYPE_DOUBLE) && type == JSVAL_TYPE_DOUBLE) { return compileMathMinMaxDouble(arg1, arg2, (native == js_math_min) ? Assembler::DoubleLessThan : Assembler::DoubleGreaterThan); } } } return Compile_InlineAbort; }
void JS_FASTCALL stubs::DefFun(VMFrame &f, JSFunction *fun) { JSObject *obj2; JSContext *cx = f.cx; StackFrame *fp = f.fp(); /* * A top-level function defined in Global or Eval code (see ECMA-262 * Ed. 3), or else a SpiderMonkey extension: a named function statement in * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ JSObject *obj = fun; if (fun->isNullClosure()) { /* * Even a null closure needs a parent for principals finding. * FIXME: bug 476950, although debugger users may also demand some kind * of scope link for debugger-assisted eval-in-frame. */ obj2 = &fp->scopeChain(); } else { obj2 = GetScopeChain(cx, fp); if (!obj2) THROW(); } /* * If static link is not current scope, clone fun's object to link to the * current scope via parent. We do this to enable sharing of compiled * functions among multiple equivalent scopes, amortizing the cost of * compilation over a number of executions. Examples include XUL scripts * and event handlers shared among Firefox or other Mozilla app chrome * windows, and user-defined JS functions precompiled and then shared among * requests in server-side JS. */ if (obj->toFunction()->environment() != obj2) { obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2); if (!obj) THROW(); JS_ASSERT_IF(f.script()->compileAndGo, obj->global() == fun->global()); } /* * ECMA requires functions defined when entering Eval code to be * impermanent. */ unsigned attrs = fp->isEvalFrame() ? JSPROP_ENUMERATE : JSPROP_ENUMERATE | JSPROP_PERMANENT; /* * We define the function as a property of the variable object and not the * current scope chain even for the case of function expression statements * and functions defined by eval inside let or with blocks. */ JSObject *parent = &fp->varObj(); /* ES5 10.5 (NB: with subsequent errata). */ PropertyName *name = fun->atom->asPropertyName(); JSProperty *prop = NULL; JSObject *pobj; if (!parent->lookupProperty(cx, name, &pobj, &prop)) THROW(); Value rval = ObjectValue(*obj); do { /* Steps 5d, 5f. */ if (!prop || pobj != parent) { if (!parent->defineProperty(cx, name, rval, JS_PropertyStub, JS_StrictPropertyStub, attrs)) { THROW(); } break; } /* Step 5e. */ JS_ASSERT(parent->isNative()); Shape *shape = reinterpret_cast<Shape *>(prop); if (parent->isGlobal()) { if (shape->configurable()) { if (!parent->defineProperty(cx, name, rval, JS_PropertyStub, JS_StrictPropertyStub, attrs)) { THROW(); } break; } if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) { JSAutoByteString bytes; if (js_AtomToPrintableString(cx, name, &bytes)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REDEFINE_PROP, bytes.ptr()); } THROW(); } } /* * Non-global properties, and global properties which we aren't simply * redefining, must be set. First, this preserves their attributes. * Second, this will produce warnings and/or errors as necessary if the * specified Call object property is not writable (const). */ /* Step 5f. */ if (!parent->setProperty(cx, name, &rval, strict)) THROW(); } while (false); }